記事一覧へ

chat template - alpaca & chatml

LLMモデルのchat-template総まとめ:AlpacaからChatMLまで

ClaudeClaude Opus 4.5による翻訳

AI生成コンテンツは不正確または誤解を招く可能性があります。

LLMモデルの中でchat(instruct)モデルの場合は、APIで使用する際に/v1/completionsを通じたテキスト文字列の代わりに/v1/chat/completionsを通じたroleとcontentで構成されたmessages配列を入力として受け取ります。

[
  { "role": "user", "content": "こんにちは" },
  { "role": "assistant", "content": "こんにちは、私は親切なチャットボットです" },
  { "role": "user", "content": "空が青い理由は何ですか?" }
]

このようなメッセージ配列を実際にモデルに入力する前にテキスト文字列形式に変換する必要がありますが、この時chat templateが使用されます。 chat templateは上記のmessages配列をどのようなフォーマットのテキスト文字列に変換するかを決定するjinja templateを意味します。

以下は多くのchat template formatの中の1つであるchatml formatの簡略化された例です。

{%- for message in messages %}
    {{- '<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n' }}
{%- endfor %}

{%- if add_generation_prompt %}
    {{- '<|im_start|>assistant\n' }}
{%- endif %}

上記のmessages配列をchatml formatで変換すると、次のようなテキスト文字列が生成されます。

<|im_start|>user
こんにちは<|im_end|>
<|im_start|>assistant
こんにちは、私は親切なチャットボットです<|im_end|>
<|im_start|>user
空が青い理由は何ですか?<|im_end|>
<|im_start|>assistant

基本的に<|im_start|><|im_end|>はそれぞれターンの開始と終了を表し、最後のハイライトされた部分はモデルに指示を提供するためにadd_generation_promptオプションをTrueにして、これから生成すべき部分の開始を(<|im_start|>assistant\n)レンダリングした結果です。

このようにレンダリングされたテキストをモデルに入力すると、モデルは次のように応答を生成します。

空が青い理由は光の散乱という現象のためです。<|im_end|>

ここで実際にモデルは<コンテキストとターンに合わせて生成された応答><|im_end|>を生成し、stop token(eos token)として<|im_end|>が認識されて生成が中断されました。 ここまで完了すると/v1/chat/completionsに対する応答としてモデルの応答が返されます。

このようなユーザーとモデルの対話を特定のフォーマットで表すことを広い範囲でChat markup language(ChatML)と呼び、その派生形が存在します。(*ChatMLという名前は特定のフォーマットを指すこともあります。) このような特定のフォーマットでレンダリングしてくれるテンプレートコードがChat templateであり、これは各モデルがInstruct tuningを行う過程で学習されるため、モデルごとに固有のテンプレートが存在します。

chat templateフォーマットの種類

ChatML

最も広範囲に採用されているフォーマットであり、優れた性能を示します。 ChatMLとその派生形を使用する代表的なモデルは次の通りです。

Qwen、Hermes、SmolLM、Dolphin、InternLM(派生形)....

ターンの開始を知らせる<|im_start|>{{role}}\nとターンの終了を知らせる<|im_end|>を使用します。

BOS: <|begin_of_text|>(モデルによる) EOS: <|im_end|>

NousResearch/Hermes-3-Llama-3.1-8B
<|begin_of_text|><|im_start|>system
あなたはユーザーをいつもテンションアップさせるエネルギッシュな友達です。<|im_end|>
<|im_start|>user
眠い。<|im_end|>
<|im_start|>assistant
コーヒー飲んで!<|im_end|>
<|im_start|>user
でもコーヒーあんまり飲みたくない。<|im_end|>
<|im_start|>assistant
じゃあ冷たいアイスチョコにしよう!<|im_end|>

Alpaca

スタンフォードで開発されたAlpaca 7Bモデルで適用されたフォーマットです。 シングルトンを前提に開発されており、prompt_no_input、prompt_inputの2つのバージョンが存在します。

### Instruction:
お昼何食べよう?

### Response:
牛丼はどう?

Mistral (Latest)

Mistral系列の特徴として、レンダリング結果が改行なしで1行で出力されますが、以下では視覚的な便宜のために改行を追加しました。 またmistralaiはchat templateを最も頻繁に更新・改善しており、バージョン別に空白処理、改行処理などの改善を見るのも面白いです。

BOS: <s> EOS: </s>

mistralai/Mistral-Small-24B-Instruct-2501 (V7-Tekken)

<s>[SYSTEM_PROMPT]あなたはユーザーに素早く天気を教える友達です。[/SYSTEM_PROMPT]
[INST]今日の天気はどう?[/INST]曇りで肌寒いよ。</s>
[INST]傘必要かな?[/INST]うん、雨が降るかもしれないよ!</s>
mistralai/Ministral-8B-Instruct-2410 (V3-Tekken)

<s>[INST]あなたはユーザーに素早く天気を教える友達です。

今日の天気はどう?[/INST]曇りで肌寒いよ。</s>
[INST]傘必要かな?[/INST]うん、雨が降るかもしれないよ!</s>

For more information about the tokenizer please refer to mistral-common

Llama 3.x

Llama系列Instructモデルで使用されるフォーマットです。 EOSと次の指示を意味するスペシャルトークン間に改行がないのが特徴です。

BOS: <|begin_of_text|> EOS: <|eot_id|>

meta-llama/Llama-3.3-70B-Instruct

<|begin_of_text|><|start_header_id|>system<|end_header_id|>

あなたはユーザーの決断を助ける現実的な友達です。<|eot_id|><|start_header_id|>user<|end_header_id|>

運動行くかやめるか?<|eot_id|><|start_header_id|>assistant<|end_header_id|>

行け、後悔しないよ。<|eot_id|><|start_header_id|>user<|end_header_id|>

でもすごくめんどくさいんだけど...<|eot_id|><|start_header_id|>assistant<|end_header_id|>

5分だけ軽くでもやってみて、そしたら体がほぐれるよ!<|eot_id|>

モデル別chat templateの探し方

モデル別に学習時に使用されたテンプレートを探して正しく使用することがモデル性能に大きな影響を与えますが、これを探す方法は難しくありません。

  1. huggingface filesでtokenizer_config.jsonファイルを探す モデルがInstructモデルなら高い確率でtokenizer_config.jsonファイルにchat templateフィールドが存在します。 通常1行でフォーマットされており、ここにテンプレートが存在すればほとんどそのまま使用すれば良いです。

    hf.co/NousResearch/Hermes-3-Llama-3.2-3B/blob/main/tokenizer_config.json
    ...
    "bos_token": "<|begin_of_text|>",
    "chat_template": "{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}",
    "clean_up_tokenization_spaces": true,
    "eos_token": "<|im_end|>",
    ...
  2. model cardに明示された「Prompting Template」を探す モデル制作者が親切な場合、どのテンプレートが動作するかについて記述している場合があります。 どの方法で学習したかによって全く異なるテンプレートが使用されることもあるので、これを参考にして使用すれば良いです。 以下の場合はMistral [INST]テンプレートとChatMLの混合形態を使用しています。 e.g. Sao10K/MN-12B-Lyra-v3

AutoTokenizerでchat templateをレンダリング

from transformers import AutoTokenizer
import os

messages = [
    {"role": "system", "content": "あなたはユーザーの一日を楽しくしてくれるいたずら好きな友達です。"},
    {"role": "user", "content": "暇だな。"},
    {"role": "assistant", "content": "モグラのジョーク1つ言うね!なぜモグラはインターネットが好きなんだと思う?"},
    {"role": "user", "content": "なぜ?"},
    {"role": "assistant", "content": "なぜなら、『トンネル』を通じていつも新しい情報を探すからだよ!"},
]

tokenizer = AutoTokenizer.from_pretrained(
    "HuggingFaceTB/SmolLM2-1.7B-Instruct",
    token=os.environ.get("HF_TOKEN"),
    legacy=False,
)

print(tokenizer.apply_chat_template(messages, tokenize=False))

Reference

作成日:
更新日:

前の記事 / 次の記事