跳轉至

第 2 章 · 準備與清洗 SFT 資料集

本章你會學到

  • SFT 是什麼、跟 pre-training 差在哪(帶徒弟 vs 讀圖書館)
  • SFT 資料長什麼樣子(system / user / assistant 對話格式)
  • 四種生出 SFT 資料的方法:人工標註、既有資料改寫、合成生成、領域資料
  • 為什麼品質遠比數量重要(LIMA:1,000 筆勝過 5 萬筆)
  • 一條 SFT 清洗流水線:格式 → 去重 → 品質評分 → 多樣性 → 安全 → 去污染 → 語言一致

2.1 SFT 是什麼?跟 pre-training 差在哪?

第 1 章那位新同事,讀完圖書館後知識很淵博,但不會「好好回話」。你問他問題,他可能接著幫你把句子寫完(因為他只學過「預測下一個字」),而不是回答你

SFT(Supervised Fine-Tuning,監督式微調) 就是接下來的「帶徒弟」階段:我們準備一大批「問題 → 理想回答」的示範,一筆一筆教他——遇到這種問法,就該這樣回。

flowchart LR
    subgraph P["Pre-training(第 1 章)"]
      P1["讀海量文字"] --> P2["學會語言與知識<br/>但只會『接話』"]
    end
    subgraph S["SFT(本章)"]
      S1["讀『問題→回答』示範"] --> S2["學會『當一個有用的助理』<br/>會回答、會遵守指令"]
    end
    P2 --> S1
    style P fill:#e8eaf6,stroke:#3f51b5
    style S fill:#fff8e1,stroke:#ff8f00
Pre-training SFT
在學什麼 語言、常識、知識(底子) 怎麼遵守指令、好好回話(行為)
資料形式 一大坨純文字 成對的「指令 → 回答」
資料量 兆級 token,越多越好 幾千~幾十萬筆,重質不重量
比喻 讀遍圖書館 師傅帶徒弟、手把手示範

2.2 SFT 資料長什麼樣子?

SFT 資料通常是一段對話,用 role(角色)標記誰在說話:

  • system:給模型的「人設 / 規則」(例如:你是一個講繁體中文的客服助理)。
  • user:使用者說的話。
  • assistant我們希望模型學會的理想回答
一筆 SFT 資料(多輪對話,messages 格式)
{
  "messages": [
    { "role": "system",    "content": "你是 APMIC 的客服助理,請用繁體中文、有禮貌地回答。" },
    { "role": "user",      "content": "請問你們的服務支援哪些語言?" },
    { "role": "assistant", "content": "您好!我們的服務目前支援繁體中文、英文與日文三種語言,若有其他語言需求也歡迎告訴我們。" },
    { "role": "user",      "content": "那有中文的技術文件嗎?" },
    { "role": "assistant", "content": "有的,我們提供完整的繁體中文技術文件,您可以在官網的『開發者中心』找到,需要我幫您附上連結嗎?" }
  ]
}

為什麼是這種格式?

這種「角色 + 內容」的結構(業界常稱 ChatML 或 messages 格式),讓模型清楚知道哪句是指令、哪句是它該產生的回答。訓練時,模型只針對 assistant 那幾句「對答案」學習。


2.3 怎麼生出這些資料?四種主流做法

  • ① 人工標註


    請真人專家一筆一筆寫問題與理想回答。

    品質最高、成本最貴。

    代表:Databricks Dolly-15k、開源眾包的 OpenAssistant (OASST)

  • ② 既有資料集改寫


    把現成的 NLP 資料(翻譯、摘要、問答)套上指令模板,變成「指令→回答」格式。

    便宜、量大,但多樣性有限。

    代表:Google FLAN、Natural Instructions。

  • ③ 合成生成(用強模型造資料)


    用一個已經很強的 LLM 大量生成指令與回答。

    最快、最能放大規模,但要嚴格把關品質與授權。

    代表:Self-InstructAlpaca(52k 筆)、Evol-Instruct(WizardLM,把指令逐步變難)、Magpie

  • ④ 領域 / 內部資料


    把公司的 FAQ、產品手冊、客服對話,整理成「問→答」對。

    這是讓模型懂你家業務的關鍵。

    需注意:內部資料的去個資與授權同樣要做。

合成資料的兩把利器(值得記住名字)

  • Self-Instruct / Evol-Instruct:讓強模型「自己出題、自己作答」,甚至把題目越改越難、越改越複雜,逼出更深的能力。
  • 蒸餾(Distillation):用一個更強的模型當「老師」,生成大量高品質回答來教「學生」模型。
  • ⚠️ 授權地雷:用某些商用模型的輸出來訓練,可能違反它的使用條款。動手前務必確認授權,這是法遵該把關的地方。

2.4 為什麼「品質 > 數量」?

這是 SFT 和 pre-training 最大的觀念差異

Meta 的知名研究 LIMA("Less Is More for Alignment") 發現:只用 1,000 筆精挑細選的高品質示範,就能微調出表現優異的助理——勝過用 5 萬筆平庸資料的結果。

為什麼?

因為 SFT 不是在「灌新知識」(那是 pre-training 的事),而是在喚醒並塑形模型已經有的能力、教它「回話的樣子」。少量但一致、高品質的示範,反而讓模型學到清楚的行為典範;大量但雜亂的資料,只會教出前後矛盾、有時很爛的習慣。

flowchart LR
    A["5 萬筆<br/>參差不齊的資料"] -->|"混入壞習慣"| B["😐 表現普通<br/>時好時壞"]
    C["1,000 筆<br/>精選高品質資料"] -->|"一致的好典範"| D["😃 表現優異<br/>穩定可靠"]
    style B fill:#ffebee,stroke:#c62828
    style D fill:#e8f5e9,stroke:#2e7d32

結論:SFT 的重點不是「撈更多」,而是「篩更準」。 這讓「清洗」在 SFT 階段變得無比關鍵。


2.5 SFT 清洗流水線

flowchart TD
    A["原始 SFT 資料<br/>(人工/改寫/合成/領域)"] --> B["① 格式驗證<br/>角色正確、非空、合法 JSON"]
    B --> C["② 去重<br/>刪掉重複與語意近似的指令"]
    C --> D["③ 品質評分<br/>LLM-as-Judge 打分、濾掉低分"]
    D --> E["④ 多樣性把關<br/>涵蓋多種任務與領域"]
    E --> F["⑤ 安全性<br/>移除有害指令、補上合理拒絕"]
    F --> G["⑥ 去污染<br/>剔除評測考題"]
    G --> H["⑦ 語言一致<br/>確認繁中、無簡體洩漏"]
    H --> I["✨ 高品質 SFT 資料集"]
    style A fill:#fff8e1,stroke:#ff8f00
    style I fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20

① 格式驗證(Format Validation)

最基本但最容易出包的一關:

  • 角色順序對嗎?(userassistant 要交替,不能兩個 assistant 連在一起)
  • 回答是空的嗎?被截斷了嗎(講到一半沒了)?
  • JSON 合法嗎?特殊字元有跳脫嗎?
  • 有沒有超過模型的最大長度?

② 去重(Deduplication)

合成資料特別容易產生大量換句話說的重複。除了完全相同要刪,還要抓語意近似

  • 做法:把每筆指令轉成向量(embedding),算餘弦相似度,太像的只留一筆。
  • 為什麼重要:重複的指令會讓模型過度偏向某類問題,浪費珍貴的資料額度。

③ 品質評分(LLM-as-Judge)

用一個強模型或評分模型,幫每筆資料的回答打分(例如 1–5 分):

  • 這個回答正確嗎?有幫助嗎?切題嗎?格式好嗎?
  • 濾掉低分的,例如:答非所問、講到一半被截斷、語言錯誤、幻覺(唬爛)、罐頭式的「身為 AI 我無法……」。
示意:用規則 + LLM 評分做初步過濾(簡化版)
def clean_sft_sample(sample) -> bool:
    msgs = sample["messages"]

    # ① 規則:角色交替、回答非空、長度合理
    if not valid_role_order(msgs):
        return False
    last = msgs[-1]
    if last["role"] != "assistant" or not last["content"].strip():
        return False
    if len(last["content"]) < 5:            # 太短,多半沒內容
        return False
    if is_truncated(last["content"]):        # 結尾像被硬切斷
        return False

    # ② 語言一致:繁中資料不該混簡體 / 整段英文
    if not is_traditional_chinese(last["content"]):
        return False

    # ③ 品質:交給評分模型打分,低於門檻就丟
    score = llm_judge_score(msgs)            # 例如回傳 1~5
    return score >= 4

④ 多樣性把關(Diversity)

好的 SFT 資料要涵蓋各種任務:問答、寫作、摘要、翻譯、程式、推理、閒聊……

  • 做法:把指令向量化後分群(clustering),檢查是不是某一類(例如「翻譯」)佔了 80%。
  • 為什麼重要:資料太偏,模型就會「偏科」。

⑤ 安全性(Safety)

  • 移除引導模型做壞事的有害指令,或為它們補上得體的拒絕示範。
  • 但要拿捏分寸:拒絕示範放太多,會養出一個動不動就「我不能回答」的過度拒絕模型,同樣惹人厭。

⑥ 去污染(Decontamination)

和第 1 章一樣重要:把評測考題從 SFT 資料裡剔除

SFT 階段的去污染尤其危險

合成資料是用強模型生成的,而那個強模型可能剛好看過某些 benchmark 的題目,於是把考題「漏」進你的 SFT 資料。這會讓你的模型在評測上假性高分。務必比對並剔除。

⑦ 語言一致(繁中重點)

確認每一筆回答都是道地的繁體中文

  • 用語言辨識抓出混進來的簡體字、整段英文。
  • 特別注意:用中國的模型做蒸餾時,回答常帶簡體或對岸用語(「視頻/軟件/信息」),必須偵測並修正或剔除。

2.6 一個具體例子:從「爛資料」到「乾淨資料」

清洗前(會被丟掉或修正)

{
  "messages": [
    { "role": "user", "content": "介绍一下台湾的夜市" },
    { "role": "assistant", "content": "作为一个AI语言模型,我很高兴为您介绍。台湾的夜市非常有名,比如说士林夜市、饶河街夜市……(内容突然截" }
  ]
}
問題:① 簡體字② 罐頭開場白「作為一個 AI 語言模型」、③ 回答被截斷

清洗後(保留)

{
  "messages": [
    { "role": "user", "content": "介紹一下台灣的夜市" },
    { "role": "assistant", "content": "台灣的夜市非常有名,是體驗在地美食與文化的好去處。像是台北的士林夜市、饒河街夜市,台中的逢甲夜市,都各有特色小吃,例如蚵仔煎、雞排、珍珠奶茶等,非常值得一逛。" }
  ]
}
修正:繁體化、拿掉罐頭開場白、補完整內容


2.7 常用工具

把上述流程串起來的開源工具

  • Argilla + distilabel:合成資料生成 + 人工回饋 + 品質評分的整套流程。
  • data-juicer(阿里):上百種資料清洗算子,pre-training 與 SFT 都能用。
  • NeMo-Curator(NVIDIA):大規模資料清洗與去重。
  • 語意去重:用 embedding 模型 + 向量相似度,或 semhash 這類工具。

本章 checklist

  • 格式對嗎?(角色交替、回答非空、沒被截斷、合法 JSON)
  • 去重了嗎?(完全重複 +語意近似
  • 品質篩過嗎?(LLM 評分、濾掉低分與罐頭回答)
  • 夠多樣嗎?(沒有偏科)
  • 安全嗎?(有害指令處理了,但沒過度拒絕)
  • 去污染了嗎?(評測考題沒混進來)
  • 語言一致嗎?(道地繁中、無簡體洩漏)

小結

SFT 教會模型「怎麼當一個有用的助理」。和 pre-training 最大的不同是:品質遠比數量重要,所以「篩選與清洗」就是這一章的主角。模型練成後,該怎麼公平地驗收它?下一章我們用 Twinkle-Eval 來考選擇題。

前往第 3 章:Twinkle-Eval 評測