本系列第 5 篇|這篇回答:為什麼模型好不好,大半在資料進模型之前就決定了? 讀完這篇,你應該能:
- 依缺失與任何變數、其他已觀察變數、或該值本身的關係,判斷 MCAR、MAR、MNAR,並在刪除、插補與缺失指示欄之間選擇
- 依變數有沒有順序、類別數是否很多、下游模型是否使用距離或梯度,選擇編碼與縮放
- 辨認前處理參數用全體資料計算時造成的資料洩漏,並改成先切分、只在訓練集 fit
一、原理:模型只會學到你餵的東西
模型調整參數時,依據的是送進去的欄位。空值被填成某個常數、沒有順序的縣市被編成會比較大小的整數、標準差用測試集一起算出來,這些都會進到它學到的規律裡。清理與前處理要決定兩件事:每個欄位以什麼形式交出去,以及轉換所用的數字是從哪一批資料算出來的。
資料長什麼樣子、從哪裡進來
半結構化與非結構化要先整理成欄位,編碼與縮放才接得上。
詳細說明
結構化資料是表格,一列一筆、一欄一個欄位,缺值處理與編碼直接做在欄位上。半結構化資料有層級,欄位卻不固定,例如 JSON、XML 與 log。非結構化資料沒有固定欄位,例如文字、影像與音訊。後兩類要先整理成欄位,後面的編碼與縮放才接得上。
收集管道有資料庫匯出、API、網路爬蟲、物聯網(IoT)感測器與應用程式日誌。爬蟲必須遵守網站的 robots.txt 與服務條款。節奏分成兩種。批次收集一次取出一段期間的資料。即時串流收集則持續寫入。
六個品質面向
交出去前用六個面向檢查:正確、完整、一致、時效、唯一、有效。
詳細說明
交出去之前,用六個面向檢查這張表。
- 正確性:值是否符合事實。
- 完整性:該有的列與欄位是否都在。
- 一致性:單位、格式、文字編碼是否統一。
- 時效性:這批資料是否還對得上現在要預測的情境。
- 唯一性:同一筆是否被重複記入。
- 有效性:值是否落在合理範圍。
缺失是完整性的問題,單位混用是一致性的問題,重複列破壞唯一性,不可能的值破壞有效性。登錄錯誤可以修正。資料若已過時,把空格填上也不會使它對上現在的情境。
前處理參數的學習範圍
先切出三個資料集,前處理參數只在訓練集 fit,避免資料洩漏。
詳細說明
平均、標準差、最小值、最大值、中位數、四分位距(IQR)與類別對應,都是前處理的參數。這些參數只能用訓練集計算,再把同一組參數套到驗證集與測試集。
用全體資料計算時,測試集的特徵值會改寫這些參數。一個特別大的測試點會把全體的最大值或標準差拉高,訓練列轉換之後的數字跟著改變。模型在訓練階段就用到了測試集的特徵分佈。這叫做資料洩漏(data leakage)。離線評估會過度樂觀。上線遇到沒有參與當時計算的新資料,分數就往下掉。
順序因此固定:先切出訓練集、驗證集與測試集,再在訓練集上 fit,然後用同一個轉換器 transform 另外兩份。驗證集與測試集只接受套用,不參與這些參數的計算。
二、缺失、編碼、縮放與特徵
處理法由三件事決定:缺是怎麼缺的、欄位是類別還是數值、下游模型用的是距離、梯度還是閾值。凡是要從資料估計的參數,都只在訓練集上估計。
缺失值:先判定機制
把 MNAR 當成 MCAR 再刪列或平均插補,估計會產生偏誤。
詳細說明
完全隨機缺失(MCAR,Missing Completely At Random)指缺不缺與任何變數都無關。隨機缺失(MAR,Missing At Random)指缺不缺與其他已經觀察到的變數有關,與這個欄位自己的值無關。例如某工廠的夜班比較常漏填溫度,而班別欄位是有記錄的。非隨機缺失(MNAR,Missing Not At Random)指缺不缺與這個值本身有關。高所得者比較不填所得,就是因為所得高才不填。
三者不能互換。把 MNAR 當成 MCAR,等於認定空值沒有集中在某一種人身上。後面若刪除或用平均插補,就會把這個篩選忽略掉,估計產生偏誤。
缺失少,而且有理由相信是 MCAR 時,刪除那一列比較安全:列被拿掉的理由與變數的值無關。缺得很多時,刪列會把其他欄位一起丟掉。
平均數、中位數與眾數插補實作簡單。數值常用平均數或中位數,類別常用眾數。空格被拉向集中趨勢之後,這欄的變異被壓縮,和別欄的關係也變弱。欄位裡已有離群值時,中位數比平均數穩,因為平均數會被極端值拉走。
時間序列必須保留順序。前值填補(forward fill)用中斷前最後一筆往後補;內插則依前後的觀測把中間補上。感測器每 5 分鐘一筆、中斷 10 分鐘,空的是時間軸上的短缺口,適合這兩種做法。前值填補讀的是表上的上一列,列要先依時間排好,否則上一列不是上一個時點。整欄平均會把不同時段合成一個常數,時間結構就沒了。
K 最近鄰插補(KNN imputation)用相似的其他列來估這個空格。模型預測插補另建一個模型,用其餘欄位預測這個欄位。兩者都用得上其他欄位,所以在 MAR、缺失與已觀察變數有關時,比整欄平均多利用那些線索。這裡的 KNN 是在補值,和後面當成預測模型的 KNN 不是同一步。補值模型自己的參數也只能從訓練集學。
缺失本身若有意義,尤其是 MNAR,可以加一個缺失指示欄(missing indicator):這一格原本是空的就記 1,否則記 0。模型就能把「沒填」當成資訊。MNAR 時若直接刪除這些列,或用平均數填成一個普通值,偏誤會留在表裡。高所得者不填所得就是這種情況:刪列會少掉高所得者,平均插補會把他們拉向中間。
離群值:先分開錯誤與極端
偵測只標示離群值,錯誤修正或刪除,真實極端可截尾,界線用訓練集訂。
詳細說明
偵測常用三條。三條都只負責標示,不負責決定刪不刪。
IQR = Q3 − Q1,也就是第三四分位數減第一四分位數。1.5×IQR 規則把落在 Q1 − 1.5×IQR 之下或 Q3 + 1.5×IQR 之上的點標成離群值。
|z| > 3 用的是 z = (x − 平均) / 標準差。這個點離平均超過 3 個標準差就標出來。平均與標準差會被極端值拉動,所以門檻本身也受極端值影響。
領域知識不看統計門檻。年齡 200 在業務上不可能,直接視為無效值。
標出來之後先問:這是登錄錯誤,還是真實發生的極端?錯誤就修正或刪除。真實極端若直接刪掉,模型上線後仍可能遇到這種情況,卻沒學過。可以截尾(winsorize/capping),把超出界線的值改寫成界線上的值;也可以取對數,或改用對極端值比較不敏感的方法。用 IQR 或 z 訂出的界線也是參數,界線用訓練集來訂。
重複與不一致
重複列要去除,否則模型反覆看到同一筆,單位與格式也要收成同一套。
詳細說明
同一筆出現多次就去除重複列,否則模型會反覆看到同一筆,唯一性也不成立。不一致則把單位、日期格式、大小寫與文字編碼收成同一套。同一長度一半用公分、一半用公尺,模型會把單位差當成真實差異。
類別怎麼變成數字
名目變數的編號差不是距離,目標編碼必須用折外平均。
詳細說明
標籤編碼(Label Encoding)與順序編碼(Ordinal Encoding)把類別換成整數。滿意度這種有順序的變數適合:1 到 5 級的數字大小對應程度。名目變數沒有順序。若仍編成 1、2、3,邏輯斯迴歸(logistic regression)這類線性模型會把數字大小放進係數,以為編號差就是距離。決策樹(decision tree)與隨機森林(random forest)是在找切點,對這種假順序較不敏感。
One-Hot 編碼為每個類別開一欄,屬於該類別就是 1,否則是 0。它不假設順序。類別一多就成為高基數(high cardinality):上萬個商品 ID 各開一欄,維度爆炸,矩陣幾乎全是 0,又稀疏。居住縣市若是固定的一小群類別,One-Hot 仍然合適。有哪些類別、各類別對應哪一欄,也是類別對應,用訓練集決定。
目標編碼(Target Encoding)用該類別的目標平均值取代類別本身。高基數時欄位仍只有一欄。計算必須用交叉驗證(cross-validation)的折外(out-of-fold)結果:某一列的編碼值,要用沒有包含這一列的那些折算出的平均。若用含自己的全體平均,該列的目標會漏進特徵,這是目標資訊的資料洩漏。
頻率編碼用類別出現次數取代類別名稱。它不使用目標值,因此沒有目標洩漏。次數仍然是從資料估出來的參數,只能在訓練集上算,再套到驗證集與測試集。它保留的是「這個類別有多常見」。
數值要不要縮放
距離或梯度方法需要縮放,決策樹與隨機森林對縮放不敏感。
詳細說明
標準化(Z-score)就是 (x − 平均) / 標準差。轉換後這欄的平均是 0、標準差是 1。平均與標準差會被極端值拉動,少數超大額會讓中心與尺度都不穩。
Min-Max 正規化是 (x − min) / (max − min),結果落在 [0, 1]。分母是全距。最大值若是極端大額,分母變大,其他值被擠在靠近 0 的一段。它對離群值敏感。
Robust Scaling是 (x − 中位數) / IQR。中位數與 IQR 不跟著少數極端值跑。交易金額有少數超大額時,這個縮放比 Min-Max 與標準化穩。
需要縮放的是以距離或梯度為基礎的方法:K 最近鄰(KNN)、K-means、支援向量機(SVM)、神經網路、含正則化的線性模型,以及主成分分析(PCA)。KNN、K-means 與 SVM 的計算依賴距離,某一欄的數字範圍特別大,結果就幾乎由那一欄決定。神經網路的梯度會被尺度大的欄位拉著走。正則化懲罰係數的大小;欄位因為單位而數字很大時,係數會變小來配合,懲罰就混進了單位。PCA 沿著變異較大的方向走,單位造成的大數字會顯得變異很大。
決策樹與隨機森林沿單一欄位找閾值來切分,不計算欄位之間的距離,對縮放不敏感。
特徵工程
把舊欄位改寫成模型能直接用的新欄:分箱、衍生、交互作用、取對數。
詳細說明
離散化(分箱)把連續值切成區間。時間戳記可以切成早上、下午、晚上。切完之後,欄位表示的是時段,不再是一串會隨日期增大的秒數。
特徵衍生用舊欄位算出新欄位。體重指數寫成 BMI = 體重 / 身高²。從時間戳記取出星期幾。平均客單價寫成 平均客單價 = 總金額 / 次數。新欄位把已經存在的關係寫成模型可以直接使用的一欄。
交互作用特徵由兩個欄位組合而成,用來表示兩者同時發生才出現的效果。只用加總的線性模型,原本只能分別看到兩個欄位各自的貢獻。
右偏、少數值特別大的變數可取對數。取對數之後,大值之間的差距被壓縮,長尾縮短,單點較不容易拉住整欄的尺度。
程式對照
查缺值不估參數,中位數、縮放、One-Hot 只從訓練集 fit。
詳細說明
下面假設 df 是完整資料表,X_train 與 X_test 是已經切好的數值欄位表。df.isna().sum() 只是看每欄缺多少,不估計任何參數,可以在切分前做。前值填補 ffill 只看同一條時間序列的上一筆,列要先依時間排好。中位數是參數,所以從 X_train 算出來,再用同一個值填訓練集與測試集。若寫成 df.fillna(df["x"].median()),會把 x 的中位數填進每一欄,而且用到了測試集。三個縮放器的 fit、transform 位置相同,這裡以標準化為例。
import pandas as pd
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
df.isna().sum() # inspect only
df = df.sort_values("time")
df["sensor"] = df["sensor"].ffill() # previous reading in time order
med = X_train["x"].median() # learned from the training set only
X_train["x"] = X_train["x"].fillna(med)
X_test["x"] = X_test["x"].fillna(med)
scalers = {
"zscore": StandardScaler(),
"minmax": MinMaxScaler(),
"robust": RobustScaler(),
}
scaler = scalers["zscore"]
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)
把字典的鍵換成 minmax 或 robust,fit 與 transform 的位置不變,參數仍只從 X_train 來。One-Hot 也一樣:pd.get_dummies(df, columns=["city"]) 寫起來最短,但類別清單取自整張表;正式流程用 scikit-learn 的 OneHotEncoder(handle_unknown="ignore") 在訓練集 fit,測試集出現訓練時沒看過的縣市時就全填 0。
三、容易混淆的地方
實務上常有兩種前處理做法都「聽起來有道理」。差別在假設,以及參數從哪一批資料來。
| 容易混淆 | 差別在哪 | 實際遇到時的線索 |
|---|---|---|
| 標籤編碼與 One-Hot | 標籤編碼換成整數,數字大小會被當成順序。One-Hot 一類一欄,不假設順序;類別很多時又寬又稀疏。樹模型對假順序較不敏感,線性模型會當真 | 資料欄位是「5 級、有順序」且下游為邏輯斯迴歸時,採順序編碼;欄位是「縣市、無順序」時用 One-Hot。程式碼審查若說 One-Hot 能保留等級順序,理由不成立 |
| 標準化、Min-Max、Robust Scaling | 標準化用平均與標準差,得到平均 0、標準差 1;這兩個統計量會被極端值拉動。Min-Max 用最小與最大,壓到 [0, 1],一個極端值就把其餘的點擠在一起。Robust Scaling 用中位數與 IQR | 欄位有少數極端大額且下游是 KNN 這類距離方法時,考慮 Robust Scaling。程式碼審查若說 KNN 不受尺度影響,就是把它和決策樹、隨機森林搞混了 |
| MCAR、MAR、MNAR | MCAR 與任何變數無關。MAR 與其他已觀察變數有關,與該值本身無關。MNAR 與該值本身有關 | 缺失紀錄顯示高所得者不填所得,較符合 MNAR;已記錄的夜班比較常漏填,較符合 MAR;漏填與任何欄位無關時,才符合 MCAR |
| 刪除列與插補 | 缺失少且 MCAR 時,刪列較安全。插補留住其他欄位;平均數、中位數、眾數會壓縮變異。MNAR 時刪列與平均插補都會產生偏誤 | 資料清理紀錄顯示缺失少且與值無關時,刪列較合適;若時間軸上有短缺口,不用整欄平均,而用前值填補或內插 |
| 在訓練集 fit 與在全體資料 fit | 平均、標準差、最小、最大、中位數、IQR、類別對應與類別次數,都只能用訓練集計算,再 transform 驗證集與測試集。全體一起算,測試集特徵會改寫訓練列的轉換結果 | 流程紀錄顯示先對全部資料標準化再切分,測試分數好、上線變差,這是資料洩漏。標準化公式本身可以留,要調整的是 fit 範圍 |
四、基礎練習
第 1 題
某電商要用邏輯斯迴歸預測顧客會不會回購。滿意度是 5 級,等級之間有順序。居住縣市有 22 類,類別之間沒有順序。編碼應該怎麼配?
- (A) 兩個欄位都做標籤編碼。整數比多欄 0/1 省欄位,線性模型也可以直接使用整數。
- (B) 兩個欄位都做 One-Hot 編碼。One-Hot 能把滿意度 1 到 5 級的順序保留在欄位裡。
- (C) 滿意度用順序編碼,居住縣市用 One-Hot 編碼。
- (D) 滿意度用 One-Hot 編碼,居住縣市用順序編碼。縣市可以排成一段整數。
看答案與解析
答案:C
滿意度的 1 到 5 級有程度差異,順序編碼的整數大小對應這個程度,邏輯斯迴歸的係數可以沿這個方向使用。居住縣市是名目變數,One-Hot 一類一欄,不假設縣市之間有距離。
- (A) 居住縣市沒有順序。兩個欄位都做標籤編碼時,線性模型會把縣市代碼的大小當成有意義的差。
- (B) One-Hot 的每一欄只是 0 或 1,欄與欄之間沒有寫入等級順序。
- (D) 把沒有順序的縣市做順序編碼,正是線性模型會誤用數字大小的情況。
第 2 題
某電商的商品 ID 有 5 萬個類別。哪一個處理判斷正確?
- (A) 做 One-Hot 編碼最好。商品 ID 沒有順序,One-Hot 又不假設順序,類別再多也適合。
- (B) One-Hot 會一類一欄,維度爆炸且矩陣稀疏。可以改用目標編碼,但必須用交叉驗證的折外結果來算;也可以用頻率編碼。
- (C) 類別太多就一定刪掉這個欄位。刪除是高基數欄位最好的處理。
- (D) 把商品 ID 的編號做 Min-Max 正規化,壓到 [0, 1] 再送進模型。
看答案與解析
答案:B
5 萬個類別若 One-Hot,就會多出 5 萬個幾乎全是 0 的欄位,維度爆炸且矩陣稀疏。目標編碼改成該類別的目標平均,欄位仍只有一欄;這個平均必須用折外資料計算,否則該列自己的目標會漏進特徵。頻率編碼改成出現次數,不使用目標值,也避開這種目標洩漏。次數仍只在訓練集上算。
- (A) 沒有順序假設是 One-Hot 的優點,但類別數是 5 萬時,維度與稀疏會蓋過這個優點。
- (C) 刪除會丟掉「是哪一種商品」。高基數仍可用折外的目標編碼或頻率編碼保留資訊,不是一定刪除最好。
- (D) Min-Max 是數值縮放。商品 ID 的編號沒有最小與最大所代表的距離。
第 3 題
某電商的交易金額大多是小額,少數幾筆特別大。這個欄位接下來要交給 KNN 做預測。縮放應該怎麼選?
- (A) 做 Min-Max 正規化。極端大額會定住兩端,其他金額就會被均勻分開。
- (B) 不必縮放。KNN 找的是鄰居,不受欄位尺度影響。
- (C) 做 One-Hot 編碼,把每個金額當成一個類別。
- (D) 做 Robust Scaling,用中位數與 IQR 當中心與尺度。
看答案與解析
答案:D
交易金額有少數極端大額,KNN 又用距離決定鄰居。Robust Scaling 寫成 (x − 中位數) / IQR。中位數與 IQR 不跟著少數極端大額移動,大單較不會獨占距離。
- (A) Min-Max 的分母是
max − min。極端大的 max 把分母拉大,其他金額擠在一起,不是被均勻分開。 - (B) KNN 以距離為基礎,欄位尺度會改變誰靠近誰。對縮放不敏感的是決策樹與隨機森林。
- (C) One-Hot 是類別編碼。交易金額是連續數值,不是要為每個金額開一欄。
第 4 題
某工廠的薪資調查裡,所得較高的員工經常把所得留空,其他題目仍有作答。哪一個判斷正確?
- (A) 這是 MNAR。缺失與所得這個值本身有關。直接刪除這些列,或用平均數插補,都會產生偏誤。
- (B) 這是 MCAR。留空與所得高低無關,只是隨機沒填到。
- (C) 這是 MAR。其他題目有填,所以缺失只與已觀察變數有關,與所得本身無關。
- (D) 不必分辨機制。無論是哪一種缺失,刪除與平均插補都不會產生偏誤。
看答案與解析
答案:A
所得高的人不填所得,缺不缺取決於該值本身,符合 MNAR。刪除這些列會少掉高所得者。平均插補把他們填成接近全體平均的數。兩種做法都改寫了所得的分佈,產生偏誤。
- (B) MCAR 要求缺失與任何變數無關。這裡缺失與所得高低有關。
- (C) MAR 是缺失與其他已觀察變數有關、與這個欄位自己的值無關。本題的原因是所得本身。
- (D) 機制會改變刪除與平均插補有沒有偏誤。MNAR 時這兩種處理都會偏。
第 5 題
某工廠的溫度感測器每 5 分鐘寫入一筆。某段連續 10 分鐘沒有新資料,前後時段都正常。要補上這個空檔,哪一種做法符合時間序列?
- (A) 用整欄溫度的平均填進空檔。平均代表這台感測器的典型狀態。
- (B) 依時間順序做前值填補,或用空檔前後的觀測做內插。
- (C) 先把資料列的時間順序打亂,再從其他時點隨機抽值填入空檔。
- (D) 既然出現空檔,就刪除這台感測器的全部紀錄。
看答案與解析
答案:B
空檔落在固定間隔的時間軸上,前後都還有正常讀值。取樣間隔是 5 分鐘,中斷長度是 10 分鐘,這是短缺口,不是整台感測器失效。前值填補沿用中斷前的最後一筆,內插用兩側觀測補中間,兩者都保留時間結構。列要先依時間排好。
- (A) 整欄平均把不同時段合成一個常數,忽略溫度沿時間的變化。
- (C) 打亂順序之後,填入的值不再屬於空檔前後的時間關係。
- (D) 短時間中斷不必丟掉這台感測器其餘時段的紀錄。
第 6 題
某電商的工程師先對全部交易資料計算平均與標準差,做完標準化,再切成訓練集與測試集。測試分數很好,模型上線之後變差。較合理的解釋是哪一個?
- (A) 標準化這個方法本身不該用,應把縮放整個移除。
- (B) 測試集切得太大,所以線下分數不可信。這與標準化的計算範圍無關。
- (C) 模型太簡單。換成較複雜的模型,線上與線下的落差就會消失。
- (D) 這是資料洩漏。應先切分,標準化只在訓練集 fit,再把同一組平均與標準差 transform 到測試集。
看答案與解析
答案:D
全體一起算平均與標準差時,測試集的數值參與了這兩個參數,訓練列的轉換結果會隨測試集改變。離線評估過度樂觀。上線的新資料沒有進入當時的 fit,沿用這組參數時對不上,分數變差。修正是先切分,scaler.fit 只用訓練集,再 transform 測試集。
- (A) 標準化公式沒有錯。距離或梯度類模型本來就需要縮放。這次線下好、線上差,出在 fit 用了全體資料。
- (B) 情境的落差來自分割以前就做了標準化,不是測試集的大小。
- (C) 換成較複雜的模型,仍然是在用被測試集影響過的特徵學習,沒有去掉洩漏。
五、重點回顧
- 先看缺失和誰有關:與任何變數都無關是 MCAR,與其他已觀察變數有關是 MAR,與該值本身有關是 MNAR。所得高才不填,是 MNAR,刪列與平均插補都會產生偏誤。
- 缺失少且為 MCAR,可以刪列。要留住其他欄位時,平均數、中位數、眾數插補會壓縮變異。沿時間的短空檔用前值填補或內插,而且列要先依時間排好。想保留「空著」這件事,就加缺失指示欄。
- 有順序才用順序編碼。沒有順序、類別數可控,用 One-Hot。類別數上萬時,One-Hot 又寬又稀疏,改折外計算的目標編碼,或改頻率編碼。線性模型會相信標籤編碼的數字大小;決策樹與隨機森林對這種假順序較不敏感。
- 有少數極端大額、又要交給距離類模型時,用 Robust Scaling,
(x − 中位數) / IQR。Min-Max 的分母被最大最小值拉大,其餘的點會擠在一起。標準化(x − 平均) / 標準差得到平均 0、標準差 1,但平均與標準差會被極端值拉動。KNN、K-means、SVM、神經網路、含正則化的線性模型與 PCA 需要縮放;決策樹與隨機森林不敏感。 - 離群值先問是錯誤還是真實極端。錯誤修正或刪除。真實極端可截尾或取對數。偵測門檻是
Q1 − 1.5×IQR、Q3 + 1.5×IQR與|z| > 3。年齡 200 這類不可能的值直接用領域知識。這些界線只用訓練集來訂。 - 平均、標準差、最小、最大、中位數、IQR、類別次數與類別對應,都只在訓練集 fit,再 transform 到驗證集與測試集。目標編碼還得用折外平均,避免該列自己的目標漏進特徵。先轉換完全部資料再切分,就是資料洩漏,離線評估會過度樂觀。