
微調 JSON 輸出:讓語言模型始終輸出有效 JSON
語言模型的 JSON 可靠性問題及其解決方案。了解為何小型模型在 JSON 格式上失敗、微調如何將有效 JSON 率從 64% 提升至 99%,以及如何構建訓練資料集以實現可靠的結構化輸出。
結構化輸出是實際生產 AI 應用程式的基礎。如果你正在構建任何需要將 LLM 輸出連接到下游系統的東西——API、資料庫、工作流程自動化——你需要 JSON。你需要它每次都有效。
問題是:小型模型在 JSON 格式上失敗的頻率高得驚人。
失敗有多嚴重
在 Llama 3.1 8B 基礎模型上的基準測試:
| 任務 | 有效 JSON 率 |
|---|---|
| 簡單鍵值提取 | 64.2% |
| 嵌套對象 | 41.7% |
| 帶驗證的數組 | 38.9% |
| 複雜的多層架構 | 22.4% |
這些數字對於生產使用來說太低了。即使是 64% 的成功率也意味著每三個請求中就有一個失敗,需要重試、回退或手動干預。
為什麼小型模型在 JSON 上苦苦掙扎
小型模型有三個使 JSON 生成困難的問題:
1. 嚴格的語法要求
JSON 沒有容錯空間。一個缺失的逗號、一個未轉義的引號或一個錯誤放置的括號就會破壞解析器。自然語言是容忍錯誤的;JSON 不是。小型模型習慣於生成自然語言並不斷「修正」自己——這種能力在 JSON 中會產生反效果。
2. 上下文追蹤
有效的 JSON 需要正確的括號配對、正確的逗號放置以及從開頭到結尾一致的架構遵循。對於長輸出,小型模型失去對其所在結構位置的追蹤,並開始以語義上合理但語法上無效的方式關閉對象。
3. Token 邊界
JSON 中的某些字符序列在訓練資料中出現的頻率低於自然語言——特別是嵌套結構和轉義字符。模型對這些 token 序列的信心較低,並且更可能生成替代序列。
微調如何修復這個問題
微調直接解決了這三個問題。你在你的目標架構 的有效 JSON 範例上訓練模型,模型學習:
- 哪些 token 序列對你的特定架構有效
- 如何正確追蹤括號深度
- 如何在整個輸出中保持架構一致性
在 2,000 個訓練範例之後,同樣的 Llama 3.1 8B 模型達到了 99.2% 的有效 JSON 率——跨越簡單和複雜架構。
建構訓練資料集
第一步:定義你的架構
首先精確定義你需要的 JSON 輸出。使用 JSON Schema 語法:
{
"type": "object",
"properties": {
"entity_type": {
"type": "string",
"enum": ["person", "organization", "location", "date"]
},
"value": {"type": "string"},
"confidence": {"type": "number", "minimum": 0, "maximum": 1},
"context": {"type": "string"}
},
"required": ["entity_type", "value", "confidence"]
}
對你的所有目標輸出格式執行此操作。如果你有十種不同的輸出類型,定義十個架構。
第二步:生成多樣化的範例
創建覆蓋你的輸入空間的訓練範例。對於每種架構類型:
{"messages": [{"role": "user", "content": "從以下文字中提取命名實體:\n\n'蘋果 公司首席執行官 Tim Cook 昨天在加利福尼亞庫比蒂諾宣布了新產品。'"}, {"role": "assistant", "content": "{\"entities\": [{\"entity_type\": \"organization\", \"value\": \"Apple Inc.\", \"confidence\": 0.98, \"context\": \"首席執行官\"}, {\"entity_type\": \"person\", \"value\": \"Tim Cook\", \"confidence\": 0.99, \"context\": \"CEO\"}, {\"entity_type\": \"location\", \"value\": \"Cupertino, California\", \"confidence\": 0.97, \"context\": \"公告地點\"}]}"}]}
包含邊緣案例:
- 未找到實體(空數組)
- 同一類型的多個實體
- 跨越多個句子的實體
- 模糊的實體(低置信度分數)
第三步:使用 jsonschema 驗證
在添加到訓練集之前,針對你的架構驗證每個訓練範例:
import jsonschema
import json
def validate_training_example(example, schema):
try:
output = json.loads(example["messages"][-1]["content"])
jsonschema.validate(output, schema)
return True
except (json.JSONDecodeError, jsonschema.ValidationError) as e:
print(f"無效的訓練範例:{e}")
return False
# 過濾到有效範例
valid_examples = [ex for ex in examples if validate_training_example(ex, schema)]
永遠不要在無效範例上訓練。 即使是幾個格式錯誤的訓練範例也可能向模型教授錯誤的模式。