LoRAとQLoRA: LLMのためのパラメータ効率的Fine-Tuning

スポンサーリンク

LoRAとQLoRA: LLMのためのパラメータ効率的Fine-Tuning

  • 4-bit量子化
  • Fine-Tuning最適化
  • GPU最適化
  • LoRA
  • Low-Rank Adaptation
  • メモリ効率
  • モデルアダプター
  • Parameter-Efficient Fine-Tuning
  • PEFT手法
  • QLoRA
  • Wasif Mehmood
  • Ready Tensor
  • Mohamed Abdelhamid

🏠
ホーム – 全レッスン

⬅️
前へ – 量子化とDouble量子化

➡️
次へ – Week 2 Knowledge Check

では実践的なブレークスルーです:データセンター規模の予算なしで、7Bや70Bパラメータのモデルを実際にどうやってfine-tuningするのでしょうか?

このレッスンでは、大規模fine-tuningを身近にした技術であるLoRAとQLoRAを紹介します。両アプローチの背後にあるメカニズム、それぞれをいつ使うべきか、そしてハードウェアの制約に応じてどう設定するかを学びます。

最終的に、モデルのパラメータのわずか0.1%を更新するだけで、完全なfine-tuningのパフォーマンスに匹敵する理由と、GPUのメモリにすら収まらないモデルをfine-tuningする方法を理解できるようになります。

アクセシビリティの問題

こんな状況を想像してください:あなたは強力な7Bモデル(LLaMAやMistralのようなもの)を持っていて、タスクに合わせてfine-tuningする準備ができています。fine-tuningがパフォーマンスを向上させることは分かっていますが、課題があります:予算が限られており、7Bパラメータのトレーニングは簡単なことではありません。

従来のfine-tuningでは、すべてのパラメータを更新する必要があります。つまり、重み、勾配、オプティマイザの状態を保存する必要があります。7Bモデルの場合、16ビット精度で重みを保存するだけで14GBのVRAMが必要です。しかし待ってください—勾配とオプティマイザの状態を加えると、合計メモリ消費量は40〜50GBになります。これはA100のような最上位GPUでは可能ですが、スケールアップするとどうなるでしょうか?

さて、70Bモデルのトレーニングを想像してみてください。16ビット精度では、重みだけで約140GBのメモリが必要で、トレーニングのオーバーヘッドを含めると300GB以上になります。予算があったとしても、このような規模のハードウェアは個人や小規模チームには手が届きません。

ここでの本当の問題は、コストだけでなくアクセシビリティでした。大手テック企業だけがこれらの巨大なモデルをfine-tuningするリソースを持っている場合、AIコミュニティの他のメンバーはどうやって実験し、反復し、限界を押し広げることができるのでしょうか?

ここでParameter-Efficient Fine-Tuning (PEFT)手法がすべてを変えました。PEFTを使えば、大企業の予算がなくても大規模モデルをfine-tuningでき、これらのモデルをすべての人にとってよりアクセス可能にします。

PEFTメソッドのメカニクスに飛び込みましょう。

Parameter-Efficient Fine-Tuningとは何か?

PEFTの天才的な点は非常にシンプルでありながら強力です:モデルをfine-tuningするためにすべてのパラメータを更新する必要はありません。

このように考えてみてください:事前学習済みモデルをfine-tuningする際、ドメイン固有の情報でモデルをガイドしています—タスクに合わせて言語を調整したり、トーンを微調整したり、専門用語を教えたりしているのです。しかし、覚えておいてください、モデルはすでに言語構造や推論パターンなどの基本を知っています。ゼロから始めているわけではありません;あなたのニーズに向けてモデルを微調整しているのです。

PEFTは元のモデルの重みを凍結し、小さな学習可能なアダプターを追加することで機能します。これらのアダプターはタスク固有の調整だけを学習し、モデルのコア知識はそのまま保たれます。数十億のパラメータを更新する代わりに、ほんのわずかな割合—時にはモデルの全パラメータのわずか0.1%—だけを訓練します。

LoRAとQLoRAは最も人気のある2つのPEFT手法であり、今日はこれらに焦点を当てます。

🎥 問題解決者のように考える:PEFT手法のブレインストーミング

このビデオでは、第一原理からパラメータ効率的fine-tuningにアプローチすることで、問題解決の直感を養います。技術を暗記するのではなく、研究者のようにソリューションをブレインストーミングし評価する方法を学びます。

LoRAの紹介:Low-Rank Adaptation

LoRA (Low-Rank Adaptation of Large Language Models) は、Microsoftの研究者による2021年の論文で紹介され、fine-tuningについての考え方を根本的に変えました。

fine-tuning中に重み行列全体を更新する代わりに、LoRAは巧妙なことをします。元の重みを凍結したまま、重み更新の低ランク分解を学習します。

実際にこれが何を意味するか見てみましょう。

モデル内に重み行列Wがあるとしましょう—おそらくアテンション層のクエリ投影です。
通常、fine-tuning中にこの行列全体を更新します。しかしLoRAはより効率的なアプローチを導入します:Wを直接変更する代わりに、Wを凍結したままにし、ΔWと呼ばれる更新行列を学習します。これは新しいタスクのためにWに適用したい変更を表します。

ΔWはWと同じ形状(m × n)ですが、そのすべてのパラメータを訓練する代わりに、LoRAはこれを2つのはるかに小さい行列AとBの積としてパラメータ化します:

  • A: m × r
  • B: r × n

ここでrは分解のランクであり、r << min(m, n)です。これはメモリとパフォーマンスのトレードオフを制御するために調整できる重要なハイパーパラメータです。このハイパーパラメータについては後ほど詳しく説明します。

トレーニング中、LoRAはAとBのみを学習します。掛け合わせると(A × B)、Wと同じ形状の行列が得られます。AとBはΔWの低ランク近似を形成し、凍結されたベース重みに対するタスク固有の調整を捉えます。

推論中、モデルは次のように計算します:

これは、事前学習済みの重みWはそのまま保たれ、アダプターが新しいタスクにモデルを特化させる小さく効率的な修正を適用することを意味します。

例を使って説明しましょう:

  • WがM × n行列(例:5000 × 4000)であるとすると、行列は2000万パラメータを持ちます。
  • ΔWの2000万すべてを訓練する代わりに、LoRAはアダプター行列のみを訓練します。r = 8を選択するとします:
    A (5000 × 8) = 40,000パラメータ
    B (8 × 4000) = 32,000パラメータ
  • A (5000 × 8) = 40,000パラメータ
  • B (8 × 4000) = 32,000パラメータ

これは、この1つの層で更新する必要があるパラメータ数の99.6%削減です。

この大幅な削減により、はるかに少ないメモリと計算能力で大規模モデルをfine-tuningできます。

🎥 LoRAが訓練可能パラメータを削減する方法

このビデオでは、LoRAがモデルの重みをどのように分解するか、レイヤーごとのパラメータ削減を示し、Hugging Face PEFTライブラリを使用して実際のLLaMA 3モデルにLoRAを適用する方法を説明します。

しかしトレードオフはないのか?

この時点で、あなたは考えているかもしれません:「すごい、fine-tuningから多くのパラメータを節約できた—何か落とし穴があるに違いない、そうでしょう?」

正解です – タダ飯はありません。

LoRAの効率性は、完全なfine-tuningと比較してわずかなパフォーマンス低下を伴います。

しかし、研究によると、このギャップは驚くほど小さく、特にメモリ節約と計算効率の大きな利点を考えると小さいです。

実際には、品質の損失は無視できるほど小さく、特に指示追従やドメイン適応などのタスクでは顕著です。巨大なモデル—7B、13B、または70Bパラメータ—でも、控えめなハードウェアでfine-tuningでき、ほぼ完全精度のパフォーマンスを維持できます。

結論:LoRAを使用すると、膨大なモデルを効率的かつ効果的にfine-tuningでき、リソースのごく一部でほぼ同じ結果を達成できます。

🎥 LoRAハイパーパラメータの設定

このビデオでは、メモリ制約とfine-tuning目標に基づいて、3つの主要なLoRAハイパーパラメータ—ランク、アルファ、ターゲットモジュール—を設定する方法を学びます。

LoRAハイパーパラメータ:制御する「ノブ」

では、特定のタスクに対してLoRAをどのように機能させるのでしょうか?ここでハイパーパラメータを調整します。

  • ランク (r) — 適応のサイズ。
  • スケーリング係数 (α) — 適応の強度。
  • ターゲットモジュール — LoRAを適用するモデルの部分。
  • Loraドロップアウト — LoRA層の正規化。
  1. ランク (r) – 「容量」ノブ:
    これはアダプターがどれだけ学習できるかを制御します。これは前述の本にどれだけの付箋を使えるかを決めるようなものです。64や128のような高いランクはより多くの学習容量を与えますが、より多くのメモリを使用し、小さなデータセットで過学習する可能性があります。4や8のような低いランクはメモリを少なく使用し、より速く訓練しますが、タスクに必要な複雑な変更を捉えられない可能性があります。最適な点はr = 8で、ほとんどのタスクでうまく機能し、人気のあるデフォルトになっています。

  2. アルファ (α) – 「ボリューム」ノブ:
    これは元のモデルと比較してアダプターがどれだけ「大声で話す」かを制御します。これは付箋の修正のボリュームを調整するようなものです。低く設定しすぎるとアダプターがモデルにほとんど影響を与えません(ささやくように)。高く設定しすぎるとアダプターが元のモデルを圧倒します(叫ぶように)。経験則は、α = 2 × rに設定することなので、r = 8の場合、α = 16を使用します。

  3. ターゲットモジュール – 「どこ」の選択:

LoRAはモデル全体を適応させるわけではありません—どの層をターゲットにするかを選択します。

トランスフォーマーモデルでは、各アテンションブロックに4つの重み行列が含まれます:

  • Query (Wq): 入力をクエリベクトルに投影
  • Key (Wk): 入力をキーベクトルに投影
  • Value (Wv): 入力をバリューベクトルに投影
  • Output (Wo): アテンション出力をモデル次元に投影し戻す

最も一般的な設定:q_projとv_proj(QueryとValue)をターゲットにします。これにより、最小限のメモリオーバーヘッドで優れた適応力が得られます。

より多くの容量のために:k_projとo_proj(KeyとOutput)を追加します。これにより訓練可能なパラメータがおよそ2倍になりますが、困難なタスクにより多くの適応力を提供します。

アテンションを超えて:フィードフォワード層(LLaMAスタイルモデルのgate_proj、up_proj、down_proj)もターゲットにできます。これはあまり一般的ではありませんが、モデルの出力スタイルだけでなく知識ベースの変更を必要とするドメインシフトに役立ちます。

実用的な戦略:非常に高いランクを少数のモジュールに使用するよりも、中程度のランクを複数のモジュールに分散させます。例えば、4つのモジュールでr = 8は、類似のパラメータ予算を使用しながら、1つのモジュールでr = 32よりも典型的に優れたパフォーマンスを発揮します。

  1. Loraドロップアウト – 正規化装置:
    Loraドロップアウトは、特に小さいまたは狭いデータセットで訓練する際に、fine-tuning中の過学習を防ぐためにLoRA層内で使用される正規化パラメータです。
    Loraドロップアウトの機能:LoRAアダプター(低ランク更新)を適用する前に、ドロップアウトはLoRAパスを流れる活性化の一部をランダムにゼロにします;したがって、訓練中にアダプターの有効容量を減らし、訓練データに過学習するのではなく、より堅牢で一般化された表現を学習するよう促します。
    一般的な値:
    0.0 → ドロップアウトなし(高速だが過学習のリスク)
    0.05–0.1 → ほとんどのセットアップに適したデフォルト
    > 0.1 → データセットが非常に小さいか過学習が明確な場合のみ

LoRAのハイパーパラメータの選択

ほとんどのfine-tuningタスクで機能する信頼性の高い設定は次のとおりです:

  • ランク (r): 8
  • アルファ (α): 16
  • ターゲットモジュール: q_proj, v_proj
  • ドロップアウト: 正規化のために0.05または0.1

この設定は7Bモデルで約400〜800万パラメータを訓練し、最小限のメモリを使用し、幅広いタスクで優れた結果を達成します。

そこから特定のニーズに基づいて調整します:

  • モデルが不適合?ランクを16に増やすか、より多くのターゲットモジュールを追加
  • メモリ不足?ランクを4に減らすか、より少ないモジュールをターゲットに
  • 小さなデータセット(< 1000例)?過学習を避けるためにランクを低く保つ
  • 大きなドメインシフト?ランク、アルファを増やし、より多くのモジュールをターゲットに
  • 破壊的忘却(一般的パフォーマンスの劣化)?一般化を保つためにランクとアルファを下げる。

QLoRA:Quantized Low-Rank Adaptation

LoRAは訓練可能なパラメータの問題を解決しましたが、1つのボトルネックが残りました:凍結されたベースモデルは依然として完全な精度でGPUメモリに収まる必要がありました。

16ビット精度の7Bモデルは約14GBのVRAMを必要とします。これは現代のGPUで管理可能です。しかし13Bモデルは約26GB、33Bモデルは約66GB、70Bモデルは約140GBを必要とします。LoRAの効率性があっても、ほとんどの実務者は7Bパラメータを超えるモデルをfine-tuningできませんでした。

2023年に導入されたQLoRAは、LoRAアプローチに量子化を追加することでこれを解決しました。

1. NF4 (NormalFloat-4) 量子化

標準的な量子化スキームは、利用可能なビン全体に値を均等に分散させます。しかし、ニューラルネットワークの重みは均等に分散されていません—ほぼ正規分布でゼロの周りに集中しています。

NF4は、正規分布データ専用に設計された量子化スキームです。ゼロ付近(ほとんどの重みが存在する場所)により多くのビンを割り当て、裾にはより少ないビンを割り当てます。これにより、ナイーブな4ビット量子化よりもモデルの品質がはるかに良く保たれます。

前のレッスンでデータ型について取り上げたので、float16、bfloat16、int8、int4の違いはすでに知っています。NF4は、ニューラルネットワークの重みのための最適な4ビット表現として特別に設計されています。

2. Double量子化

重みを4ビットに量子化した後でも、量子化定数自体からのメモリオーバーヘッドがあります。

重みのブロックを量子化するとき、そのブロックのスケーリング係数とゼロ点を保存します。大規模モデルの場合、これらの定数は数百メガバイトに達する可能性があります。

Double量子化は、これらの量子化定数自体を量子化し、通常は8ビットにします。これにより、モデルの品質に影響を与えることなく、オーバーヘッドがさらに約30%削減されます。

3. ページドオプティマイザ

トレーニング中、オプティマイザの状態(Adamのモーメンタムと分散など)は、特に大きなバッチサイズや長いシーケンスで、一時的なメモリスパイクを引き起こす可能性があります。

ページドオプティマイザは、必要に応じてオプティマイザの状態をGPUとCPUメモリ間で自動的に移動することでこれを解決します。GPUメモリの圧力が高まると、頻繁に使用されないオプティマイザの状態がCPU RAMにページアウトされます。再び必要になると、ページインされます。

これはバックグラウンドで透過的に行われ、メモリ不足にならずにより大きな有効バッチサイズで訓練できます。

QLoRAトレーニングの仕組み

QLoRAトレーニング中、次のことが起こります:

  • ベースモデルの重みは4ビットNF4形式でディスクに保存されます
  • 層が計算に必要な場合、その重みがロードされてbfloat16に逆量子化されます
  • 順伝播と逆伝播は数値安定性のためにbfloat16で行われます
  • LoRAアダプターのみが勾配を蓄積し更新されます
  • ページドオプティマイザがメモリを自動的に管理します

結果:ほぼ完全精度の品質を維持しながら、48GB未満のVRAMで70Bパラメータモデルをfine-tuningできます。

LoRA vs QLoRA:それぞれをいつ使うか

LoRAとQLoRAの間の決定は簡単です:ベースモデルがGPUメモリに収まるかどうかにかかっています。

LoRAを使用する場合:

  • ベースモデルがGPUメモリに快適に収まる(例:24GB GPUで7Bモデル、40GB GPUで13B)
  • 最速の反復で最もシンプルなセットアップが必要
  • 頻繁にfine-tuningし、最小限のオーバーヘッドが必要

QLoRAを使用する場合:

  • ベースモデルが完全精度でGPUメモリに収まらない
  • コンシューマーハードウェアで33B、65B、または70Bモデルをfine-tuningしたい
  • 大幅なメモリ節約のために約10-20%遅いトレーニングを受け入れる意思がある

実用的なリファレンスは次のとおりです:

結論:収まるならLoRAを使用。収まらないならQLoRAを使用。どちらも優れた結果をもたらします。

実装:コード例

Hugging Faceのライブラリを使用して両方のアプローチを実装する方法を見てみましょう。

LoRA実装

from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM, AutoTokenizer

# モデルとトークナイザーを通常通りロード
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")

# LoRAを設定
lora_config = LoraConfig(
 r=8, # ランク
 lora_alpha=16, # スケーリング係数 (2 × r)
 target_modules=["q_proj", "v_proj"], # 適応する層
 lora_dropout=0.05, # 正規化のためのドロップアウト
 bias="none", # バイアス項を適応しない
 task_type="CAUSAL_LM" # タスクタイプを指定
)

# モデルにLoRAアダプターを適用
model = get_peft_model(model, lora_config)

# 訓練可能なパラメータを確認
model.print_trainable_parameters()
# 出力: trainable params: 4,194,304 || all params: 6,742,609,920 || trainable%: 0.0622

以上です。モデルは標準的なトレーニングループで訓練する準備ができました。

QLoRA実装

次に、さらに大きなメモリ効率のためにQLoRAを適用する方法を見てみましょう。

from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
import torch

# 4ビット量子化を設定
bnb_config = BitsAndBytesConfig(
 load_in_4bit=True, # 4ビットロードを有効化
 bnb_4bit_quant_type="nf4", # NF4量子化を使用
 bnb_4bit_compute_dtype=torch.bfloat16, # bfloat16で計算
 bnb_4bit_use_double_quant=True, # double量子化を有効化
)

# 量子化してモデルをロード
model = AutoModelForCausalLM.from_pretrained(
 "meta-llama/Llama-2-7b-hf",
 quantization_config=bnb_config,
 device_map="auto", # GPU間で自動的に分散
 trust_remote_code=True
)

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")

# トレーニング用にモデルを準備(勾配チェックポイントなどを処理)
model = prepare_model_for_kbit_training(model)

# 以前と同じLoRA設定
lora_config = LoraConfig(
 r=8,
 lora_alpha=16,
 target_modules=["q_proj", "v_proj"],
 lora_dropout=0.05,
 bias="none",
 task_type="CAUSAL_LM"
)

# 量子化されたモデルにLoRAアダプターを適用
model = get_peft_model(model, lora_config)

model.print_trainable_parameters()
# 出力: trainable params: 4,194,304 || all params: 3,752,071,168 || trainable%: 0.1118

主な違い:QLoRAはモデルをロードする際にBitsAndBytesConfigを追加し、LoRAを適用する前にprepare_model_for_kbit_training()を呼び出します。他はすべて同じです。

prepare_model_for_kbit_training()

メモリの違いは?LoRAはこの7Bモデルに約16GBを使用し、QLoRAは約6GBを使用します。これが4ビット量子化の威力です。

よくある落とし穴とベストプラクティス

LoRAとQLoRAを使う際に注意すべき点は次のとおりです。

保守的に始めて、スケールアップ:

常にr = 8、α = 16で始め、q_projとv_projをターゲットにします。これはほとんどのタスクで機能します。明確な不適合が見られる場合にのみ、ランクを増やすかモジュールを追加します。

ランクとアルファを独立して調整しない:

ランクを16に増やす場合は、アルファを32に増やします。α/r比は約2のままにする必要があります。アルファを調整せずにランクを変更すると、パフォーマンスが低下することが多いです。

トレーニングの不安定性に注意:

損失が不規則になったり急にスパイクしたりする場合、おそらくアルファが高すぎます。下げて学習率を確認してください。

小さなデータセットでの過学習:

LoRAとQLoRAは、完全なfine-tuningと比較して過学習を自然に減らします(過学習するパラメータが少ないため)が、それでも発生する可能性があります。1000例未満のデータセットでは、ランクを低く(4-8)保ち、ドロップアウト(0.05-0.1)を使用します。

ターゲットモジュールの選択:

デフォルト(q_proj、v_proj)は、ほとんどの指示fine-tuningタスクで機能します。新しい概念を教える必要があるドメイン適応には、フィードフォワード層を追加します。スタイル/フォーマットの変更には、アテンション層のみで通常十分です。

QLoRAトレーニング速度:

QLoRAは量子化/逆量子化のオーバーヘッドのため、通常LoRAより10-20%遅くなります。必要な場合、メモリ節約はこのトレードオフを価値あるものにしますが、LoRAがメモリに収まる場合はQLoRAを使用しないでください。

保存とロード:

LoRAとQLoRAアダプターは小さい(通常10-50MB)です。アダプターの重みのみを保存し、後でベースモデルの上にロードできます。これにより、完全なモデルチェックポイントを配布するよりも、モデルの配布とバージョン管理がはるかに簡単になります。

まとめ:次のステップ

LoRAとQLoRA、大規模言語モデルをはるかに少ないメモリと計算能力でfine-tuningするための2つの強力な手法をマスターしました。これらの手法は、大規模モデルをニーズに合わせて効率的に適応させるための画期的なものです。

来週は、今週のすべてを統合し、これらの技術を実際のタスクのためにモデルをfine-tuningすることに適用します。学んだことを実践的で影響力のある方法で適用する、実践的なプロジェクトの準備をしてください。

構築を続けましょう!

  • Fine-Tuningを実用的にする

コメント

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