2025年12月27日
ハードウェア
Ollamaファインチューニングのチャットテンプレート比較|ChatML・Gemma・Llama形式の構文と実装指針
Ollamaでのファインチューニングでは、チャットテンプレートの選択が出力品質を左右します。学習時と推論時でテンプレートが一致しないとモデルは期待どおりに応答しません。本稿ではChatML・Gemma・Llamaの3形式の構文と実装上の注意点を整理します。

概要
ローカルLLMをファインチューニングして社内用途に最適化する際、見落とされがちなのがチャットテンプレートの整合です。テンプレートが学習時と推論時で一致していないと、モデルは特殊トークンを正しく解釈できず、期待した応答を返しません。これはモデルそのものの性能とは無関係に発生する、再現性の高い失敗パターンです。
本稿では、DigitalBaseの検証で頻出する3つの主要形式(ChatML・Gemma・Llama)について、構文の差分とOllamaでの設定、実装上の落とし穴を整理します。
チャットテンプレートの役割
LLMは本質的に「次のトークンを予測する」モデルであり、会話の役割(user / assistant)を構造的に区別する仕組みを内蔵していません。チャットテンプレートは特殊トークンを用いて、この役割の境界を明示します。
テンプレートを使わない場合、入力は次のように曖昧になります。
ユーザー:こんにちは アシスタント:こんにちは!
この形式では「ユーザー:」が入力の一部なのか役割ラベルなのかを、モデルが判別できません。特殊トークンによる明確な区切りが必要になります。
ChatML形式
OpenAIが提唱したフォーマットで、現在最も広く採用されている形式です。
構文
<|im_start|>system あなたは親切なアシスタントです。<|im_end|> <|im_start|>user こんにちは<|im_end|> <|im_start|>assistant こんにちは!何かお手伝いできますか?<|im_end|>
特徴
- 開始 / 終了タグ:
<|im_start|>と<|im_end|>で各発話を囲みます。 - 役割名:
system、user、assistantを直接記述します。 - 改行位置: 役割名の直後に改行し、内容の後に終了タグを置きます。
採用モデル
Qwen2.5 / Qwen3、Mistral / Mixtral、Yi、DeepSeek など多数のモデルが採用しており、エコシステムが最も充実しています。
Python実装例
from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B-Instruct") messages = [ {"role": "system", "content": "あなたは親切なアシスタントです。"}, {"role": "user", "content": "こんにちは"}, ] # テンプレート適用 text = tokenizer.apply_chat_template(messages, tokenize=False) print(text)
Gemma形式
GoogleのGemmaシリーズ専用のフォーマットで、シンプルな構文が特徴です。
構文(Gemma 3)
<start_of_turn>user こんにちは<end_of_turn> <start_of_turn>model こんにちは!何かお手伝いできますか?<end_of_turn>
特徴
- タグ形式:
<start_of_turn>と<end_of_turn>のペアで囲みます。 - 役割名:
userとmodelを使用します(assistantではない点に注意)。 - systemロール: Gemma 2までは非対応で、Gemma 3から対応しました。
Gemma 2以前との違い
Gemma 2までは、systemプロンプトをuserメッセージに含める必要がありました。
<start_of_turn>user あなたは親切なアシスタントです。 こんにちは<end_of_turn>
Gemma 3では user / model を基本とする設計に整理されています。
Python実装例
from unsloth.chat_templates import get_chat_template # Gemma用テンプレート適用 tokenizer = get_chat_template(tokenizer, chat_template="gemma-3") messages = [ {"role": "user", "content": "こんにちは"}, ] text = tokenizer.apply_chat_template(messages, tokenize=False)
Llama形式
MetaのLlamaシリーズ専用です。バージョン間で構文が大きく異なる点に注意が必要です。
Llama 2構文
<s>[INST] <<SYS>> あなたは親切なアシスタントです。 <</SYS>> こんにちは [/INST] こんにちは! </s>
Llama 3構文
<|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|>
特徴
- バージョン非互換: Llama 2と3で構文が完全に異なります。
- 複雑なトークン: Llama 3は特殊トークンの種類が多くなっています。
- 改行の厳密性: 改行位置がずれると正しく動作しません。
注意点
Llama 2用のデータでLlama 3を学習すると失敗します。データセット作成前に必ずバージョンを確認してください。
3形式の比較
| 項目 | ChatML | Gemma | Llama 3 |
|---|---|---|---|
| 開始タグ | <|im_start|>role | <start_of_turn>role | <|start_header_id|>role<|end_header_id|> |
| 終了タグ | <|im_end|> | <end_of_turn> | <|eot_id|> |
| アシスタント名 | assistant | model | assistant |
| Systemロール | あり | Gemma 3から | あり |
| 採用モデル数 | 多い | Gemmaのみ | Llamaのみ |
| 構文の複雑さ | 低 | 低 | 高 |
Ollamaでの設定
各形式をOllamaで使用する際の Modelfile の TEMPLATE 設定です。
ChatML(Qwen等)
TEMPLATE """<|im_start|>system {{ .System }}<|im_end|> <|im_start|>user {{ .Prompt }}<|im_end|> <|im_start|>assistant {{ .Response }}<|im_end|>"""
Gemma
TEMPLATE """<start_of_turn>user {{ if .System }}{{ .System }} {{ end }}{{ .Prompt }}<end_of_turn> <start_of_turn>model {{ .Response }}<end_of_turn>"""
Llama 3
TEMPLATE """<|start_header_id|>system<|end_header_id|> {{ .System }}<|eot_id|><|start_header_id|>user<|end_header_id|> {{ .Prompt }}<|eot_id|><|start_header_id|>assistant<|end_header_id|> {{ .Response }}<|eot_id|>"""
実装上の注意点
学習時と推論時の一致
最も重要なのは、学習時と推論時でテンプレートを一致させることです。GemmaをChatML形式で学習しても、モデルはその特殊トークンを認識できず、応答品質が大きく劣化します。
# 正しい: ベースモデルに合わせる tokenizer = get_chat_template(tokenizer, chat_template="gemma-3") # Gemmaの場合 # 誤り: 異なるテンプレートを適用 tokenizer = get_chat_template(tokenizer, chat_template="chatml") # Gemmaには使わない
特殊トークンの確認
手動でテンプレートを組み立てるとタイポや改行ミスの温床になります。unsloth の get_chat_template のように、トークナイザー側に処理を委ねる方法を推奨します。
# 推奨: トークナイザーに任せる text = tokenizer.apply_chat_template(messages, tokenize=False) # 非推奨: 手動で組み立て text = f"<|im_start|>user\n{user_input}<|im_end|>" # タイポや改行ミスの原因
改行位置の重要性
特にLlama形式では改行位置が厳密です。次の例のように改行が欠けると正しく動作しません。
# 誤り: 改行がない <|start_header_id|>system<|end_header_id|>あなたは... # 正しい: 改行を入れる <|start_header_id|>system<|end_header_id|> あなたは...
まとめ
チャットテンプレートは、LLMの学習と推論を正しく接続するための必須要素です。ChatML・Gemma・Llamaの3形式は互いに互換性がなく、ベースモデルに応じた選択が求められます。実装時は次の点を徹底してください。
- ベースモデルのテンプレートを確認する: 公式ドキュメントやトークナイザー設定を参照します。
unslothのget_chat_templateを活用する: 手動組み立ては避けます。- 学習・推論で統一する: Ollamaの
Modelfile側の設定も一致させます。
テンプレートの不一致は、ファインチューニングの工数を無駄にする典型的な失敗要因です。学習を始める前段で確実に潰しておくことが、安定した社内LLM運用への近道となります。
