PEFT documentation
LoRA変換
LoRA変換
非LoRA PEFTモデルをLoRAモデルに変換できる関数。
説明
PEFTは、数十種類の異なるパラメータ効率ファインチューニング手法をサポートしています。その中でも、圧倒的に人気があるのはLoRAです。そのため、他の多くのパッケージもLoRAをサポートしています。例えば、DiffusersではLoRAアダプターを読み込むことで、拡散モデルの機能を変更できます。vLLMでは、LoRAアダプター付きのモデルをサービングできます。これは素晴らしいことですが、残念ながら、LoRA以外のPEFT手法はほとんどサポートされていません。したがって、特定のユースケースにおいて他のPEFT手法の方が適している場合でも、下流のパッケージがサポートしていないため、その手法を使用できない可能性があります。
ここでは、一つの解決策を提示します。PEFTには、save_as_lora() と convert_to_lora() という2つの関数が提供されており、PEFTアダプターをLoRAアダプターに変換できます。現時点ではすべてのPEFTメソッドがこの変換に対応しているわけではありませんが、対応している場合、自分に最も適したPEFTメソッドから始め、後からLoRAアダプターとして利用することが可能になります。
例
変換されたアダプターの LoRA ランクは、rank 引数に 0 以上の int を渡すことで固定のランクに設定することも、rank 引数に 0 と 1 間の float を渡すことで各レイヤーに適応する動的ランクに設定することもできます。動的ランクは、より効率的になる可能性があります(パラメータ数を減らしつつ同じパフォーマンスを維持)。
固定されたLoRAランク
save_as_lora()の使い方は比較的シンプルです:
from peft import get_peft_model, save_as_lora
# first load and train your non-LoRA PEFT model as normal
base_model = ...
non_lora_config = ...
model = get_peft_model(base_model, non_lora_config)
# check that this PEFT method can indeed be converted to LoRA
assert model.supports_lora_conversion()
... # train the model
# the rank of the LoRA adapter that you want to convert to
target_rank = 64
# save as a LoRA checkpoint
save_as_lora(output_path, model, rank=target_rank)これにより、output_path に LoRA チェックポイントが作成されます。このチェックポイントは、他の LoRA アダプターと同様に読み込むことができ、Diffusers や vLLM などの下流のパッケージでも使用できます。
変換した LoRA アダプターを保存せず、変換後の重みをすぐに使用したい場合(評価の実行など)には、convert_to_lora() 関数が便利です。
from peft import convert_to_lora, get_peft_model, set_peft_model_state_dict
base_model = ...
non_lora_config = ...
model = get_peft_model(base_model, non_lora_config)
... # train the model
# get the lora config and state dict of the converted lora model
lora_config, lora_state_dict = convert_to_lora(model, rank=target_rank)
# reload the base model, or use model.unload()
base_model = ...
# apply the lora config to the base model
lora_model = get_peft_model(base_model, lora_config)
# load the LoRA weights onto the base model
set_peft_model_state_dict(lora_model, state_dict)動的LoRAランク
上記の例では、変換に固定のLoRAランクを使用しました。しかし、一部の層は正確な変換に高いランクを必要としない一方で、他の層はより高いランクを必要とする可能性があります。これに対応するため、PEFTではrank引数として0から1の間の浮動小数点数を渡すオプションを提供しています。例えば、rank=0.5を渡すと、各層について、元のアダプタによって導入された重みの分散の50%を説明するようにLoRAアダプタのランクが選択されます。より技術的な表現をすると、内部ではアダプタの重みへの寄与に対して特異値分解を実行し、正規化された上位の特異値の合計が渡された値に達するまでそれらを抽出します。
# set a dynamic rank by passing a float
threshold = 0.7
# save as a LoRA checkpoint
save_as_lora(output_path, model, rank=threshold)
# get the lora config and state dict directly:
lora_config, lora_state_dict = convert_to_lora(model, rank=threshold)
# inspect the different ranks per layer:
print(lora_config.rank_pattern)異なるレイヤーの寄与が大きく異なる場合、このタイプの動的なLoRAランクを使用すると有用です。デメリットとしては、一部のレイヤーでLoRAランクが非常に高くなる可能性があるため、メモリ使用量の急増を引き起こすことがあります。使用ケースに応じて最適な設定をテストしてください。
モデルのコンパイル
大規模モデルの場合、変換に時間がかかることがあります。例えば、各PEFTモジュールはSVD計算を経る必要があります。compile_kwargsをsave_as_lora()またはconvert_to_lora()に渡すことで、変換関数にtorch.compileを適用し、処理を高速化できる可能性があります。compile_kwargsはtorch.compileに渡されるキーワード引数の辞書です(空の辞書でも動作します)。以下に例を示します。
compile_kwargs = {"dynamic": True, "mode": "max-autotune-no-cudagraphs", "fullgraph": True}
save_as_lora(output_path, model, rank=rank, compile_kwargs=compile_kwargs)LoRAからLoRAへの変換
LoRA アダプターを別の LoRA アダプターに変換することも可能です。なぜそうしたいのでしょうか。1 つの理由があります。それは、LoRA アダプターのランクを下げたい場合です。学習後に LoRA アダプターを縮小したい場合は、save_as_lora() または convert_to_lora() を使用し、より小さいランクを指定してください。これにより、メモリとストレージのフットプリントが小さい新しい LoRA アダプターが得られます。
メトリクス
非LoRAからLoRAへの変換
もちろん、PEFTアダプターを別のアダプターに変換するプロセスでは情報が失われます。新しいアダプターは、初期のアダプターほど良好なパフォーマンスを発揮しない可能性が高いです。そのため、変換後のLoRAアダプターを評価することを強く推奨します。これにより、変換後のアダプターがあなたのユースケースに対して十分に良好なパフォーマンスを発揮していることを確認できます。LoRAアダプターのランクが高いほど、初期のアダプターをより正確に近似できるという一般的なルールが適用されます。つまり、変換後のLoRAアダプターは、元のアダプターと同等のパフォーマンスを実現するために、より多くのパラメータを必要とする場合があります。
例として、PEFT MetaMathQA ベンチマークから導出された数値を示します。この実験では、LoHa アダプターを使用して meta-llama/Llama-3.2-3B を MetaMathQA でファインチューニングし、GSM8K で評価しました。初期の LoKr アダプターのランクは 32 であり、学習可能なパラメータ数は 18,350,080、テスト精度は 41.85% でした。評価には 12.25 GB のメモリが必要でした。チェックポイントは、rank の値を異なるものにして LoRA に変換されました。その結果は以下の通りです。
| ランク | 学習可能なパラメータ | テスト精度(%) | 精度の変化 | リザーブされたメモリ(最大値、GB) | メモリ増加 |
|---|---|---|---|---|---|
| 8 | 2293760 | 37.60 | -4.25 | 12.41 | 0.16 |
| 16 | 4587520 | 38.89 | -2.96 | 12.15 | -0.10 |
| 32 | 9175040 | 40.11 | -1.74 | 12.41 | 0.16 |
| 64 | 18350080 | 39.20 | -2.65 | 12.18 | -0.07 |
| 0.4 | 2428928 | 37.60 | -4.25 | 12.41 | 0.16 |
| 0.5 | 4761600 | 40.18 | -1.67 | 12.41 | 0.16 |
| 0.6 | 8857600 | 39.42 | -2.43 | 12.41 | 0.16 |
| 0.7 | 16230400 | 39.04 | -2.81 | 12.15 | -0.10 |
ご覧のとおり、ランクが十分に高い場合、元のLoHaアダプターに近いテスト精度を達成できます。適切なランクの選択は、モデルの性能とモデルの効率性のトレードオフです。この実験を再現するには、https://github.com/huggingface/peft/tree/main/scripts/evaluate-lora-conversion.pyのスクリプトに従ってください。
学習可能なパラメータの数は、メモリ使用量に1対1で変換できるものではありません。学習可能なパラメータ数が同じであっても、PEFT手法によってメモリ使用量が多いものもあれば少ないものもあります。したがって、変換後にLoRAアダプタのパラメータ数が元のモデルよりも多くなった場合でも、サービング時のメモリ効率が高くなる可能性があります。
LoRAからLoRAへの変換
上記の実験と同様に、LoRAからLoRAへの変換(つまりLoRA圧縮)も評価できます。ここでは、上記と同じ設定でRS-LoRAを用いて訓練されたランク64のLoRAアダプターから始めます。初期アダプターは18,350,080の学習可能パラメータを持ち、テスト精度は52.92%、評価には12.58 GBのメモリが必要です。以下の表は、このアダプターをより小さいランクのLoRAアダプターに変換した結果を示しています。
| ランク | 学習可能なパラメータ | テスト精度(%) | 精度の変化 | リザーブされたメモリ(最大値、GB) | メモリ増加 |
|---|---|---|---|---|---|
| 8 | 2293760 | 43.37 | -9.55 | 12.38 | -0.20 |
| 16 | 4587520 | 48.90 | -4.02 | 12.38 | -0.20 |
| 32 | 9175040 | 51.48 | -1.44 | 12.49 | -0.09 |
| 48 | 13762560 | 52.01 | -0.91 | 12.38 | -0.20 |
| 0.5 | 2150400 | 44.12 | -8.80 | 12.37 | -0.21 |
| 0.6 | 3082240 | 47.54 | -5.38 | 12.37 | -0.21 |
| 0.7 | 4448256 | 50.49 | -2.43 | 12.37 | -0.21 |
| 0.8 | 6510592 | 50.11 | -2.81 | 12.37 | -0.21 |
| 0.9 | 10022912 | 51.55 | -1.37 | 12.38 | -0.20 |
| 0.95 | 12976128 | 52.62 | -0.30 | 12.39 | -0.19 |
例えば、ランク0.95の場合、パラメータ数を30%削減しながら、精度の差をわずか0.3ポイントに縮小できます。また、これらの圧縮されたLoRAは、低いランクで直接トレーニングするよりも性能が向上する場合があります。例えば、ランク32で直接トレーニングするとテスト精度は48.22%ですが、ランク64から変換すると51.48%になります。
注意事項
LoRA変換にはいくつかの制限があります。上記で述べたように、パフォーマンスの低下が予想され、変換後のLoRAは元のアダプターよりもパラメータ効率が悪くなる可能性が高いです。さらに、LoRA変換には以下の制限があります。
- 現在、線形層に適用されたアダプターのみ変換できます。
- 現在、すべてのPEFTメソッドがLoRA変換をサポートしているわけではありません。
LoRA変換の拡張に対する需要が多い場合は、GitHub discussionを作成してご連絡ください。より多くのレイヤータイプやPEFTメソッドとの互換性を確保します。
API
LoRA 以外のモデルを LoRA モデルに変換し、LoraConfig と state_dict を返します
peft.convert_to_lora
< source >( model: Modulerank: floatadapter_name: str = 'default'progressbar: bool = Falsecompile_kwargs = None )
パラメータ
- model — 変換対象のモデルです。変換をサポートする PEFT レイヤーを持つモデルである必要があります。
- rank (
intまたはfloat) — 返される LoRA アダプターに設定する希望するランクです。ランクが高いほど、元のアダプターをより正確に反映する LoRA アダプターが得られます。ただし、メモリ、計算量、ディスク容量の増加も伴います。そのため、使用ケースに最適なバランスを表す値を選択し、最終的なアダプターを検証してください。float が渡された場合、それは説明分散 / エネルギー閾値として解釈されます。上位 k 個の固有値が、二乗固有値の合計の少なくともその割合を占める最小のランク k を選択します。これにより、少数の固有値でこのレイヤーの適応を捕捉できる場合、より低いランクが割り当てられることになります。float の値が低いほどランクは低くなり、その逆も同様です。動的なランクはレイヤーごとに非常に不均衡なランクを生み出す可能性があることに注意してください。これは、一部のレイヤーがアクティベーションに対して過剰なメモリを必要とする可能性があることを意味します。予測可能なメモリ要件を実現するには、固定の (int) ランクを選択する方が望ましいです。 - adapter_name (
str, 省略可) — 変換するアダプターの名前です。一度に1つのアダプターのみ変換できます。デフォルトは"default"です。 - progressbar (
bool) — 変換の進捗を示すプログレスバーを表示するかどうか(大きなモデルでは数分かかる場合があります)。 - compile_kwargs (
dict, 任意) — 指定された場合、torch.compileに渡される kwargs を用いて関数をコンパイルし、個々のモジュールを LoRA に変換します。これにより、大規模モデルの変換が高速化される可能性があります。
PEFTレイヤーを持つ非LoRAモデルをLoRAチェックポイントに変換します。
これは、同等の変換を許可する特定のPEFTメソッドでのみサポートされています。本質的には、ベースの重みにデルタ重みを加えて更新するPEFTメソッドに該当します。また、現時点では線形層のみがサポートされています。
LoRA アダプターは、初期アダプターをできるだけ正確に近似しようとします。ランクが高いほど、近似の精度は向上します。近似が元のアダプターの完全な性能に到達することは期待されず、LoRA アダプターのパラメータ効率も元のアダプターよりも低くなることが予想されます(つまり、同程度の性能を得るには、より多くのパラメータが必要になります)。それでも、変換は多くの状況で有用です:
- PEFTでは、LoRAは他の多くの手法よりも多くの機能をサポートしており、例えば混合アダプターバッチなどが挙げられます。したがって、変換されたアダプターはこれらの機能と併せて使用できます。
- 一部のダウンストリームパッケージはLoRAアダプターをサポートしていますが、他のPEFTメソッドはサポートしていません(例: Diffusers)。変換を行うことで、これらのパッケージでLoRA以外のアダプターを使用できるようになります。
LoRAのスケーリング係数はすでにLoRAの重みに組み込まれているため、スケーリングは常に1になります(つまり、rankとalphaは同一に選択されます)。
注: この関数は、シャード化されたモデルには(まだ)対応していません。
Raises
TypeError:
If the provided model does not have any layers that can be converted to LoRA, a TypeError is raised.
ValueError:
If an invalid rank was chosen (too high or too low).
LoRA 以外のモデルを LoRA モデルに変換し、アダプターチェックポイントと設定を指定されたパスに保存します
peft.save_as_lora
< source >( path: str | os.PathLikemodel: Modulerank: floatadapter_name: str = 'default'progressbar: bool = Falsecompile_kwargs = None )
パラメータ
- model — 変換対象のモデルです。変換をサポートする PEFT レイヤーを持つモデルである必要があります。
- rank (
intまたはfloat) — 返される LoRA アダプターに設定する希望するランクです。ランクが高いほど、元のアダプターをより正確に反映する LoRA アダプターが得られます。ただし、メモリ、計算量、ディスク容量の増加も伴います。そのため、使用ケースに最適なバランスを表す値を選択し、最終的なアダプターを検証してください。float が渡された場合、それは説明分散 / エネルギー閾値として解釈されます。上位 k 個の固有値が、二乗固有値の合計の少なくともその割合を占める最小のランク k を選択します。これにより、少数の固有値でこのレイヤーの適応を捕捉できる場合、より低いランクが割り当てられることになります。float の値が低いほどランクは低くなり、その逆も同様です。動的なランクはレイヤーごとに非常に不均衡なランクを生み出す可能性があることに注意してください。これは、一部のレイヤーがアクティベーションに対して過剰なメモリを必要とする可能性があることを意味します。予測可能なメモリ要件を実現するには、固定の (int) ランクを選択する方が望ましいです。 - adapter_name (
str, 省略可) — 変換するアダプターの名前です。一度に1つのアダプターのみ変換できます。デフォルトは"default"です。 - progressbar (
bool) — 変換の進捗を示すプログレスバーを表示するかどうか(大きなモデルでは数分かかる場合があります)。 - compile_kwargs (
dict, 任意) — 指定された場合、torch.compileに渡される kwargs を用いて関数をコンパイルし、個々のモジュールを LoRA に変換します。これにより、大規模モデルの変換が高速化される可能性があります。
PEFTレイヤーを持つ非LoRAモデルをLoRAに変換し、チェックポイントファイルとPEFT設定を保存します。
これは、同等の変換を許可する特定のPEFTメソッドでのみサポートされています。本質的には、ベースの重みにデルタ重みを加えて更新するPEFTメソッドに該当します。また、現時点では線形層のみがサポートされています。
LoRA アダプターは、初期アダプターをできるだけ正確に近似しようとします。ランクが高いほど、近似の精度は向上します。近似が元のアダプターの完全な性能に到達することは期待されず、LoRA アダプターのパラメータ効率も元のアダプターよりも低くなることが予想されます(つまり、同程度の性能を得るには、より多くのパラメータが必要になります)。それでも、変換は多くの状況で有用です:
- PEFTでは、LoRAは他の多くの手法よりも多くの機能をサポートしており、例えば混合アダプターバッチなどが挙げられます。したがって、変換されたアダプターはこれらの機能と併せて使用できます。
- 一部のダウンストリームパッケージはLoRAアダプターをサポートしていますが、他のPEFTメソッドはサポートしていません(例: Diffusers)。変換を行うことで、これらのパッケージでLoRA以外のアダプターを使用できるようになります。
LoRAのスケーリング係数はすでにLoRAの重みに組み込まれているため、スケーリングは常に1になります(つまり、rankとalphaは同一に選択されます)。
変換されたLoRAの重みは、次のように読み込むことができます。
>>> lora_path = ...
>>> save_as_lora(lora_path, model, rank=...)
>>> base_model = AutoModel.from_pretrained(...)
>>> lora_model = PeftModel.from_pretrained(base_model, lora_path)注: この関数は、シャード化されたモデルには(まだ)対応していません。
Raises
TypeError:
If the provided model does not have any layers that can be converted to LoRA, a TypeError is raised.
ValueError:
If an invalid rank was chosen (too high or too low).