资讯详情

近红外光谱回归分析:深度学习模型对比与实验全解析

📅 2026/10/25 19:35:00 | 华诺云谱 👁 阅读
近红外光谱回归分析:深度学习模型对比与实验全解析
简介一份面向计算机专业学生、用于毕设或课程作业的深度学习教育项目聚焦近红外光谱数据的回归分析。近红外光谱属于非破坏性检测技术项目将深度学习模型应用于成分或性质预测是数据科学领域的典型实践。压缩包共9个文件以8个Python脚本为主辅以1个Markdown说明文档整体大小仅27KB代码结构清晰。脚本中实现了ConvNet、SpectFormer、DeepVit、VitNet等模型并提供对应的迁移学习版本便于对照不同网络结构在光谱回归任务上的表现。已有235人浏览学习。通过阅读这些源码可以掌握数据预处理、模型搭建、训练验证等完整流程也可直接复用或修改模型用于自己的实验或课题适合想快速上手深度学习回归项目的学生参考。1. 近红外光谱数据回归分析模型这套代码包不是黑匣子是一组可复现的对照实验拿到「基于深度学习的近红外光谱数据回归分析模型」这套代码包时我第一反应是又一个调参模板拆完才觉得它最值钱的不是某一个模型有多先进而是同一份数据上同时给了四条结构路线——ConvNet、VitNet、DeepVit、SpectFormer——每条还带一个带 TL 后缀的迁移学习变体。近红外光谱回归任务正好卡在几个老问题上波长点太多、样本量太少、化学吸收带相互重叠单靠一个网络很难把「为什么这样设计」讲清楚。这套结构把最常见的解题路径一次铺开了。适合两种人一种是做计算机类毕设、课程设计需要完整实验对比的同学另一种是手里已有光谱数据、想快速评估不同网络上限在哪的工程师。文件列表很干净每个网络结构一个 py 文件带 TL 的变体单独拆开README.md 负责串联入口按顺序跑就能复现。2. 从近红外光谱 CSV 到可训练张量预处理顺序、划分策略与回归指标近红外光谱的输入形式和图像差别很大。图像是二维空间信息光谱是一条一维曲线横轴是波长常见 780–2500 nm纵轴是吸光度或反射率。拿到手的数据常见格式是 CSV 表格存所有样本每行一个样本第一列是目标值水分、蛋白、某成分含量后面几百上千列是波长点的吸光度也有转置存放的。第一步不是忙着建模而是确认数据的存储方向、缺失值情况和量纲是否一致。近红外吸收主要来自 O-H、N-H、C-H 等含氢基团的倍频与合频振动谱带重叠严重这决定了后面必须做平滑和导数处理否则模型很容易被基线漂移和高频噪声带偏。2.1 把数据读进来每行一个样本字段名就是波长点先用 pandas 把 CSV 读进来打印 shape 和缺失值。光谱数据经常出现两种情况某一列某个波长点全为 0或者某几行样品吸光度异常偏高后者多半是样品表面状态不同导致的散射差异不能直接删要靠预处理矫正。import pandas as pd import numpy as np # 常见近红外光谱 CSV每行一个样本第一列是目标值其余列是波长点 data pd.read_csv(spectra.csv) print(原始形状:, data.shape) # (样本数, 波长点数 1) print(缺失值统计:\n, data.isna().sum().sum()) # 分离目标值和光谱矩阵 y data.iloc[:, 0].values.astype(float32) # 第一列是浓度/含量 X data.iloc[:, 1:].values.astype(float32) # 其余列是吸光度 # 波长点数量直接决定模型输入维度记下来后面设置网络要用 seq_len X.shape[1] print(f样本数: {X.shape[0]}, 波长点数: {seq_len})这段代码的逻辑是先把数据拆成X和yy是回归目标X是光谱矩阵形状是(N, L)。seq_len后面做 ConvNet 和 Transformer 都要用到一维卷积的输入长度和 ViT 切 patch 的总长都由它决定。注意 dtype 统一转成 float32PyTorch 默认浮点类型就是 float32不转会在 loss 计算时出现类型不匹配的报错。缺失值处理上我一般先看缺失比例低于 1% 直接按列均值填充高于 5% 就查仪器采集记录大概率是样品问题。2.2 预处理三步走SG 平滑、SNV、一阶导数顺序别反近红外光谱预处理是典型的「做对了不出彩做错了全翻车」的环节。最常见的组合是 Savitzky-Golay 平滑 SNV标准正态变换 一阶导数。SG 平滑负责去高频噪声SNV 逐行消除基线漂移和光程差异一阶导数进一步消掉基线平移。顺序不能反先平滑再求导否则噪声会被导数放大。from scipy.signal import savgol_filter # 1) SG 平滑窗口 11、多项式阶数 2对每一条光谱单独做 X_sg savgol_filter(X, window_length11, polyorder2, axis1) # 2) SNV按样本行做消除基线漂移和光程差异 X_snv (X_sg - X_sg.mean(axis1, keepdimsTrue)) / X_sg.std(axis1, keepdimsTrue) # 3) 一阶导数用 SG 在平滑的同时求导窗口可以小一点 X_deriv savgol_filter(X_snv, window_length9, polyorder2, deriv1, axis1) print(预处理后形状:, X_deriv.shape, 均值≈, X_deriv.mean().round(6))逻辑说明三个变换全部作用于axis1也就是对每条光谱内部做计算。window_length控制平滑窗口覆盖的波长点数窗口越大越平滑但会把窄的吸收峰一起抹掉近红外谱峰的半高宽通常在 10–30 nm 左右按采样间隔折算成点数后窗口取 9–15 是安全区。deriv1表示一阶导数如果二阶导数可以消掉基线斜率但对噪声更敏感毕设阶段先用一阶就够。预处理后的均值应该接近 0如果均值还明显偏离说明 SNV 的 axis 选反了大概率是按列做的。这里补一句 C 的角色如果光谱矩阵到了几十万行、几百个波长点这种规模Python 的 pandas 循环会卡在 IO 和逐行运算上用 C 写数据清洗和批量切分会快不少但毕设和课程设计这个数据量Python 的 scipy 和 pandas 足够不需要额外引入 C 模块。2.3 划分数据集近红外数据不建议直接随机切很多人在这一步直接train_test_split(random_state42)这是近红外回归里最容易埋雷的地方。近红外样品往往来自同一批次、同一工艺相邻编号的光谱差异极小随机切分会把非常相似的样本同时分进训练和测试集测试集 R² 虚高到 0.99换个批次数据直接崩。近红外领域的通行做法是按光谱距离划分Kennard-Stone 算法是主流思路是每次选离已选样本最远的样本进训练集让测试集尽可能独立。from sklearn.model_selection import train_test_split from sklearn.metrics import r2_score, mean_squared_error # 快速验证版本先固定随机种子保证实验可复现 X_train, X_test, y_train, y_test train_test_split( X_deriv, y, test_size0.2, random_state42 ) # 正式实验建议用 Kennard-Stone 按光谱距离划分 # 常见做法是用 ks 库或自己实现距离贪婪采样这里给出替代方案 def ks_split(X, test_size0.2): n X.shape[0] n_test int(n * test_size) selected [np.argmin(np.linalg.norm(X - X[0], axis1))] while len(selected) n - n_test: dist np.min(np.linalg.norm(X[:, None, :] - X[selected], axis2), axis1) selected.append(int(np.argmax(dist))) mask np.zeros(n, dtypebool) mask[selected] True return X[~mask], X[mask], y[~mask], y[mask]逻辑说明train_test_split只适合快速摸流程正式跑实验矩阵必须换成 KS 划分或者至少按批次分组。上面的ks_split是 Kennard-Stone 的简化实现np.argmax(dist)每次挑离已选样本最远的光谱进训练集保证测试集是「模型没见过的光谱范围」。这个方法在近红外文献里很常见缺点是计算距离矩阵时内存占用高样本量超过两三万就建议用分批近似。数据划分完后回归指标要同时看三个不要只盯 R²。指标作用近红外回归常用参考R²决定系数看模型解释了多大比例的方差0.95 很稳0.90–0.95 够用RMSEP测试集预测均方根误差看绝对误差越小越好必须结合 y 的量纲看RPDy 的标准差除以 RMSEP3 优秀2.5–3 可用2 谨慎RMSEP 里的 P 指 Prediction专门评价测试集预测比训练集 RMSE 更有说服力。RPD 这个指标在近红外领域几乎是默认要报的它回答的是「模型误差比样品本身的波动小了多少」写论文和答辩时比单纯报 R² 更能堵住追问。注意预处理里的统计量只能从训练集算出再用到测试集上。SNV 本身逐样本独立问题不大但后面如果做标准化均值方差必须来自训练集否则就是数据泄漏指标再好看也站不住。3. ConvNet、VitNet、DeepVit、SpectFormer四个模型家族怎么选TL 后缀到底加了什么文件列表里两组 py 的命名逻辑很清楚ConvNet、VitNet、DeepVit、SpectFormer 是四个基础结构带 TL 后缀的是各自对应的迁移学习变体。TL 基本可以断定是 Transfer Learning 的缩写具体迁移策略和权重来源以 README.md 为准但这个命名本身已经说明了一份完整的对比实验应该怎么组织——同一个结构从头训练 vs 迁移学习各跑一组正好回答答辩时最常被问的「迁移学习在你的任务里到底有没有用」。3.1 ConvNet.py一维卷积基线先把下限做扎实一维卷积是近红外光谱回归最朴素的深度学习方案也是所有对比实验的「地板」。光谱输入从二维图像降成一维序列后Conv1d的卷积核只在波长方向上滑动感受野覆盖相邻的波长点。近红外吸收峰不是孤立点而是一段连续的波段卷积核能直接学习到「峰形 局部波段相关性」这类特征。典型结构是Conv1d → BatchNorm → ReLU → AdaptiveAvgPool1d → Linear卷积核大小取 17 左右对应约 17 个波长点的邻域接近半个吸收峰的宽度。AdaptiveAvgPool1d(1)把整条光谱压成一个向量替代全连接层处理序列长度参数量小、不容易过拟合。这个结构跑出来的结果就是整组实验的基准线Transformer 模型如果连这个下限都打不过问题通常不在结构而在数据预处理或超参。3.2 VitNet.py 与 DeepVit.py把光谱切成 patch 的 Transformer 路线VitNet 的思路是把一维光谱切成长度相等的 patch每个 patch 经过线性映射后加上位置编码再送进 Transformer 编码器。近红外光谱里同一个成分的吸收峰会出现在多个波段相隔很远但相关性很强Transformer 的自注意力机制正好擅长建模这种长程依赖。比如水的 O-H 吸收在 1400 nm 和 1900 nm 附近都有信号纯 CNN 要靠堆层数才能把这两个位置的感受野连起来Transformer 一层注意力就能直接关联。DeepVit 从命名看是 VitNet 的加深加宽版本差异大概率在 Transformer encoder 的层数depth和嵌入维度embed_dim上。这两个模型的对比要回答的问题是在近红外这种中等长度序列上加大模型容量能不能带来实打实的收益还是只是把训练时间变长。光谱数据样本量通常只有几百到几千DeepVit 非常容易过拟合所以它的默认超参、正则化设置反而是这组实验里最有看点的部分。3.3 SpectFormer.pyCNN 局部特征 Transformer 全局依赖的混合SpectFormer 从名字看是 Spect Former 的组合也是我拆这个包时最期待的文件。纯 CNN 的局部感受野高效但视野窄纯 Transformer 的全局注意力强但容易在小样本上过拟合混合结构是这两条路的折中浅层先用一维卷积提取局部峰形特征再把卷积输出的特征图切成 patch 或直接作为 token 序列送进 Transformer。这个设计对近红外光谱是说得通的。卷积层输出的每个位置已经包含了邻近波段的局部信息Transformer 在这些局部特征之上建模跨波段的依赖语义上比直接用原始波长点做注意力更干净。SpectFormer 和 ConvNet 的对比实际是「局部特征 全局关系」和「纯局部特征」的对比SpectFormer 和 VitNet 的对比则是「局部建模是否必要」的对比。三组模型互相参照正好覆盖评审最关心的两个问题结构设计有没有依据、实验有没有对照组。3.4 带 TL 后缀的那一组迁移学习到底迁移了什么四个 TL 文件是这套代码包在毕设答辩时最有价值的部分。近红外深度学习最常见的困境是光谱数据量太少从头训练一个容量稍微大一点的网络就过拟合。迁移学习的常规做法是先在更大规模的数据上预训练再在目标任务上微调。但要注意ImageNet 预训练权重是二维三通道输入近红外光谱是一维输入输入层通道数和序列长度都对不上直接加载预训练权重一定会报 shape mismatch。所以这里的 TL 变体迁移的通常不是 ImageNet 权重本身而是这类结构的「训练策略」——比如冻结浅层、只微调深层或者把同一结构在公开光谱数据上做自监督预训练再微调。具体是哪种以 README.md 的实现为准但你可以把 TL 变体理解成「同一个网络换一种训练方式」而不是「换一个网络」。这张表是我拆完文件后对每个脚本的定位判断文件结构路线在实验矩阵中的角色ConvNet.py1D CNN 基线定下限作为所有模型对比的基准ConvNetTL.pyConvNet 迁移学习看预测练/微调策略对 CNN 是否有提升VitNet.py光谱切 patch 进 Transformer纯 Transformer 路线的基线VitNetTL.pyVitNet TL看迁移学习能否缓解 Transformer 过拟合DeepVit.py加深加宽的 VitNet验证模型容量在光谱数据上的上限DeepVitTL.pyDeepVit TL深度模型与迁移学习是否兼容SpectFormer.pyCNN Transformer 混合局部特征 全局依赖的混合结构SpectFormerTL.pySpectFormer TL混合结构结合迁移学习后的最终效果提示拆这类代码包时最忌直接把文件名当结论。先打开 README.md确认每个 TL 文件里预训练权重的来源和微调策略再决定你的论文里怎么写「迁移学习」这一章。4. 把训练跑起来命令行入口、六个关键超参与三组实验矩阵环境装好后这套代码的入口设计比较直白每个模型一个 py 文件直接python 文件名.py就能启动训练。动手深度学习的人都知道跑通一个骨架半小时排一个环境 bug 一晚上。近红外回归任务用 PyTorch 实现是当前最常见的方案torch 1.13 以上版本对Conv1d和 Transformer Encoder 的支持都很成熟不需要额外装复杂的库。4.1 环境与入口先跑通 README.md再换你自己的数据建议环境是 Python 3.8核心依赖包括 numpy、pandas、scipy、scikit-learn、torch。命令行入口的一般形式如下实际参数名以 README.md 为准但结构基本逃不出这套# 以 SpectFormer 从头训练为例 python SpectFormer.py --data ./data/spectra.csv \ --target column_name \ --epochs 80 --batch_size 32 --lr 1e-3 \ --patch_size 64 --embed_dim 256 --depth 8这个命令的逻辑是--data指定光谱 CSV 路径--target指定目标值列名后面四个是模型的四个核心超参。第一次运行时先把--epochs改小到 10确认数据能读进来、loss 能下降再跑完整训练。这么做的好处是避免数据格式问题在跑了 30 个 epoch 之后才暴露白白浪费时间。GPU 不是必须的。近红外光谱序列长度在几百到一千左右样本量通常不过万这类规模的模型在 CPU 上跑 80 个 epoch 也就是几十分钟到一两个小时。如果你的数据量特别大再考虑租云平台或者用 GPU 实例毕设场景完全不需要一上来就上多卡。4.2 六个关键超参值和含义要对得上调参之前先搞清楚每个参数在这个任务里的物理含义。这六个参数是近红外光谱 Transformer 和 CNN 训练里最常动的优先级从高到低排列如下参数建议范围对光谱回归的影响batch_size16–64光谱样本差异大batch 太小 loss 抖太大泛化下降lrCNN 1e-3Transformer 1e-4Transformer 对学习率极敏感1e-3 大概率发散epochs60–120配早停光谱任务收敛慢固定 epochs 不如早停可靠patch_size32–128过小序列太长、注意力稀疏过大丢失局部峰形embed_dim128–512决定 token 的表示容量小数据用 128–256 更稳depth4–12层数过深在小样本上直接过拟合4–6 层起步这里有一条血泪经验Transformer 类模型在光谱上的学习率玄学比图像还严重。图像任务 1e-3 能跑近红外 Transformer 同样用 1e-3 经常 loss 前 20 个 epoch 纹丝不动降到 1e-4 才开始下降。原因是光谱序列的数值范围和图像像素差异很大Adam 的一阶矩估计在稀疏梯度下容易走偏。所有模型先固定随机种子再在同一套超参下跑对比否则你分不清是结构差异还是随机种子差异。4.3 三组实验矩阵从基线到上限一次跑齐这套代码最省心的设计是它天然支持三组实验。第一组是四个基础模型从头训练对比回答「同样预算下哪种结构强」第二组是四个 TL 模型对比回答「迁移学习能不能涨点」第三组是消融实验挑一个模型改 patch_size、embed_dim、depth回答「你论文里的参数为什么取这个值」。实验组模型要回答的问题A 组从头训练基线ConvNet、VitNet、DeepVit、SpectFormer四种结构的默认能力排序B 组迁移学习对比ConvNetTL、VitNetTL、DeepVitTL、SpectFormerTL预测练/微调策略在同一结构上的增益C 组消融实验任选一模型改 patch_size / embed_dim / depth每个超参对结果的贡献有多大实验矩阵的管理建议用一张 Excel 或 CSV 记录每次运行的参数和结果。近红外实验跑一次不长但架不住次数多不记录的话三天后就分不清哪组是哪组了。每组实验固定同一个随机种子这是整套实验可复现的前提。4.4 训练日志怎么判早停、最优权重与 loss 曲线训练脚本骨架可以抽象成下面的逻辑不管包里的实现细节如何好的训练循环都应该包含固定种子、保存最优权重、早停三件事# 训练骨架固定种子、保存最优、早停 import torch import numpy as np def set_seed(seed42): np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) set_seed(42) model build_model_from_args(args) # 替换成包里的 ConvNet / SpectFormer 等 optimizer torch.optim.AdamW(model.parameters(), lrargs.lr, weight_decay1e-4) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_maxargs.epochs) best_val float(inf) patience 0 for epoch in range(args.epochs): train_loss run_one_epoch(model, optimizer, train_loader, modetrain) val_loss, val_r2 run_one_epoch(model, optimizer, val_loader, modeeval) if val_loss best_val: best_val val_loss torch.save(model.state_dict(), best_checkpoint.pt) patience 0 else: patience 1 if patience 15: print(fearly stop at epoch {epoch}) break逻辑说明set_seed(42)固定了 numpy 和 torch 的随机源保证两次运行结果一致这是实验可复现的地基。AdamW比 Adam 多了权重衰减解耦在光谱小数据上更不容易过拟合weight_decay1e-4是经验值。CosineAnnealingLR让学习率随训练周期余弦下降前期大步探索、后期小步收敛比固定学习率好用。判断训练是否正常重点看两个曲线train loss 持续下降说明模型在学val loss 下降后反弹说明过拟合开始val loss 震荡不降说明学习率过高或 patch_size 不合适。torch.save(model.state_dict(), best_checkpoint.pt)保存的是权重而不是整个模型加载时要用model.load_state_dict(torch.load(best_checkpoint.pt))这是 PyTorch 的推荐做法迁移到新数据时也只需要换权重文件。5. 近红外光谱深度学习避坑手册五个翻车现场与排查思路近红外深度学习的坑九成不在网络设计而在数据口径。这一章是这套代码最常见的五个翻车场景每条都是我见过或自己踩过的真实案例按「现象 → 原因 → 解决」写清楚。5.1 随机划分让 R² 虚高到 0.99换个仪器就翻车现象直接用train_test_split(random_state42)划分数据测试集 R² 到了 0.99loss 曲线漂亮得像教材里的示意图。你以为模型已经练成结果用到新采集的样品上预测值和实测值能偏出半个数量级。原因近红外样品往往来自同一批次、同一工艺相邻编号的光谱差异极小。随机切分会把非常相似的样本同时分进训练和测试集模型等于变相记住了样品编号附近的数据这是典型的数据泄漏。论文复现时别人用同样的数据却复现不出这个 R²问题不在代码在划分方式。解决正式实验用 Kennard-Stone 按光谱距离划分训练集和测试集让测试集覆盖模型没见过的光谱范围。如果只是为了快速验证流程随机划分加固定种子可以但要把「随机划分 R² 虚高」写进论文的局限性。我从那以后每次拿到近红外数据第一件事就是问一句这批样本是不是同一批采集的是的话直接放弃随机划分。5.2 axis 选错把每一列波长做归一化光谱形变被抹平现象预处理代码里写了一句X (X - X.mean(axis0)) / X.std(axis0)跑完模型 R² 和 RMSE 都还能看但换一组新数据后预测值整体偏移而且模型怎么调都救不回来。原因axis0是沿着波长方向统计每一列做标准化这会抹掉同一样本内部不同波段的相对强弱。而近红外吸光度最有用的信息恰恰是同一光谱内部不同波段的相对关系——SNV 和标准正态变换都要求按样本行计算也就是axis1。搞反方向后光谱的峰形被破坏模型学到的是被扭曲后的特征。解决先打印数组形状确认(N, L)预处理一律用axis1对每个样本内部操作。代码里写上注释标注「按样本行做不改方向」如果用了标准化而非 SNV均值方差必须来自训练集测试集复用同一组统计量。5.3 Transformer 训练抖得像心电图loss 就是下不来现象VitNet 或 SpectFormer 一上来 train loss 就高频抖动把学习率降到 1e-4 之后还在抖或者 loss 掉到一定程度就卡死不动val loss 完全不跟。原因三个最常见的原因叠加——patch_size 切得太小导致序列过长Transformer 的注意力在长序列上不稳定学习率没有 warmup前几个 step 就把权重冲乱输入特征没有归一化数值范围过大让注意力权重的 softmax 饱和。解决patch_size 取 64 以上近红外几百个波长点切出 8–16 个 patch 就够学习率降到 1e-4 并加 warmup前 10 个 epoch 线性升到目标学习率输入过一遍预处理保证均值接近 0。如果还抖就把 depth 从 8 降到 4减少一层注意力就少一层不稳定因素。5.4 R² 很好看RMSE 却大得离谱边界样本在作怪现象测试集 R² 0.96看起来很不错RMSEP 却比工业标准高一大截。画出预测-实测散点图发现大部分点贴合对角线只有一两个极端样本的残差特别大。原因R² 对线性拟合比例敏感RMSE 对平方误差敏感。一两个离群点能把 RMSE 拉爆但 R² 仍然好看。这是近红外回归报告里最常见的「指标打架」只报 R² 会高估模型只报 RMSE 会低估模型。真实原因往往是测试集里混入了极端浓度样本或者个别样品表面状态异常导致光谱失真。解决三个指标一起报R²、RMSEP、RPD 缺一不可。画出残差图把偏差超过 3 倍标准差的样本单独标出来。如果是记录错误就修正如果是真实极端工况要把这类样本单独评估或者补充更多极端样本进训练集。论文里只报 R² 不报残差答辩时被问住几乎是必然的。5.5 TL 版本跑不起来一维光谱加载不了二维预训练权重现象想用 ImageNet 预训练权重做迁移学习加载权重时直接报 shape mismatch强行忽略形状错误后训练完全不涨甚至从头开始 loss 也不降。原因ImageNet 预训练权重是二维三通道输入近红外光谱是一维输入输入层通道数和序列长度都对不上。能够匹配的只有中间层的结构输入层和输出层必然报错。就算跳过输入层预训练模型学到的二维边缘、纹理特征对一维光谱的吸收峰也没有什么可迁移的价值。解决打开 README.md先确认 TL 版本的预训练权重到底从哪来、怎么加载。如果包内没有一维预训练权重常见做法是把 TL 理解成「训练策略」而不是「直接拷权重」——比如先在公开光谱数据集上做自监督预训练再在目标数据上微调或者冻结浅层、只微调深层。把 TL 的实现方式写清楚答辩时反而成为加分项。6. 把结果做成论文级图表五折交叉验证、残差分布与 RPD 报告实验跑完只是第一步近红外回归的结论必须经过交叉验证才有说服力。单次划分的测试集 R² 只能证明「这组数据上跑了一次」五折交叉验证能把结论从「一次运气」变成「平均表现」。6.1 五折交叉验证这样跑交叉验证的关键坑在预处理统计量的泄漏。每一折都必须在训练折上重新计算标准化参数测试折只能用训练折算出来的均值方差否则等于提前偷看了答案。# 五折交叉验证每折在训练折上重算预处理统计量防止数据泄漏 from sklearn.model_selection import KFold from sklearn.metrics import r2_score, mean_squared_error import numpy as np X np.load(X_preprocessed.npy) # 已经做完 SGSNV 的谱图矩阵 y np.load(y_target.npy) # 浓度/含量真值 kf KFold(n_splits5, shuffleTrue, random_state42) r2_list, rmse_list [], [] for fold, (tr_idx, te_idx) in enumerate(kf.split(X)): X_tr, X_te X[tr_idx], X[te_idx] y_tr, y_te y[tr_idx], y[te_idx] # 标准化只能在训练折上拟合 mu, sigma X_tr.mean(axis0), X_tr.std(axis0) 1e-8 X_tr (X_tr - mu) / sigma X_te (X_te - mu) / sigma model train_model(X_tr, y_tr) # 换成包里的 ConvNet 或 SpectFormer pred model.predict(X_te) r2_list.append(r2_score(y_te, pred)) rmse_list.append(mean_squared_error(y_te, pred, squaredFalse)) print(fR2 {np.mean(r2_list):.3f} ± {np.std(r2_list):.3f}) print(fRMSE {np.mean(rmse_list):.3f} ± {np.std(rmse_list):.3f})逻辑说明KFold(shuffleTrue, random_state42)先打乱再分折避免样本顺序影响折的构成。每折循环里重算mu和sigma这一步是交叉验证和数据泄漏的分水岭。最后输出的均值加减标准差比单次 R² 可信得多写论文时直接作为模型性能的正式指标。6.2 回归图与 RPD 报告交叉验证之后还差两张图就能把结论钉死。第一张是预测值对实测值的散点图对角线是理想预测线点越贴线越好斜率偏离 1 说明存在系统性偏差。第二张是残差图横轴是实测值纵轴是预测减实测残差如果在零点上下均匀分布说明模型没有明显的系统性倾向如果呈现漏斗形则说明浓度越高误差越大。RPD 的计算方式是把测试集 y 的标准差除以 RMSEP这个比值直接反映模型的实用价值。RPD 大于 3 说明模型可以用于定量预测2.5 到 3 之间说明模型可用于粗糙筛选小于 2 说明在这个任务上不如直接取均值。这套结论连同交叉验证的均值方差一起写进论文的结果表比单独报一个 R² 的说服力强很多。从那以后我拿到任何近红外数据都强制先跑一遍五折交叉验证用平均 R² 和 RPD 决定要不要继续往下做而不是盯着单次划分的数字自我感动。这套代码包的价值也正在这里——它把结构、对比、验证的链路都铺好了剩下要你动手的是把数据口径和验证逻辑做扎实。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑