Hugging Face 聊天範本
Hugging Face 推出聊天範本以防止效能無預警下降
Hugging Face 為 tokenizer 引入了 chat_template 屬性,旨在解決「無預警效能殺手」的問題——即當聊天模型使用與訓練時不同的格式進行提示時,會導致嚴重的模型效能退化。透過將格式化邏輯作為 Jinja 範本儲存在 tokenizer 內部,開發者可以確保對話歷史被轉換為模型所預期的精確字串格式,從而消除因格式錯誤導致的分佈偏移(distribution shifts)。
聊天格式中分佈偏移的問題
在標準語言模型中,從同一個 checkpoint 載入 tokenizer 和模型通常可以防止分佈偏移。然而,聊天模型需要將一系列訊息(包含「user」、「assistant」和「system」等角色)轉換為單一的可分詞字串。由於這種轉換目前尚無業界標準,不同的模型使用了截然不同的格式,例如:
- 簡單標籤:
User: [text] \n Bot: [text] - 特殊標記:
[USER] [text] [/USER] \n [ASST] [text] [/ASST] - 邊界標記:
<|im_start|>user \n [text]<|im_end|>
使用錯誤的格式是一種「無預警錯誤」,因為它不會觸發 Python 異常或明顯的失敗;相反地,模型僅僅是表現得明顯變差,這使得問題難以除錯。
透過 Jinja 範本進行技術實現
聊天範本是以 Jinja 範本字串的形式實現的,並隨 tokenizer 一起儲存與載入。選擇這種方法而非更簡單的系統(例如針對每個角色的前綴和後綴),是因為範本化具有足夠的靈活性來支援所有已知的訊息格式,並允許將邏輯與檢查直接編碼到範本中。
範本邏輯範例
對於使用邊界標記的格式,一個 Jinja 範本可能如下所示:
{% for message in messages %}
{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}
{% endfor %}
與 Transformers 函式庫的整合
聊天範本直接整合到 tokenizer 中,以維護「預處理資訊應與模型的分詞邏輯保持一致」的原則。
從類別層級格式化的轉變
以前,聊天格式是在類別層級處理的(例如,所有 LLaMA checkpoint 在 transformers 函式庫中使用相同的硬編碼邏輯)。為了保持向後相容性,模型類別現在擁有預設聊天範本。然而,Hugging Face 強烈建議在每個聊天模型上明確設置 chat_template,以避免脆弱性,並確保未來預設範本的變更不會破壞模型的效能。
使用與部署
開發者可以使用 tokenizer.apply_chat_template() 方法來套用這些範本。如果 tokenizer 缺少 chat_template 屬性,它將回退到類別預設值,這可能會導致上述提到的無預警錯誤。鼓勵使用者從模型卡(model cards)中識別正確的格式,並提交 Pull Request 以為 Hugging Face Hub 上的 checkpoint 添加 chat_template 屬性。
對新模型的建議
雖然 Hugging Face 承認單一標準格式是最理想的,但現有的訓練模型多樣性使得硬編碼標準變得不可能。對於正在訓練新聊天模型的開發者,Hugging Face 建議使用由 OpenAI 創建的 ChatML 格式,它使用 <|im_start|> 和 <|im_end|> 標記。這種格式對角色具有靈活性,並且可以用單行範本賦值來實現:
tokenizer.chat_template = "{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}"