ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

PCA:为什么降维能「去噪」?从特征值分解到核 PCA

PCA:为什么降维能「去噪」?从特征值分解到核 PCA 从特征值分解到 SVD理解数据压缩的数学本质开头高维数据的「诅咒」上一篇我们学习了正则化防止过拟合。今天我们学习主分量分析PCA——最经典的降维算法。你有没有遇到过这种情况数据有 1000 个特征 → 计算量大 → 存储空间大 → 可视化困难 → 噪声多 PCA 的思想 找到数据变化最大的方向主成分 只保留最重要的几个方向 → 降维 去噪 可视化一、PCA 的直觉1.1 问题定义PCA 要解决的问题 ═══════════════════════════════════════════════════════════════════ 输入高维数据 X ∈ R^{n×d} n 个样本d 个特征 输出低维数据 Y ∈ R^{n×k} n 个样本k 个特征k d 目标 找到数据变化最大的 k 个方向 保留最多的信息1.2 几何直觉PCA 的几何直觉 ═══════════════════════════════════════════════════════════════════ 原始数据2D x₂ ↑ │ × × × │ × × × │ × × × │ × × × └──────────→ x₁ 数据主要沿着一个方向变化对角线方向 PCA 找到这个方向 x₂ ↑ │ × × × │ × × × ←── 主成分方向方差最大 │ × × × │ × × × └──────────→ x₁ 降维后1D 只保留主成分方向上的坐标 → 从 2D 降到 1D二、数学推导2.1 协方差矩阵协方差矩阵 ═══════════════════════════════════════════════════════════════════ 数据中心化 X_centered X - mean(X) 协方差矩阵 C (1/n) X_centeredᵀ X_centered C 是 d×d 的对称矩阵 C[i,j] 第 i 个特征和第 j 个特征的协方差 C[i,i] 第 i 个特征的方差2.2 特征值分解特征值分解 ═══════════════════════════════════════════════════════════════════ 对协方差矩阵 C 做特征值分解 C Q Λ Qᵀ 其中 Q [q₁, q₂, ..., q_d] 特征向量矩阵 Λ diag(λ₁, λ₂, ..., λ_d) 特征值对角矩阵 特征值的含义 λᵢ 第 i 个主成分方向上的方差 λ₁ ≥ λ₂ ≥ ... ≥ λ_d 按降序排列 特征向量的含义 qᵢ 第 i 个主成分方向2.3 降维PCA 降维 ═══════════════════════════════════════════════════════════════════ 选择前 k 个主成分 Q_k [q₁, q₂, ..., q_k] 降维 Y X_centered Q_k 信息保留率 ratio (λ₁ λ₂ ... λ_k) / (λ₁ λ₂ ... λ_d) 通常选择 k 使得 ratio 95%保留 95% 以上信息三、Python 实现 PCA3.1 从零实现importnumpyasnpimportmatplotlib.pyplotaspltclassPCA:主分量分析PCAdef__init__(self,n_components2): 参数: n_components: 保留的主成分数量 self.n_componentsn_components self.componentsNoneself.meanNoneself.eigenvaluesNonedeffit_transform(self,X): 拟合并转换数据 参数: X: 输入数据形状 (n_samples, n_features) 返回: X_reduced: 降维后的数据形状 (n_samples, n_components) n_samples,n_featuresX.shape# 步骤 1中心化self.meannp.mean(X,axis0)X_centeredX-self.mean# 步骤 2计算协方差矩阵Cnp.cov(X_centered.T)# 步骤 3特征值分解eigenvalues,eigenvectorsnp.linalg.eigh(C)# 步骤 4按特征值降序排列idxnp.argsort(eigenvalues)[::-1]eigenvalueseigenvalues[idx]eigenvectorseigenvectors[:,idx]# 步骤 5选择前 k 个主成分self.componentseigenvectors[:,:self.n_components]self.eigenvalueseigenvalues# 步骤 6降维X_reducedX_centered self.componentsreturnX_reduceddefexplained_variance_ratio(self):计算方差解释率returnself.eigenvalues[:self.n_components]/np.sum(self.eigenvalues)definverse_transform(self,X_reduced):逆变换重构returnX_reduced self.components.Tself.mean3.2 使用 SVD 实现更稳定classPCA_SVD:使用 SVD 实现的 PCA更稳定def__init__(self,n_components2):self.n_componentsn_components self.componentsNoneself.meanNoneself.singular_valuesNonedeffit_transform(self,X):n_samples,n_featuresX.shape# 步骤 1中心化self.meannp.mean(X,axis0)X_centeredX-self.mean# 步骤 2SVD 分解U,S,Vtnp.linalg.svd(X_centered,full_matricesFalse)# 步骤 3选择前 k 个主成分self.componentsVt[:self.n_components].T self.singular_valuesS# 步骤 4降维X_reducedU[:,:self.n_components]*S[:self.n_components]returnX_reduceddefexplained_variance_ratio(self):方差解释率return(self.singular_values[:self.n_components]**2)/np.sum(self.singular_values**2)3.3 测试二维数据降维# 生成二维数据np.random.seed(42)n_samples200# 生成有相关性的数据mean[0,0]cov[[1,0.8],[0.8,1]]# 高相关性Xnp.random.multivariate_normal(mean,cov,n_samples)# PCA 降维pcaPCA(n_components1)X_reducedpca.fit_transform(X)print(f原始数据形状:{X.shape})print(f降维后形状:{X_reduced.shape})print(f方差解释率:{pca.explained_variance_ratio():.2%})# 可视化fig,(ax1,ax2)plt.subplots(1,2,figsize(12,4))ax1.scatter(X[:,0],X[:,1],alpha0.5)ax1.set_title(Original Data (2D))ax1.set_xlabel(x₁)ax1.set_ylabel(x₂)ax1.grid(True)ax2.scatter(X_reduced,np.zeros_like(X_reduced),alpha0.5)ax2.set_title(Reduced Data (1D))ax2.set_xlabel(Principal Component 1)ax2.grid(True)plt.tight_layout()plt.show()3.4 测试高维数据可视化fromsklearn.datasetsimportload_irisfromsklearn.preprocessingimportStandardScaler# 加载鸢尾花数据4 维irisload_iris()Xiris.data yiris.target# 标准化scalerStandardScaler()X_scaledscaler.fit_transform(X)# PCA 降到 2 维pcaPCA(n_components2)X_2dpca.fit_transform(X_scaled)print(f原始数据:{X.shape}(4 维))print(f降维后:{X_2d.shape}(2 维))print(f方差解释率:{pca.explained_variance_ratio()})# 可视化plt.figure(figsize(10,6))foriinrange(3):maskyi plt.scatter(X_2d[mask,0],X_2d[mask,1],labeliris.target_names[i],alpha0.7)plt.xlabel(fPC1 ({pca.explained_variance_ratio()[0]:.1%}))plt.ylabel(fPC2 ({pca.explained_variance_ratio()[1]:.1%}))plt.title(Iris Dataset - PCA Visualization)plt.legend()plt.grid(True)plt.show()四、奇异值分解SVD4.1 SVD 的定义奇异值分解SVD ═══════════════════════════════════════════════════════════════════ 对于任意矩阵 X ∈ R^{m×n} X U Σ Vᵀ 其中 U ∈ R^{m×m}左奇异向量正交矩阵 Σ ∈ R^{m×n}奇异值对角矩阵 V ∈ R^{n×n}右奇异向量正交矩阵 奇异值 σ₁ ≥ σ₂ ≥ ... ≥ σ_r 0 r rank(X)4.2 SVD 与 PCA 的关系SVD 与 PCA 的关系 ═══════════════════════════════════════════════════════════════════ 对中心化数据 X_centered 做 SVD X_centered U Σ Vᵀ 协方差矩阵 C (1/n) X_centeredᵀ X_centered (1/n) V Σᵀ Uᵀ U Σ Vᵀ (1/n) V Σ² Vᵀ 所以 V 的列就是 C 的特征向量主成分方向 Σ²/n 就是 C 的特征值方差 结论 PCA 可以用 SVD 实现而且更稳定4.3 为什么用 SVD 而不是特征值分解SVD vs 特征值分解 ═══════════════════════════════════════════════════════════════════ 特征值分解 需要计算协方差矩阵 C XᵀX / n 然后对 C 做特征值分解 SVD 直接对 X 做分解 不需要显式计算 C 优点 - 数值更稳定 - 避免计算 XᵀX可能有精度损失 - 对大规模数据更高效五、核 PCA5.1 问题PCA 只能处理线性数据PCA 的局限性 ═══════════════════════════════════════════════════════════════════ 原始数据非线性结构 ○ ○ ○ ○ ● ● ○ ○ ● ● ○ ○ ○ ○ PCA 只能找到线性方向 → 无法捕捉非线性结构5.2 核 PCA 的思想核 PCA ═══════════════════════════════════════════════════════════════════ 核心思想 用核函数映射到高维空间再做 PCA 步骤 1. 映射x → φ(x) 2. 在高维空间做 PCA 3. 用核技巧避免显式映射 核矩阵 K[i,j] K(x_i, x_j) φ(x_i)ᵀ φ(x_j) 对核矩阵做特征值分解 K U Λ Uᵀ 降维 Y U_k √Λ_k5.3 Python 实现核 PCAclassKernelPCA:核 PCAdef__init__(self,n_components2,kernelrbf,gamma1.0):self.n_componentsn_components self.kernelkernel self.gammagamma self.eigenvectorsNoneself.eigenvaluesNoneself.X_fitNonedef_kernel_function(self,X1,X2):计算核矩阵ifself.kernellinear:returnX1 X2.Telifself.kernelrbf:sq_distsnp.sum(X1**2,axis1).reshape(-1,1)\ np.sum(X2**2,axis1).reshape(1,-1)-\2*X1 X2.Treturnnp.exp(-self.gamma*sq_dists)elifself.kernelpoly:return(X1 X2.T1)**3deffit_transform(self,X):self.X_fitX n_samplesX.shape[0]# 计算核矩阵Kself._kernel_function(X,X)# 中心化核矩阵one_nnp.ones((n_samples,n_samples))/n_samples K_centeredK-one_n K-K one_none_n K one_n# 特征值分解eigenvalues,eigenvectorsnp.linalg.eigh(K_centered)# 按特征值降序排列idxnp.argsort(eigenvalues)[::-1]eigenvalueseigenvalues[idx]eigenvectorseigenvectors[:,idx]# 选择前 k 个self.eigenvalueseigenvalues[:self.n_components]self.eigenvectorseigenvectors[:,:self.n_components]# 降维X_reducedself.eigenvectors*np.sqrt(self.eigenvalues)returnX_reduced5.4 测试非线性数据fromsklearn.datasetsimportmake_circles# 生成环形数据非线性X,ymake_circles(n_samples200,noise0.05,factor0.3,random_state42)# 线性 PCApca_linearPCA(n_components2)X_pcapca_linear.fit_transform(X)# 核 PCAkpcaKernelPCA(n_components2,kernelrbf,gamma15)X_kpcakpca.fit_transform(X)# 可视化fig,(ax1,ax2)plt.subplots(1,2,figsize(12,5))ax1.scatter(X_pca[y0,0],X_pca[y0,1],alpha0.7,labelClass 0)ax1.scatter(X_pca[y1,0],X_pca[y1,1],alpha0.7,labelClass 1)ax1.set_title(Linear PCA)ax1.legend()ax1.grid(True)ax2.scatter(X_kpca[y0,0],X_kpca[y0,1],alpha0.7,labelClass 0)ax2.scatter(X_kpca[y1,0],X_kpca[y1,1],alpha0.7,labelClass 1)ax2.set_title(Kernel PCA (RBF))ax2.legend()ax2.grid(True)plt.tight_layout()plt.show()六、工业应用6.1 数据可视化数据可视化 ═══════════════════════════════════════════════════════════════════ 高维数据3 维无法直接可视化 → 用 PCA 降到 2D 或 3D 应用场景 - 数据探索观察数据分布 - 聚类结果可视化 - 分类结果可视化6.2 去噪去噪 ═══════════════════════════════════════════════════════════════════ 思想 主成分方向包含主要信息 小特征值方向主要是噪声 步骤 1. PCA 降维保留主要成分 2. 逆变换重构 效果 去除噪声保留主要信息6.3 特征提取特征提取 ═══════════════════════════════════════════════════════════════════ 将原始特征映射到主成分空间 原始特征100 维 主成分特征10 维 用于 - 机器学习预处理 - 加速训练 - 防止维度灾难6.4 AOI 中的应用AOI 项目中的 PCA ═══════════════════════════════════════════════════════════════════ 图像预处理 - 降维加速训练 - 去除噪声 特征提取 - 将图像特征降到低维 - 用于分类/聚类 异常检测 - 正常样本在主成分空间聚集 - 异常样本偏离主成分空间七、避坑指南使用 PCA 的 3 个陷阱坑 1不中心化 → 结果错误错误做法直接做 PCA# ❌ 不中心化pcaPCA(n_components2)X_reducedpca.fit_transform(X)# X 未中心化# 结果错误正确做法先中心化# ✅ 中心化X_centeredX-np.mean(X,axis0)pcaPCA(n_components2)X_reducedpca.fit_transform(X_centered)坑 2k 选择不当 → 信息丢失太多错误做法k 太小# ❌ k 太小pcaPCA(n_components1)# 原始 100 维只保留 1 维X_reducedpca.fit_transform(X)# 信息丢失太多正确做法根据方差解释率选择 k# ✅ 选择 k 使得方差解释率 95%pcaPCA(n_components0.95)# 保留 95% 信息X_reducedpca.fit_transform(X)print(f保留{X_reduced.shape[1]}个主成分)坑 3数据尺度差异大 → 需要标准化错误做法直接用原始数据# ❌ 特征尺度差异大Xnp.array([[1000,0.1],# 第一个特征很大[2000,0.2],[3000,0.3],])# PCA 会偏向大尺度特征正确做法标准化# ✅ 标准化fromsklearn.preprocessingimportStandardScaler scalerStandardScaler()X_scaledscaler.fit_transform(X)pcaPCA(n_components2)X_reducedpca.fit_transform(X_scaled)八、本篇总结核心要点回顾PCA 的目标找到数据变化最大的方向主成分协方差矩阵描述特征之间的相关性特征值分解找到主成分方向和方差SVD更稳定的 PCA 实现核 PCA处理非线性数据的 PCA方差解释率衡量信息保留程度下篇预告下一篇我们学习自组织映射SOM。PCA 是有监督的降维需要标签SOM 是无监督的聚类不需要标签。下一篇你将学到竞争学习的原理Kohonen 网络的结构SOM 的学习算法用 Python 手写 SOM本期互动你对 PCA 有什么看法你用过 PCA 吗在什么场景下你觉得 PCA 和 t-SNE 有什么区别你知道 PCA 的哪些变种欢迎在评论区留言。系列目录篇标题状态01Haykin 精讲开篇从「只会调参」到「理解神经网络的灵魂」✅ 完成02感知器神经网络的「鼻祖」为什么它能「学会」分类✅ 完成03LMS 算法从最小二乘到随机梯度下降工业自适应滤波的核心✅ 完成04反向传播神经网络为什么能「学习」用 NumPy 手写 BP✅ 完成05核方法为什么 SVM 能处理非线性问题理解「升维」的本质✅ 完成06支持向量机最大间隔的「艺术」为什么它是「小数据之王」✅ 完成07正则化为什么模型越复杂越容易过拟合L1/L2/Dropout✅ 完成08PCA为什么降维能「去噪」从特征值分解到核 PCA✅ 当前09SOM无监督学习的「聚类之王」为什么它能「自组织」⏳ 下一篇10信息论为什么「信息最大化」能学特征从熵到 ICA⏳ 待写11玻尔兹曼机深度学习的「前世」从统计力学到 RBM⏳ 待写12动态规划强化学习的「数学基础」从 MDP 到值迭代⏳ 待写13Hopfield 网络联想记忆的「鼻祖」为什么它能「回忆」⏳ 待写14卡尔曼滤波为什么它能「预测」从贝叶斯推断到粒子滤波⏳ 待写15Haykin 精讲终篇从感知器到深度学习——一部神经网络的「进化史」⏳ 待写点赞收藏转发是我持续更新的动力
返回列表