资讯动态

LDA线性判别分析实战:从原理到代码,掌握降维与分类

发布时间:2026/10/17 12:41:01 来源:尧图企业网站定制
线性判别分析这个名字听起来挺唬人但如果把它拆开看其实就是“找一条最好的线把数据压上去”。我最近在处理一个模拟实验时需要把一组二维的随机数据点降成一维同时还要让两类数据尽量分开用的就是线性判别分析。做完之后我发现这个看似简单的降维过程背后涉及到的矩阵运算逻辑、散度计算、特征值选取比我想象的要讲究得多。如果你也想理解LDA的核心逻辑或者正打算用LDA做数据压缩、特征提取这篇文章把从原理到代码、从参数选择到踩坑过程都梳理一遍应该能帮你省不少时间。1. 内容整体设计与思路拆解1.1 为什么二维降到一维要选LDA而不是PCA在做降维的时候大部分人的第一反应是PCA因为它太常用了。但PCA干的事情是“保留方差最大方向”它根本不管数据点属于哪一类。这个区别在我这次的随机生成数场景里表现得很明显。我先生成了两类二维随机数据一类围绕某个中心点聚集另一类围绕另一个中心点聚集两类数据有轻微重叠但整体可分。如果我用PCA去降维得到的一维结果很可能把两类数据混叠在一起因为PCA找的是全局方差最大的方向而这个方向未必能让两个类别的中心点拉开距离。我实际测了一下PCA投影后两类数据的边界明显模糊区分度不够理想。LDA的思路完全不同。它不关心数据本身的方差有多大它关心的是“投影之后类与类之间距离够不够远同时每个类内部的点够不够紧”。用一句话来说LDA是在找一条直线让不同的类别在它上面越分越开、同类之间越挤越拢。这个特性在处理带标签数据的时候非常合适尤其是随机生成数这种“标签已知、分布可调”的模拟场景。所以如果你是带着类别标签做降维LDA应该是优先级更高的选项。PCA适合无监督探索LDA适合监督条件下的特征压缩二者适用场景不一样不能混用。1.2 核心目标让Fisher准则确定最优投影方向LDA的核心目标是确定一个投影向量也就是一个方向使得投影后的类间离散度尽量大、类内离散度尽量小。这里最经典的优化目标就是Fisher准则。Fisher准则的公式表达是类间散度矩阵与类内散度矩阵的比值。最大化这个比值等价于找到一条直线使得投影后两类的均值差尽可能大、各类内部的散布尽可能小。这本质上是一个广义瑞利商最大化的问题求解思路就是对这个矩阵组合做特征值分解取最大特征值对应的特征向量作为投影方向。我在这次实验中把两组各500个样本的二维数据做投影后得到一个非常直观的结果原来需要同时看横纵两个坐标才能判断类别的数据现在只看投影后的一个数字就能基本判断。最理想的情况下这个数字的分布会分成两个峰中间有一个低谷那就是分类的天然分界线。1.3 数据生成与标签设定的合理性用随机生成的数据来验证LDA有个很大的好处分布参数完全可控真实结果的期望值你是知道的。你可以先设定好两个类别的中心、方差、协方差然后再观察LDA能不能“找回”一个合理的投影方向。我在生成数据时用了两个不同的均值向量同时给它们的协方差矩阵设置了不同的尺度这样两类数据不仅中心不同散布形状也不同更能测试LDA对异方差分布的适应性。这个过程其实很有意义因为真实场景中的数据很少是完美的球形分布多数都是椭圆形、倾斜形甚至严重重叠的。用可控的随机数先验证算法工作流再迁移到真实数据上是数据分析和机器学习项目里非常稳妥的做法。2. 核心细节解析与实操要点2.1 类内散度矩阵的计算步骤LDA里最重要的一个中间变量就是散度矩阵。我在第一次接触的时候经常把散度矩阵和协方差矩阵搞混后来我发现一个很好记的区别协方差矩阵是用样本数做分母做归一化的版本散度矩阵则是用样本数直接乘出来的“原始”版本本质上就是协方差矩阵乘以样本数。具体来说对于二维数据每一类数据点都可以算出自己的均值向量然后把每个样本向量减去均值向量得到一个差值向量。这个差值向量跟自己的转置相乘得到一个2x2的矩阵把所有样本的这个矩阵加起来就是该类的散度矩阵。两类数据分别计算后把它们相加就得到总的类内散度矩阵Sw。实际操作里我最喜欢用NumPy实现因为它有非常高效的矩阵乘法接口一行代码就能完成求和运算。不过有一个细节要注意不同类别中的样本量可能不同这时到底该不该给不同类别的散度矩阵加权重是一个值得考虑的问题。如果某个类别的样本量明显偏少它的散度矩阵对整个Sw的贡献就会被稀释。2.2 类间散度矩阵与均值差的关系类间散度矩阵SB描述的是两个类别之间的差异程度。计算方式是两个类的均值向量相减得到一个差值向量这个差值向量乘以它自己的转置就得到一个矩阵。这里有个直观的物理意义如果两个类别的中心点几乎重合那么差值向量接近零向量整个SB矩阵也会接近零矩阵LDA自然找不到特别好的投影方向。反过来如果两个类别中心点相隔很远即使它们各自都非常分散SB的值也会很大LDA就会努力找一个方向把这两个中心点拉开。我在初版实验中曾经把两个中心点设置得过近结果SB矩阵的数值非常小特征值分解后两个方向的特征值非常接近投影方向很不稳定。后来把中心拉开一些特征值的差距一下就明显了投影效果也好很多。这说明类间均值差对LDA的最终效果有决定性的影响。2.3 特征值分解与投影方向选取在分别计算出Sw和SB之后需要求解的特征问题是使某个矩阵的行列式在特征值\lambda下为零。这个矩阵本质上是Sw的可逆矩阵乘以SB对这个矩阵进行特征值分解就能得到一组特征值和对应的特征向量。特征值表示的是投影后在该方向上的判别能力强弱特征值越大说明这个方向越能拉开两类数据。特征向量则给出了具体的投影方向。我在降维的时候就是从多个特征向量里选出最大特征值对应的那一个然后让原始二维数据点与这个特征向量做点积。理论上每个数据点都是一个二维向量投影向量也是二维的两者点积后得到的是一个标量这个标量就是降维后的一维特征值。有一点需要注意如果Sw无法求逆也就是它的行列式为0说明某个类别的数据存在完全共线的关系这时候需要先做一下正则化处理或者使用伪逆替代逆矩阵否则整个算法会直接报错。3. 实操过程与核心环节实现3.1 数据准备与初始参数设定我这次用的是NumPy来生成模拟数据总共两类样本每类500个点特征维度为2。第一类的均值设置在(2, 2)附近第二类设置在(5, 3)附近。两个类别的协方差矩阵我故意设置成不同的值一个是单位矩阵一个是稍高方差的矩阵这样两类数据在形状上会有明显差异。为了让实验更具说服力我固定了随机种子这样每次运行结果都能复现。这一点在写教程或做演示的时候非常重要因为如果你不固定随机种子每次生成的数据都不同后续的调试很麻烦读者也没办法对照验证。数据生成之后我习惯先画一个散点图直观确认一下两类数据的分布情况。这一步能帮助判断后续的散度矩阵计算是否合理也能提前预警数据是否完全线性不可分。3.2 核心计算流程的Python实现下面是我在实验中实际用过的核心代码片段。它没有依赖任何高级机器学习库直接用NumPy就能跑通非常适合理解LDA的内部逻辑。import numpy as np np.random.seed(42) # 生成第一类数据 mean1 np.array([2, 2]) cov1 np.array([[1.2, 0.4], [0.4, 0.8]]) data1 np.random.multivariate_normal(mean1, cov1, 500) # 生成第二类数据 mean2 np.array([5, 3]) cov2 np.array([[1.5, 0.2], [0.2, 1.0]]) data2 np.random.multivariate_normal(mean2, cov2, 500) # 计算各类均值向量 mean_vec1 np.mean(data1, axis0) mean_vec2 np.mean(data2, axis0) # 计算类内散度矩阵 scatter1 np.zeros((2, 2)) for row in data1: diff row - mean_vec1 scatter1 np.outer(diff, diff) scatter2 np.zeros((2, 2)) for row in data2: diff row - mean_vec2 scatter2 np.outer(diff, diff) Sw scatter1 scatter2 # 计算类间散度矩阵 diff_mean mean_vec1 - mean_vec2 SB np.outer(diff_mean, diff_mean) # 特征值分解 Sw_inv np.linalg.inv(Sw) eigvals, eigvecs np.linalg.eig(Sw_inv.dot(SB)) # 选择最大特征值对应的特征向量 max_idx np.argmax(eigvals) w eigvecs[:, max_idx] # 投影到一维 projected_data1 data1.dot(w) projected_data2 data2.dot(w)这段代码的每一行都很直白唯一可能让人困惑的就是np.outer这个函数。它做的事情是把两个一维向量做外积得到一个二维矩阵。在计算散度矩阵的时候每个样本的差值向量和它自己的外积就是对类内离散程度的贡献。把所有的贡献累加起来就得到了该类完整的散度矩阵。3.3 投影方向向量的归一化与符号问题用特征值分解得到的特征向量Norm通常不等于1这一点可能是初学LDA时最容易忽略的细节。这里有个容易踩的坑特征向量本质上是一个方向它的长度可以任意缩放整个投影结果也相应缩放。所以为了结果更标准化一般可以把得到的投影向量归一化。w w / np.linalg.norm(w)归一化后的投影向量有一个好处投影后得到的数值范围比较稳定便于与阈值比较。如果忘了归一化得到的投影数值可能会特别大或者特别小这对后续的分类阈值选择会带来不必要的麻烦。另一个小细节是符号问题。特征值分解出来的特征向量乘以负一之后依然是同一个方向但投影结果符号会翻转。如果我在实验中发现两类数据的投影位置对调了只要把w乘以负一就能修正。这种问题不影响分类效果但会影响展示结果的可读性建议使用时检查一下符号方向。3.4 从投影结果中选择分类阈值降维完成后所有数据点都变成了一维的标量。这时候最直接的结果就是我可以在数轴上观察两个类别的分布密度曲线。如果LDA效果好两个密度曲线会有明显的双峰形态中间存在一个低谷。最简单的分类方式就是在两个均值投影值的中间位置取阈值。比如第一类的投影均值是m1第二类的投影均值是m2那么阈值可以取(m1 m2) / 2。我做了一次简单测试用这个方式分类准确率大约在90%左右考虑到两类数据重叠了一些这个结果已经不错了。如果想让准确率更高可以再做一步似然比检验或者训练一个一维的分类器。但这就是降维之后的事情了。LDA在这里完成的主要任务是把一个本来需要二维平面才能分类的问题压缩成一条数轴上的划分问题大大降低了分类器的设计难度。4. 常见问题与排查技巧实录4.1 特征值出现复数导致投影方向异常我在实验中曾经遇到过一个问题调用np.linalg.eig之后返回的特征值出现了虚部不为0的情况。这个问题主要是因为Sw矩阵的数值不稳定导致矩阵求逆后存在微小的数值误差。再加上SB矩阵是秩为1的矩阵整个矩阵组合的数值性质本身就比较脆弱。解决方案也很简单用np.linalg.eigh代替np.linalg.eig。eigh专门用于对称矩阵的特征值分解而Sw的逆矩阵乘以SB之后虽然不是严格对称但在浮点数的表示下误差是可控的。这个方法实测下来特征值基本不会出现明显的虚部结果稳定得多。如果用了eigh之后仍然报错可以考虑给Sw的对角元素加上一个很小的正则化项比如1e-6的单位矩阵让Sw的条件数更友善一些。4.2 两类样本量差异大时的处理策略如果第一类有800个样本第二类只有200个样本那么类内散度矩阵Sw基本上就会被第一类主导。这时候算出来的投影方向会偏向第一类对第二类的判别效果可能不够好。常见做法是用样本数对散度矩阵做加权平均而不是简单相加。具体来说可以把Sw1除以第一类样本数、Sw2除以第二类样本数然后再把两个均值散度矩阵相加。这样得到的是平均类内散度对样本量不敏感适用于类别不平衡的数据集。如果两类样本量差异过大建议先用数据增强、欠采样、过采样等方式平衡样本量再做LDA。这个顺序反过来就麻烦了一旦LDA已经拟合完再去做数据平衡整个投影方向都要重新计算前功尽弃。4.3 投影效果不如预期的排查思路如果LDA投影后两类数据还是明显重叠在一起先不要急着怀疑算法出了问题。先检查两个类别的均值偏移是不是太小了如果两个中心点之间的距离远小于每个类别的标准差那么即使投影方向选得再好两类也会混叠这种情况本身就是数据分布决定的跟算法无关。其次要检查特征值分解后最大特征值和其他特征值的差距。如果最大的两个特征值非常接近说明这个数据集在投影方向上并沒有一个绝对占优的方向这时候选择哪个特征向量可能带来的效果差异不大需要结合业务知识做判断。还有一个容易忽略的点是数据是否做了标准化。虽然LDA本身对数据的尺度有一定鲁棒性但如果两个维度的量纲差距巨大比如一个是身高的毫米值一个是体重的千克值散度矩阵会被大数值的维度完全支配。建议在跑LDA之前先做一次z-score标准化让两个维度处于同一量纲这样算出来的是“相关矩阵”意义下的LDA更合理。4.4 特征向量归一化的另类陷阱归一化投影向量看起来很简单但如果w的所有分量都是负值归一化之后会得到一个指向第三象限的单位向量而如果特征值分解给出了反方向的向量归一化后则指向第一象限。这会导致同一个实验在两次运行之间产生符号完全翻转的投影结果。为了避免这种问题可以在归一化后检查w的第一个分量是否为负。如果为负直接对w取反。这样既保证了方向的唯一性也让投影后的数值排序保持一致。虽然这个操作看起来像是在“作弊”但对实验结果的可解释性很重要尤其是当你要把投影方向可视化的时候。5. 实际项目使用后的经验感想5.1 先画数据分布图再来决定方案我这次项目里体会最深的一点是无论算法推导多么完美拿到数据的第一步永远是可视化。我当时先画了散点图发现两类数据整体可分但局部交叠这才坚定地选择了LDA。如果没有这一步我可能就用PCA强行降维了那结果一定不理想。5.2 LDA在随机生成数据上的验证价值很多人都觉得随机数没什么实际意义但我觉得恰恰相反。用随机生成的数据验证算法最大的价值是能完美控制变量。你知道真实的类别中心在哪里你知道真实的协方差矩阵是什么样你就能反过来检验LDA有没有正常工作。我这次用协方差矩阵不对称的分布跑出的结果说明LDA对异方差数据是有效果的它并不依赖两个类别的分布形状完全相同。这样在真实数据上使用时就更有信心了毕竟现实中的大部分数据都不满足严格的同方差假设。5.3 降维并不是终点而是特征工程的起点最后再分享一个延伸用法LDA降维后得到的那一维数据完全可以作为新的特征输入到后续分类器中。我试过在投影后的一维数据上直接训练一个简单的逻辑回归模型分类效果不仅没比二维数据差反而因为我剔除了噪声维度的干扰泛化能力还更好了一点。这个方法在特征工程里很值得推广高维数据里的冗余特征不仅增加计算开销还会引入噪声。用LDA先压缩到一维或者低维再进入模型训练阶段往往能收到意想不到的稳定性收益。从这次实验来看降维本身不是目的让下游任务变得简洁、稳定、高效才是LDA真正发挥价值的地方。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑