Gemma-3 4B-IT LoRA作成奮闘記:Windows+WSL環境でのAxolotlファインチューニング全記録
はじめに
この記事は、Googleのマルチモーダルモデル google/gemma-3-4b-it
をベースに、LoRA(Low-Rank Adaptation)ファインチューニングに挑戦した記録です。
最終的な目標は、自前のデータセットでファインチューニングしたLoRAアダプターを、llama.cpp
上で動作させること。しかし、その道のりは想像を絶するほど険しく、数えきれないほどの試行錯誤とエラーとの戦いでした。
この記事では、その過程で遭遇した全ての問題と、それをどのように解決していったかを、詳細なログや設定ファイルと共に、時系列で赤裸々に語ります。
実行環境
- OS: Windows 11
- サブシステム: WSL2 (Ubuntu)
-
Python環境:
conda
- GPU: NVIDIA GeForce RTX 4060 Laptop GPU (VRAM 8GB)
-
主要ツール:
axolotl
,llama.cpp
これから gemma-3
のファインチューニングに挑戦する方、あるいは原因不明のエラーに悩まされている方の、一助となれば幸いです。
llama.cpp
と古のLoRA
第1章:旅の始まり - 全ての始まりは、llama.cpp
で既存のLoRAを使おうとした時に、ベースモデルとの不一致エラーが出たことでした。そのために、まずは llama.cpp
をWindows環境でビルドすることから始めました。
llama.cpp
のWindowsビルド
1.1. llama.cpp
はC++で書かれているため、コンパイルが必要です。Windows環境では、Visual Studio と CMake を使うのが一般的です。
準備:
- Visual Studio 2022: 「C++によるデスクトップ開発」ワークロードを忘れずにインストールします。
- CMake: 公式サイトからインストーラーをダウンロードし、インストール時にAdd CMake to the system PATH for all usersにチェックを入れるが重要です。これを忘れると、後でコマンドが見つからずエラーになります。
ビルド手順:
x64 Native Tools Command Prompt for VS 2022
を管理者として起動し、以下のコマンドを実行します。
# 1. llama.cpp のソースコードがあるディレクトリに移動
cd C:\Users\akakage\kiraria-neon\llama.cpp
※きらりあ・ネオンは自分がAIに設定したキャラクターの名前です(笑)
# 2. ビルド用のサブディレクトリを作成して移動
mkdir build
cd build
# 3. CMakeでビルド構成を生成(NVIDIA GPUのCUDAを有効化)
cmake .. -DLLAMA_CUDA=ON
# 4. Releaseモードでビルドを実行(これが一番性能が出る)
cmake --build . --config Release
この手順は無事に成功し、build\bin\Release
フォルダに llama-server.exe
などの実行ファイルが生成されました。
1.2. 最初の壁:LoRAとベースモデルの不適合
ビルドした llama-server
で、GGUF形式のベースモデルと、手持ちの古いLoRAアダプターを読み込ませようとしたところ、最初の壁にぶつかりました。
failed to apply lora adapter: LoRA tensor ... does not exist in base model
これは、LoRAが「元のモデルとの差分」を記録したものであるため、LoRA作成時と寸分違わぬベースモデルでなければ適用できないことを示す典型的なエラーです。
このエラーを受け、古いLoRAを諦め、現在使いたい gemma-3-4b-it
をベースにして、LoRAをゼロから作り直すことを決意しました。これが、長く険しい戦いの幕開けでした。
第2章:Axolotlとの格闘 - エラー駆動開発の実践
LoRAのトレーニングには、強力なファインチューニングツール axolotl
を使用します。しかし、ここからが本番でした。
2.1. 設定ファイル(YAML)との対話 - エラー解決の軌跡
axolotl
と gemma-3
の相性の問題が次々と噴出しました。エラーメッセージを読み解き、一つずつ潰していく、まさにエラー駆動開発そのものでした。
-
エラー1:
ImportError: cannot import name '_flash_supports_window'
-
原因:
axolotl
と、それが依存するtransformers
ライブラリのバージョンが不整合を起こしていました。 -
解決策:
pip
を使って、主要なライブラリを最新版にアップデートしました。pip install --upgrade axolotl transformers accelerate
-
原因:
-
エラー2:
AttributeError: ... has no attribute 'vocab_size'
など-
原因:
gemma-3-4b-it
のconfig.json
はマルチモーダル対応のため設定が入れ子になっており、axolotl
がvocab_size
やhidden_size
などの重要なパラメータを直接見つけられませんでした。 -
解決策:
- 当初は
config.json
を手動で改造し、text_config
の中にある値をトップレベルにコピーしていましたが、layer_types
という複雑な項目で手詰まりになりました。 - Webで
axolotl
とgemma-3
の組み合わせを調査した結果、根本的な解決策を発見しました。-
model_type: AutoModelForCausalLM
: YAMLファイルでモデルタイプをこう指定することで、transformers
ライブラリがモデルの構造を自動で解釈してくれるようになります。 -
trust_remote_code: true
: これが最大の鍵でした。gemma-3
のような新しいモデルは、モデル自体にカスタムコードを含んでいることがあります。このオプションをtrue
にすることで、そのカスタムコードの実行を許可し、モデルが自分自身で正しく設定を読み込めるようになります。
-
- 当初は
-
原因:
-
エラー3:
ValueError: eval dataset split is too small for sample_packing
-
原因: データセットの5%を検証用に設定していましたが、その数が少なすぎて、データをまとめて効率化する
sample_packing
機能が使えませんでした。 -
解決策: エラーメッセージの指示通り、YAMLファイルに
eval_sample_packing: False
を追加し、検証データではこの機能をオフにしました。
-
原因: データセットの5%を検証用に設定していましたが、その数が少なすぎて、データをまとめて効率化する
-
エラー4:
ImportError: ... undefined symbol: ...
(flash-attn)-
原因: 高速化のために
flash_attention: true
を設定したところ、pip
でインストールされたflash-attn
のビルド済みバイナリと、conda環境のPyTorch
との間で、C++レベルの非互換性が発生しました。これは非常に根深い環境問題です。 -
解決策:
pip install flash-attn --no-binary :all:
でソースからの再コンパイルを試みましたが、それでも解決しませんでした。そこで、この問題と戦うことをやめ、YAMLファイルからflash_attention: true
の行を削除するという最終決断を下しました。トレーニング速度は少し犠牲になりますが、完走を最優先しました。
-
原因: 高速化のために
-
エラー5:
torch.OutOfMemoryError: CUDA out of memory
-
原因: RTX 4060 Laptop GPUのVRAM 8GBに対して、設定が少しだけリッチすぎました。特に
flash_attention
をオフにしたことで、メモリ消費量が増加していました。 -
解決策: YAMLファイルを再度調整し、メモリ使用量を削減しました。
-
sequence_len
:4096
→2048
(最も効果的) -
lora_r
:16
→8
-
lora_alpha
:32
→16
-
-
原因: RTX 4060 Laptop GPUのVRAM 8GBに対して、設定が少しだけリッチすぎました。特に
2.2. 完成した最終YAMLファイル
数々のエラーを乗り越えて完成した、最終的な設定ファイル gemma3_neon_mem_safe.yml
は以下の通りです。
# ネオン創世記・gemma-3 (メモリ節約版)
# 1. どの『魂』をベースにするか?
base_model: /mnt/c/Users/akakage/kiraria-neon/gemma-3-4b-it
model_type: AutoModelForCausalLM
tokenizer_type: AutoTokenizer
trust_remote_code: true
# 2. どの『教科書』を読み聞かせるか?
datasets:
- path: /mnt/c/Users/akakage/kiraria-neon/neon_finetuning_dataset.jsonl
type: alpaca
# 3. どれくらい、教えるか? (QLoRAの設定)
load_in_4bit: true
adapter: qlora
lora_r: 8
lora_alpha: 16
lora_dropout: 0.05
lora_target_modules:
- q_proj
- k_proj
- v_proj
- o_proj
- gate_proj
- up_proj
- down_proj
# 4. トレーニングの設定
sequence_len: 2048
sample_packing: true
eval_sample_packing: false
pad_to_sequence_len: true
val_set_size: 0.05
micro_batch_size: 1
gradient_accumulation_steps: 8
num_epochs: 1
optimizer: paged_adamw_8bit
lr_scheduler: cosine
learning_rate: 2.0e-5
# 5. ハードウェアの性能を最大限に引き出すおまじない
bf16: true
fp16: false
tf32: true
gradient_checkpointing: true
# 6. 儀式の結果を、どこに保存するか?
output_dir: ./lora-out/gemma3_4b_it_neon_lora_final
この設定で、ついにLoRAのトレーニングが最後まで完走しました!
第3章:LoRAのGGUF形式への変身
axolotl
が出力したLoRAは、そのままでは llama.cpp
で使えません。llama.cpp
専用のGGUF形式に変換します。
-
問題:
llama.cpp
に付属のconvert_lora_to_gguf.py
を実行すると、ValueError: Can not map tensor 'model.vision_tower...'
というエラーが発生。 -
原因:
axolotl
はgemma-3
のテキスト部分と画像部分両方のLoRAを学習しましたが、変換スクリプトが画像部分のテンソルに対応していなかったため。 -
解決策:
convert_lora_to_gguf.py
をconvert_lora_to_gguf_text_only.py
としてコピーし、スクリプト自体を改造しました。get_tensors
メソッド内のループの先頭に、以下の処理を追加しました。この改造版スクリプトを実行することで、無事にテキスト部分だけのLoRAアダプターをGGUF形式で出力できました。# --- MODIFICATION START --- # Skip vision tower tensors if "vision_tower" in name: continue # --- MODIFICATION END ---
第4章:最終章 - 進化したAIの起動
全ての準備が整い、いよいよ llama.cpp
で、自作したLoRAを読み込みます。
-
問題:
failed to open GGUF file
エラー。 -
原因: LoRAアダプターのファイルパスが深すぎたためか、
llama-server
がファイルを正しく見つけられませんでした。 -
解決策:
- 完成したLoRAアダプター(
.gguf
ファイル)を、llama.cpp/models/
フォルダに移動させました。 -
llama-server.exe
の--lora
引数に、移動先へのパスを指定しました。
- 完成したLoRAアダプター(
# Windowsのコマンドプロンプトで、Releaseフォルダから実行
.\llama-server.exe -m "..\..\..\models\google_gemma-3-4b-it-Q4_K_M.gguf" --lora "..\..\..\models\Gemma3_4B_It_Neon_Lora_Final-F16-LoRA.gguf" -c 4096
このコマンドで、ついにサーバーはエラーを吐くことなく、ベースモデルと新しいLoRAアダプターを両方読み込んで起動に成功しました。
おわりに
gemma-3
のような新しいマルチモーダルモデルのファインチューニングは、多くの予期せぬエラーとの戦いでした。しかし、一つ一つのエラーメッセージを丁寧に読み解き、原因を特定し、適切な解決策を試していくことで、最終的に目標を達成することができました。
この長い旅路が、これから同じ道を通る誰かの助けになることを、心から願っています。
Discussion