モデルを自分の用途に合わせたい、でもファインチューニングは重い。
全パラメータを更新する以上、GPU メモリも保存容量もモデル本体と同じスケールで必要になります。
その重さを避けつつ振る舞いだけを寄せる方法として定着したのが LoRA です。
ここでは LoRA が何をしているのか、なぜ小さな行列で足りるのかを整理してみます。
LoRA とは何をする手法か
LoRA(Low-Rank Adaptation)は、事前学習済みの重みを凍結したまま、各層に小さな行列のペアだけを差し込んで学習する手法です。
2021 年に Microsoft の Edward J. Hu ら 8 名が発表し、論文は ICLR 2022 に採択されました(arXiv:2106.09685)。
ポイントは、元の重み W をいっさい書き換えないこと。
代わりに「W にどれだけ足し引きすれば目的のタスクに合うか」という差分だけを、2 枚の小さな行列 A と B の積として覚えさせます。
学習が終われば、その差分は元の重みへ足し込める仕組みです。
だから推論時に層が増えず、レイテンシが伸びません。
なぜ小さな行列で足りるのか
鍵になるのが「ランク(rank)」という考え方です。
論文の仮説は、ファインチューニングで生じる重みの変化は、行列としては見かけほど自由度が高くないというもの。
d×d の行列をそのまま学習すると d² 個のパラメータが要ります。
しかし変化の本質が r 次元ぶんしかないなら、d×r と r×d の 2 枚に分ければ 2dr 個で表現できるのです。
r を 8 や 16 といった小さい値にすれば、学習対象は桁で落ちるわけです。
コードのうえでは、線形層を差し替えるだけで済みます。
import loralib as lora
# 通常の線形層
# layer = nn.Linear(in_features, out_features)
# ランク r=16 の LoRA 層に差し替える
layer = lora.Linear(in_features, out_features, r=16)
# LoRA のパラメータだけを学習対象にする
lora.mark_only_lora_as_trainable(model)
数字で見るとどれくらい軽いのか
公式リポジトリ(microsoft/LoRA)には比較しやすい実測値が並んでいます。
RoBERTa base では学習対象が 125M から 0.8M へ、DeBERTa XXL では 1.5B から 4.7M へ減りました。
GPT-2 Medium なら 354.92M が 0.35M です。
GPT-3 175B を Adam でフルにファインチューニングした場合と比べると、学習パラメータは 1 万分の 1、GPU メモリ要求は 3 分の 1 になると報告されています。
しかも精度は落ちていません。
GLUE の平均スコアは RoBERTa base のフルチューニングが 86.40、LoRA が 87.24 でした。
保存容量の差はさらに極端で、GPT-2 Medium 用の LoRA チェックポイントは 1.5MB 前後にしかなりません。
タスクごとにモデル本体を丸ごと抱える必要がなくなる、という運用面の効きかたが大きいところです。
実際に触る設定はどこか
論文の実験では、Transformer の q(query)と v(value)の射影にだけ LoRA を当てる構成が基本になっています。
適用先は自由で、埋め込み層や MLP 層に広げても構いません。
最適な配置はモデルとタスクで変わるため、公式リポジトリも「いろいろ試してほしい」と書いています。
もうひとつ知っておきたいのがマージの挙動です。
model.eval() を呼ぶと LoRA の重みが元の重みへ統合され、以降の推論で追加コストがゼロになります。
model.train() に戻せば統合が解除される作りです。
この自動マージが邪魔なときは merge_weights=False を渡して止められます。
チェックポイントに保存されるのも LoRA 部分だけです。
# LoRA のパラメータだけを保存する
torch.save(lora.lora_state_dict(model), checkpoint_path)
# 読み込みは「元のモデル → LoRA」の順、strict=False を忘れない
model.load_state_dict(torch.load('ckpt_pretrained.pt'), strict=False)
model.load_state_dict(torch.load('ckpt_lora.pt'), strict=False)
なお 2023 年 2 月以降は Hugging Face の PEFT ライブラリが LoRA に対応しており、実務ではそちら経由で使う場面が多くなっています。
LoRA で解けない問題もある
LoRA が変えるのは、あくまでモデルの振る舞いです。
社内文書や最新情報のように「知識そのもの」を持ち込みたいなら、検索して渡す RAG のほうが素直に効きます。
推論コストやモデルサイズを下げたいという話であれば、別の軸として用意されているのが量子化です。
「そもそもファインチューニングとは何だったのか」を先に押さえたい場合は、こちらから読むと流れがつながります。
最後に
LoRA を一言でまとめるなら、元の重みは触らず、差分だけを低ランクの行列で覚える手法です。
学習パラメータが 1 万分の 1 になっても精度が保てるのは、変化そのものが低ランクだったから、という仮説がうまく当たった例だと思います。
チェックポイントが数 MB で済むので、タスクごとに差分を切り替える運用とも相性が良いでしょう。
以上です。












コメントを残す