LLMのファインチューニングは、LoRA・QLoRAのようなパラメータ効率的な手法を用いることで、単一のA100 80GB 1枚で70B級モデルまで調整可能になっています。重要なのは学習の技術よりもデータセットの質であり、「数千件の良質なデータ」は「数十万件の低品質なデータ」を簡単に上回ります。社内用語やトーン調整といった特定用途には非常に強力な一方、知識注入の目的では期待より効果が薄いことも多く、用途の見極めが成功と失敗の分水嶺です。

ファインチューニングとは

ファインチューニングとは、事前学習済みのLLMに対して、追加の学習データで再訓練を行い、特定のタスクやドメインに適応させる手法です。かつては「フルファインチューニング」が主流で、全パラメータを更新していました。70Bクラスのモデルでは数百GB級のGPUメモリが必要で、個人や中小企業には手が届かない世界でした。

この状況を一変させたのがPEFT(Parameter-Efficient Fine-Tuning)で、全パラメータの0.1〜1%程度だけを更新する手法の総称です。LoRA、QLoRA、Adapter、Prefix Tuning、P-Tuningなどが含まれ、メモリ要件を劇的に削減しつつ、フルファインチューニングに近い性能を出せることが実証されています。現代のLLMファインチューニングは、事実上PEFTが標準です。

【ファインチューニング手法の分類】

Full Fine-Tuning
  └── 全パラメータ更新
        └── 精度は最高だが GPU コスト膨大

PEFT (Parameter-Efficient Fine-Tuning)
  ├── LoRA
  │     └── 低ランク行列のみ学習
  ├── QLoRA
  │     └── 4bit 量子化 + LoRA
  ├── Adapter
  │     └── 層の間に小さな NN を挿入
  ├── Prefix Tuning
  │     └── 入力前の学習可能なプレフィックス
  └── P-Tuning v2
        └── 各層に学習可能なプロンプト

オープンソースLLM比較の記事で対象モデルを選定した後、本記事の手法で用途特化を進めるのが一般的な流れです。

LoRA・QLoRAの仕組み

LoRA(Low-Rank Adaptation)は、事前学習済みモデルの重み行列Wを凍結したまま、その差分ΔWを2つの低ランク行列AとBの積で近似する手法です。r=8程度の小さいランクで十分な性能が出ることが実験的に示されており、学習パラメータ数は元モデルの0.1〜1%に抑えられます。推論時は、LoRA重みを元のWにマージすれば、追加の計算コストはゼロに近くなります。

QLoRAはLoRAをさらに一歩進めた手法で、事前学習済みの重みを4bitに量子化した状態でメモリに載せ、その上でLoRAを学習します。「NF4」と呼ばれる正規分布に最適化された4bitフォーマットを使い、ダブル量子化と呼ばれる技術でメモリ効率をさらに向上させます。これにより、かつては8枚のA100が必要だった65Bモデルのファインチューニングを、単一のA100 48GB程度で可能にした革新的な手法です。

手法学習パラメータ数メモリ要件(70B)学習速度精度適したケース
Full Fine-Tuning100%A100×8+遅い最高大規模リソース・最高品質
LoRA (r=16)0.1%〜1%A100×2〜4速いほぼFFTと同等十分なGPU・高品質
QLoRA (4bit + r=16)0.1%〜1%A100×1やや遅いLoRAより0.5%程度低下限定リソース・実用品質
Adapter1%〜5%A100×2〜4速いやや低い研究用途中心
Prefix Tuning0.1%以下A100×1〜2速いタスク依存プロンプト拡張

多くの実務シナリオでは、QLoRAが費用対効果の点で最有力です。ただし、学習スピードはフル精度LoRAよりやや遅いため、GPUリソースに余裕があるならLoRA(BF16)を選ぶのが定石です。GPU基盤選定の記事も併せて参考にしてください。

実践ステップ

実際のファインチューニングは、データセット準備、トークナイザー設定、モデルロード、LoRA構成、学習ループ、評価、マージの順で進めます。HuggingFaceのecosystemを使うのが最も楽で、transformers、peft、trl、accelerateの4つのライブラリで大半のケースをカバーできます。

from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from trl import SFTTrainer, SFTConfig

model_id = "meta-llama/Llama-3.1-8B-Instruct"

# 4bit 量子化設定
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype="bfloat16",
    bnb_4bit_use_double_quant=True,
)

tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
    model_id, quantization_config=bnb_config, device_map="auto"
)
model = prepare_model_for_kbit_training(model)

# LoRA 設定
lora_config = LoraConfig(
    r=16, lora_alpha=32, lora_dropout=0.05, bias="none",
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
    task_type="CAUSAL_LM",
)

# データセット
dataset = load_dataset("json", data_files="train.jsonl", split="train")

training_args = SFTConfig(
    output_dir="./lora-output", num_train_epochs=3,
    per_device_train_batch_size=4, gradient_accumulation_steps=4,
    learning_rate=2e-4, bf16=True, logging_steps=10, save_steps=100,
)

trainer = SFTTrainer(
    model=model, tokenizer=tokenizer,
    train_dataset=dataset, peft_config=lora_config, args=training_args,
)
trainer.train()
trainer.save_model("./lora-output/final")

学習後は、`peft.PeftModel.from_pretrained`でLoRA重みをベースモデルに適用するか、`model.merge_and_unload()`でマージしたモデルを保存して、LLM推論基盤にデプロイします。

データセットの設計と品質管理

ファインチューニングの成否は、アルゴリズムよりもデータセットで8割決まります。「良いデータ」とは、目的の応答スタイルや知識を正確に反映した、ノイズの少ない例示の集まりです。指示追従タスクでは、ユーザー指示とアシスタント応答のペアをJSONLで用意するのが標準で、ChatML形式やLlama専用テンプレートでラップします。

データ量の目安は、タスクによって大きく異なります。分類タスクなら数百件から成果が見え始め、指示追従の調整なら数千件、複雑な会話エージェントの学習なら数万件が必要です。データを増やせば精度が上がるのは最初の数千件までで、そこから先は品質の方が効いてきます。

タスク種別最小データ数推奨データ数注意点
文書分類5002,000〜5,000クラス間バランス
固有表現抽出1,0005,000〜10,000表記ゆれの網羅
指示追従調整1,0005,000〜20,000多様な指示バリエーション
トーン・スタイル変換5002,000〜5,000一貫性のある例示
ドメイン対話2,00010,000〜50,000実務シナリオの網羅
コード生成3,00020,000〜テスト通過コードのみ

品質チェックの実務的なアプローチは、データを人手でランダムサンプリングして「自分が答えるとしてこれは正解か」を100件程度確認することです。明らかな誤りやバイアスを含む例は除去し、必要に応じてLLMで合成データを追加します。RAG vs ファインチューニングの判断も、データ準備のコストに大きく影響します。

ファインチューニングの評価と判断

学習が終わったら、必ずベースモデルとの比較評価を行います。目的タスクでの精度向上を確認するのはもちろん、一般的なベンチマーク(MMLU、GSM8K等)での大幅低下が起きていないかも確認すべきです。ファインチューニングは簡単に「壊滅的忘却(Catastrophic Forgetting)」を引き起こし、特定タスクで高精度になった代わりに、他のタスクが軒並み劣化することがあります。

import matplotlib.pyplot as plt
import pandas as pd

# TensorBoard ログから学習曲線を抽出
log_df = pd.read_csv("./lora-output/trainer_state.csv")

fig, ax = plt.subplots(1, 2, figsize=(12, 4))
ax[0].plot(log_df["step"], log_df["loss"], label="Train Loss")
ax[0].plot(log_df["step"], log_df["eval_loss"], label="Eval Loss")
ax[0].set_xlabel("Step"); ax[0].legend()

ax[1].plot(log_df["step"], log_df["learning_rate"])
ax[1].set_xlabel("Step"); ax[1].set_title("Learning Rate")

plt.savefig("training_curves.png")
print("Best eval_loss:", log_df["eval_loss"].min())

過学習の検知には、学習損失と評価損失の乖離を見ます。評価損失が2〜3エポック連続で上昇していれば、そこが学習を止めるべきポイントです。モデルレジストリに評価結果を記録しておくと、後続のモデルバージョンとの比較が楽になります。RLHF・DPOへの発展も視野に入れておきましょう。

まとめ

LoRAとQLoRAの登場により、LLMのファインチューニングは少数精鋭チームでも実践可能な技術になりました。成功の9割はデータセット設計で決まり、学習技術は意外とコモディティ化しています。最初はLoRAで小さく試し、評価指標で効果を確認してから本格運用へ進むのが鉄則です。「ファインチューニングすれば何でもできる」という過剰期待は、データ準備コストを見積もり始めた瞬間に現実と出会います。

よくある質問

Q. LoRAとは?

A. Low-Rank Adaptationの略で、モデルの全パラメータではなく低ランク行列のみを学習する手法です。メモリ使用量を大幅に削減しながら、フルファインチューニングに近い精度を実現できるのが特徴です。

Q. ファインチューニングに必要なデータ量は?

A. タスクによりますが、分類であれば数百件、指示追従であれば数千件が目安です。品質の高いデータ少量の方が、低品質データ大量より効果的であることが実証されています。

Q. RAGとファインチューニングはどちらを選ぶべきですか?

A. 社内知識の検索にはRAG、モデルの振る舞いやスタイルの変更にはファインチューニングが適しています。多くの場合、RAGを先に試してから、必要に応じてファインチューニングを組み合わせる順序が合理的です。