从零实现PCA:幂法求解特征值仅首个正确的问题排查求助
问题分析与修复方案
你的核心问题出在两个关键环节:幂法的向量归一化方式错误,以及矩阵消去逻辑依赖错误的特征向量,导致后续特征值计算完全偏离。
错误点1:幂法的归一化选择错误
你用max(v)作为归一化系数,这是错误的。幂法正确的做法是用向量的**L2范数(欧几里得范数)**来归一化,因为我们需要的是单位向量,而不是最大值为1的向量。用最大值归一化会破坏幂法收敛到主特征向量的逻辑,后续消去矩阵时误差会被指数级放大。
错误点2:矩阵消去(Deflation)的前提不成立
当你用错误的特征向量去更新协方差矩阵时,消去后的矩阵已经不再是原矩阵减去主特征子空间的投影,后续幂法计算的自然不是次大的特征值。
修正后的完整代码
import numpy as np import pandas as pd from sklearn.preprocessing import StandardScaler def normalize_data(train_x, test_x=None): scaler = StandardScaler() train_normalized = pd.DataFrame(scaler.fit_transform(train_x), columns=train_x.columns) test_normalized = pd.DataFrame(scaler.transform(test_x), columns=test_x.columns) if test_x is not None else None return train_normalized, test_normalized def extract_PCA(X, n_components=3): n = X.shape[0] cov_matrix = (X.T @ X) / (n-1) eigen_vectors = [] eigen_values = [] for _ in range(n_components): # 计算当前矩阵的主特征值和特征向量 eigen_value, eigen_vector = using_power_method_find_dominant_eigen_value(cov_matrix) eigen_values.append(eigen_value) eigen_vectors.append(eigen_vector) # 矩阵消去:减去当前特征值对应的投影矩阵 eigen_vector = eigen_vector.reshape(-1, 1) cov_matrix = cov_matrix - eigen_value * eigen_vector @ eigen_vector.T print("计算得到的特征值:", eigen_values) return eigen_values, eigen_vectors def using_power_method_find_dominant_eigen_value(mat_A, tolerance=1e-10, max_iter=1000): n = mat_A.shape[0] # 初始化随机向量(比全1向量更稳定) x = np.random.rand(n) x = x / np.linalg.norm(x) # 初始化为单位向量 prev_value = 0 prev_eigen_vector = x.copy() for _ in range(max_iter): x = mat_A @ x norm = np.linalg.norm(x) eigen_value = norm x = x / norm # L2范数归一化 # 检查收敛条件 if abs(eigen_value - prev_value) < tolerance and np.linalg.norm(prev_eigen_vector - x) < tolerance: break prev_value = eigen_value prev_eigen_vector = x.copy() return eigen_value, x if __name__ == "__main__": train_df = pd.read_csv("train.csv") test_df = pd.read_csv("test.csv") X_train, y_train = train_df.drop(columns=['y'], axis=1), train_df['y'] X_test, y_test = test_df.drop(columns=['y'], axis=1), test_df['y'] X_train, X_test = normalize_data(X_train, X_test) extract_PCA(X_train)
关键修正说明
- 替换原
vector_norm函数的逻辑,直接用np.linalg.norm做L2归一化,保证特征向量是单位向量。 - 初始化向量改为随机向量,避免全1向量在某些特殊矩阵下收敛缓慢。
- 简化幂法的迭代逻辑,用numpy矩阵运算替代列表推导,提升效率和准确性。
- 确保消去矩阵时使用的是单位特征向量,保证投影矩阵的正确性。
运行修正后的代码,计算得到的特征值会和numpy的结果一致:[9.49506917, 8.11919641, 5.27687829]。
内容的提问来源于stack exchange,提问作者Sidhant
相关产品推荐
相关产品推荐

