この記事のゴール
Llama を本番投入する全体像で述べたとおり、オープンウェイトを選ぶ最大の理由のひとつが「重みを自社データで微調整(ファインチューニング, FT)できる」ことです。本稿はその FT を、デモではなく本番——再現性・評価・コスト・ライセンスまで——の観点で、実コードで最後まで通します。
読み終えたとき、次の3つができる状態を目指します。
- そもそも FT が必要かを判断できる(多くの場合は RAG で足りる、を含めて)。
- LoRA/QLoRA で実際に回せる(torchtune と Hugging Face TRL の両経路)。
- 評価ゲートを通してから安全にデプロイでき、ライセンスの命名規約まで守れる。
信頼性の開示:私は AWS Bedrock / Vercel AI SDK を土台に生成AIシステムを本番運用しており、ドメイン特化(型番・専門語彙・出力フォーマットの固定)は音声接客の事例でも中心的な課題でした。本稿は「FT は最後の手段」という実務の順序に忠実です。盛った精度向上の数字は出しません。
まず疑う:その課題、本当にFTが要りますか?
FT は強力ですが、最初に手を出すべき道具ではありません。コスト・運用負荷・陳腐化リスクが大きいからです。次の順で検討してください。
| 手法 | 効くもの | 効かないもの | コスト/運用 |
|---|---|---|---|
| プロンプト/Few-shot | 形式の指示、簡単な振る舞い | 大量の知識、強い様式固定 | 最小 |
| RAG(検索拡張) | 最新知識・社内文書の参照、出典提示 | 口調・出力様式の根本変更 | 中(pgvector RAG) |
| ファインチューニング | 振る舞い・出力形式・口調・専門語彙の固定、レイテンシ短縮 | 知識の鮮度(学習時点で凍結) | 大(データ・学習・評価・再学習) |
鉄則:
- 知識を足したい → まず RAG。モデルの中に事実を焼き込むのは陳腐化と幻覚の温床。
- いつも同じ形式・口調・判断で答えさせたい、プロンプトが長くなりすぎた、専門語彙の取りこぼしを消したい → FT の出番。
- 実務では RAG + 軽い FT の併用が最も費用対効果が高い(知識は RAG、様式は FT)。
💡 「FT すれば賢くなる」は誤解です。FT が変えるのは主に振る舞いの分布であって、新しい事実の信頼できる記憶ではありません。事実は RAG に持たせるのが本番の定石です。
何を、どう微調整するか(現実的な対象とLoRA/QLoRA)
対象モデル:まず密モデルから
Llama 4 は MoEで総パラメータが巨大(Scout 109B / Maverick 400B)です。これをフルFTするのは相当な分散学習基盤を要し、最初の一歩には向きません。実務の現実的な対象は密(dense)モデル——Llama 3.3 8B / 70B や用途特化の小型版——で、ここに LoRA/QLoRA を当てるのが王道です。
LoRA と QLoRA を一言で
- LoRA(Low-Rank Adaptation):巨大な重みを凍結し、小さな低ランク行列だけを学習する。更新対象がごく一部なので、省メモリ・高速・成果物(アダプタ)が数十MB。
- QLoRA:ベースモデルを 4bit 量子化して載せ、その上で LoRA を学習する。単一GPUでも 70B 級に手が届くのが利点。精度劣化は実用上小さいことが多い。
| 手法 | メモリ | 速度 | 成果物 | 向く場面 |
|---|---|---|---|---|
| フルFT | 最大 | 最遅 | モデル全体 | 大規模・基盤を作り替える |
| LoRA | 小 | 速 | アダプタ(小) | 標準。様式・語彙の固定 |
| QLoRA | 最小 | 速 | アダプタ(小) | 単一GPUで大きめモデル |
データ準備:成否の9割はここ
FT の品質はデータの品質で決まります。モデルやハイパラの差は誤差です。
- 形式:instruction/chat 形式の JSONL。1行=1サンプル。
- 品質 > 量:ノイズだらけの10万件より、手で磨いた1,000件。誤りは“正しく学習されてしまう”。
- 重複排除:near-dup を除く。同じ例の重複は過学習を招く。
- train/eval 分割:必ず評価用を切り分ける。これを混ぜると「自分の宿題を採点」する状態(リーク)になり、精度を錯覚する。
- 多様性:本番で来る入力分布をカバーする。エッジケース・断り方(「分かりません」)も学習対象に入れる。
{"messages":[
{"role":"system","content":"あなたは当社の見積もりアシスタント。数値は推測せず、無い情報は『不明』と返す。"},
{"role":"user","content":"型番 TX-200 の標準納期は?"},
{"role":"assistant","content":"TX-200 の標準納期は5営業日です。在庫状況により前後します。"}
]}
⚠️ リークは“静かに”精度を盛る最悪のバグ。評価セットの一部が学習に混ざると、ベンチは上がるのに本番で滑ります。分割は最初に、決定的に(ハッシュで振り分ける等)行ってください。
実装A:torchtune(PyTorchネイティブ・公式)
Meta/PyTorch 公式の torchtune は、「トレーナも抽象も挟まない、素のPyTorch」が思想です。YAML で recipe を設定し、CLI で回します。
pip install torchtune
# 重みを取得(ライセンス同意済みのHFアカウントが必要)
tune download meta-llama/Llama-3.3-70B-Instruct \
--output-dir ./Llama-3.3-70B-Instruct
# LoRA で単一デバイス学習(recipe と config を指定するだけ)
tune run lora_finetune_single_device \
--config llama3_3/70B_lora_single_device \
dataset.source=json \
dataset.data_files=./data/train.jsonl
torchtune は config に学習率・LoRA rank・エポック・量子化などが宣言的にまとまっており、差分管理(誰がどの設定で回したか)が効きます。これは再現性=本番運用の生命線です。
実装B:Hugging Face TRL + PEFT(QLoRA)
最も普及している経路。TRL の SFTTrainer に PEFT の LoraConfig を渡すだけで QLoRA になります。
# pip install trl peft transformers bitsandbytes datasets
import torch
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig
from trl import SFTTrainer, SFTConfig
MODEL_ID = "meta-llama/Llama-3.3-70B-Instruct" # 密モデルが現実的な対象
# QLoRA:ベースを 4bit(NF4) で載せる=単一GPUでも大きめモデルに届く
bnb = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
)
model = AutoModelForCausalLM.from_pretrained(MODEL_ID, quantization_config=bnb, device_map="auto")
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
# 学習するのは低ランクのアダプタだけ。注意機構の射影行列を対象にするのが定番。
lora = LoraConfig(
r=16, lora_alpha=32, lora_dropout=0.05,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
task_type="CAUSAL_LM",
)
dataset = load_dataset("json", data_files={"train": "data/train.jsonl"})["train"]
trainer = SFTTrainer(
model=model,
train_dataset=dataset,
peft_config=lora,
args=SFTConfig(
output_dir="out/llama-domain-lora",
num_train_epochs=2, # 入れすぎは過学習。1〜3で様子を見る
per_device_train_batch_size=1,
gradient_accumulation_steps=8, # 実効バッチを稼ぐ
learning_rate=2e-4,
bf16=True,
logging_steps=10,
save_strategy="epoch",
),
)
trainer.train()
trainer.save_model("out/llama-domain-lora") # 成果物はアダプタ(小さい)
ポイントは 学習するのがアダプタだけなこと。ベース 70B は凍結・4bit のまま動くので、必要VRAMが現実的な水準に収まります。
実装C:マネージド(運用を AWS に寄せる)
学習基盤を持ちたくないなら AWS にマネージドで寄せます。
- Bedrock のマネージドFT:本稿執筆時点で Llama 2 / Llama 3.2(Vision 含む)等が対象。学習データを S3 に置き、コンソール/APIでジョブを回すとカスタムモデルが払い出される。GPU 運用ゼロ。
- SageMaker + Bedrock Custom Model Import:torchtune/TRL で作った重み(Llama 2/3/3.1/3.2 系アーキ)を Bedrock に取り込み、既存の Converse API でそのまま叩く。
📌 正確性のための注記:マネージドFTの対応モデル・リージョンは更新されます。最新は Bedrock のモデルカスタマイズ対応表 を必ず確認してください。「やりたいモデルがマネージドFTの対象か」を先に確かめるのが事故防止になります。
評価ゲート:これ無しに本番へ出さない
FT で最も危険なのは「なんとなく良くなった気がする」で出すことです。ホールドアウトでFT前後を機械比較し、回帰を検出してから初めてデプロイします。
# evaluate.py — ベース vs FT後 を同じ評価セットで比較し、合否を判定する
import json, statistics
from typing import Callable
def run_eval(generate: Callable[[str], str], eval_path: str) -> float:
"""各サンプルを採点し平均スコアを返す。採点は完全一致/数値一致/LLM-judge等、用途で選ぶ。"""
scores = []
with open(eval_path) as f:
for line in f:
ex = json.loads(line)
out = generate(ex["input"])
scores.append(score(out, ex["expected"])) # 0.0〜1.0
return statistics.mean(scores)
base = run_eval(generate_base, "data/eval.jsonl")
tuned = run_eval(generate_tuned, "data/eval.jsonl")
# 改善が閾値未満、または既存能力の回帰があれば“不合格”としてデプロイを止める。
assert tuned >= base + 0.03, f"改善不足: base={base:.3f} tuned={tuned:.3f}"
print(f"PASS base={base:.3f} -> tuned={tuned:.3f}")
評価は用途に合わせた採点関数が肝です。構造化出力なら完全一致/スキーマ準拠率、要約なら LLM-judge、分類なら F1。「本番で何が正解か」を数値化できて初めて、FT は工学になります。
デプロイとライセンス(命名規約に注意)
- アダプタをマージ:LoRA アダプタをベースに統合し、単一の重みにする(推論を軽くするため)。
- サーブ:vLLM で自前サーブするか、Bedrock Custom Model Import で Converse から叩く。
- コストを見積もる:自前運用は推論コストの設計で損益分岐を出してから。
⚖️ ライセンス必読:Llama Community License では、Llama を使って作った派生モデルを配布・提供する場合、モデル名の先頭に「Llama」を付けることが求められます(例:
Llama-YourCompany-Support-8B)。さらにBuilt with Llama表示が必要です。社内利用のみなら配布には当たりませんが、外部提供する瞬間に命名・表示義務が発生します。詳細はピラー記事のライセンス章を参照してください。
よくある落とし穴
- 破滅的忘却(catastrophic forgetting):特化に振りすぎて、元の汎用能力が落ちる。汎用タスクも評価セットに混ぜて回帰を監視する。
- 過学習:エポック過多・データ過少で、評価が悪化する。早期に eval を見て止める。
- データリーク:評価が学習に混入。分割を最初に決定的に。
- 小さすぎるデータ:数十件で様式は変わらない。数百〜数千の良質例を狙う。
- RAGで足りたのにFTした:知識追加は RAG が基本。FTで事実を焼くと陳腐化する。
よくある質問(FAQ)
Q. RAG とファインチューニング、どちらを先にやるべき? A. RAG が先です。知識・鮮度は RAG、振る舞い・様式は FT。多くの案件は RAG+プロンプトで要件を満たし、足りない様式固定だけ軽い LoRA を足す、が費用対効果の最適点です。
Q. どれくらいのデータが必要? A. 様式・口調の固定なら数百〜数千の良質サンプルが目安。量より質。手で磨いた少数が、雑多な大量に勝ちます。
Q. Llama 4(MoE)も微調整できる? A. 技術的には可能ですが、巨大MoEのFTは分散基盤が要り最初の一歩には不向き。まず Llama 3.3 8B/70B などの密モデル+LoRA/QLoRA から始めるのが現実的です。
Q. GPUが無くてもできる? A. できます。Bedrock のマネージドFT(対象モデルは要確認)なら GPU 運用ゼロ。検証だけなら小型モデルを Colab/単一GPUの QLoRA で回せます。
Q. 成果物(アダプタ)はどれくらいの大きさ? A. LoRA アダプタは数十MB級。ベースとは別に配布・差し替えでき、複数ドメイン用のアダプタを使い分ける運用も可能です。
まとめ
ファインチューニングは「賢くする魔法」ではなく、「振る舞いを設計どおりに固定する工学」です。だからこそ、RAGで足りないかを先に疑い、データ品質に投資し、評価ゲートを通し、ライセンス命名を守る——この規律が、デモと本番を分けます。
ドメイン特化した Llama を、RAG との役割分担・評価ハーネス・コスト設計・ライセンス対応まで含めて本番に載せたいなら、実績をご覧のうえご相談ください。一人 × 生成AIで、PoC から本番運用まで一気通貫で設計します。
出典・公式リソース
- torchtune(meta-pytorch/torchtune) — PyTorchネイティブの LoRA/QLoRA recipe
- Llama 公式 Fine-tuning ガイド
- Hugging Face TRL / PEFT
- Amazon Bedrock モデルカスタマイズ対応表
- Llama Community License — 派生モデルの命名規約・Built with Llama
※ 対応モデル・リージョン・ライセンスは更新されます。実装前に必ず一次情報を確認してください。