在许多领域的研究与应用中,通常需要对含有多个变量的数据进行观测,收集大量数据后进行分析寻找规律。多变量大数据集无疑会为研究和应用提供丰富的信息,但是也在一定程度上增加了数据采集的工作量。更重要的是在很多情形下,许多变量之间可能存在相关性,从而增加了问题分析的复杂性。如果分别对每个指标进行分析,分析往往是孤立的,不能完全利用数据中的信息,因此盲目减少指标会损失很多有用的信息,从而产生错误的结论。
因此需要找到一种合理的方法,在减少需要分析的指标同时,尽量减少原指标包含信息的损失,以达到对所收集数据进行全面分析的目的。由于各变量之间存在一定的相关关系,因此可以考虑将关系紧密的变量变成尽可能少的新变量,使这些新变量是两两不相关的,那么就可以用较少的综合指标分别代表存在于各个变量中的各类信息。主成分分析与因子分析就属于这类降维算法。
降维就是一种对高维度特征数据预处理方法。降维是将高维度的数据保留下最重要的一些特征,去除噪声和不重要的特征,从而实现提升数据处理速度的目的。在实际的生产和应用中,降维在一定的信息损失范围内,可以为我们节省大量的时间和成本。降维也成为应用非常广泛的数据预处理方法。
降维具有如下一些优点:
(1)使得数据集更易使用。
(2)降低算法的计算开销。
(3)去除噪声。
(4)使得结果容易理解。
降维的算法有很多,比如奇异值分解(SVD)、主成分分析(PCA)、因子分析(FA)、独立成分分析(ICA)。
PCA(Principal Component Analysis),即主成分分析方法,是一种使用最广泛的数据降维算法。PCA的主要思想是将n维特征映射到k维上,这k维是全新的正交特征也被称为主成分,是在原有n维特征的基础上重新构造出来的k维特征。PCA的工作就是从原始的空间中顺序地找一组相互正交的坐标轴,新的坐标轴的选择与数据本身是密切相关的。其中,第一个新坐标轴选择是原始数据中方差最大的方向,第二个新坐标轴选取是与第一个坐标轴正交的平面中使得方差最大的,第三个轴是与第1,2个轴正交的平面中方差最大的。依次类推,可以得到n个这样的坐标轴。通过这种方式获得的新的坐标轴,我们发现,大部分方差都包含在前面k个坐标轴中,后面的坐标轴所含的方差几乎为0。于是,我们可以忽略余下的坐标轴,只保留前面k个含有绝大部分方差的坐标轴。事实上,这相当于只保留包含绝大部分方差的维度特征,而忽略包含方差几乎为0的特征维度,实现对数据特征的降维处理。
那么我们如何得到这些包含最大差异性的主成分方向呢?事实上,通过计算数据矩阵的协方差矩阵,然后得到协方差矩阵的特征值特征向量,选择特征值最大(即方差最大)的k个特征所对应的特征向量组成的矩阵。这样就可以将数据矩阵转换到新的空间当中,实现数据特征的降维。
由于得到协方差矩阵的特征值特征向量有两种方法:特征值分解协方差矩阵、奇异值分解协方差矩阵,所以PCA算法有两种实现方法:基于特征值分解协方差矩阵实现PCA算法、基于SVD分解协方差矩阵实现PCA算法。
下面具体介绍基于特征值分解协方差矩阵实现PCA算法的原理。
一般来说,欲获得原始数据新的表示空间,最简单的是对原始数据进行线性变换(基变换):Y=PX,其中Y是样本在新空间的表达,P是基向量,X是原始样本。我们可知选择不同的基可以对一组数据给出不同的表示,同时当基的数量少于原始样本本身的维数则可达到降维的效果,矩阵表示如下:
其中,pi是一个行向量,表示第i个基;aj是一个列向量,表示第j个原始数据记录。特别要注意的是,这里R可以小于N,而R决定了变换后数据的维数。也就是说,我们可以将一个N维数据变换到更低维度的空间中去,变换后的维度取决于基的数量。从原本X∈RN*M降维到Y∈RR*M。因此这种矩阵相乘的表示也可以表示降维变换。
最后,上述分析同时给矩阵相乘找到了一种物理解释:两个矩阵相乘的意义是将右边矩阵中的每一列列向量变换到左边矩阵中每一行行向量为基所表示的空间中去。更抽象的说,一个矩阵可以表示一种线性变换。
那么如何选择一个方向或者基才是最优的?观察下图,
我们将所有的点分别向两条直线做投影,基于PCA最大可分思想,我们要找的方向是降维后损失最小,可以理解为投影后的数据尽可能的分开,那么这种分散程度可以用数学上的方差来表示,方差越大数据越分散。方差公式如下:
V a r ( a ) = 1 m ∑ i = 1 n ( a i − μ ) 2 Var(a)=\frac{1}{m}\sum_{i=1}^n(a_i-μ)^2 Var(a)=m1i=1∑n(ai−μ)2
对数据进行了中心化后(可以方便后面的操作):
V a r ( a ) = 1 m ∑ i = 1 m ( a i ) 2 Var(a)=\frac{1}{m}\sum_{i=1}^m(a_i)^2 Var(a)=m1i=1∑m(ai)2
从二维降到一维可以使用方差最大来选出能使基变换后数据分散最大的方向(基),但如果遇到高维的变换,当完成第一个方向(基)选择后,第二个投影方向应该与第一个“几乎重合在一起”,这显然是没有用的,因此要有其它的约束条件。我们希望两个字段尽可能表示更多的信息,使其不存在相关性。
数学上用协方差表示其相关性:
C o v ( a , b ) = 1 m ∑ i = 1 m a i b i Cov(a,b)=\frac{1}{m}\sum_{i=1}^ma_ib_i Cov(a,b)=m1i=1∑maibi
当Cov(a,b)=0时,表示两个字段完全独立,这也是我们的优化目标。
我们想达到的目标与字段内方差及字段间协方差有密切关系,假如只有 、 两个字段,那么我们将它们按行组成矩阵X,表示如下:
X = ( a 1 a 2 . . . a m b 1 b 2 . . . b m ) X=\left( \begin{matrix} a_1 & a_2 & ... & a_m \\ b_1 & b_2 & ... & b_m \end{matrix} \right) X=(a1b1a2b2......ambm)
然后我们用X乘以X的转置,并乘上系数1/m:
1 m X X T = ( 1 m ∑ i = 1 m a i 2 1 m ∑ i = 1 m a i b i 1 m ∑ i = 1 m a i b i 1 m ∑ i = 1 m b i 2 ) \frac{1}mXX^T=\left( \begin{matrix} \frac{1}{m}\sum_{i=1}^ma_i^2 & \frac{1}{m}\sum_{i=1}^ma_ib_i \\ \frac{1}{m}\sum_{i=1}^ma_ib_i & \frac{1}{m}\sum_{i=1}^mb_i^2 \end{matrix} \right) m1XXT=(m1∑i=1mai2m1∑i=1maibim1∑i=1maibim1∑i=1mbi2)
可见,协方差矩阵是一个对称的矩阵,而且对角线是各个维度的方差,而其它元素是a和b的协方差,即两者被统一到了一个矩阵。
我们的目标是使
,根据上述推倒,可以看出我们的优化目标
等价于协方差矩阵对角化。即除对角线外的其它元素化为0,并且在对角线上将元素按大小从上到下排列,这样我们就达到了优化目的。这样说可能还不是很明晰,我们进一步看下原矩阵与基变换后矩阵协方差矩阵的关系:
设原始数据矩阵X对应的协方差矩阵为C,而P是一组基按行组成的矩阵,设Y=PX,则Y为X对P做基变换后的数据。设Y的协方差矩阵为D,我们推导一下D与C的关系:
可见,我们要找的P不是别的,而是能让原始协方差矩阵对角化的P。换句话说,优化目标变成了寻找一个矩阵P,满足PCPT是一个对角矩阵,并且对角元素按从大到小依次排列,那么P的前K行就是要寻找的基,用P的前K行组成的矩阵乘以X就使得X从N维降到了K维并满足上述优化条件。
我们希望的是投影后的方差最大化,于是我们的优化目标可以写为:
m a x p t r ( P C P T ) max_p tr(PCP^T) maxptr(PCPT)
s . t . P P T = I s.t. PP^T = I s.t.PPT=I
利用拉格朗日函数可以得到:J§=tr(PCPT)+ λ(PPT-I),对P求导有CPT+λPT=0,整理下即为:CPT=(-λ)PT。
于是,只需对协方差矩阵C进行特征分解,对求得的特征值进行排序,再对PT=(P1,P2,…,PR)取前K列组成的矩阵乘以原始数据矩阵X,就得到了我们需要的降维后的数据矩阵Y。
从上面可以看出,求样本 xi 的 n’ 维的主成分其实就是求样本集的协方差矩阵的前 n’ 个特征值对应特征向量矩阵 P,然后对于每个样本xi ,做如下变换yi =Pxi ,即达到降维的PCA目的。
下面是具体的算法流程:
输入:n维样本集X=(x1 ,x2,…,xm),要降维到的维数 n’;
输出:降维后的样本集Y。
1.对所有的样本进行中心化;
2.计算样本的协方差矩阵;
3.求出协方差矩阵的特征值及对应的特征向量;
4.将特征向量按对应特征值大小从上到下按行排列成矩阵,取前k行组成矩阵P;
5.Y=PX即为降维到k维后的数据。
注意:有时候,我们不指定降维后的n’的值,而是换种方式,指定一个降维到的主成分比重阈值t。这个阈值t在(0,1]之间。假如我们的n个特征值为λ1≥λ2≥…≥λn,则n’可以通过下式得到:
作为一个非监督学习的降维方法,它只需要特征值分解,就可以对数据进行压缩,去噪。因此在实际场景应用很广泛。为了克服PCA的一些缺点,出现了很多PCA的变种,比如为解决非线性降维的KPCA,还有解决内存限制的增量PCA方法Incremental PCA,以及解决稀疏数据降维的PCA方法Sparse PCA等。
PCA算法的主要优点有:
(1) 仅仅需要以方差衡量信息量,不受数据集以外的因素影响。
(2)各主成分之间正交,可消除原始数据成分间的相互影响的因素。
(3)计算方法简单,主要运算是特征值分解,易于实现。
PCA算法的主要缺点有:
(1)主成分各个特征维度的含义具有一定的模糊性,不如原始样本特征的解释性强。
(2)方差小的非主成分也可能含有对样本差异的重要信息,因降维丢弃可能对后续数据处理有影响。
首先需要实现几个函数,分别是数据中心化、最小化降维造成的损失,确定k、得到最大的k个特征值和特征向量、得到降维后的数据、重构数据,然后通过PCA函数整合,最后调用main函数执行。代码如下:
import numpy as np
import cv2 as cv
# 数据中心化
def Z_centered(dataMat):
rows, cols = dataMat.shape
meanVal = np.mean(dataMat, axis=0) # 按列求均值,即求各个特征的均值
meanVal = np.tile(meanVal, (rows, 1))
newdata = dataMat - meanVal
return newdata, meanVal
# 最小化降维造成的损失,确定k
def Percentage2n(eigVals, percentage):
sortArray = np.sort(eigVals) # 升序
sortArray = sortArray[-1::-1] # 逆转,即降序
arraySum = sum(sortArray)
tmpSum = 0
num = 0
for i in sortArray:
tmpSum += i
num += 1
if tmpSum >= arraySum * percentage:
return num
# 得到最大的k个特征值和特征向量
def EigDV(covMat, p):
D, V = np.linalg.eig(covMat) # 得到特征值和特征向量
k = Percentage2n(D, p) # 确定k值
print("保留" + str(p*100) + "%信息,降维后的特征个数:" + str(k) + "\n")
eigenvalue = np.argsort(D)
K_eigenValue = eigenvalue[-1:-(k + 1):-1]
K_eigenVector = V[:, K_eigenValue]
return K_eigenValue, K_eigenVector
# 得到降维后的数据
def getlowDataMat(DataMat, K_eigenVector):
return DataMat * K_eigenVector
# 重构数据
def Reconstruction(lowDataMat, K_eigenVector, meanVal):
reconDataMat = lowDataMat * K_eigenVector.T + meanVal
return reconDataMat
# PCA算法
def PCA(data, p):
dataMat = np.float32(np.mat(data))
# 数据中心化
dataMat, meanVal = Z_centered(dataMat)
# 计算协方差矩阵
# covMat = Cov(dataMat)
covMat = np.cov(dataMat, rowvar=0)
# 得到最大的k个特征值和特征向量
D, V = EigDV(covMat, p)
# 得到降维后的数据
lowDataMat = getlowDataMat(dataMat, V)
# 重构数据
reconDataMat = Reconstruction(lowDataMat, V, meanVal)
return reconDataMat
def main():
imagePath = 'lenna.jpg'
image = cv.imread(imagePath)
image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
rows, cols = image.shape
print("降维前的特征个数:" + str(cols) + "\n")
print(image)
print('----------------------------------------')
reconImage = PCA(image, 0.99) # 通过改变保留信息的程度来看这个图片的特征值
reconImage = reconImage.astype(np.uint8)
print(reconImage)
cv.imshow('test', reconImage)
cv.waitKey(0)
cv.destroyAllWindows()
if __name__ == '__main__':
main()
保留不同信息量,降维后特征个数如下:
特征个数为52和95的降维后图片如下:
可以发现,降维后保留的特征越多,图片越清晰。
P.S. 在Python的sklearn的库里面集成很多机器学习算法的库,其中也包括主成分分析的方法。
图像处理、机器学习的常用算法汇总