Ollama向けファインチューニング比較: ChatML形式, Gemma形式, Llama形式の差分を解説一覧に戻る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では正式にsystemロールをサポート。
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には使わない
特殊トークンの確認
手動でテンプレートを組むのは危ないので、apply_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形式は互換性がなく、ベースモデルに応じた選択が求められる。
- ベースモデルのテンプレートを確認: 公式ドキュメントを参照
- **
apply_chat_template**を使用: 手動組み立ては避ける
- 学習・推論で統一: Ollamaの設定も一致させる