
簡介一份面向計算機專業(yè)學生、用于畢設或課程作業(yè)的深度學習教育項目聚焦近紅外光譜數(shù)據(jù)的回歸分析。近紅外光譜屬于非破壞性檢測技術項目將深度學習模型應用于成分或性質(zhì)預測是數(shù)據(jù)科學領域的典型實踐。壓縮包共9個文件以8個Python腳本為主輔以1個Markdown說明文檔整體大小僅27KB代碼結(jié)構(gòu)清晰。腳本中實現(xiàn)了ConvNet、SpectFormer、DeepVit、VitNet等模型并提供對應的遷移學習版本便于對照不同網(wǎng)絡結(jié)構(gòu)在光譜回歸任務上的表現(xiàn)。已有235人瀏覽學習。通過閱讀這些源碼可以掌握數(shù)據(jù)預處理、模型搭建、訓練驗證等完整流程也可直接復用或修改模型用于自己的實驗或課題適合想快速上手深度學習回歸項目的學生參考。1. 近紅外光譜數(shù)據(jù)回歸分析模型這套代碼包不是黑匣子是一組可復現(xiàn)的對照實驗拿到「基于深度學習的近紅外光譜數(shù)據(jù)回歸分析模型」這套代碼包時我第一反應是又一個調(diào)參模板拆完才覺得它最值錢的不是某一個模型有多先進而是同一份數(shù)據(jù)上同時給了四條結(jié)構(gòu)路線——ConvNet、VitNet、DeepVit、SpectFormer——每條還帶一個帶 TL 后綴的遷移學習變體。近紅外光譜回歸任務正好卡在幾個老問題上波長點太多、樣本量太少、化學吸收帶相互重疊單靠一個網(wǎng)絡很難把「為什么這樣設計」講清楚。這套結(jié)構(gòu)把最常見的解題路徑一次鋪開了。適合兩種人一種是做計算機類畢設、課程設計需要完整實驗對比的同學另一種是手里已有光譜數(shù)據(jù)、想快速評估不同網(wǎng)絡上限在哪的工程師。文件列表很干凈每個網(wǎng)絡結(jié)構(gòu)一個 py 文件帶 TL 的變體單獨拆開README.md 負責串聯(lián)入口按順序跑就能復現(xiàn)。2. 從近紅外光譜 CSV 到可訓練張量預處理順序、劃分策略與回歸指標近紅外光譜的輸入形式和圖像差別很大。圖像是二維空間信息光譜是一條一維曲線橫軸是波長常見 780–2500 nm縱軸是吸光度或反射率。拿到手的數(shù)據(jù)常見格式是 CSV 表格存所有樣本每行一個樣本第一列是目標值水分、蛋白、某成分含量后面幾百上千列是波長點的吸光度也有轉(zhuǎn)置存放的。第一步不是忙著建模而是確認數(shù)據(jù)的存儲方向、缺失值情況和量綱是否一致。近紅外吸收主要來自 O-H、N-H、C-H 等含氫基團的倍頻與合頻振動譜帶重疊嚴重這決定了后面必須做平滑和導數(shù)處理否則模型很容易被基線漂移和高頻噪聲帶偏。2.1 把數(shù)據(jù)讀進來每行一個樣本字段名就是波長點先用 pandas 把 CSV 讀進來打印 shape 和缺失值。光譜數(shù)據(jù)經(jīng)常出現(xiàn)兩種情況某一列某個波長點全為 0或者某幾行樣品吸光度異常偏高后者多半是樣品表面狀態(tài)不同導致的散射差異不能直接刪要靠預處理矯正。import pandas as pd import numpy as np # 常見近紅外光譜 CSV每行一個樣本第一列是目標值其余列是波長點 data pd.read_csv(spectra.csv) print(原始形狀:, data.shape) # (樣本數(shù), 波長點數(shù) 1) print(缺失值統(tǒng)計:\n, data.isna().sum().sum()) # 分離目標值和光譜矩陣 y data.iloc[:, 0].values.astype(float32) # 第一列是濃度/含量 X data.iloc[:, 1:].values.astype(float32) # 其余列是吸光度 # 波長點數(shù)量直接決定模型輸入維度記下來后面設置網(wǎng)絡要用 seq_len X.shape[1] print(f樣本數(shù): {X.shape[0]}, 波長點數(shù): {seq_len})這段代碼的邏輯是先把數(shù)據(jù)拆成X和yy是回歸目標X是光譜矩陣形狀是(N, L)。seq_len后面做 ConvNet 和 Transformer 都要用到一維卷積的輸入長度和 ViT 切 patch 的總長都由它決定。注意 dtype 統(tǒng)一轉(zhuǎn)成 float32PyTorch 默認浮點類型就是 float32不轉(zhuǎn)會在 loss 計算時出現(xiàn)類型不匹配的報錯。缺失值處理上我一般先看缺失比例低于 1% 直接按列均值填充高于 5% 就查儀器采集記錄大概率是樣品問題。2.2 預處理三步走SG 平滑、SNV、一階導數(shù)順序別反近紅外光譜預處理是典型的「做對了不出彩做錯了全翻車」的環(huán)節(jié)。最常見的組合是 Savitzky-Golay 平滑 SNV標準正態(tài)變換 一階導數(shù)。SG 平滑負責去高頻噪聲SNV 逐行消除基線漂移和光程差異一階導數(shù)進一步消掉基線平移。順序不能反先平滑再求導否則噪聲會被導數(shù)放大。from scipy.signal import savgol_filter # 1) SG 平滑窗口 11、多項式階數(shù) 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) 一階導數(shù)用 SG 在平滑的同時求導窗口可以小一點 X_deriv savgol_filter(X_snv, window_length9, polyorder2, deriv1, axis1) print(預處理后形狀:, X_deriv.shape, 均值≈, X_deriv.mean().round(6))邏輯說明三個變換全部作用于axis1也就是對每條光譜內(nèi)部做計算。window_length控制平滑窗口覆蓋的波長點數(shù)窗口越大越平滑但會把窄的吸收峰一起抹掉近紅外譜峰的半高寬通常在 10–30 nm 左右按采樣間隔折算成點數(shù)后窗口取 9–15 是安全區(qū)。deriv1表示一階導數(shù)如果二階導數(shù)可以消掉基線斜率但對噪聲更敏感畢設階段先用一階就夠。預處理后的均值應該接近 0如果均值還明顯偏離說明 SNV 的 axis 選反了大概率是按列做的。這里補一句 C 的角色如果光譜矩陣到了幾十萬行、幾百個波長點這種規(guī)模Python 的 pandas 循環(huán)會卡在 IO 和逐行運算上用 C 寫數(shù)據(jù)清洗和批量切分會快不少但畢設和課程設計這個數(shù)據(jù)量Python 的 scipy 和 pandas 足夠不需要額外引入 C 模塊。2.3 劃分數(shù)據(jù)集近紅外數(shù)據(jù)不建議直接隨機切很多人在這一步直接train_test_split(random_state42)這是近紅外回歸里最容易埋雷的地方。近紅外樣品往往來自同一批次、同一工藝相鄰編號的光譜差異極小隨機切分會把非常相似的樣本同時分進訓練和測試集測試集 R2 虛高到 0.99換個批次數(shù)據(jù)直接崩。近紅外領域的通行做法是按光譜距離劃分Kennard-Stone 算法是主流思路是每次選離已選樣本最遠的樣本進訓練集讓測試集盡可能獨立。from sklearn.model_selection import train_test_split from sklearn.metrics import r2_score, mean_squared_error # 快速驗證版本先固定隨機種子保證實驗可復現(xiàn) X_train, X_test, y_train, y_test train_test_split( X_deriv, y, test_size0.2, random_state42 ) # 正式實驗建議用 Kennard-Stone 按光譜距離劃分 # 常見做法是用 ks 庫或自己實現(xiàn)距離貪婪采樣這里給出替代方案 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 的簡化實現(xiàn)np.argmax(dist)每次挑離已選樣本最遠的光譜進訓練集保證測試集是「模型沒見過的光譜范圍」。這個方法在近紅外文獻里很常見缺點是計算距離矩陣時內(nèi)存占用高樣本量超過兩三萬就建議用分批近似。數(shù)據(jù)劃分完后回歸指標要同時看三個不要只盯 R2。指標作用近紅外回歸常用參考R2決定系數(shù)看模型解釋了多大比例的方差0.95 很穩(wěn)0.90–0.95 夠用RMSEP測試集預測均方根誤差看絕對誤差越小越好必須結(jié)合 y 的量綱看RPDy 的標準差除以 RMSEP3 優(yōu)秀2.5–3 可用2 謹慎RMSEP 里的 P 指 Prediction專門評價測試集預測比訓練集 RMSE 更有說服力。RPD 這個指標在近紅外領域幾乎是默認要報的它回答的是「模型誤差比樣品本身的波動小了多少」寫論文和答辯時比單純報 R2 更能堵住追問。注意預處理里的統(tǒng)計量只能從訓練集算出再用到測試集上。SNV 本身逐樣本獨立問題不大但后面如果做標準化均值方差必須來自訓練集否則就是數(shù)據(jù)泄漏指標再好看也站不住。3. ConvNet、VitNet、DeepVit、SpectFormer四個模型家族怎么選TL 后綴到底加了什么文件列表里兩組 py 的命名邏輯很清楚ConvNet、VitNet、DeepVit、SpectFormer 是四個基礎結(jié)構(gòu)帶 TL 后綴的是各自對應的遷移學習變體。TL 基本可以斷定是 Transfer Learning 的縮寫具體遷移策略和權重來源以 README.md 為準但這個命名本身已經(jīng)說明了一份完整的對比實驗應該怎么組織——同一個結(jié)構(gòu)從頭訓練 vs 遷移學習各跑一組正好回答答辯時最常被問的「遷移學習在你的任務里到底有沒有用」。3.1 ConvNet.py一維卷積基線先把下限做扎實一維卷積是近紅外光譜回歸最樸素的深度學習方案也是所有對比實驗的「地板」。光譜輸入從二維圖像降成一維序列后Conv1d的卷積核只在波長方向上滑動感受野覆蓋相鄰的波長點。近紅外吸收峰不是孤立點而是一段連續(xù)的波段卷積核能直接學習到「峰形 局部波段相關性」這類特征。典型結(jié)構(gòu)是Conv1d → BatchNorm → ReLU → AdaptiveAvgPool1d → Linear卷積核大小取 17 左右對應約 17 個波長點的鄰域接近半個吸收峰的寬度。AdaptiveAvgPool1d(1)把整條光譜壓成一個向量替代全連接層處理序列長度參數(shù)量小、不容易過擬合。這個結(jié)構(gòu)跑出來的結(jié)果就是整組實驗的基準線Transformer 模型如果連這個下限都打不過問題通常不在結(jié)構(gòu)而在數(shù)據(jù)預處理或超參。3.2 VitNet.py 與 DeepVit.py把光譜切成 patch 的 Transformer 路線VitNet 的思路是把一維光譜切成長度相等的 patch每個 patch 經(jīng)過線性映射后加上位置編碼再送進 Transformer 編碼器。近紅外光譜里同一個成分的吸收峰會出現(xiàn)在多個波段相隔很遠但相關性很強Transformer 的自注意力機制正好擅長建模這種長程依賴。比如水的 O-H 吸收在 1400 nm 和 1900 nm 附近都有信號純 CNN 要靠堆層數(shù)才能把這兩個位置的感受野連起來Transformer 一層注意力就能直接關聯(lián)。DeepVit 從命名看是 VitNet 的加深加寬版本差異大概率在 Transformer encoder 的層數(shù)depth和嵌入維度embed_dim上。這兩個模型的對比要回答的問題是在近紅外這種中等長度序列上加大模型容量能不能帶來實打?qū)嵉氖找孢€是只是把訓練時間變長。光譜數(shù)據(jù)樣本量通常只有幾百到幾千DeepVit 非常容易過擬合所以它的默認超參、正則化設置反而是這組實驗里最有看點的部分。3.3 SpectFormer.pyCNN 局部特征 Transformer 全局依賴的混合SpectFormer 從名字看是 Spect Former 的組合也是我拆這個包時最期待的文件。純 CNN 的局部感受野高效但視野窄純 Transformer 的全局注意力強但容易在小樣本上過擬合混合結(jié)構(gòu)是這兩條路的折中淺層先用一維卷積提取局部峰形特征再把卷積輸出的特征圖切成 patch 或直接作為 token 序列送進 Transformer。這個設計對近紅外光譜是說得通的。卷積層輸出的每個位置已經(jīng)包含了鄰近波段的局部信息Transformer 在這些局部特征之上建??绮ǘ蔚囊蕾囌Z義上比直接用原始波長點做注意力更干凈。SpectFormer 和 ConvNet 的對比實際是「局部特征 全局關系」和「純局部特征」的對比SpectFormer 和 VitNet 的對比則是「局部建模是否必要」的對比。三組模型互相參照正好覆蓋評審最關心的兩個問題結(jié)構(gòu)設計有沒有依據(jù)、實驗有沒有對照組。3.4 帶 TL 后綴的那一組遷移學習到底遷移了什么四個 TL 文件是這套代碼包在畢設答辯時最有價值的部分。近紅外深度學習最常見的困境是光譜數(shù)據(jù)量太少從頭訓練一個容量稍微大一點的網(wǎng)絡就過擬合。遷移學習的常規(guī)做法是先在更大規(guī)模的數(shù)據(jù)上預訓練再在目標任務上微調(diào)。但要注意ImageNet 預訓練權重是二維三通道輸入近紅外光譜是一維輸入輸入層通道數(shù)和序列長度都對不上直接加載預訓練權重一定會報 shape mismatch。所以這里的 TL 變體遷移的通常不是 ImageNet 權重本身而是這類結(jié)構(gòu)的「訓練策略」——比如凍結(jié)淺層、只微調(diào)深層或者把同一結(jié)構(gòu)在公開光譜數(shù)據(jù)上做自監(jiān)督預訓練再微調(diào)。具體是哪種以 README.md 的實現(xiàn)為準但你可以把 TL 變體理解成「同一個網(wǎng)絡換一種訓練方式」而不是「換一個網(wǎng)絡」。這張表是我拆完文件后對每個腳本的定位判斷文件結(jié)構(gòu)路線在實驗矩陣中的角色ConvNet.py1D CNN 基線定下限作為所有模型對比的基準ConvNetTL.pyConvNet 遷移學習看預測練/微調(diào)策略對 CNN 是否有提升VitNet.py光譜切 patch 進 Transformer純 Transformer 路線的基線VitNetTL.pyVitNet TL看遷移學習能否緩解 Transformer 過擬合DeepVit.py加深加寬的 VitNet驗證模型容量在光譜數(shù)據(jù)上的上限D(zhuǎn)eepVitTL.pyDeepVit TL深度模型與遷移學習是否兼容SpectFormer.pyCNN Transformer 混合局部特征 全局依賴的混合結(jié)構(gòu)SpectFormerTL.pySpectFormer TL混合結(jié)構(gòu)結(jié)合遷移學習后的最終效果提示拆這類代碼包時最忌直接把文件名當結(jié)論。先打開 README.md確認每個 TL 文件里預訓練權重的來源和微調(diào)策略再決定你的論文里怎么寫「遷移學習」這一章。4. 把訓練跑起來命令行入口、六個關鍵超參與三組實驗矩陣環(huán)境裝好后這套代碼的入口設計比較直白每個模型一個 py 文件直接python 文件名.py就能啟動訓練。動手深度學習的人都知道跑通一個骨架半小時排一個環(huán)境 bug 一晚上。近紅外回歸任務用 PyTorch 實現(xiàn)是當前最常見的方案torch 1.13 以上版本對Conv1d和 Transformer Encoder 的支持都很成熟不需要額外裝復雜的庫。4.1 環(huán)境與入口先跑通 README.md再換你自己的數(shù)據(jù)建議環(huán)境是 Python 3.8核心依賴包括 numpy、pandas、scipy、scikit-learn、torch。命令行入口的一般形式如下實際參數(shù)名以 README.md 為準但結(jié)構(gòu)基本逃不出這套# 以 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確認數(shù)據(jù)能讀進來、loss 能下降再跑完整訓練。這么做的好處是避免數(shù)據(jù)格式問題在跑了 30 個 epoch 之后才暴露白白浪費時間。GPU 不是必須的。近紅外光譜序列長度在幾百到一千左右樣本量通常不過萬這類規(guī)模的模型在 CPU 上跑 80 個 epoch 也就是幾十分鐘到一兩個小時。如果你的數(shù)據(jù)量特別大再考慮租云平臺或者用 GPU 實例畢設場景完全不需要一上來就上多卡。4.2 六個關鍵超參值和含義要對得上調(diào)參之前先搞清楚每個參數(shù)在這個任務里的物理含義。這六個參數(shù)是近紅外光譜 Transformer 和 CNN 訓練里最常動的優(yōu)先級從高到低排列如下參數(shù)建議范圍對光譜回歸的影響batch_size16–64光譜樣本差異大batch 太小 loss 抖太大泛化下降lrCNN 1e-3Transformer 1e-4Transformer 對學習率極敏感1e-3 大概率發(fā)散epochs60–120配早停光譜任務收斂慢固定 epochs 不如早??煽縫atch_size32–128過小序列太長、注意力稀疏過大丟失局部峰形embed_dim128–512決定 token 的表示容量小數(shù)據(jù)用 128–256 更穩(wěn)depth4–12層數(shù)過深在小樣本上直接過擬合4–6 層起步這里有一條血淚經(jīng)驗Transformer 類模型在光譜上的學習率玄學比圖像還嚴重。圖像任務 1e-3 能跑近紅外 Transformer 同樣用 1e-3 經(jīng)常 loss 前 20 個 epoch 紋絲不動降到 1e-4 才開始下降。原因是光譜序列的數(shù)值范圍和圖像像素差異很大Adam 的一階矩估計在稀疏梯度下容易走偏。所有模型先固定隨機種子再在同一套超參下跑對比否則你分不清是結(jié)構(gòu)差異還是隨機種子差異。4.3 三組實驗矩陣從基線到上限一次跑齊這套代碼最省心的設計是它天然支持三組實驗。第一組是四個基礎模型從頭訓練對比回答「同樣預算下哪種結(jié)構(gòu)強」第二組是四個 TL 模型對比回答「遷移學習能不能漲點」第三組是消融實驗挑一個模型改 patch_size、embed_dim、depth回答「你論文里的參數(shù)為什么取這個值」。實驗組模型要回答的問題A 組從頭訓練基線ConvNet、VitNet、DeepVit、SpectFormer四種結(jié)構(gòu)的默認能力排序B 組遷移學習對比ConvNetTL、VitNetTL、DeepVitTL、SpectFormerTL預測練/微調(diào)策略在同一結(jié)構(gòu)上的增益C 組消融實驗任選一模型改 patch_size / embed_dim / depth每個超參對結(jié)果的貢獻有多大實驗矩陣的管理建議用一張 Excel 或 CSV 記錄每次運行的參數(shù)和結(jié)果。近紅外實驗跑一次不長但架不住次數(shù)多不記錄的話三天后就分不清哪組是哪組了。每組實驗固定同一個隨機種子這是整套實驗可復現(xiàn)的前提。4.4 訓練日志怎么判早停、最優(yōu)權重與 loss 曲線訓練腳本骨架可以抽象成下面的邏輯不管包里的實現(xiàn)細節(jié)如何好的訓練循環(huán)都應該包含固定種子、保存最優(yōu)權重、早停三件事# 訓練骨架固定種子、保存最優(yōu)、早停 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 的隨機源保證兩次運行結(jié)果一致這是實驗可復現(xiàn)的地基。AdamW比 Adam 多了權重衰減解耦在光譜小數(shù)據(jù)上更不容易過擬合weight_decay1e-4是經(jīng)驗值。CosineAnnealingLR讓學習率隨訓練周期余弦下降前期大步探索、后期小步收斂比固定學習率好用。判斷訓練是否正常重點看兩個曲線train loss 持續(xù)下降說明模型在學val loss 下降后反彈說明過擬合開始val loss 震蕩不降說明學習率過高或 patch_size 不合適。torch.save(model.state_dict(), best_checkpoint.pt)保存的是權重而不是整個模型加載時要用model.load_state_dict(torch.load(best_checkpoint.pt))這是 PyTorch 的推薦做法遷移到新數(shù)據(jù)時也只需要換權重文件。5. 近紅外光譜深度學習避坑手冊五個翻車現(xiàn)場與排查思路近紅外深度學習的坑九成不在網(wǎng)絡設計而在數(shù)據(jù)口徑。這一章是這套代碼最常見的五個翻車場景每條都是我見過或自己踩過的真實案例按「現(xiàn)象 → 原因 → 解決」寫清楚。5.1 隨機劃分讓 R2 虛高到 0.99換個儀器就翻車現(xiàn)象直接用train_test_split(random_state42)劃分數(shù)據(jù)測試集 R2 到了 0.99loss 曲線漂亮得像教材里的示意圖。你以為模型已經(jīng)練成結(jié)果用到新采集的樣品上預測值和實測值能偏出半個數(shù)量級。原因近紅外樣品往往來自同一批次、同一工藝相鄰編號的光譜差異極小。隨機切分會把非常相似的樣本同時分進訓練和測試集模型等于變相記住了樣品編號附近的數(shù)據(jù)這是典型的數(shù)據(jù)泄漏。論文復現(xiàn)時別人用同樣的數(shù)據(jù)卻復現(xiàn)不出這個 R2問題不在代碼在劃分方式。解決正式實驗用 Kennard-Stone 按光譜距離劃分訓練集和測試集讓測試集覆蓋模型沒見過的光譜范圍。如果只是為了快速驗證流程隨機劃分加固定種子可以但要把「隨機劃分 R2 虛高」寫進論文的局限性。我從那以后每次拿到近紅外數(shù)據(jù)第一件事就是問一句這批樣本是不是同一批采集的是的話直接放棄隨機劃分。5.2 axis 選錯把每一列波長做歸一化光譜形變被抹平現(xiàn)象預處理代碼里寫了一句X (X - X.mean(axis0)) / X.std(axis0)跑完模型 R2 和 RMSE 都還能看但換一組新數(shù)據(jù)后預測值整體偏移而且模型怎么調(diào)都救不回來。原因axis0是沿著波長方向統(tǒng)計每一列做標準化這會抹掉同一樣本內(nèi)部不同波段的相對強弱。而近紅外吸光度最有用的信息恰恰是同一光譜內(nèi)部不同波段的相對關系——SNV 和標準正態(tài)變換都要求按樣本行計算也就是axis1。搞反方向后光譜的峰形被破壞模型學到的是被扭曲后的特征。解決先打印數(shù)組形狀確認(N, L)預處理一律用axis1對每個樣本內(nèi)部操作。代碼里寫上注釋標注「按樣本行做不改方向」如果用了標準化而非 SNV均值方差必須來自訓練集測試集復用同一組統(tǒng)計量。5.3 Transformer 訓練抖得像心電圖loss 就是下不來現(xiàn)象VitNet 或 SpectFormer 一上來 train loss 就高頻抖動把學習率降到 1e-4 之后還在抖或者 loss 掉到一定程度就卡死不動val loss 完全不跟。原因三個最常見的原因疊加——patch_size 切得太小導致序列過長Transformer 的注意力在長序列上不穩(wěn)定學習率沒有 warmup前幾個 step 就把權重沖亂輸入特征沒有歸一化數(shù)值范圍過大讓注意力權重的 softmax 飽和。解決patch_size 取 64 以上近紅外幾百個波長點切出 8–16 個 patch 就夠?qū)W習率降到 1e-4 并加 warmup前 10 個 epoch 線性升到目標學習率輸入過一遍預處理保證均值接近 0。如果還抖就把 depth 從 8 降到 4減少一層注意力就少一層不穩(wěn)定因素。5.4 R2 很好看RMSE 卻大得離譜邊界樣本在作怪現(xiàn)象測試集 R2 0.96看起來很不錯RMSEP 卻比工業(yè)標準高一大截。畫出預測-實測散點圖發(fā)現(xiàn)大部分點貼合對角線只有一兩個極端樣本的殘差特別大。原因R2 對線性擬合比例敏感RMSE 對平方誤差敏感。一兩個離群點能把 RMSE 拉爆但 R2 仍然好看。這是近紅外回歸報告里最常見的「指標打架」只報 R2 會高估模型只報 RMSE 會低估模型。真實原因往往是測試集里混入了極端濃度樣本或者個別樣品表面狀態(tài)異常導致光譜失真。解決三個指標一起報R2、RMSEP、RPD 缺一不可。畫出殘差圖把偏差超過 3 倍標準差的樣本單獨標出來。如果是記錄錯誤就修正如果是真實極端工況要把這類樣本單獨評估或者補充更多極端樣本進訓練集。論文里只報 R2 不報殘差答辯時被問住幾乎是必然的。5.5 TL 版本跑不起來一維光譜加載不了二維預訓練權重現(xiàn)象想用 ImageNet 預訓練權重做遷移學習加載權重時直接報 shape mismatch強行忽略形狀錯誤后訓練完全不漲甚至從頭開始 loss 也不降。原因ImageNet 預訓練權重是二維三通道輸入近紅外光譜是一維輸入輸入層通道數(shù)和序列長度都對不上。能夠匹配的只有中間層的結(jié)構(gòu)輸入層和輸出層必然報錯。就算跳過輸入層預訓練模型學到的二維邊緣、紋理特征對一維光譜的吸收峰也沒有什么可遷移的價值。解決打開 README.md先確認 TL 版本的預訓練權重到底從哪來、怎么加載。如果包內(nèi)沒有一維預訓練權重常見做法是把 TL 理解成「訓練策略」而不是「直接拷權重」——比如先在公開光譜數(shù)據(jù)集上做自監(jiān)督預訓練再在目標數(shù)據(jù)上微調(diào)或者凍結(jié)淺層、只微調(diào)深層。把 TL 的實現(xiàn)方式寫清楚答辯時反而成為加分項。6. 把結(jié)果做成論文級圖表五折交叉驗證、殘差分布與 RPD 報告實驗跑完只是第一步近紅外回歸的結(jié)論必須經(jīng)過交叉驗證才有說服力。單次劃分的測試集 R2 只能證明「這組數(shù)據(jù)上跑了一次」五折交叉驗證能把結(jié)論從「一次運氣」變成「平均表現(xiàn)」。6.1 五折交叉驗證這樣跑交叉驗證的關鍵坑在預處理統(tǒng)計量的泄漏。每一折都必須在訓練折上重新計算標準化參數(shù)測試折只能用訓練折算出來的均值方差否則等于提前偷看了答案。# 五折交叉驗證每折在訓練折上重算預處理統(tǒng)計量防止數(shù)據(jù)泄漏 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) # 已經(jīng)做完 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)先打亂再分折避免樣本順序影響折的構(gòu)成。每折循環(huán)里重算mu和sigma這一步是交叉驗證和數(shù)據(jù)泄漏的分水嶺。最后輸出的均值加減標準差比單次 R2 可信得多寫論文時直接作為模型性能的正式指標。6.2 回歸圖與 RPD 報告交叉驗證之后還差兩張圖就能把結(jié)論釘死。第一張是預測值對實測值的散點圖對角線是理想預測線點越貼線越好斜率偏離 1 說明存在系統(tǒng)性偏差。第二張是殘差圖橫軸是實測值縱軸是預測減實測殘差如果在零點上下均勻分布說明模型沒有明顯的系統(tǒng)性傾向如果呈現(xiàn)漏斗形則說明濃度越高誤差越大。RPD 的計算方式是把測試集 y 的標準差除以 RMSEP這個比值直接反映模型的實用價值。RPD 大于 3 說明模型可以用于定量預測2.5 到 3 之間說明模型可用于粗糙篩選小于 2 說明在這個任務上不如直接取均值。這套結(jié)論連同交叉驗證的均值方差一起寫進論文的結(jié)果表比單獨報一個 R2 的說服力強很多。從那以后我拿到任何近紅外數(shù)據(jù)都強制先跑一遍五折交叉驗證用平均 R2 和 RPD 決定要不要繼續(xù)往下做而不是盯著單次劃分的數(shù)字自我感動。這套代碼包的價值也正在這里——它把結(jié)構(gòu)、對比、驗證的鏈路都鋪好了剩下要你動手的是把數(shù)據(jù)口徑和驗證邏輯做扎實。希望幫到你。本文還有配套的精品資源點擊獲取