🙆‍♀️

LoRA入門:軽量な大規模言語モデルのファインチューニング手法

に公開

大規模言語モデル(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メモリ消費を大幅に削減
    • 複数のアダプタを合成可能(マルチタスク対応)
  • 実装手順

    1. Hugging Faceのモデルを準備
    2. LoRA設定を定義
    3. Trainerで学習
    4. アダプタを保存・再利用

LoRAは、LLMのような大規模モデルを扱う際の強力な武器になります。特に「自分のドメインに合わせたカスタマイズ」を低コストで実現できるため、実務導入のハードルを大幅に下げられるでしょう。

Discussion