主成分分析(Principal Component Analysis)是一种常用的降维算法,可通过线性组合的方法将多个特征综合为少数特征,且综合后的特征相互独立,又可以表示原始特征的大部分信息。
观察以下的几个样本的特征向量X:
x1 x2 x3 x4 y
0 10 -2 2
0 10 -3 3
0 10 -4 4
可以看出上述样本有四维特征,假设对这几个样本进行回归分析,可以得到 y = w1x1 + w2x2 + w3x3 + w4x4 + d , 其中 wi 为各个特征的系数,d为常数。
可以发现,因为 x1、x2 两个特征在样本中的特征值没有变化,因此不管这两个特征的系数w1、w2是多少,w1x1和w2x2这两项可以用常数d的改变来代替;对于 x3、x4 这两个特征,它们可以合并为同一个特征。因此上述样本的四维特征可以用一个维度来代替:
z1 y
2
3
4
因此,特征z1可以看作样本的一个主成分。而PCA就是对样本的主成分进行分析来到达降维的效果。通常来说,主成分可显示出样本在某个特征上的最大差异,这种差异的大小可以使用方差来表示。
如下图所示:四个样本点在原始特征x1、x2上进行分布,可将这些样本点投影映射到 z1 上的维度。在 z1 维度上,样本间表现出了较大的差异性,而若在 z2 维度上投影,样本间表现的差异性在这个维度上会少很多。
将能让样本点方差最大的直线方向称为主方向 。
为了使样本点在 主 方向上分散得最开,即在 主 方向上方差最大,一般需要对样本进行中心化,即样本点的每一个维度上的值减去所有样本点在这个维度上的值的总和的平均值。
由上可知,我们需要寻找样本点的主方向 ,即将m个样本值投影到主方向上,得到m个位于主方向上的点,计算m个投影后样本点的方差。
由图可知投影的矢量长度 ,而 向量
和 向量
的内积为
。
又因为我们只需要知道 主方向 的方向,所以可以令
为单位向量,即
。
因此计算投影 d 可以转换为 样本向量 与主方向
的内积。
首先对原始样本点进行去均值化:计算m个样本的均值,将样本真实值减去均值。即对原始样本点进行预处理 使得 变换后的样本集的中心点为零点:
,其中 m 为样本的个数。
对于含有 n 个特征维度的 m 个样本,将每个样本向量写成行向量,得到矩阵 A 如下:(以下默认向量表现为 列向量)
取 样本点 被投影的延伸方向 ,计算 矩阵 A 与
内积 如下:
其中 为 第 i 个(去均值化的)样本点 投影后的矢量长度 d(有正负),即可将
看作 样本点 在
这个特征维度上的特征值。即可将
可看作 样本集 在
这个特征维度上的特征值向量。
又由上可知: 为 单位向量,其模为 1 。
为了使得 样本集在 这个特征维度上表现出了较大的差异性,因此需要令样本间的方差最大:即令
最大。其中
为:
其中 E 为 的均值,又因为 原始样本 已去均值化,即 矩阵A每一列的均值 为 0,可有
,因此
。所以可推导出样本间的方差如下:
。
由上可得,我们需要最大化的目标函数为: ,且又有 约束条件 为
,即
。
带有约束条件的极值求解可使用 拉格朗日乘子法 来解决:由 拉格朗日乘子法 可建立 拉格朗日方程 为:
。令 拉格朗日方程 对
的偏导 为 0 :
因此 可看作 矩阵
的一个特征向量,其对应的特征值 为
。其中
是一个 实对阵矩阵,它的不同特征值对应的特征向量一定是正交的。
因为 目标函数 为 ,且又有
,因此可有:
。 因此矩阵
的特征值
即为我们所要求解的 样本点 投影在其对应特征向量
后的 方差。
由上可知,我们可以通过求解 矩阵 的最大特征值,来得到 能让样本点方差最大的投影方向(即主方向)。
一般情况下,会选择矩阵 的若干个最大的特征值对应的特征向量分别对样本进行投影,即将样本点在多个特征维度上进行投影。又因为矩阵
为 实对阵矩阵,其不同特征值对应的特征向量一定是正交的,因此投影后的多个特征是相互独立的。
假设上述矩阵 A (已去均值化)是一个 mxn 的矩阵,即有样本个数m 个,样本的特征个数 n 个。
现在我们需要把样本的特征个数降维到 k 个,因此需要计算矩阵 (nxn的矩阵) 的前k个最大的特征值对应的特征向量(列向量)。
再将这前k个特征值所对应的特征向量,构成一个n*k的矩阵,这个矩阵就是我们要求的可让样本方差最大的样本投影的矩阵。
mxn的样本矩阵A 乘以 这个nxk的样本投影矩阵,就得到了一个m*k的矩阵,这矩阵即降维之后的样本矩阵,样本特征个数已变为了k个,每一列表示每个样本在该列维度上的特征值。(将样本往 k 个投影方向上投影)。
以上即完成了PCA的推导。
主要使用 sklearn 的主成分分析PCA进行降维分析:
from sklearn.decomposition import PCA
import numpy as np
data = np.array([[0,10,2],[0,10,4],[0,10,6]])
print data # 降维前的样本
[[ 0 10 2]
[ 0 10 4]
[ 0 10 6]]
pca = PCA(n_components=1,copy=True,random_state=8)
newdata = pca.fit_transform(data)
print newdata # 降维后的样本
[[ 2.] # 已去均值化
[ 0.]
[-2.]]
print pca.components_ # 投影方向向量
[[-0. 0. -1.]]
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
iris = load_iris()
X = iris.data
y = iris.target
print X.shape
(150L, 4L)
iris_pca = PCA(n_components=2,copy=False,random_state=8)
X = iris_pca.fit_transform(X)
print X.shape
(150L, 2L)
plt.scatter(X[:,0],X[:,1],c=y)
plt.show()
print iris_pca.components_ # 两个投影方向上的向量
[[ 0.36158968 -0.08226889 0.85657211 0.35884393]
[ 0.65653988 0.72971237 -0.1757674 -0.07470647]]
from sklearn.datasets import load_digits
digits = load_digits()
digit_data = digits.data
sub_data = digit_data[0:100,:]
print sub_data.shape
(100L, 64L)
fig, axe = plt.subplots(1,12,subplot_kw=dict(xticks=[], yticks=[]))
for i in range(0,12):
axe[i].imshow(sub_data[i,:].reshape((8,8)),cmap=plt.cm.binary, interpolation='nearest')
plt.show()
digit_pca = PCA(n_components=36,copy=True,whiten=False)
new_data = digit_pca.fit_transform(sub_data);
print new_data.shape
(100L, 36L)
fig, axe = plt.subplots(1,12,subplot_kw=dict(xticks=[], yticks=[]))
for i in range(0,12):
axe[i].imshow(new_data[i,:].reshape((6,6)),cmap=plt.cm.binary, interpolation='nearest')
plt.show()