編碼是機(jī)器學(xué)習(xí)流程里最容易被低估的環(huán)節(jié)之一,模型沒辦法直接處理文本形式的分類數(shù)據(jù),尺寸(Small/Medium/Large)、顏色(Red/Blue/Green)、城市、支付方式等都是典型的分類特征,必須轉(zhuǎn)成數(shù)值才能輸入到模型中。
那么問題來了:為什么不直接把 Red 編成 1,Blue 編成 2?這個做法看起來簡單粗暴,但其實藏著大坑。下面用一個小數(shù)據(jù)集來說明。
數(shù)據(jù)集概述
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 思路很簡單:給每個類別分配一個數(shù)字,但是模型會把這些數(shù)字當(dāng)作有序的。
假設(shè)對payment_method做編碼:Cash = 1,PayPal = 2。模型會認(rèn)為 Cash < PayPal,仿佛 PayPal 比 Cash "更好" 或 "更大"。但支付方式之間根本沒有這種大小關(guān)系因為它們只是不同的選項而已。
什么時候 Ordinal Encoding 才合適?當(dāng)數(shù)據(jù)本身就存在真實的順序關(guān)系時。比如education_level:High School < Associate < Bachelor's < Master's < PhD。這是客觀存在的遞進(jìn)關(guān)系,用數(shù)字表示完全沒問題,模型的理解也是對的。
所以 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 換了個思路:不用數(shù)字而是給每個類別創(chuàng)建一列。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 |
這樣做的好處是消除了虛假的順序關(guān)系,所有類別被平等對待和線性模型配合得也很好。
那么代價是什么?維度會膨脹。customer_tier和payment_method各 4 個值,合起來就是 8 列。如果遇到城市這種特征,50 多個類別直接炸成 50 多列,維度災(zāi)難就來了。
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
面對高基數(shù)特征(比如 City 有 50 多個值)One-Hot Encoding 會把特征空間撐得太大,Target Encoding 的做法是:用每個類別對應(yīng)的目標(biāo)變量均值來替換。也叫 Mean Encoding。
舉個例子,目標(biāo)變量是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 |
計算每個城市的目標(biāo)均值:Austin → (1 + 1) / 2 = 1.0,New York → (1 + 0 + 0 + 0 + 1) / 5 = 0.4,這樣得到的編碼結(jié)果就是:
| City | Encoded Value |
|----------|----------------|
| Austin | 1.0 |
| New York | 0.4 |
這里有一個坑,Austin 只出現(xiàn)了 2 次而且剛好都是正例,編碼值直接變成 1.0。模型可能會 "學(xué)到" 一個規(guī)律:看到 Austin 就預(yù)測 will_return = Yes。
但這個 "規(guī)律" 完全是數(shù)據(jù)量不足造成的假象。樣本太少均值就很不可靠。
Smoothing 的思路是把類別均值往全局均值方向 "拉" 一拉。公式:
Encoded Value = (w * Category Mean) + ((1 - w) * Global Mean)
其中 Category Mean 是該類別的目標(biāo)均值Global Mean 是整個數(shù)據(jù)集的目標(biāo)均值,w 是一個和樣本量相關(guān)的權(quán)重。樣本越少w 越小,編碼值就越接近全局均值;樣本越多類別自己的均值就越占主導(dǎo)。這能有效抑制小樣本帶來的過擬合。
另一個問題就是 Data Leakage。如果用全量數(shù)據(jù)計算編碼值再把這個編碼喂給模型,模型等于直接 "看到了" 答案的統(tǒng)計信息。比如模型發(fā)現(xiàn) City = 0.34 對應(yīng)的樣本大概率是 will_return = Yes,那它干脆走捷徑,不從其他特征里學(xué)東西了。
所以就要引入交叉驗證,以 5 折為例:把數(shù)據(jù)分成 5 份,對第 1 份的數(shù)據(jù),用第 2 到第 5 份來計算編碼;對第 2 份的數(shù)據(jù),用第 1、3、4、5 份來計算編碼;以此類推。每個樣本的編碼值都來自于它 "沒見過" 的數(shù)據(jù),泄露就切斷了。
但是副作用是同一個城市在不同折里的編碼值會略有差異:New York 在 Fold 1 里可能是 0.50,在 Fold 2 里是 0.45。但這反而是好事,這樣可以讓模型被迫學(xué)習(xí)更一般化的模式而不是死記某個精確數(shù)值。
Target Encoding 的優(yōu)點:避免維度爆炸,適合高基數(shù)特征,還能把目標(biāo)變量的統(tǒng)計信息編進(jìn)去。
但用的時候得小心:必須加 Smoothing 防止小樣本過擬合,必須用交叉驗證防止數(shù)據(jù)泄露。
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]]
"""
總結(jié)
三種編碼方法各有適用場景,選擇取決于特征本身的性質(zhì)。
實際操作中可以這樣判斷:特征有天然順序就用 Ordinal Encoding;沒有順序、類別數(shù)量也不多就用 One-Hot Encoding;類別太多就上 Target Encoding,記得配合 Smoothing 和交叉驗證。
真實項目里,一個數(shù)據(jù)集往往會同時用到這三種方法。
本文轉(zhuǎn)自:DeepHub IMBA
作者: adham ayman
-
編碼
+關(guān)注
關(guān)注
6文章
1040瀏覽量
57071 -
機(jī)器學(xué)習(xí)
+關(guān)注
關(guān)注
66文章
8558瀏覽量
137113
發(fā)布評論請先 登錄
機(jī)器學(xué)習(xí)算法的特征工程與意義詳解
【下載】《機(jī)器學(xué)習(xí)》+《機(jī)器學(xué)習(xí)實戰(zhàn)》
如何選擇機(jī)器學(xué)習(xí)的各種方法
圖像分類的方法之深度學(xué)習(xí)與傳統(tǒng)機(jī)器學(xué)習(xí)
機(jī)器學(xué)習(xí)應(yīng)用中的常見問題分類問題你了解多少
想掌握機(jī)器學(xué)習(xí)技術(shù)?從了解特征工程開始
機(jī)器學(xué)習(xí)的特征工程是將原始的輸入數(shù)據(jù)轉(zhuǎn)換成特征
機(jī)器學(xué)習(xí)特征工程的五個方面優(yōu)點
機(jī)器學(xué)習(xí)算法學(xué)習(xí)之特征工程1
機(jī)器學(xué)習(xí)算法學(xué)習(xí)之特征工程2
機(jī)器學(xué)習(xí)算法學(xué)習(xí)之特征工程3
什么是特征工程?機(jī)器學(xué)習(xí)的特征工程詳解解讀
機(jī)器學(xué)習(xí)特征工程:分類變量的數(shù)值化處理方法
評論