sklearn基于SVD的PCA与手动特征值分解PCA的差异探究
PCA的SVD实现与特征值分解实现差异解析
手动实现PCA时,会发现sklearn的PCA采用奇异值分解(SVD)实现,而多数教程则使用特征值分解实现,二者得到的特征向量看似存在差异。
sklearn的PCA核心实现代码
self.mean_ = np.mean(X, axis=0) X -= self.mean_ U, S, Vt = scipy.linalg.svd(X, full_matrices=False) # 翻转特征向量符号以保证输出确定性 U, Vt = svd_flip(U, Vt) eig_vec = Vt
修正后的手动PCA实现代码(原代码存在错误)
原手动代码中错误地对中心化后的X直接做特征分解,正确做法是对协方差矩阵操作:
self.mean_ = np.mean(X, axis=0) X -= self.mean_ covX = np.cov(X.T) # 注意:np.cov默认按行计算,特征是列的话需要转置 eig_val, eig_vec = np.linalg.eig(covX) # 需手动按特征值从大到小排序特征向量 sorted_idx = np.argsort(eig_val)[::-1] eig_val = eig_val[sorted_idx] eig_vec = eig_vec[:, sorted_idx]
为什么sklearn选择SVD实现PCA?
- 数值稳定性更强:当样本量远大于特征数,或者数据矩阵接近奇异时,直接计算协方差矩阵再做特征分解容易引入数值误差,而SVD对这类场景的鲁棒性更好。
- 计算效率更高:SVD可以直接对中心化后的原始数据矩阵计算,无需先计算协方差矩阵,当样本量N远大于特征数D时,SVD的计算步骤更高效。
- 输出确定性:SVD得到的奇异向量符号是不唯一的,sklearn通过
svd_flip强制统一符号,保证每次运行输出一致;而特征分解的特征向量符号同样可能存在不确定性,需要额外处理。
二者得到的主成分差异
修正手动实现的错误后,两种方法得到的主成分本质是等价的,仅存在以下细节差异:
- 符号差异:SVD得到的
Vt(主成分)与特征分解得到的特征向量可能符号相反,这是因为奇异向量和特征向量的符号都可以翻转,不影响方差解释能力。sklearn的svd_flip就是为了消除这种符号不确定性。 - 排序差异:sklearn的SVD输出的
Vt是按奇异值从大到小排序的,对应主成分的方差依次降低;而特征分解的np.linalg.eig不会自动排序,需要手动对特征值和特征向量按降序排列才能得到正确的主成分顺序。 - 数值精度差异:SVD的数值计算稳定性优于特征值分解,尤其是当协方差矩阵的特征值非常接近时,SVD得到的主成分精度更高。
特征值分解描述的是矩阵A对那些不被旋转的向量的缩放作用;而奇异值分解描述的是矩阵A在左右空间之间,缩放比例相同的对应向量的变换关系。当矩阵A是方阵时,奇异值分解描述的是A和Aᵀ对同一向量的缩放比例相同的情况下,向量的旋转和反射变换。
内容的提问来源于stack exchange,提问作者Wassim Jaoui
相关产品推荐
相关产品推荐

