本节摘要:预处理菜单三五年也试不完——把调参变成实验设计:散射校正(无、SNV、MSC)乘窗口(7、15、31)乘导数阶(0、2)共十八种组合进网格,RMSECV 当唯一裁判。最优组合恰好印证 2.6 节"导数必须配宽窗口"的理论;学习曲线裁定样本量;分辨率、可解释性、小样本三个附带疑难收口。
复诊登记簿的最后一页是新手分析员的留言:"预处理方法排列组合几十种,文献里各家用的都不一样,我按感觉选了一套,被质询时说不出为什么。"这不是他一个人的病——化学计量学的调参自由度大得出了名,没有裁判的调参就是高级掷骰子。本节给出一套把感觉换成数字的流程,顺带把分辨率不足、可解释性、小样本三个常见疑难一次收口。
实验设计很简单:把三个最要紧的旋钮摆进网格——散射校正(无、SNV、MSC)、SG 窗口(7、15、31)、导数阶(0 阶只平滑、2 阶求导),共十八种组合;每种组合跑同一个五折交叉验证 PLS,记 RMSECV。裁判只有一个,排序说话:
# 十八种组合的网格搜索(数据含 12% 乘性散射、加性偏移、随机倾斜) import numpy as np from scipy.signal import savgol_filter rng = np.random.default_rng(99) p = 200 x = np.linspace(1100, 1250, p) s_ana = 0.8*np.exp(-0.5*((x-1180)/12)**2) # 待测组分 s_int = 0.5*np.exp(-0.5*((x-1210)/20)**2) # 干扰物 s_bg = 1.0*np.exp(-0.5*((x-1140)/40)**2) # 强背景带 tilt = (x - x.mean())/np.ptp(x) n = 50 c = rng.uniform(0, 10, n) ci = rng.uniform(2, 6, n) X = np.array([(1+rng.normal(0,0.12))*(0.15*c[i]*s_ana + 0.5*ci[i]*s_int + 2.0*s_bg) + 0.3 + rng.normal(0, 0.06) + rng.normal(0, 0.3)*tilt + rng.normal(0, 0.02, p) for i in range(n)]) def snv(M): return (M - M.mean(1, keepdims=True))/M.std(1, keepdims=True) def msc(M): ref = M.mean(0) out = np.zeros_like(M) for i in range(len(M)): a, b = np.polyfit(ref, M[i], 1) out[i] = (M[i]-b)/a return out def pls_cv(Xin, y, ncomp=2, k=5): # 五折交叉验证(3.3 节实现) idx = rng.permutation(len(y)); folds = np.array_split(idx, k) errs = [] for f in folds: tr = np.setdiff1d(idx, f) mu, my = Xin[tr].mean(0), y[tr].mean() Xc, yc = Xin[tr]-mu, y[tr]-my Wl, Pl, Ql = [], [], [] for _ in range(ncomp): w = Xc.T@yc; w /= np.linalg.norm(w) t = Xc@w; pl = Xc.T@t/(t@t); q = yc@t/(t@t) Xc -= np.outer(t, pl); yc -= q*t Wl.append(w); Pl.append(pl); Ql.append(q) W, P, Q = np.array(Wl).T, np.array(Pl).T, np.array(Ql) pr = my + (Xin[f]-mu) @ W @ np.linalg.inv(P.T@W) @ Q errs.append((pr-y[f])**2) return float(np.sqrt(np.concatenate(errs).mean())) rows = [] for prep in ['none', 'SNV', 'MSC']: X1 = {'none': X.copy(), 'SNV': snv(X), 'MSC': msc(X)}[prep] # 先散射校正 for win in [7, 15, 31]: for order in [0, 2]: Xp = np.apply_along_axis(lambda r: savgol_filter(r, win, 3, deriv=order), 1, X1) rows.append((prep, win, order, pls_cv(Xp, c))) rows.sort(key=lambda r: r[3]) for r in rows[:6]: print(f"prep={r[0]:4s} window={r[1]:2d} deriv={r[2]} RMSECV={r[3]:.4f}") print("...") for r in rows[-2:]: print(f"prep={r[0]:4s} window={r[1]:2d} deriv={r[2]} RMSECV={r[3]:.4f}")
输出:
prep=SNV window=31 deriv=2 RMSECV=0.5408 prep=MSC window=31 deriv=2 RMSECV=0.5539 prep=none window=31 deriv=2 RMSECV=0.5563 prep=none window=15 deriv=0 RMSECV=0.6793 prep=none window= 7 deriv=0 RMSECV=0.6849 prep=SNV window=15 deriv=2 RMSECV=0.6951 ... prep=none window= 7 deriv=2 RMSECV=2.1895 prep=MSC window= 7 deriv=2 RMSECV=2.2345
榜单信息量很大,逐层拆开看。冠军是 SNV 加 31 点窗口加二阶导(0.5408),且窗口 31 加导数 2 霸占前三——这正是 2.6 节的理论预言:二阶导放大噪声,必须配宽窗口压制,窗口 7 配二阶导是最差的组合之一(垫底两名都是它,2.19 与 2.23,比冠军差四倍)。SNV 与 MSC 只差 0.013(0.5408 对 0.5539)——印证 2.5 节"两者高度等价",选哪个看部署便利(MSC 要带参考谱)。"不做散射校正"居然排第三(0.5563,只比冠军差 3%)——PLS 的潜变量自己吸收了一部分散射,这提醒我们预处理不是越多越好,每个工序都要能回答"我在偿还哪笔债"(2 章支柱页的老规矩),答不上来的默认不做。
还有一条纪律藏在排序规则里:组合间 RMSECV 差距小于一成时,任选简单者。0.5408 对 0.5563 的差距就是典型的不值得恋战——省下的复杂度比那 0.015 的误差值钱。
网格搜出的"最优"还有一个隐含前提——五十个样本撑得起十八种组合的比较吗?学习曲线回答样本量问题:取不同规模的子集分别算 RMSECV,重复二十次取均值压抖动:
# 学习曲线:样本量 vs RMSECV(最优组合,20 次重复均值) X1 = snv(X) Xb = np.apply_along_axis(lambda r: savgol_filter(r, 31, 3, deriv=2), 1, X1) for m in [10, 20, 30, 50]: acc = [] for _ in range(20): idx = rng.choice(n, m, replace=False) acc.append(pls_cv(Xb[idx], c[idx])) print(f"n={m:2d} RMSECV={np.mean(acc):.4f}")
输出:
n=10 RMSECV=0.5855 n=20 RMSECV=0.5335 n=30 RMSECV=0.5473 n=50 RMSECV=0.5294
曲线从 0.585 降到 0.53 后躺平:二十个样本之后再加样本收益边际化,五十个样本对这套两成分模型绰绰有余。反向的警示更值钱:如果曲线在 n=50 还在陡降,说明模型"饿"着——此时调参榜单的前后差距(比如冠军与第三名的 0.015)完全淹没在样本量效应里,小样本上调参结论不可信,先补样本再回来搜。这条曲线还有一个免费副产品:躺平点的 RMSECV 就是这套方法的现实误差预期,写进方法文件,比任何文献引用都有说服力。
分辨率不足。两峰间距小于半峰宽(1.2 节判据),先分物理还是数学问题:物理问题(仪器分辨率不够)换高分辨模式(代价:扫描时间长、信噪比降)或换仪器;数学问题(信息其实还在,只是熔在一起)上 4.1 节解卷积。判据很简单:能写出可信的峰形假设就解卷积,写不出就回采集端。
模型可解释性差。PLS 系数像天书、机器学习是黑箱——处方是 4.2 节的波段重要性复核:重要波段落在已知特征峰上,模型可信度大增;落在噪声区,几乎必是伪相关(训练集批次泄漏了标签)。可解释性不是美学要求,是伪相关的探测器。
小样本。五十个以下样本建模,三条铁律:成分数不超过样本数的十分之一(宁欠勿过);每折交叉验证至少留五个样本(折太少方差爆炸);任何"最优参数"都附上重复实验的波动范围(本案学习曲线那招),单次数字在十几个样本上毫无统计意义。根治办法只有一个:扩数据,掺杂不同批次、不同日期、不同装样手的变异——样本的"多样性"比"数量"更值钱。
💡 关键直觉:调参的尽头不是找到"最优组合",而是知道"差距多小就不值得再调"。会诊的收官判断永远是性价比,不是完美。
会诊室清空,登记簿合上。全书从"指纹为什么指纹"开始,到"参数为什么这么选"结束——中间每一步,你现在都有数字可依。