合距離、KS對比與評估避坑指南)
簡介面向近紅外光譜建模與機器學習入門的樣本集劃分實踐材料聚焦SPXY樣本劃分法及其與蒙特卡羅交叉驗證結合時的模型優(yōu)化思路適合需要處理訓練集、驗證集與測試集劃分并評估模型泛化能力的高年級本科生、研究生和算法初學者。壓縮包共4個文件約291KB以3個MATLAB腳本和1份PDF文獻為主腳本覆蓋SPXY劃分、隨機劃分與KS檢驗等環(huán)節(jié)文獻結合橘葉中橙皮苷含量測定案例說明光譜數(shù)據預處理、樣本選擇及交叉驗證的完整流程。已有1827人學習反映該主題在光譜定量分析與模型驗證中關注度較高。讀者可據此理解SPXY如何兼顧特征空間與濃度分布比較不同劃分策略對預測精度的影響并借助KS檢驗判斷預測分布與真實分布的差異為參數(shù)調優(yōu)和最終評估提供可復用的腳本與實驗參照。1. 一份「自己出題自己考」的成績單樣本集劃分到底在分什么小時候都干過這種事把練習冊的答案抄一遍再把同一套題當考試做分數(shù)當然漂亮可換一套題就原形畢露。模型也是這個毛病——喂給它見過的樣本它答得又快又準真正要看的是它面對沒見過的樣本還能不能站住。樣本集劃分要解決的正是這個問題把有限的樣本切成幾份一份用來“學”一份藏起來“考”。SPXY 是其中比較講究的一種做法全稱叫“基于 X 與 Y 聯(lián)合距離的樣本集劃分”。名字唬人說白了它只是在決定“哪些樣本去當練習題、哪些去當考卷”時比隨機抓鬮多考慮了一件事——樣本的標簽值也得分布均勻。搞懂它你才能判斷自己那份模型成績單到底是真本事還是背答案。2. 為什么不能隨手一劃KS 與 SPXY 差在「看不看 Y」這一刀很多人第一次做劃分直接train_test_split一下比例 8:2跑完指標 0.98開心得不行。等換一批新樣本進來誤差翻了三倍才發(fā)現(xiàn)當初那份成績單是摻了水的。問題往往不在模型而在“分”這一步就已經埋下了雷。2.1 隨機劃分翻車的三個典型場景第一個場景是濃度斷層。一批樣本的參考值集中在中間某個窄區(qū)間只有零星幾個落在兩端。隨機切 20% 做測試集很可能低端那幾個樣本一個都沒被抽中于是模型在低濃度段的表現(xiàn)完全無從驗證。指標好看是因為考卷里壓根沒有難題。第二個場景是批次漂移。樣本按時間順序采集儀器上午和下午的狀態(tài)有細微差別。如果直接按順序切前 80% 訓練、后 20% 測試測試集就整段落在“下午”測出來的其實是跨批次泛化能力。這未必是錯但你得清楚自己在測什么而不是把結果當成“模型精度”四個字貼到報告上。第三個場景是平行樣泄漏。同一樣品測了三次或者為了擴樣本量做了輕微擾動這些“親戚樣本”被隨機分到不同集合里。測試集里坐著一堆訓練集的近親指標自然虛高。這類翻車最隱蔽因為流程上每一步都合規(guī)只有把樣本的來源捋一遍才會發(fā)現(xiàn)。隨機劃分的問題在于它不保證任何事。它只保證每個樣本有固定概率落進某一側至于落完之后兩邊的分布像不像它不管。樣本量上千、分布本身就均勻的時候這種“不管”影響不大樣本量只有一百出頭、標簽范圍又拉得很開的時候一次不走運的隨機就足以讓整篇結論作廢。2.2 KS 方法只看 X 距離的均勻撒點KS 全稱 Kennard-Stone比 SPXY 早出現(xiàn)很多年思路樸素得很樣本在特征空間里鋪得越開模型見到的情況就越全。它只關心 X也就是光譜、圖像特征、工藝參數(shù)這一側。做法是這樣的先算出所有樣本兩兩之間的 X 距離挑出距離最遠的那一對作為種子然后每一輪對每個還沒入選的樣本算出它到“已選集合”里所有樣本的距離取其中最小的那個再從所有候選里挑出這個最小值最大的樣本加入已選集合。重復到訓練集湊夠數(shù)為止。這套“最大最小距離”的邏輯效果是讓選出來的樣本彼此盡量疏遠像往一張地圖上撒點先撒最邊上的再往中間填空。好處是確定性算法沒有隨機種子同一個數(shù)據集跑一百遍結果都一樣可復現(xiàn)壞處是它眼里只有 X。如果兩個樣本在 X 空間離得很遠但標簽值幾乎相同KS 會優(yōu)先把它們都選進訓練集白白占掉名額。更麻煩的情況是 X 和 Y 的關系不單調。比如某段光譜區(qū)間對濃度不敏感兩個濃度差很多的樣本光譜卻長得幾乎一樣。KS 看不出這個差別它按 X 距離挑很可能把一整段濃度區(qū)間的樣本全漏掉。等到測試集里冒出一個該區(qū)間的樣本模型只能靠外推硬猜。2.3 SPXY 方法把 Y 也拉進距離公式SPXY 的關鍵改動只有一處距離不再只算 X而是把 Y 也算一份各自歸一化后相加。$$d_{xy}(p,q)\frac{d_x(p,q)}{\max d_x}\frac{d_y(p,q)}{\max d_y}$$$d_x$ 是樣本 $p$、$q$ 在特征矩陣上的歐氏距離$d_y$ 是它們在參考值上的歐氏距離。單變量時 $d_y$ 就是兩個數(shù)值之差的絕對值多變量時按歐氏距離算。分母上的兩個 $\max$ 是整套方法里最容易被忽略、也最不能省的一步。原始光譜的強度動輒幾千算出來的 $d_x$ 可能是幾百上千而參考值如果是含量百分比$d_y$ 可能只有零點幾。不歸一化直接相加Y 的貢獻會被 X 徹底淹沒SPXY 就退化成了 KS。除以各自的最大值之后兩項都被壓進 0 到 1 之間權重才對等。換個說法KS 挑樣本時只問“這兩個樣本長得像不像”SPXY 還要多問一句“它們的答案差多少”。長得不像、答案又差得多的樣本SPXY 會優(yōu)先送進訓練集因為這類樣本信息量最大。2.4 一個公式看清兩者的分水嶺把三種常見做法擺在一起差別一目了然。維度隨機劃分KSSPXY挑選依據無純概率僅 X 距離X 與 Y 聯(lián)合距離結果是否可復現(xiàn)需固定隨機種子是是訓練集覆蓋 Y 范圍不保證不保證更容易覆蓋兩端額外計算開銷幾乎沒有一個 X 距離矩陣兩個距離矩陣適合的場面樣本量大、分布本身均勻只關心特征空間覆蓋樣本少、參考值跨度大分水嶺就在中間那一列。KS 和 SPXY 的流程幾乎一模一樣代碼改一行就能互換但選出來的樣本集合可能差得很遠。判斷該用哪個看的不是哪個名字更時髦而是你的 Y 分布是不是均勻——Y 本來就均勻KS 夠用Y 有明顯斷層或者兩端樣本稀少SPXY 那額外一份距離就算得值。3. SPXY 走一遍從距離矩陣到挑出第一批樣本公式看懂了落到操作上還有幾步容易踩坑的地方。這一章把流程拆開走一遍順便說清楚每個參數(shù)改動的后果。3.1 第一步永遠是看數(shù)據不是算距離拿到一份樣本集先做三件事看樣本量、看 Y 的分布、看有沒有重復或近似重復的樣本。這三件事決定了后面所有參數(shù)怎么定。樣本量決定測試集能有多大??偣?60 個樣本按 8:2 分測試集只有 12 個任何一個樣本預測偏了指標就大幅波動。這時候更合理的比例是 2:1 甚至 3:1把測試集撐到 20 個以上。樣本量上千比例反倒可以放松因為統(tǒng)計量本身已經穩(wěn)了。Y 的分布決定要不要用 SPXY。把 Y 畫成直方圖如果兩端稀稀拉拉、中間堆成一座山隨機劃分和 KS 都有較大概率漏掉兩端SPXY 值得上。如果 Y 近似均勻分布那 KS 省一半計算量效果差不多。重復樣本要在劃分之前處理掉。同一樣品測三次這三條記錄必須分進同一個集合否則就是前面說的泄漏。做法很簡單給每個原始樣品編一個組號劃分時按組號分而不是按行號分。還有一步常被跳過X 的各列要不要標準化。如果特征里既有光譜強度量級上千又有溫度量級幾十直接算歐氏距離光譜會獨占話語權溫度等于沒參與。用歐氏距離之前把 X 各列標準化到同一量級這個距離才有意義。SPXY 公式里那層歸一化解決的是“X 距離和 Y 距離之間”的量級差解決不了“X 內部各列之間”的量級差兩件事不能混。3.2 選出起手的那一對最遠樣本算完聯(lián)合距離矩陣第一步是在整個矩陣里找最大值。這一對樣本就是種子直接進訓練集。為什么從最遠的一對開始因為它們代表了這個數(shù)據集能覆蓋的邊界。先把邊界占住后面的樣本才是在邊界之間填空整個訓練集的覆蓋范圍從第一輪就定下了基調。這里有個細節(jié)如果數(shù)據里存在測量錯誤的離群樣本它很可能就是那個“最遠點”第一輪就被選進訓練集。這是好事還是壞事取決于那個離群點是真實極端情況還是錄入錯誤。真實極端情況該進訓練集模型得見過錄入錯誤進了訓練集等于往模型嘴里塞了一口沙子。所以劃分之前先看一眼最遠的那幾對是誰值不值得信任這一步花不了五分鐘能省掉后面很多返工。3.3 逐輪挑選誰離已選集合最遠就選誰種子定下之后進入循環(huán)。每一輪做三件事算每個未選樣本到已選集合里所有樣本的距離、取最小值、在所有候選里挑這個最小值最大的那個。注意這里是“最小距離的最大值”不是“最大距離的最大值”。區(qū)別在于如果只看最大距離一個離某個已選樣本很遠的樣本會被優(yōu)先選中但它可能同時離另一個已選樣本很近選進來等于在密集區(qū)又添了一個點沒有增加覆蓋。取最小距離意思是“這個候選離已選集合最近的那個點有多遠”這個值越大說明它離整個已選集合越疏遠選它才真正擴大覆蓋。這個機制帶來一個隱含后果值得單獨說一句SPXY 選出的順序本質上是“從邊緣往中間收”。排在前面的樣本偏邊緣越往后越靠近密集區(qū)。如果你的訓練集取前 70%測試集就是剩下的 30%那測試集大概率集中在密集區(qū)幾乎不含極端樣本。這意味著模型的外推能力沒有被檢驗測出來的指標反映的是內插表現(xiàn)。這不是 SPXY 的缺陷是它的特性但你得知道。如果業(yè)務上關心的是“超出歷史范圍的新樣本能不能預測準”那測試集里必須人為保留幾個極端樣本不能全交給算法去分。做法是從兩端各留一兩個樣本不參與劃分直接塞進測試集剩下的再走 SPXY。這一手有點土但管用。3.4 完整實現(xiàn)與參數(shù)逐個拆開看下面這段是標準實現(xiàn)把前面幾節(jié)的邏輯落成代碼。import numpy as np def spxy(X, y, n_train): 基于 X-Y 聯(lián)合距離的樣本集劃分 X: 特征矩陣形狀 (n_samples, n_features) y: 參考值形狀 (n_samples,) 或 (n_samples, n_targets) n_train: 訓練集樣本數(shù) 返回: (訓練集下標, 測試集下標) X np.asarray(X, dtypefloat) y np.asarray(y, dtypefloat).reshape(len(X), -1) # 1. 分別計算 X 空間與 Y 空間的歐氏距離矩陣 dx np.sqrt(((X[:, None, :] - X[None, :, :]) ** 2).sum(axis-1)) dy np.sqrt(((y[:, None, :] - y[None, :, :]) ** 2).sum(axis-1)) # 2. 各自除以本矩陣最大值壓到 0~1讓兩項權重對等 d dx / dx.max() dy / dy.max() n len(X) # 3. 起手聯(lián)合距離最大的一對樣本 i, j np.unravel_index(np.argmax(d), d.shape) selected [int(i), int(j)] # 4. 循環(huán)每輪選“到已選集合最小距離”最大的候選 while len(selected) n_train: rest [k for k in range(n) if k not in selected] # d[np.ix_(rest, selected)] 取出候選行 × 已選列的子矩陣 min_to_selected d[np.ix_(rest, selected)].min(axis1) selected.append(rest[int(np.argmax(min_to_selected))]) test [k for k in range(n) if k not in selected] return np.array(selected), np.array(test)幾個參數(shù)的含義和改動后果n_train決定訓練集大小。它和測試集大小是互補的設成 0.7 倍樣本數(shù)就是 7:3。改這個值會直接改變訓練集覆蓋的疏密程度n_train 越小訓練集越偏向邊緣模型見到的中段樣本越少。dy的算法對單變量和多變量不一樣。單變量時用絕對差也行多變量時必須用歐氏距離否則多個目標值之間沒法合成一個標量。如果你的 y 是多列輸出reshape(len(X), -1)那一步不能省否則廣播會出錯。dx.max()和dy.max()這兩個分母如果某個矩陣全零所有樣本特征完全一致會除零報錯。實際數(shù)據里很難出現(xiàn)但做小規(guī)模試驗時可能碰到加個判斷更穩(wěn)妥。整個函數(shù)返回的是下標不是數(shù)據本身。這個設計是為了讓原始數(shù)據保持不動訓練集和測試集在需要的時候按下標取避免復制帶來的內存開銷也避免改動順序后對不上號。跑完之后別急著建模先把兩組樣本的 Y 值分布畫出來對比一眼。理想狀態(tài)下訓練集的 Y 覆蓋整個范圍測試集的 Y 落在范圍之內且分布均勻。如果測試集某一段完全空白說明劃分還有問題得回去調 n_train 或者手動補樣本。4. 易混與誤區(qū)SPXY、KS、交叉驗證別攪成一鍋這三個詞經常在同一段話里出現(xiàn)功能卻完全不在一個層面上。把它們分開比記住公式更重要。4.1 誤區(qū)一SPXY 是一種建模算法易混點名字里沒有“劃分”二字看起來和 PLS、SVR 這些算法是并列關系很多資料里也確實把它們排在一起講。為什么混SPXY 總是出現(xiàn)在建模流程的上下文里前后文都在聊模型精度很容易被當成“又一種建模方法”。怎么區(qū)分看它的輸出。SPXY 輸出的是兩組下標——訓練集是哪幾行、測試集是哪幾行它不產生任何預測值也不擬合任何參數(shù)。建模算法吃的是數(shù)據吐的是預測SPXY 吃的是數(shù)據吐的是“怎么分”。它跑完之后模型還是那個模型只是喂進去的樣本換了。所以 SPXY 的效果不能單獨評價只能通過“用了它之后模型在測試集上的表現(xiàn)”間接體現(xiàn)。順帶提一句拼寫。有些資料里寫成sxpy其實就是 SPXY 的手誤兩者指的是同一個東西。搜索的時候兩種寫法都試一下能多找到一些材料。4.2 誤區(qū)二KS 和 SPXY 只差一個字母可以隨便換易混點流程一模一樣代碼只改一行距離矩陣結果看起來也差不多于是很多人默認兩者等價。為什么混在 Y 分布本來就均勻的數(shù)據集上兩者的選擇結果確實高度重合跑出來的模型精度也相差無幾。這時候換不換指標上看不出來。怎么區(qū)分把兩份劃分結果各自的 Y 直方圖拉出來比。如果 Y 分布均勻兩者幾乎一樣用 KS 更省事如果 Y 有兩端稀薄、中間堆積的情況SPXY 的訓練集會明顯更均衡地覆蓋兩端KS 則可能在中段扎堆。判斷標準不在算法本身在你的數(shù)據長什么樣。另外KS 的計算量少一個距離矩陣樣本上萬時這個差別不小值得納入考慮。4.3 誤區(qū)三做了交叉驗證就不用留測試集易混點k 折交叉驗證給出的分數(shù)看起來很嚴謹每一折都當過驗證集感覺已經把泛化能力測透了。為什么混兩者測的都是“模型在沒見過的樣本上表現(xiàn)如何”聽起來像同一件事的兩種叫法。怎么區(qū)分交叉驗證發(fā)生在訓練集內部用途是選超參數(shù)、比模型結構測試集是完全不參與任何決策的一批樣本只在最后用一次。區(qū)別在“用了幾次”。交叉驗證會被反復用——試十組參數(shù)每組都跑一遍交叉驗證取分數(shù)最高的那組。這個過程里驗證集的信息已經通過你的選擇滲進了模型。測試集如果也這么用它就不再是獨立的考卷而是變成了第二個驗證集指標會越來越樂觀越來越不真實。正確的順序是訓練集內部做交叉驗證定參數(shù)模型定死之后測試集只跑一次跑完就封存。4.4 誤區(qū)四測試集指標不好看調一調再測一次易混點第一次測出來誤差偏大回去改了改特征再測一遍好多了于是認為模型進步了。為什么混這個操作在流程上完全跑得通代碼也不會報錯結果也確實變好了看不出問題。怎么區(qū)分問題在于“變好”的原因。你看到了測試集的結果然后根據這個結果改了模型——這一步之后測試集的信息就已經泄漏進模型了。第二次的指標不再是無偏估計而是“針對這份考卷調過參”的成績。改一次還能接受改十次之后那個指標基本沒什么參考價值了。真要做這一步就把原測試集降級成驗證集另外再劃一批從來沒碰過的樣本當新測試集。樣本不夠劃怎么辦那就別調接受第一次的結果或者承認這份數(shù)據的結論強度有限。4.5 誤區(qū)五比例照著 7:3 抄就不會錯易混點到處都在寫 7:3、8:2仿佛這是某種標準答案。為什么混大多數(shù)教程的示例數(shù)據樣本量都在幾百上千那個量級下比例怎么定影響都不大于是比例被當成了固定參數(shù)。怎么區(qū)分比例的本質是“測試集要裝多少個樣本才夠支撐一個可信的判斷”。樣本量 50 的時候7:3 意味著測試集 15 個一個樣本預測偏了指標就跳一大截這時候 2:1 讓測試集到 17 個也沒什么本質改善反而訓練集更緊張。樣本量 50 這種小數(shù)據集更實際的做法是留一交叉驗證或者重復多次隨機劃分取平均而不是糾結一個固定比例。反過來樣本量上萬的時候測試集留 10% 就是一千個統(tǒng)計上足夠穩(wěn)多留純屬浪費訓練數(shù)據。比例跟著樣本量和任務難度走不跟著教程走。5. 什么時候該用 SPXY什么時候該收手把前面幾章的意思收成一句話SPXY 是解決“Y 分布不均、樣本又少”這個組合問題的工具脫離這個前提它的優(yōu)勢就不成立。下面這張表可以直接對著自己的數(shù)據比。你的情況建議做法樣本量一百出頭參考值跨度大、兩端樣本稀少SPXY訓練集比例放寬到 2:1 或 3:1樣本量幾百以上Y 分布本身均勻KS 或隨機劃分都行省計算樣本按批次、按時間采集存在系統(tǒng)漂移先按批次分層再在每層內部用 SPXY存在同一樣品的多次測量按樣品分組劃分絕不按行號隨機關心的是超出歷史范圍的外推能力SPXY 劃分后手動從兩端各抽樣本補進測試集樣本量不到 30別硬分測試集用留一交叉驗證Y 是多列輸出且量綱差異大先對每列 y 標準化再算聯(lián)合距離需要收手的場面主要有兩類。一類是數(shù)據量本來就撐不起劃分。樣本二三十個硬切出 20% 做測試集測試集只有五六個樣本任何一個偏了誤差就翻倍。這時候與其糾結 SPXY 還是 KS不如老老實實做留一交叉驗證把每一個樣本都當過一次驗證樣本雖然計算量大一點但結論穩(wěn)得多。劃分方法的價值建立在“測試集樣本數(shù)夠多”這個前提上前提不成立方法再精巧也白搭。另一類是 Y 分布本身很均勻。這時候 SPXY 多算的那份 Y 距離幾乎不會改變選擇結果付出的計算成本換不來對應的收益。樣本量上千的時候兩個距離矩陣的內存占用也不是小數(shù)目。用 KS甚至用固定種子的隨機劃分效果一樣代碼還短。還有一個需要主動放棄的場景如果數(shù)據里存在大量測量錯誤、標簽可疑的樣本SPXY 會把它們優(yōu)先挑出來塞進訓練集——因為它挑選的依據就是“最遠”而錯誤樣本往往正好最遠。這時候該先做的是數(shù)據清洗而不是換劃分方法。劃分解決的是“怎么分得公平”解決不了“數(shù)據本身靠不靠得住”。具體到一個例子某次做近紅外光譜的定量模型樣本 80 個參考值從低到高分三段中間一段樣本占了七成。第一次用隨機劃分測試集里一個高值樣本都沒有交叉驗證的分數(shù)漂亮但模型一上真實樣品就系統(tǒng)性偏低。換成 SPXY 之后訓練集的 Y 覆蓋了全段測試集也落在合理范圍內指標從 0.98 掉到了 0.87——數(shù)字變難看了但這次的 0.87 是可信的。如果當時只看那個 0.98 就交付后面踩的坑只會更深。從那以后我每次分完樣本集都會先把兩組樣本的 Y 分布畫出來并排比一眼確認測試集不是一片空白才動手建模。這個動作花不了兩分鐘能擋掉大部分看起來很美的假成績。希望幫到你。本文還有配套的精品資源點擊獲取