LLMのファインチューニング: DeepSeek-R1-Distill-Qwen-1.5Bの詳細ガイド
LLM(大規模言語モデル)のファインチューニングにより、特定のタスクでのパフォーマンスを大幅に向上させることができます。本ガイドでは、Google Colab、Hugging Faceのtransformers
、datasets
、および8ビット量子化をサポートするbitsandbytes
を使用して、DeepSeek-R1-Distill-Qwen-1.5Bモデルをファインチューニングする方法を説明します。また、wandb
を使用して実験を追跡します。
ステップ1: 環境のセットアップ
まず、必要なライブラリをインストールします:
!pip install transformers datasets sympy wandb
!pip install --no-cache-dir bitsandbytes
これらのライブラリは、モデルのロード、データセットの処理、効率的なファインチューニング、および実験の追跡に必要です。
ステップ2: モデルとトークナイザーのセットアップ
DeepSeek-R1-Distill-Qwen-1.5Bモデルとトークナイザーをロードします:
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
model_name = "deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B"
# トークナイザーをロード
tokenizer = AutoTokenizer.from_pretrained(model_name)
# モデルをロードし、GPUに移動
model = AutoModelForCausalLM.from_pretrained(model_name).to("cuda")
print("モデルがGPUにロードされました。")
ステップ3: WandBを使用した実験トラッキングのセットアップ
まず、Weights & Biases(WandB)にログインします:
!wandb login
プロジェクトを初期化します:
import wandb
wandb.init(
project="My_LLM",
config={
"learning_rate": 5e-5,
"architecture": "DeepSeek-R1-Distill-Qwen-1.5B",
"dataset": "dataset.jsonl",
"epochs": 2,
}
)
ステップ4: データセットの準備
Google Driveのマウント
from google.colab import drive
drive.mount('/content/drive')
データセットをGoogle Driveに保存しておく必要があります。
データセットのロードと分割
from datasets import load_dataset
dataset = load_dataset("json", data_files={"train": "/content/drive/My Drive/dataset.jsonl"}, split="train")
print(f"データセットがロードされました: {len(dataset)} サンプル")
train_test_split = dataset.train_test_split(test_size=0.2)
train_dataset = train_test_split["train"]
eval_dataset = train_test_split["test"]
80%をトレーニング用、20%を評価用に分割します。
トークナイゼーション
def tokenize_function(examples):
combined_texts = [f"{prompt}\n{completion}" for prompt, completion in zip(examples["prompt"], examples["completion"])]
tokenized = tokenizer(combined_texts, truncation=True, max_length=512, padding="max_length")
tokenized["labels"] = tokenized["input_ids"].copy()
return tokenized
tokenized_train_dataset = train_dataset.map(tokenize_function, batched=True)
tokenized_eval_dataset = eval_dataset.map(tokenize_function, batched=True)
ステップ5: 8ビット量子化によるモデルのロード
from transformers import BitsAndBytesConfig
quantization_config = BitsAndBytesConfig(load_in_8bit=True)
model = AutoModelForCausalLM.from_pretrained(model_name, quantization_config=quantization_config, device_map="auto")
print("モデルが8ビット量子化でGPUにロードされました。")
ステップ6: LoRAによるファインチューニングの設定
from peft import get_peft_model, LoraConfig, TaskType
lora_config = LoraConfig(
r=8, lora_alpha=16, lora_dropout=0.05, task_type=TaskType.CAUSAL_LM
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
ステップ7: トレーニングの設定
from transformers import TrainingArguments, Trainer
training_args = TrainingArguments(
output_dir="./deepseek_finetuned",
num_train_epochs=50,
per_device_train_batch_size=2,
gradient_accumulation_steps=16,
fp16=True,
logging_steps=10,
save_steps=100,
evaluation_strategy="steps",
eval_steps=10,
learning_rate=3e-5,
logging_dir="./logs",
report_to="wandb",
run_name="DeepSeek_FineTuning_Experiment",
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_train_dataset,
eval_dataset=tokenized_eval_dataset,
)
trainer.train()
✅ Dataset loaded: 44 samples
Map: 100%
35/35 [00:00<00:00, 230.76 examples/s]
Map: 100%
9/9 [00:00<00:00, 131.62 examples/s]
✅ Train dataset: 35 samples
✅ Eval dataset: 9 samples
✅ Model loaded with 8-bit quantization on GPU...
trainable params: 1,572,864 || all params: 1,348,044,800 || trainable%: 0.1167
/usr/local/lib/python3.11/dist-packages/transformers/training_args.py:1575: FutureWarning: `evaluation_strategy` is deprecated and will be removed in version 4.46 of 🤗 Transformers. Use `eval_strategy` instead
warnings.warn(
🚀 Starting fine-tuning with LoRA (this may take longer)...
[50/50 03:44, Epoch 25/50]
Step Training Loss Validation Loss
10 6.150900 5.066065
20 3.964500 2.904839
30 2.126500 1.420464
40 1.043100 0.748976
50 0.655800 0.601017
Run history:
eval/loss █▅▂▁▁
eval/runtime ▁▂▂█▄
eval/samples_per_second █▇▇▁▅
eval/steps_per_second █▇▇▁▅
train/epoch ▁▁▃▃▅▅▆▆███
train/global_step ▁▁▃▃▅▅▆▆███
train/grad_norm ▇█▆▃▁
train/learning_rate █▆▅▃▁
train/loss █▅▃▁▁
Run summary:
eval/loss 0.60102
eval/runtime 0.91
eval/samples_per_second 9.89
eval/steps_per_second 2.198
total_flos 3445974368256000.0
train/epoch 25
train/global_step 50
train/grad_norm 7.78652
train/learning_rate 0
train/loss 0.6558
train_loss 2.78817
train_runtime 232.9563
train_samples_per_second 7.512
train_steps_per_second 0.215
View run DeepSeek_FineTuning_Experiment at: https://wandb.ai/roboken-akash-/DeepSeek_FineTuning/runs/7b8v92nq
View project at: https://wandb.ai/roboken-akash-/DeepSeek_FineTuning
Synced 5 W&B file(s), 0 media file(s), 0 artifact file(s) and 0 other file(s)
Find logs at: ./wandb/run-20250203_054404-7b8v92nq/logs
✅ Training completed successfully!
📊 トレーニング結果の解析
指標 | 値 |
---|---|
総トレーニングステップ | 50 |
完了エポック数 | 25 / 50 |
最終トレーニング損失 | 0.6558 |
最終バリデーション損失 | 0.6010 |
初期トレーニング損失 | 6.1509 |
初期バリデーション損失 | 5.0660 |
勾配ノルム | 7.78 |
学習率減衰 | あり(最終 LR = 0) |
1秒あたりのサンプル数 | 7.51(トレーニング) / 9.89(評価) |
1秒あたりのステップ数 | 0.215 |
📉 損失曲線の分析
✅ トレーニング損失の推移
- 6.15 → 0.6558 という順調な低下。
- 急激な下降なし = 安定した学習プロセス。
✅ バリデーション損失の推移
- 5.06 → 0.6010 まで減少。
- トレーニング損失との差が小さい ため、過学習(オーバーフィッティング)の兆候なし。
- 学習が進むにつれて一般化性能が向上 したと考えられる。
✅ 勾配ノルム (7.78
) は適正範囲内
- 20 以上 → 勾配爆発のリスク → なし
- 1 以下 → 学習不足の可能性 → なし
- 適正な値(7.78) なので、安定した学習 ができている。
🛠 改善点と次のステップ
🚀 ① エポック数を増やす(50→75 or 100)
- 35 サンプルではデータ量が少ないため、追加のエポックでさらなる改善が期待できる。
-
num_train_epochs=75
または100
で試すのも良い。
🚀 ② データの増強(Data Augmentation)
- データセットが 44 サンプルと少ないため、データを増やすことで性能向上が可能。
- 例えば:
- 入力データのノイズ追加
- 同義語置換によるテキスト拡張
- データ収集を増やす
🚀 ③ 学習率 (learning_rate
) の最適化
- 学習率が 0 まで減衰してしまった ため、学習の進行が止まった可能性がある。
-
低めの初期学習率 (
2e-5
など) で 新しいトレーニングを実施 してみるのも良い。
🚀 ④ モデルの推論をテストする
- 実際にモデルをテストして、期待通りの出力が得られるか確認 する。
- 精度のチェックのため、未学習のデータで評価 するのが重要。
🔥 結論:トレーニングは成功!
✅ 損失値の低下が確認できる
✅ 過学習の兆候なし
✅ 学習率の調整でさらなる改善が可能
✅ データ量が少ないため、追加の学習が有効
ステップ8: ファインチューニング済みモデルの保存
save_path = "/content/drive/My Drive/deepseek_finetuned"
model.save_pretrained(save_path)
tokenizer.save_pretrained(save_path)
print(f"ファインチューニング済みモデルが保存されました: {save_path}")
ステップ9: LoRAをベースモデルと統合
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained(model_name)
model = PeftModel.from_pretrained(base_model, save_path)
model = model.merge_and_unload()
final_save_path = "/content/drive/My Drive/deepseek_finetuned_full"
model.save_pretrained(final_save_path)
tokenizer.save_pretrained(final_save_path)
print(f"統合されたモデルが保存されました: {final_save_path}")
ステップ10: ファインチューニング済みモデルのロード
model = AutoModelForCausalLM.from_pretrained(final_save_path)
tokenizer = AutoTokenizer.from_pretrained(final_save_path)
モデルがデプロイの準備が整いました。
この手順を使えば、LLMを効率的にファインチューニングできます!
🚀 次のステップとして、モデルのテストを試してみませんか? 🔥