本节摘要:机理不明、变量几百、类别标签在手——这是机器学习的主场。但光谱机器学习的胜负手不在分类器,在特征工程:二阶导加 SNV 加降维仍是标配管线。随机森林附赠波段重要性排名,多仪器数据融合要看信息量而不是列数。二维相关光谱与光谱模拟作为两个高阶视角一并交代。
解卷积对付"峰熔在一起",机器学习对付"说不清机理"。设想委托:九十个样品分三类(两个厂家正品、一批掺假),掺假物在 1550 处只有一个弱特征峰,正品两厂的峰位只差 10 个波数——没有清晰的化学模型可写,只能让算法从数据里学判据。本节是第 3 章 PCA 与 PLS 思想的延伸:无监督看结构,有监督学边界,再加数据融合与两个高阶视角。
光谱喂给分类器之前,第 2 章的整备一样不能省——散射用 SNV 压、肩峰用二阶导顶出来、维度用 PCA 截住。分类器(支持向量机、随机森林)只是管线末端的一环,换了分类器常常只挪一两个百分点,换了特征工程能挪十几个。完整跑一遍:
# 光谱分类管线:整备 + 降维 + 三种分类器对决 import numpy as np from sklearn.svm import SVC from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import cross_val_score, StratifiedKFold from scipy.signal import savgol_filter rng = np.random.default_rng(23) x = np.linspace(900, 1700, 300) def snv(v): return (v - v.mean())/v.std() classes = { 'A厂': [(1050,0.8,20),(1250,0.5,25),(1500,0.2,30)], 'B厂': [(1060,0.75,22),(1255,0.52,24),(1500,0.2,30)], # 峰位差 10 '掺假': [(1050,0.8,20),(1250,0.5,25),(1500,0.2,30),(1550,0.14,15)], # 弱特征峰 } def sample(cls, n): out = [] for _ in range(n): s = sum(a*np.exp(-0.5*((x-n0)/w)**2) for n0, a, w in classes[cls]) out.append(s*(1+rng.normal(0,0.06)) + rng.normal(0,0.05, x.size)) return np.array(out) X = np.vstack([sample(c, 30) for c in classes]) y = np.repeat(list(classes), 30) Xd = np.apply_along_axis(lambda r: savgol_filter(r, 21, 3, deriv=2), 1, X) # 二阶导 Xs = np.apply_along_axis(snv, 1, Xd) # SNV Xc = Xs - Xs.mean(0) U, S, Vt = np.linalg.svd(Xc, full_matrices=False) T5 = U[:, :5]*S[:5] # 前 5 个得分 cv = StratifiedKFold(5, shuffle=True, random_state=1) for name, clf in [('线性SVM', SVC(kernel='linear')), ('RBF核SVM', SVC(kernel='rbf')), ('随机森林', RandomForestClassifier(n_estimators=200, random_state=1))]: acc = cross_val_score(clf, T5, y, cv=cv).mean() print(f"{name:8s} 5折CV准确率={acc:.3f}")
输出:
线性SVM 5折CV准确率=0.989 RBF核SVM 5折CV准确率=0.978 随机森林 5折CV准确率=1.000
三行输出先给一个反直觉:线性核击败了 RBF 核(0.989 对 0.978)。光谱类间的差异主要沿特征方向线性分布(峰高有或没有、位置偏一点),线性边界足够,RBF 的额外自由度反而让交叉验证多跌一跤。随机森林满分登顶,靠的是它对弱特征峰的贪心搜索——决策树每层都在找"最能分"的分裂点,1550 那个 0.14 的小峰被它反复利用。别默认"核越花哨越好",也别迷信森林——样本再少一半,它的满分就会变成过拟合。
流程图末端"波段重要性复核"这一步常被省略,但它恰恰是把机器学习从黑箱拉回化学的关键动作。
树模型训练完顺手输出每个变量的重要性分数(按分裂增益累计)。直接用原始谱喂它:
# 波段重要性:弱特征峰被自动顶到榜首 rf = RandomForestClassifier(n_estimators=200, random_state=1).fit(X, y) top = np.argsort(rf.feature_importances_)[-3:][::-1] print("随机森林波段重要性 top3:", np.round(x[top]).astype(int), "(掺假特征峰真值 1550)")
输出:
随机森林波段重要性 top3: [1553 1550 1558] (掺假特征峰真值 1550)
重要性前三名落在 1553、1550、1558——正好把掺假特征峰 1550 夹在中间,与特征工程无关、纯靠数据自己找出来的。这个赠品的用法是双向的:落点若对应已知官能团,模型的可信度大增(本例即是);落点若在毫不相干的噪声区,几乎可以断定模型在拟合伪相关——训练集的装样顺序或批次混杂泄漏了标签信息。第 6 章会诊"可解释性"问题时,这张排名表就是第一证物。
一台仪器答不完的问题(拉曼看骨架、红外看官能团、近红外看整体成分),把多源数据拼起来建模——数据融合。三个层级要分清:低级融合把各源特征拼成一个大向量再建模;中级融合各源先各自提取潜变量再融合;高级融合各源各建模型、结论层投票。低级最常用也最常被误用,做个实验看清它的脾气——给"仪器二"(只有均值、方差、局部积分三个统计量,掺假类带 8% 系统偏移)一次机会:
# 低级融合:弱信息源有没有用 feat2 = np.c_[X.mean(1), X.std(1), X[:, 140:170].sum(1)] n_au = (y == '掺假').sum() feat2[y == '掺假'] *= 1.08 + rng.normal(0, 0.05, n_au)[:, None] feat2 = np.apply_along_axis(snv, 1, feat2) def acc_of(F): return cross_val_score(SVC(kernel='rbf'), F, y, cv=cv).mean() print(f"RBF-SVM 仅仪器二统计量: {acc_of(feat2):.3f}") print(f"RBF-SVM 仅光谱得分: {acc_of(T5):.3f}") print(f"RBF-SVM 低级融合: {acc_of(np.hstack([T5, feat2])):.3f}")
输出:
RBF-SVM 仅仪器二统计量: 0.511 RBF-SVM 仅光谱得分: 0.978 RBF-SVM 低级融合: 0.989
仪器二单独只有 0.511(勉强高于瞎猜的三分之一),但拼进光谱特征后把 RBF 核从 0.978 抬到 0.989——它贡献的不是自身精度,是与光谱正交的那一点独立信息。反过来记住失败模式:若第二信息源只有噪声(系统偏移为零),融合的结果就停在原位,甚至因为维度增加、样本相对变稀而略跌。融合前先问第二源有没有独立信息,而不是先拼了再说。中级融合(各源先各自 PLS 提潜变量再拼)在两源量纲与维度悬殊时更稳,高级融合(结论投票)在单源各自够强时最省心。
二维相关光谱(2D-COS):对样品施加一个渐进扰动(温度、浓度、时间),把系列谱整理成同步谱与异步谱——同步谱的交叉峰告诉你哪些波段同涨同跌(同属一个组分或协同变化),异步谱的交叉峰告诉你谁先谁后(变化次序)。它把"一维指纹"扩展成"二维地图",重叠峰在二维里常常自动分开,蛋白质变性与聚合物相变的机理研究都靠它。光谱模拟:从量子化学计算或振动频率表正向模拟谱(算出峰位与强度再卷上线形),与实测谱对照——正向模拟是"从分子到谱",本教程一路都在做"从谱到分子",两个方向互相校核,峰归属才有铁证。这两条路在实录案例里点到即止,深入属于专业方向。
💡 关键直觉:机器学习模型的可信度不在准确率里,在"它学到的判据能不能被化学复述"。波段重要性落在已知峰上,模型才是化学模型;落在噪声区,它只是记住了你装样的顺序。
高阶武器库到此清点完毕。下一章进入案例实录:食用油掺假、药品真伪、高光谱病虫害三场演习,把前四章的每件工具都拉到现场用一遍。