👏

huggingfaceのPEFTについて

2023/10/22に公開

そもそも、PEFTとは?

  • PEFT(Parameter-Efficient Fine Tuning)とは事前学習済み言語モデル(LLM)作成する際に新しいタスクに効率的に適応させるためのモデルのパラメーター更新手法です。この手法は、モデルの一部のパラメータのみをファインチューニングすることで、全体を再学習する必要がなくなります。これにより、計算コストとストレージコストを大幅に削減し、効率的なファインチューニングが実現できるため、フルファインチューニングと同等の性能を達成することができます。

  • PEFTは、LLM(Large Language Model)のような事前学習済みモデルを再学習する際に注目されており、効率的なファインチューニング手法として利用されています。具体的には、LLMの一部のパラメータを更新することで、モデルの性能を保ちつつ計算コストを削減します。

  • 具体的には、以下のような目的を持っています:

    • 計算コストの削減
      • 数千億パラメータを持つ LLM を完全なファインチューニング (Full FT) で再学習することは時間的・コスト的に現実的ではありません。PEFT は一部のパラメータだけをファインチューニングすることで、計算コストを削減します。
    • 壊滅的忘却の抑制
      • モデルが新しい概念やタスクを学習する過程で、事前学習時に獲得した一般的な言語知識を失う「壊滅的忘却」が発生することがあります。PEFT では限られた数のパラメータだけを更新するため、この問題を回避します。
    • モデルサイズの管理
      • 大規模な事前学習モデルを用いたサービスやビジネスが広く社会に普及していくには、通常のファインチューニングとは異なる効率的なアプローチが必要です。PEFTはその一つです。

PEFTの手法一覧

  • PEFTとは、事前学習済みの言語モデル(LLM)を作成する際に、すべてのモデルパラメータを微調整することなく、様々な下流のアプリケーションに効率的に適応させるための手法です。HuggingFaceでは、以下の8つのPEFT手法がサポートされています。
  1. LoRA:
    • 低ランク適応(Low-Rank Adaptation)と呼ばれる手法で、LLMの各層に低ランク行列を導入して、パラメータ数を大幅に削減します。この手法は、生成や分類などのさまざまなタスクに対応できます。
# peftの設定は読み込んだモデルをget_peft modelでパラメーターを凍結する際に指定します。
peft_config = LoraConfig(task_type=TaskType.CAUSAL_LM,inference_mode=False, r=8, lora_alpha=32, lora_dropout=0.1)

# creating model
model = AutoModelForCausalLM.from_pretrained(model_name_or_path)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()

# output
"""
Downloading (…)lve/main/config.json: 100%
747/747 [00:00<00:00, 59.3kB/s]
Downloading pytorch_model.bin: 100%
14.1G/14.1G [00:39<00:00, 280MB/s]
trainable params: 3,932,160 || all params: 7,072,948,224 || trainable%: 0.055594355783029126
"""
  1. Prefix Tuning:
    • LLMの入力に連続したプロンプトと呼ばれるパラメータ化されたトークン列を追加して、タスクに応じてプロンプトのパラメータだけを微調整する手法です。この手法は、生成タスクに特化しており、GPT-2やT5などのモデルに適用できます。
# peftの設定は読み込んだモデルをget_peft modelでパラメーターを凍結する際に指定します。
peft_config = PrefixTuningConfig(task_type=TaskType.CAUSAL_LM, num_virtual_tokens=30)

# creating model
model = AutoModelForCausalLM.from_pretrained(model_name_or_path)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()

# output
"""
Downloading (…)lve/main/config.json: 100%
715/715 [00:00<00:00, 52.6kB/s]
Downloading model.safetensors: 100%
1.12G/1.12G [00:45<00:00, 28.4MB/s]
trainable params: 1,474,560 || all params: 560,689,152 || trainable%: 0.26299064191632515
"""

  1. P-Tuning:
    • LLMの入力に離散的なプロンプトと呼ばれる固定されたトークン列を追加して、タスクに応じてプロンプトとモデルの出力間の重みだけを微調整する手法です。この手法は、分類タスクや生成タスクに対応できます。
  2. Prompt Tuning:
    • LLMの入力に離散的なプロンプトと呼ばれる固定されたトークン列を追加して、タスクに応じてプロンプトとモデルの出力間の重みだけを微調整する手法です。この手法は、P-Tuningと同様に分類タスクや生成タスクに対応できますが、より大規模なモデルやデータセットに適用できます。
# peftの設定は読み込んだモデルをget_peft modelでパラメーターを凍結する際に指定します。
peft_config = PromptTuningConfig(
    task_type=TaskType.CAUSAL_LM,
    prompt_tuning_init=PromptTuningInit.TEXT,
    num_virtual_tokens=8,
    prompt_tuning_init_text="Classify if the tweet is a complaint or not:",
    tokenizer_name_or_path=model_name_or_path,
)

# creating model
model = AutoModelForCausalLM.from_pretrained(model_name_or_path)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()

# output
"""
Downloading (…)lve/main/config.json: 100%
715/715 [00:00<00:00, 12.1kB/s]
Downloading model.safetensors: 100%
1.12G/1.12G [00:04<00:00, 255MB/s]
trainable params: 8,192 || all params: 559,222,784 || trainable%: 0.0014648902430985358
"""

  1. AdaLoRA:
    • 適応的予算割り当て(Adaptive Budget Allocation)と呼ばれる手法で、LoRAとPrefix Tuningを組み合わせて、タスクやモデルの難易度に応じて低ランク行列やプロンプトのサイズを動的に調整します。この手法は、生成タスクや分類タスクに対応できます。
  2. (IA)^3:
    • 内部活性化を抑制・増幅するアダプタ(Infused Adapter by Inhibiting and Amplifying Inner Activations)と呼ばれる手法で、LLMの各層にアダプタと呼ばれる小さなニューラルネットワークを挿入して、内部活性化を変更します。この手法は、分類タスクや生成タスクに対応できます。
  3. MultiTask Prompt Tuning:
    • 複数のタスクを同時に学習するためのプロンプトチューニングの拡張です。この手法は、各タスクごとに異なるプロンプトと重みを持ち、共通のモデルパラメータを共有します。この手法は、分類タスクや生成タスクに対応できます。
  4. LoHa:
    • 低ランクアダマール積(Low-Rank Hadamard Product)と呼ばれる手法で、LLMの各層に低ランク行列を導入して、アダマール積を計算します。この手法は、連合学習(Federated Learning)という分散学習の枠組みにおいて、通信効率を向上させることができます。

おまけ

  • 上記のように、peftを使用してLLMをファインチューニングする際には、1度読み込んだモデルをget_peft_modelという関数にモデルと peftの指定を行ったconfigを追加しないと行けません。
  • QLoraのようにモデルを量子化したい場合は、はじめにモデルを読み込む際に、以下のような量子化方法を指定したconfigを使用します。
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, AutoTokenizer

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.float16,
)

model = AutoModelForCausalLM.from_pretrained(
    model_name,
    quantization_config=bnb_config,
    trust_remote_code=True
)
model.config.use_cache = False

Discussion