はじめに
近年、LoRA(Low-Rank Adaptation)という手法が注目を集めています。LoRAは大規模言語モデルの重みをほぼ固定したまま少ないパラメータのみを効率的に学習させることで、従来のファインチューニングに比べて計算コストや必要なデータ量を大幅に削減できる点が特徴です。
本記事では、Hugging Faceのtransformersライブラリを利用して、実際にLoRAを使ったファインチューニングを試してみた流れを紹介します。初心者の方にもわかりやすいように、準備から実装、モデルの活用まで解説します。
LoRA(Low-Rank Adaptation)とは?
LoRAは「低ランク行列分解」を用いてモデルの重み更新を効率化する技術です。
- 元のモデルの重みは凍結(固定)
- 低ランクの補助行列のみを学習
- パラメータ数が大幅に減り、ファインチューニングが高速・省リソースで可能
ファインチューニングにLoRAを使うメリット
- 計算コストの大幅削減
- 少量の学習データでも有効
- 複数タスク向けに軽量な適応モデルを複数作成可能
- 大規模モデルのローカル環境での扱いやすさ向上
事前準備
- Google Colabを使用。Googleアカウントを事前に作成する必要あり。
- 学習したアダプタ情報を保存するためのGoogle Driveも準備。以下のコードよりGoogle ColabからGoogle Driveをマウント
from google.colab import drive drive.mount('/content/drive')
!pip install -U transformers datasets peft accelerate
- transformers: Hugging Faceのモデルを扱う
- datasets: データセット用
- peft: Parameter-Efficient Fine-Tuningの実装でLoRAも含む
- accelerate: 分散・高速化ツール
- bitsandbytes: 量子化・低精度演算用
LoRAでファインチューニングする流れ
- モデルのロード
- LoRAを適用し、学習可能パラメータを絞る
- データセットを用意
- トレーニングループまたはTrainer APIで学習
- 保存&推論
実際にやってみる
実施内容
BERTベースのモデルに対してLoRA(Low-Rank Adaptation)を適用してファインチューニングを行う。
使用モデル
bert-base-uncased(Hugging Face Transformersで提供される事前学習済みBERTモデル)
このモデルは、テキスト分類、質問応答、NERなどへのファインチューニング用ベースモデルとして使われる・
データセット準備
- GLUE ベンチマークの MRPC(Microsoft Research Paraphrase Corpus) サブセットを使用
- 2つの文が意味的に同じかどうか(パラフレーズか否か)を2値分類するタスク(ラベルは 0 or 1)
from transformers import AutoModelForSequenceClassification, AutoTokenizer, Trainer, TrainingArguments
from datasets import load_dataset
from peft import get_peft_model, LoraConfig, TaskType
モデルとトークナイザーのロード
model_name = "bert-base-uncased"
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)
tokenizer = AutoTokenizer.from_pretrained(model_name)
LoRA設定
lora_config = LoraConfig(
task_type=TaskType.SEQ_CLS,
inference_mode=False,
r=8,
lora_alpha=32,
lora_dropout=0.1,
)
model = get_peft_model(model, lora_config)
データセットの準備
dataset = load_dataset("glue", "mrpc")
def preprocess(examples):
return tokenizer(examples['sentence1'], examples['sentence2'], truncation=True, padding='max_length')
encoded_dataset = dataset.map(preprocess, batched=True)
モデルの設定と学習
# 学習したモデルファイルの出力先を指定
output_dir = "/content/drive/MyDrive/rola/result"
training_args = TrainingArguments(
output_dir=output_dir, # 出力先
learning_rate=2e-4,
per_device_train_batch_size=16,
per_device_eval_batch_size=16,
num_train_epochs=3,
weight_decay=0.01,
report_to="none"
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=encoded_dataset['train'],
eval_dataset=encoded_dataset['validation'],
)
trainer.train()
モデルの保存
trainer.save_model(output_dir)
tokenizer.save_pretrained(output_dir)
学習結果ディレクトリ内容の説明
この表は、LoRA付きBERTモデルを学習した際に生成された各ファイルとフォルダの意味を説明したものです。
名前 | 説明 |
---|---|
adapter_config.json |
LoRA(PEFT)の設定ファイル。アダプタ層の数、rank、dropout などが記載されており、LoRAモデルの構造を再構成するのに必要です。推論に必須。 |
adapter_model.safetensors |
LoRAの学習済み重み(アダプタ部分)を格納したファイル。.bin より安全かつ高速に読み込み可能。推論に必須。 |
README.md |
モデルの概要、使用方法、著作権などのドキュメント。ユーザー補助目的であり、推論処理には関与しません。 |
special_tokens_map.json |
[CLS] , [SEP] , [PAD] などの特殊トークンをモデルでどう扱うかを定義したファイル。トークナイザーとモデルの整合性を保つために推奨。 |
tokenizer_config.json |
トークナイザーの設定(例:lower casing の有無など)が記載されたファイル。トークナイザー読み込み時に使用されます。推論時に必要。 |
tokenizer.json |
トークナイザー本体の設定と語彙情報を含んだJSONファイル。高速トークナイズを可能にするために使用され、推論で必須。 |
training_args.bin |
Trainer の設定情報を保存したバイナリファイル。再学習や実験記録には有用だが、推論には不要。 |
vocab.txt |
BERT系トークナイザーが使用する語彙リスト。1行に1トークン(単語)が記載されており、tokenizer.json を使わない場合に必要。推論で使用される可能性あり(特に古いモデルやBERT系列)。 |
推論時のロード・活用方法
LoRA適用済みモデルを推論用に読み込むには、以下のように「元モデルに LoRA 重みを注入」します
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from peft import PeftModel, PeftConfig
import torch
# 保存されたモデルディレクトリ
model_dir = "/content/drive/MyDrive/lora/result"
# LoRA設定を読み込み、ベースモデルを復元
peft_config = PeftConfig.from_pretrained(model_dir)
base_model = AutoModelForSequenceClassification.from_pretrained(peft_config.base_model_name_or_path)
model = PeftModel.from_pretrained(base_model, model_dir)
model.eval()
# トークナイザーの読み込み
tokenizer = AutoTokenizer.from_pretrained(model_dir)
# 推論する文のペア
sentence1 = "The company released a new product."
sentence2 = "A new product was launched by the company."
# トークナイズ
inputs = tokenizer(sentence1, sentence2, return_tensors="pt", truncation=True, padding=True)
# 推論
with torch.no_grad():
outputs = model(**inputs)
logits = outputs.logits
predicted_class = torch.argmax(logits, dim=1).item()
print(f"Predicted label: {predicted_class} ({'Paraphrase' if predicted_class == 1 else 'Not Paraphrase'})")
LoRA前とLoRA後の精度比較
from transformers import AutoModelForSequenceClassification, AutoTokenizer, Trainer, TrainingArguments
from peft import PeftModel, PeftConfig
from datasets import load_dataset
import evaluate
import numpy as np
import torch
# --- 評価関数の定義 ---
metric = evaluate.load("glue", "mrpc")
def compute_metrics(eval_pred):
predictions, labels = eval_pred
preds = np.argmax(predictions, axis=1)
return metric.compute(predictions=preds, references=labels)
# --- MRPC データの読み込みと前処理 ---
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
dataset = load_dataset("glue", "mrpc")
def preprocess(examples):
return tokenizer(examples['sentence1'], examples['sentence2'], truncation=True, padding='max_length')
encoded_dataset = dataset.map(preprocess, batched=True)
eval_dataset = encoded_dataset["validation"]
# --- 評価設定 ---
eval_args = TrainingArguments(
output_dir="/content/tmp_eval",
per_device_eval_batch_size=16,
report_to="none"
)
# LoRAモデルの保存場所
lora_model_path = "/content/drive/MyDrive/lora/result"
# PEFT構成を読み込んでベースモデルを用意
peft_config = PeftConfig.from_pretrained(lora_model_path)
base_model_for_lora = AutoModelForSequenceClassification.from_pretrained(peft_config.base_model_name_or_path)
# LoRAモデルを構築
lora_model = PeftModel.from_pretrained(base_model_for_lora, lora_model_path)
lora_model.eval()
# 評価
trainer_lora = Trainer(
model=lora_model,
args=eval_args,
eval_dataset=eval_dataset,
compute_metrics=compute_metrics
)
lora_result = trainer_lora.evaluate()
print("With LoRA:", lora_result)
# ファインチューニングなしの素のBERTモデル
base_model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)
trainer_base = Trainer(
model=base_model,
args=eval_args,
eval_dataset=eval_dataset,
compute_metrics=compute_metrics
)
base_result = trainer_base.evaluate()
print("Without LoRA (Pretrained only):", base_result)
出力(例)
LoRA適用モデル
With LoRA: {'eval_loss': 0.36813414096832275, 'eval_model_preparation_time': 0.0265, 'eval_accuracy': 0.8406862745098039, 'eval_f1': 0.8877374784110535, 'eval_runtime': 13.1101, 'eval_samples_per_second': 31.121, 'eval_steps_per_second': 1.983}
そのままのモデル
Without LoRA (Pretrained only): {'eval_loss': 0.8044962882995605, 'eval_model_preparation_time': 0.0043, 'eval_accuracy': 0.3161764705882353, 'eval_f1': 0.0, 'eval_runtime': 11.6593, 'eval_samples_per_second': 34.994, 'eval_steps_per_second': 2.23}
終わりに
本記事ではLoRAによる省メモリなファインチューニングを、Hugging Faceのエコシステム上で動かす手順を紹介しました。低コストで手軽にモデルをカスタマイズできるため、個人開発でも大規模モデルを活用した機械学習アプリに挑戦しやすくなっています。
今後はタスクごとの応用例や、LoRAモデルのAPI化・サービス組み込み方法なども発信していく予定です。ぜひ皆さんも手元の環境で試してみてください!