機器學習模型處理不了原始文本。無論是線性迴歸、XGBoost還是神經網絡,遇到
"red"
、
"medium"
、
"CA"
這類分類變量都沒法直接處理。所以必須把它們轉成數字這個過程就是分類編碼。
大家入門時肯定都學過獨熱編碼或序數編碼,但編碼方法其實非常多。目標編碼、CatBoost編碼、James-Stein編碼這些高級技術,用對了能給模型帶來質的飛躍,尤其面對高基數特徵的時候。
編碼到底有多重要
拿
"Toyota"
舉例,它本身沒有數值含義,但模型只認數字:
{"Toyota": 0, "Ford": 1, "Honda": 2}
或者寫成向量形式:
[0, 1, 0]
更高級的做法是直接編碼成目標相關的數值:
Toyota → +0.12 mean adjusted uplift in target
編碼方式選得好不好,直接影響模型準確率、可解釋性、過擬合程度、訓練速度、內存佔用,還有對稀有類別的處理能力。
示例代碼準備
後面所有例子都基於這個簡單數據集:
import pandas as pd
from sklearn.model_selection import train_test_split
import category_encoders as ce
from sklearn.linear_model import LogisticRegression
df = pd.DataFrame({
"color": ["red", "blue", "green", "green", "blue", "red"],
"city": ["NY", "LA", "NY", "SF", "LA", "NY"],
"target": [1, 0, 1, 0, 0, 1]
})
X = df.drop("target", axis=1)
y = df["target"]
1、序數編碼 Ordinal Encoding
最簡單粗暴的方法,給每個類別分配一個整數。red是0,blue是1,green是2。
XGBoost、LightGBM這類樹模型用這個就夠了。另外當類別本身有順序含義(比如small/medium/large)時也很合適。
encoder=ce.OrdinalEncoder(cols=["color"])
X_trans=encoder.fit_transform(X, y)
2、獨熱編碼 One-Hot Encoding
每個類別單獨開一列,是就標1,不是就標0。
線性迴歸、邏輯迴歸、神經網絡經常用這個。不過類別太多的話列數會爆炸,低基數特徵才適合。
encoder=ce.OneHotEncoder(cols=["color"], use_cat_names=True)
X_trans=encoder.fit_transform(X)
3、 二進制編碼 Binary Encoding
把類別索引轉成二進制。比如索引5變成101,拆成三列。
這個方法在類別數量中等偏多(50-500個)的時候很好使,既保持了稀疏性又比獨熱編碼省內存。
encoder=ce.BinaryEncoder(cols=["city"])
X_trans=encoder.fit_transform(X)
4、Base-N編碼
二進制編碼的泛化版本,可以用任意進制。base=3時,索引5就變成
"12"
。想精細控制輸出維度的話可以試試。
encoder=ce.BaseNEncoder(cols=["city"], base=3)
X_trans=encoder.fit_transform(X)
5、哈希編碼 Hashing Encoding
用哈希函數把類別映射到固定數量的列上。速度極快,輸出寬度固定,也不用存儲類別映射表。
缺點就是:會有哈希衝突而且結果不可解釋。
encoder=ce.HashingEncoder(cols=["city"], n_components=8)
X_trans=encoder.fit_transform(X)
6、Helmert編碼
正交對比編碼的一種,每個類別跟它後面所有類別的均值做比較。統計建模和分類對比迴歸分析會用到。
encoder=ce.HelmertEncoder(cols=["color"])
X_trans=encoder.fit_transform(X, y)
7、求和編碼 Sum Encoding
也叫偏差編碼。每個類別跟總體均值比較,而不是跟某個基準類別比。
encoder=ce.SumEncoder(cols=["color"])
X_trans=encoder.fit_transform(X, y)
8、多項式編碼 Polynomial Encoding
給有序類別生成線性、二次、三次對比項。如果懷疑類別對目標有非線性影響,可以考慮這個。
encoder=ce.PolynomialEncoder(cols=["color"])
X_trans=encoder.fit_transform(X, y)
9、向後差分編碼 Backward Difference Encoding
每個類別跟前面所有類別的均值比較,跟Helmert正好相反。
encoder=ce.BackwardDifferenceEncoder(cols=["color"])
X_trans=encoder.fit_transform(X, y)
10、計數編碼 Count Encoding
直接用類別出現的次數替換類別值。
高基數特徵用這個效果不錯,計算快、結果穩定。只要在訓練集上fit就不會有數據泄露問題。
encoder=ce.CountEncoder(cols=["city"])
X_trans=encoder.fit_transform(X)
11. 目標編碼 Target Encoding
把每個類別替換成該類別下目標變量的均值。
威力很大但有個坑,就是容易造成目標泄露。必須配合平滑處理或者用交叉驗證的方式來做。
encoder = ce.TargetEncoder(cols=["city"])
X_trans = encoder.fit_transform(X, y)
12、CatBoost編碼
CatBoost編碼是目標編碼的改良版。編碼每一行時只用它前面的行來計算,這樣就大大降低了泄露風險。
這是目前最安全的目標編碼方案,高基數特徵、時序數據都能用,效果很穩。
encoder = ce.CatBoostEncoder(cols=["city"])
X_trans = encoder.fit_transform(X, y)
13、留一法編碼 Leave-One-Out Encoding
計算類別的目標均值時把當前行排除掉。既保留了目標編碼的效果,又減輕了泄露。
encoder = ce.LeaveOneOutEncoder(cols=["city"])
X_trans = encoder.fit_transform(X, y)
14、M估計編碼 M-Estimate Encoding
用貝葉斯思想對目標編碼做平滑。
高基數和噪聲目標場景下表現不錯。
encoder = ce.MEstimateEncoder(cols=["city"], m=5)
X_trans = encoder.fit_transform(X, y)
15、WOE證據權重編碼
這是信用評分領域的老朋友了。
邏輯迴歸配WOE是經典組合,可解釋性很強。
encoder = ce.WOEEncoder(cols=["city"])
X_trans = encoder.fit_transform(X, y)
16、James-Stein編碼
基於James-Stein估計的收縮編碼器。能有效降低方差,做分類變量回歸時效果很好。
encoder = ce.JamesSteinEncoder(cols=["city"])
X_trans = encoder.fit_transform(X, y)
17、GLMM編碼
用廣義線性混合模型來編碼。處理層次結構數據或者類別組很大的時候可以一試。
encoder = ce.GLMMEncoder(cols=["city"])
X_trans = encoder.fit_transform(X, y)
18、分位數編碼 Quantile Encoding
不用均值,用目標分佈的分位數來編碼。
encoder = ce.QuantileEncoder(cols=["city"], quantile=0.5)
X_trans = encoder.fit_transform(X, y)
19、RankHot編碼
獨熱編碼的變體,列按類別頻率排序。對樹模型友好。
encoder = ce.RankHotEncoder(cols=["city"])
X_trans = encoder.fit_transform(X)
20、格雷編碼 Gray Encoding
用格雷碼錶示類別,相鄰編碼只差一位。
encoder = ce.GrayEncoder(cols=["city"])
X_trans = encoder.fit_transform(X)
怎麼選編碼器
低基數(<10個類別):獨熱、二進制、序數都行。統計模型的話可以試試求和、Helmert、多項式編碼。
中等基數(10-100):二進制、BaseN、CatBoost、帶平滑的目標編碼。
高基數(100-50000):計數編碼、CatBoost編碼(首選)、留一法、M估計、帶交叉驗證的目標編碼,內存緊張就用哈希編碼。
常見的坑
目標編碼泄露:用CatBoost編碼、交叉驗證或留一法來規避。
樹模型誤讀序數整數:樹模型可能會把序數編碼的數字當連續變量處理,換成獨熱或目標編碼更穩妥。
獨熱編碼維度爆炸:類別太多就別用獨熱了,換二進制、BaseN或哈希。
稀有類別噪聲:M估計、James-Stein或目標平滑能緩解這個問題。
總結
分類編碼是特徵工程裏最容易被忽視卻又最能出效果的環節。scikit-learn自帶的編碼器只是冰山一角,
category_encoders
這個庫才是真正的百寶箱:統計編碼、貝葉斯編碼、哈希編碼、對比編碼應有盡有,用好了模型效果能上一個台階。
作者:Abish Pius