一、介绍
PCA(Principal Component Analysis)是一种常用的数据降维和特征提取方法。它通过线性变换将原始数据转换为一组互相正交的新变量,这些新变量称为主成分,其中第一个主成分尽可能地保留原始数据的信息,而后续的主成分依次保留剩余的信息。这样可以将高维数据降维到较低维度,并且在降维的同时保留尽可能多的原始信息。
PCA的主要思想是通过找到数据中的主要方向或者说主成分,来减少数据的维度。通过计算特征值和特征向量,可以找到数据中的主成分,从而实现数据的降维。
二、原理
2.1PCA算法的原理
- 数据标准化:为了消除不同变量之间的量纲影响,需要对原始数据进行标准化。标准化可以使得不同变量具有相同的尺度,便于后续的主成分分析。常用的标准化方法包括均值中心化和方差放缩。均值中心化将数据的均值移动到原点,即对所有数据减去其均值;方差放缩将每个维度的数据除以其标准差,以确保每个维度的方差为1。
- 计算协方差矩阵:协方差矩阵反映了不同变量之间的相关性,可以通过计算数据的协方差来得到。协方差的计算公式为:cov(X,Y) = E[(X-μX)(Y-μY)],其中X和Y分别表示两个变量,μX和μY分别表示它们的均值。
- 特征值分解:对协方差矩阵进行特征值分解。特征值表示了特征向量对应的方向上的方差大小,特征向量表示了数据的主要方向。特征向量是由单位化的协方差矩阵的特征值对应的特征向量构成的。
- 选择主成分:主成分的选择依据主要是基于特征值的大小来确定。特征值越大,表示数据在该主成分方向上的方差越大,保留该主成分所包含的信息越多。一般来说,选择特征值大于某个阈值的主成分作为保留的主成分。在选择主成分时,可以根据特征值的大小进行排序,选择前k个主成分。
2.2 协方差矩阵
协方差矩阵是描述两个或多个随机变量之间关系的矩阵。在PCA算法中,协方差矩阵起着关键的作用,因为它可以帮助我们理解数据中特征之间的相关性和方差分布。
假设我们有n个特征数据的集合,我们可以将这些特征表示为一个n维向量。协方差矩阵通过计算不同维度之间的协方差来反映数据特征之间的相关性。
协方差矩阵的元素C(i,j)表示第i个特征和第j个特征之间的协方差。对称矩阵的对角线元素表示每个特征的方差,非对角线元素表示不同特征之间的协方差。
协方差矩阵可以通过以下公式来计算:
其中,n是样本数,(X_i)是一个n维样本向量,是均值向量。
计算协方差矩阵:
- 对每个特征算均值,得到一个长度为d的均值向量
。
- 将数据集按组织成一个d×n的矩X,每一列代表一个样本的特征向量。
- 将均值向量重复n次,得到一个d×n的矩阵
。
- 计算中心化矩阵
。
- 计算协方差矩阵。
2.3 特征值和特征向量
在矩阵论中,可以这样去理解特征值和特征向量,一个矩阵由一个变换到另一个矩阵,Aα=λα,其中α称为矩阵A的一个特征向量,λ称为矩阵A的一个特征值。特征向量确定了矩阵变换的方向,特征值确定了矩阵变换的比例。
一个协方差矩阵有着不同的特征值与特征向量,最高特征值的对应的特征向量就是这个数据集的主成分。通常来说,一旦协方差矩阵的特征值和特征向量被计算出来了之后,就是按照特征值的大小从高到低依次排列。特征值的大小确定了主成分的重要性。
主成分分析的基本原理就是:选择特征值较大的作为主成分,从而进行降维。比如:一开始数据集是N维的,在进行了协方差矩阵的特征值计算后,得到了N个特征值和与这些特征值相对应的特征向量。然后在主成分分析时,选取了前P个较大的特征值,如此一来,就将原来N维的数据降维到只有P维。这样就起到了降维的效果了。
2.4 算法流程
(1)对所有的样本进行中心化处理,满足均值为0的分布
(2)计算样本的协方差矩阵XXT
(3)对矩阵XXT进行特征值分解
(4)取出最大的P个特征值对应的特征向量(1, u2.… .up)将所有的特征向量标准化后,组成特征向量矩阵(投影矩阵)W
(5)对样本集中的每一个样本x(i),转化为新的样本z(i)= WTa(i)
(6)得到输出样本集X'=(z(1),z(2),... z(m))。
三、代码实现
3.1 构造数据
import numpy as np
import matplotlib.pyplot as plt
X = np.empty((100, 2))
X[:,0] = np.random.uniform(0., 100., size=100)
X[:,1] = 0.75 * X[:,0] + 3. + np.random.normal(0, 5, size=100)
plt.scatter(X[:,0], X[:,1])
plt.show()
3.2 去除噪音
对制造的数据先降维,后还原,就可以去除噪音了:
from sklearn.decomposition import PCA
pca = PCA(n_components=1)
pca.fit(X)
X_reduction = pca.transform(X)
X_restore = pca.inverse_transform(X_reduction)
plt.scatter(X_restore[:,0], X_restore[:,1])
plt.show()
transform降维成一维数据,再inverse_transform返回成二维数据。此时数据成为了一条直线。这个过程可以理解为将原有的噪音去除了。当然,我们丢失的信息也不全都是噪音。我们可以将PCA的过程定义为:降低了维度,丢失了信息,也去除了一部分噪音。
3.3 手写数字降噪实例
from sklearn import datasets
digits = datasets.load_digits()
X = digits.data
y = digits.target
# 添加一个正态分布的噪音矩阵
noisy_digits = X + np.random.normal(0, 4, size=X.shape)
# 绘制噪音数据:从y==0数字中取10个,进行10次循环;
# 依次从y==num再取出10个,将其与原来的样本拼在一起
example_digits = noisy_digits[y==0,:][:10]
for num in range(1,10):
example_digits = np.vstack([example_digits, noisy_digits[y==num,:][:10]])
example_digits.shape
# 输出:
(100, 64) # 即含有100个数字(0~9各10个),每个数字有64维
def plot_digits(data):
fig, axes = plt.subplots(10, 10, figsize=(10, 10),
subplot_kw={'xticks':[], 'yticks':[]},
gridspec_kw=dict(hspace=0.1, wspace=0.1))
for i, ax in enumerate(axes.flat):
ax.imshow(data[i].reshape(8, 8),
cmap='binary', interpolation='nearest',
clim=(0, 16))
plt.show()
plot_digits(example_digits)
下面将这100个数字绘制出来,得到有噪音的数字:
下面使用PCA进行降噪:
3.4 代码分析
这段代码主要做了以下几件事情:
- 生成模拟数据:
- 使用
numpy
库生成了一个100x2的二维数据集X
,其中第一列是0到100之间的均匀分布随机数,第二列是第一列的0.75倍加上一个常数3和均值为0、标准差为5的正态分布随机噪声。 - 使用
matplotlib.pyplot
绘制了原始数据集的散点图。
- 使用
- 主成分分析(PCA):
- 使用
sklearn.decomposition.PCA
进行了主成分分析,并指定只保留一个主成分。 - 对数据集
X
进行了PCA降维,得到了降维后的数据X_reduction
。 - 使用PCA的
inverse_transform
方法尝试从降维后的数据恢复原始数据(这里应该只是近似恢复),得到了X_restore
。 - 绘制了恢复后的数据集的散点图(注意,由于只保留了一个主成分,恢复后的数据将不再完全反映原始数据的二维结构)。
- 使用
- 加载手写数字数据集并添加噪声:
- 使用
sklearn.datasets.load_digits()
加载了手写数字数据集,包含了1797个8x8像素的手写数字图片。 - 给原始数据集添加了均值为0、标准差为4的正态分布随机噪声,得到了
noisy_digits
。
- 使用
- 选择并绘制特定数字样本:
- 从加噪后的数据集中选择了数字0到9的样本,每种数字选择前10个,并将它们垂直堆叠起来,得到了
example_digits
,这是一个100x64的数组。 - 定义了一个函数
plot_digits
,用于绘制8x8像素的手写数字图片。 - 调用
plot_digits(example_digits)
绘制了100个加噪后的手写数字图片。
- 从加噪后的数据集中选择了数字0到9的样本,每种数字选择前10个,并将它们垂直堆叠起来,得到了
注意事项:
- PCA恢复的数据
X_restore
只是原始数据的一个近似,因为PCA在降维过程中丢失了部分信息。 - 在绘制加噪后的手写数字时,由于数字是8x8像素的,所以使用
reshape(8, 8)
将其转换为了二维数组进行显示。同时,由于像素值的范围是0-16(基于原始数据集),在imshow
函数中设置了clim=(0, 16)
来正确显示图片的亮度。 plot_digits
函数使用了matplotlib.pyplot.subplots
来创建了一个10x10的子图网格,并遍历每个子图来显示对应的数字图片。
四、结论
这里对PCA算法做一个总结。作为一个非监督学习的降维方法,它只需要特征值分解,就可以对数据进行压缩,去噪。因此在实际场景应用很广泛。为了克服PCA的一些缺点,出现了很多PCA的变种,比如为解决非线性降维的KPCA,还有解决内存限制的增量PCA方法Incremental PCA,以及解决稀疏数据降维的PCA方法Sparse PCA等。
4.1 优点:
-
简化数据:通过减少数据集的维数,PCA可以简化数据,使得数据更容易使用。
-
降噪:PCA能够去除数据中的噪音和冗余,使得数据的特征更加突出。
-
可视化:对于二维或三维的数据,PCA可以将其投影到平面上或空间中,便于可视化。
-
优化性能:在机器学习和数据挖掘中,降维后的数据可以加速算法的训练速度,并减少计算资源的使用。
-
保留重要信息:PCA通过保留数据中的主要特征分量,能够最大限度地保留原始数据中的信息。
4.2 缺点:
-
对数据的解释性不强:PCA转换后的新变量(主成分)是原始变量的线性组合,可能难以解释其实际意义。
-
对数据分布的假设:PCA假设数据符合高斯分布,对于非高斯分布的数据,PCA可能无法有效地捕获数据的非线性结构。
-
对参数敏感:PCA的性能受所选主成分数量的影响,需要用户指定要保留的主成分数量,这通常需要通过交叉验证等方法来确定。
-
可能丢失重要信息:虽然PCA能够保留数据中的主要信息,但在降维过程中,一些次要但可能重要的信息可能会被丢失。
-
计算复杂度:对于高维数据,PCA的计算复杂度可能较高,需要较大的计算资源。
-
非线性关系无法处理:PCA是一种线性降维方法,对于数据中的非线性关系可能无法有效地处理。