![]()
生產日誌是你能擁有的最佳訓練資料,也是最危險的。它們是真實分布中的真實輸入——但充滿了你目前模型的輸出,而這正是你絕對不能用來訓練的內容。
日誌傾印不是資料集。百分之九十的生產流量是模型已經在正確執行的事,訓練這些內容不會教會你任何原本沒有的知識。值得納入的範例,是那些攜帶目前模型所缺乏資訊的案例。
選擇給了你輸入。目標則是另一個問題,而且只有三種來源,依品質與成本由高到低排序。
| 目標來源 | 描述 |
|---|---|
| 人類編輯的輸出 | 如果你的產品已經有審核步驟則免費,若沒有則極為昂貴。品質最高,也是唯一能反映使用者真正想要什麼、而非模型認為他們想要什麼的來源。 |
| 更強模型(已過濾) | 前沿模型的回答,僅在通過驗證器或評分標準時保留。便宜且可擴展。會繼承教師的盲點,並受限於教師的使用條款——見 /learn/fine-tuning-licenses。 |
| 你自己模型的輸出 | 免費,但未經過濾幾乎總是錯誤的。你無法透過模仿自己來學習原本沒有的行為。只有在有嚴格外部過濾器時才合理——例如通過的單元測試、已對帳的交易。 |
直覺是把一切都正規化:去除空白、統一大小寫、統一日期格式、將樣板內容模板化。大部分都要抵抗。推論時的輸入會以日誌中同樣混亂的方式呈現,而只在乾淨輸入上訓練的模型,處理混亂輸入時會表現更差。
真正需要做的事:
max_tokens 而非結束序列 token 停止的完成,都是在教導模型在中途句子停止。請根據結束原因而非長度來過濾。以隨機抽樣分割後,出現 400 次的模板化請求會同時出現在訓練集與測試集。你的評估會因此測量到記憶而誤報為泛化,數字會非常漂亮,直到生產環境與其不符為止。
請在分割之前先去重,並根據分組鍵而非單列來分割——依租戶、文件、工作階段、模板 ID 來分割。如果兩個範例有可能來自同一個底層事物,它們就應該屬於分割的同一側。
隨機分割也會讓模型在下個月資料上訓練,卻在上個月資料上測試。對於任何輸入分布會漂移的情況——而產品流量總是會漂移——請依時間分割:在截止時間之前的所有資料用於訓練,之後的所有資料用於測試。得到的數字會較低,但才是能預測生產環境的那一個。
這是專屬於日誌衍生資料的陷阱,而且它很安靜。你的日誌包含目前模型的輸出。如果你拿來訓練並部署,下個月的日誌就會包含新模型的輸出,然後你又拿來訓練。每一個世代都是在擬合前一個世代,而不是擬合使用者真正想要的內容。
可見的症狀是收窄:輸出變得更一致、避險消失、模型發展出語言上的抽搐,而稀有但有效的回應形式則完全消失。這與 Shumailov 等人在 2024 年《Nature》論文中描述的合成資料動態相同,只是透過日誌管線的後門進入。
防禦方式是硬性規則而非警覺:只有當目標來自模型外部——人類編輯、通過的測試、已對帳的交易、不同的模型——時,範例才能進入資料集。將來源記錄為一個欄位,並讓訓練作業拒絕沒有來源的列。
最後一步是機械性的,也是許多執行在無聲中失敗的地方。有兩個規則。
使用基礎模型自己的聊天模板。 不是看起來像的那個。特殊 token、角色標記與空白字元都很重要,如果訓練時使用的模板與服務堆疊套用的模板不同,適配器就會被要求在格式偏移中進行無謂的泛化。在啟動執行前,請渲染一個範例並印出可見的特殊 token。
在提示上遮罩損失。 許多訓練腳本預設會計算整個序列的損失,因此模型同時被訓練去預測你的指令以及它的答案。對於指令微調,你幾乎總是想要僅完成部分的損失:
tokens <|system|> You are ... <|user|> Summarise ... <|assistant|> The report ... <|eot|>
labels -100 -100 -100 ... -100 -100 -100 ... The report ... <|eot|>
________________ masked ________________/ ____ supervised ____/
Enter fullscreen mode
Exit fullscreen mode
結束回合的 token 必須位於受監督的區域內。如果遮罩它,模型就永遠不會被訓練如何停止,這會產生最常見的「微調搞壞了一切」報告:一個正確回答後卻不斷持續下去的模型。
https://dev.to/multigrid/building-a-fine-tuning-dataset-from-production-logs-e95
https://www.worldprogramming.org/posts/building-a-fine-tuning-dataset-from-production-logs-y3ppn0