ごく普通のテキストのみのデータでGemma 4のファインチューニングを始めたら、最初のステップでトレーナーがこのエラーを吐いて止まった——そんな経験はありませんか?
ValueError: `mm_token_type_ids` is required as a model input when training画像もなければ音声もない。ただのチャットデータです。なのになぜGemma 4はマルチモーダル用のトークンフィールドを要求してくるのでしょうか。これはGemma 4のリリース初日から知られている既知の引っかかりどころで(transformers issue #45200)、仕組みさえ理解すれば修正はごく小さなものです。本記事では、1行で済む暫定対処と、mm_token_type_idsエラーを完全に消し去るコピペ可能なカスタムdata collatorの両方を紹介します。
このエラーで実際に起きていること
トレースバックの最下層は、モデルのforwardパスの内部にたどり着きます。
File ".../transformers/models/gemma4/modeling_gemma4.py", line ..., in forward
raise ValueError(
ValueError: `mm_token_type_ids` is required as a model input when trainingこのエラーは、データセットやLoRAの設定、GPUの種類に関係なく、トレーニングのまさに最初のステップで発生します。素のTrainerでも、TRLのSFTTrainerでも、QLoRA構成でも同じように踏み抜きます。共通点はただ一つ——マルチモーダル前提で設計されたモデルに、テキストのみのデータセットを食わせていることです。
なぜ起きるのか(根本原因)
Gemma 4は設計からしてマルチモーダルです。テキストトークンを画像・音声トークンと区別するために、このアーキテクチャはinput_idsに加えて2つの入力を導入しました。
token_type_ids— 従来からあるセグメント用のフィールド。mm_token_type_ids— どの位置がテキスト/画像/音声なのかを示す、マルチモーダル用のトークンタイプフィールド。
問題は、Gemma 4のモデルコードが、loss を計算するトレーニング時のforwardパス(labelsが渡されたトレーニングモード)でmm_token_type_idsの存在を検証してしまう点にあります。バッチの中に画像が1枚も含まれていなくても、です。一方で、標準のtokenizerもデフォルトのdata collatorも、そのフィールドを生成することは決してありません。テキストのみの実行ならすべての値は単純に0になるはずなのですが、フィールドがゼロ埋めされているのではなく完全に欠落しているため、モデルは妥当なデフォルト値を仮定する代わりにエラーを投げてしまうわけです。(リリース初日のtransformers==5.5.0.dev0のソースビルド時点で、該当するraiseはmodeling_gemma4.pyのforward内、トレーニング分岐に存在します。正確な行番号はコミットによって動きます。)
つまりこのバグの正体はミスマッチです——モデルはmm_token_type_idsを要求するのに、tokenizerはそれを決して作らず、しかもトレーナーはたとえ自分で作っても捨ててしまう。この3つすべてを直せば、エラーは消えます。
まずは1行で動かす暫定対処
トレーニングループを自分で書いていて、とにかく今すぐ前に進めたいだけなら、forward呼び出しの前にゼロのテンソルを差し込みましょう(torchをimportしておいてください)。
import torch
inputs["mm_token_type_ids"] = torch.zeros_like(inputs["input_ids"])テキストのみのデータでは、すべてのトークンがテキストトークンなので、input_idsと同じ形状のオールゼロのテンソルがまさに正解です。手書きのトレーニングループを動かすにはこれで十分です。ただしTrainer/SFTTrainerを使う場合はforward呼び出しを自分で握っていないため、このフィールドをバッチそのものに焼き込んでおく必要があります——それを担うのが、次に紹介するcollatorです。
完全な解決策:カスタムGemma 4 data collator
ポイントは3つあり、そのすべてが必要です。どれか1つでも飛ばすと、mm_token_type_idsエラーはすぐに戻ってきます。
0. 最小限のセットアップ
以下のスニペットを端から端まで実行できるように、前提となる足場のコードを示しておきます——モデルとデータセットはご自身のものに差し替えてください。
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
model_id = "google/gemma-4-4b-it" # any Gemma 4 checkpoint
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, torch_dtype=torch.bfloat16)
# This guide assumes each example has a `messages` field (chat format):
# {"messages": [{"role": "user", "content": "..."},
# {"role": "assistant", "content": "..."}]}
# If your data is instruction/output style, convert it to messages first.
dataset = load_dataset("your/dataset", split="train")1. tokenize時に両方のフィールドを追加する
tokenizeするときに、token_type_idsとmm_token_type_idsを、input_idsと同じ長さのオールゼロのリストとして付け加えます。
def format_chat(example):
text = tokenizer.apply_chat_template(
example["messages"], tokenize=False, add_generation_prompt=False
)
tokenized = tokenizer(text, truncation=True, max_length=4096)
tokenized["token_type_ids"] = [0] * len(tokenized["input_ids"])
tokenized["mm_token_type_ids"] = [0] * len(tokenized["input_ids"])
tokenized["labels"] = tokenized["input_ids"].copy()
return tokenized
tokenized_dataset = dataset.map(format_chat, remove_columns=dataset.column_names)このコードはシーケンス全体(プロンプト+応答)に対してlossを計算する点に注意してください。標準的なSFTの挙動——アシスタントの返答部分だけでlossを取りたい——を望むなら、ここでプロンプト部分のトークンのlabelsを-100に設定してマスクしてください。
2. それらを保持してpaddingするcollatorを使う
デフォルトのcollatorはこれらのカスタムフィールドをどうpaddingすればよいか分かりません。そこで自前のcollatorを定義します。これはinput_idsをpaddingし、attention_maskを組み立て、2つのトークンタイプフィールドをバッチ内の最大長までゼロ埋めし、labelsのpadding部分を-100でマスクします。
from dataclasses import dataclass
import torch
@dataclass
class GemmaCollator:
tokenizer: object
def __call__(self, features):
max_len = max(len(f["input_ids"]) for f in features)
# Gemma tokenizers define a pad token; fall back to eos just in case.
pad_id = self.tokenizer.pad_token_id or self.tokenizer.eos_token_id
batch = {
"input_ids": [],
"attention_mask": [],
"token_type_ids": [],
"mm_token_type_ids": [],
"labels": [],
}
for f in features:
pad_len = max_len - len(f["input_ids"])
batch["input_ids"].append(f["input_ids"] + [pad_id] * pad_len)
batch["attention_mask"].append([1] * len(f["input_ids"]) + [0] * pad_len)
batch["token_type_ids"].append([0] * max_len)
batch["mm_token_type_ids"].append([0] * max_len)
batch["labels"].append(
f.get("labels", f["input_ids"]) + [-100] * pad_len
)
return {k: torch.tensor(v) for k, v in batch.items()}3. remove_unused_columns=Falseを設定する
これが誰もが忘れがちなステップです。デフォルトではトレーナーは、collatorが走る前に、モデルのシグネチャが「認識しない」列をすべて削ぎ落としてしまいます。そのため、せっかく丁寧に追加したmm_token_type_idsが途中で消され、エラーが戻ってくるのです。この挙動をオフにしましょう。
from trl import SFTConfig, SFTTrainer
training_args = SFTConfig(
output_dir="gemma4-finetune",
per_device_train_batch_size=1,
gradient_accumulation_steps=8,
num_train_epochs=1,
learning_rate=2e-4,
remove_unused_columns=False, # <-- critical: keep mm_token_type_ids
dataset_kwargs={"skip_prepare_dataset": True}, # we pre-tokenized ourselves
)
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset,
data_collator=GemmaCollator(tokenizer),
)
trainer.train()ステップ1でフィールドを作り、ステップ2で保持・paddingし、ステップ3で列の削除から守る——これで、モデルはmm_token_type_idsを受け取れるようになり、トレーニングは正常に進みます。
修正できたかを確認する
本番の実行を始める前に、バッチを1つダンプして、mm_token_type_idsが実際に存在し、ゼロの形状になっているかを確認しましょう。
collator = GemmaCollator(tokenizer)
batch = collator([tokenized_dataset[0], tokenized_dataset[1]])
print(batch.keys())
print("mm_token_type_ids" in batch) # True
print(batch["mm_token_type_ids"].shape) # matches input_ids
print(batch["mm_token_type_ids"].sum().item()) # 0 for text-onlymm_token_type_idsがキーに含まれていて、その合計が0なら完了です——次のtrainer.train()は、最初のステップを問題なく通過します。
よくあるミスと関連エラー
token_type_idsだけを追加してしまう。 この2つは別物です。Gemma 4が明確にチェックするのはmm_token_type_idsであり、mmの付かない方のフィールドだけでは検証を通過できません。remove_unused_columns=Falseを忘れる。 「フィールドを追加したのにエラーが消えない」の最も多い原因がこれです。トレーナーが何も言わずに先にそれを捨ててしまいます。- collatorがトークンタイプフィールドを誤った長さでpaddingしてしまう。 これらはpadding後の
input_idsの長さに一致させる必要があります。元の長さに合わせると、forwardパスのさらに奥で形状不一致エラーが出ます。 - Gemma 3で
token_type_ids is requiredが出る。 一世代前のGemma 3では、TRLでのファインチューニング時にこれと同じエラーのmmなし版が発生します(trl issue #5032)。同じゼロ埋め戦略が有効です。 - QLoRAが別の原因でクラッシュする。 4-bitのGemma 4ファインチューニングが、これとは無関係なadapter周りのエラーで失敗する場合、それは別の既知の問題であって(peft issue #3129)、collatorとは関係ありません。まず
mm_token_type_idsエラーを直し、その後で量子化に取り組みましょう。
LoRAとQLoRAの比較、Unsloth、データセットの準備、GGUFエクスポートまで——より広範なファインチューニングのセットアップについては、Gemma 4 ファインチューニングガイドをご覧ください。
upstreamで修正される見込みは?
おそらく修正されます。transformers issue #45200で提案されているのは、テキストのみのトレーニングでmm_token_type_idsが欠落している場合、エラーを投げる代わりにモデル側で**mm_token_type_idsをゼロにデフォルト設定する**という案です。これが安定版リリースに取り込まれるまで(現状このエラーはtransformers==5.5.0.dev0のソースビルドで再現します)、上記のcollatorが信頼できる回避策です。モデルが期待する入力は、公式のGemma model docsで追跡できます。修正版が出荷されたら、カスタムcollatorを外して標準のものに戻して構いません——とはいえ、残しておいても害はありません。
FAQ
画像でファインチューニングする場合、mm_token_type_idsは必要ですか?
はい、しかもその場合は実際に意味を持ちます——マルチモーダルなトークン位置はオールゼロではなく、きちんとマークされるべきです。本記事のオールゼロという近道が有効なのは、テキストのみのデータに限られます。
Unslothを使っています。これは当てはまりますか?
一部当てはまります。Unslothはモデルとtokenizerをラップし、独自のデータ処理を備えているため、このcollatorをそのまま無造作に放り込まないでください。まずUnslothを更新して再テストしましょう——そのパッチが、すでにあなたの代わりにmm_token_type_idsを注入してくれているかもしれません。それでもエラーが続く場合は、このcollatorを丸ごと差し替えるのではなく、Unslothのフローの中で同じ考え方を適用します——データのフォーマット処理(あるいはそのdata_collator)の中でmm_token_type_idsをゼロ埋めし、remove_unused_columns=Falseを維持するのです。
単にtransformersをダウングレードして回避できませんか? Gemma 4対応より前のリリースにpinすることはできますが、そうするとGemma 4自体をまったくロードできなくなります。collatorによる修正の方が、バージョンを固定するよりも安全です。
これは推論にも影響しますか?
いいえ。このチェックはトレーニングのlossパスにのみゲートされています。素のgeneration/推論では、テキストのみのプロンプトに対してmm_token_type_idsを渡す必要はありません。
TRLではなく素のTrainerを使っています。これでも動きますか?
はい。GemmaCollatorはフレームワークに依存しません——素のTrainerにdata_collatorとして渡し、TrainingArgumentsでremove_unused_columns=Falseを設定すればOKです。
次のステップ
- エンドツーエンドで本格的なファインチューニングを実行する → Gemma 4 ファインチューニングガイド
- 他のエラー(OOM、トレーニングが遅い、GPUが検出されない)に遭遇している? → Gemma 4 トラブルシューティング
- 自分のGPUに合ったモデルサイズを選ぶ → どのGemma 4モデルを使うべき?
Stop reading. Start building.
~/gemma4 $ Get hands-on with the models discussed in this guide. No deployment, no friction, 100% free playground.
Launch Playground />


