情緒辨識 SLM 微調實作指南
用 ISMOTE、LoRA 和 focal loss,把開放權重小語言模型微調成多標籤情緒辨識器。

用 ISMOTE、LoRA 和 focal loss,把開放權重小語言模型微調成多標籤情緒辨識器。
這篇給要把客服單、社群貼文、電子郵件,轉成可查詢情緒標籤的開發者看。你照著做完,會拿到一條可重現的訓練流程,能處理類別不平衡,並輸出可評估的情緒分類器。
完成後,你會有一個可直接跑的資料準備腳本、平衡後的訓練集、加入 LoRA 的 Mistral Small 模型,以及可在測試集上輸出每個情緒 F1 的評估結果。
開始之前
訂閱 AI 趨勢週報
每週精選模型發布、工具應用與深度分析,直送信箱。不定期,不騷擾。
不會寄垃圾信,隨時可取消。
- Python 3.10+
- Node 不需要;訓練環境請準備 CUDA 相容 GPU,至少 24 GB VRAM
- PyTorch 2.4+,且已安裝 CUDA 版
- Hugging Face 帳號與可用的 access token
- 可存取 GoEmotions dataset 與 Unsloth repository
- 已安裝 transformers、datasets、scikit-learn、numpy、pandas、unsloth
Step 1: 建立情緒標籤表
這一步的產出是固定順序的 15 類情緒標籤表,後續資料切分、訓練與評估都會共用它。先從 GoEmotions 挑出你要的類別,再把輸出順序鎖死,避免多標籤向量在不同 split 之間對不上。

EMOTION_LABELS = [
"fear", "sadness", "disgust", "disapproval", "annoyance",
"anger", "disappointment", "optimism", "amusement", "surprise",
"admiration", "excitement", "confusion", "joy", "love"
]接著檢查每筆資料的 label vector 長度是否一致。你應該看到所有 split 都是固定 15 維的 multi-hot 格式。
Step 2: 平衡訓練資料分布
這一步的產出是去偏態的訓練集,讓 neutral 不會壓過其他情緒。先對多數類做隨機下採樣,再用 ISMOTE 擴增稀有情緒,讓少數類達到可訓練的樣本量。

驗證集與測試集要保持原樣,這樣最後的分數才反映真實不平衡分布。這個做法把下採樣、合成擴增與 loss weighting 放在一起,目標是改善少數類表現,不是美化指標。
你可以先畫出各標籤在前後的頻率圖。你應該看到 neutral 明顯下降,而目標情緒的數量彼此更接近。
Step 3: 載入 Mistral 基座模型
這一步的產出是可在本機運行的基座模型,準備接收參數高效率微調。使用 Unsloth 以 4-bit 方式載入 Mistral Small 3.1 24B Instruct,讓模型能放進可用 GPU 記憶體。
from unsloth import FastLanguageModel
import torch
MODEL_NAME = "unsloth/Mistral-Small-3.1-24B-Instruct-2503"
base_model, _ = FastLanguageModel.from_pretrained(
model_name=MODEL_NAME,
max_seq_length=2,
load_in_4bit=True,
dtype=torch.bfloat16,
)驗收方式是模型成功初始化,沒有 OOM,並且確實以 4-bit 權重載入。你應該看到 backbone 已在 GPU 上可用,能直接進入 adapter 注入。
Step 4: 掛上 LoRA 與 focal loss
這一步的產出是輕量訓練配置,讓模型不用全參數微調也能學到多標籤情緒模式。把 LoRA 掛到 attention 與 MLP 投影層,再加上自訂多標籤 head 與 focal loss,讓較難、較稀有的標籤得到更高權重。
base_model = FastLanguageModel.get_peft_model(
base_model,
r=16,
lora_alpha=32,
lora_dropout=0,
bias="none",
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
use_gradient_checkpointing="unsloth",
random_state=3407,
use_rslora=False,
)驗收方式是可訓練參數數量明顯下降,但模型仍能輸出 15 個情緒的 logits。你應該看到 adapter 佔比遠小於完整模型,且輸入多標籤目標時不會報格式錯誤。
Step 5: 訓練並輸出測試分數
這一步的產出是已訓練完成的情緒分類器,以及一份可讀的測試集報告。設定按 epoch 驗證、計算 exact accuracy 與 macro、micro F1,並在驗證集表現最佳時保存 checkpoint。原始做法顯示,多數目標情緒的 F1 可高於 0.7。
from sklearn.metrics import f1_score, precision_score, recall_score, accuracy_score
# train with epoch evaluation, then score on the test set
# compute_metrics should threshold sigmoid outputs at 0.5驗收方式是你能在 log 看到每個 epoch 的評估結果,並在測試集報告中讀到各情緒的 per-class metrics。你應該看到最佳 checkpoint 由驗證集分數選出,而不是手動挑模型。
| 指標 | 基準/優化前 | 結果/優化後 |
|---|---|---|
| 少數類平衡 | 資料明顯偏向 neutral | neutral 被下採樣,稀有情緒用 ISMOTE 擴增 |
| 訓練方式 | 全參數微調需要更高記憶體 | 4-bit 基座模型加 LoRA adapters |
| 情緒 F1 | 原始 baseline 未提供穩定結果 | 多數目標情緒可達 F1 > 0.7 |
常見錯誤
- 直接拿不平衡資料開訓練。修法:先下採樣 neutral,再對少數情緒做 ISMOTE。
- 把多標籤問題當成單標籤分類。修法:輸出用 sigmoid,閾值用 0.5,不要用 softmax。
- GPU 記憶體不足。修法:保留 4-bit 載入、LoRA 與 gradient checkpointing。
接下來可以看什麼
下一步可以把模型推到 Hugging Face Hub,為每個情緒單獨調整閾值,並比較 ISMOTE 與簡單 oversampling 在你自己的資料上的差異。