所要時間: 約40分 | 難易度: ★★★☆☆
この記事で作るもの
- ローカル環境(またはColab)のVRAM 8GBという制限下で、最新のGemma 4を自分の指示通りに動くよう再学習させるPythonスクリプト
- 独自の業務知識や特定の口調をモデルに学習させ、推論させるまでの全工程
- 学習済みモデルをGGUF形式で書き出し、普段使いのチャットツールで利用可能にする手順
📦 この記事に関連する商品
NVIDIA GeForce RTX 4060 Ti 16GB8GBでも動くが、16GBあれば長文の学習も余裕を持って行えるため最適解
※アフィリエイトリンクを含みます
前提知識として、Pythonの基本的な文法とTerminalの操作ができることを想定しています。 必要なものは、8GB以上のVRAMを搭載したNVIDIA製GPU(RTX 3060等)またはGoogle Colabの無料枠です。
なぜこの方法を選ぶのか
通常、LLMのファインチューニングには数十GBのVRAMを積んだA100などのプロフェッショナルGPUが必須でした。 標準的なHugging Faceのライブラリ(PEFT/TRL)でGemmaクラスのモデルを回そうとすると、8GBのVRAMでは瞬時にOut Of Memory(OOM)で落ちます。
今回紹介する「Unsloth」ライブラリは、手動で書き直されたTritonカーネルによって、メモリ消費量を従来の70%削減し、学習速度を2倍以上に引き上げています。 私自身、RTX 4090を2枚挿して検証していますが、この手法なら1枚の4090で複数のモデルを同時に回せるほど効率が良いです。 既存の bitsandbytes を使った4bit量子化よりも圧倒的に高速で、かつ精度劣化が抑えられているため、現時点ではこれが個人開発者にとってのベストプラクティスと言えます。
Step 1: 環境を整える
まずは必要なライブラリをインストールします。 UnslothはPyTorchのバージョンに非常に敏感なため、以下のコマンドを順番に実行してください。
# 仮想環境の作成を推奨します
python -m venv venv
source venv/bin/activate # Windowsの場合は venv\Scripts\activate
# CUDAバージョンに合わせたUnslothのインストール
# ここでは多くの環境で動作する最新の構成を指定します
pip install --no-deps "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git"
pip install --no-deps "xformers<0.0.27" "trl<0.9.0" peft accelerate bitsandbytes
Unslothは「特定のバージョン構成」で真価を発揮します。 xformersはメモリ効率化アテンションを動かすために必須で、これがないと8GBのVRAMには収まりません。
⚠️ 落とし穴:
Windows環境で bitsandbytes が動かない場合、pip install bitsandbytes-windows を試す人が多いですが、これは古い解決策です。
現在は本家 bitsandbytes がWindowsを公式サポートしているため、エラーが出る場合は nvcc --version を確認し、CUDA Toolkit 12.xが正しくパスに通っているかを確認してください。
Step 2: 基本の設定とモデルのロード
次に、Pythonスクリプトを作成します。 ここでは、4bit量子化されたGemma 4(または最新のGemmaシリーズ)を読み込み、LoRA(Low-Rank Adaptation)の設定を行います。
import os
import torch
from unsloth import FastLanguageModel
# 1. 設定値の定義
max_seq_length = 2048 # 8GB VRAMなら2048が安全圏。4096にするとOOMのリスクが高まります
dtype = None # GPUが対応していれば自動でbfloat16を選択します
load_in_4bit = True # 8GB環境では必須の設定です
# 2. モデルとトークナイザーのロード
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = "unsloth/gemma-4-2b-it-bnb-4bit", # 最新のGemmaを指定
max_seq_length = max_seq_length,
dtype = dtype,
load_in_4bit = load_in_4bit,
)
# 3. LoRA設定(重みの更新範囲を限定してメモリを節約する)
model = FastLanguageModel.get_peft_model(
model,
r = 16, # ランク。高いほど表現力が増すがメモリを食う。16がコスパ最強です
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj",],
lora_alpha = 16,
lora_dropout = 0, # 学習速度向上のため0を推奨
bias = "none", # 同上
use_gradient_checkpointing = "unsloth", # メモリ消費を極限まで抑える
random_state = 3407,
)
r=16 という設定にしているのは、私の過去20件以上の案件経験から、これが精度とメモリ負荷のバランスが最も良いためです。
もし特定の専門用語を覚えさせるだけなら r=8 でも十分機能します。
Step 3: 学習データの準備と実行
今回は「SIerのベテランエンジニア風の口調」を学習させてみます。 データセットは指示(Instruction)と回答(Output)のペアで構成します。
from datasets import Load_dataset
from trl import SFTTrainer
from transformers import TrainingArguments
# プロンプトフォーマットの定義
prompt_style = """Below is an instruction that describes a task. Write a response that appropriately completes the request.
### Instruction:
{}
### Response:
{}"""
def formatting_prompts_func(examples):
instructions = examples["instruction"]
outputs = examples["output"]
texts = []
for instruction, output in zip(instructions, outputs):
text = prompt_style.format(instruction, output)
texts.append(text)
return { "text" : texts, }
# ここでは例として小規模な自作データを想定
from datasets import Dataset
data = {
"instruction": ["このプロジェクトの進捗はどうですか?", "バグが見つかりました"],
"output": ["エビデンスは取れているのか?リスケの調整が必要だな。", "まずは切り戻しを検討しろ。原因分析はその後だ。"]
}
dataset = Dataset.from_dict(data)
dataset = dataset.map(formatting_prompts_func, batched = True)
# トレーナーの設定
trainer = SFTTrainer(
model = model,
tokenizer = tokenizer,
train_dataset = dataset,
dataset_text_field = "text",
max_seq_length = max_seq_length,
args = TrainingArguments(
per_device_train_batch_size = 2, # 8GBなら2が限界
gradient_accumulation_steps = 4, # 擬似的にバッチサイズを稼ぐ
warmup_steps = 5,
max_steps = 60, # 試行のため少なめに設定
learning_rate = 2e-4,
fp16 = not torch.cuda.is_bf16_supported(),
bf16 = torch.cuda.is_bf16_supported(),
logging_steps = 1,
optim = "adamw_8bit", # メモリ節約のため8bit Adamを使用
weight_decay = 0.01,
lr_scheduler_type = "linear",
seed = 3407,
output_dir = "outputs",
),
)
trainer.train()
期待される出力
学習が始まると、Terminalにロス率(Loss)が表示されます。
{'loss': 2.456, 'learning_rate': 4e-05, 'epoch': 0.1}
...
{'loss': 0.842, 'learning_rate': 0.0, 'epoch': 1.0}
ステップを追うごとにLossが下がっていけば、モデルがあなたの用意した「SIer構文」を学習している証拠です。
Step 4: 実用レベルにする(推論と保存)
学習したモデルを実際に動かしてみましょう。 ここでは推論モードに切り替えて、モデルの回答を確認します。
# 推論用に高速化
FastLanguageModel.for_inference(model)
inputs = tokenizer(
[
prompt_style.format(
"本番環境でエラーが出ました。どうすればいいですか?", # 指示
"", # 回答欄は空
)
], return_tensors = "pt").to("cuda")
outputs = model.generate(**inputs, max_new_tokens = 64)
print(tokenizer.batch_decode(outputs))
# GGUF形式で保存してLM Studio等で使えるようにする
model.save_pretrained_gguf("model_gguf", tokenizer, quantization_method = "q4_k_m")
この save_pretrained_gguf が非常に強力です。
通常、LoRAをマージして、llama.cppのリポジトリをクローンして、複雑なコマンドを打って変換する手間が必要ですが、Unslothなら1行で終わります。
これで、自分が育てたモデルをスマホや軽量なチャットUIで持ち運べるようになります。
よくあるトラブルと解決法
| エラー内容 | 原因 | 解決策 |
|---|---|---|
| CUDA Out of Memory | max_seq_length または batch_size が大きすぎる | max_seq_length=1024 に下げ、batch_sizeを1にする |
AttributeError: 'NoneType' object has no attribute 'get' | PyTorchとUnslothのバージョン不整合 | pip install --upgrade unsloth で最新にする |
| 学習が進んでも回答が変わらない | データセットの形式がプロンプトと一致していない | formatting_prompts_func 内の改行やタグを厳密にチェックする |
次のステップ
無事にモデルが動いたら、次はデータセットの「質」にこだわってみてください。 LLMの性能は、モデルのサイズよりも「学習データの清潔さ」に依存します。 具体的には、100件のノイズ混じりデータよりも、20件の完璧な回答例の方が良い結果を生むことが多いです。
また、今回は LoRA を使いましたが、Unslothはさらに効率的な「DPO (Direct Preference Optimization)」にも対応しています。 これは「Aという回答よりもBという回答の方が好ましい」という比較データを学習させる手法で、モデルの毒性を抜いたり、より人間に近い回答をさせたりする際に使われます。 私のブログでも DPO の実戦投入ガイドを公開予定ですので、チェックしてみてください。
よくある質問
Q1: 8GB VRAMのノートPCでも本当に動きますか?
はい、実際にRTX 3060 Laptop (6GB/8GB) での動作報告が多数あります。ただし、ブラウザや他のアプリがVRAMを消費しているとOOMになるため、学習中は極力他の作業を控えるのがコツです。
Q2: 独自のPDFファイルを読み込ませて学習できますか?
PDFから直接はできません。一度テキスト化し、上記のような「問い」と「答え」の形式に整形する必要があります。RAG(検索拡張生成)と混同されがちですが、ファインチューニングは「型」や「知識の定着」に向いています。
Q3: 学習させたモデルの商用利用は可能ですか?
Gemma自体のライセンスに従います。GoogleのGemma商用利用規約を確認してください。基本的には、月間アクティブユーザー数が数億人規模にならない限り、多くのビジネスで無償利用が可能です。






