28. Llama 3をQLoRAでファインチューニング!完全なトレーニングパイプライン解説

スポンサーリンク

28. Llama 3をQLoRAでファインチューニング!完全なトレーニングパイプライン解説

ついに来ました!これまで学んできたすべてを統合して、実際にモデルをファインチューニングする時です。

今回は、QLoRA(4ビット量子化とLoRAアダプターの組み合わせ)を使って、SAMSum対話要約データセットでLlama 3.2 1B-Instructをトレーニングします。

以前のレッスンで構築したすべてを再利用します。データセットのロード、設定管理、評価関数。そこにトレーニングパイプラインを追加します。マスキングロジック、Trainerのセットアップ、モデルチェックポイント作成、すべてです!

最後には、再現可能なトレーニングスクリプト、LoRAアダプターのセット、ベースラインに対する測定可能なパフォーマンス向上が得られます。

注意:このレッスンは、メカニクスを正しく理解することに焦点を当てています。エンドツーエンドで動作するパイプラインの構築です。まだピークパフォーマンスの最適化は行いません。それは次で、実験追跡とハイパーパラメーター検索を追加するときです。

ベースラインからカスタムモデルのトレーニングへ

これまでに、SAMSumをロード・キャッシュし、ベースモデルで推論を実行し、ベースライン評価のROUGEメトリクスを計算しました。

今度はこれらのコンポーネントを再利用し、新しいことに焦点を当てます。モデルのファインチューニングです!

このワークフローは、ノートブックではなくモジュール式のPythonスクリプトで実装されています。スケールし、実行間で再現可能であるように設計されています。プロフェッショナルな開発環境ですね。

このレッスンで行うこと:

  • アシスタントのみのマスキングを適用して要約にトレーニングを集中
  • QLoRA固有の設定でTrainer APIを設定
  • 最初の完全なトレーニングループを実行
  • LoRAアダプターを保存して評価

QLoRA Training Pipeline:全体像

トレーニングの背後にあるツール

このワークフローは、以前のレッスンで使用してきた同じスタックに依存しています:

  • transformers:モデルとトークナイザーをロードし、Trainer APIを介してトレーニングループを処理
  • PEFT:パラメーター効率的なファインチューニングのためのLoRA/QLoRA機能を提供
  • bitsandbytes:4ビット量子化と8ビットオプティマイザーを可能にする
  • datasets:SAMSumのロード、キャッシング、前処理を処理
  • accelerate:デバイス配置と分散トレーニングセットアップを管理

これらのツールはすでに評価に使用していますよね。今回は、それらをトレーニングに使用します!

再利用するコンポーネント

以前のレッスンで構築したこれらのユーティリティが、今まとまります:

  • config.yaml:すべてのパラメーターを一箇所で管理
  • load_and_prepare_dataset():データセットのロードとサンプリング
  • compute_rouge():評価メトリクスの計算
  • setup_model_and_tokenizer():モデルとトークナイザーのセットアップ

これらはデータセット、設定、モデルのセットアップを処理します。私たちはトレーニングロジックに純粋に焦点を当てられるんです。

Configuration:トレーニングの青写真

トレーニングコードに入る前に、それを駆動するものを見てみましょう。config.yamlファイルです。

以前のレッスンでデータセットセクションをすでに見ました。今度は設定の残りの部分を使用します。量子化設定、LoRAパラメーター、トレーニングハイパーパラメーター。

重要な設定項目

モデル設定:

base_model: meta-llama/Llama-3.2-1B-Instruct
tokenizer_type: meta-llama/Llama-3.2-1B-Instruct

量子化設定:

quantization:
  use_4bit: true
  bnb_4bit_compute_dtype: bfloat16
  bnb_4bit_quant_type: nf4
  bnb_4bit_use_double_quant: true

これは、前回学んだNF4量子化とダブル量子化の設定ですね!

LoRA設定:

lora:
  r: 8
  lora_alpha: 16
  target_modules: ["q_proj", "v_proj"]
  lora_dropout: 0.05

これも前回学んだ推奨設定です。ランク8、アルファ16、q_projとv_projをターゲットにします。

トレーニングハイパーパラメーター:

training:
  num_train_epochs: 1
  per_device_train_batch_size: 4
  gradient_accumulation_steps: 4
  learning_rate: 0.0002
  warmup_steps: 50
  logging_steps: 10
  save_strategy: "steps"
  save_steps: 100

これらのパラメーターがトレーニングの挙動を決定します。

データ前処理:アシスタントのみのマスキング

ファインチューニングで最も重要な部分の1つが、アシスタントのみのマスキングです。

通常、モデルは入力全体から学習しますが、要約タスクでは、対話部分は学習せず、要約部分だけを学習させたいんです。

これを実現するために、対話部分のトークンをマスク(-100に設定)します。こうすると、損失計算時にその部分が無視されます。

前処理の流れ

  1. プロンプトの作成:対話をユーザープロンプトとして構造化
  2. トークン化:プロンプトと要約をトークンに変換
  3. マスキング:ユーザープロンプト部分のラベルを-100に設定
  4. パディング:バッチ処理のために長さを揃える
def preprocess_function(examples):
    # プロンプト作成
    prompts = [build_prompt(dialogue) for dialogue in examples['dialogue']]
    summaries = examples['summary']

    # トークン化
    model_inputs = tokenizer(prompts, max_length=max_length, truncation=True)
    labels = tokenizer(summaries, max_length=max_length, truncation=True)

    # マスキング適用
    for i in range(len(model_inputs['input_ids'])):
        prompt_len = len(model_inputs['input_ids'][i])
        labels['input_ids'][i] = [-100] * prompt_len + labels['input_ids'][i]

    model_inputs['labels'] = labels['input_ids']
    return model_inputs

これで、モデルは要約部分だけから学習するようになります!

モデルとLoRAアダプターのセットアップ

次に、量子化されたモデルをロードし、LoRAアダプターを追加します。

ステップ1:量子化設定

from transformers import BitsAndBytesConfig
import torch

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

ステップ2:モデルのロード

from transformers import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.2-1B-Instruct",
    quantization_config=bnb_config,
    device_map="auto",
)

ステップ3:LoRAアダプターの追加

from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training

# トレーニング用にモデルを準備
model = prepare_model_for_kbit_training(model)

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

# アダプターを適用
model = get_peft_model(model, lora_config)

# 訓練可能なパラメーター確認
model.print_trainable_parameters()
# 出力例: trainable params: 4,194,304 || all params: 1,235,814,400 || trainable%: 0.34

わずか0.34%のパラメーターだけを訓練します!効率的ですね。

トレーニングの実行

いよいよトレーニングです!Hugging FaceのTrainer APIを使用します。

TrainingArguments の設定

from transformers import TrainingArguments

training_args = TrainingArguments(
    output_dir="./outputs/llama-3.2-1b-samsum",
    num_train_epochs=1,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    learning_rate=2e-4,
    warmup_steps=50,
    logging_steps=10,
    save_strategy="steps",
    save_steps=100,
    evaluation_strategy="steps",
    eval_steps=100,
    fp16=False,
    bf16=torch.cuda.is_available(),
    optim="paged_adamw_8bit",
)

Trainerのセットアップと実行

from transformers import Trainer

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_train,
    eval_dataset=tokenized_val,
    data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False),
)

# トレーニング開始!
trainer.train()

トレーニングが始まると、ログが表示されます:

Step 10: loss=2.45, learning_rate=0.0001
Step 20: loss=2.12, learning_rate=0.0002
Step 30: loss=1.89, learning_rate=0.0002
...

損失が下がっていくのを見るのは気持ちいいですよね!

アダプターの保存と評価

トレーニングが完了したら、LoRAアダプターを保存します。

# アダプターを保存
model.save_pretrained("./outputs/llama-3.2-1b-samsum/adapter")
tokenizer.save_pretrained("./outputs/llama-3.2-1b-samsum/adapter")

アダプターは通常10~50MB程度で、とても小さいです。元のモデル全体を保存する必要がないんですね!

評価

前回作成した評価関数を使って、ファインチューニング済みモデルを評価します:

# ベースモデルとアダプターをロード
model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.2-1B-Instruct",
    quantization_config=bnb_config,
    device_map="auto",
)

from peft import PeftModel
model = PeftModel.from_pretrained(model, "./outputs/llama-3.2-1b-samsum/adapter")

# 評価実行
scores = evaluate_model(model, tokenizer, val_data)
print(f"ROUGE-1: {scores['rouge1']:.2%}")
print(f"ROUGE-2: {scores['rouge2']:.2%}")
print(f"ROUGE-L: {scores['rougeL']:.2%}")

期待される結果

ファインチューニング後、ROUGEスコアは通常以下のように改善します:

メトリック ベースライン ファインチューニング後 改善
ROUGE-1 約30-35% 約40-45% +10%程度
ROUGE-2 約15-20% 約25-30% +10%程度
ROUGE-L 約25-30% 約35-40% +10%程度

数字で改善が見えるって、本当に嬉しいですよね!

まとめ

今回は、完全なQLoRAトレーニングパイプラインを構築しました!

重要なポイントを振り返りましょう:

1. 設定管理
config.yamlですべてのパラメーターを一元管理
– 再現可能で、実験間で変更が追跡しやすい

2. データ前処理
– アシスタントのみのマスキングで効率的な学習
– 対話部分は学習せず、要約部分だけを学習

3. QLoRAセットアップ
– 4ビット量子化でメモリ効率的
– LoRAアダプターでパラメーター効率的
– わずか0.34%のパラメーターだけを訓練

4. トレーニング実行
– Hugging Face Trainer APIで簡単にトレーニング
– ログで進捗を監視
– チェックポイントを定期的に保存

5. 評価と保存
– 小さなアダプターファイル(10~50MB)を保存
– ROUGEスコアで測定可能な改善を確認

これで、あなたは完全なファインチューニングパイプラインを持っています。このパイプラインは、任意のモデルやタスクに再利用できます!

次回は、このパイプラインをクラウドGPUにスケールして、より速くトレーニングする方法を学びます。楽しみにしていてくださいね!

コメント

タイトルとURLをコピーしました