結論:QLoRAを使えば、RTX 4090などコンシューマGPU1枚(VRAM 12GB以上)で7Bパラメータのオープンモデルを業務ドメインに特化させ、Ollamaを介してLangChainエージェントに統合できる。
- 要点1:QLoRAの4bit量子化によりVRAM使用量を標準LoRAの約50〜60%に削減。RTX 4090(24GB)で7B〜13Bモデルのファインチューニングが実現可能(Unsloth公式ドキュメント、2026年6月時点)。
- 要点2:Unsloth + PEFT + bitsandbytes の組み合わせが2026年現在の標準スタック。Unslothは通常のPEFT QLoRAと比較してVRAM使用量を30〜50%削減、学習速度を最大2倍向上させる。
- 要点3:GGUF形式でエクスポートしてOllamaでserveし、LangChainのChatOllamaクラスからエージェントとして呼び出す3ステップが現時点で最も低コストなエンドツーエンドパイプライン。
対象読者:Pythonの基礎知識があり、業務特化LLMやドメイン適応エージェントを自社構築したい開発者・AI担当者
今日やること:UnslothをColab or ローカル環境にインストールし、サンプルデータセット10件でQLoRAの動作確認まで完了させる(本記事の「Step 1」を実行)
「汎用LLMをそのまま使うと、社内専門用語や業務フローを理解してくれない…」
複数のAIエージェント導入プロジェクトを支援する中で、最も多く聞かれる悩みです。RAGで社内文書を参照させても、モデル自体の語彙や推論パターンが業務ドメインに合っていないと、応答の精度に天井があります。
そこで注目されているのが、LoRA(Low-Rank Adaptation)/ QLoRA(Quantized LoRA)によるドメイン特化ファインチューニングです。特にQLoRAは4bit量子化を活用することで、数万円クラスのコンシューマGPU1枚でも実用的なファインチューニングが可能になりました。本記事では、Unsloth + PEFT + bitsandbytesによるQLoRAセットアップから、GGUF変換→Ollama serve→LangChainエージェント統合までのエンドツーエンド実装をPythonコードつきで解説します。
なお、Sakana AIが発表したDoc-to-LoRAのような「論文から自動でLoRAを生成する」アプローチについては別記事で詳しく解説しています。本記事では業務データを使った手動ファインチューニングの実装にフォーカスします。
LoRA / QLoRAとは何か:仕組みを5分で理解する
LoRAは2021年にMicrosoft Researchが発表したパラメータ効率的ファインチューニング(PEFT)手法です。フルファインチューニングがモデルの全パラメータを更新するのに対し、LoRAは重み行列に低ランク分解の小さな行列を追加し、そこだけを学習させます。
通常のLoRAとQLoRAの違い
LoRAはFP16(半精度)でベースモデルを保持しながら、低ランク行列(アダプター)だけを学習します。QLoRAはさらに一歩進み、ベースモデル自体を4bit量子化して保持することでVRAMを大幅に削減します。
| 手法 | ベースモデルの精度 | 7B必要VRAM目安 | 精度への影響 |
|---|---|---|---|
| フルファインチューニング | FP32/BF16 | 100〜120GB | なし |
| LoRA | FP16 | 16〜20GB | 軽微 |
| QLoRA(4bit NF4) | 4bit量子化 | 8〜12GB | 軽微(NF4は性能劣化が小さい) |
上表のVRAM数値はUnslothおよびllmhardware.ioの公開情報をもとにした目安値です(2026年6月時点)。実際のVRAM使用量はバッチサイズ、シーケンス長、LoRAランクによって変動します。本番環境で使用する前に、必ずテスト環境で動作確認してください。
【コスト比較】LoRA vs RAG vs プロンプトエンジニアリング:いつ何を選ぶか
RAG・ファインチューニング・プロンプトエンジニアリングの使い分けガイドでも詳述していますが、ここでは実装コストと適合場面を整理します。
| アプローチ | 初期構築コスト | ランニングコスト | 適合場面 | 苦手場面 |
|---|---|---|---|---|
| プロンプトエンジニアリング | 無料〜数万円 | APIトークン代のみ | 汎用タスク・PoC | 特定語彙・独自フォーマット固定 |
| RAG | 数万円〜数十万円 | API代+ベクトルDB | 知識が変わる・大量文書参照 | 推論パターン・文体の統一 |
| QLoRA(自前GPU) | 約1万円〜(A100クラウド換算で約数万円/回) | Ollamaローカルなら実質ゼロ | 専門語彙・業務フロー固定・オフライン運用 | 知識の頻繁な更新 |
| フルファインチューニング | 数十万円〜数百万円(H100複数枚) | インフラ代 | 大規模ドメイン適応 | 中小規模の業務エージェント |
2026年現在、「知識の更新はRAG、推論パターン・語彙の統一はQLoRA」というハイブリッド構成が多くのプロジェクトで採用されています(BigData Boutiqueの分析、2026年)。RAGとQLoRAは競合ではなく組み合わせることが最適な場合が多いです。
環境構築:Unsloth + PEFT + bitsandbytesのインストール
検証環境でのセットアップ手順を紹介します。
動作環境
- Python 3.10以上(bitsandbytes要件)
- PyTorch 2.3以上
- CUDA 11.8〜13.0対応NVIDIAのGPU(RTX 4090推奨)
- bitsandbytes 0.49.2以上(2026年6月時点の最新安定版)
# Unsloth + PEFT + bitsandbytes をインストール
pip install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git"
pip install --no-deps trl peft accelerate bitsandbytes
# バージョン確認
python -c "import unsloth; print(unsloth.__version__)"
python -c "import bitsandbytes as bnb; print(bnb.__version__)"
Unslothは通常のPEFT QLoRAと比べてVRAM使用量を30〜50%削減し、学習速度を最大2倍向上させます(Unsloth公式ドキュメント、2026年6月時点)。ColabのT4(16GB)でも7Bモデルのファインチューニングが可能です。
Step 1:QLoRAでベースモデルをロードする
Llama-3.1-8B-Instructを例に、4bit量子化でロードするコードを示します。
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。
# 動作環境: Python 3.11+, unsloth>=2025.6, CUDA 12.x, VRAM 12GB以上
from unsloth import FastLanguageModel
import torch
max_seq_length = 2048 # モデルの最大コンテキスト長
dtype = None # Noneで自動判定(A100/H100はbfloat16、RTX系はfloat16)
load_in_4bit = True # QLoRA: 4bit量子化を有効化
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="unsloth/Meta-Llama-3.1-8B-Instruct", # HuggingFaceのモデルID
max_seq_length=max_seq_length,
dtype=dtype,
load_in_4bit=load_in_4bit,
# token="hf_xxxx", # プライベートモデルの場合はトークンを設定
)
print(f"モデルロード完了。使用デバイス: {next(model.parameters()).device}")
LoRAアダプターを追加する
model = FastLanguageModel.get_peft_model(
model,
r=16, # LoRAランク(高いほど学習パラメータ増加、典型値:8〜64)
target_modules=[ # LoRAを適用するモジュール名
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj",
],
lora_alpha=16, # スケーリング係数(通常はrと同じ値)
lora_dropout=0.05, # ドロップアウト率(過学習防止)
bias="none",
use_gradient_checkpointing="unsloth", # VRAMをさらに節約
random_state=42,
use_rslora=False, # Rank Stabilized LoRA(大きなrを使う場合に検討)
)
# 学習可能パラメータ数を確認
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
all_params = sum(p.numel() for p in model.parameters())
print(f"学習対象パラメータ: {trainable_params:,} / 全体: {all_params:,}")
print(f"学習パラメータ比率: {100 * trainable_params / all_params:.2f}%")
LoRAランク(r)の典型的な設定は業務タスクの複雑さに依存します。単純な語彙適応ならr=8、複雑な推論パターンの変更にはr=32〜64が目安です。
Step 2:業務データセットを作成してファインチューニングする
データセット形式の準備
ファインチューニングには「インストラクション形式」が一般的です。最低でも100件、実用的な品質には500〜1,000件以上のサンプルを推奨します。
from datasets import Dataset
# 業務特化データセットの例(カスタマーサポート向け)
# 実際には社内ナレッジベース・FAQ・対話ログから生成する
raw_data = [
{
"instruction": "製品Aの返品手続きを教えてください",
"input": "",
"output": "製品Aの返品は購入から30日以内にお申し込みいただけます。手順:1. マイページ>注文履歴から対象注文を選択 2. 「返品申請」ボタンをクリック 3. 理由を選択して送信 配送料は当社負担です。",
},
{
"instruction": "請求書の再発行はできますか",
"input": "",
"output": "はい、請求書の再発行が可能です。経理担当(accounting@example.com)に「請求書再発行希望」の件名でご連絡ください。注文番号と請求月をお知らせいただければ、3営業日以内に対応いたします。",
},
# ... 実際には数百〜数千件のサンプルを用意する
]
# Alpaca形式のプロンプトテンプレート
alpaca_prompt = """以下は業務タスクを説明する指示文です。要求に適切に応答してください。
### 指示:
{instruction}
### 入力:
{input}
### 応答:
{output}"""
def format_sample(sample):
return {
"text": alpaca_prompt.format(
instruction=sample["instruction"],
input=sample["input"],
output=sample["output"],
) + tokenizer.eos_token # EOSトークンで応答終了を明示
}
dataset = Dataset.from_list([format_sample(s) for s in raw_data])
print(f"データセット件数: {len(dataset)}")
SFTTrainerでファインチューニングを実行する
from trl import SFTTrainer
from transformers import TrainingArguments
from unsloth import is_bfloat16_supported
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=dataset,
dataset_text_field="text",
max_seq_length=max_seq_length,
dataset_num_proc=2,
packing=False, # 短いサンプルを詰め込む(高速化); 短文データセットではTrueも検討
args=TrainingArguments(
per_device_train_batch_size=2,
gradient_accumulation_steps=4, # 実効バッチサイズ = 2 × 4 = 8
warmup_steps=5,
num_train_epochs=3, # エポック数(小さいデータは3〜5が目安)
learning_rate=2e-4,
fp16=not is_bfloat16_supported(),
bf16=is_bfloat16_supported(),
logging_steps=1,
optim="adamw_8bit", # 8bit AdamWでVRAMを節約
weight_decay=0.01,
lr_scheduler_type="linear",
seed=42,
output_dir="outputs",
),
)
# 学習開始
trainer_stats = trainer.train()
print(f"学習完了。Loss: {trainer_stats.training_loss:.4f}")
Step 3:GGUF形式でエクスポートしてOllamaで動かす
学習済みアダプターをGGUF形式に変換してOllamaで配信することで、Ollama×ローカルLLMガイドで解説したOllamaベースのエージェントと同じ手順でカスタムモデルを呼び出せます。
GGUFエクスポート
# Q4_K_M量子化でGGUFエクスポート(品質とサイズのバランスが良い)
model.save_pretrained_gguf(
"custom-agent-model", # 保存先ディレクトリ名
tokenizer,
quantization_method="q4_k_m", # 他: q8_0(高品質), q5_k_m, q2_k(軽量)
)
print("GGUFエクスポート完了: ./custom-agent-model/*.gguf")
OllamaのModelfileを作成してインポートする
# Modelfileを作成(チャットテンプレートはファインチューニング時と同一のものを使う)
cat > Modelfile <<'EOF'
FROM ./custom-agent-model/unsloth.Q4_K_M.gguf
PARAMETER temperature 0.1
PARAMETER stop "<|eot_id|>"
PARAMETER stop "<|end_of_text|>"
SYSTEM "あなたは社内業務を支援するAIアシスタントです。正確な情報を日本語で提供してください。"
EOF
# Ollamaにインポート
ollama create business-agent -f Modelfile
# 動作確認
ollama run business-agent "製品Aの返品手続きを教えてください"
OllamaへのGGUFインポートについてはUnsloth公式ドキュメントに詳細な手順が掲載されています(参照日: 2026-06-11)。
Step 4:LangChainエージェントに統合する
OllamaでserveしたカスタムモデルをLangChainのChatOllamaクラスで呼び出し、ツール付きエージェントとして動かします。vLLMで本番スケールに拡張する場合はvLLM本番運用ガイドも参照してください。
# 動作環境: Python 3.11+, langchain-ollama>=0.3, langgraph>=0.2
# pip install langchain-ollama langgraph
from langchain_ollama import ChatOllama
from langchain.agents import AgentExecutor, create_react_agent
from langchain.tools import tool
from langchain import hub
# カスタムOllamaモデルを呼び出すLLMクライアント
llm = ChatOllama(
model="business-agent", # ollama createで登録したモデル名
temperature=0, # エージェントは決定的な動作のためtemperature=0推奨
base_url="http://localhost:11434",
)
# 業務ツールを定義(例: 社内DBへのクエリ)
@tool
def search_product_info(product_id: str) -> str:
"""製品IDで社内製品情報を検索します。入力: 製品IDの文字列"""
# 実際にはDBやAPIを呼び出す
dummy_db = {
"A001": "製品A: 保証期間1年, 返品30日以内可能",
"B002": "製品B: 保証期間2年, 返品14日以内可能",
}
return dummy_db.get(product_id, f"製品ID '{product_id}' は見つかりませんでした")
@tool
def get_business_hours() -> str:
"""営業時間と連絡先を返します"""
return "営業時間: 平日9:00〜18:00 / 電話: 03-XXXX-XXXX / メール: support@example.com"
tools = [search_product_info, get_business_hours]
# ReActプロンプトを取得してエージェントを作成
# 注意: hub.pull はオンラインが必要。オフライン環境ではカスタムプロンプトを使う
prompt = hub.pull("hwchase17/react")
agent = create_react_agent(llm, tools, prompt)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
max_iterations=5, # 無限ループ防止
handle_parsing_errors=True,
)
# 実行例
result = agent_executor.invoke({
"input": "製品A001について教えてください"
})
print(result["output"])
LangChain 0.3系でChatOllamaのtemperature=0を指定することで、ツール選択の一貫性が向上します(LangChain公式ドキュメント、2026年4月時点)。
【要注意】よくある失敗パターンと回避策
失敗1:チャットテンプレートの不一致
ファインチューニング時に使ったテンプレートとOllama/推論時のテンプレートが異なると、モデルが意図しない応答を返します。
❌ 学習時はAlpacaテンプレート、Modelfileではデフォルトテンプレートを使う
⭕ 学習時とOllamaのModelfileで同じSYSTEMプロンプト・停止トークンを使う
なぜ重要か:Unsloth公式ドキュメントでも「最も多いエラーの原因はチャットテンプレートの不一致」と明記されています。
失敗2:学習データの品質が低い
❌ 社内Wikiの全文をそのままデータセットに流し込む
⭕ 「指示→応答」の形式に整形し、曖昧な応答・矛盾した応答を除去する
なぜ重要か:「ゴミを入れればゴミが出る(Garbage in, Garbage out)」はLLMファインチューニングでも同様です。100件の高品質サンプルが1,000件の低品質サンプルを上回ることがあります。
失敗3:過学習(オーバーフィッティング)
❌ 少量データ(50件未満)で10エポック以上学習する
⭕ 検証ロスを監視し、上昇し始めたら早期終了(EarlyStoppingCallback)を使う
from transformers import EarlyStoppingCallback
# 検証データを用意する場合の追加設定
trainer = SFTTrainer(
# ... 上記と同様の設定 ...
args=TrainingArguments(
# ... 既存設定 ...
evaluation_strategy="steps",
eval_steps=50,
load_best_model_at_end=True,
),
eval_dataset=eval_dataset, # 検証データセット(全体の10〜20%)
callbacks=[EarlyStoppingCallback(early_stopping_patience=3)],
)
失敗4:RAGとの役割分担が曖昧
❌ 頻繁に変わる商品価格・在庫情報もQLoRAで学習させる
⭕ 変動する知識はRAGで参照し、QLoRAは応答スタイル・専門語彙・推論パターンの統一に使う
Embeddingモデル選定ガイドを参考に、ベクトル検索と組み合わせたハイブリッド構成を検討してください。
パフォーマンス指標の確認方法
学習ログの解釈
ファインチューニング後の評価では、タスク固有のメトリクスを使います。
from unsloth import FastLanguageModel
# 推論モードに切り替え(学習時より高速)
FastLanguageModel.for_inference(model)
# テストサンプルで応答を確認
test_prompt = alpaca_prompt.format(
instruction="製品の返品方法を教えてください",
input="",
output="",
)
inputs = tokenizer(test_prompt, return_tensors="pt").to("cuda")
outputs = model.generate(
**inputs,
max_new_tokens=128,
temperature=0.1,
do_sample=True,
)
response = tokenizer.decode(outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
print(f"応答: {response}")
よくある質問(FAQ)
Q1. LoRAとQLoRA、業務エージェントにはどちらを使うべきですか?
コンシューマGPU(RTX 4090 24GB以下)を使う場合はQLoRA一択です。A100 80GBクラスのGPUが使える場合は、より高品質な結果を求めてLoRA(FP16)を選ぶ余地があります。品質差は多くの業務タスクでは軽微です。
Q2. 最低何件のデータがあればファインチューニングできますか?
動作確認は10件でも可能ですが、実用品質には最低100〜200件、安定した結果には500件以上を推奨します。品質の高いサンプルを慎重に作ることが、データ件数を増やすより重要です。
Q3. Ollamaを使わずに直接デプロイする方法はありますか?
はい。vLLMを使えばSafetensors形式のLoRAアダプターをマージして高スループットの推論サーバーを構築できます。詳細はvLLM本番運用ガイドをご参照ください。
Q4. ファインチューニング後にRAGと組み合わせることはできますか?
はい、むしろ推奨です。QLoRAでドメイン語彙・応答スタイルを固定し、最新の知識・変動するデータはRAGで補完するハイブリッド構成が2026年現在のベストプラクティスです。
Q5. クラウドGPUを使う場合のコスト目安はどのくらいですか?
7B QLoRAをA100 40GB 1枚で2〜4時間学習する場合、クラウドGPUレンタルコストは概ね数百円〜数千円程度です(プロバイダーや時期によって変動します。最新料金は各プロバイダーの公式サイトをご確認ください)。
参考・出典
- Unsloth Documentation: Requirements — Unsloth公式(参照日: 2026-06-11)
- Unsloth Documentation: Saving Models to Ollama — Unsloth公式(参照日: 2026-06-11)
- Unsloth and Training Hub: Lightning-fast LoRA and QLoRA fine-tuning — Red Hat Developer(参照日: 2026-06-11)
- Fine-Tuning LLMs in 2026: When RAG Isn’t Enough (and When It Still Is) — BigData Boutique(参照日: 2026-06-11)
- NVIDIA A100 vs RTX 4090: Best GPU for LLM Fine-tuning (2026) — ThunderCompute(参照日: 2026-06-11)
- LangChain Ollama Integration — LangChain公式ドキュメント(参照日: 2026-06-11)
まとめ:今日から始める3つのアクション
- 今日やること:Unslothをインストールし、Colab(T4 16GB、無料枠)で7Bモデルの4bit量子化ロードと10件のサンプルデータでのQLoRA学習を動かしてみる
- 今週中:業務ドメインのデータセット100件を「指示→応答」形式で整備し、Ollamaへのエクスポートまで完了させる
- 今月中:LangChainエージェントに統合し、社内ツール(検索・DB参照)との連携をPoC環境で検証。RAGとのハイブリッド構成を評価する
あわせて読みたい:
- ローカルLLM運用ガイド Ollama×llama.cpp 2026 — Ollamaを使ったローカルLLM推論の基礎から応用まで
- RAG・ファインチューニング・プロンプトの使い分け — どの手法をいつ選ぶかの判断フレームワーク
著者: 佐藤傑(さとう・すぐる)
株式会社Uravation代表取締役。X(@SuguruKun_ai)フォロワー10万人以上。著書『AIエージェント仕事術』。
100社以上の企業向けAI研修・導入支援。SoftBank IT連載執筆。
ご質問・ご相談は お問い合わせフォーム からお気軽にどうぞ。
この記事を読んで導入イメージが固まってきた方へ
UravationではAIエージェント導入の研修・コンサルを行っています。
