2025年12月27日
ハードウェア
Ollama向けファインチューニング比較: ChatML形式, Gemma形式, Llama形式の差分を解説
Ollamaのファインチューニングにおいて、チャットテンプレートの選択は成否を分ける重要な要素だ。テンプレートが学習時と推論時で一致しないと、モデルは期待通りの出力を返さない。本記事では、主要な3形式(ChatML・Gemma・Llama)の構文を比較し、実装上の注意点を解説。

チャットテンプレートの役割
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を直接記述 - 改行位置: 役割名の直後に改行、内容の後に終了タグ
採用モデル
Qwen 2.5、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, assistantのみの設計思想
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設定。
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の設定も一致させる