1. 为什么说到降维大家总是先想起PCA而忘了LDA1.1 我那次用PCA做分类预处理栽的跟头先说一个我自己的经历。某次做一个图像分类的小项目特征工程做完之后特征维度到了好几百模型训练慢不说过拟合也明显。当时脑子里的第一反应就是降维嘛上PCA。于是二话不说把特征降到50维丢给分类器跑了一轮。结果挺打脸的准确率比降维之前还低了两三个点更诡异的是把降维后的特征可视化出来三个类别在二维平面里挤成一团几乎看不出结构。后来一个做统计出身的朋友提醒了一句你下一步要做分类怎么用了个不管标签死活的无监督降维这句话让我意识到降维不是只有PCA这一条路。对于降完维之后还要做分类的场景应该优先考虑今天这篇的主角——LDA线性判别分析。它和PCA最大的区别从名字就能看出来PCA只管方差最大LDA管的则是分类聚团。很多教程喜欢把LDA放在PCA后面当对照组讲但实际项目里用到LDA的频率并不低尤其是做特征压缩、数据可视化、分类前预处理这几件事的时候。如果你和我一样曾经在降维和分类之间走过弯路这篇文章应该能帮你把思路理清楚。1.2 降维这件事有两种完全不同的目的想把LDA理解到位先得把降维目的这件事掰开。同样是降维PCA和LDA看起来都是把高维数据投影到低维空间但它们的优化目标和适用场景几乎相反。PCA做的是无监督降维它完全不看数据标签只盯着样本本身的方差结构找到那些能最大限度保留原始数据散布的方向。PCA的理想结果是投影之后样本之间的整体距离尽量拉大信息损失尽量小。它适合做特征压缩、去相关、可视化探索但它不保证降维后的空间对分类友好。两个类别在原始空间里可能本来分得开PCA却可能因为整体方差最大这个目标把一个对分类极有区分度的方向给压缩掉了我上面那个项目就是典型例子。LDA做的是有监督降维它需要知道每个样本属于哪一类。LDA的目标非常明确找一个投影方向让不同类别的样本在这个方向上尽可能分开同时让同类别的样本尽可能聚拢。用一个形象的比喻就是——一屋子人乱糟糟站着PCA想的是怎么把大家的活动范围摊得最开LDA想的是怎么给每个人找个位置让同一家人挨在一起、不同家庭离得远远的。所以判断标准很简单如果你的下游任务是分类、识别、检索这类需要区分度的场景先试LDA而不是PCA。这也是LDA常被称为Fisher线性判别的原因——它本质是在做判别降维只是它的副产品。2. LDA的分类聚团直觉把散点图当成一群人要合影2.1 核心思想就两句话类内紧凑、类间疏远LDA的数学推导看起来有点绕但背后直觉极其简单。设我们有一堆二维平面上的点分属三个类别现在想把它们投影到一条直线上也就是降成一维。问题来了往哪个方向投影最好先看看两个极端方向。如果投影方向选得不好比如恰好选了和类别分布垂直的方向三类样本投到一维之后会完全重叠在一起什么信息都看不出来。如果选了一个斜着的方向可能某一类分开了另外两类还缠在一起效果也不理想。LDA要找的是那个最优方向它同时满足两个条件类内距离小同类别样本投影之后尽量挤在一起别散成一盘沙。类间距离大不同类别的中心点投影之后尽量离得远别互相叠着。这两个条件合起来就是标题里说的分类聚团思维。类内紧凑保证分类器在低维空间里不容易混淆同类样本类间疏远保证不同类别之间有足够间隙分类边界想怎么划都行。一个特别容易误解的地方是LDA并不是只关心类间距离如果只看类间距离投影方向最远的那条线往往会让每一类内部也极度分散分类边界依然很难找。LDA真正的目标是一个比值最大化——类间距离和类内离散度的比值而不是单独追求任何一个。2.2 Fisher线性判别里的那个经典比值这个比值最早来自Fisher在1936年提出的线性判别思想公式写出来是这样的最大化 ( J(w) \frac{w^T S_b w}{w^T S_w w} )。我先把符号用人话翻译一遍。( w ) 是我们要求的投影方向一个向量( S_b ) 是类间散度矩阵度量不同类别中心相对全局中心的分散程度( S_w ) 是类内散度矩阵度量每个类别内部样本相对自己类别中心的分散程度。( J(w) ) 的含义就是投影之后类间分散程度与类内分散程度的比值。为什么要最大化这个比值举个例子A方向投影后类间距离是10类内标准差是2比值是5B方向投影后类间距离是12但类内标准差是4比值只有3。虽然B方向类间距离更大但类别内部被拉扯得太厉害边界处两类样本反而容易交叉所以A方向更优。比值最大化本质上是在找一个折中点距离要够大但聚类不能散架。这就是Fisher判别的精髓。如果你不想深究矩阵推导先记住这个直觉就行——LDA找到的方向是让组间差异除以组内差异最大的方向。这个比值也是LDA和PCA之间最本质的分水岭PCA最大化的是整体方差LDA最大化的是可分性。3. LDA降维的数学骨架科普版推演3.1 散度矩阵到底在算什么很多人在这一步被劝退其实散度矩阵没那么可怕。我们先从一维类比开始。假设现在只看一个特征想衡量类内紧凑程度最自然的做法是算每个类别内部样本到该类中心距离的平方和。把所有类的这个值加起来就是总体类内离散度。放在多维空间里这个距离平方和就变成向量外积的累加也就是 ( S_w )。同理类间散度 ( S_b ) 衡量的是每个类别的中心点相对于全局中心的偏离程度也是用向量外积累加得到的。两个矩阵都是对称半正定的它们把分散程度用矩阵的形式编码了起来。矩阵对一个向量 ( w ) 做二次型 ( w^T S w )本质上就是在算所有样本沿 ( w ) 方向投影之后离散程度的加权总和。所以 ( J(w) ) 这个式子并不是从天而降的公式它就是在回答一个问题沿着某个方向看类别间差异相对类别内差异有多大矩阵只是把方向和离散度之间的函数关系包装得更紧凑而已。3.2 特征值分解出来的为什么是判别方向最大化 ( J(w) ) 这个问题标准的解法是对 ( S_w^{-1} S_b ) 做特征值分解。这里有一个很优雅的数学结论当 ( S_w ) 可逆时使比值最大的方向 ( w ) 就是 ( S_w^{-1} S_b ) 最大的特征值对应的特征向量。为什么特征值分解能同时兼顾类间大、类内小关键在于 ( S_w^{-1} ) 这个逆矩阵的几何意义。它相当于在做一次白化变换先沿着类内离散度大的方向把数据压扁再在这个修正过的空间里找类间散度最大的方向。换句话说LDA首先惩罚了那些类内已经很散的方向然后再在剩下状态良好的方向里找区分度。这就是它比单独看类间距离更稳健的数学原因。如果你只需要降维到 ( k ) 维就取前 ( k ) 个最大特征值对应的特征向量组成投影矩阵。实际使用中可以不用自己手写这些矩阵运算sklearn里的LinearDiscriminantAnalysis封裝得很好但理解背后的特征值逻辑对解读结果、判断是否适用LDA都很有帮助。3.3 最多只能降到类别数减一这是LDA一个非常容易踩坑的硬性限制LDA降维后最多只能得到 ( C-1 ) 个维度其中 ( C ) 是类别数。比如做二分类LDA最多只能降到一维五分类最多降到四维。原因是 ( S_b ) 这个矩阵的秩最多是 ( C-1 )它描述的是类别中心之间的差异而 ( C ) 个中心之间的自由度天然只有 ( C-1 )。这意味着什么如果你有10个类原始特征有100维LDA最多帮你降到9维。这和PCA完全不同PCA最多能降到特征维度数。很多第一次用LDA的人以为它和PCA一样想降几维降几维结果sklearn直接报错或者返回的维度比自己设的小一脸懵——其实这是LDA的数学性质决定的不是bug。在实操里这个限制也不完全是坏事。类别数通常不会太多LDA把高维特征压到 ( C-1 ) 维之后再配分类器往往既能大幅降低维度、缓解过拟合又能保留类别可分性。比如一个10分类问题1000维特征压到9维压缩比非常可观。4. 跑通一个LDA降维实验Python示例4.1 人造三类数据集准备理论讲再多不如跑一次实验来得直观。我这边用Python的sklearn写个完整示例方便你直接复现。先造一个三类、三特征的数据集。为了让效果明显我故意让每个类别的均值中心在三维空间里分得比较开但类内又有一定噪声这样既贴近真实场景又方便观察LDA的效果。import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import make_classification from sklearn.discriminant_analysis import LinearDiscriminantAnalysis from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score # 生成三类、三维特征、共600个样本的数据集 X, y make_classification( n_samples600, n_features3, n_informative3, n_redundant0, n_classes3, n_clusters_per_class1, class_sep1.8, random_state42 ) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42 ) print(X.shape) # (600, 3)make_classification是sklearn里很方便的合成数据工具可以控制类别数、信息特征数、类别间隔等参数。我这里特意设了n_informative3让三个特征都携带分类信息方便后续观察LDA降维是否真的能保留区分度。4.2 使用sklearn中的LDA进行降维接下来直接调用LinearDiscriminantAnalysis。三分类情况下LDA最多能降维到 ( 3-12 ) 维我们正好降到2维然后做可视化。# 初始化LDA指定降到2维 lda LinearDiscriminantAnalysis(n_components2) X_train_lda lda.fit_transform(X_train, y_train) X_test_lda lda.transform(X_test) print(降维后训练集形状:, X_train_lda.shape) # (420, 2) print(两个判别方向解释的类间方差比例:, lda.explained_variance_ratio_)输出中会有explained_variance_ratio_这个属性它表示每个判别方向承载的类间散度占比。比如出现[0.85, 0.15]意味着第一个判别方向已经解释了85%的类间差异信息非常集中。这个值对判断降到几维合适很有参考意义。这里有个使用细节值得注意fit_transform和transform必须分开用先在训练集上拟合再用同样的投影矩阵变换测试集。如果直接把测试集丢进去fit_transform会让测试集的信息泄漏到训练过程里评估结果会虚高。4.3 降维后可视化与分类效果对比把降维后的数据画出来你能非常直观地看到分类聚团的效果plt.figure(figsize(12, 5)) plt.subplot(1, 2, 1) scatter0 plt.scatter(X_train[y_train 0][:, 0], X_train[y_train 0][:, 1], cskyblue, labelclass 0, alpha0.7, edgecolorsk) scatter1 plt.scatter(X_train[y_train 1][:, 0], X_train[y_train 1][:, 1], csalmon, labelclass 1, alpha0.7, edgecolorsk) scatter2 plt.scatter(X_train[y_train 2][:, 0], X_train[y_train 2][:, 1], cmediumseagreen, labelclass 2, alpha0.7, edgecolorsk) plt.title(Original Feature Space (first two dims)) plt.legend() plt.subplot(1, 2, 2) plt.scatter(X_train_lda[y_train 0][:, 0], X_train_lda[y_train 0][:, 1], cskyblue, labelclass 0, alpha0.7, edgecolorsk) plt.scatter(X_train_lda[y_train 1][:, 0], X_train_lda[y_train 1][:, 1], csalmon, labelclass 1, alpha0.7, edgecolorsk) plt.scatter(X_train_lda[y_train 2][:, 0], X_train_lda[y_train 2][:, 1], cmediumseagreen, labelclass 2, alpha0.7, edgecolorsk) plt.title(After LDA (2D)) plt.legend() plt.tight_layout() plt.show()视觉效果通常是原始三维特征中随便挑两个维度画类别之间可能有重叠LDA降维后的二维图里三个类别会像三团云一样明显分开边界干净很多。这也是LDA作为可解释降维手段的价值——投影方向时刻在往好分类的方向上靠。为了量化效果我再跑一个对比实验分别在原始特征和LDA降维后特征上训练相同的随机森林分类器。clf_raw RandomForestClassifier(n_estimators200, random_state42) clf_raw.fit(X_train, y_train) acc_raw accuracy_score(y_test, clf_raw.predict(X_test)) clf_lda RandomForestClassifier(n_estimators200, random_state42) clf_lda.fit(X_train_lda, y_train) acc_lda accuracy_score(y_test, clf_lda.predict(X_test_lda)) print(f原始特征准确率: {acc_raw:.4f}) print(fLDA降维后准确率: {acc_lda:.4f})在我用这个参数跑出来的结果里LDA降维后的准确率往往比原始3维特征略高或者持平绝不会出现PCA那种明显掉点的情形。如果原始特征维度更高、噪声更大LDA的优势会更突出——因为它降维的同时等于在帮你做了一次针对分类任务的去噪。5. LDA的适用边界与常见误区5.1 数据服从高斯分布且各类协方差相近LDA虽然好用但它并非在所有场景下都是最优选择。它有一个隐含假设每个类别的数据近似服从高斯分布并且各类别的协方差矩阵大致相同。在这个假设下贝叶斯最优分类器会退化成一个线性决策边界LDA恰好是这个边界对应的投影。如果各类别协方差差异很大比如一类数据很瘦长、另一类很矮胖LDA找出来的线性方向可能很难把两类干净地分开。这时候可以考虑QDA二次判别分析它允许各类协方差不同决策边界是二次曲线拟合能力更强但更需要充足样本。一个常见误区是LDA名字里带线性所以以为它只能处理线性可分的数据。其实LDA在处理非线性数据时同样能给出一个有意义的投影方向只是它无法保证在这个方向下类别彻底分离。对于明显非线性的数据比如环形分布、螺旋分布更合适的是先做核技巧Kernel LDA或者直接用深度特征加上Metric Learning。LDA不是万能的但作为线性基准非常可靠。5.2 样本量远小于特征数时的坑LDA求解需要 ( S_w ) 可逆而 ( S_w ) 的维度等于特征维度。当特征数量远大于样本数量时也就是所谓的p n问题( S_w ) 往往是奇异矩阵求逆根本无从谈起。这就是典型的小样本问题在高维生物数据、文本数据里非常常见。应对策略有几个第一先用PCA做一个初步降维把特征压到样本量以内再跑LDA第二对 ( S_w ) 做正则化sklearn里LinearDiscriminantAnalysis提供了shrinkage参数设置为一个0到1之间的小数即可对类内散度矩阵的对角线做收缩从而让它可逆第三用解( S_w^{-1} S_b )的替代解法比如基于广义特征值分解sklearn内部在svd求解器下就是这么处理的。实话说在我处理过的几个高维小样本数据集上shrinkage的作用肉眼可见。比如一组数据原始维度5000、每类只有30个样本直接跑LDA会崩或者结果极不稳定开了shrinkage之后不仅稳定分类效果也明显提升。遇到类似场景先试试这个参数别急着换算法。5.3 LDA与PCA的选型对比表我把两者的关键差异整理成一张表平时做选型的时候可以直接参考对比维度PCALDA监督/无监督无监督不依赖标签有监督依赖标签优化目标最大化投影后方差最大化类间/类内散度比值类别信息完全忽略核心依赖下游任务压缩、可视化、去相关分类前降维、特征判别降维上限可降到特征维数最多 ( C-1 ) 维适用假定无特殊分布假定类别高斯分布、协方差相近小样本高维一般可用需要正则化或先PCA非线性数据需核方法Kernel PCA需核方法Kernel LDA特别强调一下PCA和LDA不是互斥关系实践中经常组合使用。先跑PCA降低维度到一定程度再跑LDA强化可分性是处理高维数据的高频套路。我见过不少比赛方案就是这么干的效果比单用一个要稳。顺序一定是先PCA后LDA反过来不行——LDA已经把维度压到( C-1 )了再用无监督的PCA去压缩这个判别空间意义不大。6. 我在实际项目中用LDA的一些体会这个部分谈不上什么宏大结论就是分享几个我实打实用过的经验。第一LDA非常适合做降维可视化后续建模的双重任务。有一次做一个四分类的文本主题判别任务原始词频特征上完TF-IDF之后维度接近8000。我先是PCA降到50维再在这个基础上跑LDA降到3维然后直接用3维特征训练随机森林。模型效果比直接用8000维TF-IDF还好了将近两个百分点训练时间从两分钟缩到几秒。更让我意外的是把3维投影特征画在三维图里四个类别的样本形成了非常清晰的四团连非技术的同事看了图都能理解模型在看到什么。这种可解释性红利是LDA相比纯黑盒降维方法最容易被忽视的价值。第二如果你想把这个思路再往前推一步可以试试在LDA投影空间里构建分类器而不是回到原始空间。因为LDA的投影方向本身就是按最优判别来设计的在这个空间里哪怕用最简单的最近邻分类器效果也经常能和原始高维空间里调过参的复杂模型打平。做基线模型或者快速验证概念的时候这条路相当省事。我现在的习惯是拿到一个分类任务先花五分钟用LDA降个维、跑个最近邻基线再决定要不要上复杂模型——这个基线在大多数时候都出乎意料地能打。第三关于到底该降多少维这个问题我的经验是看explained_variance_ratio_的累计曲线。如果前两个判别方向已经累计解释了90%以上的类间散度说明绝大多数判别信息都被保留了再增加维度收益很小反过来如果前几个方向只解释了50%那不妨保留到( C-1 )维上限或者考虑数据本身是否适合LDA。最后想多说一句LDA不是那种一招鲜吃遍天的算法它和PCA的关系更像是分类视角和重构视角的区别。选哪个取决于你降维之后要干什么。只要记住分类聚团这个思维——让同类的更近、异类的更远——你在绝大多数分类前预处理任务里都能做出比盲目套PCA更好的决定。