補一堂課,
讓它學會回話
預訓練完的語言模型知識淵博,卻只會把你的問題當文章接著寫。 SFT 做的事:用幾十筆「怎麼問、該怎麼答」的示範資料補一堂課, 行為就徹底改變。按按看那顆綠色開關——
同一個模型、同一個問題:SFT 前像鸚鵡學舌,SFT 後像個助理。 這一課回答三個問題:資料長什麼樣?只學回答不學問題怎麼做到? 為什麼一張普通顯卡就微調得動 LLM?
右欄是真的 Python(瀏覽器內執行,首次載入約 30–60 秒)。壓軸實驗會在你的 瀏覽器裡現場訓練一個迷你語言模型;想跑真正的 0.5B 模型,最後有 GPU 軌道 🚀。
SFT 站在訓練管線的哪裡
關鍵認知:SFT 不是重新訓練,是在 base model 上「補課」。 它教的是行為(聽指令、格式、語氣), 不太能教知識——知識早在預訓練就裝進去了。 這個區別會在最後的實驗裡被你親眼驗證。
指令資料:一問一答的示範集
SFT 的原料就是「理想對話的錄影帶」。訓練前每筆資料會套上 chat template——用特殊記號標出誰在說話:
模型學會這套格式後,推論時只要看到 <|im_start|>assistant,
就知道「輪到我了」。右欄的迷你模型用簡化版模板
q: 問題 / a: 回答——概念一模一樣。
模型眼中沒有字,只有編號
文字進模型前要先切成 token、換成整數編號。真實 LLM 用 BPE 子詞 (詞表數萬到數十萬);右欄的迷你模型用最簡單的字元級切法。
有個小實驗值得做:在右欄的編碼器裡打中文。迷你詞表只收英文小寫,
中文全變 ⟨unk⟩——這就是「詞表覆蓋率」問題的縮影:
模型看不懂的字,連學的機會都沒有。
Loss Masking:只在回答上學
一筆訓練資料裡問題和回答都在,但我們只想教模型「怎麼回答」。 做法:算損失時把問題部分遮起來(label 設 -100), 只有回答部分的錯誤會產生梯度:
注意最後那顆綠色的 ⏎:回合結束符也要算 loss
(真實模型是 <|im_end|>)。這堂課開發時真的踩過這個坑——
漏掉它,模型學會回答卻學不會「停」,答完正確答案繼續瞎講。
右欄有一顆「loss masking」開關,你可以親手做這個消融實驗。
LoRA:不動原模型,外掛小補丁
全參數微調 0.5B 模型要更新五億個數字。LoRA 的解法: 原權重 W 凍結,旁邊掛一對秩為 r 的小矩陣 B·A,只訓練它們:
為什麼小小的 r 夠用?因為 SFT 要教的行為改變(格式、語氣、身分) 本質上是低秩的——右欄有一張「秩 vs 重建誤差」的實驗圖讓你拉著玩。
在瀏覽器裡親手 SFT 一個模型
右欄有一個約 1.1 萬參數的字元級迷你語言模型,完全在你的瀏覽器裡 訓練:先在通用語料上預訓練(載入時已自動完成),然後由你按下 SFT。 流程與真實 LLM 完全同構:
| 迷你模型(右欄) | 真實 LLM(GPU 軌道) | |
|---|---|---|
| 預訓練語料 | 英文短句 | 兆級 token 網路文本 |
| SFT 資料 | 16 筆 QA | 50 筆繁中助教示範 |
| 模板 | q:/a: | <|im_start|> 系列 |
| 結束符 | 換行 ⏎ | <|im_end|> |
| 微調方法 | 手寫 numpy SGD | Unsloth + LoRA(4-bit) |
| 訓練時間 | 你瀏覽器裡 1–2 秒 | GPU 上 60 步、約 1–3 分鐘 |
三個一定要做的實驗:
- 問訓練過的問題 → 幾乎每次答對
- 問同格式但沒訓練過的問題 → 格式對、內容亂掰——SFT 教行為不教知識,這就是幻覺的縮影
- 關掉 loss masking → 模型開始自己出題,學習力被問題部分偷走
換你動手
在右欄 sft_pairs 加 3–5 筆你自己的 QA(小寫英文),重跑後測試模型學不學得會。
把 SFT 學習率 lr=0.25 改成 1.0,觀察 loss 曲線發生什麼事——親眼看「學習率太大」長什麼樣。
思考題:SFT 資料混進錯誤答案(the sky is green.)會怎樣?先猜,再加進資料集驗證——體會「資料品質 > 資料數量」。
跑真的:把 0.5B 模型調教成助教
迷你模型是概念教具;真實版在本課附的 sft_gpu.py——
用業界常用的 Unsloth 快速微調法:4-bit 量化載入、LoRA 補丁、
SFTTrainer 60 步、loss masking 一行搞定(train_on_responses_only),
把 Qwen2.5-0.5B(base)微調成自稱「小樹」的繁中助教。
瀏覽器跑不動 torch,所以這一段借用 molab(marimo 官方雲)的 GPU:
- 登入 molab(marimo 帳號,可用 GitHub/Google)
- 在新分頁開啟課程 notebook,Fork 成自己的副本即可編輯(molab 的登入狀態進不了內嵌框架,所以要新分頁跑——跟本頁並排開,教學照樣對照)
- 執行環境選 Server / GPU(例如 RTX Pro 6000)
- 從第一格往下全部執行,看 loss 掉下來、看 base 與 SFT 後的對比
誠實說明:GPU 軌道是選配延伸,不跑它也能完整理解本課。
GPU 供應與額度依 molab 帳號方案而定(可能需要付費方案或有用量限制);
molab 連不上時,下載 sft_gpu.py 在任何有 NVIDIA GPU 的機器
uvx marimo edit sft_gpu.py 也能跑(Unsloth 需要 GPU,純 CPU 跑不動這份——
但概念版的所有實驗都不受影響)。
這一課的三句話
- SFT 教行為,預訓練給知識——別指望用 50 筆資料教會模型新學問
- loss 只算在回答和結束符上——遮罩錯了,模型不是學會出題就是學不會停
- 行為改變是低秩的——所以 LoRA 用 0.4% 的參數就辦得到
下一步往哪走:
- 偏好對齊(RLHF / DPO)——SFT 教「會答」,對齊教「答得好」
- 資料工程——真實世界的 SFT 成敗八成在資料品質與多樣性
- 右上角「下載 .py」把兩個 notebook 都帶回家繼續玩
真實 0.5B 模型的 SFT,在 molab 的 GPU 上跑
molab 的登入狀態進不了內嵌框架(瀏覽器的跨站 cookie 保護), 所以 GPU 版要在新分頁執行——把它跟本頁並排開,左邊教學照樣對照。
- 登入 molab(GitHub / Google)
- 開啟課程 notebook,Fork 成自己的副本
- 執行環境選 Server / GPU(例如 RTX Pro 6000)
- Run all,約 5–10 分鐘
沒有 molab 帳號或 GPU 額度?下載 sft_gpu.py 在任何有 NVIDIA GPU 的機器
uvx marimo edit sft_gpu.py(Unsloth 需要 GPU;概念版實驗不受影響)。