PCA(Principal Components Analysis)主成分分析是一个简单的机器学习算法,利用正交变换把由线性相关变量表示的观测数据转换为由少量线性无关比变量表示的数据,实现降维的同时尽量减少精度的损失,线性无关的变量称为主成分。大致流程如下:

  首先对给定数据集(数据是向量)进行规范化,使得数据集的平均值为0,方差为1(规范化是为了使数据散布在原点附近,而不是远离原点的某块区域,便于后面的计算)。之后对每个数据进行正交变换,把数据投影到几个少量的相互正交的方向(这些方向构成了数据空间的一个子空间)上。数据在每个方向上都有对应的坐标,而用这些方向和对应的坐标(坐标×方向的累加)就能近似表示原来高维的数据。因此这些方向的求解就是PCA的关键,这些方向有如下性质:

  如果再由这些坐标通过对应的方向映射回原来的数据,精度损失是同等方向数量的方向集合(或者叫同维度的子空间吧)中最小的,而数据集在各个方向上的坐标的方差之和是同等方向数量的方向集合中最大的,也正对应着方差越大表示这个方向上保存数据的信息量越大(方差越小所有数据越集中在一个点上信息量当然越小)。数据集在这些方向的上的坐标的方差,从大到小排序,就把这每一个方向称为第一主成分、第二主成分……

主成分推导

  接下来推导什么样的方向是主成分,即什么样的方向集合能保存原数据集更多的信息。下面把数据映射为各个方向上的坐标称为编码(即降维),再反映射回来称为解码。

  定义矩阵$X\in R^{m\times n}$为数据集,由$m$个$n$维行向量数据$\{x_1,x_2,...,x_m\}$组成。首先求得数据均值与方差(以下是按元素进行的运算):

$\displaystyle\overline{x}=\sum\limits_{i=1}^{m}\frac{x_i}{m}$

$\displaystyle s=\frac{1}{m-1} \sum\limits_{i=1}^{m}\left(x_i-\overline{x}\right)^2 $

  然后规范化样本:

$\displaystyle x_i^\ast = \frac{x_i-\overline{x}}{\sqrt{s}}, i=1,2,\ldots,m$

  获得规范化后的数据集$X^*$。随后PCA将把$X^*$编码成矩阵$Y^*\in R^{m\times k},k<n$,通过右乘列向量单位正交的方向矩阵$D\in R^{n\times k}$得到编码后的矩阵$Y^*$:

$Y^* = X^*\cdot D$

  而再解码回来是再乘上$D^T$:

$\hat{X^*} = Y^*\cdot D^{T}$

  因为矩阵$D$并不可逆,所以编码是不可逆的,也就是说解码并不能完全恢复回$X^*$。为了最小化精度损失,容易想到,优化$D$来最小化$\hat{X^*}$与$X^*$之差的Frobenius范数:

$ \begin{aligned} &D = \displaystyle\text{arg}\min\limits_D{||\hat{X^*} - X^*||^2_F}\\ &D = \text{arg}\min\limits_D{|| X^*DD^T - X^*||^2_F}\\ &D = \text{arg}\min\limits_D{\text{Tr}[(X^*DD^T - X^*)^T(X^*DD^T - X^*)]}\\ &D = \text{arg}\min\limits_D{\text{Tr}[DD^TX^{*T}X^*DD^T-DD^TX^{*T}X^*-X^{*T}X^*DD^T+X^{*T}X^*]}\\ \end{aligned} $

  去掉与$D$不相关的$X^{*T}X^*$项,再由迹的性质,矩阵乘法循环右移迹的值不变:

$ \begin{aligned} &D = \text{arg}\min\limits_D{\text{Tr}[X^*DD^TDD^TX^{*T}-X^*DD^TX^{*T}-D^TX^{*T}X^*D]}\\ &D = \text{arg}\min\limits_D{\text{Tr}[X^*DD^TX^{*T}-X^*DD^TX^{*T}-D^TX^{*T}X^*D]}\\ &D = \text{arg}\max\limits_D{\text{Tr}[D^TX^{*T}X^*D]}\\ \end{aligned} $

  容易发现当$D$的列向量取$X^{*T}X^*$的前$k$大特征值对应的特征向量时,有这个迹的值最大,等于前$k$大特征值之和,且此时压缩的精度损失最少。

性质

  经过上面的推导,我们可以发现,$X^{*T}X^*$实际上就是$X$行向量每个元素之间的协方差矩阵$\Sigma_x$(没有除以$m-1$)。定义$\lambda_1,\lambda_2,...,\lambda_n$为$\Sigma_x$从大到小排列的特征值。我们可以推出下面两个性质。

  性质一、编码得到的矩阵$Y^*\in R^{m\times k}$的行向量各个元素对应的协方差矩阵$\displaystyle\Sigma_y = \text{diag}(\lambda_1,\lambda_2,...,\lambda_k)$ 。

  性质二、如果不进行压缩,即编码矩阵形如$D\in R^{n\times n}$,从而$Y^*\in R^{m\times n}$,那么$Y^*$的行向量各个元素方差之和等于$X^*$($X$也一样)的行向量各个元素方差之和,也就是:

$\displaystyle\sum\limits_{i=1}^n \Sigma_{y_{ii}}= \sum\limits_{i=1}^n \Sigma_{x_{ii}} = \sum\limits_{i=1}^n (X^{*T}X^*)_{ii}$

  首先证明性质一。

  由之前的定义,$X^*$是$X$经过行标准化后的矩阵,所以对$X^*$的任意列进行求和有

$\displaystyle\sum\limits_{i=1}^mX^*_{ij}=0,\;\;j=1,2,...,n$

  而对于$Y^*=X^*D$,对$Y^*$的任意列进行求和有

$ \begin{aligned} \sum\limits_{i=1}^m Y^*_{ij} &= \sum\limits_{i=1}^mX^*_{i:}D_{:j} = \sum\limits_{i=1}^m\sum\limits_{k=1}^nX^*_{ik}D_{kj}\\ &= \sum\limits_{k=1}^nD_{kj}\sum\limits_{i=1}^mX^*_{ik}= 0,\;\;j=1,2,...,k \end{aligned} $

  每列求和都为0,说明,$Y^*$行向量每个元素的均值都为0,无需再计算行向量的均值进行标准化。因此,可以直接计算$Y^*$的协方差矩阵:

$ \begin{aligned} \Sigma_y = Y^{*T}Y^* = D^TX^{*T}X^*D=\text{diag}(\lambda_1,\lambda_2,...,\lambda_k) \end{aligned} $

  有性质一以后,性质二就容易了:

  如果不进行压缩,由性质一可得,$Y^*$行向量各元素方差之和就是$X^{*T}X^*$的特征值之和,矩阵特征值之和又等于矩阵的迹,而$X^{*T}X^*$的迹就是$X^*$行向量各个元素的方差之和。所以得到性质二。

  另外,映射矩阵$D$的列向量在人脸识别中就是所谓的特征脸(PCA+SVM实现人脸识别)。

手动代码实现

  实验代码:

import matplotlib.pyplot as plt
import pylab #原本在jupyter里才能显示的图片,可以用窗口显示
import numpy as np def PCA_img(img,n):
t = np.mean(img,0) #以行向量为压缩单元,求行平均
s = np.std(img,0) #求行标准差
img=(img-t)/s #每行减去平均值进行规范化
v,vectors = np.linalg.eig(np.dot(img.T,img)/(len(img)-1)) #生成规范化矩阵后,转置乘自身再除以(行数-1),即为行向量各个元素的协方差矩阵,求协方差矩阵的特征值与特征向量
indv = v.argsort() #将特征值排序,从小到大
vectors = vectors[:,indv] #将特征向量按特征值大小排序
vectors = vectors[:,-n:] #选择前n大特征值对应的特征向量,作为映射矩阵。在人脸识别中就是特征脸
img = np.dot(np.dot(img,vectors),vectors.T) #规范化矩阵先乘映射矩阵再乘映射矩阵的转置,也就是先压缩再解压缩
img = np.multiply(img,s)+t #把减掉的均值加回去变为原矩阵
return img img=plt.imread("aaa.jpg") #读取图片 img.flags["WRITEABLE"] = True
img = img.astype(float)
n = 50
img[:,:,0] = PCA_img(img[:,:,0],n)
img[:,:,1] = PCA_img(img[:,:,1],n)
img[:,:,2] = PCA_img(img[:,:,2],n)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(img/255)
print(img)
pylab.show()

  原图是1200×1920的图:

  压缩为1200×50后再解压回去:

sklearn包PCA

  sklearn中计算PCA比直接使用numpy矩阵计算快得多,这是因为它里面实现了比较高效的随机算法,虽然每次算出来的值不一样,但是相差不大。代码如下:

import sklearn.decomposition as dc 

pca = dc.PCA(n_components = 10)       #规定PCA要取前k大特征值对应的特征向量的个数,即要映射到几维,PCA默认压缩行,比如100*100压缩到100*10
pca.fit(A) #将要用于压缩的矩阵传入,用来“学习”压缩矩阵
D = pca.components_ #获取“学习”到的编码矩阵(压缩矩阵)D。原矩阵A经过AD^T会得到压缩后的矩阵(这里的D是上面推算的D^T)
C = pca.transform(B) #传入新的矩阵B,用“学习”到的编码矩阵进行压缩,即BD^T。在人脸特征提取中,同一个人的不同人脸照片,用同一个压缩矩阵压缩后的坐标向量通常相似,因此可以用于人脸识别的降维

PCA——主成分分析的更多相关文章

  1. 用PCA(主成分分析法)进行信号滤波

    用PCA(主成分分析法)进行信号滤波 此文章从我之前的C博客上导入,代码什么的可以参考matlab官方帮助文档 现在网上大多是通过PCA对数据进行降维,其实PCA还有一个用处就是可以进行信号滤波.网上 ...

  2. 机器学习之PCA主成分分析

    前言            以下内容是个人学习之后的感悟,转载请注明出处~ 简介 在用统计分析方法研究多变量的课题时,变量个数太多就会增加课题的复杂性.人们自然希望变量个数较少而得到的 信息较多.在很 ...

  3. PCA主成分分析Python实现

    作者:拾毅者 出处:http://blog.csdn.net/Dream_angel_Z/article/details/50760130 Github源代码:https://github.com/c ...

  4. 机器学习 - 算法 - PCA 主成分分析

    PCA 主成分分析 原理概述 用途 - 降维中最常用的手段 目标 - 提取最有价值的信息( 基于方差 ) 问题 - 降维后的数据的意义 ? 所需数学基础概念 向量的表示 基变换 协方差矩阵 协方差 优 ...

  5. PCA(主成分分析)方法浅析

    PCA(主成分分析)方法浅析 降维.数据压缩 找到数据中最重要的方向:方差最大的方向,也就是样本间差距最显著的方向 在与第一个正交的超平面上找最合适的第二个方向 PCA算法流程 上图第一步描述不正确, ...

  6. PCA主成分分析(上)

    PCA主成分分析 PCA目的 最大可分性(最大投影方差) 投影 优化目标 关键点 推导 为什么要找最大特征值对应的特征向量呢? 之前看3DMM的论文的看到其用了PCA的方法,一开始以为自己对于PCA已 ...

  7. PCA主成分分析方法

    PCA: Principal Components Analysis,主成分分析. 1.引入 在对任何训练集进行分类和回归处理之前,我们首先都需要提取原始数据的特征,然后将提取出的特征数据输入到相应的 ...

  8. 【建模应用】PCA主成分分析原理详解

    原文载于此:http://blog.csdn.net/zhongkelee/article/details/44064401 一.PCA简介 1. 相关背景 上完陈恩红老师的<机器学习与知识发现 ...

  9. PCA主成分分析+白化

    参考链接:http://deeplearning.stanford.edu/wiki/index.php/%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90 h ...

  10. CS229 6.6 Neurons Networks PCA主成分分析

    主成分分析(PCA)是一种经典的降维算法,基于基变换,数据原来位于标准坐标基下,将其投影到前k个最大特征值对应的特征向量所组成的基上,使得数据在新基各个维度有最大的方差,且在新基的各个维度上数据是不相 ...

随机推荐

  1. 【noi 2.6_666】放苹果 & 【noi 2.6_8467】鸣人的影分身(DP)

    这题其实在2.6前面的专题也有出现过,我还以为我有写,结果发现,并没有.于是就现在写了.这2题其实重复了......我就按放苹果的来说. 题意:把N个苹果放在M个盘子里,允许有的盘子空着不放,问共有多 ...

  2. Codeforces Round #531 (Div. 3) C. Doors Breaking and Repairing (博弈)

    题意:有\(n\)扇门,你每次可以攻击某个门,使其hp减少\(x\)(\(\le 0\)后就不可修复了),之后警察会修复某个门,使其hp增加\(y\),问你最多可以破坏多少扇门? 题解:首先如果\(x ...

  3. C# EventWaitHandle类解析

    EventWaitHandle 类用于在异步操作时控制线程间的同步,即控制一个或多个线程继行或者等待其他线程完成. 构造函数 EventWaitHandle(bool initialState, Ev ...

  4. C#程序报找不到时区错误

    原因:win10电脑里的时区在win7里不全有 解决:将win10时区注册表导出,在win7电脑上安装 时区注册表路径:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Wi ...

  5. C#之Dispose

    前言 谈到Dispose,首先需要理解C#的资源 资源类型 托管资源:由CLR创建和释放 非托管资源:资源的创建和释放不由CLR管理.比如IO.网络连接.数据库连接等等.需要开发人员手动释放. 如何释 ...

  6. OpenStack Train版-9.安装neutron网络服务(计算节点)

    在计算节点安装neutron网络服务(computel01计算节点192.168.0.20)安装组件 yum install openstack-neutron-linuxbridge ebtable ...

  7. Hexo准备---Node.js、Vue

    Hexo准备---Node.js.Vue 安装node.js 1.下载node 配置node.js环境官网下载,一直next就好,非常方便. 下载官网: http://nodejs.cn/downlo ...

  8. 利用windows api共享内存通讯

    主要涉及CreateFile,CreateFileMapping,GetLastError,MapViewOfFile,sprintf,OpenFileMapping,CreateProcess Cr ...

  9. redis字符串-sds

    redis自己实现了一种名为简单动态字符串的抽象类型(simple dynamic string)作为字符串的表示.下面将简单介绍sds的实现原理. 一.sds的结构

  10. ossutilmac64

    ossutilmac64 ossutil是以命令行方式管理OSS数据的工具,提供方便.简洁.丰富的存储空间(Bucket)和文件(Object)管理命令,支持Windows.Linux. Mac平台. ...