PEFT documentation

Adapter 주입

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.21.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Adapter 주입

PEFT를 사용하면 torch 모듈에 학습 가능한 어댑터를 삽입할 수 있으며, 이를 통해 PEFT의 모델링 클래스에 의존하지 않고도 어댑터 방법을 사용할 수 있습니다. 이는 프롬프트 학습 기반(예: prefix tuning 또는 p-tuning)인 어댑터와 여러 대상 레이어 간에 상태를 공유하는 어댑터를 제외한 모든 어댑터에 적용됩니다.

어떤 시점에 어댑터를 주입해야 하는지 확인하려면 아래 표를 참조하세요.

장점단점
inplace로 모델이 수정되며, 모든 원래 속성과 메서드가 유지됩니다Hugging Face의 from_pretrained와 save_pretrained 유틸리티 함수를 수동으로 작성하여 어댑터를 저장하고 로드합니다
모든 torch 모듈과 모달리티에 적용됩니다PeftModel에서 제공하는 어댑터 비활성화 및 병합과 같은 유틸리티 메서드와 함께 작동하지 않습니다

inject_adapter_in_model은 여러 대상 레이어 간에 어댑터 상태를 공유하는 PEFT 메서드를 지원하지 않습니다. 현재 TinyLoRA, UniLoRA, VeRA, PVeRA, VBLoRA, FRoD가 해당됩니다. 이러한 메서드의 경우 대신 get_peft_model()을 사용하세요.

새 PEFT 모델 만들기

어댑터 주입을 수행하려면 inject_adapter_in_model() 메서드를 사용합니다. 이 메서드는 PEFT 설정, 모델, 선택적 어댑터 이름이라는 3개의 인수를 받습니다. 서로 다른 어댑터 이름으로 inject_adapter_in_model()을 여러 번 호출하면 모델에 여러 어댑터를 연결할 수도 있습니다.

예를 들어, DummyModel 모듈의 linear 서브모듈에 LoRA 어댑터를 주입하려면:

import torch
from peft import inject_adapter_in_model, LoraConfig

class DummyModel(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.embedding = torch.nn.Embedding(10, 10)
        self.linear = torch.nn.Linear(10, 10)
        self.lm_head = torch.nn.Linear(10, 10)

    def forward(self, input_ids):
        x = self.embedding(input_ids)
        x = self.linear(x)
        x = self.lm_head(x)
        return x


lora_config = LoraConfig(
    lora_alpha=16,
    lora_dropout=0.1,
    r=64,
    bias="none",
    target_modules=["linear"],
)

model = DummyModel()
model = inject_adapter_in_model(lora_config, model)

dummy_inputs = torch.LongTensor([[0, 1, 2, 3, 4, 5, 6, 7]])
dummy_outputs = model(dummy_inputs)

…

DummyModel(
  (embedding): Embedding(10, 10)
  (linear): Linear(
    in_features=10, out_features=10, bias=True
    (lora_dropout): ModuleDict(
      (default): Dropout(p=0.1, inplace=False)
    )
    (lora_A): ModuleDict(
      (default): Linear(in_features=10, out_features=64, bias=False)
    )
    (lora_B): ModuleDict(
      (default): Linear(in_features=64, out_features=10, bias=False)
    )
    (lora_embedding_A): ParameterDict()
    (lora_embedding_B): ParameterDict()
  )
  (lm_head): Linear(in_features=10, out_features=10, bias=True)
)

state_dict 기반의 주입

가끔 PEFT 어댑터 체크포인트는 존재하지만, 그 이유를 불문하고 해당 PEFT 설정이 알려지지 않은 경우가 있습니다. 이 체크포인트에 PEFT 레이어를 주입하려면 일반적으로 체크포인트의 state_dict를 기반으로, 특히 target_modules 인수를 포함하여, 해당 PEFT 설정을 역설계해야 합니다. 이는 번거롭고 오류가 발생하기 쉽습니다. 이를 피하기 위해 inject_adapter_in_model()을 호출하고 로드된 state_dict를 인수로 전달하는 것도 가능합니다:

from safetensors.torch import load_file

model = ...
state_dict = load_file(<path-to-safetensors-file>)
lora_config = LoraConfig(...)
model = inject_adapter_in_model(lora_config, model, state_dict=state_dict)

이 경우 PEFT는 PEFT config 대신 state_dict를 참조하여 대상 레이어를 결정합니다. 사용자는 이 기능이 작동하도록 PEFT config의 정확한 target_modules를 설정할 필요가 없습니다. 그러나 여전히 올바른 유형의 PEFT config, 이 예시에서는 LoraConfig를 전달해야 하며, target_modules는 None으로 두면 됩니다.

이 과정은 초기화되지 않은 PEFT 레이어만 생성할 뿐, state_dict의 값으로 모델 가중치를 채우지 않습니다. 가중치를 채우려면 아래 설명에 따라 set_peft_model_state_dict()을 호출하세요.

⚠️ PEFT 설정에 구성된 내용과 state_dict에서 발견된 내용이 일치하지 않으면 PEFT가 이를 경고합니다. PEFT 설정이 올바르게 지정되지 않은 것을 알고 있다면 이 경고를 무시할 수 있습니다.

원본 PEFT 어댑터가 target_modules 대신 target_parameters를 사용했다면, state_dict에서 주입하는 것은 올바르게 작동하지 않습니다. 이 경우 주입에 올바른 PEFT 설정을 사용하는 것이 필수적입니다.

모델 저장

어댑터만 저장하려면 get_peft_model_state_dict() 함수를 사용하세요:

from peft import get_peft_model_state_dict

peft_state_dict = get_peft_model_state_dict(model)
print(peft_state_dict)

그렇지 않은 경우, model.state_dict()는 모델의 전체 state dict를 반환합니다.

모델 로드

저장된 state_dict를 로드한 후, set_peft_model_state_dict() 함수를 사용하여 적용할 수 있습니다:

from peft import set_peft_model_state_dict

model = DummyModel()
model = inject_adapter_in_model(lora_config, model)
outcome = set_peft_model_state_dict(model, peft_state_dict)
# check that there were no wrong keys
print(outcome.unexpected_keys)

어댑터 주입이 느리거나 대량의 어댑터를 로드해야 하는 경우, 메타 디바이스에서 “빈” 어댑터를 생성하고 set_peft_model_state_dict()이 호출될 때만 실제 가중치로 가중치를 채우는 최적화를 사용할 수 있습니다. 이를 위해 low_cpu_mem_usage=True를 inject_adapter_in_model()과 set_peft_model_state_dict() 양쪽에 전달하세요.

model = DummyModel()
model = inject_adapter_in_model(lora_config, model, low_cpu_mem_usage=True)

print(model.linear.lora_A["default"].weight.device.type == "meta")  # should be True
set_peft_model_state_dict(model, peft_state_dict, low_cpu_mem_usage=True)
print(model.linear.lora_A["default"].weight.device.type == "cpu")  # should be True

Hub에서 가중치를 로드하려면 저수준 load_peft_weights() 함수가 있습니다:

state_dict = load_peft_weights("my-account/my-adapter-repo")

기본 가중치 설정 및 로드

위 함수들은 adapter의 state dict를 다룹니다. base model의 가중치를 PEFT 래퍼를 통해 읽거나 작성해야 하는 상황도 있습니다. PEFT가 대상 모듈의 매개변수를 이름 변경(예: q_proj.weight이 q_proj.base_layer.weight으로 변경)하고 base model에 없는 adapter 매개변수를 추가하기 때문에 이 작업은 완전히 자명하지 않습니다. 두 명명 규칙 사이를 변환하려면 get_base_model_state_dict()와 set_base_model_state_dict()를 사용하세요:

from peft import LoraConfig, get_peft_model, get_base_model_state_dict, set_base_model_state_dict
from transformers import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained("facebook/opt-125m")
model = get_peft_model(model, LoraConfig(target_modules="all-linear"))

# state dict with the original (pre-PEFT) key names, adapter parameters excluded
base_state_dict = get_base_model_state_dict(model)

# load a state dict with original key names into the PEFT-wrapped model
outcome = set_base_model_state_dict(model, base_state_dict, strict=False)
# check that there were no wrong keys
print(outcome.missing_keys, outcome.unexpected_keys)

이 기능의 주요 사용 사례는 모델이 이미 PEFT로 래핑된 이후에 기본 가중치를 로드하는 것입니다. 예를 들어, TorchTitan과 같은 FSDP 학습 설정은 모델을 meta device에서 초기화하고, PEFT를 적용한 후, 그 결과를 샤딩합니다. 실제 메모리는 샤딩 이후에만 존재하므로, 원래 이름을 키로 사용하는 체크포인트는 이미 래핑된 모델에 로드되어야 합니다. 참고로 get_base_model_state_dict()는 모델의 라이브 텐서를 반환하며, 복사본을 반환하지 않습니다.

Update on GitHub