【机器学习】关于PCA
注:PCA是最常用的一种降维方法,降维后得到的每一个主成分是各个特征的线性组合。PCA也被称为“没有截距的回归模型”[1],因为其简单和直观,应用非常广泛。
本文相关的代码和数据:https://github.com/OnlyBelter/jupyter-note/blob/master/machine_learning/PCA/basic.ipynb
做PCA之前的预处理
这里可以问以下几个问题:
- 需不需要对feature进行变换,例如减去均值,或其它标准化处理(例如计算z-score,即减均值除以标准差)?
- 减去均值对结果有什么影响?
- 需不需要对数化处理?
虽然用了很长时间的PCA,但是对一些基本的参数理解的还是不够透彻。将原始数据定义为$X = \mathbb{R}^{m \times n}$,其中$X$表示整个数据集,有$m$个样本(行),每个样本有$n$个特征(列)。
我们可以用以下三种方法得到PCA分析的结果:
- 直接利用 scikit-learn 中的 PCA 函数
- 对$X$做 SVD 分解
- 先计算$X$的协方差矩阵$C$,然后对$C$进行特征分解(Eigendecomposition)
对于第一种方法,直即接使用scikit-learn进行PCA分析,在文档中有以下介绍:
Linear dimensionality reduction using Singular Value Decomposition of the data to project it to a lower dimensional space. The input data is centered but not scaled for each feature before applying the SVD.
可以看出来,在scikit-learn中对原始数据的每个特征减去了对应的均值(centered),但是并没有做其它处理(not scaled)。此时相当于计算协方差矩阵,然后再做奇异值分解(SVD)。下面的特征分解只能用于方形矩阵,SVD可以用于任意形状的矩阵。
对于第二种方法,其实也是scikit-learn中封装的方法,如果我们自己直接对$X$进行 SVD 分解,则需要先对$X$中的每一个特征减去其对应的均值。如何利用 SVD 的结果计算 PCA 的结果可以参考:https://stats.stackexchange.com/a/134283/134555
在第三种方法中,做不做对特征的中心化结果都一样,因为是否中心化不影响协方差矩阵的值。另外,如果我们对原始数据的特征做了“中心化 + 缩放到单位方差”的操作,即将原始数据按特征标准化到z-score,那么此时关于$X$特征的协方差矩阵(COV)和聚类系数矩阵(COR)的特征分解得到的结果中,特征向量是相等的(特征值部分可能不相等)。此时分解后得到的特征向量,相当于是每个特征与对应主成分之间的相关系数。
对于COV和COR的比较可以参考:https://stats.stackexchange.com/a/78/134555
这里有一句话,可以帮助我们判断如何选择COV和COR:
You tend to use the covariance matrix when the variable scales are similar and the correlation matrix when variables are on different scales.
在Python中,如果使用scikit-learn,默认是计算COV,如果想先计算相关系数矩阵(COR),可以先对$X$中的每一列进行标准化(z-score),代码如下:
from sklearn.preprocessing import StandardScaler x_std = StandardScaler().fit_transform(x)
参考:https://github.com/scikit-learn/scikit-learn/issues/2689
有时候在做PCA分析之前,可以对数据进行对数化(取log),目前看到了两个原因,可参考:https://stats.stackexchange.com/a/164389/134555
- 数据基本上服从正态分布(not strongly skewed)
- 避免一些极大值对结果的影响
每个特征对各主成分的贡献
利用PCA分析,可以进行特征筛选。如果知道每个特征对每个主成分(PCs)的贡献,也能帮助我们认识每个特征是如何被分解到线性不相关的主成分坐标系中的,以及不同特征的相对重要性。
Jake VanderPlas写了一本开源数据科学相关的书:《Python Data Science Handbook》,里面有一个例子:
%matplotlib inline import numpy as np import matplotlib.pyplot as plt import seaborn as sns; sns.set() rng = np.random.RandomState(1) X = np.dot(rng.rand(2, 2), rng.randn(2, 200)).T plt.scatter(X[:, 0], X[:, 1]) plt.axis('equal'); from sklearn.decomposition import PCA pca = PCA(n_components=2) pca.fit(X) print(pca.components_) # [[ 0.94446029 0.32862557] # [ 0.32862557 -0.94446029]] print(pca.explained_variance_) # [ 0.75871884 0.01838551]
其中,pca.components_ 的形状为:[n_components, n_features],因此从每一行中数值的相对大小也可以看出对对应主成分的贡献(与上述协方差矩阵或相关系数矩阵分解后得到的特征向量相同)。例如第一行就表示两个特征对PC1的贡献(如果用z-score或相关系数矩阵,这里就表示各个特征与PC1之间的相关性;否则表示协方差),从数值的相对大小来看,第一个特征对PC1的贡献远远大于第二个特征。此外,该数组的每一列也可以表示对应特征投影到主成分坐标轴中的方向,例如,第一列只取前两行(这里n_components_等于2,因此只有两行),就可以得到第一个特征投影到PC1和PC2平面中的方向。
pca.explained_variance_ 的长度与 n_components 相同,表示对应主成分方向的方差(该值的平方根表示标准差,对应协方差矩阵分解后得到的特征值)。例如,第一个值表示所有数据点(包含多个特征)投影到PC1方向上的方差。
可以检验:
Y = (X - X.mean(axis=0)) @ pca.components_.T
其中先对$X$中每个特征减去均值(按列),$Y$表示对X进行PCA转换之后的值,与 pca.transform(X) 相等。每一个主成分,相当于是原始数据$X$中所有特征的线性组合,这里 pca.components_ 中的每一行就相当于该线性组合的系数。
此外有一个专门的名词,来衡量每个特征对各主成分的贡献,component loading (L),可以用以下方式计算:$L = V \sqrt{E}$,其中$V$表示协方差矩阵分解后得到的特征向量,$E$表示特征值。$V$保存在 pca.components_.T,$E$保存在 pca.explained_varance_
可参考下面两篇文章:
https://towardsdatascience.com/pca-clearly-explained-how-when-why-to-use-it-and-feature-importance-a-guide-in-python-7c274582c37e
https://scentellegher.github.io/machine-learning/2020/01/27/pca-loadings-sklearn.html
其它
对于稀疏的数据(例如单细胞数据),可以使用 truncated SVD (aka LSA),此时不需要减去每个feature的均值,文档中的描述如下:
This transformer performs linear dimensionality reduction by means of truncated singular value decomposition (SVD). Contrary to PCA, this estimator does not center the data before computing the singular value decomposition. This means it can work with sparse matrices efficiently.
利用numpy和scipy进行PCA分析:
https://stats.stackexchange.com/a/235931/134555
https://machinelearningmastery.com/calculate-principal-component-analysis-scratch-python/
与loading和score相关:
http://www.statistics4u.info/fundstat_eng/cc_pca_loadscore.html
https://stats.stackexchange.com/a/143949/134555
https://stats.stackexchange.com/a/119758/134555
Reference
[1] ttnphns (https://stats.stackexchange.com/users/3277/ttnphns), How does centering the data get rid of the intercept in regression and PCA?, URL (version: 2016-09-16): https://stats.stackexchange.com/q/22331
其它链接见文中对应位置