协方差微小变化致特征向量符号翻转的原因及解决方法
问题背景
两个协方差矩阵仅右上和左下元素存在差异,但计算得到的主成分特征向量(对应最大特征值的右列)符号发生了翻转。以下是复现代码及运行结果:
复现代码(Python)
import numpy as np cov = np.array([[9369, 7060, 16469], [7060, 8127, 23034], [16469, 23034, 126402]]) eigenvalues, eigenvectors = np.linalg.eigh(cov) print('Cov 1') print(eigenvalues) print(eigenvectors) cov2 = np.array([[9369, 7060, 18969], [7060, 8127, 23034], [18969, 23034, 126402]]) eigenvalues, eigenvectors = np.linalg.eigh(cov2) print('Cov 2') print(eigenvalues) print(eigenvectors)
运行结果
Cov 1 [ 1188.72951707 9507.3058357 133201.96464723] [[-0.5549483 0.82002403 0.13997491] [ 0.8280924 0.52849364 0.18696912] [-0.07934332 -0.21967035 0.97234231]] Cov 2 [ 1390.92828009 8581.02234156 133926.04937835] [[-0.57175381 0.80502124 -0.15823521] [ 0.817929 0.54427789 -0.18642352] [-0.06395096 -0.23601353 -0.96964318]]
解决方案
Python 符号统一方法
特征向量的符号不确定性是数值计算的固有特性(若v是特征向量,则-v也是对应同一特征值的特征向量),可通过以下两种方式修正:
方法1:基于参考向量的点积符号对齐
选择一个固定参考向量(比如第一个矩阵的特征向量,或全1向量),计算每个特征向量与参考向量的点积,若点积为负则翻转该特征向量的符号:
import numpy as np def align_eigenvectors(eigenvectors, ref_vectors): aligned = eigenvectors.copy() for i in range(eigenvectors.shape[1]): dot = np.dot(eigenvectors[:, i], ref_vectors[:, i]) if dot < 0: aligned[:, i] = -aligned[:, i] return aligned # 计算第一个矩阵的特征向量作为参考 cov = np.array([[9369, 7060, 16469], [7060, 8127, 23034], [16469, 23034, 126402]]) vals1, vecs1 = np.linalg.eigh(cov) # 计算第二个矩阵的特征向量并对齐 cov2 = np.array([[9369, 7060, 18969], [7060, 8127, 23034], [18969, 23034, 126402]]) vals2, vecs2 = np.linalg.eigh(cov2) aligned_vecs2 = align_eigenvectors(vecs2, vecs1) print("对齐后的Cov2特征向量:") print(aligned_vecs2)
方法2:基于特征向量最大元素的符号对齐
对每个特征向量,找到绝对值最大的元素,若该元素为负则翻转整个向量的符号,确保最大元素始终为正:
def align_eigenvectors_by_max(eigenvectors): aligned = eigenvectors.copy() for i in range(eigenvectors.shape[1]): max_idx = np.argmax(np.abs(eigenvectors[:, i])) if eigenvectors[max_idx, i] < 0: aligned[:, i] = -aligned[:, i] return aligned # 应用到第二个矩阵的特征向量 aligned_vecs2 = align_eigenvectors_by_max(vecs2) print("对齐后的Cov2特征向量:") print(aligned_vecs2)
C# 与 MathNet.Numerics 解决方案
MathNet.Numerics的EVD类同样会出现特征向量符号不确定的问题,可采用类似的思路统一符号:
方法:基于最大元素的符号对齐示例
using MathNet.Numerics.LinearAlgebra; using MathNet.Numerics.LinearAlgebra.Double; public static Matrix<double> AlignEigenvectors(Matrix<double> eigenvectors) { var aligned = eigenvectors.Clone(); for (int col = 0; col < eigenvectors.ColumnCount; col++) { var column = eigenvectors.Column(col); int maxIdx = column.AbsoluteMaximumIndex(); if (column[maxIdx] < 0) { aligned.SetColumn(col, -column); } } return aligned; } // 使用示例 var cov = DenseMatrix.OfArray(new double[,] { {9369, 7060, 16469}, {7060, 8127, 23034}, {16469, 23034, 126402} }); var evd1 = cov.Evd(); var vecs1 = evd1.EigenVectors; var cov2 = DenseMatrix.OfArray(new double[,] { {9369, 7060, 18969}, {7060, 8127, 23034}, {18969, 23034, 126402} }); var evd2 = cov2.Evd(); var alignedVecs2 = AlignEigenvectors(evd2.EigenVectors); // 输出对齐后的特征向量 Console.WriteLine("对齐后的Cov2特征向量:"); Console.WriteLine(alignedVecs2.ToString());
注:若需要与参考矩阵的特征向量对齐,可计算对应列的点积,根据点积符号翻转向量,逻辑与Python版本一致。
内容的提问来源于stack exchange,提问作者ManInMoon
相关产品推荐
相关产品推荐

