观文听傑

返回

上一篇的多项式朴素贝叶斯用条件独立假设,把高维词频联合分布拆成许多一维计数。这让模型在小数据、稀疏文本中极其高效,却会把“优惠”和“领取”这类相关特征重复当成独立证据。

在体检或传感器数据中,相关性更难忽略:身高与体重通常一起变化,温度与压力可能沿某个斜方向波动。若模型只看每一维自己的均值和方差,就会把数据云的方向丢掉。

线性判别分析(Linear Discriminant Analysis,LDA)保留生成式分类框架,用多元高斯分布显式描述特征共同变化。本文只回答三个紧密问题:每一类的高斯数据云怎样写成概率、共享协方差为何让二次项消失,以及马氏距离怎样变成可计算的线性分类分数。

01 独立方差为什么看不见“斜着的数据云”?#

设两个类别的二维测量数据如下:

x₂
▲                         类别 1
│                    ●  ●
│                 ●  ●
│              ●
│        ○  ○                  两类数据都沿右上方向延展
│     ○  ○
│  ○                         类别 0
└──────────────────────────────────► x₁
text

每一维的方差只能回答沿坐标轴有多分散;协方差(Covariance)还回答两个特征是否一起增减。二维协方差矩阵为:

Σ=[Var(x1)Cov(x1,x2)Cov(x2,x1)Var(x2)]\Sigma= \begin{bmatrix} \operatorname{Var}(x_1) & \operatorname{Cov}(x_1,x_2)\\ \operatorname{Cov}(x_2,x_1) & \operatorname{Var}(x_2) \end{bmatrix}

DD 维输入,ΣRD×D\Sigma\in\mathbb{R}^{D\times D}。对角线保存各特征方差,非对角线保存成对协方差。高斯朴素贝叶斯相当于只保留对角线;LDA 则保留完整矩阵,但要求各类别共享它。

02 LDA 对数据分布作了什么假设?#

训练集为:

XRN×D,y{0,1,,K1}NX\in\mathbb{R}^{N\times D},\qquad y\in\{0,1,\ldots,K-1\}^{N}

LDA 假设类别 kk 的特征服从多元高斯分布(Multivariate Gaussian Distribution):

xy=kN(μk,Σ)x\mid y=k\sim\mathcal{N}(\mu_k,\Sigma)

密度为:

p(xy=k)=1(2π)D/2Σ1/2exp[12(xμk)Σ1(xμk)]p(x\mid y=k)= \frac{1}{(2\pi)^{D/2}|\Sigma|^{1/2}} \exp\left[-\frac12(x-\mu_k)^\top\Sigma^{-1}(x-\mu_k)\right]
  • μkRD\mu_k\in\mathbb{R}^{D}:类别 kk 的均值向量;
  • ΣRD×D\Sigma\in\mathbb{R}^{D\times D}:所有类别共享的类内协方差矩阵;
  • Σ|\Sigma|:协方差行列式,反映数据云总体体积;
  • Σ1\Sigma^{-1}:精度矩阵(Precision Matrix);
  • πk=P(y=k)\pi_k=P(y=k):类别先验,全部先验组成 πRK\pi\in\mathbb{R}^{K}

模型训练后保存的主要状态是:

class means       mu       [K,D]
shared covariance Sigma    [D,D]
class priors      pi       [K]
text

与朴素贝叶斯一样,LDA 先建模 p(xy=k)p(x\mid y=k)P(y=k)P(y=k),再用贝叶斯定理计算 P(y=kx)P(y=k\mid x);它不是把标签作为连续值做最小二乘回归。

03 马氏距离为什么比欧氏距离多看了一层?#

高斯指数中的二次型:

dk2(x)=(xμk)Σ1(xμk)d_k^2(x)=(x-\mu_k)^\top\Sigma^{-1}(x-\mu_k)

叫作平方马氏距离(Squared Mahalanobis Distance)。欧氏距离把每个方向视为同等尺度;马氏距离先按协方差“白化”数据:高方差方向上的同样位移惩罚较小,低方差方向上的位移惩罚较大,并考虑特征相关导致的椭圆方向。

原空间:共享斜椭圆                 白化空间:协方差变成单位阵

      ╱╱ ● 类1                         ○      ● 类1
   ○ ╱╱                              类0
 类0                                      距离可直接用圆形欧氏距离

按 Sigma^{-1} 拉伸、旋转 ───────────────────────────────►
text

所以 LDA 可以直观理解为:在共享协方差定义的几何中,选择马氏距离更近的类中心,同时用类别先验修正答案。

04 从高斯后验推导线性分数#

贝叶斯分类选择最大后验类别:

y^=argmaxkp(xy=k)πk\hat y=\arg\max_k p(x\mid y=k)\pi_k

取对数并去掉对所有类别相同的常数:

δk(x)=12(xμk)Σ1(xμk)+logπk\delta_k(x)= -\frac12(x-\mu_k)^\top\Sigma^{-1}(x-\mu_k)+\log\pi_k

展开二次项:

(xμk)Σ1(xμk)=xΣ1x2xΣ1μk+μkΣ1μk(x-\mu_k)^\top\Sigma^{-1}(x-\mu_k) =x^\top\Sigma^{-1}x -2x^\top\Sigma^{-1}\mu_k +\mu_k^\top\Sigma^{-1}\mu_k

关键在第一项 xΣ1xx^\top\Sigma^{-1}x:因为所有类别共享同一个 Σ\Sigma,它对每个 kk 完全相同,比较类别时消去。剩下:

δk(x)=xΣ1μk12μkΣ1μk+logπk\delta_k(x)= x^\top\Sigma^{-1}\mu_k -\frac12\mu_k^\top\Sigma^{-1}\mu_k +\log\pi_k

定义:

wk=Σ1μkRDw_k=\Sigma^{-1}\mu_k\in\mathbb{R}^{D} bk=12μkΣ1μk+logπkRb_k=-\frac12\mu_k^\top\Sigma^{-1}\mu_k+\log\pi_k\in\mathbb{R}

便得到熟悉的线性分数:

δk(x)=wkx+bk\delta_k(x)=w_k^\top x+b_k

任意两个类别 a,ba,b 的边界满足 δa(x)=δb(x)\delta_a(x)=\delta_b(x),也就是:

(wawb)x+(babb)=0(w_a-w_b)^\top x+(b_a-b_b)=0

这是一条超平面。LDA 的“线性”不是先拍脑袋规定边界必须直,而是共享协方差使所有类别共同的输入二次项在后验比较中抵消。

05 用两个二维高斯手算边界#

设两个类别参数已经由训练数据估计为:

μ0=[00],μ1=[21],Σ=I2,qquadπ0=π1=12\mu_0=\begin{bmatrix}0\\0\end{bmatrix},\qquad \mu_1=\begin{bmatrix}2\\1\end{bmatrix},\qquad \Sigma=I_2,qquad \pi_0=\pi_1=\frac12

因为 Σ1=I2\Sigma^{-1}=I_2

δ0(x)=log12\delta_0(x)=\log\frac12 δ1(x)=2x1+x212(22+12)+log12=2x1+x22.5+log12\delta_1(x)=2x_1+x_2-\frac12(2^2+1^2)+\log\frac12 =2x_1+x_2-2.5+\log\frac12

令两分数相等,公共先验抵消,决策边界为:

2x1+x2=2.52x_1+x_2=2.5

对查询点 x=[1,1]x=[1,1]^\top,两类分数差为:

δ1(x)δ0(x)=2×1+12.5=0.5\delta_1(x)-\delta_0(x)=2\times1+1-2.5=0.5

因此预测类别 1。二分类时,将两个联合分数归一化等价于对差值做 Sigmoid:

P(y=1x)=σ(0.5)0.622P(y=1\mid x)=\sigma(0.5)\approx0.622
交互手算:类别 1 先验降为 0.2 会怎样?

先验差变为 log0.2log0.8=log0.251.386\log0.2-\log0.8=\log0.25\approx-1.386。查询点原来的分数差 0.5 变为 0.886-0.886,预测翻转为类别 0。先验改变只平移边界,不改变法向量 [2,1][2,1]

06 均值、先验和共享协方差怎样从数据得到?#

类别 kkNkN_k 个样本,样本集合记为 CkC_k。均值与先验的自然估计是:

μ^k=1Nki:yi=kxi,qquadπ^k=NkN\hat\mu_k=\frac{1}{N_k}\sum_{i:y_i=k}x_i,qquad \hat\pi_k=\frac{N_k}{N}

每类先计算围绕本类均值的残差,再把全部类内散布汇总为共享协方差:

Σ^=1Nk=1Ki:yi=k(xiμ^k)(xiμ^k)\hat\Sigma= \frac{1}{N}\sum_{k=1}^{K}\sum_{i:y_i=k} (x_i-\hat\mu_k)(x_i-\hat\mu_k)^\top

这里写的是有偏最大似然形式,分母为 NN;不同统计语境可能使用自由度修正。scikit-learn 的 covariance_ 是按类别先验加权的类内协方差,并使用有偏估计。不要把它与围绕全局均值计算的总协方差混淆,否则类间均值差会被错误计入类内噪声。

训练与推理的数据流是:

07 不显式求逆:用 NumPy 写出 LDA 核心#

公式里出现 Σ1\Sigma^{-1},实现时不应先计算完整逆矩阵。求解线性方程 Σwk=μk\Sigma w_k=\mu_k 通常更稳定:

这段代码的 ridge 只用于展示数值保护,不是完整的 shrinkage 选择策略。生产中应在训练数据内部用交叉验证评估正则化,而不是遇到报错就不断增大常数。

08 用 scikit-learn 1.9 正确选择求解器#

当前 LinearDiscriminantAnalysis 提供 svdlsqreigen 三种求解器。先用默认 svd 建立基线:

三种求解器不是单纯的速度开关:

求解器是否显式依赖协方差shrinkagetransform适用提醒
svd默认不显式计算不支持支持默认选择;高维时避免显式协方差
lsqr计算并求解线性系统支持不支持只做分类,可配 shrinkage='auto'
eigen显式计算支持支持可分类和投影,但高维协方差成本高

当特征数接近或超过样本数,经验协方差会不稳定。可比较:

regularized_pipeline = make_pipeline(
    StandardScaler(),
    LinearDiscriminantAnalysis(
        solver='lsqr',
        shrinkage='auto',
    ),
)
python

shrinkage 只支持 lsqreigen;若传 covariance_estimator,必须让 shrinkage=None,两者不能同时控制协方差。n_components 只影响 transform 的监督降维输出,不会改变 fitpredict 的分类结果。

09 怎样诊断协方差而不是只看准确率?#

先用分层交叉验证评价模型,再检查训练状态:

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
result = cross_validate(
    regularized_pipeline,
    X,
    y,
    cv=cv,
    scoring=['balanced_accuracy', 'neg_log_loss'],
    return_train_score=True,
)
python

遇到不稳定或概率极端时,按下面顺序排查:

  1. 检查每类样本数。 某类样本少于特征维数时,均值与协方差方向都很不可靠;
  2. 检查秩与条件数。 重复列、线性组合列和常数列会使协方差奇异或病态;
  3. 画每类均值与协方差椭圆。 若各类椭圆方向、长短明显不同,共享协方差假设可能不成立;
  4. 比较训练与验证概率。 极端分数但验证对数损失很差,常提示协方差估计或分布假设有问题;
  5. 核对 classes_ predict_proba 列顺序按类别排序,二分类 decision_functionclasses_[1]classes_[0] 的对数后验差。

最小数值检查:

assert np.isfinite(model.means_).all()
assert np.isfinite(model.priors_).all()
assert np.isclose(model.priors_.sum(), 1.0)
assert np.allclose(model.covariance_, model.covariance_.T)
print('covariance eigenvalues:', np.linalg.eigvalsh(model.covariance_))
python

10 常见错误与失败场景#

  1. 把总协方差当成类内协方差。 必须先减各自类均值,不能只减全局均值;
  2. 显式调用 np.linalg.inv 直接解线性方程或使用稳定求解器,避免放大数值误差;
  3. 在全数据上标准化后交叉验证。 预处理必须置于 Pipeline,防止验证折泄漏;
  4. 高维小样本仍使用未经收缩的经验协方差。 比较 svd 与带 shrinkage 的 lsqr/eigen
  5. 盲信高斯后验概率。 异常值、重尾、类别混合和分布漂移都会让概率失真;
  6. 把 LDA 投影当成无监督降维。 它使用标签寻找判别方向,不能在划分前对全数据拟合;
  7. 把系数解释为因果作用。 wk=Σ1μkw_k=\Sigma^{-1}\mu_k 同时受相关特征与量纲影响,只表达模型中的分类关联。

共享协方差假设尤其容易在以下情况失败:不同类别数据云形状明显不同;每类本身由多个簇组成;边界是同心圆或 XOR;存在强异常值;部署数据的均值、协方差或类别先验发生变化。

11 LDA、QDA、高斯 NB 和逻辑回归的边界#

方法类条件分布协方差假设边界参数与数据需求
高斯 NB每类高斯每类对角协方差通常二次、轴对齐参数少,忽略相关性
LDA每类高斯各类共享完整协方差线性共享信息,较省样本
QDA每类高斯每类独立完整协方差二次更灵活,也更需样本
逻辑回归不建模 p(xy)p(x\mid y)无高斯要求线性直接优化条件似然

LDA 与逻辑回归都能产生线性边界,但来源不同。LDA 假设每类特征高斯且共享协方差,在假设接近真实且样本不多时可高效利用结构;逻辑回归只规定后验对数几率线性,分布假设更弱,数据足够时通常更稳健。

LDA 还有监督降维用途:transform 最多输出 min(K1,D)\min(K-1,D) 个方向,因为 KK 个类中心在中心化后最多张成 K1K-1 维。PCA 则完全不看标签,只保留总体方差最大的方向;高方差方向未必最能分开类别。本文的核心仍是分类器,不要把两种用途混成同一件事。

12 今天真正需要记住什么?#

  1. LDA 为每个类别拟合一个均值,并让所有类别共享完整类内协方差;
  2. 马氏距离用协方差修正尺度和相关性,相当于在白化空间比较类中心;
  3. 共享协方差让后验中的 xΣ1xx^\top\Sigma^{-1}x 对所有类别相同并消去,因此边界是线性的;
  4. 实现时求解 Σwk=μk\Sigma w_k=\mu_k,不要先显式求逆;高维小样本应评估稳定求解与 shrinkage;
  5. n_components 只控制监督投影,不改变分类拟合与预测,验证时仍要防止标签泄漏。

13 思考题与小练习#

练习 1:写出不等先验下的新边界

在手算例中令 π0=0.8,π1=0.2\pi_0=0.8,\pi_1=0.2。边界满足 2x1+x22.5+log(0.2/0.8)=02x_1+x_2-2.5+\log(0.2/0.8)=0,即 2x1+x23.8862x_1+x_2\approx3.886。稀有的类别 1 需要更强特征证据才会被预测。

练习 2:共享协方差为什么消二次项?

分别展开 δ1(x)\delta_1(x)δ0(x)\delta_0(x),相减后标出 12xΣ1x-\frac12x^\top\Sigma^{-1}x。它在两式中完全相同;若改为 Σ1\Sigma_1Σ0\Sigma_0,两项不能抵消,边界便保留二次项。

练习 3:制造病态协方差

在代码中新增第三列 x3=x1+x2,计算协方差特征值与条件数。比较未经正则化的手写求解、solver='svd'solver='lsqr', shrinkage='auto',解释完全线性相关为何导致零特征值。

相关工作#

14 下一篇预告#

到这里,经典监督学习已经从线性概率、局部邻域、树集成、最大间隔推进到生成式判别。下一步将移除标签:只有一堆样本而没有类别答案时,怎样自动寻找代表性中心?下一篇进入无监督学习,从 K 均值的“分配—更新”循环与失败几何开始。

两团高斯数据为何产生直线边界?从共享协方差到 LDA
https://zwjcode.cn/blog/lda-shared-covariance-linear-boundary
作者
发布于 2026年8月25日
版权协议 CC BY-NC-SA 4.0
评论加载似乎遇到了问题,请尝试刷新页面。