Transformers documentation

パラメータ効率の良いファインチューニング

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

パラメータ効率の良いファインチューニング

パラメータ効率の良いファインチューニング(PEFT)手法では、事前学習済みモデルの上に追加された少数のモデルパラメータ(アダプター)のみをファインチューニングします。更新されるのはアダプターのパラメータのみであるため、オプティマイザーが追跡する勾配や状態が大幅に少なくなり、メモリ使用量が著しく削減されます。アダプターは軽量であるため、共有・保存・読み込みが容易です。

Transformers は、すべての PreTrainedModel クラスに追加された PeftAdapterMixin を通じて、PEFT ライブラリと直接統合されています。モデルを別の PeftModel でラップすることなく、アダプターを読み込み、追加、学習、切り替え、削除できます。プロンプト学習以外のすべての PEFT メソッド(LoRA、IA3、AdaLoRA)がサポートされています。プロンプトチューニングやプレフィックスチューニングなどのプロンプトベースのメソッドを使用するには、PEFT ライブラリ を直接使用する必要があります。

PEFTをインストールして始めましょう。この統合にはpeft >= 0.20.0が必要です。

pip install -U peft

アダプターを追加する

LoraConfig のような PEFT 設定を作成し、add_adapter() を使用してモデルにアタッチします。

from peft import LoraConfig, TaskType
from transformers import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained("google/gemma-2-2b")

lora_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    inference_mode=False,
    r=8,
    lora_alpha=32,
    lora_dropout=0.1,
)

model.add_adapter(lora_config, adapter_name="my_adapter")

特定のレイヤーを完全にファインチューニングする

アダプターと並んで追加のモジュール(言語モデルヘッドなど)を学習させる場合は、modules_to_saveで指定します。modules_to_saveは、アダプターと並んで完全にファインチューニングされるレイヤーを指定するため、すべてのパラメータが更新されます。これは、シーケンス分類用に因果LMを適応させる際に、言語モデルヘッド(lm_head)などの特定のレイヤーを更新する必要がある場合に有用です。

lora_config = LoraConfig(
    modules_to_save=["lm_head"],
    ...
)
model.add_adapter(lora_config)

適応させるレイヤーの選択

一般的なアーキテクチャ(Llama、Gemma、Qwen など)では、PEFT に事前定義されたデフォルトのターゲット(q_proj や v_proj など)が用意されているため、target_modules を指定する必要はありません。異なるレイヤーを対象にしたい場合、またはモデルに事前定義されたターゲットがない場合は、モジュール名のリストまたは正規表現パターンとして target_modules を明示的に渡してください。

lora_config = LoraConfig(
    target_modules=["q_proj", "k_proj"],
    ...
)
model.add_adapter(lora_config)

トレーニング

アダプターがアタッチされたモデルをTrainerに渡し、train()を呼び出します。ベースモデルはフリーズされているため、Trainerはアダプターのパラメーター(requires_grad=Trueのもの)のみを更新します。

from transformers import Trainer, TrainingArguments

training_args = TrainingArguments(
    output_dir="./output",
    num_train_epochs=3,
    per_device_train_batch_size=4,
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=dataset,
)

trainer.train()

学習中、Trainerのチェックポイントには、アダプター重み(adapter_model.safetensors)と設定(adapter_config.json)のみが含まれ、チェックポイントのサイズを小さく保ちます。ベースモデルは含まれません。

学習が完了したら、save_pretrained() を使用して最終的なアダプターを保存します。

model.save_pretrained("./my_adapter")

チェックポイントからの再開

Trainer は、再開時にアダプターチェックポイントを自動的に検出します。Trainer は、チェックポイントディレクトリ内のアダプター重みを含むサブディレクトリをスキャンし、各アダプターを正しい学習可能な状態で再読み込みします。

trainer.train(resume_from_checkpoint="./output/checkpoint-1000")

分散学習

PEFT アダプターは、分散学習をそのままサポートしています。

ZeRO-3 の場合、Trainer は PEFT モデルでチェックポイントを保存する際に exclude_frozen_parameters=True を渡します。凍結されたベースモデルの重みはスキップされ、学習可能なアダプターのパラメータのみが保存されるため、チェックポイントのサイズと保存時間が削減されます。

FSDPの場合、TrainerはFSDPのオートラップポリシーを更新し、LoRAレイヤーを正しく処理します。QLoRA(量子化されたベースモデル + LoRA)の場合、Trainerは、量子化のストレージdtypeに合わせるよう、混合精度ポリシーも調整します。

PEFT アダプターをテンソル並列ベースモデルにロードし、アダプター重みのシャーディングを PEFT に処理させることができます。アダプターのロードには peft >= 0.21.1 が必要です。TP を用いた学習については、テンソル並列性を参照してください。

アダプターの読み込み

アダプターを読み込むには、Hubのリポジトリまたはローカルディレクトリにadapter_config.jsonファイルとアダプター重みを含む必要があります。

from_pretrained
load_adapter

from_pretrained() はアダプターを自動的に検出します。adapter_config.json が見つかった場合、base_model_name_or_path フィールドを読み取って正しいベースモデルをロードし、その上にアダプターをロードします。

from transformers import AutoModelForCausalLM

# Automatically loads the base model and attaches the adapter
model = AutoModelForCausalLM.from_pretrained("klcsp/gemma7b-lora-alpaca-11-v1")

大規模モデルでは、bitsandbytes を使用して 8 ビットまたは 4 ビットの精度で量子化されたバージョンを読み込み、メモリを節約します。利用可能なハードウェアにモデルを分散させるには、device_map="auto" を追加します。

from transformers import AutoModelForCausalLM, BitsAndBytesConfig

model = AutoModelForCausalLM.from_pretrained(
    "klcsp/gemma7b-lora-alpaca-11-v1",
    quantization_config=BitsAndBytesConfig(load_in_8bit=True),
    device_map="auto",
)

複数のアダプタの管理

モデルは複数のアダプタを同時に保持できます。一意の名前を付けてアダプタを追加し、必要に応じて切り替えてください。

from peft import LoraConfig

model.add_adapter(LoraConfig(r=8, lora_alpha=32), adapter_name="adapter_1")
model.add_adapter(LoraConfig(r=16, lora_alpha=64), adapter_name="adapter_2")

set_adapter() を使用して、特定のアダプターを有効にします。他のアダプターは無効化されますが、メモリ内に保持されます。

model.set_adapter("adapter_2")

enable_adapters() はすべてのアタッチされたアダプターを有効にし、disable_adapters() はすべてを無効にします。

# Disable all adapters for base model inference
model.disable_adapters()

# Re-enable all adapters
model.enable_adapters()

現在アクティブなアダプターを確認するには、active_adapters()を使用します。

model.active_adapters()
# ["adapter_1"]

不要になったアダプターは、delete_adapter() を使用して削除し、メモリを解放してください。

model.delete_adapter("adapter_1")

アダプターのホットスワップ

リクエストのたびに新しいアダプターを読み込むと、新しいメモリが割り当てられます。モデルがtorch.compileでコンパイルされている場合、新しいアダプターのたびに再コンパイルがトリガーされます。Hotswapping はアダプター重みをインプレースで置き換えるため、これらの問題の両方を回避できます。サポートされるのは LoRA アダプターのみです。

LoRA アダプターを読み込む際に hotswap=True を渡すと、既存のアダプタースロットにその重みをスワップできます。置換するアダプターの名前を adapter_name に設定します("default" がデフォルトのアダプター名です)。

model = AutoModel.from_pretrained(...)
# Load the first adapter normally
model.load_adapter(adapter_path_1)
# Generate outputs with adapter 1
...
# Hotswap the second adapter in-place
model.load_adapter(adapter_path_2, hotswap=True, adapter_name="default")
# Generate outputs with adapter 2

torch.compile

コンパイル済みのモデルでは、最初のアダプターを読み込む前に、かつコンパイルする前に、enable_peft_hotswap() を 呼び出してください。

model = AutoModel.from_pretrained(...)
max_rank = ...  # highest rank among all LoRAs you'll load
model.enable_peft_hotswap(target_rank=max_rank)
model.load_adapter(adapter_path_1, adapter_name="default")
model = torch.compile(model, ...)
output_1 = model(...)

# Hotswap without recompilation
model.load_adapter(adapter_path_2, adapter_name="default")
output_2 = model(...)

target_rank 引数は、読み込むすべての LoRA アダプターの中で最大のランクを設定します。ランク 8 とランク 16 のアダプターがある場合は、target_rank=16 を指定してください。デフォルト値は 128 です。

enable_peft_hotswapを呼び出した後、以降のload_adapter呼び出しはデフォルトでホットスワップを行います。ホットスワップを無効にするには、hotswap=Falseを明示的に渡してください。

ホットスワップされたアダプターが初期アダプターよりも多くのレイヤーを対象としている場合、再コンパイルが発生する可能性があります。再コンパイルを回避するには、最も多くのレイヤーを対象とするアダプターを最初に読み込んでください。

予期しない再コンパイルを検出するには、コードをwith torch._dynamo.config.patch(error_on_recompile=True)でラップしてください。上記の手順に従ったにもかかわらず再コンパイルが検出された場合は、再現可能な例を添えてPEFTに問題を報告してください。

次のステップ

  • PEFTのドキュメントでは、PEFTのすべての手法とオプションについて説明しています。
  • PEFTのホットスワッピングの参考資料には、制約事項やエッジケースの詳細が記載されています。
  • ブログ記事では、ホットスワッピングを併用したtorch.compileがランタイムをどのように改善するかをベンチマークしています。
Update on GitHub