mcpField
L
llm-fine-tuning-guide

llm-fine-tuning-guide

github.com/qodex-ai開発47194 回インストール42 週間前に更新
リポジトリ

llm-fine-tuning-guide について

LLMを特定の分野やタスクに適応させる開発者向けの実践ガイドです。データ準備から、全量・パラメーター効率の高いファインチューニング、評価まで扱います。

主な機能

  1. 01全量およびPEFTの手法
  2. 02学習データの準備と検証
  3. 03学習手法と設定例
  4. 04モデル評価の指標と手順

活用シーン

  1. 01法律・医療向けモデルの適応
  2. 02LoRAやQLoRAによる学習コスト削減
  3. 03対象タスクでの調整済みモデル評価

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/qodex-ai/ai-agent-skills --skill llm-fine-tuning-guide

インストール後、Claude Code で/llm-fine-tuning-guideを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
examples/
full_fine_tuning.py
lora_fine_tuning.py
qlora_fine_tuning.py
scripts/
data_preparation.py
evaluation_metrics.py

詳細

LLM ファインチューニングガイド

特定のユースケース、ドメイン、パフォーマンス要件に合わせて最適化した専門モデルを作成するために、LLM のファインチューニング技術を習得しましょう。

概要

ファインチューニングでは、厳選したデータセットで事前学習済みの LLM をトレーニングし、特定のタスク、ドメイン、スタイルに適応させます。これにより、精度が向上し、ハルシネーションが減り、コストが最適化されます。

ファインチューニングが適している場合
  • ドメイン特化:法律文書、医療記録、財務レポート
  • タスク固有のパフォーマンス:特定のタスクでベースモデルより良い結果を得られる
  • コストの最適化:高価な大規模モデルを、ファインチューニング済みの小規模モデルに置き換えられる
  • スタイルの適応:特定の文体やトーンに合わせられる
  • コンプライアンス要件:機密データを自社のインフラ内に保持できる
  • レイテンシ要件:小規模モデルはより迅速にデプロイできる
ファインチューニングが適していない場合
  • 1 回限りのクエリ(代わりにプロンプトを使用)
  • 急速に変化する情報(代わりに RAG を使用)
  • トレーニングデータが限られている(通常、100 件未満の例では不十分)
  • 一般知識に関する質問(ベースモデルで十分)

クイックスタート

フルファインチューニング:

python examples/full_fine_tuning.py

LoRA(ほとんどの場合に推奨):

python examples/lora_fine_tuning.py

QLoRA(単一 GPU):

python examples/qlora_fine_tuning.py

データの準備:

python scripts/data_preparation.py

ファインチューニングのアプローチ

1. フルファインチューニング

トレーニング中にすべてのモデルパラメーターを更新します。

メリット:

  • パフォーマンスを最大限に向上できる
  • モデルの挙動を完全に書き換えられる
  • ドメインを大きく変更する場合に最適

デメリット:

  • 計算コストが高い
  • 大規模なデータセットが必要(1000 件以上の例)
  • 壊滅的忘却のリスク
  • トレーニングに時間がかかる
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer

model_id = "meta-llama/Llama-2-7b"
model = AutoModelForCausalLM.from_pretrained(model_id)
tokenizer = AutoTokenizer.from_pretrained(model_id)

training_args = TrainingArguments(
    output_dir="./fine-tuned-llama",
    num_train_epochs=3,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    learning_rate=2e-5,
    weight_decay=0.01,
    logging_steps=10,
    save_steps=100,
    eval_strategy="steps",
    eval_steps=50,
    load_best_model_at_end=True,
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=eval_dataset,
)

trainer.train()
2. パラメーター効率の高いファインチューニング(PEFT)

パラメーターのごく一部だけをトレーニングします。

LoRA(低ランク適応)

既存の重みに、トレーニング可能な低ランク行列を追加します。

メリット:

  • トレーニング可能なパラメーターを 99% 削減
  • ベースモデルの知識を維持
  • トレーニングが高速(10~20 倍)
  • アダプターを簡単に切り替え可能

デメリット:

  • フルファインチューニングよりパフォーマンスがわずかに低い
  • 推論時にベースモデルが必要
from peft import get_peft_model, LoraConfig, TaskType
from transformers import AutoModelForCausalLM, AutoTokenizer

base_model_id = "meta-llama/Llama-2-7b"
model = AutoModelForCausalLM.from_pretrained(base_model_id)
tokenizer = AutoTokenizer.from_pretrained(base_model_id)

# Configure LoRA
lora_config = LoraConfig(
    r=8,  # Rank of low-rank matrices
    lora_alpha=16,  # Scaling factor
    target_modules=["q_proj", "v_proj"],  # Which layers to adapt
    lora_dropout=0.05,
    bias="none",
    task_type=TaskType.CAUSAL_LM
)

# Wrap model with LoRA
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# Output: trainable params: 4,194,304 || all params: 6,738,415,616 || trainable%: 0.06

# Train as normal
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
)
trainer.train()

# Save only LoRA weights
model.save_pretrained("./llama-lora-adapter")
QLoRA(量子化 LoRA)

LoRA と量子化を組み合わせ、高い効率を実現します。

from peft import prepare_model_for_kbit_training, get_peft_model, LoraConfig
from transformers import AutoModelForCausalLM, BitsAndBytesConfig

# Quantization config
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype="float16",
    bnb_4bit_use_double_quant=True
)

# Load quantized model
model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-2-7b",
    quantization_config=bnb_config,
    device_map="auto"
)

# Prepare for training
model = prepare_model_for_kbit_training(model)

# Apply LoRA
lora_config = LoraConfig(
    r=8,
    lora_alpha=32,
    target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
    lora_dropout=0.05,
    bias="none",
    task_type=TaskType.CAUSAL_LM
)

model = get_peft_model(model, lora_config)

# Train on single GPU
trainer = Trainer(
    model=model,
    args=TrainingArguments(
        output_dir="./qlora-output",
        per_device_train_batch_size=1,
        gradient_accumulation_steps=4,
        learning_rate=5e-4,
        num_train_epochs=3,
    ),
    train_dataset=train_dataset,
)
trainer.train()
Prefix Tuning

トレーニング可能なトークンを入力の先頭に追加します。

from peft import get_peft_model, PrefixTuningConfig

config = PrefixTuningConfig(
    num_virtual_tokens=20,
    task_type=TaskType.CAUSAL_LM,
)

model = get_peft_model(model, config)
# Only 20 * embedding_dim parameters trained
3. インストラクションファインチューニング

例を使って、指示に従うようモデルをトレーニングします。

# Training data format
training_data = [
    {
        "instruction": "Translate to French",
        "input": "Hello, how are you?",
        "output": "Bonjour, comment allez-vous?"
    },
    {
        "instruction": "Summarize this text",
        "input": "Long document...",
        "output": "Summary..."
    }
]

# Template for training
template = """Below is an instruction that describes a task, paired with an input that provides further context.

### Instruction:
{instruction}

### Input:
{input}

### Response:
{output}"""

# Create formatted dataset
formatted_data = [
    template.format(**example) for example in training_data
]
4. ドメイン特化型ファインチューニング

特定の業界や分野に合わせてモデルを調整します。

法律分野の例
legal_training_data = [
    {
        "prompt": "What are the key clauses in an NDA?",
        "completion": """Key clauses typically include:
1. Definition of Confidential Information
2. Non-Disclosure Obligations
3. Permitted Disclosures
4. Term and Termination
5. Return of Information
6. Remedies"""
    },
    # ... more legal examples
]

# Train on legal domain
model = fine_tune_on_domain(
    base_model="gpt-3.5-turbo",
    training_data=legal_training_data,
    epochs=3,
    learning_rate=0.0002,
)

データの準備

1. データセットの品質
class DatasetValidator:
    def validate_dataset(self, data):
        issues = {
            "empty_samples": 0,
            "duplicates": 0,
            "outliers": 0,
            "imbalance": {}
        }

        # Check for empty samples
        for sample in data:
            if not sample.get("text"):
                issues["empty_samples"] += 1

        # Check for duplicates
        texts = [s.get("text") for s in data]
        issues["duplicates"] = len(texts) - len(set(texts))

        # Check for length outliers
        lengths = [len(t.split()) for t in texts]
        mean_length = sum(lengths) / len(lengths)
        issues["outliers"] = sum(1 for l in lengths if l > mean_length * 3)

        return issues

# Validate before training
validator = DatasetValidator()
issues = validator.validate_dataset(training_data)
print(f"Dataset Issues: {issues}")
2. データ拡張
from nlpaug.augmenter.word import SynonymAug, RandomWordAug
import nlpaug.flow as naf

# Create augmentation pipeline
text = "The quick brown fox jumps over the lazy dog"

# Synonym replacement
aug_syn = SynonymAug(aug_p=0.3)
augmented_syn = aug_syn.augment(text)

# Random word insertion
aug_insert = RandomWordAug(action="insert", aug_p=0.3)
augmented_insert = aug_insert.augment(text)

# Combine augmentations
flow = naf.Sequential([
    SynonymAug(aug_p=0.2),
    RandomWordAug(action="swap", aug_p=0.2)
])
augmented = flow.augment(text)
3. トレーニング/検証データの分割
from sklearn.model_selection import train_test_split

# Create splits
train_data, eval_data = train_test_split(
    data,
    test_size=0.2,
    random_state=42
)

eval_data, test_data = train_test_split(
    eval_data,
    test_size=0.5,
    random_state=42
)

print(f"Train: {len(train_data)}, Eval: {len(eval_data)}, Test: {len(test_data)}")

トレーニング手法

1. 学習率スケジューリング
from torch.optim.lr_scheduler import CosineAnnealingLR, LinearLR

# Linear warmup + cosine annealing
def get_scheduler(optimizer, num_steps):
    lr_scheduler = get_linear_schedule_with_warmup(
        optimizer,
        num_warmup_steps=500,
        num_training_steps=num_steps
    )
    return lr_scheduler

training_args = TrainingArguments(
    learning_rate=1e-4,
    lr_scheduler_type="cosine",
    warmup_steps=500,
    warmup_ratio=0.1,
)
2. 勾配累積
training_args = TrainingArguments(
    gradient_accumulation_steps=4,  # Accumulate gradients over 4 steps
    per_device_train_batch_size=1,   # Effective batch size: 1 * 4 = 4
)

# Simulates larger batch on limited GPU memory
3. 混合精度トレーニング
training_args = TrainingArguments(
    fp16=True,  # Use 16-bit floats
    bf16=False,
)

# Reduces memory usage by 50%, speeds up training
4. マルチ GPU トレーニング
training_args = TrainingArguments(
    output_dir="./results",
    num_train_epochs=3,
    per_device_train_batch_size=8,
    per_device_eval_batch_size=8,
    gradient_accumulation_steps=4,
    dataloader_pin_memory=True,
    dataloader_num_workers=4,
)

# Automatically uses all available GPUs
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=eval_dataset,
)

ファインチューニングに人気のモデル

オープンソースモデル
Llama 3.2(Meta)
from transformers import AutoModelForCausalLM, AutoTokenizer

model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.2-7b")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-7b")

# Fine-tune on custom data
# ... training code

特徴:

  • 7B、70B パラメーターのバージョン
  • 指示に従う能力が高い
  • ドメイン適応に最適
  • Apache 2.0 ライセンス
Gemma 3(Google)
model = AutoModelForCausalLM.from_pretrained("google/gemma-3-2b")
tokenizer = AutoTokenizer.from_pretrained("google/gemma-3-2b")

# Gemma 3 sizes: 2B, 7B, 27B
# Very efficient, great for fine-tuning

特徴:

  • 小、中、大のサイズ
  • 効率的なアーキテクチャ
  • エッジデプロイに適している
  • 最先端の研究に基づいて構築
Mistral 7B
model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1")
tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-v0.1")

# Strong performance, efficient architecture

特徴:

  • スライディングウィンドウアテンション
  • 効率的な推論
  • モデルサイズに対するパフォーマンス比が高い
商用モデル
OpenAI ファインチューニング API
import openai

# Prepare training data
training_file = openai.File.create(
    file=open("training_data.jsonl", "rb"),
    purpose="fine-tune"
)

# Create fine-tuning job
fine_tune_job = openai.FineTuningJob.create(
    training_file=training_file.id,
    model="gpt-3.5-turbo",
    hyperparameters={
        "n_epochs": 3,
        "learning_rate_multiplier": 0.1,
    }
)

# Wait for completion
fine_tuned_model = openai.FineTuningJob.retrieve(fine_tune_job.id)
print(f"Status: {fine_tuned_model.status}")

# Use fine-tuned model
response = openai.ChatCompletion.create(
    model=fine_tuned_model.fine_tuned_model,
    messages=[{"role": "user", "content": "Hello"}]
)

評価と指標

1. パープレキシティ
import torch
from math import exp

def calculate_perplexity(model, eval_dataset):
    model.eval()
    total_loss = 0
    total_tokens = 0

    with torch.no_grad():
        for batch in eval_dataset:
            outputs = model(**batch)
            loss = outputs.loss
            total_loss += loss.item() * batch["input_ids"].shape[0]
            total_tokens += batch["input_ids"].shape[0]

    perplexity = exp(total_loss / total_tokens)
    return perplexity

perplexity = calculate_perplexity(model, eval_dataset)
print(f"Perplexity: {perplexity:.2f}")
2. タスク固有の指標
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score

def evaluate_task(predictions, ground_truth):
    return {
        "accuracy": accuracy_score(ground_truth, predictions),
        "precision": precision_score(ground_truth, predictions, average='weighted'),
        "recall": recall_score(ground_truth, predictions, average='weighted'),
        "f1": f1_score(ground_truth, predictions, average='weighted'),
    }

# Evaluate on task
predictions = [model.predict(x) for x in test_data]
metrics = evaluate_task(predictions, test_labels)
print(f"Metrics: {metrics}")
3. 人間による評価
class HumanEvaluator:
    def evaluate_response(self, prompt, response):
        criteria = {
            "relevance": self._score_relevance(prompt, response),
            "coherence": self._score_coherence(response),
            "factuality": self._score_factuality(response),
            "helpfulness": self._score_helpfulness(response),
        }
        return sum(criteria.values()) / len(criteria)

    def _score_relevance(self, prompt, response):
        # Score 1-5
        pass

    def _score_coherence(self, response):
        # Score 1-5
        pass

よくある課題と解決策

課題:壊滅的忘却

新しいドメインに適応する一方で、モデルが事前学習時の知識を忘れてしまいます。

解決策:

  • 学習率を低くする(2e-5~5e-5)
  • トレーニングエポック数を減らす(1~3)
  • 正則化手法を使う
  • 継続学習のアプローチを取り入れる
# Conservative training settings
training_args = TrainingArguments(
    learning_rate=2e-5,  # Lower learning rate
    num_train_epochs=2,   # Few epochs
    weight_decay=0.01,    # L2 regularization
    warmup_steps=500,
    save_total_limit=3,
    load_best_model_at_end=True,
)
課題:過学習

トレーニングデータでは高い性能を示すものの、新しいデータでは性能が低くなります。

解決策:

  • トレーニングデータを増やす
  • ドロップアウトを導入する
  • 早期終了を行う
  • 検証結果を監視する
training_args = TrainingArguments(
    eval_strategy="steps",
    eval_steps=50,
    load_best_model_at_end=True,
    early_stopping_patience=3,
    metric_for_best_model="eval_loss",
)
課題:学習データの不足

ファインチューニングに使える例が少ない場合。

解決策:

  • データ拡張
  • 全体をファインチューニングする代わりに PEFT(LoRA)を使用する
  • プロンプトを使った少数ショット学習
  • 転移学習
# Use LoRA when data is limited
lora_config = LoraConfig(
    r=8,
    lora_alpha=16,
    target_modules=["q_proj", "v_proj"],
    lora_dropout=0.05,
)

ベストプラクティス

ファインチューニングの前
  • ✓ 強力なベースモデルから始める
  • ✓ 高品質な学習データを用意する(100 件以上を推奨)
  • ✓ 明確な評価指標を定義する
  • ✓ 適切な学習用/検証用データの分割を設定する
  • ✓ 目標を文書化する
ファインチューニング中
  • ✓ 学習/検証損失を監視する
  • ✓ 適切な学習率を使用する
  • ✓ チェックポイントを定期的に保存する
  • ✓ 学習に使用していないデータで検証する
  • ✓ 過学習/学習不足に注意する
ファインチューニングの後
  • ✓ テストセットで評価する
  • ✓ ベースラインと比較する
  • ✓ 定性的な分析を実施する
  • ✓ 設定と結果を文書化する
  • ✓ ファインチューニング済みモデルにバージョンを付ける

実装チェックリスト

  • ファインチューニングの方法を決定する(フル、LoRA、QLoRA、インストラクション)
  • 学習データセットを用意し、検証する(100 件以上)
  • ベースモデルを選択する(Llama 3.2、Gemma 3、Mistral など)
  • パラメーター効率の高い方法を使う場合は PEFT を設定する
  • 学習引数とハイパーパラメーターを設定する
  • データの読み込みと前処理を実装する
  • 評価指標を設定する
  • 監視しながらモデルを学習させる
  • テストセットで評価する
  • ファインチューニング済みモデルを保存し、バージョンを付ける
  • 本番環境でテストする
  • プロセスと結果を文書化する

リソース

フレームワーク
モデル
論文
  • 「LoRA: Low-Rank Adaptation of Large Language Models」(Hu ほか)
  • 「QLoRA: Efficient Finetuning of Quantized LLMs」(Dettmers ほか)

コメント