編碼是機器學習流程里最容易被低估的環節之一,模型沒辦法直接處理文本形式的分類數據,尺寸(Small/Medium/Large)、顏色(Red/Blue/Green)、城市、支付方式等都是典型的分類特征,必須轉成數值才能輸入到模型中。
那么問題來了:為什么不直接把 Red 編成 1,Blue 編成 2?這個做法看起來簡單粗暴,但其實藏著大坑。下面用一個小數據集來說明。
數據集概述
Feature | Description
-------------------|----------------------------------------------------------
customer_id | Unique customer identifier
gender | Male or Female
education_level | High School → Associate → Bachelor's → Master's → PhD
employment_status | Full-time, Part-time, Self-employed, Unemployed
city | Customer's city (50+ US cities)
product_category | Electronics, Clothing, Books, Sports, Home & Garden, Beauty, Food & Beverage
payment_method | Credit Card, Debit Card, PayPal, Cash
customer_tier | Bronze → Silver → Gold → Platinum
satisfaction_level | Dissatisfied → Neutral → Satisfied → Very Satisfied
credit_score_range | Poor → Fair → Good → Very Good → Excellent
purchase_amount | Purchase amount in USD
will_return | Yes or No (target variable)
Ordinal Encoding
Ordinal Encoding 思路很簡單:給每個類別分配一個數字,但是模型會把這些數字當作有序的。
假設對payment_method做編碼:Cash = 1,PayPal = 2。模型會認為 Cash < PayPal,仿佛 PayPal 比 Cash "更好" 或 "更大"。但支付方式之間根本沒有這種大小關系因為它們只是不同的選項而已。
什么時候 Ordinal Encoding 才合適?當數據本身就存在真實的順序關系時。比如education_level:High School < Associate < Bachelor's < Master's < PhD。這是客觀存在的遞進關系,用數字表示完全沒問題,模型的理解也是對的。
所以 Ordinal Encoding 的使用場景很明確:只用于那些排名確實有意義的特征。
from sklearn.preprocessing import OrdinalEncoder
ordEnc = OrdinalEncoder()
print(ordEnc.fit_transform(data[["education_level"]])[:5])
# Output
"""
[[1.]
[2.]
[3.]
[4.]
[2.]]
"""
One-Hot Encoding
One-Hot Encoding 換了個思路:不用數字而是給每個類別創建一列。payment_method有 4 個值,就變成 4 列,每行只有一個位置是 1,其余全是 0。
| payment_cash | payment_credit_card | payment_debit_card | payment_paypal |
|--------------|---------------------|--------------------|----------------|
| 1 | 0 | 0 | 0 |
| 0 | 1 | 0 | 0 |
| 0 | 0 | 1 | 0 |
| 0 | 0 | 0 | 1 |
這樣做的好處是消除了虛假的順序關系,所有類別被平等對待和線性模型配合得也很好。
那么代價是什么?維度會膨脹。customer_tier和payment_method各 4 個值,合起來就是 8 列。如果遇到城市這種特征,50 多個類別直接炸成 50 多列,維度災難就來了。
from sklearn.preprocessing import OneHotEncoder
oneEnc = OneHotEncoder()
print(oneEnc.fit_transform(data[["customer_tier", "payment_method"]]).toarray()[:5])
#output
"""
[[0. 1. 0. 0. 0. 1. 0. 0.]
[0. 0. 0. 1. 0. 0. 1. 0.]
[0. 0. 1. 0. 0. 0. 0. 1.]
[0. 1. 0. 0. 0. 1. 0. 0.]
[1. 0. 0. 0. 1. 0. 0. 0.]]
"""
Target Encoding
面對高基數特征(比如 City 有 50 多個值)One-Hot Encoding 會把特征空間撐得太大,Target Encoding 的做法是:用每個類別對應的目標變量均值來替換。也叫 Mean Encoding。
舉個例子,目標變量是will_return(Yes = 1,No = 0):
| City | will_return |
|-----------|-------------|
| Austin | 1 |
| Austin | 1 |
| New York | 1 |
| New York | 0 |
| New York | 0 |
| New York | 0 |
| New York | 1 |
計算每個城市的目標均值:Austin → (1 + 1) / 2 = 1.0,New York → (1 + 0 + 0 + 0 + 1) / 5 = 0.4,這樣得到的編碼結果就是:
| City | Encoded Value |
|----------|----------------|
| Austin | 1.0 |
| New York | 0.4 |
這里有一個坑,Austin 只出現了 2 次而且剛好都是正例,編碼值直接變成 1.0。模型可能會 "學到" 一個規律:看到 Austin 就預測 will_return = Yes。
但這個 "規律" 完全是數據量不足造成的假象。樣本太少均值就很不可靠。
Smoothing 的思路是把類別均值往全局均值方向 "拉" 一拉。公式:
Encoded Value = (w * Category Mean) + ((1 - w) * Global Mean)
其中 Category Mean 是該類別的目標均值Global Mean 是整個數據集的目標均值,w 是一個和樣本量相關的權重。樣本越少w 越小,編碼值就越接近全局均值;樣本越多類別自己的均值就越占主導。這能有效抑制小樣本帶來的過擬合。
另一個問題就是 Data Leakage。如果用全量數據計算編碼值再把這個編碼喂給模型,模型等于直接 "看到了" 答案的統計信息。比如模型發現 City = 0.34 對應的樣本大概率是 will_return = Yes,那它干脆走捷徑,不從其他特征里學東西了。
所以就要引入交叉驗證,以 5 折為例:把數據分成 5 份,對第 1 份的數據,用第 2 到第 5 份來計算編碼;對第 2 份的數據,用第 1、3、4、5 份來計算編碼;以此類推。每個樣本的編碼值都來自于它 "沒見過" 的數據,泄露就切斷了。
但是副作用是同一個城市在不同折里的編碼值會略有差異:New York 在 Fold 1 里可能是 0.50,在 Fold 2 里是 0.45。但這反而是好事,這樣可以讓模型被迫學習更一般化的模式而不是死記某個精確數值。
Target Encoding 的優點:避免維度爆炸,適合高基數特征,還能把目標變量的統計信息編進去。
但用的時候得小心:必須加 Smoothing 防止小樣本過擬合,必須用交叉驗證防止數據泄露。
from sklearn.preprocessing import TargetEncoder
data["will_return_int"] = data["will_return"].map({"Yes": 1, "No": 0})
tarEnc = TargetEncoder(smooth="auto", cv=5) # Those are the default value
print(data[["city"]][:5])
print(tarEnc.fit_transform(data[["city"]], data["will_return_int"])[:5])
"""
city
0 Houston
1 Phoenix
2 Chicago
3 Phoenix
4 Phoenix
[[0.85364466]
[0.69074308]
[0.65024828]
[0.74928653]
[0.81359495]]
"""
總結
三種編碼方法各有適用場景,選擇取決于特征本身的性質。
實際操作中可以這樣判斷:特征有天然順序就用 Ordinal Encoding;沒有順序、類別數量也不多就用 One-Hot Encoding;類別太多就上 Target Encoding,記得配合 Smoothing 和交叉驗證。
真實項目里,一個數據集往往會同時用到這三種方法。
本文轉自:DeepHub IMBA
作者: adham ayman
-
編碼
+關注
關注
6文章
1037瀏覽量
56910 -
機器學習
+關注
關注
66文章
8550瀏覽量
136797
發布評論請先 登錄
【下載】《機器學習》+《機器學習實戰》
機器學習的特征工程是將原始的輸入數據轉換成特征
機器學習特征工程:分類變量的數值化處理方法
評論