参考文章:
【20250527发现已失效】http://blog.codinglabs.org/articles/pca-tutorial.html
https://zhuanlan.zhihu.com/p/37777074
在学习的过程中发现线性代数的知识非常重要,回顾了一下考研期间看的线性代数的本质:https://www.bilibili.com/video/BV1Ys411k7yQ
选择不同的基可以对同样一组向量给出不同的表示,而且如果基的数量少于向量本身的维数,则可以达到降维的效果。
去均值化(去中心化)
数据中的每一个特征中的数据都减去其平均值。
二维降为一维:投影与方差
以二维降维至一维为例,我们希望在选择这个基后,所有的点在这个基的方向上的投影值尽可能分散,因为如果过于紧凑的话说明特征在该维度上的不同值没有差别,从而丢失了该特征所记录的信息。
量化分散的程度:方差,在进行均值化后,由于平均数为 0,计算方差时可以直接使用
于是乎,寻找基地的依据就是要尽可能地使方差变大。
三维降为二维:协方差与协方差矩阵
协方差 Covariance
在选择了一个方向(基)后,在选择另一个方向时,我们不能再单纯地选择方差最大的方向,因为这会和第一个方向几乎重合在一起,我们希望他们之前尽可能地线性无关,可以用协方差来量化 相关性,当协方差为 0 时,表示两个字段完全独立。为了让两个基向量完全线性无关,我们选择第二个基时只能在与第一个基正交的方向上选择。最终选择的两个方向一定是正交的:
对于去中心化的向量,由于每个特征均值均为 0,协方差的计算可以直接使用下面的式子,将所有数据的特征 的值构成一个列向量 ,等价于计算 和 的内积然后除以样本的数量:
Note
内积的几何本质: 在 方向上的投影长度乘以 的长度。如果 是一个单位向量,则表示 在 上的投影长度;
当 和 的协方差为 0 的时候,则说明 在 上的投影长度为 0 或 的长度为 0,表明 和 正交,故他们线性无关;
协方差矩阵
协方差矩阵是为了方便计算出各个特征之间的协方差,对于已经去中心化的特征矩阵 :
的特征矩阵
设矩阵 大小为 ,即由 个 维列向量组成。 计算出来是一个对称矩阵,对角线为各个字段的方差, 为第 和第 个特征的协方差
🔥 维降到 维:PCA 的目标
将一组 维向量降为 维,其目标是找到 个单位正交基,使得原始数据变换到这组基上后:
- 各特征两两间协方差为 :保证每个各个特征完全线性无关,在最少的维度中存储最多的信息。
- 特征内部的方差则尽可能大:保证特征的不同值在这些基上仍有显著差别。
对角化
设:
- 原始矩阵对应的协方差矩阵为
- 是一种变换, 在降维变换后得到 ,即
- 的协方差矩阵为
可以推导出 与 的关系:
计算去中心化后的矩阵的协方差代入是经过去中心化后的矩阵的协方差矩阵由 PCA 的优化目标可得,降维后的矩阵的协方差矩阵 要求除了对角元素外的元素均为 :
- 各特征的方差尽可能选择尽可能大的,故让对角元素从上往下大小递减
- 降维后不同特征之间协方差为 0,故只有对角线元素不为 0
故 是对角矩阵。
由上可知, 可视为 对角化后的一个对角矩阵,优化目标变成了寻找一个正交矩阵 ,满足是一个对角矩阵,并且对角元素按从大到小依次排列,那么 的前 行就是要寻找的基,用 的前 行组成的矩阵乘以 就使得 从 维降到了 维并满足 PCA 的目标。
由于 作为协方差矩阵,必然是实对称的,也就必然可以用正交矩阵进行对角化,所以:
- 可以直接通过计算特征向量来得到变换 ( 的特征向量作为 的每一列)
- 的结果就是响应的特征值作为对角元素的对角矩阵就是符合PCA目标的
有了 之后我们就可以用 对 进行降维
Note
矩阵的对角化
在处理对称矩阵(如协方差矩阵)时,我们常将其对角化。
对 对角化的时候,就是找到其所有的特征向量作为变化 (要求所有特征向量互相线性无关),然后其对应的特征值摆在对角线上就是其对角化后的矩阵
其中 是一个对角矩阵, 是可逆矩阵。对角线元素是 的特征值的矩阵可以作为 。
📌 常见要求:将特征值按从大到小排序,依次排列在对角线:,方便选取“最重要”的方向;
实对称矩阵的特征向量可以构成一个正交矩阵 ,满足:
其中:
- :由单位正交的特征向量组成,是正交矩阵
- :对角矩阵,对角线上是特征值
Python 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 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)
|