オレンジ色のケーブルが接続されたパッチパネル
ニュース深掘り

Gemma 4 オンデバイス推論でJinjaテンプレートが落とす罠

目次を見る

ファインチューニング済みモデルがロードに成功し、トークナイザーも正常に初期化された。しかし最初の推論リクエストを投げた瞬間に `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に組み込む流れが出てきています。

モデルの精度よりも先に「実行できる状態か」を検証するスモークテストを、エクスポート直後のステップとして用意しておくと、この種の問題を早期に捕捉できます。

参考

My Fine-Tuned Gemma 4 Loaded Fine, Then Broke on the First Message

この記事について: 本記事は AI を活用して作成し、forva AI 編集部が内容を確認・監修しています。

AI 駆動開発のご相談は forva AI へ。まずはお気軽にどうぞ。