Python LLM ファインチューニング深掘り(2026):QLoRA 全工程

AI与大数据本番環境の AI Agent(更新: 2026年7月20日)

クイックプレビュー

7B モデルの全パラメータ微調には A100 4 枚が必要——大多数のチームには非現実的。QLoRA は 4-bit 量子化・低ランク適応・ページ化オプティマイザを組み合わせ、単一の民生用 GPUで大規模モデルを微調可能にする。

本記事は NF4 量子化理論から PEFT 設定、TRL 学習、vLLM 展開までの再現可能な全工程を提供する。全ステップにコード・データ・注意点付き。


深層原理:LoRA と QLoRA の実際

LoRA:ベース凍結、バイパスのみ学習

LoRA は事前学習済み重み W を変更しない。各注意層の脇に 2 つの学習可能な低ランク行列 AB を挿入する:

h = W·x + (B·A)·x
    ^^^^   ^^^^^^^^^
    凍結    学習可能(r << d、通常 8–64)

逆伝播時は AB のみが非ゼロ勾配を持つ——W は常にゼロ(凍結)。最適化状態は AB だけ追跡すればよく W 全体は不要。これがメモリ効率の根本理由。

初期化テクニックA を Kaiming/Xavier 正規分布で、B を全ゼロで初期化。学習開始時 BA=0 で、モデルの初期出力は元の事前学習モデルと完全一致——学習安定性を保証する。

7B モデルの場合、学習可能パラメータは全体の 0.1%–0.5% に抑えられる。

QLoRA:ベースも圧縮

QLoRA は凍結ベース重み W4-bit NormalFloat(NF4) に量子化する。

NF4 が INT4 に勝つ理由:INT4 は均等間隔の均一量子化で、重みが一様分布すると仮定する。しかし実際の事前学習済み重みはゼロ平均正規分布に近似——平均付近に集中し、両端は疎。NF4 は情報理論的に最適な非均一量子化であり、確率密度が高い領域により多くの量子化レベルを、裾野には少なく割り当てる。実験では INT4 比で情報損失が約 1/2–1/3。

二重量子化:スケーリング係数(FP32)自体を FP8 に再量子化し、約 0.4 bit/パラメータ追加節約。

ページ化オプティマイザ:VRAM 逼迫時に optimizer state を固定サイズのページに分割し、CPU-GPU 間で自動ページング——OS の仮想メモリ機構に類似。

VRAM 計算

Llama 3.1-8B、BF16 約 16 GB:

構成要素 全パラメータ QLoRA
モデル重み 16 GB (BF16) ~3.8 GB (NF4)
Optimizer (AdamW) 32 GB (m+v) ~0.08 GB
勾配 16 GB ~0.08 GB
活性値 (batch=4,seq=2048) ~12 GB ~12 GB
合計 ~76 GB ~16 GB

環境構築

pip install torch transformers accelerate peft bitsandbytes trl datasets tensorboard

バージョン注意:bitsandbytes は CUDA バージョンと一致させる。trl 0.9+ は SFTConfig 使用。Windows は WSL2 推奨。


4-bit 量子化モデル読込

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True, bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16, bnb_4bit_use_double_quant=True,
)

model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.1-8B",
    quantization_config=bnb_config, device_map="auto", torch_dtype=torch.bfloat16,
)
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B")
tokenizer.pad_token = tokenizer.eos_token

LoRA 設定

from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training

model = prepare_model_for_kbit_training(model)

lora_config = LoraConfig(
    r=16, lora_alpha=32, lora_dropout=0.05,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                    "gate_proj", "up_proj", "down_proj"],
    bias="none", task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora_config)
# trainable: ~42M / ~8B = 0.52%

ハイパーパラメータガイド

パラメータ 推奨 根拠
r 8–32 ランク。r=8 で ΔW を 8 基底ベクトルで表現。多くのタスクで十分。32 超で収穫逓減。
lora_alpha r×1–2 実効学習率 ∝ alpha/r。r=16, alpha=32 でスケーリング 2.0。
lora_dropout 0.05 正則化。小データ(<500)→0.1。大データ→0。
target_modules 多いほど良 Llama: q/k/v/o+gate/up/down。他アーキテクチャは state_dict 確認。

データセット準備

from datasets import load_dataset

dataset = load_dataset("tatsu-lab/alpaca")

def format_instruction(example):
    prompt = f"### Instruction:\n{example['instruction']}\n\n### Response:\n{example['output']}"
    return {"text": prompt}

dataset = dataset.map(format_instruction)

💡 200–500 件で検証 → loss 低下確認 → 2k–5k で効果実感 → 10k+ で競争力。品質 >> 量


学習:TRL SFTTrainer

from trl import SFTTrainer, SFTConfig

training_args = SFTConfig(
    output_dir="./qlora-llama3-chat",
    per_device_train_batch_size=4, gradient_accumulation_steps=4,
    num_train_epochs=3, max_seq_length=2048,
    logging_steps=10, save_steps=200,
    learning_rate=2e-4, lr_scheduler_type="cosine", warmup_ratio=0.03,
    optim="paged_adamw_8bit", bf16=True, gradient_checkpointing=True,
    report_to="tensorboard",
)

trainer = SFTTrainer(model=model, args=training_args,
                     train_dataset=dataset["train"], tokenizer=tokenizer)
trainer.train()

tensorboard --logdir ./qlora-llama3-chat で監視:loss(0.5–1.5 に安定低下)、grad_norm(0.1–1.0 が理想)、GPU 利用率(100% 付近)。


評価

from peft import PeftModel

base = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.1-8B", torch_dtype=torch.bfloat16, device_map="auto"
)
base = PeftModel.from_pretrained(base, "./qlora-llama3-chat/checkpoint-600")
base = base.merge_and_unload()

def generate(prompt, max_new=256):
    inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
    outputs = base.generate(**inputs, max_new_tokens=max_new, temperature=0.7)
    return tokenizer.decode(outputs[0], skip_special_tokens=True)

定量的には GPT-4 を judge に、正確性・完全性・有用性を 1–5 評価。平均 > 4.0 で合格。


保存と展開

trainer.save_model("./qlora-llama3-chat-final")  # 約 60 MB、アダプタのみ

model = PeftModel.from_pretrained(base, "./qlora-llama3-chat-final")
model = model.merge_and_unload()
model.save_pretrained("./merged-model")

vLLM

python -m vllm.entrypoints.openai.api_server \
  --model ./merged-model --dtype bfloat16 --max-model-len 4096 --port 8000

FAQ

Q1:QLoRA と全パラメータの品質差?——多くのタスクで 96–99%。極端な場合のみ全パラメータ必要。

Q2:r=8 vs r=64?——中規模データで r=8 十分。r に計算資源を浪費する前にデータ品質を。

Q3:複数アダプタ切替?——model.set_adapter() ミリ秒切替、ベース再ロード不要。

Q4:loss 低いが回答悪い?——プロンプトテンプレート過適合。多様性追加・epoch 減・dropout 増。

Q5:CPU 学習?——極めて遅い。Colab 無料 T4 / Kaggle 無料 P100 推奨。


結論

推奨パス:200 件検証 → TensorBoard 調整 → 手動テスト → データ追加(r ではない) → merge → vLLM 展開。

#LLM#微调#QLoRA#LoRA#PEFT#量化