ファインチューニング済みモデルがロードに成功し、トークナイザーも正常に初期化された。しかし最初の推論リクエストを投げた瞬間に `Failed to apply template: unknown method: map has no method named get (in template:238)` というエラーで止まる。これはGemma 4 E2Bをオンデバイス推論エンジン「LiteRT-LM」向けにデプロイした際に実際に発生した問題で、モデルのロード段階ではまったく検出できない類のバグです。
LiteRT-LMとJinjaテンプレートの関係
まず前提技術を整理します。LiteRT-LMはGoogleがAndroid向けに提供するエッジ推論エンジンで、`.litertlm`という独自フォーマットのモデルファイルを使います。TensorFlow Liteの後継にあたるLiteRTランタイム上で動作し、NPUやGPUを活用したオンデバイス推論を実現します。
Jinjaテンプレートは、HuggingFaceのトークナイザー設定ファイル (`tokenizer_config.json`) の中に埋め込まれるチャット形式の変換ロジックです。モデルに渡すプロンプトを「ユーザー」「アシスタント」「システム」といったロール別に整形する役割を担います。PythonのJinja2ライブラリを前提に書かれていることが多く、`.get()` メソッドや `map` フィルターといったPython寄りの機能を使うテンプレートが存在します。
問題の核心はここにあります。LiteRT-LMはJinjaテンプレートのサブセットしか解釈できません。HuggingFaceのトークナイザーが生成するデフォルトテンプレートには、LiteRT-LMのランタイムが未対応のメソッドが含まれることがあります。Gemma 4のデフォルトテンプレートはまさにそのケースに該当し、`map has no method named get` というエラーが `template:238` 行目で発生します。
QLoRAファインチューニングからデプロイまでのパイプライン
今回の事例では以下のパイプラインが使われました。
- HuggingFaceのベースウェイト取得
- QLoRAでアダプターを学習 (Colab上)
- アダプターをベースモデルにマージ
- チャットテンプレートのパッチ適用 ← ここが盲点
- 量子化して `.litertlm` へエクスポート
- デバイスへプッシュ
QLoRA (Quantized Low-Rank Adaptation) は、モデル全パラメーターを更新する代わりに小さなアダプター行列だけを学習する手法です。Gemma 4 E2Bは総パラメーター数5.1Bですが、Per-Layer Embeddingsにより実効パラメーター数は2.3Bに抑えられています。この「E」(Effective)アーキテクチャのおかげで、Colab上の単一GPUでファインチューニングが完結します。
学習に使われた設定ではランク8、アルファ16というLoRA標準的な比率 (alpha/rank = 2) が採用されました。学習対象はアテンション層 (q/k/v/o_proj) とMLP層 (gate/up/down_proj) の全7モジュールで、追加される訓練可能パラメーターは約285万個、全体の0.06%です。3,000サンプルで1エポック、所要時間は217秒という軽量な学習でも、デプロイ機構の検証としては機能します。
テンプレートパッチという「未文書の手順」
モデルをマージした後、`tokenizer_config.json` の `chat_template` フィールドを書き換える必要があります。具体的には `.get()` 呼び出しや `map` フィルターを使わないLiteRT-LM互換の記法に差し替えます。
import json
with open("merged_model/tokenizer_config.json", "r") as f:
config = json.load(f)
# LiteRT-LM が解釈できるシンプルなテンプレートに差し替える
config["chat_template"] = YOUR_COMPATIBLE_TEMPLATE
with open("merged_model/tokenizer_config.json", "w") as f:
json.dump(config, f, indent=2)このパッチをエクスポート前に適用しないと、`.litertlm` ファイル自体には問題がなくても推論実行時にクラッシュします。モデルロード時のバリデーションではテンプレートの構文解釈が走らないため、エラーが推論フェーズまで隠れてしまうのです。
同種の問題はLlama系モデルやMistralでも発生しうる話です。HuggingFaceのチャットテンプレートはPython実行環境を前提に設計されており、iOSのMLXやExpoのReact Native向けモデルランタイムなど、エッジランタイム全般でJinja互換性の差異は共通の課題となっています。
Webフロントエンドの観点で言えば、WebAssembly上で動くweb-llm (MLC-LLM) も独自のチャットテンプレートレイヤーを持ち、同様の互換問題が起きます。エッジランタイム向けにモデルを配布するライブラリ作者は、テンプレートの互換性テストをCI/CDに組み込む流れが出てきています。
モデルの精度よりも先に「実行できる状態か」を検証するスモークテストを、エクスポート直後のステップとして用意しておくと、この種の問題を早期に捕捉できます。