别再只懂PCA了!用Python手写LDA,从鸢尾花分类实战看监督降维的威力
别再只懂PCA了用Python手写LDA从鸢尾花分类实战看监督降维的威力鸢尾花数据集在机器学习领域就像Hello World之于编程——经典、简洁却蕴含丰富可能性。当大多数人用PCA处理这类数据时我们往往忽略了数据本身携带的宝贵标签信息。LDA线性判别分析正是那个被低估的监督学习神器它能将类别标签转化为降维动力在投影后空间中让不同类别的数据点尽可能分开。今天我们不依赖scikit-learn的LinearDiscriminantAnalysis黑箱而是从零开始用Python实现LDA算法核心逻辑。通过可视化对比PCA与LDA在鸢尾花数据上的表现差异你会直观理解为什么在分类任务中有监督的LDA往往比无监督的PCA更具优势。本文代码将全程使用NumPy进行矩阵运算并配合Matplotlib动态展示降维过程适合已经掌握PCA基础想进一步提升的算法实践者。1. 环境准备与数据洞察工欲善其事必先利其器。我们使用Jupyter Notebook作为实验环境确保可以交互式地观察每一步的数据变化。首先导入必要的库import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import load_iris from sklearn.decomposition import PCA %matplotlib inline加载鸢尾花数据集并初步观察数据结构iris load_iris() X iris.data # 150个样本4个特征萼片长宽、花瓣长宽 y iris.target # 3个类别山鸢尾、变色鸢尾、维吉尼亚鸢尾 feature_names iris.feature_names target_names iris.target_names print(f特征矩阵形状{X.shape}) print(f类别分布{dict(zip(*np.unique(y, return_countsTrue)))})注意鸢尾花数据集的特征量纲基本一致厘米单位实践中若特征量纲差异大需先进行标准化处理。通过pairplot观察原始特征空间中的分布import seaborn as sns iris_df sns.load_dataset(iris) sns.pairplot(iris_df, huespecies, palettehusl) plt.show()从散点矩阵可以清晰看到花瓣长度和宽度对类别区分度最高山鸢尾setosa与其他两类线性可分变色鸢尾versicolor和维吉尼亚鸢尾virginica存在部分重叠2. LDA算法核心实现LDA的核心思想是找到投影方向使得类间散布最大化同时类内散布最小化。我们需要依次计算以下关键矩阵2.1 计算类内散布矩阵Within-class scatter对于每个类别计算其协方差矩阵并加权求和def compute_within_scatter(X, y): n_features X.shape[1] S_W np.zeros((n_features, n_features)) for c in np.unique(y): X_c X[y c] mean_c X_c.mean(axis0) # 对每个特征中心化后计算协方差 cov_c (X_c - mean_c).T (X_c - mean_c) S_W cov_c return S_W S_W compute_within_scatter(X, y)2.2 计算类间散布矩阵Between-class scatter衡量各类均值与全局均值的差异def compute_between_scatter(X, y): overall_mean X.mean(axis0) n_features X.shape[1] S_B np.zeros((n_features, n_features)) for c in np.unique(y): X_c X[y c] mean_c X_c.mean(axis0) n_c X_c.shape[0] # 计算均值差异的外积 diff (mean_c - overall_mean).reshape(-1, 1) S_B n_c * (diff diff.T) return S_B S_B compute_between_scatter(X, y)2.3 求解广义特征值问题LDA的最优投影方向对应矩阵$S_W^{-1}S_B$的前k大特征值对应的特征向量def lda(X, y, n_components2): S_W compute_within_scatter(X, y) S_B compute_between_scatter(X, y) # 计算广义特征分解 eig_vals, eig_vecs np.linalg.eig(np.linalg.inv(S_W) S_B) # 取实部并按特征值降序排序 sorted_idx np.argsort(eig_vals.real)[::-1] eig_vals eig_vals[sorted_idx].real eig_vecs eig_vecs[:, sorted_idx].real # 选择前n_components个特征向量 W eig_vecs[:, :n_components] return X W X_lda lda(X, y)提示实际应用中应添加正则化项防止$S_W$奇异如S_W 1e-6 * np.eye(S_W.shape[0])3. 可视化对比PCA与LDA为了直观理解两种降维方法的差异我们并排展示它们的二维投影结果# PCA降维 pca PCA(n_components2) X_pca pca.fit_transform(X) # 绘制结果对比 plt.figure(figsize(12, 5)) plt.subplot(121) plt.scatter(X_pca[:, 0], X_pca[:, 1], cy, cmapviridis) plt.title(PCA Projection) plt.xlabel(PC1 (解释方差: %.2f%%) % (pca.explained_variance_ratio_[0]*100)) plt.ylabel(PC2 (解释方差: %.2f%%) % (pca.explained_variance_ratio_[1]*100)) plt.subplot(122) plt.scatter(X_lda[:, 0], X_lda[:, 1], cy, cmapviridis) plt.title(LDA Projection) plt.xlabel(LD1) plt.ylabel(LD2) plt.tight_layout() plt.show()关键观察点PCA方向沿最大方差方向完全忽略类别标签LDA方向最大化类间距离/类内距离比值类别分离更明显在PCA结果中versicolor和virginica仍有较多重叠在LDA结果中三个类别几乎线性可分4. 数学原理与工程实践的结合理解LDA背后的数学原理能帮助我们在实际应用中做出更好决策。以下是几个关键问题的工程思考4.1 为什么LDA在分类任务中表现更好LDA的优化目标直接服务于分类——最大化类别可分性。其目标函数可以表示为$$ J(w) \frac{w^T S_B w}{w^T S_W w} $$这个比值被称为Fisher准则通过求解该优化问题我们得到的投影方向天然适合后续分类。4.2 何时选择LDA而非PCA决策依据可参考以下对比表格特性PCALDA监督/无监督无监督有监督优化目标最大方差方向最大类别分离方向适用阶段探索性分析、特征提取分类任务前的降维类别重叠处理不考虑类别信息显式优化类别分离度计算复杂度$O(p^2n p^3)$$O(p^2n p^3)$特征相关性处理通过协方差矩阵自动处理同样有效处理4.3 实际应用中的注意事项小样本问题当样本数远小于特征数时$S_W$可能奇异。解决方案包括添加正则化项如L2正则化先使用PCA降维再应用LDA多分类问题LDA天然支持多分类最多可降至类别数-1维非线性扩展对于非线性可分数据可考虑核LDAKernel LDA等变体# 正则化LDA示例 def regularized_lda(X, y, alpha0.01, n_components2): S_W compute_within_scatter(X, y) S_B compute_between_scatter(X, y) # 添加正则化项 S_W_reg S_W alpha * np.eye(S_W.shape[0]) eig_vals, eig_vecs np.linalg.eig(np.linalg.inv(S_W_reg) S_B) sorted_idx np.argsort(eig_vals.real)[::-1] W eig_vecs[:, sorted_idx].real[:, :n_components] return X W5. 进阶应用与性能优化将我们的LDA实现应用于实际分类任务对比原始特征与降维后特征的分类性能from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score # 原始特征分类 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2) lr_raw LogisticRegression(max_iter200) lr_raw.fit(X_train, y_train) raw_acc accuracy_score(y_test, lr_raw.predict(X_test)) # LDA降维后分类 X_lda lda(X, y) # 使用全部数据计算投影矩阵 X_train_lda lda(X_train, y_train) X_test_lda X_test np.linalg.pinv(X_train.T) X_train_lda # 近似投影 lr_lda LogisticRegression(max_iter200) lr_lda.fit(X_train_lda, y_train) lda_acc accuracy_score(y_test, lr_lda.predict(X_test_lda)) print(f原始特征准确率{raw_acc:.2%}) print(fLDA降维后准确率{lda_acc:.2%})典型结果可能显示原始特征准确率~97%LDA降维后准确率~100%这验证了适当降维有时能提升模型性能因为消除了冗余和噪声在低维空间中数据更具可分性减少了过拟合风险6. 从鸢尾花到现实场景的迁移虽然我们以鸢尾花数据集为例但LDA的应用远不止于此。以下是一些典型应用场景人脸识别将人脸图像投影到判别空间Fisherfaces生物信息学基因表达数据分类文档分类文本数据降维后分类金融风控客户信用评分模型的特征处理在实现这些应用时有几个工程细节值得注意数据预处理流程缺失值处理 → 特征缩放 → 异常值处理 → LDA降维分类变量需要先进行适当编码计算效率优化对于大规模数据使用随机SVD近似计算利用GPU加速矩阵运算如CuPy库模型监控定期检查$S_W$的条件数防止数值不稳定监控降维后的类别可分性指标变化# 计算类别可分性指标 def separability_index(X_lda, y): overall_mean X_lda.mean(axis0) S_T (X_lda - overall_mean).T (X_lda - overall_mean) S_W compute_within_scatter(X_lda, y) return np.trace(S_T) / np.trace(S_W) print(LDA空间可分性指数, separability_index(X_lda, y))这个指数越大表示类别可分性越好。在实际项目中我们可以用它作为特征工程有效性的监控指标。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2605745.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!