大規模言語モデル(LLM)は高性能ですが、そのままファインチューニングすると膨大な計算リソースやストレージを必要とします。例えば、数十億パラメータを持つモデル全体を更新するのは現実的ではありません。
そこで登場するのが LoRA(Low-Rank Adaptation) です。LoRAは「全パラメータを更新する代わりに、低ランク行列を追加して学習」する手法で、以下のような特徴があります。
- 低コスト:更新するパラメータがごく一部に限られる
- 高速:GPUメモリ使用量が大幅に削減される
- 柔軟:複数のLoRAアダプタを組み合わせて利用可能
本記事では、LoRAを使ったファインチューニングの実装を具体的なコードとともに紹介します。
解決策:LoRAの実装手順
1. 環境構築
まずは必要なライブラリをインストールします。
pip install transformers peft datasets accelerate bitsandbytes
- transformers: Hugging Faceのモデル管理
- peft: LoRAを含む効率的ファインチューニングのライブラリ
- datasets: サンプルデータの取得
- accelerate: 分散学習の最適化
- bitsandbytes: 量子化(メモリ削減)に便利
2. モデルとデータの準備
今回はサンプルとして distilbert-base-uncased
をLoRAでファインチューニングします。
from datasets import load_dataset
from transformers import AutoTokenizer
# サンプルデータ:SST-2 (感情分類)
dataset = load_dataset("glue", "sst2")
tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
def tokenize_fn(batch):
return tokenizer(batch["sentence"], truncation=True, padding="max_length", max_length=128)
encoded_dataset = dataset.map(tokenize_fn, batched=True)
3. LoRA構成の設定
PEFTライブラリを用いてLoRAを適用します。
from transformers import AutoModelForSequenceClassification
from peft import LoraConfig, get_peft_model
# 元のモデル
model = AutoModelForSequenceClassification.from_pretrained("distilbert-base-uncased", num_labels=2)
# LoRA設定
lora_config = LoraConfig(
r=8, # 低ランク次元
lora_alpha=16, # スケーリング係数
target_modules=["q_lin", "v_lin"], # LoRAを適用する層
lora_dropout=0.1,
bias="none",
task_type="SEQ_CLS"
)
# LoRAモデルに変換
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
print_trainable_parameters()
を実行すると、更新対象のパラメータが全体の数%に削減されていることが確認できます。
4. トレーニング
LoRAモデルを実際にファインチューニングします。
from transformers import TrainingArguments, Trainer
# データセット分割
train_dataset = encoded_dataset["train"].shuffle(seed=42).select(range(2000))
eval_dataset = encoded_dataset["validation"].shuffle(seed=42).select(range(500))
# 訓練設定
training_args = TrainingArguments(
output_dir="./lora-distilbert",
per_device_train_batch_size=16,
per_device_eval_batch_size=16,
num_train_epochs=3,
evaluation_strategy="epoch",
save_strategy="epoch",
logging_dir="./logs",
logging_steps=50,
learning_rate=5e-4
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
tokenizer=tokenizer
)
trainer.train()
これで、DistilBERTにLoRAを適用した軽量なファインチューニングが可能になります。
5. 学習済みLoRAアダプタの保存と読み込み
LoRAは「アダプタ」として独立保存でき、後から合成・再利用できます。
# 保存
model.save_pretrained("./lora-adapter")
# 再利用
from peft import PeftModel
base_model = AutoModelForSequenceClassification.from_pretrained("distilbert-base-uncased", num_labels=2)
lora_model = PeftModel.from_pretrained(base_model, "./lora-adapter")
6. 推論の実行
ファインチューニング済みのLoRAモデルを使って推論します。
from transformers import pipeline
inference = pipeline("sentiment-analysis", model=lora_model, tokenizer=tokenizer)
print(inference("I really enjoyed this movie!"))
print(inference("This was the worst film I have ever seen."))
まとめ
-
従来の課題
モデル全体のファインチューニングはコストが高い。 -
LoRAの利点
- パラメータ更新がごく一部に限定される
- GPUメモリ消費を大幅に削減
- 複数のアダプタを合成可能(マルチタスク対応)
-
実装手順
- Hugging Faceのモデルを準備
- LoRA設定を定義
- Trainerで学習
- アダプタを保存・再利用
LoRAは、LLMのような大規模モデルを扱う際の強力な武器になります。特に「自分のドメインに合わせたカスタマイズ」を低コストで実現できるため、実務導入のハードルを大幅に下げられるでしょう。