黑未必黑,白或非白。
資料本無聲,語言使其有形。
你以何為標,他便以何為界,
與它共築一方世界。
📝 前言
在進行 Kaggle 專案時,早期我常是將缺失值補完後,就直接把資料丟進模型訓練看看結果。但分數總是差強人意。尤其是在那些經典入門題目中,各種模型與技巧早已被前人發揮得淋漓盡致,單純丟入整理過的數值,往往無法再帶來突破。
這讓我不禁好奇:我缺少的,是什麼?
特徵工程,或有人稱作「特徵強化」,就是為了解決這個問題而存在。
它通常與資料清理緊密相連,因為許多特徵的產生與轉換,仰賴清理階段所做的分析與歸類。
模型並不理解人類的邏輯與語意,它只看得懂數字之間的規律與關聯。即便資料量再大,如果我們沒有先整理出好的教材,它也只會在數據海中亂抓,學不到真正有用的知識與連結。
🧩特徵工程:你,作為模型的老師
📊 數值型特徵:數字有大有小
剛開始做題的時候,我習慣把數值欄位直接丟進模型。畢竟,那些看起來不就是「可以計算」的資料嗎?
但隨著我對模型的理解逐漸深入,才發現不是所有數字,都適合原封不動地餵給模型。
例如:
- 「收入」欄位從 20,000 到 800,000,會不會讓模型以為差距是線性相關?
- 「年齡」欄位中,有幾位極端值 99 歲,會不會影響整體的判斷?
- 某些欄位其實是分級(1、2、3),但模型會把它當成連續值來處理。
模型只看到「數值」,它看不出這些數字代表的涵義。
✂️ 一、標準化與正規化
若資料中的數值尺度差異很大(例如收入 vs 貸款次數),某些模型會受影響,如:
- Logistic Regression
- KNN
- 神經網路(Neural Network)
這時我們會考慮:
- 標準化(Standardization):將資料轉為平均值為 0、標準差為 1
- 正規化(Normalization):將數值壓縮到 0~1 之間
📌 小提醒:像樹模型(如 XGBoost、LightGBM)不太受這些影響,通常不需要做標準化。
📦 二、分箱(Binning)
有些數值型欄位其實不需要精確的數值,而是「落在哪個範圍」比較有意義。例如:
- 年齡:是否是「高齡族群」
- 消費次數:是「偶爾」、「頻繁」、還是「極高活躍」?
這時我們可以把數值切成數個區間(bin):
年齡 ≤ 20 → 青年
21~50 → 中壯年
51~75 → 高齡
> 75 → 超高齡
Binning 降低模型負擔、增加穩定性,但也可能犧牲預測的精度,需視情況使用。
🧪 三、數值轉換:log、平方根、倒數…
當某些數值分布極度偏斜(例如 90% 的收入集中在低於 30,000、但有少數破百萬),可以考慮轉換處理:
- log 轉換:壓縮極端值、拉近距離
- 平方根 / 倒數:強調低值、壓抑高值
- 對數尺度在金融、人口等資料常見
📌 記得不要對 0 做 log,會出錯。
⚠️ 四、處理極端值與常數欄位
- 某些欄位可能有極端異常值(如 999999),可能是填補缺值的預設碼,要先排查。
- 若某欄位幾乎都是固定值(如「交易方式」99% 為一種),這個欄位可能沒有資訊價值,可以考慮捨去。
✅ 小結
數值型特徵看似簡單,每個數字都有意義,但人類能理解的意義,模型不見得懂。
模型只會依照你給它的樣子去學。如果你餵它一個失真的世界,它就會努力學出失真的邏輯。
🏷️ 類別型特徵:模型不分男女
數值型特徵是模型學習的主食,但類別型特徵,往往才是最容易被誤解的一塊。
例如將「男」與「女」編碼成 0 與 1,對人類來說,我們知道這只是兩種性別,但對模型來說,若是線性模型或 KNN,它可能會錯誤地認為「女(1)大於男(0)」,進而學到不存在的關係。性別本沒有大小之分。
在實務上,類別欄位的編碼方式,會直接影響模型如何「理解」這個欄位。
尤其在像 Kaggle 這類競賽中,類別處理的細節,往往是勝負分水嶺之一。
一、Label Encoding vs One-Hot Encoding
最基本的兩種處理方式如下:
- Label Encoding:每個類別轉成一個數字(如 男 → 0,女 → 1)
優點是簡單快速,不增加欄位維度;缺點是容易讓模型誤以為數字有大小順序(特別是對非樹模型來說)。 - One-Hot Encoding:每個類別創造一個欄位,該類別為 1,其餘為 0
優點是完全不會引入順序性錯誤;缺點是在類別很多時容易造成維度爆炸與訓練變慢。
📌 什麼時候該用哪個?
- 樹模型(如 XGBoost、LightGBM):Label Encoding 通常沒問題,因為它們不依賴數值順序。
- 線性模型 / 神經網路:建議用 One-Hot Encoding,以免模型學到錯誤的順序關係。
二、高基數類別怎麼辦?
當欄位裡有成百上千種可能(如:郵遞區號、職業、商品 ID)時,One-Hot Encoding 就會變得難以維護,模型也容易因維度太高而無法收斂。
這時可以考慮:
- Frequency Encoding:用每個類別出現的次數替代
- Target Encoding:用該類別對應的平均目標值(例如轉換率)來表示
- 📌 要小心資料洩漏(data leakage),記得在 K-Fold 裡做分層編碼
- Group Mapping:依據商業邏輯或觀察結果,把稀有類別整合成一類(例如「其他」、「不明」)
三、文字型欄位 ≠ 自然語言處理
有時候欄位名稱看起來像文字(如商品名稱、使用者簡述),但在本質上只是分類標籤,這時不需要用 NLP 技術去斷詞。
但若是「客服留言」這類自由輸入文字欄位,才可能要用 TF-IDF、詞頻統計、或語言模型處理,那已是另一種任務類型。
四、欄位是否有順序性?
像「教育程度」這種欄位(高中、大學、碩士、博士),其實是有潛在順序的。這種就可以考慮:
- 使用 Label Encoding 並手動定義數值順序(如 高中=1、大學=2…)
- 或轉成階層式欄位(是否碩士以上…)
🧠 重點不是「類別是不是字串」,而是「這個分類有沒有順序性」
🧭 實務反思:我沒轉數字,模型怎麼還是能學?
我在做「Spaceship Titanic」任務時,像是「出發地」、「目的地」這些文字欄位我根本沒轉數字,模型卻依然能學得不錯。
後來我才搞懂這是因為:
- 我用的是樹模型(LightGBM),它本來就不太依賴數值大小順序。
- 訓練資料是 Pandas
DataFrame
,而 LightGBM 其實可以自動處理 object 欄位(或我無意中已轉成數字)。 - 樹模型的容錯性很高,即便類別不完美,也能透過分裂學到一點規律。
但這不代表我可以忽略類別處理——尤其在高基數、多分類、有語意結構的資料中,主動設計 encoding 策略依然至關重要。
✅ 小結
類別型特徵雖然看似簡單,但它其實是模型理解世界的「語言」,如果這層語言被翻譯錯了,模型學得再好也無法真正理解問題。
這一節讓我學到最重要的一件事是:
不是資料本身複雜,而是我們太容易假設模型看得懂我們的意思。
🔗 特徵組合:模型沒想到的,你要幫它想
光是清理與轉換原始資料,往往還不夠。我們還可以透過「組合特徵」的方式,補上模型自己無法發現的線索。
雖然說好的模型能主動找出一些特徵之間的關聯,但它的判斷仍是依賴資料中的模式,無法理解背後的意圖或邏輯。
模型能從數字中學習規律,但對於哪些特徵應該結合、哪些變化才有意義,它無法自行判斷。
就像學生在讀歷史時,光知道年份與事件名稱可能記不住;但如果有人提醒:「這兩件事其實有共通背景」,理解與記憶的效率往往會大幅提升。
模型也是一樣。它不會自己串起邏輯,只能根據你給它的數據推測關係。
你不幫它說清楚,它就永遠不會懂。
一、特徵的加減乘除:不是數學,是邏輯
最基本的特徵組合方式,就是直接對數值型欄位進行加減乘除,這些操作看似簡單,卻能揭示模型原本看不到的結構。
舉個例子:
- 在「房價預測」任務中,
總樓層面積 / 房間數
可以估計出每房間平均大小,這比單看房間數或面積更有意義。 - 在「消費預測」中,
購買金額 / 購買次數
可以推估單筆平均消費,有助於辨別使用者的價值。
這些組合雖然只是簡單的數學,但往往能帶來額外的邏輯線索,幫助模型找到更穩定的趨勢。
📌 重點不在數學,而是意義:我們不是為了算數而組合,而是要讓模型讀懂原本無法看懂的關係。
二、類別交叉:你跟我,其實有點關係
有時候,單一欄位的資訊太過分散,模型無法看出其中的意義。但當兩個欄位結合在一起,就可能揭露出有趣的規律,這就是「類別交叉特徵」的由來。
舉個例子,在 Titanic 任務中,性別
和 船艙等級
各自都有點訊號,但若你將它們組合成 性別_船艙等級
(如 女_1等
、男_3等
),就能發現某些特定組合的生還率特別高或特別低。
這是因為:
- 「女性」和「頭等艙」本來就各自偏高生還率
- 但同時具備這兩個特徵的乘客,可能有更顯著的趨勢
📌 類別交叉的目的不是硬湊新欄位,而是針對有邏輯關聯的組合,讓模型有機會「讀懂上下文」。
例如性別與等級:
df["性別_等級"] = df["Sex"] + "_" + df["Pclass"].astype(str)
也可以用 map()
或 apply()
自訂更複雜的組合規則,接著用 Label Encoding 或 Count Encoding 處理這個新欄位。
💡 使用時機與提醒
- 適用於類別欄位較少但有交互可能性的情境
- 不適用於兩個類別都高基數(會爆炸成上百種組合)
- 如果某組合出現次數極少,模型可能無法穩定學習,可考慮設為「Other」
三、Groupby 聚合:你說的是「平均」,還是「平均在哪一群人裡」?
有些資訊,單看個體是看不出規律的,但如果把人「分群」之後再觀察,就能挖掘出更有意義的訊號。
這就是 Groupby 聚合特徵 的精髓:
不是把資料平均,而是用某個欄位作為依據,把資料「依群體重算一次」。
🧪 舉例來說:
以 Spaceship Titanic 為例,每個人都有一個 Group ID
,也許單看某人的 Age
沒什麼特殊,但如果我們計算:
df["group_mean_age"] = df.groupby("Group")["Age"].transform("mean")
這欄就代表「這個人所在群組的平均年齡」。
這可能揭露出:小孩團 vs 成年人團、同齡家族 vs 混合團體……等隱藏特徵。
同理,在購物任務中,可以用使用者 ID 分組,統計他們的:
- 總消費次數(count)
- 平均每次金額(mean)
- 最後一次消費距今幾天(max datetime → 差值)
🧠 為什麼 Groupby 這麼重要?
因為很多時候,模型其實不關心絕對數字,而是關心你「在這群人中屬於哪種狀況」。
也就是所謂的:相對特徵(Relative Features)。
🚨 使用注意事項:
- 避免 target leakage:不能用 test 資料來幫 train 算平均。
- 記得做 transform 而不是 aggregate:否則結果會變成 group 級別,不再是逐行特徵。
- 可以加 rank / std / diff 等進階版本:如每人在群內的收入排名、差距等。
📌 小結:不是做越多越好,而是做對方向
特徵組合的方法非常多,但最重要的不是數量,而是你是否有看懂資料背後的邏輯與故事。
除了前面介紹的:
- 數學組合(加減乘除)
- 類別交叉
- Groupby 聚合
還有許多其他常見做法像是:
- 對每一筆樣本進行 統計摘要(如平均、標準差、最大最小值)
- 從時間欄位中提取 時間特徵(如週末、購買間隔、節慶前)
這些我們會在接下來的專案實作中,依照不同任務會再補充。
畢竟不是每個任務都需要同樣的技巧,而是要懂得挑對武器、對症下藥。
🎯 特徵選擇:與其丟更多,不如丟對的
在特徵工程過程中,我們常會加上很多新欄位——有的是加乘組合,有的是群體統計,有的只是「感覺可能有用就先放著看看」。
但資料一多,模型反而可能被噪音干擾,訓練時間拉長、泛化能力下降。
這時我們就需要做 特徵選擇(Feature Selection),把那些「沒幫上忙」的特徵剃除掉。
🧪 怎麼判斷一個特徵有沒有用?
以下是幾種我曾經用過的方法。
1. 用模型來評分特徵重要性
- 訓練一個 LightGBM 模型(或你喜歡的),觀察
feature_importances_
- 使用 Permutation Importance(打亂某欄位觀察準確率下降幅度)
📌 適合快速篩選大致方向,但不代表分數低就一定沒用(可能是跟其他欄位重複了)
2. 根據缺值比例或唯一值比例過濾
- 欄位缺值率過高(如超過 80%),可考慮剃除
- 只有一種值(如整欄都是 0)毫無意義
📌 這類欄位是最容易被自動刪除的,屬於清理與篩選的交界區段。
3. 用相關性分析(Correlation)排除重複特徵
- 兩個欄位高度相關(如相關係數 > 0.95),可能只保留一個即可
- 可視覺化熱力圖(heatmap)來輔助判斷
📌 對於線性模型來說,這一步尤其重要,避免共線性問題。

Space Titanic 中顯示的熱力圖,可以很明顯看到各特徵與target的關係。
4. 手動刪除與任務無關的欄位
- 像 ID、UUID、命名亂碼、訓練集特有的時間戳等
- 即使這些欄位在訓練時有高分,可能是「過擬合」假象
🧠 選擇的不是特徵,是模型的理解方式
特徵選擇不只是刪資料,而是在幫模型排除干擾,留下最有助於「建立解釋邏輯」的素材。
而這背後,需要的不是公式,而是你對任務的熟悉與直覺。
🧾 結語:特徵,是模型世界的語言
特徵工程不是資料前處理的副產品,而是建構模型理解能力的關鍵一環。
我們給模型的每一欄資料,都是一種語言、一種暗示。模型無法像人類那樣從各個特徵中推論關係,它學得如何,全看你怎麼說。
你說得越清楚,才能使接下來的模型,學得更好。