参考文章:
http://blog.codinglabs.org/articles/pca-tutorial.html
https://zhuanlan.zhihu.com/p/37777074
在学习的过程中发现线性代数的知识非常重要,回顾了一下考研期间看的线性代数的本质:https://www.bilibili.com/video/BV1Ys411k7yQ
选择不同的基可以对同样一组向量给出不同的表示,而且如果基的数量少于向量本身的维数,则可以达到降维的效果。
去均值化(去中心化)
数据中的每一个特征中的数据都减去其平均值。
二维降为一维:投影与方差
以二维降维至一维为例,我们希望在选择这个基后,所有的点在这个基的方向上的投影值尽可能分散。
量化分散的程度:方差,在进行均值化后,由于平均数为 0,计算方差时可以直接使用
Var(a)=m1i=1∑mai2
于是乎,寻找基地的依据就是要尽可能地使方差变大。
三维降为二维:协方差与协方差矩阵
协方差
在选择了一个方向(基)后,在选择另一个方向时,我们不能再单纯地选择方差最大的方向,因为这会和第一个方向几乎重合在一起,我们希望他们之前尽可能地线性无关,可以用协方差来量化相关性,当协方差为 0 时,表示两个字段完全独立。为了让协方差为 0,我们选择第二个基时只能在与第一个基正交的方向上选择。因此最终选择的两个方向一定是正交的:
Cov(a,b)=m1i=1∑m(ai−aˉ)(bi−bˉ)
优化目标:将一组 n 维向量降为 k 维,其目标是选择 k 个单位正交基,使得原始数据变换到这组基上后,各特征两两间协方差为0,而特征内部的方差则尽可能大
由于每个特征均值均为 0,协方差的计算可以直接使用下面的式子,将所有数据的特征 a 的值构成一个列向量 a,等价于计算 a 和 b 的内积然后除以样本的数量:
Cov(a,b)=m1i=1∑maibi
协方差矩阵
协方差矩阵本质上是为了能够方便地计算出各个特征之间的协方差:
X=⎝⎛a1b1...a2b2a3b3......anbn⎠⎞C=m1XXT
设矩阵 X 大小为 m*n,即由 m 个 n 维列向量组成,则 C 是一个对称矩阵,对角线为各个字段的方差,而 C(i, j) 为第 i 和第 j 个特征的协方差
对角化
设原始矩阵对应的协方差矩阵为 C,而 P 是一种变换,X 在变换后得到 Y,即 Y=PX,设 Y 的协方差矩阵为 D,由上述的优化目标可得,我们的 D 要求除了对角元素外的元素均为 0,且对角元素从上往下大小递减,易得
D=m1YYT=m1(PX)(PX)T=m1PXXTPT=P(m1XXT)PT=PCPT
由上可知,D 可视为 C 的一个对角矩阵,优化目标变成了寻找一个正交矩阵 P(P−1=PT) ,满足PCPT是一个对角矩阵,并且对角元素按从大到小依次排列,那么 P 的前 K 行就是要寻找的基,用 P 的前 K 行组成的矩阵乘以 X 就使得 X 从 N 维降到了 K 维并满足上述优化条件。
由于 C 作为协方差矩阵,必然是实对称的,也就必然可以进行对角化,所以可以直接通过计算特征向量来得到 P,而特征值做为对角元素的矩阵即为 Q
Python 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import numpy as np from sklearn.decomposition import PCA
target = 1 x = np.array([[-1, -2], [-1, 0], [0, 0], [2, 1], [0, 1]])
x = x.astype("float64") x_mean = np.mean(x, axis=0) x -= x_mean
x_t = np.transpose(x) cov = np.matmul(x_t, x) / x.shape[0]
eigen, feature = np.linalg.eig(cov)
sorted_index = np.argsort(eigen)[::-1] top_k = np.array([feature[:, sorted_index[0]]]) for i in range(1, target): top_k = np.append(top_k, feature[:, sorted_index[i]]) result_x = np.matmul(x, np.transpose(top_k)) print(result_x)
|