昨天我們完成了用 Target Encoding + RandomForest baseline model ,分數只有 0.533,雖然低(大概1600名,第一名分數高達0.93),但至少我們成功確認了資料處理流程沒有問題。
今天我們要試試更有語意理解能力的特徵表示:Word2Vec + XGBoost,看看能不能把分數提高!
前幾天我使用的是 One-Hot 、 Frequency Encoding 跟 Target Encoding 之後接 RandomForest。
雖然這樣能快速得到一個基準分數,但它其實沒有理解文字的「語意」,只是把「類別」去做編號轉成數字而已。下面在複習一次這三個編碼方式:
把每個類別拆成一個新的欄位,用 0/1 表示是否屬於該類別。
原始顏色 | 紅 | 綠 | 藍 |
---|---|---|---|
紅 | 1 | 0 | 0 |
綠 | 0 | 1 | 0 |
藍 | 0 | 0 | 1 |
把每個類別替換成「它出現的頻率」或比例
原始顏色 | Frequency Encoding |
---|---|
紅 | 0.50 |
綠 | 0.30 |
藍 | 0.20 |
用每個類別的目標變數平均值來取代類別
原始顏色 | 違規率 |
---|---|
紅 | 0.7 |
綠 | 0.3 |
藍 | 0.1 |
於是今天我們換個想法來思考:
Word2Vec 是一個 NLP 的經典模型,他可以讓電腦真的開始理解語意!
想像一下,One-Hot Encoding 只會告訴電腦:
對電腦來說,蘋果和香蕉一點關係都沒有,它們只是不同的位置亮了一個燈。
電腦完全不知道這兩個字都是水果。
Word2Vec 做了什麼呢?
它把每個詞都變成一個向量,這個向量有幾百個維度,並且這些向量會有語意上的關係。
舉例來說:
vec("蘋果")
和 vec("香蕉")
會很接近,因為它們都出現在「吃水果」、「剝皮」這些語境。換句話說,Word2Vec 不只是數字化,而是讓電腦學到「哪個詞跟哪個詞比較像」。
Word2Vec 的核心想法叫 「你是誰,取決於你的鄰居」。
也就是說:如果兩個詞經常出現在類似的上下文裡,這兩個詞就應該有相似的向量表示。
這樣訓練下來,模型就會讓語意相近的詞,向量也靠得更近。
當我們把句子裡的每個詞轉成向量,然後取平均或做 pooling(池化),我們就得到一句話的「語意向量」。
這時候,模型就不只看到詞頻,還能理解整句話的語境。
舉例:
以下用 gensim
載入 Google News 的預訓練 Word2Vec ,然後用 xgboost
訓練分類器。
如果有套件沒有下載,記得要去當初建立的虛擬環境中下載並且所有套件的版本需要相容。
#下載預訓練 Word2Vec (Google News 300d)
import kagglehub
# Download latest version
path = kagglehub.dataset_download("leadbest/googlenewsvectorsnegative300")
print("Path to dataset files:", path)
import numpy as np
import pandas as pd
from gensim.models import KeyedVectors
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import xgboost as xgb
# 1) 載入預訓練 Word2Vec (Google News 300d)
# 因為 Kaggle 執行上傳檔案會沒網路,要先把模型上傳成 Dataset 然後從本地路徑載入
w2v_path = "要改成前面Path to dataset files中有一個.bin檔的路徑"
w2v = KeyedVectors.load_word2vec_format(w2v_path, binary=True)
# 2) 把句子轉成向量(平均池化)
def sentence_to_vec(sentence, model, dim=300):
words = [w for w in sentence.split() if w in model]
if len(words) == 0:
return np.zeros(dim)
return np.mean(model[words], axis=0)
#使用 body 欄位(主文字內容)作為輸入特徵。
X_text = train["body"].astype(str).tolist()
y = train["rule_violation"].values
X_vec = np.vstack([sentence_to_vec(text, w2v) for text in X_text])
# 切割資料
X_tr, X_val, y_tr, y_val = train_test_split(
X_vec, y, test_size=0.2, random_state=42, stratify=y
)
# 訓練 XGBoost 分類器
clf = xgb.XGBClassifier(
n_estimators=500,
max_depth=6,
learning_rate=0.05,
subsample=0.8,
colsample_bytree=0.8,
random_state=42,
n_jobs=-1,
use_label_encoder=False,
eval_metric="logloss"
)
clf.fit(X_tr, y_tr)
# 5) 驗證
y_pred = clf.predict(X_val)
print("Word2Vec + XGBoost Accuracy:", accuracy_score(y_val, y_pred))
今天的分數是0.736,雖然還沒有上傳到 kaggle ,但確實比之前嘗試的 baseline model 還高分,我們已經從單純的類別編碼,進階到「語意向量 」,算是真正邁入了 NLP 特徵工程世界。
雖然這只是第一步,分數也沒有上升到讓人驚豔的程度,但可以看出向量化的威力。
明天我們會把這次訓練上傳到 kaggle ,確定分數是真的有進步的,再繼續把特徵做得更好,讓模型更接近人類理解語意的方式,也會嘗試更多模型,希望能提高分數。