LoRAとは?低ランク更新の仕組み・rank・メモリ・実装を詳しく解説

LoRAの記事概要

LoRA(Low-Rank Adaptation、低ランク適応)は、事前学習済みモデルの重みを固定し、各層に加える更新差分だけを小さな行列で学習するPEFT(Parameter-Efficient Fine-Tuning、少数パラメータだけを更新する追加学習)手法です。学習対象、勾配、optimizer state、タスク別checkpointを小さくできる一方、ベースモデルの読み込みやactivation(順伝播の中間値)は残ります。LoRAを正しく使うには、rankだけでなく、どのmoduleへ挿入するか、更新のscaleをどう決めるか、何を保存・評価するかまで設計する必要があります。

3文要約

LoRAの記事概要

  1. LoRAは元の重み \(W_0\) を圧縮するのではなく、更新差分 \(\Delta W\) を \(BA\) という低ランク積で表します。
  2. \(d\times k\) 行列の更新対象は、rankを \(r\) とすると \(dk\) 個から \(r(d+k)\) 個へ減りますが、ベース重みとactivationのメモリは消えません。
  3. 実運用では、rank、alpha、target modules、学習率、保存するbase revision、merge方法、生成品質の評価を一つの設計として扱います。

LoRAは「何を変え、何を変えない」のか

LoRA原論文は、追加学習で必要な重み更新には低いintrinsic rank(本質的な次元)があるという仮説に基づきます。巨大な重み行列全体を書き換える代わりに、更新を二つの細い行列へ分解します。

凍結したWへ低ランク行列BとAの積を加えるLoRAの図

重要なのは、低ランクにする対象がベースモデルの重みそのものではなく、追加する更新差分だという点です。元の知識を保持したベースモデルはそのまま読み込み、横に小さな学習経路を足します。

項目 LoRAでの扱い 実務上の意味
事前学習済み重み \(W_0\) 凍結 gradientとoptimizer stateを持たない
更新差分 \(\Delta W\) \(BA\) として学習 保存・切替対象を小さくできる
forward \(W_0x\) とadapter出力を加算 未mergeなら追加演算がある
activation 通常どおり保持 sequence length由来のメモリは残る
学習目的 LoRA自体は規定しない SFT、Instruction Tuning、DPOなどと組み合わせる

LoRAは「何を更新するか」を制限する方法であり、「どのlossで学ぶか」を決める方法ではありません。教師ありデータでSFTする場合も、選好pairでDPOする場合も、更新対象としてLoRAを選べます。

更新式を行列の形から理解する

入力 \(x\in\mathbb{R}^{k}\) を重み \(W_0\in\mathbb{R}^{d\times k}\) へ通す線形層を考えます。Full Fine-tuningでは \(W_0\) 自体を更新しますが、LoRAでは \(W_0\) を凍結して次の出力を計算します。

\[h = W_0x + sBAx\]

ここで、二つの学習行列の形は次のとおりです。

\[A\in\mathbb{R}^{r\times k},\qquad B\in\mathbb{R}^{d\times r}\]

したがって、\(A\) は入力を \(k\) 次元からrank \(r\) の小さな空間へ写し、\(B\) はそれを出力側の \(d\) 次元へ戻します。積の形は次になります。

\[BA\in\mathbb{R}^{d\times k}\]

元の重みと同じ形なので、更新差分として加算できます。\(s\) はscaling(更新量を調整する係数)で、原論文の標準形では \(s=\alpha/r\) です。

凍結経路とLoRA adapter経路を並列に流すforward

計算順序としては、巨大な \(BA\) を毎回明示的に作る必要はありません。未mergeの学習中は \(x\) を \(A\)、次に \(B\) へ通し、その結果へscaleを掛けて \(W_0x\) に足します。この構造によって、学習対象を小さく保てます。

パラメータ数はrankと行列形状から計算する

Full Fine-tuningで一つの \(d\times k\) 行列を更新すると、学習対象は \(dk\) 個です。LoRAの学習対象は \(A\) と \(B\) なので、次の数になります。

\[N_{\mathrm{LoRA}} = rk + dr = r(d+k)\]

削減率は次の式で見積もれます。

\[\frac{N_{\mathrm{LoRA}}}{N_{\mathrm{Full}}} =\frac{r(d+k)}{dk}\]

4096×4096、rank 8の例

元の行列が4096×4096なら、Full Fine-tuningの更新対象は16,777,216個です。rank 8では次のようになります。

\[8\times4096 + 4096\times8 = 65{,}536\]

一行列に限れば、元行列の約0.39%です。

更新方法 一行列の学習パラメータ 元行列比
Full Fine-tuning 16,777,216 100%
LoRA rank 8 65,536 約0.39%
LoRA rank 64 524,288 約3.13%

Full更新とLoRA更新のパラメータ数比較

ただし「LoRAなら常に全モデルの0.39%」ではありません。Transformer blockのqueryとvalueだけへ入れる場合と、attentionの全projectionやMLPまで含める場合では、対象行列の個数と形が変わります。全体のtrainable parameterは、対象moduleごとに \(r(d+k)\) を足して求めます。

例えば同じ4096×4096行列へrank 8のLoRAを32層、各層2個ずつ入れる単純化した例では、次の計算になります。

\[65{,}536\times 32\times 2 = 4{,}194{,}304\]

このように、rankだけでなく「何層の何行列へ入れたか」を記録しないと、adapterの規模を再現できません。

初期化時にベースモデルの出力を変えない仕組み

LoRAでは、学習開始時に \(BA=0\) となるよう一方の行列をゼロ初期化します。原論文では \(A\) をrandom Gaussian、\(B\) をゼロで初期化します。これによりstep 0では次が成立します。

\[\Delta W = BA = 0\]

したがって、LoRAを挿入した直後の出力はベースモデルと一致します。ランダムなadapter出力で、学習前からモデルの挙動を壊すのを避けられます。

ここには、最初のbackwardで起きる非対称性があります。\(B=0\) なら、最初は \(B\) には勾配が流れますが、\(A\) の勾配は0になります。\(B\) が更新されて0でなくなると、次のstep以降は両方が学習されます。

LoRAのゼロ出力初期化と最初の勾配

時点 \(A\) \(B\) adapter出力 主な勾配
初期化直後 random 0 0 まず \(B\) に流れる
数step後 更新対象 0ではない 0ではない \(A\)、\(B\) の両方

現在のlibraryには複数の初期化方式があります。初期化名を変えた場合は「LoRA一般の性質」と思い込まず、その方式がstep 0でno-op(出力を変えない状態)か、追加のデータ依存処理を必要とするかを公式文書で確認します。

rankとalphaは何を調整しているのか

rank \(r\) は、更新 \(\Delta W\) が表現できる部分空間の上限を決めます。rankを増やすほど複雑な更新を表現できますが、パラメータ、gradient、optimizer state、保存容量、計算量もほぼ線形に増えます。

alpha \(\alpha\) はadapter出力のscaleを調整します。元のLoRAでは次の係数を使います。

\[s=\frac{\alpha}{r}\]

このため、rankだけを変えてalphaを固定すると、係数 \(\alpha/r\) も変わります。rank、alpha、learning rateは別々の名前を持ちますが、実際の更新量へ相互に影響します。

Rank-Stabilized LoRAの論文は、高rankでのscale挙動を改善するため、次の形を提案しています。

\[s=\frac{\alpha}{\sqrt{r}}\]

これは元のLoRAと同じ規則ではなく、rsLoRAという派生設定です。利用しているlibraryのoptionを有効にした場合だけ適用されます。

LoRAのrankとalphaが容量・scale・コストへ与える影響

設定 大きくしたとき 注意点
rank \(r\) 表現できる更新方向が増える メモリと保存量も増える。高いほど必ず高品質ではない
alpha \(\alpha\) 他条件が同じならadapterのscaleが増える learning rateと合わせて検証する
dropout adapter経路の過学習を抑える可能性 強すぎると学習不足になる
target modules 適応できる場所が広がる 対象数に応じてコストが増える

最適値はモデル、dataset、loss、対象moduleで変わります。「rank 8が標準だから固定する」のではなく、小さな候補集合でvalidation lossと生成品質を比較します。

どのmoduleへLoRAを入れるか

Transformerには、attentionのquery、key、value、output projectionと、MLPのgate、up、down projectionなど複数の大きな線形層があります。LoRA原論文の実験ではattentionの重みを対象とし、多くの設定でqueryとvalueを用いました。ただし、これはすべてのモデル・タスクでの唯一の正解ではありません。

Transformer block内のLoRA target modules候補

対象 長所 トレードオフ
query・value中心 学習対象が小さく、原論文に近い比較を作りやすい MLP側の適応力は増えない
attention全projection attention全体へ変更を入れられる adapter数と計算が増える
attention+MLP 広い表現を適応できる memory、保存量、過学習リスクが増える
all-linear指定 architecture差を吸収しやすい実装もある 出力headなど意図しない層を含まないか確認が必要

Hugging Face PEFTのLoRA設定には、target_modules、exclude_modules、modules_to_save、層ごとのrankやalpha指定などがあります。層名はモデル実装ごとに異なるため、文字列を設定しただけで安心せず、次を確認します。

  • LoRAが挿入されたmodule名
  • trainable parameter数と全parameterに占める比率
  • 各adapter parameterに勾配が存在するか
  • 出力headやembeddingも更新したい場合、保存対象に含まれるか

対象名が一致せずLoRAが一つも挿入されない事故と、広すぎる指定で意図しない層まで学習する事故は、どちらも起こり得ます。

LoRAで減るメモリと減らないメモリ

「学習パラメータが1%未満なら、必要VRAMも1%未満になる」という理解は誤りです。学習時メモリは複数の内訳に分かれます。

LoRAで減るメモリと残るメモリの内訳

メモリ要素 LoRAでの変化 理由
ベース重み 原則残る forwardに全層が必要
LoRA重み 追加されるが小さい \(r(d+k)\) 個
gradient 大幅に減る 凍結重みには通常保持しない
optimizer state 大幅に減る 学習対象parameter分だけ持つ
activation 基本的に残る adapterへgradientを流すためforward中間値が必要
attention一時領域 基本的に残る sequence lengthやkernelに依存

LoRAが特に効くのは、全parameterのgradientとAdam系optimizerのmomentを持たずに済む部分です。一方、長いcontextでactivationやattentionが支配的なら、rankを下げるだけではOut Of Memoryを解消できないことがあります。その場合は、micro batch、gradient accumulation、gradient checkpointing、sequence length、attention実装、量子化を別軸で検討します。

LoRA原論文はGPT-3 175Bの特定設定で、trainable parameterを最大10,000分の1、GPU memoryを約3分の1に減らしたと報告しています。これはその実験条件の結果であり、任意のモデルで同じ倍率になる保証ではありません。

一つのbatchが通る処理

LoRAを使った学習も、dataset、tokenization、loss、optimizerという基本構造は通常のFine-tuningと同じです。違うのは、backward後に更新されるparameterの集合です。

  1. 入力をtokenizeし、batchを作る。
  2. 各対象層で \(W_0x\) と \((\alpha/r)BAx\) を足す。
  3. SFTやDPOなど、選んだ目的関数でlossを計算する。
  4. backwardでLoRA行列や明示した追加moduleへ勾配を流す。
  5. optimizerが学習対象parameterだけを更新する。
  6. validationではadapterを有効にした生成も評価する。

凍結したベース重みへgradientを保存しないことと、入力までのgradient経路が不要になることは同じではありません。LoRAの \(A\)、\(B\) を更新するには、その入力activationを使って勾配を計算します。

実装前に固定する設定

再現可能なLoRA学習では、parameter数だけでなく次の設定を保存します。

分類 記録する項目 なぜ必要か
ベース model ID、revision、dtype 同名modelでも重みが変わり得る
tokenizer revision、special tokens、chat template 入力列とlabel位置が変わる
adapter rank、alpha、dropout、初期化 更新容量とscaleが変わる
挿入先 target/exclude modules、対象層 parameter数と適応範囲が変わる
追加学習 bias、modules to save adapter外に更新対象があるか決まる
optimization learning rate、scheduler、batch、step数 学習の軌跡が変わる
data dataset revision、split、最大長 比較可能性とleak防止に必要

設定後は、学習開始前にtrainable parameterを列挙し、期待値と照合します。例えば出力headを追加学習したのにadapterだけを保存すると、再読み込み後にその更新が失われます。逆にbiasまで学習した場合は、adapterを無効にしても元のベース出力と完全には一致しないことがあります。

最小の確認項目

  • trainable parameterが0でない
  • 対象moduleの名前と個数が想定どおり
  • 全parameterに対するtrainable比率が計算と一致
  • step 0で初期化方式どおりの出力になる
  • 一つのbatchでLoRA parameterに有限なgradientが入る
  • save後に別processでloadし、同じ入力のlogitsを比較できる

adapterを保存・読み込むときの落とし穴

adapter fileは単独で完全なモデルではありません。少なくとも、対応するベースモデル、LoRA設定、必要なら追加保存moduleと組み合わせて初めて同じ挙動を再現できます。

LoRA adapterの保存・読込・切替・mergeのライフサイクル

運用単位として、次をひとまとめに管理します。

  • adapterの重みと設定
  • base modelの正確なIDとrevision
  • tokenizer、special token、chat template
  • 学習時のprompt形式
  • libraryと主要dependencyのversion
  • 評価結果と想定用途

base revisionやtokenizerが違うと、shape errorで即座に失敗する場合だけでなく、読み込めても出力品質が変わる場合があります。adapterが小さいことと、互換性管理が不要なことは同じではありません。

複数adapterを一つのbaseへ切り替えて使う構成は、LoRAの大きな運用上の利点です。ただし同時適用や線形合成は、それぞれを単独評価した結果をそのまま保証しません。更新方向が干渉する可能性があるため、合成後を別モデルとして評価します。

mergeすると何が起きるか

古典的なLoRAでは、推論前に次の重みを計算できます。

\[W_{\mathrm{merged}} = W_0 + sBA\]

これを通常の線形層として保存すれば、推論時にadapter経路を別計算する必要がなくなります。原論文が述べる「追加の推論latencyなし」は、このように重みへmergeできる構成を指します。

ただし、merge前に次を確認します。

条件 注意点
浮動小数点base merge前後の誤差許容値を決め、logitsと生成を比較する
量子化base dequantize、加算、再量子化で丸め誤差やmemory増加が起き得る
複数adapter 適用順とweightを記録し、合成品質を再評価する
運用で切替が必要 mergeするとadapter単独切替の利点が薄れる
配布 base modelのlicenseと配布条件も確認する

merge済みmodelだけを残すと、元adapterへの分離や別baseへの差し替えが難しくなります。少なくとも検証が終わるまでは、元のbase指定とadapter artifactを保持します。

よくある失敗と切り分け方

lossがほとんど変わらない

まずtrainable parameter数、対象module、gradient normを確認します。target名の不一致、全parameterのfreeze、label maskの誤り、学習率が小さすぎる問題を、rank不足より先に切り分けます。

train lossだけ急速に下がる

datasetが小さい、重複が多い、rankや対象moduleが大きすぎる、学習stepが長すぎる可能性があります。prompt単位の重複除去とvalidation分割を確認し、生成例を固定して過学習を追います。

adapterを再読み込みすると品質が変わる

base revision、tokenizer、chat template、dtype、adapterの有効状態、追加保存moduleを比較します。train modeとeval modeでdropoutの状態が違うことも確認します。

merge後に出力が一致しない

scale、dtype、transposeの規約、fan-in/fan-out形式、量子化の有無を確認します。完全なbit一致を前提にせず、数値誤差の許容範囲と生成品質の両方で判定します。

rankを上げたのに良くならない

rankは容量の上限であり、データ品質や目的関数を改善しません。target modules、学習率、data coverage、validation設計がボトルネックなら、高rankはparameterと過学習リスクだけを増やします。

Full Fine-tuning・LoRA・QLoRAの使い分け

QLoRAはLoRAと別のlossではありません。QLoRA論文が示すのは、凍結したbase weightを主に4-bitで保持し、その重みを使った計算を通じてLoRA adapterを学習する構成です。

比較軸 Full Fine-tuning LoRA QLoRA
base weight 更新 凍結、通常は浮動小数点 凍結、主に4-bit保存
学習対象 原則全parameter LoRA adapter LoRA adapter
gradient・optimizer 大きい 小さい 小さい
base weight memory 大きい 残る 量子化で小さくする
activation memory 残る 残る 残る
表現力 制約が最も少ない rankと対象層に制約 LoRA制約と量子化影響を評価
推論運用 model全体を配布 adapter切替やmerge 量子化対応runtimeとmerge条件を確認

選択の目安は次のとおりです。

  • 最大限の更新自由度が必要で、計算資源とmodel全体の保存を許容できるならFull Fine-tuning
  • base modelは載るがoptimizer stateまで含めたFull FTが重いならLoRA
  • base model自体を通常精度で載せるのが難しいならQLoRA

QLoRAのNF4、Double Quantization、Paged Optimizerは次の記事で詳しく扱います。LoRAの記事では「baseの保存精度」と「学習対象」を混同しないことが重要です。

評価はadapterのlossだけで終わらせない

LoRAの学習が成功したかは、train lossだけでは判断できません。少なくとも次の層に分けます。

  1. 実装検証: 対象module、trainable数、gradient、save/load、merge前後を確認する。
  2. 学習診断: train/validation loss、gradient norm、過学習、長さ別性能を追う。
  3. タスク評価: 未見promptで正解率、形式遵守、品質rubricを測る。
  4. 回帰評価: ベースモデルが持っていた一般能力、安全性、多言語性能の低下を測る。
  5. 運用評価: latency、throughput、VRAM、adapter切替、再現性を確認する。

比較では、base、LoRA候補、必要ならFull FTやQLoRAを、同じvalidation splitと生成設定で評価します。rankやtargetを変えた実験ではtrainable parameter数も併記し、品質改善がコスト増に見合うか判断します。

実験軸 最小候補 見る指標
rank 小・中・大 品質、過学習、adapter size
target modules q/v、attention全体、attention+MLP 品質とtrainable数
alpha alpha/rが異なる複数候補 update norm、収束、生成品質
learning rate 低・中・高 loss、発散、回帰
merge 未merge・merge logits差、生成、latency

実装・公開前チェックリスト

  • [ ] LoRAが低ランク化するのは更新差分であると説明できる
  • [ ] 行列形状からtrainable parameter数を計算した
  • [ ] rank、alpha、scaling規則をセットで記録した
  • [ ] target modulesと実際の挿入先を照合した
  • [ ] base modelとactivationを含むmemory内訳を見積もった
  • [ ] base revision、tokenizer、chat templateを固定した
  • [ ] adapter外に学習したmoduleも保存した
  • [ ] save/load後の出力を別processで検証した
  • [ ] merge前後と量子化時の差を評価した
  • [ ] validationと回帰評価をtrain lossから分けた

まとめ

LoRAは、巨大な重み行列を直接更新せず、更新差分を \(BA\) という低ランク積で学習する手法です。これにより、gradient、optimizer state、タスク別checkpointを大幅に小さくできます。

一方で、ベース重み、activation、attentionの一時メモリは残ります。実際の性能とコストは、rankだけでなく、alpha、scaling、target modules、dataset、loss、学習率によって決まります。

LoRAを「小さいadapterを付けるだけ」と考えず、更新範囲、memory内訳、保存互換性、merge、評価まで含む設計として扱うと、学習できたのに再現できない、VRAMが想定ほど減らない、rankを上げても品質が伸びないといった失敗を避けやすくなります。

次に読むべき記事

次回はQLoRAを扱います。凍結したbase modelを4-bitで保持しながらLoRAを学習する仕組みを、NF4、Double Quantization、Paged Optimizerに分けて説明します。

参考資料

コメント

タイトルとURLをコピーしました