Gemma-3-1b-it日本語LoRAファインチューニング実践
作ったもの
Gemma-3-1b-itモデルを日本語データセットでLoRAファインチューニングする実装をしました。
1Bパラメータの小規模モデルを、日本語QAデータで特化させる。Google ColabのA100 GPUで、20エポックほど学習させました。
LoRA(Low-Rank Adaptation)は、大規模モデルの一部のパラメータだけを効率的に微調整する手法です。全パラメータを更新するより、計算コストが1/10以下に削減できる。
まずは、データセットのプロンプト化の流れを見てみましょう:
Gemma-3専用のプロンプトフォーマットに変換する必要がありました。<start_of_turn>タグで会話を区切る独自の形式です。
なぜ作ったか
小規模なLLMを日本語に特化させたかった。
Gemma-3-1b-itは1Bパラメータ。軽量で動作が速いけど、日本語の精度は微妙。英語のデータで学習されているから、日本語の回答が不自然になる。
LoRAなら、Google Colabの無料枠でもファインチューニングできる。全パラメータを更新すると、メモリ不足でクラッシュする。でも、LoRAなら追加するパラメータは数十MBだけ。
収入がない身としては、GPUコストを抑えたい。無料枠のA100で試せる範囲で、実用的なモデルを作りたかった。
データセットの加工
プロンプト形式への変換
最初、通常のJSON形式でデータを用意した。大きくつまずいた。
Gemma-3は独自のプロンプト形式を使う。<start_of_turn>userと<start_of_turn>modelでユーザーとモデルの発言を区切る必要がある。
この形式に変換しないと、学習そのものが一度つまずいた。エラーメッセージもなく、ただ損失が下がらない。3時間くらい原因が分からず苦労した。
公式ドキュメントを読み直して、ようやく気づいた:
def format_gemma_prompt(question, answer):
return f"""<start_of_turn>user
{question}<end_of_turn>
<start_of_turn>model
{answer}<end_of_turn>"""
この形式に全データを変換。これでようやく学習が進むようになった。
データ量の調整
日本語QAデータは約5,000件用意した。
最初は全件使おうとした。メモリ不足で落ちる。これは課題。
学習データを1,000件に減らしたら、動いた。でも精度が不十分。
3,000件まで増やしたら、精度と速度のバランスが取れた。これなら実用的だと判断。
LoRAパラメータの設定
r(ランク)の選択
LoRAのrankは、追加する行列のランクを指定する。大きいほど表現力が高いけど、計算コストも増える。
最初、r=8で試した。学習は速いけど、精度が出ない。
r=16に上げたら、明らかに改善。質問に対する回答が自然になった。
r=32も試したけど、メモリ使用量が倍増。Colabの無料枠だと、途中で落ちる。
結論:r=16がベスト。精度と速度の両立ができた。
target_modules の選択
どの層にLoRAを適用するかも重要。
最初はq_projとv_projだけ。これで動作確認。速いけど、精度がイマイチ。
k_projも追加したら、少し改善。
最終的に、["q_proj", "v_proj", "k_proj", "o_proj"]の4つに適用。これで精度が大幅に向上した。
全層に適用すれば、さらに精度は上がるかもしれない。でも、計算コストとのトレードオフ。今回は4層で妥協した。
学習パラメータの調整
学習率(learning_rate)
学習率の調整が一番難しかった。
最初、5e-4(=0.0005)で試した。損失が発散。学習が不安定になって、まったく収束しない。
1e-4に下げたら、収束したけど遅い。20エポックで終わらない。
3e-4に上げたら、いい感じになった。10エポックくらいで損失が十分に下がる。
最終的に3e-4に落ち着いた。試行錯誤に2日かかった。
エポック数
エポック数も悩んだ。
5エポックだと、まだ損失が下がり続けている。早期終了はもったいない。
50エポックだと、過学習の兆候が出る。訓練データには答えられるけど、新しい質問には対応できない。
20エポックがちょうど良かった。損失が安定して、精度も十分。
バッチサイズ
バッチサイズは8に設定。
16にしたら、メモリ不足。Colabの無料枠だと、16GBのVRAMでは足りない。
4に下げたら、学習が遅すぎる。1エポックに30分かかる。
8で妥協。1エポックが10分程度で、現実的な時間で終わる。
学習結果の保存
LoRAのみ保存 vs マージ保存
LoRAの保存方法は2種類ある。
① LoRAのみ保存:追加パラメータだけ保存。ファイルサイズが小さい(約50MB)。
② マージ保存:ベースモデルとLoRAを統合。ファイルサイズが大きい(約2GB)。
最初、①だけ保存した。推論時にベースモデルと組み合わせる必要があって、面倒。
②も保存することにした。推論がシンプルになる。ファイルサイズは大きいけど、使い勝手が良い。
Google Driveに両方保存。用途に応じて使い分けられるようにした。
# LoRAのみ保存
trainer.save_model("lora_only")
# マージ保存
model = model.merge_and_unload()
model.save_pretrained("lora_merged")
実際に試した結果
日本語QAの精度
「日本の首都は?」と質問したら、「東京」と即答。完璧。
「日本で一番高い山は?」→「富士山(3,776m)」。標高まで正確に答えた。意外でした。
「夏目漱石の代表作は?」→「『吾輩は猫である』『坊っちゃん』『こころ』」。3つも挙げてくれた。
ファインチューニング前だと、「Tokyo is the capital of Japan」と英語で答えていた。日本語でちゃんと答えるようになった。これは嬉しい。
複雑な質問
「LoRAとは何ですか?」と聞いてみた。
「大規模言語モデルを効率的にファインチューニングする手法です。全パラメータを更新せず、一部の行列だけを追加して学習します。」
かなり正確に答えた。データセットにLoRAの説明を入れてあったから、ちゃんと学習できてる。
失敗例
「量子コンピュータの仕組みは?」と聞いたら、曖昧な回答。
データセットに量子コンピュータの情報がない。知識がないことは答えられない。当然だけど、限界はある。
使ってみて
Gemma-3-1b-itのLoRAファインチューニングは、小規模モデルを特化させる効果的な方法でした。
ポイントは以下の3つ:
- プロンプト形式の正確な変換:Gemma-3専用の
<start_of_turn>形式が必須、これを間違えると学習が失敗
- LoRAパラメータの調整:r=16、target_modules=["q_proj", "v_proj", "k_proj", "o_proj"]で精度とコスト削減を両立
- 学習率の段階的調整:5e-4→1e-4→3e-4と試行、3e-4で安定した学習が可能
最初はプロンプト形式を間違えて3時間無駄にしました。公式ドキュメントを読み直して、ようやく正しい形式を理解。データ加工の部分が一番重要だと痛感しました。
小規模モデルのファインチューニングを考えている方の参考になれば嬉しいです。
なお、より安定したコードや高精度な学習については、midori293で公開しているColab用ノートブックが最終的な成果物です。そちらも合わせてご覧ください。
まとめ
今回は、Gemma-3-1b-itの日本語LoRAファインチューニングを実装しました。
ポイントは以下の5つ:
- データ加工:Gemma-3専用のプロンプト形式への変換が最重要、これを間違えると学習失敗
- LoRAランク:r=16で精度とコスト削減を両立、r=8では不十分、r=32ではメモリ不足
- 学習率:3e-4で安定した収束、5e-4では発散、1e-4では遅すぎる
- エポック数:20エポックが最適、50では過学習の兆候
- 保存方法:LoRAのみ(50MB)とマージ(2GB)の両方を保存、用途に応じて使い分け
小規模モデルでも、適切なデータセットとパラメータ調整で実用的な精度を達成できました。Google Colabの無料枠でも十分に学習可能です。
同じような小規模モデルのファインチューニングを考えている方の参考になれば嬉しいです。
さらに深く学ぶには
この記事で興味を持った方におすすめのリンク:
自分の関連記事:
最後まで読んでくださり、ありがとうございました。