- Published on
UnslothでLLMファインチューニング完全ガイド2025:QLoRA、4bit量子化、2倍高速学習
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- はじめに:なぜUnslothなのか
- 1. LoRA/QLoRA理論(りろん)
- 2. 環境設定(かんきょうせってい)
- 3. Unslothファインチューニング ステップバイステップ
- 4. データ準備(じゅんび)
- 5. 学習設定(がくしゅうせってい)
- 6. VRAM最適化(さいてきか)テクニック
- 7. モデルエクスポートと変換(へんかん)
- 8. 評価(ひょうか)とテスト
- 9. 高度(こうど)なテクニック
- 10. よくある問題(もんだい)と解決法(かいけつほう)
- 11. クイズ
- 12. 参考資料(さんこうしりょう)
はじめに:なぜUnslothなのか
LLMファインチューニングの最大(さいだい)の障壁(しょうへき)は**GPUメモリ(VRAM)**です。Llama 3.1 8BをFull Fine-tuningするには約(やく)60GB VRAMが必要(ひつよう)で、A100 80GB 1台(だい)でもギリギリです。QLoRAがこの問題(もんだい)を解決(かいけつ)しましたが、学習速度(がくしゅうそくど)は依然(いぜん)として遅(おそ)いままでした。
Unslothはこの2つの問題(もんだい)を同時(どうじ)に解決(かいけつ)します:
| 比較項目(ひかくこうもく) | HuggingFace PEFT | Axolotl | Unsloth |
|---|---|---|---|
| 学習速度 | 1x(基準) | 1.1x | 2x |
| メモリ使用量 | 100% | 95% | 40% |
| 設定難易度 | 中程度 | 高い | 低い |
| 対応モデル | 全体 | 全体 | 主要モデル |
| Flash Attention | 別途インストール | 内蔵 | 内蔵 |
| カスタムカーネル | なし | なし | Tritonカーネル |
Unslothの核心(かくしん)の秘密(ひみつ)はカスタムTritonカーネルです。Attention、MLP、Cross-Entropy Lossなどの核心(かくしん)演算(えんざん)をGPU最適化(さいてきか)されたカスタムカーネルに置(お)き換(か)え、2倍高速(こうそく)な学習(がくしゅう)と60%のメモリ節約(せつやく)を実現(じつげん)しています。
対応(たいおう)モデル(2025年(ねん)時点(じてん)):
- Llama 3 / 3.1 / 3.2 (8B, 70B)
- Mistral / Mixtral
- Phi-3 / Phi-3.5
- Qwen 2 / 2.5
- Gemma 2
- Yi
- DeepSeek V2
1. LoRA/QLoRA理論(りろん)
1.1 Full Fine-tuning vs LoRA vs QLoRA
Full Fine-tuning(全パラメータ更新)
+------------------------+
| W (d x d) | <- 全体の重み更新
| 例: 4096 x 4096 | = 16Mパラメータ
| = 64MB (FP16) |
+------------------------+
LoRA (Low-Rank Adaptation)
+------------------------+
| W0(固定)+ B * A |
| W0: 4096 x 4096 | <- 固定(更新なし)
| B: 4096 x 16 | <- 学習 (65Kパラメータ)
| A: 16 x 4096 | <- 学習 (65Kパラメータ)
| = 0.25MB (FP16) | 合計130Kパラメータ
+------------------------+
QLoRA (Quantized LoRA)
+------------------------+
| W0 (4bit) + B * A |
| W0: 4096 x 4096 | <- 4bit量子化 (8MB)
| B: 4096 x 16 | <- FP16学習
| A: 16 x 4096 | <- FP16学習
| = 8.25MB 合計 |
+------------------------+
1.2 Low-Rank Decompositionの原理(げんり)
LoRAの核心(かくしん)アイデアは、重(おも)み更新行列(こうしんぎょうれつ)が実際(じっさい)には低(てい)ランク(low-rank)であるという観察(かんさつ)に基(もと)づいています。
元(もと)の重(おも)み更新(こうしん):
W_new = W_old + delta_W
LoRAはdelta_Wを2つの小(ちい)さな行列(ぎょうれつ)の積(せき)に分解(ぶんかい)します:
delta_W = B * A
ここで:
Bは d x r 行列 (d=モデル次元, r=LoRAランク)
Aは r x d 行列
r << d (例: r=16, d=4096)
パラメータ節約効果(せつやくこうか):
# Full Fine-tuningパラメータ数
d = 4096
full_params = d * d # = 16,777,216 (16.7M)
# LoRAパラメータ数
r = 16
lora_params = d * r + r * d # = 131,072 (131K)
# 節約率
savings = 1 - (lora_params / full_params)
print(f"パラメータ節約: {savings:.2%}") # 99.22%
1.3 4-bit NormalFloat量子化(りょうしか)(NF4)
QLoRAで使用(しよう)するNF4量子化(りょうしか)は一般的(いっぱんてき)な4-bitとは異(こと)なります:
一般的(いっぱんてき)な4-bit INT量子化(りょうしか):
- 均一(きんいつ)に16区間(くかん)に分割(ぶんかつ)
- 値(あたい)の分布(ぶんぷ)を考慮(こうりょ)しない
NF4 (NormalFloat4):
- 重(おも)みが正規分布(せいきぶんぷ)に従(したが)うという事実(じじつ)を活用(かつよう)
- 正規分布(せいきぶんぷ)の分位数(ぶんいすう)に合(あ)わせて16個(こ)の値(あたい)を設定(せってい)
- 情報理論的(じょうほうりろんてき)に最適(さいてき)に近(ちか)い量子化(りょうしか)
1.4 メモリ比較表(ひかくひょう)
| モデル | Full FT (FP16) | LoRA (FP16) | QLoRA (4bit) |
|---|---|---|---|
| Llama 3 8B | 約60GB | 約18GB | 約6GB |
| Llama 3 70B | 約500GB | 約160GB | 約40GB |
| Mistral 7B | 約52GB | 約16GB | 約5GB |
| Phi-3 3.8B | 約28GB | 約9GB | 約3GB |
| Qwen 2 7B | 約52GB | 約16GB | 約5GB |
2. 環境設定(かんきょうせってい)
2.1 GPU要件(ようけん)
| GPU | VRAM | 学習(がくしゅう)可能(かのう)モデル(QLoRA) |
|---|---|---|
| T4 (Colab Free) | 16GB | 7B〜8B (seq_len 1024) |
| A10G | 24GB | 7B〜13B |
| RTX 4090 | 24GB | 7B〜13B |
| A100 40GB | 40GB | 7B〜70B |
| A100 80GB | 80GB | 70B+ |
| Apple M2 Ultra | 192GB | CPU学習(遅い) |
2.2 Google Colabセットアップ
# ColabでのUnslothインストール(T4 GPU基準)
# ランタイム -> ランタイムのタイプを変更 -> T4 GPU選択
# 1. Unslothインストール
!pip install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git"
!pip install --no-deps "xformers<0.0.27" "trl<0.9.0" peft accelerate bitsandbytes
# 2. GPU確認
import torch
print(f"GPU: {torch.cuda.get_device_name(0)}")
print(f"VRAM: {torch.cuda.get_device_properties(0).total_mem / 1024**3:.1f} GB")
2.3 ローカル環境設定(かんきょうせってい)
# Conda環境作成
conda create -n unsloth python=3.11
conda activate unsloth
# PyTorchインストール (CUDA 12.1)
conda install pytorch torchvision torchaudio pytorch-cuda=12.1 -c pytorch -c nvidia
# Unslothインストール
pip install "unsloth[cu121] @ git+https://github.com/unslothai/unsloth.git"
pip install --no-deps trl peft accelerate bitsandbytes
# インストール確認
python -c "from unsloth import FastLanguageModel; print('Unsloth OK')"
3. Unslothファインチューニング ステップバイステップ
3.1 モデルローディング
from unsloth import FastLanguageModel
import torch
# モデルとトークナイザのロード
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="unsloth/Meta-Llama-3.1-8B-bnb-4bit", # 4bit事前量子化モデル
max_seq_length=2048, # 最大シーケンス長
dtype=None, # 自動検出 (A100: bfloat16, その他: float16)
load_in_4bit=True, # 4bit量子化ロード
)
# GPUメモリ確認
gpu_stats = torch.cuda.get_device_properties(0)
start_gpu_memory = round(torch.cuda.max_memory_reserved() / 1024 / 1024 / 1024, 3)
max_memory = round(gpu_stats.total_mem / 1024 / 1024 / 1024, 3)
print(f"GPU = {gpu_stats.name}. Max memory = {max_memory} GB.")
print(f"{start_gpu_memory} GB of memory reserved.")
推奨(すいしょう)事前量子化(りょうしか)モデル:
| 用途(ようと) | モデル | サイズ |
|---|---|---|
| 一般韓国語 | unsloth/Meta-Llama-3.1-8B-bnb-4bit | 約5GB |
| 韓国語特化 | beomi/Llama-3-Open-Ko-8B-bnb-4bit | 約5GB |
| コーディング | unsloth/Mistral-7B-v0.3-bnb-4bit | 約4.5GB |
| 軽量 | unsloth/Phi-3.5-mini-instruct-bnb-4bit | 約2.5GB |
| 多言語 | unsloth/Qwen2.5-7B-bnb-4bit | 約4.5GB |
3.2 LoRAアダプタ設定(せってい)
# LoRAアダプタ追加
model = FastLanguageModel.get_peft_model(
model,
r=16, # LoRAランク (8, 16, 32, 64)
target_modules=[ # LoRAを適用するモジュール
"q_proj", "k_proj", "v_proj", "o_proj", # Attention
"gate_proj", "up_proj", "down_proj", # MLP
],
lora_alpha=16, # LoRA alpha(通常rと同じ)
lora_dropout=0, # Unslothでは0が最適
bias="none", # bias学習なし
use_gradient_checkpointing="unsloth", # Unsloth最適化チェックポインティング
random_state=3407,
use_rslora=False,
loftq_config=None,
)
# 学習可能なパラメータ確認
def print_trainable_parameters(model):
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
total = sum(p.numel() for p in model.parameters())
print(f"学習可能: {trainable:,} / 全体: {total:,} = {trainable/total:.2%}")
print_trainable_parameters(model)
# 学習可能: 41,943,040 / 全体: 8,030,261,248 = 0.52%
LoRAランク選択(せんたく)ガイド:
| LoRA r | パラメータ数(すう) | VRAM追加(ついか) | 推奨用途(すいしょうようと) |
|---|---|---|---|
| 8 | 約21M | 約80MB | 簡単なタスク、VRAM制限 |
| 16 | 約42M | 約160MB | 一般的な推奨値 |
| 32 | 約84M | 約320MB | 複雑なタスク |
| 64 | 約168M | 約640MB | 大規模データ、高い表現力 |
| 128 | 約336M | 約1.3GB | 実験的、Full FTに近い |
4. データ準備(じゅんび)
4.1 Chat Templateフォーマッティング
# Alpacaプロンプトテンプレート
alpaca_prompt = """Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
### Instruction:
{}
### Input:
{}
### Response:
{}"""
# データセットフォーマッティング関数
EOS_TOKEN = tokenizer.eos_token
def formatting_prompts_func(examples):
instructions = examples["instruction"]
inputs = examples["input"]
outputs = examples["output"]
texts = []
for instruction, input_text, output in zip(instructions, inputs, outputs):
text = alpaca_prompt.format(instruction, input_text, output) + EOS_TOKEN
texts.append(text)
return {"text": texts}
4.2 データセットのロードと変換(へんかん)
from datasets import load_dataset
# KoAlpacaデータセットのロード
dataset = load_dataset("beomi/KoAlpaca-v1.1a", split="train")
# フォーマット変換
def format_koalpaca(examples):
texts = []
for instruction, output in zip(examples["instruction"], examples["output"]):
text = alpaca_prompt.format(instruction, "", output) + EOS_TOKEN
texts.append(text)
return {"text": texts}
dataset = dataset.map(format_koalpaca, batched=True)
# OpenAI Messagesフォーマット(Llama 3 chat template使用)
def format_openai_messages(examples):
texts = []
for messages in examples["messages"]:
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=False,
)
texts.append(text)
return {"text": texts}
5. 学習設定(がくしゅうせってい)
5.1 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=2048,
dataset_num_proc=2,
packing=False,
args=TrainingArguments(
# === 基本設定 ===
output_dir="./outputs",
num_train_epochs=3,
# === バッチ & メモリ ===
per_device_train_batch_size=2,
gradient_accumulation_steps=4, # 有効バッチ = 2 * 4 = 8
# === 学習率 ===
learning_rate=2e-4, # QLoRA推奨学習率
lr_scheduler_type="cosine",
warmup_steps=5,
# === 精度 ===
fp16=not is_bfloat16_supported(),
bf16=is_bfloat16_supported(),
# === ロギング ===
logging_steps=1,
logging_dir="./logs",
report_to="wandb",
# === 保存 ===
save_strategy="steps",
save_steps=100,
save_total_limit=3,
# === 最適化 ===
optim="adamw_8bit",
weight_decay=0.01,
max_grad_norm=0.3,
seed=3407,
),
)
5.2 学習率(がくしゅうりつ)ガイド
| シナリオ | 推奨学習率(すいしょうがくしゅうりつ) | 理由(りゆう) |
|---|---|---|
| QLoRA基本 | 2e-4 | QLoRA論文推奨値 |
| 大規模データ (100K+) | 1e-4 | 過学習防止 |
| 小規模データ (1K以下) | 5e-5〜1e-4 | 細かい学習 |
| ドメイン適応 | 2e-5〜5e-5 | 既存知識の保存 |
| Continued Pre-training | 1e-5〜5e-5 | 安定した学習 |
5.3 学習実行(がくしゅうじっこう)
# 学習開始
trainer_stats = trainer.train()
# 学習結果出力
print(f"学習時間: {trainer_stats.metrics['train_runtime']:.2f}秒")
print(f"最終Loss: {trainer_stats.metrics['train_loss']:.4f}")
# GPUメモリ使用量確認
used_memory = round(torch.cuda.max_memory_reserved() / 1024 / 1024 / 1024, 3)
print(f"最大VRAM使用量: {used_memory} GB")
6. VRAM最適化(さいてきか)テクニック
6.1 Gradient Checkpointing
# Unsloth最適化Gradient Checkpointing
model = FastLanguageModel.get_peft_model(
model,
r=16,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"],
use_gradient_checkpointing="unsloth", # 重要!30% VRAM節約
)
# Gradient Checkpointingオプション:
# "unsloth": Unsloth最適化版(より高速でメモリ効率的)
# True: 標準PyTorch gradient checkpointing
# False: 無効(最速だがメモリ最大使用)
6.2 シーケンスパッキング
# 短いシーケンスを1つにまとめてGPU活用率向上
trainer = SFTTrainer(
model=model,
train_dataset=dataset,
packing=True, # シーケンスパッキング有効化
max_seq_length=2048, # パッキング後の全体長
)
# パッキング効果:
# パッキングOFF: [トークンPAD PAD PAD] [トークンPAD PAD PAD]
# パッキングON: [トークンSEPトークントークン] -> GPU活用率向上
6.3 VRAM使用量表(しようりょうひょう)(Unsloth QLoRA基準(きじゅん))
| モデル | Batch=1 | Batch=2 | Batch=4 | Batch=8 |
|---|---|---|---|---|
| Llama 3 8B | 4.2GB | 5.8GB | 8.5GB | 14.2GB |
| Mistral 7B | 3.8GB | 5.2GB | 7.8GB | 13.0GB |
| Phi-3 3.8B | 2.4GB | 3.2GB | 4.8GB | 7.6GB |
| Qwen 2 7B | 3.8GB | 5.2GB | 7.8GB | 13.0GB |
| Llama 3 70B | 36GB | 42GB | 56GB | OOM |
* max_seq_length=2048, gradient_checkpointing="unsloth" 基準(きじゅん)
7. モデルエクスポートと変換(へんかん)
7.1 LoRAアダプタ保存(ほぞん)
# LoRAアダプタのみ保存(小さいサイズ)
model.save_pretrained("lora_adapter")
tokenizer.save_pretrained("lora_adapter")
# 保存ファイル確認
import os
for f in os.listdir("lora_adapter"):
size = os.path.getsize(f"lora_adapter/{f}") / 1024 / 1024
print(f" {f}: {size:.1f} MB")
7.2 アダプタマージ
# LoRAアダプタをベースモデルとマージ
merged_model = model.merge_and_unload()
# マージされたモデルを保存
merged_model.save_pretrained("merged_model")
tokenizer.save_pretrained("merged_model")
7.3 GGUF変換(へんかん)(llama.cpp用(よう))
# Unslothの内蔵GGUF変換機能
# 様々な量子化レベルに対応
# Q4_K_M: 最も一般的(品質/サイズのバランス)
model.save_pretrained_gguf(
"model_gguf",
tokenizer,
quantization_method="q4_k_m",
)
# Q5_K_M: より高品質
model.save_pretrained_gguf(
"model_q5",
tokenizer,
quantization_method="q5_k_m",
)
# Q8_0: 最高品質(サイズ大)
model.save_pretrained_gguf(
"model_q8",
tokenizer,
quantization_method="q8_0",
)
GGUF量子化(りょうしか)比較(ひかく):
| 量子化(りょうしか) | ファイルサイズ(8B) | 品質(ひんしつ) | 推論速度(すいろんそくど) | 推奨(すいしょう) |
|---|---|---|---|---|
| Q4_K_M | 約4.5GB | 良好 | 高速 | 一般使用 |
| Q5_K_M | 約5.5GB | 非常に良好 | 普通 | 品質重視 |
| Q8_0 | 約8.0GB | 優秀 | 低速 | 最高品質 |
| F16 | 約16GB | 原本 | 最も遅い | 参考用 |
7.4 Hugging Face Hubアップロード
# モデルをHugging Face Hubにアップロード
# LoRAアダプタのみアップロード
model.push_to_hub(
"my-org/llama3-8b-korean-lora",
token="hf_xxxxx",
private=True,
)
tokenizer.push_to_hub(
"my-org/llama3-8b-korean-lora",
token="hf_xxxxx",
private=True,
)
# GGUFファイルアップロード
model.push_to_hub_gguf(
"my-org/llama3-8b-korean-gguf",
tokenizer,
quantization_method="q4_k_m",
token="hf_xxxxx",
)
8. 評価(ひょうか)とテスト
8.1 ファインチューニング済(ず)みモデルでの推論(すいろん)
# 推論モードに切り替え
FastLanguageModel.for_inference(model)
# 単一プロンプト推論
def generate_response(instruction, input_text=""):
prompt = alpaca_prompt.format(instruction, input_text, "")
inputs = tokenizer([prompt], return_tensors="pt").to("cuda")
outputs = model.generate(
**inputs,
max_new_tokens=512,
temperature=0.7,
top_p=0.9,
repetition_penalty=1.15,
do_sample=True,
)
response = tokenizer.batch_decode(outputs)[0]
response = response.split("### Response:\n")[-1]
response = response.replace(tokenizer.eos_token, "").strip()
return response
# テスト
test_questions = [
"韓国の伝統的な祝日について説明してください。",
"Pythonでデコレータの動作原理を説明してください。",
"健康的な食習慣のためのアドバイスを教えてください。",
]
for q in test_questions:
print(f"Q: {q}")
print(f"A: {generate_response(q)}")
print("-" * 80)
8.2 生成(せいせい)パラメータチューニング
# 生成パラメータ別の効果
generation_configs = {
"正確な回答 (factual)": {
"temperature": 0.1,
"top_p": 0.9,
"repetition_penalty": 1.0,
},
"創造的な回答 (creative)": {
"temperature": 0.8,
"top_p": 0.95,
"repetition_penalty": 1.15,
},
"バランスの取れた回答 (balanced)": {
"temperature": 0.5,
"top_p": 0.9,
"repetition_penalty": 1.1,
},
}
8.3 lm-eval-harnessベンチマーク
# lm-eval-harnessでベンチマーク評価
pip install lm-eval
lm_eval --model hf \
--model_args pretrained=./merged_model \
--tasks kobest_boolq,kobest_copa,kobest_hellaswag,kobest_sentineg,kobest_wic \
--batch_size 4 \
--output_path ./eval_results
9. 高度(こうど)なテクニック
9.1 マルチGPU学習(がくしゅう)(DeepSpeed ZeRO)
# deepspeed_config.json
"""
{
"zero_optimization": {
"stage": 2,
"offload_optimizer": {
"device": "cpu",
"pin_memory": true
},
"allgather_partitions": true,
"reduce_scatter": true
},
"bf16": {
"enabled": true
},
"train_batch_size": "auto",
"train_micro_batch_size_per_gpu": "auto"
}
"""
# 実行
# deepspeed --num_gpus 4 train.py --deepspeed deepspeed_config.json
9.2 DPO学習(がくしゅう)
from trl import DPOTrainer, DPOConfig
from unsloth import FastLanguageModel, PatchDPOTrainer
# DPOパッチ適用
PatchDPOTrainer()
# DPOデータセット準備
dpo_dataset = load_dataset("argilla/ultrafeedback-binarized-preferences", split="train")
# DPO Trainer設定
dpo_trainer = DPOTrainer(
model=model,
ref_model=None, # Unslothでは None(自動処理)
args=DPOConfig(
output_dir="./dpo_output",
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
learning_rate=5e-7, # DPOは低い学習率
num_train_epochs=1,
beta=0.1, # DPO beta(KL divergence重み)
fp16=not is_bfloat16_supported(),
bf16=is_bfloat16_supported(),
logging_steps=1,
),
train_dataset=dpo_dataset,
tokenizer=tokenizer,
)
dpo_trainer.train()
9.3 Continued Pre-training(ドメイン適応(てきおう))
# ドメイン特化テキストでのContinued Pre-training
from trl import SFTTrainer
domain_dataset = load_dataset("my-org/medical-korean-corpus", split="train")
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=domain_dataset,
dataset_text_field="text",
max_seq_length=4096, # 長いドキュメント
packing=True, # 効率性のためパッキング使用
args=TrainingArguments(
output_dir="./cpt_output",
learning_rate=2e-5, # 非常に低い学習率
num_train_epochs=1, # 1 epochで十分
per_device_train_batch_size=1,
gradient_accumulation_steps=8,
optim="adamw_8bit",
warmup_ratio=0.1,
),
)
trainer.train()
10. よくある問題(もんだい)と解決法(かいけつほう)
10.1 OOM(Out of Memory)エラー
# 症状: CUDA out of memory
# 解決法の順序:
# 1. batch_sizeを減らす
per_device_train_batch_size = 1 # 最小値
# 2. gradient_accumulation_stepsを増やす
gradient_accumulation_steps = 8
# 3. max_seq_lengthを減らす
max_seq_length = 1024 # 2048 -> 1024
# 4. LoRA rankを減らす
r = 8 # 16 -> 8
# 5. gradient checkpointingを確認
use_gradient_checkpointing = "unsloth"
# 6. キャッシュクリア
torch.cuda.empty_cache()
import gc
gc.collect()
10.2 NaN Loss
# 症状: lossがNaNに発散
# 原因: 学習率が高すぎるかデータの問題
# 解決法:
# 1. 学習率を下げる
learning_rate = 1e-5 # 2e-4 -> 1e-5
# 2. max_grad_normを設定
max_grad_norm = 0.3 # gradient clipping
# 3. データ検証
def check_data_issues(dataset, tokenizer):
"""データの問題を検査"""
issues = []
for i, item in enumerate(dataset):
text = item["text"]
if not text.strip():
issues.append(f"[{i}] 空テキスト")
tokens = tokenizer.encode(text)
if len(tokens) > 4096:
issues.append(f"[{i}] テキストが長すぎる: {len(tokens)} tokens")
return issues
10.3 Catastrophic Forgetting(破滅的忘却(はめつてきぼうきゃく))
# 症状: ファインチューニング後に既存知識が消える
# 解決法:
# 1. 低い学習率を使用
learning_rate = 5e-5
# 2. 少ないepoch (1-3)
num_train_epochs = 1
# 3. データに一般知識を混合
# オリジナルデータ 80% + 一般知識データ 20%
# 4. LoRA rankを下げる(変更幅を制限)
r = 8
# 5. 正則化を強化
weight_decay = 0.1
11. クイズ
Q1. LoRAでr=16の場合、元の重みに対して何%のパラメータだけ学習しますか?
正解(せいかい):約(やく)0.5%(99.5%節約(せつやく))
d=4096の場合(ばあい):
- Full: 4096 x 4096 = 16,777,216
- LoRA r=16: (4096 x 16) + (16 x 4096) = 131,072
- 比率(ひりつ): 131,072 / 16,777,216 = 0.78%
実際(じっさい)には複数(ふくすう)のモジュール(q, k, v, o, gate, up, down)に適用(てきよう)するため、総(そう)パラメータ対比(たいひ)約(やく)0.5%レベルです。
Q2. QLoRAのNF4量子化が一般的なINT4より優れている理由は?
正解(せいかい):重(おも)みの正規分布(せいきぶんぷ)特性(とくせい)を活用(かつよう)した最適(さいてき)量子化(りょうしか)
NF4はニューラルネットワークの重(おも)みが概(おおむ)ね正規分布(せいきぶんぷ)に従(したが)うという点(てん)を利用(りよう)します。正規分布(せいきぶんぷ)の分位数(ぶんいすう)に合(あ)わせて16個(こ)の量子化値(りょうしかち)を配置(はいち)するため、均一分割(きんいつぶんかつ)のINT4より情報損失(じょうほうそんしつ)が少(すく)なくなります。
Q3. Unslothが既存のHuggingFace PEFTより2倍高速な核心的理由は?
正解(せいかい):カスタムTritonカーネル
UnslothはAttention、MLP、Cross-Entropy Lossなどの核心演算(かくしんえんざん)をTritonで記述(きじゅつ)したカスタムGPUカーネルに置(お)き換(か)えます。これらのカーネルはメモリアクセスパターンを最適化(さいてきか)し、不要(ふよう)な中間(ちゅうかん)テンソル生成(せいせい)を削減(さくげん)して、2倍高速(こうそく)な学習(がくしゅう)と60%のメモリ節約(せつやく)を達成(たっせい)します。
Q4. Gradient Checkpointingの原理とトレードオフは?
正解(せいかい):
原理(げんり): Forward passで中間(ちゅうかん)活性化値(かっせいかち)(activation)をメモリに保存(ほぞん)せず、Backward passで必要(ひつよう)な時(とき)に再計算(さいけいさん)します。
トレードオフ:
- メリット:VRAM使用量(しようりょう)約(やく)30〜50%削減(さくげん)
- デメリット:再計算(さいけいさん)により学習時間(がくしゅうじかん)約(やく)20〜30%増加(ぞうか)
Unslothのカスタムgradient checkpointingは標準(ひょうじゅん)PyTorch実装(じっそう)より効率的(こうりつてき)で、時間増加(じかんぞうか)が少(すく)ないです。
Q5. GGUF Q4_K_MとQ8_0の違いと、それぞれの推奨使用シナリオは?
正解(せいかい):
Q4_K_M(4-bit Mixed):
- ファイルサイズ:原本(げんぽん)の約(やく)28%(8Bモデル基準(きじゅん)約(やく)4.5GB)
- 品質(ひんしつ):原本(げんぽん)から若干(じゃっかん)の性能低下(せいのうていか)
- 速度(そくど):高速(こうそく)
- 推奨(すいしょう):日常使用(にちじょうしよう)、モバイル/エッジデプロイ、VRAM/RAM制限環境(せいげんかんきょう)
Q8_0(8-bit):
- ファイルサイズ:原本(げんぽん)の約(やく)50%(8Bモデル基準(きじゅん)約(やく)8GB)
- 品質(ひんしつ):原本(げんぽん)に非常(ひじょう)に近(ちか)い
- 速度(そくど):Q4より低速(ていそく)
- 推奨(すいしょう):品質最優先(ひんしつさいゆうせん)、十分(じゅうぶん)なメモリがある環境(かんきょう)、正確(せいかく)な推論(すいろん)が必要(ひつよう)なサービス
12. 参考資料(さんこうしりょう)
- LoRA: Low-Rank Adaptation of Large Language Models - Hu et al., 2021
- QLoRA: Efficient Finetuning of Quantized LLMs - Dettmers et al., 2023
- Unsloth Documentation - github.com/unslothai/unsloth
- PEFT: Parameter-Efficient Fine-Tuning - HuggingFace
- TRL: Transformer Reinforcement Learning - HuggingFace
- Flash Attention 2 - Dao et al., 2023
- LLM.int8(): 8-bit Matrix Multiplication - Dettmers et al., 2022
- llama.cpp - github.com/ggerganov/llama.cpp
- GPTQ: Accurate Post-Training Quantization - Frantar et al., 2022
- DeepSpeed ZeRO - Rajbhandari et al., 2020
- Direct Preference Optimization - Rafailov et al., 2023
- Scaling Data-Constrained Language Models - Muennighoff et al., 2023
- Training Compute-Optimal Large Language Models (Chinchilla) - Hoffmann et al., 2022
- The Llama 3 Herd of Models - Meta AI, 2024