Transformers documentation
매개변수 효율적 미세 조정
매개변수 효율적 미세 조정
Parameter-efficient fine-tuning (PEFT) 방법은 사전 학습된 모델 위에 추가된 소수의 모델 파라미터(어댑터)만 fine-tune합니다. 어댑터 파라미터만 업데이트되므로 옵티마이저가 추적하는 그래디언트와 상태가 훨씬 적어지고, 이에 따라 메모리 사용량이 크게 줄어듭니다. 어댑터는 경량화되어 있어 공유, 저장, 로딩이 편리합니다.
Transformers는 PeftAdapterMixin을 통해 PEFT 라이브러리와 직접 통합되며, 이는 모든 PreTrainedModel 클래스에 추가되었습니다. 모델을 별도의 PeftModel로 감싸지 않고도 어댑터를 로드, 추가, 학습, 전환 및 삭제할 수 있습니다. 프롬프트 학습이 아닌 모든 PEFT 방법(LoRA, IA3, AdaLoRA)이 지원됩니다. 프롬프트 튜닝 및 프리픽스 튜닝과 같은 프롬프트 기반 방법은 PEFT 라이브러리를 직접 사용해야 합니다.
시작하려면 PEFT를 설치하세요. 이 통합에는 peft >= 0.20.0이 필요합니다.
pip install -U peft
어댑터 추가
LoraConfig와 같은 PEFT config를 생성하고 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는 어댑터와 함께 완전히 미세 조정되는 레이어를 지정하며, 따라서 모든 매개변수가 업데이트됩니다. 이는 시퀀스 분류를 위해 causal 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는 LoRA 레이어를 올바르게 처리하도록 FSDP 자동 랩 정책을 업데이트합니다. QLoRA(양자화된 베이스 모델 + LoRA)의 경우, Trainer는 양자화 저장 dtype에 맞게 혼합 정밀도 정책도 조정합니다.
PEFT 어댑터를 텐서 병렬 기반 모델에 로드하고 PEFT가 어댑터 가중치의 샤딩을 처리하도록 할 수 있습니다. 어댑터를 로드하려면 peft >= 0.21.1이 필요합니다. TP를 활용한 학습에 대해서는 Tensor parallelism을 참고하세요.
어댑터 로드
어댑터를 로드하려면 Hub 저장소 또는 로컬 디렉토리에 adapter_config.json 파일과 어댑터 가중치가 포함되어 있어야 합니다.
from_pretrained()는 어댑터를 자동으로 감지합니다. adapter_config.json을 찾으면 base_model_name_or_path 필드를 읽어 올바른 base model을 로드한 후, 그 위에 어댑터를 로드합니다.
from transformers import AutoModelForCausalLM
# Automatically loads the base model and attaches the adapter
model = AutoModelForCausalLM.from_pretrained("klcsp/gemma7b-lora-alpaca-11-v1")기존 모델에 어댑터를 로드하려면 load_adapter()를 사용하세요.
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("google/gemma-7b")
model.load_adapter("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 2torch.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을 수행합니다. hotswapping을 비활성화하려면 hotswap=False를 명시적으로 전달하세요.
초기 어댑터보다 더 많은 레이어를 대상으로 하는 어댑터가 핫스왑되면 재컴파일(recompilation)이 여전히 발생할 수 있습니다. 재컴파일을 피하려면 가장 많은 레이어를 대상으로 하는 어댑터를 먼저 로드하세요.
예기치 않은 재컴파일을 감지하려면 코드를
with torch._dynamo.config.patch(error_on_recompile=True)로 감싸세요. 위의 단계를 따랐음에도 재컴파일을 감지했다면, 재현 가능한 예시를 포함하여 PEFT에 이슈를 등록하세요.
다음 단계
- PEFT 문서에는 PEFT 방법과 옵션의 전체 범위가 포함되어 있습니다.
- PEFT hotswapping 참고 문서에는 제한 사항과 엣지 케이스가 자세히 설명되어 있습니다.
- 블로그 게시물에서 핫스왑을 적용한
torch.compile이 런타임을 어떻게 개선하는지 벤치마크를 수행합니다.