MCM520 ← 资料站首页 2017C「颜色与物质判别」(一):判别分析与可视化——降维与线性判别 打开交互阅读器 →

2017C「颜色与物质判别」(一):判别分析与可视化——降维与线性判别

1 问题重述

2017 年国赛 C 题「颜色与物质判别」的核心任务是:给定物质的颜色特征数据,建立由"颜色特征"到"物质类别"的判别模型,并能在存在异常样本时识别之。在工程与质检场景中,这对应着"凭光谱/成像颜色快速区分材料种类""在来料中挑出成分可疑的批次"等典型需求。题目本质上是一个有监督的二分类判别问题,同时隐含了对异常与不确定样本的检测要求。

本题附件给出每条样本在 6 个颜色通道(记为特征 f1–f6)上的观测值,以及该样本所属的物质类别标签(0 或 1 两类)。完整建模应回答三个递进问题:(一)两类物质在颜色特征上到底可不可分、怎么分(本篇);(二)用哪些分类器、如何选型最稳妥(下一篇);(三)如何应对测量异常与噪声扰动(再下一篇)。

我们构造了与赛题结构一致的合成数据集(详见附录):共 200 条样本,每条含 6 个颜色特征,类别标签为 0/1(分别 106 / 94 条),并人为注入了 12 条远离两类主体的离群异常(用于第三篇的异常检测)。全部数据在纯 Python 下确定性生成,所有数字不依赖任何外部数值库,保证图、正文、附录、工具四处完全一致。

图1

2 数据特征与描述性统计

图1 给出了两类物质在 6 个颜色特征上的均值对比。肉眼可见两类并非在所有特征上都分得很开:例如 f1、f5 上两类均值接近,而 f3、f4、f6 上则出现明显错位。这说明类别信息分散在多个特征中,且各特征的判别力强弱不一——这正是判别分析要量化的对象。

数据集的基本结构如下:样本量 n=200n=200,特征维 d=6d=6,两类规模接近均衡(106 对 94,比例约 0.53 : 0.47),不存在严重的类别不平衡,因此后续分类器可直接以"准确率"为主指标,不必过度依赖加权或重采样。

在正式建模前,必须先做特征标准化(z-score):对每个特征减去训练集均值、除以训练集标准差,使 6 个通道处于同一量纲。这一步看似平凡,却至关重要——PCA 的协方差矩阵、LDA 的 SW−1(μ1−μ0)S_W^{-1}(\mu_1-\mu_0) 方向、逻辑回归的系数尺度,全部依赖"各特征方差可比"。若不标准化,f3、f6 这类数值较大的通道会"霸占"投影方向,掩盖真正有判别力的弱信号。本数据各特征原始量纲本就接近,标准化后仍保持相对秩序,因此结论对是否标准化不敏感,但这恰恰说明"养成标准化的习惯"在任何判别任务里都值得。

此外,训练/测试按 7:3 分层切分(140 训练、60 测试),保证两类在两端比例一致,避免某一折恰好抽到类别失衡的样本而误导评估。

3 特征层面的类别可分性

在降维之前,先在原始特征空间做初步判别力评估。对每个特征计算两类均值的"标准化差异"(Cohen's dd,即 ∣μ1−μ0∣|\mu_1-\mu_0| 除以合并标准差),数值越大表示该特征越能区分两类。由数据可得各特征的判别强度为:

特征 f1 f2 f3 f4 f5 f6
判别强度 dd 0.49 1.35 1.75 1.46 0.18 1.84

f6(d=1.84d=1.84)与 f3(d=1.75d=1.75)是最强的区分特征,f5(d=0.18d=0.18)几乎不携带类别信息,而 f2、f4 居中。这一排序将在第二篇通过逻辑回归系数与决策树增益得到交叉验证——多方法给出一致的特征重要性,是结论可信的标志。

4 PCA 降维与二维可视化

6 维特征直接画图困难,先用主成分分析(PCA)压缩到 2 维观察整体结构。PCA 在标准化后的特征协方差矩阵上做特征值分解(幂迭代取前两大特征向量,纯 Python 实现)。结果如图3:前两个主成分分别解释 35.5% 与 17.4% 的方差,累计 52.9%;后续 PC3–PC6 各约 10%–15%,衰减平缓,说明数据没有单一主导方向,而是沿多个特征"摊开"。

图3

图2 把每条测试样本投影到 PC1–PC2 平面并按真实类别着色(蓝=类0,红=类1)。可见两类在二维平面上确实分成了两团,但边界处明显交叠——这正是"可判别但非完全可分"的直观体现。交叠区样本正是后续分类器最易判错的部分,也暗示单靠线性投影难以彻底分开,需要更丰富的分类器来刻画边界。

图2

进一步看主成分到底"由哪些特征构成"。PC1 的载荷向量为 v1=(−0.24, 0.47, −0.50, 0.46, −0.06, 0.50)\mathbf v_1=(-0.24,\ 0.47,\ -0.50,\ 0.46,\ -0.06,\ 0.50),在 f2、f3、f4、f6 上权重最大且 f3 取负——说明 PC1 主要刻画"以 f3 为反号、f2/f4/f6 为同号"的综合颜色对比;PC2 的载荷 v2=(0.30, 0.29, −0.01, −0.15, 0.89, 0.10)\mathbf v_2=(0.30,\ 0.29,\ -0.01,\ -0.15,\ 0.89,\ 0.10) 则几乎只由 f5 主导(权重 0.89)。这意味着:f5 是一条相对独立、信息量不大的"边路"特征(它单特征判别力 d=0.18d=0.18 也最低),而真正区分两类的 f3/f4/f6 被摊进了 PC1 与更高阶主成分中。若想用 PCA 做"降维后再分类",单取前 2 主成分只能保留约 53% 的方差,把 f3/f4/f6 的判别信息切散了;要覆盖到 80% 方差需取前 4 个主成分(PC1–PC4 累计 0.355+0.174+0.154+0.115=0.7980.355+0.174+0.154+0.115=0.798)。这再次说明——PCA 适合看结构,不适合直接当分类器用,分类还是应交还给 LDA 或专门的分类算法。

5 线性判别分析(LDA):最大化类间可分性

PCA 是无监督的"方差最大化",并不关心类别标签;要直接为分类服务,应使用线性判别分析(LDA)。LDA 求解投影方向 w\mathbf w,使投影后类间散度与类内散度之比(Fisher 准则 J=w⊤SBw/w⊤SWwJ=\mathbf w^\top S_B\mathbf w/\mathbf w^\top S_W\mathbf w)最大。在两类情形下存在闭式解 w∝SW−1(μ1−μ0)\mathbf w\propto S_W^{-1}(\boldsymbol\mu_1-\boldsymbol\mu_0),其中 SWS_W 为类内散度矩阵(需对其求逆,纯 Python 用高斯消元实现)。

本数据下求得 Fisher 准则值 J=0.094J=0.094。Fisher 值本身大小依赖于特征尺度,其意义在于确认存在一个使两类显著拉开的方向。图5 给出了该判别向量的 6 个分量(权重),其符号与相对大小刻画了"沿哪个特征组合走,两类分得最开",可与第3节的单特征判别强度相互印证。

图5

将全部测试样本投影到该一维方向并按类别绘制直方图(图4),两类各自聚成一个峰、中间仅有少量重叠,说明 LDA 方向有效地把"类别信号"提炼了出来。图中虚线位置即分类阈值(取两类投影均值的中点)。

图4

6 逻辑回归基线模型

为给出可解释的判别方程与概率输出,建立逻辑回归基线:

P(y=1∣x)=σ(w0+w1f1+⋯+w6f6),σ(z)=11+e−zP(y=1\mid\mathbf x)=\sigma\big(w_0+w_1f_1+\cdots+w_6f_6\big),\qquad \sigma(z)=\frac{1}{1+e^{-z}}

在标准化特征上用梯度下降估计系数(学习率 0.3,4000 步),再还原到原始量纲,得

logit=−0.280−1.815 f1+2.491 f2−4.798 f3+3.059 f4−0.810 f5+3.969 f6\text{logit}= -0.280 -1.815\,f_1 +2.491\,f_2 -4.798\,f_3 +3.059\,f_4 -0.810\,f_5 +3.969\,f_6

图6 展示了截距与 6 个特征系数。系数绝对值越大,该特征对判别的贡献越突出:f3(2.492.49→取绝对值 4.804.80 经标准化后最显著)、f6(3.973.97)、f4(3.063.06)贡献最大,f5(0.810.81)最小——与第3节 Cohen's dd 的排序(f6>f3>f4>f5)高度一致,进一步坐实了"特征判别力"的结论。

图6

逻辑回归系数的可解释性远不止"谁大谁小"。以 f3 为例,其系数 −4.798-4.798 意味着:在其它特征保持不变时,f3 每升高 1 个标准化单位,对数几率下降 4.798,对应胜率(odds)变为原来的 e−4.798≈0.008e^{-4.798}\approx 0.008——即 f3 越大,样本越不可能是类1。把 6 个系数读成"每个颜色通道对'属于类1'的投票权重",模型的判别逻辑一目了然,这是黑箱模型难以提供的。也正因如此,当业务方问"为什么判它是类1",逻辑回归能直接给出逐特征的解释,而不仅是抛出一个标签。

逻辑回归在测试集上给出 ROC 曲线(图8),曲线下面积 AUC = 0.959,表明该线性模型已具备很强的排序判别力。AUC 的含义是:随机抽一个类1样本和一个类0样本,模型把类1排在前面的概率高达 95.9%——即便不卡阈值,仅靠"打分高低"就能极好地分开两类。图7 是 LDA 的测试集混淆矩阵:真类0共 32 例判对 29、错 3;真类1共 28 例判对 26、错 2,整体错分仅 5 例,测试准确率约 91.7%。两种线性模型相互印证:本题的判别信号充足,线性方法已是高性价比的基线选择。

逻辑回归在测试集上给出 ROC 曲线(图8),曲线下面积 AUC = 0.959,表明该线性模型已具备很强的排序判别力。图7 是 LDA 的测试集混淆矩阵:真类0共 32 例判对 29、错 3;真类1共 28 例判对 26、错 2,整体错分仅 5 例。

图7

图8

7 小结与向下一篇的铺垫

本篇完成了"判别分析与可视化"这一基础环节,得到三点结论:(1)两类物质在 6 维颜色特征上可判别但存在交叠,非完全线性可分;(2)判别信息主要由 f3、f4、f6 承载,f5 几乎无用;(3)即使最简单的 LDA/逻辑回归,也能取得约 0.92–0.96 的 AUC,说明问题本身"信号充足"。

值得进一步讨论的是"降维"与"判别"的目标差异。PCA 图(图2、图3)告诉我们"数据朝哪些方向展开最多",那是为了观察结构、压缩维度、便于画图;LDA 图(图4、图5)则直接为分类服务,把"类间散度拉大、类内散度压小"。两者方向并不相同:PCA 第一主成分 v1=(-0.24, 0.47, -0.50, 0.46, -0.06, 0.50) 偏向 f2/f3/f4/f6 的方差最大组合,而 LDA 判别向量 w=(-0.11, 0.36, -0.53, 0.47, -0.08, 0.59) 更强调 f3/f4/f6 的类别差异。这种"方差最大方向"与"判别最强方向"的错位,正是降维可视化里最容易误导新手的地方——看着 PC1 上两类有重叠,不代表模型分不了;真正为分类优化的方向可能藏在 PC2 甚至更后面。因此本篇坚持"先可视化看结构、再用 LDA 做判别"的分两步策略,避免被 PCA 的视角带偏。

线性模型对交叠边界的刻画能力毕竟有限,且"哪个分类器最适合本题"尚未回答。下一篇将系统地比较逻辑回归、LDA、KNN、朴素贝叶斯、决策树五种分类器,用交叉验证与多指标选型,并确定最终推荐模型;在此基础上,第三篇再探讨异常检测与噪声鲁棒性,把"实用判别系统"的拼图补完整。

8 与原赛题的对应及方法局限

本篇的"6 个颜色特征→两类物质标签"对应原题中"由颜色/光谱数据判别物质种类";PCA/LDA 可视化对应原题"对数据做降维观察结构"的隐含要求。需说明两点简化:其一,真实赛题中物质类别可能多于两类、且颜色与浓度的关系常呈连续非线性,本合成数据取二分类以便把"判别—选型—异常"三条主线讲透;其二,LDA 要求各类协方差近似同质,若实际各类散布形态差异很大(异方差),应改用二次判别或核方法,但本数据的线性结构下线性判别已足够且更易解释。这些简化不影响"先看清结构、再选型、最后防异常"这一主线,也为后两篇提供了干净可控的实验台。

附录:可复现的 Python 实现

以下代码在 mcm520-site/tools/ 目录下运行,调用唯一数据真源 gen_data.gen_2017c() 复现本篇全部结论。

# 在 mcm520-site/tools/ 目录下运行:python3 this_script.py
import gen_data as GD

D = GD.gen_2017c()                       # 确定性合成数据 + 全部权威数字
n, d, nanom = D["n"], D["n_feat"], D["n_anom"]     # 200 / 6 / 12
cls = D["cls_counts"]                    # {0: 106, 1: 94}
print("样本数=%d 特征维=%d 注入异常=%d 类别=%s" % (n, d, nanom, cls))

# PCA 方差解释(前 2 主成分累计)
pv = D["pca_var_norm"]
print("PC方差解释:", [round(v, 3) for v in pv])
print("前2主成分累计=%.3f" % (pv[0] + pv[1]))    # 0.529

# Fisher 线性判别准则
print("Fisher准则 J=%.3f" % D["fisher_J"])        # 0.094
print("LDA判别向量:", [round(v, 3) for v in D["wlda"]])
print("LDA分类阈值=%.3f" % D["lda_thr"])

# 单特征判别强度 Cohen's d
print("特征判别强度 d:", [round(v, 3) for v in D["feat_sep"]])

# 逻辑回归系数(原始量纲)
print("LR系数(截距+6特征):", [round(v, 3) for v in D["lr_coef"]])

# 逻辑回归 ROC / AUC 与 LDA 混淆矩阵
print("LR测试集 AUC=%.3f" % D["model_metrics"]["LR"]["auc"])   # 0.959
print("LDA混淆矩阵(行=真,列=预测):", D["lda_cm"])             # [[29,3],[2,26]]
print("LDA测试集准确率=%.3f" % D["model_metrics"]["LDA"]["acc"])