PEFT documentation

LoRA

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

LoRA

LoRA는 가장 인기 있는 PEFT 방법 중 하나로, PEFT를 처음 접하는 분들에게 좋은 출발점이 됩니다. 처음에는 대규모 언어 모델을 위해 개발되었지만, 효율성과 효과성 덕분에 diffusion 모델의 학습 방법으로도 매우 널리 사용됩니다.

Low-Rank Adaptation(LoRA)는 큰 행렬을 두 개의 더 작은 저랭크 행렬로 분해하는 PEFT 방법입니다. 이를 통해 미세 조정해야 하는 매개변수의 수를 크게 줄일 수 있습니다.

해당 논문의 초록은 다음과 같습니다:

자연어 처리의 중요한 패러다임 중 하나는 일반 도메인 데이터에 대한 대규모 사전 학습과 특정 작업 또는 도메인에 대한 적응입니다. 더 큰 모델을 사전 학습할수록 모든 모델 파라미터를 다시 학습하는 전체 fine-tuning은 실현 가능성이 낮아집니다. GPT-3 175B를 예로 들어 보면, 175B 파라미터를 가진 fine-tuned 모델의 독립적인 인스턴스를 배포하는 것은 비용 면에서 감당하기 어렵습니다. 우리는 사전 학습된 모델 가중치를 고정하고 Transformer 아키텍처의 각 레이어에 학습 가능한 랭크 분해 행렬을 주입하여 하위 작업의 학습 가능한 파라미터 수를 크게 줄이는 Low-Rank Adaptation, 즉 LoRA를 제안합니다. Adam으로 fine-tuning된 GPT-3 175B와 비교할 때, LoRA는 학습 가능한 파라미터 수를 10,000배, GPU 메모리 요구량을 3배까지 줄일 수 있습니다. LoRA는 학습 가능한 파라미터 수가 적고, 학습 처리량이 높으며, 어댑터와 달리 추가적인 추론 지연이 없음에도 불구하고 RoBERTa, DeBERTa, GPT-2, GPT-3에서 모델 품질 측면으로 fine-tuning과 동등하거나 더 나은 성능을 보입니다. 또한 언어 모델 적응에서의 랭크 부족 현상에 대한 실증적 조사를 제공하여 LoRA의 유효성에 대한 통찰을 제시합니다. PyTorch 모델과의 LoRA 통합을 용이하게 하는 패키지를 공개하고, 이 https URL에서 RoBERTa, DeBERTa, GPT-2에 대한 구현 및 모델 체크포인트를 제공합니다.

LoRA는 가중치 업데이트를 ΔW\Delta W 두 개의 더 작은 행렬(업데이트 행렬이라고 함)을 통해 저랭크 분해로 표현합니다. 이 새로운 행렬은 전체 매개변수 수를 낮게 유지하면서 새로운 데이터에 적응하도록 학습할 수 있습니다. 원래의 가중치 행렬은 동결된 상태로 유지되며 추가적인 업데이트를 받지 않습니다. 최종 결과를 생성하려면 원래의 가중치와 추가된 적응 가중치를 결합합니다. 추론 지연을 제거하기 위해 어댑터 가중치를 베이스 모델에 병합하는 것도 가능합니다.)

이 접근 방식에는 다음과 같은 여러 장점이 있습니다:

  • LoRA는 학습 가능한 매개변수의 수를 크게 줄여 파인튜닝을 더 효율적으로 만듭니다.
  • …
  • LoRA는 다른 매개변수 효율적 방법과 직교하며, 그 중 많은 방법과 결합할 수 있습니다.
  • LoRA를 사용하여 파인튜닝한 모델의 성능은 완전한 파인튜닝 모델의 성능과 비슷합니다.

원칙적으로 LoRA는 학습 가능한 매개변수의 수를 줄이기 위해 신경망의 가중치 행렬 부분집합에 적용할 수 있습니다. 그러나 단순성과 추가적인 매개변수 효율성을 위해 LoRA는 일반적으로 Transformer 모델의 attention 블록에만 적용되며, 다른 레이어를 대상으로 하는 것도 고려해 볼 가치가 있습니다. LoRA 모델의 학습 가능한 매개변수 수는 업데이트 행렬의 크기에 따라 달라지며, 이는 주로 랭크 r와 원래 가중치 행렬의 형태에 의해 결정됩니다.

다양한 용도를 염두에 두고 저순위 행렬을 초기화할 수 있습니다. 예를 들어, 작업 인식(CorDA, EVA, Astra), 더 빠른 수렴(PiSSA), 양자화 완화(LoftQ) 등이 있습니다. 다양한 초기화 방식에 대해서는 아래에서 확인하세요. 기본 초기화 방식은 LoRA가 no-op(무작동)이 되도록 설정하여, 기존 모델에 크게 간섭하지 않으면서 점진적으로 새로운 동작을 학습하도록 하는 것입니다.

사용

저항 랭크 업데이트 행렬의 크기는 rank 또는 r에 의해 결정됩니다. 랭크가 높을수록 학습할 매개변수가 많아지지만, 모델의 학습 용량도 커집니다. 다음 예시에서는 어텐션 블록의 query 및 value 행렬을 대상으로 합니다. 설정해야 할 다른 중요한 매개변수로는 lora_alpha(스케일링 팩터), bias(none, all 또는 LoRA bias 매개변수만 학습할지 여부), 그리고 modules_to_save(LoRA 레이어 외에 학습 및 저장할 모듈)가 있습니다. 이 모든 매개변수와 그 외의 항목은 LoraConfig에서 찾을 수 있습니다.

from peft import LoraConfig, get_peft_model

config = LoraConfig(
    r=16,
    lora_alpha=16,
    target_modules=["query", "value"],
    lora_dropout=0.1,
    bias="none",
    modules_to_save=["classifier"],
)
model = get_peft_model(model, config)
model.print_healthcheck()

벤치마크 개요

초기화

LoRA 가중치의 초기화는 LoraConfig의 init_lora_weights 매개변수로 제어됩니다. 기본적으로 PEFT는 가중치 A에 Kaiming-uniform, 가중치 B에 제로를 사용하여 LoRA 가중치를 초기화하며, 이는 항등 변환(참조 구현과 동일)을 생성합니다.

GPT-2의 투영과 같은 transformers.Conv1D 레이어의 경우, 가중치가 nn.Linear 레이아웃이 아닌 (in_features, out_features)로 저장되므로 fan_in_fan_out=True를 사용해야 합니다. 지원되는 가중치 기반 초기화기는 분해 시 Linear 레이아웃을 사용하고, 기본 가중치를 업데이트할 때 저장된 레이아웃을 복원합니다. LoRA-GA의 "stable" 스케일링도 저장 레이아웃과 무관하게 출력 차원을 사용합니다.

init_lora_weights="gaussian"를 전달하는 것도 가능합니다. 이름에서 알 수 있듯이, 이는 가중치 A를 가우시안 분포로, 가중치 B를 0으로 초기화합니다(Diffusers가 LoRA 가중치를 초기화하는 방식입니다).

from peft import LoraConfig

config = LoraConfig(init_lora_weights="gaussian", ...)

디버깅 및 테스트에 유용한 init_lora_weights=False 옵션도 있습니다. 이 옵션은 이 경우에만 사용해야 합니다. 이 옵션을 선택하면 LoRA 가중치가 항등 변환(identity transform)을 생성하지 않도록 초기화됩니다.

from peft import LoraConfig

config = LoraConfig(init_lora_weights=False, ...)
NoRA
PiSSA
MiCA
CorDA
Astra
OLoRA
EVA
LoftQ
rsLoRA
LoRA-GA

Normalized Low-Rank Adaptation (NoRA)는 LoRA 다운프로젝션 행렬을 정규화합니다. PEFT는 LoraConfig의 init_lora_weights="nora"를 통해 초기화 단계에서만 이 정규화를 적용합니다: 무작위로 초기화된 LoRA A 가중치의 열을 정규화하고, LoRA B 가중치를 0으로 초기화합니다. 두 행렬 모두 학습 가능하며, 기본 가중치는 변경되지 않습니다.

선형 레이어의 경우, 어댑터 가중치 업데이트는 $\Delta W = sBA$이며, 여기서 AA 의 형태는 (r, in_features), BB 의 형태는 (out_features, r), 이고 ss 은 LoRA 스케일링 팩터입니다. NoRA는 LoRA 선형 레이어가 생성한 무작위 A 가중치에서 시작하여 랭크 차원별로 각 열을 정규화합니다: A:,j←A:,jmax⁡(∥A:,j∥2,10−6),B←0. A_{:,j} \leftarrow \frac{A_{:,j}}{\max(\lVert A_{:,j} \rVert_2, 10^{-6})}, \qquad B \leftarrow 0.

B가 0이므로 학습 전 어댑터 가중치 업데이트는 0입니다. bias="none" 및 lora_bias=False를 사용하면 어댑터는 초기에 베이스 모델의 출력을 유지합니다. NoRA는 교정 데이터셋이나 가중치 분해가 필요하지 않습니다.

NoRA의 경우 권장 설정은 lora_alpha을 r과 동일하게 하는 것입니다(1:1의 alpha/rank 비율). 예를 들어, r=8, lora_alpha=8입니다. 기본값인 use_rslora=False를 사용하면 스케일링 계수가 1로 유지되며, rank-stabilized LoRA를 활성화하면 lora_alpha / sqrt(r)로 변경됩니다.

다음 예제는 작은 PyTorch 모델의 두 선형 레이어에 NoRA를 적용합니다:

from torch import nn

from peft import LoraConfig, get_peft_model

model = nn.Sequential(
    nn.Linear(32, 32),
    nn.ReLU(),
    nn.Linear(32, 16),
)

config = LoraConfig(
    r=8,
    lora_alpha=8,
    target_modules=["0", "2"],
    init_lora_weights="nora",
    bias="none",
    lora_bias=False,
    use_rslora=False,
)
model = get_peft_model(model, config)
model.print_healthcheck()

Transformers 모델의 경우, target_modules를 모델에 해당 이름이 존재하는 경우 ["q_proj", "v_proj"]와 같이 적응(adapt)하려는 선형 레이어의 이름으로 설정합니다.

NoRA는 기존 LoRA 구성, 학습, 저장 및 로드 API를 사용하므로 별도의 NoraConfig 또는 NoraModel 클래스가 없습니다. 공유 구성 및 모델 API에 대해서는 LoraConfig 및 LoraModel을 참조하세요.

NoRA는 torch.nn.Linear 대상 모듈 또는 target_parameters를 통해 직접 전달된 nn.Parameter를 지원합니다. 임베딩, 컨볼루션 레이어 등에는 지원하지 않습니다.

학습

이 섹션에서는 모델에 저순위 어댑터만 적용하고 데이터를 공급하는 것보다 더 복잡한 학습 시나리오를 처리하는 방법을 보여줍니다.

QLoRA 스타일 학습

PEFT의 기본 LoRA 설정은 각 attention 블록의 query와 value 레이어에 학습 가능한 가중치를 추가합니다. 그러나 트랜스포머 모델의 모든 linear 레이어에 학습 가능한 가중치를 추가하는 QLoRA는 완전한 fine-tuning 모델과 동등한 성능을 제공할 수 있습니다. QLoRA처럼 모든 linear 레이어에 LoRA를 적용하려면 target_modules="all-linear"을 설정합니다(아키텍처에 따라 달라질 수 있는 개별 모듈을 이름으로 지정하는 것보다 간단합니다).

config = LoraConfig(target_modules="all-linear", ...)

PEFT 어댑터에 양자화를 적용하는 방법에 대한 자세한 내용은 양자화 가이드를 참고하세요.

LoRA를 활용한 메모리 효율적인 레이어 복제

모델의 성능을 향상시키기 위한 방법 중 하나는, 주어진 크기의 사전 학습 모델에서 레이어를 복제하여 모델을 확장함으로써 더 큰 모델을 구축하는 것입니다. 예를 들어, SOLAR 논문에서 설명된 바와 같이 7B 모델을 10B 모델로 늘리는 경우입니다. PEFT LoRA는 레이어 복제 후 해당 레이어에 연결된 LoRA 어댑터를 사용하여 추가적인 미세 조정을 지원하는 방식으로, 이러한 확장을 메모리 효율적으로 지원합니다. 복제된 레이어는 기존 가중치를 공유하므로 추가 메모리를 사용하지 않으며, 필요한 추가 메모리는 어댑터 가중치를 위한 메모리뿐입니다. 이 기능을 사용하려면 layer_replication 인수를 사용하여 설정을 생성합니다.

config = LoraConfig(layer_replication=[[0,4], [2,5]], ...)

원본 모델이 5개의 레이어 [0, 1, 2 ,3, 4]를 가졌다고 가정하면, 이는 [0, 1, 2, 3, 2, 3, 4]로 구성된 7개의 레이어를 가진 모델을 생성합니다. 이는 mergekit의 pass through merge 규약을 따르며, 시작 레이어를 포함하고 끝 레이어를 제외하는 튜플로 지정된 레이어 시퀀스를 쌓아 최종 모델을 구성합니다. 최종 모델의 각 레이어에는 고유한 LoRA 어댑터 세트가 할당됩니다.

Fewshot-Metamath-OrcaVicuna-Mistral-10B는 Mistral-7B를 10B로 확장하여 이 방법으로 학습한 모델의 예시입니다. adapter_config.json은 이 방법을 적용하여 파인튜닝을 수행하는 LoRA 어댑터 설정의 샘플을 보여줍니다.

랭크와 알파(스케일링)에 대한 세밀한 제어

기본적으로 LoRA가 적용되는 모든 레이어는 LoraConfig에서 지정된 값에 따라 동일한 랭크 r와 동일한 lora_alpha(LoRA 스케일링을 결정)을 사용합니다. 그러나 일부 경우에는 레이어별로 다른 값을 지정하고 싶을 수 있습니다. LoraConfig에 rank_pattern 및 alpha_pattern 인수를 전달하면 이를 수행할 수 있습니다. 이 인수는 키가 레이어 이름이고 값이 랭크/알파 값인 사전(dictionary)이어야 합니다. 키는 정규 표현식(regex)일 수 있습니다. rank_pattern과 alpha_pattern에 명시적으로 언급되지 않은 모든 LoRA 레이어는 기본 r 및 lora_alpha 값을 사용합니다.

예를 들어, 다음과 같은 구조를 가진 모델이 있다고 가정해 보겠습니다.

>>> print(model)
Outer(
  (foo): Linear(...)
  (module): Middle(
    (foo): Linear(...)
    (foobar): Linear(...)
    (module): Inner(
      (foo): Linear(...)
      (barfoo): Linear(...)
    )
  )
)
  • rank_pattern={"foo": 42}은 모든 3개의 foo 레이어와 일치합니다. foobar도 barfoo도 일치하지 않습니다.
  • rank_pattern={"^foo": 42}은 모델의 foo 레이어만 일치시키며, module.foo나 module.module.foo는 일치시키지 않습니다. 이는 정규식에서 ^가 “문자열의 시작”을 의미하기 때문이며, foo만 "foo"로 시작하고 다른 레이어 이름은 접두사가 있기 때문입니다.
  • rank_pattern={"^module.foo": 42}는 같은 이유로 module.foo만 일치하고 module.module.foo는 일치하지 않습니다.
  • rank_pattern={"module.foo": 42}은 module.foo와 module.module.foo 모두에 일치하지만 foo에는 일치하지 않습니다.
  • rank_pattern={"^foo": 42, "^module.module.foo": 55}은 각각 foo와 module.module.foo에 일치하지만 module.foo에는 일치하지 않습니다.
  • PEFT가 자동으로 추가하므로 매치의 끝을 표시하기 위해 $를 명시할 필요가 없습니다.

alpha_pattern에도 동일한 논리가 적용됩니다. 확실하지 않다면 정규식으로 복잡한 처리를 시도하지 마세요. rank/alpha가 다른 각 모듈에 대해 ^ 접두사가 붙은 전체 이름을 그대로 전달하면 됩니다.

적합한 대상 모듈을 자동으로 감지

peft.helpers.KappaTuneSelector는 KappaTune 논문의 조건수 기반 대상 선택 전략을 구현합니다. 모든 nn.Linear 모듈을 스캔하고, MoE 전문가 가중치가 융합된 3차원 nn.Parameter 텐서로 저장된 모델(예: Llama-4, Qwen3-MoE)의 경우 해당 파라미터도 포함하여 각각의 행렬 조건수 κ = σ_max / σ_min을 계산한 후, 가장 등방적인 레이어(가장 낮은 κ)를 선택합니다. 이러한 등방적인 레이어는 고엔트로피 특성으로 인해 새로운 정보를 더 쉽게 흡수할 수 있어 미세 조정의 이상적인 후보로 작용하며, 전문화된 비등방적인 레이어는 그대로 유지되어 지속 학습过程中的 재앙적 망각을 완화합니다.

LoraConfig에 대한 최적의 target_modules를 한 줄로 가져오려면 peft.helpers.find_kappa_target_modules()를 사용하세요:

from peft import LoraConfig, get_peft_model
from peft.helpers import find_kappa_target_modules

model = AutoModelForCausalLM.from_pretrained("mistralai/Mixtral-8x7B-Instruct-v0.1")

targets = find_kappa_target_modules(model, top_p=0.2)
config = LoraConfig(
    target_modules=targets["target_modules"],
    target_parameters=targets["target_parameters"] if stable_modules_dic["target_parameters"] else None,
    r=64,
    lora_alpha=32,
    task_type="CAUSAL_LM",
)
peft_model = get_peft_model(model, config)

완전한 예제는 여기에서 확인하세요.

nn.Parameter를 직접 대상으로 지정

일반적으로 모듈(예: nn.Linear)을 대상으로 지정하려면 target_modules를 사용해야 합니다. 그러나 일부 상황에서는 이것이 불가능합니다. 예를 들어, HF Transformers의 많은 mixture of expert (MoE) 레이어에서는 nn.Linear 대신 nn.Parameter를 사용합니다. PEFT는 LoRA의 경우 forward 메서드를 기본적으로 오버라이드하지만, nn.Parameter에는 해당 메서드가 없습니다. 따라서 해당 파라미터에 LoRA를 적용하려면 target_parameters로 대상 지정을 해야 합니다. 예를 들어, Llama4의 경우 다음을 전달할 수 있습니다: target_parameters=['feed_forward.experts.gate_up_proj', 'feed_forward.experts.down_proj].

expert 파라미터를 대상으로 할 때 PEFT는 상당한 런타임 오버헤드를 추가할 수 있습니다. 그 이유는 필요한 expert의 수가 적더라도 PEFT가 각 expert에 대해 LoRA 기여분을 항상 materialize하기 때문입니다. 학습 중에는 시퀀스 진행 과정에서 일반적으로 expert의 상당 부분이 최소 한 번 이상 활성화되므로 이 영향이 덜합니다. 그러나 추론 시에는 일반적으로 KV cache가 사용되어 마지막 토큰만 계산하면 되므로, 활성화되는 expert의 수가 적습니다. 따라서 MoE 레이어에 LoRA를 사용하면 추론 시 상당한 지연이 발생할 수 있습니다. 이에 대한 권장 사항은 가중치를 병합하는 것입니다(model.merge_adapter() 또는 model = model.merge_and_unload()). 이렇게 하면 PEFT 오버헤드가 제거됩니다.

이 문제에 대한 보다 자세한 조사는 이 MoE 최적화 pull request에서 확인할 수 있습니다.

주의 사항

  • 현재 이 인수는 2차원 또는 3차원 nn.Parameter를 대상으로 할 수 있습니다. 3차원 매개변수의 경우, 0번째 차원이 전문가(expert) 차원으로 가정됩니다.
  • target_parameters를 사용하는 여러 LoRA 어댑터(model.add_adapter 또는 model.load_adapter를 통해 추가)가 지원되지만, 모든 어댑터는 동일한 매개변수 집합을 대상으로 해야 합니다.

MoE 전문가 파라미터와 vLLM

Transformers의 일부 MoE 모델은 전문가 가중치를 nn.Linear 모듈이 아닌 nn.Parameter 텐서(종종 3차원)로 저장합니다. 해당 전문가에 LoRA를 적용하려면 target_parameters를 사용하고 rank_pattern으로 레이어별 랭크를 설정하세요:

num_experts = getattr(model.config, "num_local_experts", None) or model.config.num_experts
effective_r = max(1, r // num_experts)
config = LoraConfig(
    r=r,
    lora_alpha=32,
    target_modules=["q_proj", "v_proj"],
    target_parameters=[
        # Mixtral / Qwen3-MoE / GPT-OSS
        "mlp.experts.gate_up_proj",
        "mlp.experts.down_proj",
        # Llama4
        # "feed_forward.experts.gate_up_proj",
        # "feed_forward.experts.down_proj",
    ],
    rank_pattern={
        "experts.gate_up_proj": effective_r,
        "experts.down_proj": effective_r,
    },
)

이로 인해 LoRA 파라미터의 총 예산이 dense layer와 유사하게 유지됩니다(Schulman 외의 LoRA Without Regret 참고). Non-expert 모듈은 기본 rank r를 사용합니다.

예를 들어, v0.11.2부터 fused MoE expert layers를 지원하는 vLLM을 사용하면 fine-tuned 모델로 가속화된 추론이 가능합니다.

LoRA와 함께 토큰을 효율적으로 학습

PEFT LoRA 어댑터는 trainable_token_indices 파라미터를 통해 새 토큰을 추가하는 것을 지원합니다. 이를 통해 특정 레이어의 파인튜닝과 함께 다른 토큰도 튜닝할 수 있습니다. 지정된 토큰만 학습되며 모든 다른 토큰은 변경되지 않습니다. 이는 메모리를 절약하고, 전체 임베딩 행렬을 학습하는 경우와 달리 기존 토큰 임베딩에서 학습된 컨텍스트를 유지합니다. 내부적으로 이 방법은 TrainableTokensModel의 레이어를 사용합니다.

# for layer 'embed_tokens'
config = LoraConfig(trainable_token_indices=[idx_1, idx_2, ...], ...)

# specific embedding layer
config = LoraConfig(trainable_token_indices={'emb_tokens': [idx_1, idx_2, ...]}, ...)

아래 코드 조각에서는 모델에 새로운 토큰을 추가하는 방법과 모델의 다른 레이어와 함께 이를 학습하는 방법을 보여줍니다.

from transformers import AutoTokenizer, AutoModelForCausalLM
from peft import get_peft_model, LoraConfig

base_model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1")
tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-v0.1")

# we define our new tokens and add them to the tokenizer as special tokens
special_tokens = ['<|start_think|>', '<|stop_think|>']
tokenizer.add_special_tokens({'additional_special_tokens': special_tokens})

# make room for new tokens in the embedding matrix if it isn't big enough already
base_model.resize_token_embeddings(max(len(tokenizer), base_model.model.embed_tokens.num_embeddings))

# typical LoRA config with `trainable_token_indices` targeting embedding layer `embed_tokens`
# and specifically our new tokens we just added
lora_config = LoraConfig(
    target_modules='all-linear',
    trainable_token_indices={'embed_tokens': tokenizer.convert_tokens_to_ids(special_tokens)},
)
peft_model = get_peft_model(base_model, lora_config)

# proceed to train the model like normal
[...]

토큰 가중치는 LoRA 가중치와 함께 어댑터 상태 딕셔너리의 일부로 저장됩니다. 전체 파인튜닝 및 임베딩 행렬을 저장했다면 훨씬 더 큰 파일이 저장되었을 것입니다.

VRAM을 얼마나 절약할 수 있는지 대략적인 참고를 위해, 위 예시를 기준으로 임베딩 행렬을 완전히 학습하는 경우(modules_to_save=["embed_tokens"]), 임베딩 행렬에 LoRA를 사용하는 경우(target_modules=[..., "embed_tokens"], 랭크 32), 학습 가능한 토큰을 사용하는 경우(trainable_token_indices=[...], 6개 토큰)를 비교했습니다:

학습 가능한 토큰LoRAFull Fine-tuning
VRAM15,562 MB15,581MB~16,500MB
영향6 토큰모든 토큰모든 토큰

Weight tying

많은 causal LM은 weight tying를 사용합니다. 이는 두 개 이상의 가중치가 동일한 기본 매개변수를 공유하는 기법입니다. 가장 일반적인 경우, 입력 임베딩 가중치(embed_tokens)와 출력 투영 가중치(lm_head)가 동일한 텐서를 공유합니다. 이는 매개변수 수를 줄이면서도 일반적으로 모델 품질을 유지하기 때문입니다.

PEFT가 파인튜닝 대상으로 지정된 이러한 tied weights를 어떻게 처리하는지는 항상 명확하지 않습니다. LoRA의 경우, LoraConfig의 ensure_weight_tying가 해당 레이어에 대해 PEFT가 어댑터 측 업데이트를 명시적으로 tied 상태로 유지할지 여부를 제어합니다. 실제로 이는 modules_to_save, target_modules, trainable_token_indices에 영향을 줄 수 있습니다. 레이어 이름 지정("embed_tokens", "lm_head")에 있어 이 로직은 부분적으로 관례에 의존하며, 이러한 관례가 사용되지 않으면 정상적인 동작을 보장할 수 없습니다.

아래 표는 예상 동작을 요약합니다.

modules_to_save

기본 모델 가중치 연결ensure_weight_tyingLoraConfig 형식동작
아니요Falsemodules_to_save=["embed_tokens"] 또는 ["lm_head"]ModulesToSaveWrapper를 선택된 레이어에만 추가
아니요Truemodules_to_save=["embed_tokens"] 또는 ["lm_head"]경고를 표시한 다음 선택된 레이어에만 ModulesToSaveWrapper를 추가합니다
예Falsemodules_to_save=["embed_tokens"] 또는 ["lm_head"]별도로 취급
예Truemodules_to_save=["embed_tokens"] 또는 ["lm_head"]tied layers를 래핑하고 래퍼도 tied 상태로 유지
아니요Falsemodules_to_save=["embed_tokens", "lm_head"]별도로 취급
아니요Truemodules_to_save=["embed_tokens", "lm_head"]경고한 후, 개별 항목으로 취급
예Falsemodules_to_save=["embed_tokens", "lm_head"]경고한 후, 개별 항목으로 취급
예Truemodules_to_save=["embed_tokens", "lm_head"]ModulesToSaveWrapper를 연결 상태로 유지

target_modules

기본 모델 가중치 연결ensure_weight_tyingLoraConfig 형식동작
아니요Falsetarget_modules=["embed_tokens"] 또는 ["lm_head"]…
아니요Truetarget_modules=["embed_tokens"] 또는 ["lm_head"]경고를 표시한 다음 선택된 레이어에만 LoRA를 추가
예Falsetarget_modules=["embed_tokens"] 또는 ["lm_head"]별도로 취급
예Truetarget_modules=["embed_tokens"] 또는 ["lm_head"]LoRA 어댑터를 연결 상태로 유지
아니요Falsetarget_modules=["embed_tokens", "lm_head"]별도로 취급
아니요Truetarget_modules=["embed_tokens", "lm_head"]경고한 후, 개별 항목으로 취급
예Falsetarget_modules=["embed_tokens", "lm_head"]경고한 후, 개별 항목으로 취급
예Truetarget_modules=["embed_tokens", "lm_head"]LoRA 어댑터를 연결 상태로 유지

trainable_token_indices

학습 가능한 토큰의 경우, LM head와 embeddings가 연결되어 있더라도 사용자가 이 둘에 대해 다른 토큰을 미세 조정하고 싶어 할 수 있다는 추가적인 복잡성이 있습니다. 따라서 아래 예시 표에서는 동일한 토큰을 미세 조정하는 경우와 다른 토큰을 미세 조정하는 경우를 구분합니다.

기본 모델 가중치 연결ensure_weight_tyingLoraConfig 형식동작
아니요Falsetrainable_token_indices=[1, 2, 3]임베딩에만 학습 가능한 토큰
아니요Truetrainable_token_indices=[1, 2, 3]경고, 그런 다음 임베딩에만 학습 가능한 토큰
예Falsetrainable_token_indices=[1, 2, 3]연결된 학습 가능 토큰
예Truetrainable_token_indices=[1, 2, 3]연결된 학습 가능 토큰
아니요Falsetrainable_token_indices={"lm_head": [1, 2], "embed_tokens": [1, 2]}별도로 취급
아니요Truetrainable_token_indices={"lm_head": [1, 2], "embed_tokens": [1, 2]}경고한 후, 개별 항목으로 취급
예Falsetrainable_token_indices={"lm_head": [1, 2], "embed_tokens": [1, 2]}연결된 학습 가능 토큰
예Truetrainable_token_indices={"lm_head": [1, 2], "embed_tokens": [1, 2]}연결된 학습 가능 토큰
아니요Falsetrainable_token_indices={"lm_head": [1, 2], "embed_tokens": [3, 4]}별도로 취급
아니요Truetrainable_token_indices={"lm_head": [1, 2], "embed_tokens": [3, 4]}경고한 후, 개별 항목으로 취급
예Falsetrainable_token_indices={"lm_head": [1, 2], "embed_tokens": [3, 4]}별도로 취급
예Truetrainable_token_indices={"lm_head": [1, 2], "embed_tokens": [3, 4]}오류

사용자에게는 다음과 같은 의미를 갖습니다:

  • 일반적으로, 연결된 가중치를 미세 조정하되 연결 상태를 유지하려면 ensure_weight_tying=True를 전달하세요.
  • 기본 모델의 가중치가 untied 상태인 경우, ensure_weight_tying=True는 tying을 강제할 수 없으며 경고만 출력합니다.
  • trainable_token_indices의 경우, ensure_weight_tying=True일 때 묶인 레이어는 동일한 토큰 인덱스를 사용해야 합니다.

옵티마이저

LoRA 학습에는 선택적으로 특수 목적의 옵티마이저를 포함할 수 있습니다. 현재 PEFT는 LoRA-FA, LoRA+, Riemannian-preconditioned LoRA를 지원합니다.

LoRA-FA 옵티마이저

LoRA-FA에 설명된 바와 같이, LoRA-FA를 사용하면 LoRA 학습이 더 효과적이고 효율적일 수 있습니다. LoRA-FA는 행렬 A를 고정하고 행렬 B만 조정함으로써 활성화 메모리 사용량을 줄입니다. 학습 중에 B의 기울기는 전체 파라미터 미세 조정 기울기를 근사하도록 최적화됩니다. 또한 LoRA-FA의 메모리 사용량은 랭크에 민감하지 않으므로($A$의 활성화를 제거하기 때문), 메모리 사용량 증가 없이 lora 랭크를 늘려 성능을 향상시킬 수 있습니다.

from peft import LoraConfig, get_peft_model
from peft.optimizers import create_lorafa_optimizer
from transformers import Trainer, get_cosine_schedule_with_warmup

base_model = AutoModelForCausalLM.from_pretrained("meta-llama/Meta-Llama-3-8B-Instruct")

config = LoraConfig(...)
model = get_peft_model(base_model, config)

optimizer = create_lorafa_optimizer(
    model=model,
    r=128,
    lora_alpha=32,
    lr=7e-5,
)

scheduler = get_cosine_schedule_with_warmup(
    optimizer,
    num_warmup_steps=100,
    num_training_steps=1000,
)

trainer = Trainer(
    ...,
    optimizers=(optimizer, scheduler),
)

LoRA+ 최적화 LoRA

LoRA 학습은 LoRA+를 사용하여 최적화할 수 있으며, 이는 어댑터 행렬 A와 B에 서로 다른 학습률을 적용하여 파인튜닝 속도를 최대 2배, 성능을 1~2%까지 향상시키는 것으로 입증되었습니다.

from peft import LoraConfig, get_peft_model
from peft.optimizers import create_loraplus_optimizer
from transformers import Trainer
import bitsandbytes as bnb

base_model = ...
config = LoraConfig(...)
model = get_peft_model(base_model, config)

optimizer = create_loraplus_optimizer(
    model=model,
    optimizer_cls=bnb.optim.Adam8bit,
    lr=5e-5,
    loraplus_lr_ratio=16,
)
scheduler = None

...
trainer = Trainer(
    ...,
    optimizers=(optimizer, scheduler),
)

Riemannian-preconditioned LoRA

LoRA 학습은 Riemannian preconditioner를 사용하여 개선할 수 있으며, 이는 Riemannian Preconditioned LoRA. 모든 optimizer step에서 LoRA 행렬 AA 과 BB 의 그래디언트는 r×rr \times r preconditioner에 의해 곱해집니다. 이 preconditioner는 저순위 행렬 다양체 위에서 유클리드 그래디언트를 Riemannian(scaled-gradient) 방향으로 재조정하여 업데이트의 조건을 더 좋게 만들고 수렴을 개선할 수 있습니다. preconditioner가 $r \times r$ 크기이므로, LoRA rank에 비해 메모리와 런타임 오버헤드는 작습니다. create_riemannian_optimizer 는 모든 기본 optimizer 클래스(예: torch.optim.AdamW 또는 torch.optim.SGD): 를 래핑합니다. LoRA가 아닌 파라미터는 기본 optimizer에 의해 변경 없이 업데이트되고, 오직 nn.Linear-형태의 LoRA 레이어(lora_A / lora_B) 에 대해 preconditioning이 적용됩니다.

from peft import LoraConfig, get_peft_model
from peft.optimizers import create_riemannian_optimizer
from transformers import Trainer, get_cosine_schedule_with_warmup
import torch

base_model = AutoModelForCausalLM.from_pretrained("meta-llama/Meta-Llama-3-8B-Instruct")

config = LoraConfig(...)
model = get_peft_model(base_model, config)

optimizer = create_riemannian_optimizer(
    model=model,
    optimizer_cls=torch.optim.AdamW,
    lr=5e-5,
    reg=1e-2,
)

scheduler = get_cosine_schedule_with_warmup(
    optimizer,
    num_warmup_steps=100,
    num_training_steps=1000,
)

trainer = Trainer(
    ...,
    optimizers=(optimizer, scheduler),
)

reg 는 역전환 전에 r×rr \times r 행렬 대각선에 추가되는 감쇠 항입니다. LoRA 인자가 (거의) 랭크 부족 상태일 때 사전 조건자를 안정화합니다.

Post-Training

이 섹션에서는 학습된 어댑터에 적용할 수 있는 후처리 방법을 보여 줍니다.

LoRA 가중치를 기본 모델에 병합

While LoRA is significantly smaller and faster to train, you may encounter latency issues during inference due to separately loading the base model and the LoRA adapter. To eliminate latency, use the merge_and_unload() function to merge the adapter weights with the base model. This allows you to use the newly merged model as a standalone model. The merge_and_unload() function doesn’t keep the adapter weights in memory.

아래는 LoRA 어댑터 병합의 직관을 설명하는 다이어그램입니다:

아래 스니펫에서 PEFT를 사용하여 이를 실행하는 방법을 보여줍니다.

from transformers import AutoModelForCausalLM
from peft import PeftModel

base_model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1")
peft_model_id = "alignment-handbook/zephyr-7b-sft-lora"
model = PeftModel.from_pretrained(base_model, peft_model_id)
model = model.merge_and_unload()

반환된 모델을 변수에 할당하고 사용하는 것이 중요합니다. merge_and_unload()는 in-place 연산이 아니기 때문입니다. 나중에 어댑터를 unmerge하거나 삭제하고 다른 어댑터를 로드하기 위해 가중치 사본을 유지해야 하는 경우, 대신 merge_adapter() 함수를 사용해야 합니다. 이제 unmerge_adapter()를 사용하여 베이스 모델로 되돌릴 수 있습니다.

from transformers import AutoModelForCausalLM
from peft import PeftModel

base_model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1")
peft_model_id = "alignment-handbook/zephyr-7b-sft-lora"
model = PeftModel.from_pretrained(base_model, peft_model_id)
model.merge_adapter()

# unmerge the LoRA layers from the base model
model.unmerge_adapter()

양자화된 모델에서는 unmerge가 정확하지 않습니다. merge는 base weight를 다시 양자화하며, merge와 unmerge 사이클이 반복되면 반올림 오차가 누적될 수 있습니다. 양자화된 가중치에 어댑터 병합을 참조하세요.

여러 개의 어댑터가 활성화되어 있고 그중 일부만 병합된 경우, 병합되지 않은 어댑터는 조용히 적용되지 않습니다. 이 동작은 PEFT v1.0에서 변경됩니다. 활성화된 어댑터 중 일부만 병합하기를 참조하세요.

weights 매개변수에 사용자가 지정한 가중치 스킴을 기반으로 여러 LoRA를 새로운 어댑터로 병합하는 데 add_weighted_adapter() 함수가 유용합니다. 아래는 끝에서 끝까지(end-to-end) 진행되는 예시입니다.

먼저 기본 모델을 로드합니다:

from transformers import AutoModelForCausalLM
from peft import PeftModel
import torch

base_model = AutoModelForCausalLM.from_pretrained(
    "mistralai/Mistral-7B-v0.1", dtype=torch.float16, device_map="auto"
)

그런 다음 첫 번째 어댑터를 로드합니다:

peft_model_id = "alignment-handbook/zephyr-7b-sft-lora"
model = PeftModel.from_pretrained(base_model, peft_model_id, adapter_name="sft")

그 다음 다른 어댑터를 로드하고 첫 번째 어댑터와 병합합니다:

weighted_adapter_name = "sft-dpo"
model.load_adapter("alignment-handbook/zephyr-7b-dpo-lora", adapter_name="dpo")
model.add_weighted_adapter(
    adapters=["sft", "dpo"],
    weights=[0.7, 0.3],
    adapter_name=weighted_adapter_name,
    combination_type="linear"
)
model.set_adapter(weighted_adapter_name)

combination_type에 대해 여러 가지 지원되는 방법이 있습니다. 자세한 내용은 문서를 참조하세요. 데이터 타입으로 torch.float16 또는 torch.bfloat16를 사용하는 경우, combination_type으로 “svd”는 지원되지 않습니다.

이제 추론을 수행합니다:

device = torch.accelerator.current_accelerator().type if hasattr(torch, "accelerator") else "cuda"

tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-v0.1")

prompt = "Hey, are you conscious? Can you talk to me?"
inputs = tokenizer(prompt, return_tensors="pt")
inputs = {k: v.to(device) for k, v in inputs.items()}

with torch.no_grad():
    generate_ids = model.generate(**inputs, max_length=30)
outputs = tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
print(outputs)

침입자 차원 축소로 베이스 모델 성능 복구

LoRA vs Full Fine-tuning: An Illusion of Equivalence 논문은 LoRA 학습이 이미 학습된 가중치와 거의 공통점이 없는 추가 차원을 가중치에 도입하여 이미 학습된 정보를 망각하게 만든다고 주장합니다. PEFT는 peft.tuners.lora.intruders.reduce_intruder_dimension()에서 제안된 완화 조치를 구현합니다.

완화 방법은 로드된 LoRA가 포함된 PEFT 모델을 가져와서, 기존 어댑터와 함께 로드되며 이제 활성 어댑터가 되는 새로운 수정된 어댑터를 생성합니다.

사용 예시:

from peft.tuners.lora.intruders import reduce_intruder_dimension

peft_model = AutoPeftModelForCausalLM.from_pretrained('hubnemo/llama-3.2b-metamathqa-lora64')

reduce_intruder_dimension(
    peft_model,
    mitigation_lambda=0.75,
)

peft_model.generate(...)

완화 효과의 효율성을 조정하는 데 사용할 수 있는 하이퍼파라미터가 몇 가지 있지만, 해당 논문의 Figure 8에서 입증된 바와 같이 어댑터가 학습한 작업 정확도와 베이스 모델의 지식 망각 사이에는 항상 트레이드오프가 존재합니다. 완화는 이전 지식의 망각에 미치는 영향을 줄이기 위해 어댑터에서 정보를 제거하지만, 이는 어댑터가 학습한 작업에 대한 일부 정보도 함께 손실된다는 것을 의미합니다.

기본값은 두 요인 간의 좋은 균형점을 제공하도록 설정되어 있지만, 해당 기본값이 사용자의 어댑터, 모델 및 데이터에 항상 적용된다는 보장은 없으므로, 그 효과를 측정할 수 있는 벤치마크를 미리 준비해 두는 것이 현명합니다.

어댑터 로드

Adapters는 load_adapter()를 통해 사전 학습된 모델에 로드할 수 있으며, 가중치가 병합되지 않은 다양한 어댑터를 테스트하는 데 유용합니다. 활성 어댑터 가중치는 set_adapter() 함수로 설정합니다.

from transformers import AutoModelForCausalLM
from peft import PeftModel

base_model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1")
peft_model_id = "alignment-handbook/zephyr-7b-sft-lora"
model = PeftModel.from_pretrained(base_model, peft_model_id)

# load different adapter
model.load_adapter("alignment-handbook/zephyr-7b-dpo-lora", adapter_name="dpo")

# set adapter as active
model.set_adapter("dpo")

기본 모델을 반환하려면 unload()를 사용하여 모든 LoRA 모듈을 언로드하거나 delete_adapter()를 사용하여 어댑터를 완전히 삭제할 수 있습니다. unload()는 in-place 연산이 아니므로, 반환된 모델을 변수에 할당하여 사용해야 합니다.

# unload adapter
model = model.unload()

# delete adapter
model.delete_adapter("dpo")

Tensor 병렬

LoRA는 Transformers에서 제공하는 Tensor Parallelism (TP)를 지원합니다. 기본 모델이 tp_plan과 함께 로드되면, PEFT는 각 대상 모듈의 TP 구성을 자동으로 감지하고 LoRA 어댑터 가중치에 적절한 훅을 추가하여 텐서 병렬 계산에 올바르게 참여하도록 합니다.

LoRA에 대한 Tensor Parallelism 지원에는 transformers >= 5.4.0이 필요합니다. DTensor 기반 Tensor Parallelism API에는 peft >= 0.21.1과 transformers >= 5.17.0이 필요합니다.

사용법은 표준 LoRA 워크플로우와 동일합니다. PEFT로 래핑하기 전에 tp_plan을 사용하여 기본 모델을 로드하면 됩니다:

from transformers import AutoModelForCausalLM
from peft import get_peft_model, LoraConfig

model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.1-8B", tp_plan="auto")
lora_config = LoraConfig(r=16, target_modules=["q_proj", "v_proj"])
model = get_peft_model(model, lora_config)

save_pretrained / from_pretrained를 통해 저장 및 로딩은 평소와 동일하게 작동합니다. PEFT는 저장 전에 샤드된 어댑터 가중치를 전체 텐서로 다시 모으므로, 체크포인트는 이동 가능하며 학습에 사용된 장치 수와 무관합니다.

TP 플랜 없이 텐서 병렬성을 적용하는 경우, 예를 들어 PyTorch의 parallelize_module를 사용하는 경우, PEFT는 가중치의 디바이스 메시에 "tp"라는 이름의 차원이 있으면 샤딩된 nn.Linear의 LoRA 가중치를 해당 로컬 샤드 크기로 설정하고, 그렇지 않으면 전체 레이어 형상으로 설정합니다(FSDP2와 동일). PEFT는 이러한 LoRA 가중치에 TP 훅을 추가하지 않으며, 저장 시에도 이를 수집하지 않습니다. TP 메시 차원의 이름이 다른 경우, PEFT 모델을 생성하기 전에 해당 이름을 추가하세요:

from peft.utils.integrations import TpMeshDimRegistry

TpMeshDimRegistry.register("tp_dim")

추론

이 섹션에서는 LoRA를 사용하여 추론 시기에 수행할 수 있는 작업, 예를 들어 어댑터 분리(uncoupling) 등을 소개합니다.

활성화 LoRA(aLoRA)

Activated LoRA(aLoRA)는 인과적 LM을 위한 저랭크 어댑터 아키텍처로, 기존 베이스 모델의 KV 캐시를 재사용하여 보다 효율적인 추론을 수행합니다. 이 방식은 대부분의 작업/생성에서 베이스 모델에 의존하지만, 체인 내에서 전문화된 작업을 수행하기 위해 aLoRA 어댑터를 사용하는 추론 파이프라인에 가장 적합합니다. 예를 들어, 베이스 모델의 생성된 출력을 확인하거나 수정하는 경우입니다. 이러한 설정에서는 추론 시간을 10배 이상 단축할 수 있습니다. aLoRA에 대한 추가 정보와 다양한 사용 사례는 aLoRA 논문을 참고하세요.

이 기법은 각 입력에서 호출 시퀀스(alora_invocation_tokens)의 마지막 발생 위치를 스캔합니다(이것은 1개의 토큰만으로도 가능합니다). 호출 시퀀스의 시작 부분부터 시작하는 토큰에 대해 어댑터 가중치를 활성화합니다. 호출 시퀀스 이후의 모든 입력도 적응되며, 생성되는 모든 토큰은 적응된 가중치를 사용합니다. 이전 토큰의 가중치는 적응되지 않은 상태로 유지되며, 이는 인과적 LM의 인과적 어텐션 마스크로 인해 해당 토큰의 캐시가 베이스 모델 캐시와 상호 교환 가능하게 만듭니다. 사용법은 표준 LoRA와 매우 유사합니다. 주요 차이점은 어댑터 생성 시 호출 시퀀스를 지정해야 한다는 것입니다:

from peft import LoraConfig

config = LoraConfig(alora_invocation_tokens=alora_invocation_tokens, task_type="CAUSAL_LM", ...)

alora_invocation_tokens는 정수 토큰 ID의 목록입니다. 원하는 호출 문자열이 주어지면, 다음과 같이 이를 얻을 수 있습니다:

invocation_string = "placeholder"
alora_invocation_tokens = tokenizer.encode(invocation_string, add_special_tokens=False).

토크나이저는 베이스 모델의 토크나이저입니다. 검색 문자열에 SOS/EOS 토큰이 추가되지 않도록 add_special_tokens=False를 사용하세요(추가되면 검색이 실패할 가능성이 높습니다).

참고

  • aLoRA는 캐시 재사용에 초점을 맞추고 있어 task_type=CAUSAL_LM 작업만 지원합니다.
  • 가중치가 더 적은 토큰에 대해 적응되므로, aLoRA는 LoRA보다 더 높은 랭크(r)가 필요한 경우가 많습니다(항상 그런 것은 아닙니다). r=32를 좋은 시작점으로 사용할 수 있습니다.
  • aLoRA 가중치는 어댑터 가중치가 토큰의 부분 집합에 선택적으로 적용되기 때문에 정의상 베이스 모델에 병합할 수 없습니다. 병합을 시도하면 오류가 발생합니다.
  • Beam search은 아직 지원되지 않습니다.
  • 기본 모델에 없는 토큰을 토크나이저에 추가하는 것은 일반적으로 권장되지 않습니다. 이는 기본 모델과 어댑터 모델이 겹치는 컨텍스트에서 작동하는 대상 사용 사례를 복잡하게 만들 수 있습니다. 이를 우회하려면 어댑터 학습 전에 기본 모델에 학습 가능한 토큰을 추가할 수 있습니다.

호출 시퀀스 선택 및 SFT 설계

alora_invocation_tokens 시퀀스는 자동으로 추가되지 않으므로 반드시 추가해야 합니다. 캐시 재사용을 해치지 않으면서 모델 성능을 극대화하기 위해, 어댑터 가중치를 가능한 한 초기(어댑터 전용 프롬프팅의 시작 부분)에 활성화하되, 긴 입력 이후에 활성화하는 것을 권장합니다. 모든 모델과 마찬가지로, 학습과 테스트 간에 포맷이 일관되어야 합니다.

다음 예시를 살펴보세요. 베이스 모델에 채팅 템플릿이 있고, 어댑터가 원하는 출력을 생성하도록 학습하는 것이 목표입니다.

  • 옵션 1: 작업별 프롬프트가 없으면, 즉 입력이 assistant 프롬프트를 가진 채팅 히스토리인 경우, 채팅 템플릿의 assistant 프롬프트(예: <|start_of_role|>assistant<|end_of_role|>)를 호출 문자열로 사용하는 것이 자연스러운 선택입니다. 모델의 채팅 템플릿을 확인하여 해당 모델의 프롬프트를 찾아보세요.
  • 옵션 2: 어댑터가 학습하는 작업을 설명하는 작업별 프롬프트가 있고, 해당 프롬프트가 생성 직전의 user 턴으로 배치된다면, 채팅 템플릿의 user 프롬프트(예: <|start_of_role|>user<|end_of_role|>)가 호출 문자열로 자연스러운 선택입니다.

호출 문자열을 결정하면, 모델의 토크나이저를 가져와 alora_invocation_tokens를 다음과 같이 얻습니다

alora_invocation_tokens = tokenizer.encode(invocation_string, add_special_tokens=False).

추론 설정의 예시는 alora finetuning에 있습니다.

invocation string에 사용자 정의 문자열을 사용하는 경우, 경계에서의 토큰화 문제를 방지하기 위해 문자열의 시작과 끝이 특수 토큰인지 확인하세요.

그 이유를 이해하려면, 토크나이저에서 ‘a’, ‘b’, ‘c’, ‘ab’가 각각 토큰(번호 1, 2, 3, 4)이라고 가정해 보세요. alora_invocation_tokens = [2, 3]이라고 가정합니다. 이제 입력 문자열이 “abc”라고 생각해 보세요. “ab”가 토큰이므로, 이는 [4,3]으로 토큰화됩니다. 따라서 문자열에 “bc”가 포함되어 있음에도 불구하고 alora_invocation_tokens는 발견되지 않습니다. 그러나 호출 문자열의 시작과 끝이 특수 토큰이라면, 특수 토큰은 다른 문자와 동일한 토큰으로 토큰화되지 않으므로 이러한 실패 사례는 절대 발생하지 않습니다.

생성을 위한 캐시 사용(및 재사용)

aLoRA의 주요 목적은 기본 모델과 aLoRA 어댑터 모델 간의 KV 캐시를 호출 시퀀스 이전에 교환 가능하게 만드는 것입니다. 기본 모델과 어댑터 모델의 KV 값은 호환되지 않기 때문입니다. 구체적으로, 한 번의 모델 생성 동안 저장된 키와 값은 컨텍스트 토큰에 대한 비용이 높은 prefill 작업을 피하기 위해 후속 생성에 사용할 수 있습니다. 기본 모델과 aLoRA 어댑터 간에 캐시를 공유할 때, 두 가지 주요 패턴이 있습니다:

  1. 기본 모델이 무언가를 생성한 후, aLoRA 어댑터가 호출되어 후속 생성을 수행합니다. 예를 들어, 기본 모델이 질문에 답하고, 환각(hallucination) 감지를 위해 학습된 aLoRA가 기본 모델의 응답을 검사하는 경우입니다.
  2. aLoRA 어댑터가 어떤 것을 생성한 후, 베이스 모델 또는 다른 aLoRA 어댑터가 원래 aLoRA와 부분적으로 컨텍스트가 겹치는 후속 생성을 수행하도록 호출되는 경우입니다. 예를 들어, 사용자가 쿼리를 제공하고 aLoRA가 RAG 시스템에서 검색을 개선하기 위해 쿼리를 더 자기 완결적으로 재작성합니다. 그런 다음 문서가 검색되어 컨텍스트에 로드되고, aLoRA가 이 문서들이 질문에 관련되는지 확인한 후, 베이스 모델이 답변을 생성합니다.

캐싱을 사용할 때 위 동작을 시연하기 위해 transformers의 DynamicCache를 사용합니다. 적응된 캐시 값이 기본 캐시 값과 섞이지 않도록 주의하세요. 특히 부분적인 컨텍스트 중복이 있는 경우(패턴 2)에는 캐시를 공유하려면 추가 단계가 필요합니다.

패턴 1: 베이스 모델 후 aLoRA 여기서 베이스 모델의 전체 입력과 생성 결과가 호출 시퀀스와 함께 aLoRA 어댑터에 입력됩니다:

from transformers import DynamicCache
...
cache = DynamicCache()
inputs_base = tokenizer(prompt_base, return_tensors="pt")
# Generate from base model and save cache
with model_alora.disable_adapter():
    output = model_alora.generate(inputs_base["input_ids"].to(device),attention_mask=inputs_base["attention_mask"].to(device),past_key_values = cache,return_dict_in_generate=True)
output_text_base = tokenizer.decode(output.sequences[0])
cache = output.past_key_values

# Generate with aLoRA adapter from cache
prompt_alora = output_text + INVOCATION_STRING
inputs_alora = tokenizer(prompt_alora, return_tensors="pt").to(device)
output = model_alora.generate(**inputs_alora, past_key_values=cache)
output_text_alora = tokenizer.decode(output[0])

# Note: cache is now tainted with adapter values and cannot be used in base model from here on!

패턴 2: aLoRA 생성 후 부분적인 컨텍스트가 겹치는 베이스 모델(또는 다른 aLoRA) 사용 여기서는 베이스 모델을 사용하여 공유 컨텍스트를 사전 입력(prefill)한 다음 생성을 수행합니다.

from transformers import DynamicCache
import copy
...
cache = DynamicCache()
inputs_shared = tokenizer(prompt_shared, return_tensors="pt").to(device)

# Prefill from base model and save cache
with model_alora.disable_adapter():
    with torch.no_grad():
        model_alora(**inputs_shared, past_key_values=cache)
cache_copy = copy.deepcopy(cache)

# Generate from aLoRA using prefilled cache
prompt_alora = prompt_shared + INVOCATION_STRING
inputs_alora = tokenizer(prompt_alora, return_tensors="pt").to(device)
output = model_alora.generate(**inputs_alora, past_key_values=cache)
output_text_alora = tokenizer.decode(output[0])

# Generate from base model using saved cache not tainted by aLoRA KV values
prompt_base = prompt_shared
inputs_base = tokenizer(prompt_base, return_tensors="pt").to(device)
with model_alora.disable_adapter():
    output = model_alora.generate(**inputs_base, past_key_values=cache_copy)
output_text_base = tokenizer.decode(output[0])

같은 배치에서 서로 다른 LoRA 어댑터로 추론

일반적으로 PEFT에서는 각 추론 배치에 동일한 어댑터를 사용해야 합니다. 이는 때때로 번거로울 수 있습니다. 서로 다른 LoRA 어댑터와 함께 사용하도록 의도된 샘플이 포함된 배치가 있을 수 있기 때문입니다. 예를 들어, 영어에서 잘 작동하는 베이스 모델과 프랑스어용, 독일어용 LoRA 어댑터 두 개가 있을 수 있습니다. 일반적으로 각 배치에 하나의 언어 샘플만 포함되도록 배치를 분리해야 하며, 서로 다른 언어를 동일한 배치에 결합할 수 없습니다.

다행히도 adapter_name 인수를 사용하여 동일한 배치에 서로 다른 LoRA 어댑터를 혼합할 수 있습니다. 아래에서는 이를 실제로 적용하는 예시를 보여줍니다. 먼저, 기본 모델, 영어, 그리고 두 개의 어댑터인 프랑스어와 독일어를 다음과 같이 로드합니다:

from transformers import AutoTokenizer, AutoModelForCausalLM
from peft import PeftModel

model_id = ...
tokenizer = AutoTokenizer.from_pretrained(model_id)

model = AutoModelForCausalLM.from_pretrained(model_id)
# load the LoRA adapter for French
peft_model = PeftModel.from_pretrained(model, <path>, adapter_name="adapter_fr")
# next, load the LoRA adapter for German
peft_model.load_adapter(<path>, adapter_name="adapter_de")

이제 세 가지 언어가 모두 포함된 샘플에서 텍스트를 생성하려고 합니다. 첫 번째 샘플 3개는 영어, 다음 3개는 프랑스어, 마지막 3개는 독일어입니다. 각 샘플에 사용할 어댑터를 지정하려면 adapter_names 인수를 사용할 수 있습니다. 기본 모델이 영어에 사용되므로, 해당 샘플에는 특수 문자열 "__base__"을 사용합니다. 다음 3개 샘플에는 프랑스어 LoRA 파인튜닝의 어댑터 이름, 이 경우 "adapter_fr"을 지정합니다. 마지막 3개 샘플에는 독일어 LoRA 파인튜닝의 어댑터 이름, 이 경우 "adapter_de"를 지정합니다. 이렇게 하면 기본 모델과 두 개의 어댑터를 단일 배치에서 사용할 수 있습니다.

inputs = tokenizer(
    [
        "Hello, my dog is cute",
        "Hello, my cat is awesome",
        "Hello, my fish is great",
        "Salut, mon chien est mignon",
        "Salut, mon chat est génial",
        "Salut, mon poisson est super",
        "Hallo, mein Hund ist süß",
        "Hallo, meine Katze ist toll",
        "Hallo, mein Fisch ist großartig",
    ],
    return_tensors="pt",
    padding=True,
)

adapter_names = [
    "__base__", "__base__", "__base__",
    "adapter_fr", "adapter_fr", "adapter_fr",
    "adapter_de", "adapter_de", "adapter_de",
]
output = peft_model.generate(**inputs, adapter_names=adapter_names, max_new_tokens=20)

여기서는 순서가 중요하지 않으며, 즉 위의 예시처럼 배치 내 샘플을 어댑터별로 그룹화할 필요가 없습니다. adapter_names 인수가 샘플과 올바르게 정렬되어 있는지 확인하면 됩니다.

또한, 동일한 접근 방식은 modules_to_save 기능과도 함께 작동합니다. 이 기능은 분류 작업용 커스텀 헤드와 같은 특정 신경망 레이어를 저장하고 다른 LoRA 어댑터 간에 재사용할 수 있도록 합니다.

주의 사항

이 기능을 사용하면 다음과 같은 단점이 있습니다:

  • 추론에만 작동하며, 학습에는 작동하지 않습니다.
  • with model.disable_adapter() 컨텍스트를 사용하여 어댑터를 비활성화하는 것이 adapter_names보다 우선합니다.
  • 일부 어댑터 가중치가 merge_adapter 메서드를 사용하여 기본 가중치와 병합된 경우 adapter_names를 전달할 수 없습니다. 먼저 model.unmerge_adapter()를 호출하여 모든 어댑터를 병합 해제하십시오. adapter_names 외부에서도 활성 어댑터 중 일부만 병합하는 경우 유사한 주의 사항이 있습니다.
  • 명백한 이유로, 이 방법은 merge_and_unload() 호출 이후에는 사용할 수 없습니다. 이 경우 모든 LoRA 어댑터가 기본 가중치에 병합되기 때문입니다.
  • 이 기능은 현재 DoRA와 함께 작동하지 않으므로, 이 기능을 사용하려면 LoraConfig에서 use_dora=False로 설정하세요.
  • modules_to_save 기능은 현재 Linear, Embedding, Conv2d, Conv1d 유형의 레이어만 지원합니다.
  • 추론 시 adapter_names, 에 대한 예상 오버헤드가 있습니다. 특히 배치 내 서로 다른 어댑터의 수가 많을 경우 더욱 그렇습니다. 이는 배치 크기가 실제로는 어댑터당 샘플 수로 줄어듦을 의미하기 때문입니다. 런타임 성능이 최우선이라면 다음을 시도해 보세요: 배치 크기를 늘리세요. 같은 배치에 서로 다른 어댑터가 너무 많이 포함되지 않도록 하세요. 동질적인 배치를 선호하는 것이 좋습니다. 이는 동일한 어댑터를 가진 샘플을 버퍼링하고, 소수의 서로 다른 어댑터만 사용하여 추론을 수행함으로써 달성할 수 있습니다. LoRAX, punica, S-LoRA와 같이 서로 다른 어댑터가 많이 있는 환경에 특화된 대안 구현을 살펴보세요.
    • 배치 크기를 증가시킵니다.
    • batch 내에 서로 다른 adapter가 너무 많도록 하는 것은 피하고, 동질적인 batch를 사용하는 것이 좋습니다. 이를 위해 동일한 adapter를 가진 샘플을 버퍼링하고, 소수의 서로 다른 adapter만 사용하여 추론을 수행하면 됩니다.
    • 다양한 어댑터와 함께 작동하도록 특화된 LoRAX, punica, S-LoRA와 같은 대안 구현을 살펴보세요.

LoRA 어댑터 구성 및 재사용

Arrow

Arrow은 여러 사전 학습된 작업별 LoRA 어댑터를 결합하여 주어진 작업을 해결하도록 설계된 모듈형 라우팅 알고리즘으로, Polytropon과 유사하지만 미세 조정 없이 작동합니다. 모든 어댑터를 단순히 병합하는 대신, Arrow는 그라디언트 없이 토큰 단위로 동작하는 mixture-of-experts(MoE) 라우팅 메커니즘을 도입합니다. 추론 시, 각 LoRA의 SVD 분해에서 상위 우측 특이 벡터를 추출하여 프로토타입을 계산합니다. 이후 각 토큰 표현을 이러한 프로토타입과 코사인 유사도를 통해 비교하여 라우팅 계수를 구합니다. 토큰은 상위 k개의 가장 관련성 높은 LoRA 어댑터에 할당되며, 계수는 softmax를 통해 정규화되고, 그 출력은 선형 결합됩니다. 이를 통해 기존 LoRA 모듈을 새로운 작업에 효과적으로 재사용할 수 있으며, 더 강력한 제로샷 일반화 성능을 달성합니다.

In PEFT, Arrow is enabled through [`ArrowConfig]andcreate_arrow_model. You can also configure parameters such astop_k(the number of LoRA adapters combined per token),router_temperature(the softmax temperature applied to the routing coefficients), andrng_seed` (for reproducibility).

from peft import create_arrow_model, ArrowConfig
from transformers import AutoModelForCausalLM

# Loading the model
base_model = AutoModelForCausalLM.from_pretrained("microsoft/Phi-3-mini-4k-instruct")

# Creating the Arrow config
arrow_config = ArrowConfig(
    top_k=3,
    router_temperature=1.0,
    rng_seed=42,
)

# The LoRA adapters below were trained on a clustered FLAN dataset.
# Task clustering was performed using the Model-Based Clustering (MBC) method,
# as described in the Arrow paper.
# While one could train a separate LoRA for each task and let Arrow route tokens among them,
# training LoRAs on clusters of tasks instead provides an indirect optimization for
# transfer across the multi-task dataset.
task_specific_adapter_paths = [
        f"TahaBa/phi3-mini-clustered-flan/ts_expert_{i}" for i in range(10)
    ]

# Creating the Arrow model
model = create_arrow_model(
        base_model=base_model,
        task_specific_adapter_paths=task_specific_adapter_paths,
        arrow_config=arrow_config,
    )

# Now the forward path could be called on this model, like a normal PeftModel.

또한 create_arrow_model 호출 후 어댑터를 추가하거나 제거할 수 있습니다. 예를 들어, 새로운 어댑터를 파인튜닝하거나 불필요한 어댑터를 삭제하는 경우입니다. 어댑터가 설정된 후에는 추론 시 Arrow를 사용하도록 "arrow_router"를 활성화할 수 있습니다. create_arrow_model 호출 후 새로운 LoRA 어댑터를 추가하여 파인튜닝하려는 경우, create_arrow_model에서 기본적으로 "arrow_router"가 활성화되어 있으므로 새로운 어댑터를 명시적으로 활성 상태로 설정해야 합니다.

from trl import SFTTrainer, SFTConfig

# Adding a new adapter and activating it
model.add_adapter(adapter_name='new_adapter')
model.set_adapter('new_adapter')

# Now the model could be trained along the `new_adapter`.
trainer = SFTTrainer(
        model=model,
        args=SFTConfig(...),
        ...
    )

# Once the training is done, you can activate `arrow_router` and use it in inference
model.set_adapter('arrow_router')    # Model is ready to be used at inference time now

GenKnowSub

GenKnowSub는 라우팅 전에 작업별 LoRA 어댑터를 정제하여 Arrow를 강화합니다. 핵심 아이디어는 부정을 통한 망각(forgetting-via-negation) 원리에 기반하여 LoRA 공간에 인코딩된 일반 지식을 차감하는 것입니다. 이를 통해 작업 어댑터가 더 격리되고 작업 관련 신호에 집중되도록 합니다. 구체적으로, GenKnowSub는 일반(작업 비특정) LoRA 어댑터 집합에서 저차원 “일반” 부분공간을 추정하고, Arrow의 토큰 단위 라우팅 전에 각 작업 어댑터의 LoRA 업데이트에서 이 구성 요소를 제거합니다. 이는 여러 작업 어댑터를 결합할 때 조합성을 향상시키고 간섭을 줄이는 데 일반적으로 도움이 됩니다.

PEFT에서 use_gks=True를 ArrowConfig에 설정하고 create_arrow_model에 general_adapter_paths를 제공하여 GenKnowSub를 활성화합니다:

from peft import create_arrow_model, ArrowConfig
from transformers import AutoModelForCausalLM

# Loading the model
base_model = AutoModelForCausalLM.from_pretrained("microsoft/Phi-3-mini-4k-instruct")

# Creating the Arrow config
arrow_config = ArrowConfig(
    top_k=3,
    router_temperature=1.0,
    use_gks=True,
    rng_seed=42,
)

# Path to task-specific, trained on flan clustered dataset (as we explained before.)
task_specific_adapter_paths = [
        f"TahaBa/phi3-mini-clustered-flan/ts_expert_{i}" for i in range(10)
    ]
# These general adapters are trained on English, German, and French Wikipedia dataset,
# with causal language modelling objective, each pair like: (507 token tsentence, 5 token completion), and the loss computed on the completion
general_adapter_paths = [
        "TahaBa/phi3-mini-general-adapters/cluster0_batch16_prop1.0_langen/checkpoint-17",
        "TahaBa/phi3-mini-general-adapters/cluster0_batch16_prop1.0_langfr/checkpoint-35",
        "TahaBa/phi3-mini-general-adapters/cluster0_batch16_prop1.0_langger/checkpoint-17"
    ]

# Creating the Arrow model
model = create_arrow_model(
        base_model=base_model,
        task_specific_adapter_paths=task_specific_adapter_paths,
        general_adapter_paths=general_adapter_paths,
        arrow_config=arrow_config,
    )

# Now the forward path could be called on this model, like a normal PeftModel.

일반 지식을 인코딩하기 위해 GenKnowSub는 라우팅이 시작되기 전에 제공된 일반 어댑터의 평균을 각 작업별 어댑터에서 한 번 뺍니다. 또한, create_arrow_model을 호출한 후(Arrow 섹션에 설명된 대로) 어댑터를 추가하거나 제거하는 기능도 이 경우 여전히 지원됩니다.

Arrow + GenKnowSub를 사용할 때 알아두어야 할 사항:

  • 모든 LoRA 어댑터(작업별 및 일반)는 동일한 rank와 target_modules를 공유해야 합니다.

  • 이러한 설정에 일관성이 없으면 create_arrow_model에서 오류가 발생합니다.

  • 작업 어댑터마다 다른 스케일링 팩터(lora_alpha)를 사용하는 것이 지원되며, Arrow가 이를 자동으로 처리합니다.

  • 동적 라우팅 동작으로 인해 "arrow_router" 병합은 지원되지 않습니다.

  • create_arrow_model에서 태스크 어댑터는 task_i로, 일반 어댑터는 gks_j로 로드됩니다(여기서 i와 j는 인덱스입니다). 이 함수는 target_modules, rank, 그리고 어댑터가 Linear 레이어인지 Linear4bit 레이어인지에 적용되는지의 일관성을 보장합니다. 그런 다음 "arrow_router" 모듈을 추가하고 활성화합니다. 이 프로세스의 커스터마이징은 create_arrow_model을 오버라이드해야 합니다.

  • 이 구현은 4-bit 양자화(bitsandbytes를 통해)와 호환됩니다:

    from transformers import AutoModelForCausalLM, BitsAndBytesConfig
    import torch
    
    # Quantisation config
    bnb_config = BitsAndBytesConfig(
            load_in_4bit=True,
            bnb_4bit_quant_type="nf4",
            bnb_4bit_compute_dtype=torch.bfloat16,
            bnb_4bit_use_double_quant=False,
        )
    
    # Loading the model
    base_model = AutoModelForCausalLM.from_pretrained(
        "microsoft/Phi-3-mini-4k-instruct",
        dtype=torch.bfloat16,
        device_map="auto",
        quantization_config=bnb_config,
    )
    
    # Now call create_arrow_model() as we explained before.

API

LoraConfig

클래스 peft.LoraConfig

< >

( task_type: Optional[Union[str, TaskType]] = Nonepeft_type: Optional[Union[str, PeftType]] = Noneauto_mapping: Optional[dict] = Nonepeft_version: Optional[str] = Nonebase_model_name_or_path: Optional[str] = Nonerevision: Optional[str] = Noneinference_mode: bool = Falser: int = 8target_modules: Optional[Union[list[str], str]] = Noneexclude_modules: Optional[Union[list[str], str]] = Nonelora_alpha: int = 8lora_dropout: float = 0.0fan_in_fan_out: bool = Falsebias: Literal['none', 'all', 'lora_only'] = 'none'use_rslora: bool = Falsemodules_to_save: Optional[list[str]] = Noneinit_lora_weights: bool | Literal['gaussian', 'eva', 'olora', 'pissa', 'pissa_niter_[number of iters]', 'corda', 'astra', 'loftq', 'orthogonal', 'mica', 'nora'] = Truelayers_to_transform: Optional[Union[list[int], int]] = Nonelayers_pattern: Optional[Union[list[str], str]] = Nonerank_pattern: Optional[dict] = <factory>alpha_pattern: Optional[dict] = <factory>megatron_config: Optional[dict] = Nonemegatron_core: Optional[str] = 'megatron.core'trainable_token_indices: Optional[Union[list[int], dict[str, list[int]]]] = Noneloftq_config: Union[LoftQConfig, dict] = <factory>eva_config: Optional[EvaConfig] = Nonecorda_config: Optional[CordaConfig] = Noneastra_config: Optional[AstraConfig] = Nonelora_ga_config: Optional[LoraGAConfig] = Noneuse_dora: bool = Falsevelora_config: Optional[Union[VeloraConfig, dict]] = Nonealora_invocation_tokens: Optional[list[int]] = Noneuse_qalora: bool = Falseqalora_group_size: int = 16monteclora_config: Optional[MontecloraConfig] = Nonelayer_replication: Optional[list[tuple[int, int]]] = Noneruntime_config: LoraRuntimeConfig = <factory>lora_bias: bool = Falsetarget_parameters: Optional[list[str]] = Noneuse_bdlora: Optional[BdLoraConfig] = Nonearrow_config: Optional[ArrowConfig] = Nonekasa_config: Optional[KasaConfig] = Noneensure_weight_tying: bool = False )

매개변수

  • r (int) — Lora attention 차원(“rank”).
  • target_modules (Optional[Union[List[str], str]]) — 어댑터를 적용할 모듈의 이름입니다. 이 값이 지정되면, 지정된 이름을 가진 모듈만 교체됩니다. 문자열을 전달하면 정규식 매칭이 수행됩니다. 문자열 목록을 전달하면 정확한 매칭이 수행되거나, 모듈 이름이 전달된 문자열 중 하나로 끝나는지 확인됩니다. 이 값이 ‘all-linear’로 지정되면 모든 linear/Conv1D 모듈이 선택됩니다(모델이 PreTrainedModel인 경우 출력 레이어는 제외). 이 값이 지정되지 않으면 모델 아키텍처에 따라 모듈이 선택됩니다. 아키텍처가 알려지지 않은 경우 오류가 발생하며, 이 경우 대상 모듈을 수동으로 지정해야 합니다. 어떤 모듈도 대상으로 삼지 않으려면(target_parameters를 적용하려는 경우) target_modules=[]로 설정하세요.
  • exclude_modules (Optional[Union[List[str], str]]) — 어댑터를 적용하지 않을 모듈의 이름입니다. 문자열을 전달하면 정규식 매칭이 수행됩니다. 문자열 목록을 전달하면 정확한 매칭이 수행되거나, 모듈 이름이 전달된 문자열 중 하나로 끝나는지 확인됩니다.
  • lora_alpha (int) — Lora 스케일링을 위한 alpha 매개변수입니다.
  • lora_dropout (float) — Lora 레이어의 dropout 확률입니다.
  • fan_in_fan_out (bool) — 교체할 레이어가 가중치를 (fan_in, fan_out) 형태로 저장하는 경우 True로 설정합니다. 예를 들어, gpt-2는 가중치를 (fan_in, fan_out) 형태로 저장하는 Conv1D를 사용하므로 이 값을 True로 설정해야 합니다.
  • bias (str) — LoRA의 Bias 유형입니다. ‘none’, ‘all’, ‘lora_only’ 중 하나를 사용할 수 있습니다. ‘all’ 또는 ‘lora_only’인 경우, 해당 bias가 학습 중에 업데이트됩니다. 어댑터를 비활성화하더라도, 어댑팅이 없는 기본 모델과 동일한 출력을 생성하지 않는다는 점에 유의하세요.
  • use_rslora (bool) — True로 설정하면, 더 나은 성능이 입증된 Rank-Stabilized LoRA를 사용하여 어댑터 스케일링 계수를 lora_alpha/math.sqrt(r)로 설정합니다. 그렇지 않으면 원래 기본값인 lora_alpha/r를 사용합니다.
  • modules_to_save (List[str]) — 어댑터 레이어를 제외한 최종 체크포인트에 학습 가능 상태로 설정되고 저장될 모듈 목록.
  • init_lora_weights (bool | Literal["gaussian", "eva", "olora", "pissa", "pissa_niter_[number of iters]", "corda", "astra", "loftq", "orthogonal", "mica", "nora"]) — 어댑터 레이어의 가중치를 초기화하는 방법입니다. True(기본값)를 전달하면 Microsoft의 참조 구현에서 제공하는 기본 초기화가 적용되며, LoRA B 가중치는 0으로 설정됩니다. 이는 추가 학습 없이 LoRA 어댑터가 아무 동작도 하지 않는(no-op) 상태를 의미합니다. 초기화를 False로 설정하면 LoRA A와 B가 무작위로 초기화되어 학습 전에도 LoRA가 no-op이 되지 않으며, 이 설정은 디버깅 목적으로 사용됩니다. 'gaussian'을 전달하면 선형 레이어와 임베딩 레이어에 대해 LoRA 랭크로 스케일링된 가우시안 초기화가 적용됩니다. 'loftq' 을 전달하여 LoftQ 초기화를 사용합니다. 'eva' 을 전달하면 설명된 분산 적응(Explained Variance Adaptation). 에 기반한 데이터 주도 초기화가 적용됩니다. EVA는 레이어 입력 활성화의 SVD를 기반으로 LoRA를 초기화하며, 파인튜닝 데이터에 적응할 수 있는 능력 덕분에 SOTA 성능을 달성합니다. 'olora' 을 전달하여 OLoRA 초기화를 사용합니다. 'pissa' 을 전달하면 https://huggingface.co/papers/2404.02948’의 Principal Singular values and Singular vectors Adaptation(PiSSA)이 초기화되며, 이는 LoRA보다 수렴 속도가 빠르고 궁극적으로 더 우수한 성능을 달성합니다. 또한 PiSSA는 QLoRA에 비해 양자화 오류를 줄여 추가적인 성능 향상을 가져옵니다. 'pissa_niter_[number of iters]'를 전달하면 Fast-SVD 기반 PiSSA 초기화가 시작되며, [number of iters]는 FSVD를 수행할 부분공간 반복 횟수를 나타내며 음이 아닌 정수여야 합니다. [number of iters]가 16으로 설정되면 7B 모델의 초기화를 몇 초 만에 완료할 수 있으며, 학습 효과는 SVD를 사용하는 것과 거의 동일합니다. 'corda'를 전달하면 Context-Oriented Decomposition Adaptation이 초기화되며, 이는 Instruction-Previewed Mode에서 PiSSA보다 수렴 속도가 훨씬 빠르고, Knowledge-Preserved Mode에서 LoRA보다 세계 지식을 더 잘 보존합니다. 'astra'를 전달하면 Activation-Space Tail-Eigenvector Low-Rank Adaptation이 초기화되며, 이는 작은 교정 데이터셋에서 추정된 출력 활성화의 고유공간으로 기본 가중치를 투영합니다. "orthogonal"을 전달하면 LoRA A와 B가 직교하도록 초기화되며, 이는 "olora"와 유사하지만 기본 가중치는 변경되지 않습니다(r이 짝수여야 하며, 현재는 선형 레이어만 지원). "mica"를 전달하면 Minor Component Adaptation(MiCA)이 초기화되며, 이는 가장 작은 특이값에 해당하는 기본 가중치의 r개의 왼쪽 특이벡터로부터 B를 초기화하고 A를 0으로 설정한 후, 학습 중에 B를 동결하고 A만 업데이트합니다. 현재 선형 레이어와 임베딩 레이어를 지원합니다. "nora"를 전달하면 Normalized Low-Rank Adaptation(NoRA)이 초기화되며, 이는 무작위로 초기화된 LoRA A 가중치의 각 열을 단위 L2 노름으로 정규화하고 LoRA B 가중치를 0으로 설정합니다. 정규화는 초기화 시에만 적용되며, 기본 가중치는 변경되지 않고 A와 B 모두 학습 가능합니다. 권장 설정은 use_rslora=False와 함께 lora_alpha를 r로 설정하는 것이며, 이는 1:1 비율과 스케일링 계수 1을 유지합니다. 현재 선형 레이어와 target_parameters로 지정된 nn.Parameters를 지원합니다.

    Principal Singular values and Singular vectors Adaptation (PiSSA), which converges more rapidly than LoRA and ultimately achieves superior performance. Moreover, PiSSA reduces the quantization error compared to QLoRA, leading to further enhancements. Passing 'pissa_niter_[number of iters]' initiates Fast-SVD-based PiSSA initialization, where [number of iters] indicates the number of subspace iterations to perform FSVD, and must be a nonnegative integer. When [number of iters] is set to 16, it can complete the initialization of a 7B model within seconds, and the training effect is approximately equivalent to using SVD. Passing 'corda' results in the initialization of Context-Oriented Decomposition Adaptation, which converges even more rapidly than PiSSA in Instruction-Previewed Mode, and preserves world knowledge better than LoRA in Knowledge-Preserved Mode. Passing 'astra' results in the initialization of Activation-Space Tail-Eigenvector Low-Rank Adaptation, which projects the base weight onto the eigenspace of the output activations estimated from a small calibration dataset. Passing "orthogonal" results in LoRA A and B being initialized orthogonally; in this, it resembles "olora", but the base weights are left untouched (requires r to be even, only supported for linear layers for now). Passing "mica" results in the initialization of Minor Component Adaptation (MiCA), which initializes B from the r left singular vectors of the base weight associated with the smallest singular values, sets A to zero, and freezes B during training; only A is updated. Currently supported for linear and embedding layers. Passing "nora" results in the initialization of Normalized Low-Rank Adaptation (NoRA), which normalizes each column of the randomly initialized LoRA A weight to unit L2 norm and sets the LoRA B weight to zero. Normalization is applied only at initialization; the base weights are unchanged and both A and B remain trainable. The recommended setting is lora_alpha equal to r with use_rslora=False, which keeps a 1:1 ratio and a scaling factor of 1. Currently supported for linear layers and for nn.Parameters targeted with target_parameters.

  • layers_to_transform (Union[List[int], int]) — 변환할 레이어의 인덱스입니다. 정수 목록을 전달하면 이 목록에 지정된 레이어 인덱스에 어댑터를 적용합니다. 단일 정수를 전달하면 해당 인덱스의 레이어에 변환을 적용합니다.
  • layers_pattern (Optional[Union[List[str], str]]) — layers_to_transform가 None과 다르면만 사용되는 레이어 패턴 이름입니다. 이는 모델의 nn.ModuleList를 대상으로 해야 하며, 보통 'layers' 또는 'h'라고 불립니다.
  • rank_pattern (dict) — r로 지정된 기본 rank와 다른 rank를 가지는 레이어 이름 또는 정규식 표현식에서 해당 rank로의 매핑입니다. 예를 들어, {'^model.decoder.layers.0.encoder_attn.k_proj': 16}.
  • alpha_pattern (dict) — lora_alpha로 지정된 기본 alpha와 다른 alpha를 레이어 이름 또는 정규식 표현으로 매핑합니다. 예를 들어, {'^model.decoder.layers.0.encoder_attn.k_proj': 16}.
  • megatron_config (Optional[dict]) — Megatron의 TransformerConfig 인수입니다. LoRA의 병렬 선형 레이어를 생성하는 데 사용됩니다. Megatron의 core_transformer_config_from_args(get_args())와 같은 방식으로 가져올 수 있으며, 이 두 함수는 Megatron에서 제공됩니다. 이 인수는 Megatron의 TransformerConfig를 초기화하는 데 사용됩니다. megatron의 ColumnParallelLinear 및 RowParallelLinear 레이어에 LoRA를 적용하려면 이 매개변수를 지정해야 합니다.
  • megatron_core (Optional[str]) — 사용할 Megatron의 핵심 모듈이며, 기본값은 "megatron.core"입니다.
  • trainable_token_indices (Optional[Union[List[int], dict[str, List[int]]]]) — peft.TrainableTokensModel 메서드를 사용하여 전체 임베딩 행렬을 다시 학습하지 않고 선택적으로 미세 조정할 토큰 인덱스를 지정할 수 있습니다. 토큰 인덱스는 두 가지 방식으로 지정할 수 있습니다. 인덱스 목록을 지정하면 모델의 입력 임베딩 레이어(또는 해당 레이어가 없으면 embed_tokens)를 대상으로 합니다. 또는, 키에 임베딩 모듈 이름, 값에 토큰 인덱스 목록을 가진 사전(딕셔너리)을 지정할 수 있습니다. 예를 들어 {'embed_tokens': [0, 1, ...]}와 같습니다. FSDP로 학습할 경우, 균일하지 않은 requires_grad로 인한 문제를 피하기 위해 use_orig_params=True가 필요합니다.
  • loftq_config (Optional[LoftQConfig]) — LoftQ의 설정입니다. 이 값이 None이 아닌 경우, LoftQ를 사용하여 백본 가중치를 양자화하고 Lora 레이어를 초기화합니다. 또한 init_lora_weights='loftq'를 전달해야 합니다. 이 경우 양자화된 모델을 전달해서는 안 되며, LoftQ가 모델을 직접 양자화하기 때문입니다. LoftQ는 target_parameters를 통해 지정된 레이어를 포함하여 선형 레이어에만 적용됩니다. 임베딩 및 컨볼루션 레이어는 지원되지 않으며, 대신 기본 LoRA 초기화가 사용됩니다. nn.MultiheadAttention 레이어는 지원되지 않으며 오류가 발생합니다.
  • eva_config (Optional[EvaConfig]) — EVA의 설정입니다. 최소한 dataset 인수를 설정해야 합니다(파인튜닝에 사용한 것과 동일한 dataset을 사용).
  • corda_config (Optional[CordaConfig]) — CorDA의 구성입니다. 이 값이 None이 아니면 CorDA를 사용하여 어댑터 레이어를 빌드합니다. 또한 init_lora_weights='corda'를 전달해야 합니다.
  • astra_config (Optional[AstraConfig]) — Astra의 구성입니다. 이 값이 None이 아니면 Astra를 사용하여 어댑터 레이어를 빌드합니다. 이 경우 init_lora_weights='astra'도 전달하고 get_peft_model 호출 전에 preprocess_astra를 호출해야 합니다.
  • lora_ga_config (Optional[LoraGAConfig]) — LoRA-GA의 구성입니다. 이 값이 전달되면 LoRA-GA를 사용하여 어댑터 레이어를 초기화합니다. 이 경우 init_lora_weights='lora_ga'도 설정해야 합니다.
  • use_dora (bool) — ‘Weight-Decomposed Low-Rank Adaptation’(DoRA)를 활성화합니다. 이 기법은 가중치 업데이트를 크기(magnitude)와 방향(direction) 두 부분으로 분해합니다. 방향은 일반적인 LoRA가 처리하고, 크기는 별도의 학습 가능한 파라미터가 처리합니다. 이는 특히 낮은 랭크에서 LoRA의 성능을 향상시킬 수 있습니다. 현재 DoRA는 linear 및 Conv2D 레이어만 지원합니다. DoRA는 순수 LoRA보다 더 큰 오버헤드를 발생시키므로 추론 시 가중치를 병합하는 것이 권장됩니다. 자세한 내용은 https://huggingface.co/papers/2402.09353을 참조하세요.
  • velora_config (Optional[VeloraConfig]) — VeloraConfig를 제공하여 VeLoRA를 활성화합니다. VeLoRA는 LoRA A 투영에 대해 전체 입력 활성화 대신 압축된 활성화를 저장하는 사용자 지정 역전파를 적용합니다.
  • kasa_config (Optional[KasaConfig]) — KasaConfig를 제공하여 KaSA(Knowledge-aware Singular-value Adaptation)를 활성화합니다. KaSA는 1회 SVD를 통해 동결된 기본 가중치의 가장 작은 r개의 특이 성분들을 잘라내고, LoRA A와 B 인자 사이에 학습 가능한 특이값 대각선을 삽입합니다. 현재 선형 레이어만 지원됩니다.
  • alora_invocation_tokens (List[int]) — None이 아닌 경우, ‘Activated LoRA’ (aLoRA)를 활성화합니다. 이때 alora_invocation_tokens는 어댑터의 토큰화된 호출 문자열을 나타내며(모든 모델 입력 문자열에 반드시 포함되어야 함), 이 기법은 alora_invocation_tokens 중 및 이후의 토큰에 대해서만 선택적으로 어댑터 가중치를 활성화합니다. CausalLM에서 사용할 경우, 호출 이전의 KV 캐시는 베이스 모델(및 동일한 방식으로 동작하는 다른 aLoRA 어댑터)의 KV 캐시와 상호 교환 가능합니다. 그 결과, 베이스 모델 추론과 어댑터 추론을 전환하는 추론 파이프라인(예: 에이전틱 파이프라인, 예시는 논문을 참조)에서 prefill 연산을 저장함으로써 (LoRA 대비) 상당한 절감 효과가 발생합니다. 공유 컨텍스트의 길이에 따라 vLLM에서 어댑터 추론 속도가 10배 이상 향상될 수 있습니다. 가중치가 선택적으로 적용되므로 병합은 불가능합니다.
  • use_qalora (bool) — 현재 GPTQ에서만 구현되어 있습니다. Quantization-Aware Low-Rank Adaptation (QALoRA)를 활성화합니다. 이 기법은 양자화 인식 학습을 LoRA와 결합하여 양자화 모델의 성능을 향상시킵니다. 이를 통해 LoRA의 성능, 특히 낮은 랭크에서의 성능을 개선할 수 있습니다. 현재 QALoRA는 선형 레이어만 지원합니다.
  • qalora_group_size (int) — QALoRA 풀링의 그룹 크기 파라미터로, 차원 축소 계수를 제어합니다. 입력 차원이 이 크기의 그룹으로 풀링되어 계산 비용이 줄어듭니다. 값이 높을수록 압축률이 높아지지만 모델 품질이 저하될 수 있습니다. 이 파라미터는 하나의 풀링된 특징을 생성하기 위해 몇 개의 원래 특징을 평균화하는지를 결정합니다. use_qalora=True일 때만 사용됩니다.
  • monteclora_config (Optional[MontecloraConfig]) — Monteclora(Monte Carlo Low-Rank Adaptation)의 설정입니다. 전달되면, LoRA 어댑터 위에 변분 Monte Carlo 샘플링을 추가하기 위해 Monteclora가 사용됩니다. 개별 하이퍼파라미터에 대한 자세한 내용은 MontecloraConfig를 참조하세요.
  • layer_replication (List[Tuple[int, int]]) — 지정된 범위에 따라 원래 모델의 레이어를 쌓아 새로운 레이어 스택을 구성합니다. 이를 통해 기본 모델 가중치를 중복하지 않고 모델을 확장(또는 축소)할 수 있습니다. 새로 생성된 모든 레이어에는 각각 별도의 LoRA 어댑터가 연결됩니다.
  • runtime_config (LoraRuntimeConfig) — 저장되거나 복원되지 않는 런타임 구성입니다.
  • lora_bias (bool) — 기본값은 False입니다. LoRA B 매개변수에 대해 bias 항을 활성화할지 여부를 지정합니다. 일반적으로 비활성화해야 합니다. 이 옵션의 주요 사용 사례는 LoRA 가중치가 완전히 fine-tuning된 매개변수에서 추출된 경우로, 해당 매개변수의 bias를 고려할 수 있습니다.
  • target_parameters (List[str], 선택 사항) — LoRA로 대체할 매개변수 이름의 목록입니다. 이 인수는 target_modules과 유사하게 작동하지만, 모듈 이름 대신 매개변수 이름을 전달해야 합니다. 일반적으로 모듈(예: nn.Linear)을 대상으로 하려면 target_modules를 사용해야 합니다. 그러나 일부 상황에서는 이것이 불가능합니다. 예를 들어, HF Transformers의 많은 mixture of expert (MoE) 레이어에서는 nn.Linear 대신 nn.Parameter를 사용합니다. PEFT는 LoRA를 위해 forward 메서드를 일반적으로 오버라이드하지만, nn.Parameter에는 해당 메서드가 없습니다. 따라서 해당 매개변수에 LoRA를 적용하려면 target_parameters로 대상 지정을 해야 합니다. 예를 들어, Llama4의 경우 다음과 같이 전달할 수 있습니다: target_parameters=['feed_forward.experts.gate_up_proj', 'feed_forward.experts.down_proj]. 정규식 매칭을 위한 문자열 전달은 아직 구현되지 않았습니다. 모델이 컴파일되어 있고 target_parameters를 사용하는 경우, 재컴파일 및/또는 그래프 브레이크가 발생할 수 있습니다. 두 인수를 동시에 사용하는 것은 권장하지 않습니다.
  • use_bdlora (Optional[BdLoraConfig]) — BdLoraConfig를 제공하여 BD-LoRA(Block-Diagonal LoRA)를 활성화합니다. 이 기법은 LoRA-A 또는 LoRA-B 인자에 블록 대각 행렬을 사용하여 분산 환경에서 통신 오버헤드를 제거함으로써 더 빠른 멀티-LoRA 서빙을 가능하게 합니다.
  • arrow_config (Optional[ArrowConfig]) — 모델에 arrow 라우팅을 적용하는 데 필요한 설정입니다.
  • ensure_weight_tying (bool, 선택 사항) — peft 초기화 후 가중치를 묶을지 여부입니다. 이를 통해 묶인 레이어에 추가된 어댑터도 함께 묶이도록 보장합니다. 이는 modules_to_save와 target_modules를 통해 전달된 레이어에만 적용됩니다.

이것은 LoraModel의 구성을 저장하기 위한 구성 클래스입니다.

to_dict

< >

( )

어댑터 모델의 구성을 사전으로 반환합니다. 런타임 구성을 제거합니다.

LoraModel

클래스 peft.LoraModel

< >

( modelpeft_config: Union[PeftConfig, dict[str, PeftConfig]]adapter_name: strlow_cpu_mem_usage: bool = Falsestate_dict: Optional[dict[str, torch.Tensor]] = None ) → torch.nn.Module

매개변수

  • model (torch.nn.Module) — 적응할 모델.
  • config (LoraConfig) — Lora 모델의 구성입니다.
  • adapter_name (str) — 어댑터의 이름이며, 기본값은 "default"입니다.
  • low_cpu_mem_usage (bool, optional, 기본값 False) — 메타 디바이스에 빈 어댑터 가중치를 생성합니다. 로딩 프로세스를 가속하는 데 유용합니다.

반환값

torch.nn.Module

Lora 모델.

pretrained transformers model에서 Low Rank Adapter (LoRA) 모델을 생성합니다.

해당 방법은 https://huggingface.co/papers/2106.09685에서 자세히 설명되어 있습니다.

예:

>>> from transformers import AutoModelForSeq2SeqLM
>>> from peft import LoraConfig, get_peft_model

>>> config = LoraConfig(
...     task_type="SEQ_2_SEQ_LM",
...     r=8,
...     lora_alpha=32,
...     target_modules=["q", "v"],
...     lora_dropout=0.01,
... )

>>> model = AutoModelForSeq2SeqLM.from_pretrained("t5-base")
>>> lora_model = get_peft_model(model, config)
>>> import torch
>>> import transformers
>>> from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training

>>> rank = ...
>>> target_modules = ["q_proj", "k_proj", "v_proj", "out_proj", "fc_in", "fc_out", "wte"]
>>> config = LoraConfig(
...     r=4, lora_alpha=16, target_modules=target_modules, lora_dropout=0.1, bias="none", task_type="CAUSAL_LM"
... )
>>> quantization_config = transformers.BitsAndBytesConfig(load_in_8bit=True)

>>> tokenizer = transformers.AutoTokenizer.from_pretrained(
...     "kakaobrain/kogpt",
...     revision="KoGPT6B-ryan1.5b-float16",  # or float32 version: revision=KoGPT6B-ryan1.5b
...     bos_token="[BOS]",
...     eos_token="[EOS]",
...     unk_token="[UNK]",
...     pad_token="[PAD]",
...     mask_token="[MASK]",
... )
>>> model = transformers.GPTJForCausalLM.from_pretrained(
...     "kakaobrain/kogpt",
...     revision="KoGPT6B-ryan1.5b-float16",  # or float32 version: revision=KoGPT6B-ryan1.5b
...     pad_token_id=tokenizer.eos_token_id,
...     use_cache=False,
...     device_map={"": rank},
...     torch_dtype=torch.float16,
...     quantization_config=quantization_config,
... )
>>> model = prepare_model_for_kbit_training(model)
>>> lora_model = get_peft_model(model, config)

속성:

add_weighted_adapter

< >

( adapters: list[str]weights: list[float]adapter_name: strcombination_type: Literal['svd', 'linear', 'cat', 'ties', 'ties_svd', 'dare_ties', 'dare_linear', 'dare_ties_svd', 'dare_linear_svd', 'magnitude_prune', 'magnitude_prune_svd'] = 'svd'svd_rank: int | None = Nonesvd_clamp: int | None = Nonesvd_full_matrices: bool = Truesvd_driver: str | None = Nonedensity: float | None = Nonemajority_sign_method: Literal['total', 'frequency'] = 'total' )

매개변수

  • adapters (list) — 병합할 어댑터 이름 목록.
  • weights (list) — 각 어댑터에 대한 가중치 목록입니다. 가중치는 양수 또는 음수일 수 있으며, 이를 통해 어댑터 효과를 더하거나 뺄 수 있습니다.
  • adapter_name (str) — 새 어댑터의 이름.
  • combination_type (str) — 병합 유형은 [svd, linear, cat, ties, ties_svd, dare_ties, dare_linear, dare_ties_svd, dare_linear_svd, magnitude_prune, magnitude_prune_svd] 중 하나일 수 있습니다. cat combination_type를 사용하는 경우, 결과 어댑터의 랭크는 모든 어댑터 랭크의 합과 같습니다(혼합 어댑터가 너무 커져 OOM 오류가 발생할 수 있음). cat과 svd는 정밀한 방법이며 좋은 정확도를 제공하지만, linear는 효율적이지만 매우 거친 근사치이므로 가능하면 피해야 합니다.
  • svd_rank (int, 선택 사항) — svd의 출력 어댑터 랭크입니다. None이 제공되면 병합된 어댑터의 최대 랭크를 사용합니다.
  • svd_clamp (float, 선택 사항) — SVD 분해 출력을 클램프할 분위수 임계값입니다. None이 제공되면 클램핑을 수행하지 않습니다. 기본값은 None입니다.
  • svd_full_matrices (bool, 선택 사항) — 완전 SVD 또는 축소 SVD를 계산할지 여부를 제어하며, 이에 따라 반환되는 텐서 U와 Vh의 형태가 결정됩니다. 기본값은 True입니다.
  • svd_driver (str, 선택 사항) — 사용할 cuSOLVER 메서드의 이름입니다. 이 키워드 인수는 CUDA에서 병합할 때만 작동합니다. [None, gesvd, gesvdj, gesvda] 중 하나여야 합니다. 자세한 내용은 torch.linalg.svd 문서를 참조하세요. 기본값은 None입니다.
  • density (float, 선택 사항) — 0과 1 사이의 값입니다. 0은 모든 값이 제거됨을, 1은 값이 제거되지 않음을 의미합니다. [ties, ties_svd, dare_ties, dare_linear, dare_ties_svd, dare_linear_svd, magnintude_prune, magnitude_prune_svd]와 함께 사용해야 합니다.
  • majority_sign_method (str) — 부호 값의 크기를 구하는 데 사용할 방법입니다. [“total”, “frequency”] 중 하나여야 합니다. [ties, ties_svd, dare_ties, dare_ties_svd]와 함께 사용해야 합니다.

이 메서드는 주어진 어댑터를 주어진 가중치로 병합하여 새로운 어댑터를 추가합니다.

cat combination_type를 사용할 때, 생성되는 어댑터의 랭크는 모든 어댑터의 랭크 합과 같다는 점을 유의해야 합니다. 따라서 혼합된 어댑터가 너무 커져 OOM 오류가 발생할 수 있습니다.

subtract_mutated_init

< >

( output_state_dict: dict[str, torch.Tensor]adapter_name: strkwargs = None )

이 함수는 output_state_dict의 PiSSA/CorDA/OLoRA 어댑터 매개변수와 adapter_name의 PiSSA/CorDA/OLoRA 초기 값을 비교하여 PiSSA/CorDA/OLoRA 업데이트를 계산하고, 이를 통해 PiSSA/CorDA/OLoRA를 LoRA로 변환합니다.

PiSSA와 같은 방법은 학습을 위해 모델의 기본 가중치 중 일부를 제거하기 때문에 쉽게 교체할 수 없습니다. 하지만 초기 사전 학습 가중치와 학습 후 가중치를 모두 알고 있다면 \Delta W를 계산하여 이를 LoRA 어댑터로 사용할 수 있습니다.

계산 단계:

  • W=Wres+A0B0W = W_{res} + A_0 B_0 (PiSSA 초기화
  • W+ΔW=Wres+ABW + \Delta W = W_{res} + A B (학습 후 PiSSA)
  • ΔW=Wres+AB−W=Wres+AB−Wres−A0B0\Delta W = W_{res} + AB - W = W_{res} + AB - W_{res} - A_0 B_0 (LoRA의 dW 유도
  • ΔW=AB−A0B0\Delta W = AB - A_0 B_0

유틸리티

ArrowConfig

class peft.ArrowConfig

< >

( top_k: int = 3router_temperature: float = 1.0use_gks: bool = Falserng_seed: Optional[int] = None )

이것은 Arrow 및 GenKnowSub 알고리즘의 설정을 저장하기 위한 하위 설정 클래스입니다. Arrow는 https://huggingface.co/papers/2405.11157에서 제안된, 학습된 LoRA 모듈을 결합하여 새로운 작업을 해결하기 위한 라우팅 알고리즘입니다. GenKnowSub는 https://aclanthology.org/2025.acl-short.54/에서 소개된, Arrow를 통해 결합되기 전에 학습된 모듈을 정제하는 방법입니다.

LoftQ

replace_lora_weights_loftq

peft.replace_lora_weights_loftq

< >

( peft_modelmodel_path: Optional[str] = Noneadapter_name: str = 'default'callback: Optional[Callable[[torch.nn.Module, str], bool]] = None )

매개변수

  • peft_model (PeftModel) — 가중치를 교체할 대상 모델입니다. LoRA 레이어가 포함된 양자화 PEFT 모델이어야 합니다.
  • model_path (Optional[str]) — 모델 safetensors 파일의 경로입니다. 모델이 Hugging Face 모델인 경우 모델의 config에서 추론됩니다. 그렇지 않은 경우 직접 제공해야 합니다.
  • adapter_name (str) — 교체할 가중치를 가진 어댑터의 이름입니다. 기본 어댑터 이름은 “default”입니다.
  • callback (Optional[Callable[[PeftModel, str], bool]]) — 각 모듈이 교체된 후 호출되는 콜백 함수입니다. 콜백 함수는 모델과 현재 모듈의 이름을 입력으로 받아, 교체를 유지할지 여부를 나타내는 불리언 값을 반환해야 합니다. 콜백이 False를 반환하면 교체가 롤백됩니다. 이는 LoftQ 초기화가 실제로 모델의 양자화 오류를 줄이는지 확인하는 데 매우 유용할 수 있습니다. 예를 들어, 이 콜백은 주어진 입력에 대한 logits를 생성하고 동일한 입력을 가진 원래의 비양자화 모델의 logits와 비교한 후, 개선이 있을 경우에만 True를 반환할 수 있습니다. 이는 탐욕스러운 최적화이므로, 이 함수를 여러 번 호출하면 점진적인 개선이 가능할 수 있습니다.

LoftQ 기법을 사용하여 bitsandbytes로 양자화된 모델의 LoRA 가중치를 교체합니다.

대체는 로컬에 저장된 safetensors 모델 파일에서 비양자화 가중치를 로드하고, 원래 가중치와 양자화 가중치 사이의 양자화 오류를 최소화하도록 LoRA 가중치를 초기화함으로써 실시간으로 수행됩니다.

lazy loading은 pickle로 불가능하므로, 일반적인 PyTorch 체크포인트 파일은 지원할 수 없습니다.

모델 크기에 따라 이 함수를 호출하는 데 시간이 걸릴 수 있습니다.

LoftQConfig

class peft.LoftQConfig

< >

( loftq_bits: int = 4loftq_iter: int = 1 )

매개변수

  • loftq_bits (int) — LoftQ의 양자화 비트 수.
  • loftq_iter (int) — LoftQ의 교대 반복 횟수.

이것은 LoraModel의 구성을 저장하기 위한 하위 구성 클래스입니다.

Eva

EvaConfig

클래스 peft.EvaConfig

< >

( rho: float = 2.0tau: float = 0.99use_label_mask: bool = Truelabel_mask_value: int = -100whiten: bool = Falseadjust_scaling_factors: bool = True )

매개변수

  • rho (float) — EVA 재분배를 위한 Rho 값 (>= 1.0). 레이어의 최대 랭크는 lora_r * rho입니다. 기본값은 2.0이며, 이는 레이어에 허용되는 최대 랭크가 2r임을 의미합니다. rho를 증가시키면 레이어 간 랭크 재분배의 정도가 높아집니다. 일부 사전 학습된 모델은 랭크 재분배에 더 민감할 수 있습니다. 따라서 성능이 기대보다 낮을 경우 rho=1.0(재분배 없음)을 시도하는 것이 유리할 수 있습니다.
  • tau (float) — 조기 정지를 위한 코사인 유사도 임계값입니다. 두 연속된 SVD 단계 사이의 우측 특이 벡터의 코사인 유사도를 비교합니다. 코사인 유사도가 이 임계값보다 높으면 SVD 반복이 중단됩니다. 기본값은 0.99입니다.
  • use_label_mask (bool) — EVA 초기화에 label mask를 사용합니다. 이는 label이 label_mask_value인 위치가 SVD 계산에서 무시됨을 의미합니다. 대부분의 경우 use_label_mask=True를 설정하는 것이 권장되며, 특히 다중 턴 대화에서 특히 유용할 수 있습니다. 기본값은 True입니다. label mask를 기반으로 항목을 필터링하면 작은 배치 크기를 유발하여 SVD 계산의 불안정성을 초래할 수 있습니다. 배치 항목의 상당 부분이 필터링되는 경우, use_label_mask=False를 설정하십시오.
  • label_mask_value (int) — use_label_mask=True인 경우 무시할 토큰을 마스킹하기 위해 찾을 값입니다. 기본값은 -100입니다.
  • whiten (bool) — 고유 벡터에 화이트닝을 적용합니다. 기본값은 False입니다. 화이트닝은 비전 도메인에서 EVA에 이롭다는 것이 입증되었습니다.
  • adjust_scaling_factors (bool) — 랭크 재분배 후 LoRA 스케일링 계수를 조정합니다. True로 설정하면 모든 LoRA 그래디언트가 랭크와 관계없이 동일한 스케일을 가지도록 스케일링 계수가 조정됩니다. 기본값은 True입니다.

이것은 EVA를 통한 데이터 기반 초기화 설정을 저장하기 위한 하위 구성 클래스입니다. EVA는 Explained Variance Adaptation에서 소개되었습니다.

initialize_lora_eva_weights

peft.initialize_lora_eva_weights

< >

( model: Moduledataloader: collections.abc.Iterable | None = Noneeva_state_dict: dict | None = Noneforward_fn: collections.abc.Callable | None = <function forward_fn_dict at 0x7fc5dc338880>prepare_model_inputs_fn: collections.abc.Callable | None = <function prepare_model_inputs_fn_language_modeling at 0x7fc5dc338670>prepare_layer_inputs_fn: collections.abc.Callable | dict[str, collections.abc.Callable] | None = <function prepare_layer_inputs_fn_language_modeling at 0x7fc5dc3387d0>adapter_name: str = 'default'gather_distributed_inputs: bool = Trueshow_progress_bar: bool = True ) → model (torch.nn.Module)

매개변수

  • model (PeftModel) — SVD를 계산할 peft 모델입니다.
  • dataloader (Optional[Iterable]) — forward pass에 사용할 dataloader입니다. None인 경우 eva_state_dict를 제공해야 합니다.
  • eva_state_dict (Optional[dict]) — 모델에 로드할 state_dict입니다. None인 경우 dataloader를 제공해야 하며, get_eva_state_dict을 사용하여 state_dict가 계산됩니다.
  • forward_fn (Callable) — forward pass에 사용할 forward 함수입니다. 두 개의 인수를 받습니다: model과 inputs. 기본 동작은 return model(**inputs)입니다.
  • prepare_model_inputs_fn (Optional[Callable]) — 이 함수는 모델 입력과 peft_config를 받아 출력 결과를 prepare_layer_inputs_fn으로 전달합니다. 원래 모델 입력을 기반으로 SVD 계산에 사용되는 입력을 수정하는 데 사용할 수 있습니다. 예를 들어 언어 모델링의 경우 어텐션 마스크를 사용하여 패딩 토큰인 인덱스를 결정하고, 이러한 인덱스는 SVD에 사용되지 않도록 합니다. 여기에 정의된 모든 함수는 model_input과 peft_config 두 개의 인수를 기대합니다. 기본적으로 peft.tuners.lora.eva.prepare_model_inputs_fn_language_modeling가 사용됩니다.
  • prepare_layer_inputs_fn (Union[Callable, Dict[str, Callable], None]) — 이 함수는 레이어 입력, 모델 입력(prepare_model_inputs_fn에 의해 수정되었을 수 있음) 및 레이어 이름을 인수로 받아 해당 레이어의 SVD에 사용해야 할 입력을 반환합니다. 여기에 정의된 사용자 정의 함수는 layer_input, model_input, layer_name이라는 세 개의 인수를 받아 2차원 텐서를 반환해야 합니다. 기본 로직은 peft.tuners.lora.eva.prepare_layer_inputs_fn_language_modeling에서 찾을 수 있으며 언어 모델링에 적용됩니다. 이 경우 model_inputs는 SVD에 사용해야 할 인덱스를 결정하는 데 사용되는 마스크(prepare_model_inputs_fn_language_modeling에 의해 생성됨)입니다.
  • adapter_name (str) — 가중치를 초기화할 어댑터의 이름입니다.
  • gather_distributed_inputs (bool) — 모든 rank에서 레이어 입력을 수집할지 여부입니다. 기본값은 True이며, 분산 환경에서는 SVD 계산을 위해 모든 rank에서 레이어 입력을 수집합니다. 비분산 환경에서는 이 인수가 무시됩니다. 분산 환경에서 비분산 dataloader를 사용하는 경우 False로 설정하세요.
  • show_progress_bar (bool) — 진행 표시줄을 표시할지 여부. 기본값은 True입니다.

반환값

model (torch.nn.Module)

초기화된 LoRA 가중치를 가진 모델.

EVA 방법을 사용하여 LoRA 레이어의 가중치를 초기화합니다.

이 함수는 EVA 방법을 사용하여 LoRA 레이어의 가중치를 초기화합니다. 각 어댑터 레이어에 대해 SVD를 계산하고 그에 따라 가중치를 업데이트합니다.

get_eva_state_dict

peft.get_eva_state_dict

< >

( model: Moduledataloader: Iterablepeft_config: peft.tuners.lora.config.LoraConfig | None = Noneforward_fn: collections.abc.Callable | None = <function forward_fn_dict at 0x7fc5dc338880>prepare_model_inputs_fn: collections.abc.Callable | None = <function prepare_model_inputs_fn_language_modeling at 0x7fc5dc338670>prepare_layer_inputs_fn: collections.abc.Callable | dict[str, collections.abc.Callable] | None = <function prepare_layer_inputs_fn_language_modeling at 0x7fc5dc3387d0>adapter_name: str = 'default'gather_distributed_inputs: bool = Trueshow_progress_bar: bool = True ) → eva_state_dict (dict)

매개변수

  • model (torch.nn.Module) — SVD를 계산할 모델입니다. PeftModel일 필요는 없습니다.
  • dataloader (Iterable) — forward pass에 사용할 dataloader.
  • peft_config (Optional[LoraConfig]) — LoRA 레이어에 대한 설정입니다. model이 PeftModel이 아닌 경우에만 필요합니다.
  • forward_fn (Callable) — forward pass에 사용할 forward 함수입니다. 두 개의 인수를 받습니다: model과 inputs. 기본 동작은 return model(**inputs)입니다.
  • prepare_model_inputs_fn (Optional[Callable]) — 이 함수는 모델 입력과 peft_config를 받아 출력 결과를 prepare_layer_inputs_fn으로 전달합니다. 원래 모델 입력을 기반으로 SVD 계산에 사용되는 입력을 수정하는 데 사용할 수 있습니다. 예를 들어 언어 모델링의 경우 어텐션 마스크를 사용하여 패딩 토큰인 인덱스를 결정하고, 이러한 인덱스는 SVD에 사용되지 않도록 합니다. 여기에 정의된 모든 함수는 model_input과 peft_config 두 개의 인수를 기대합니다. 기본적으로 peft.tuners.lora.eva.prepare_model_inputs_fn_language_modeling가 사용됩니다.
  • prepare_layer_inputs_fn (Union[Callable, Dict[str, Callable], None]) — 이 함수는 레이어 입력, 모델 입력(prepare_model_inputs_fn에 의해 수정되었을 수 있음) 및 레이어 이름을 인수로 받아 해당 레이어의 SVD에 사용해야 할 입력을 반환합니다. 여기에 정의된 사용자 정의 함수는 layer_input, model_input, layer_name이라는 세 개의 인수를 받아 2차원 텐서를 반환해야 합니다. 기본 로직은 peft.tuners.lora.eva.prepare_layer_inputs_fn_language_modeling에서 찾을 수 있으며 언어 모델링에 적용됩니다. 이 경우 model_inputs는 SVD에 사용해야 할 인덱스를 결정하는 데 사용되는 마스크(prepare_model_inputs_fn_language_modeling에 의해 생성됨)입니다.
  • adapter_name (str) — SVD를 계산할 어댑터의 이름입니다.
  • gather_distributed_inputs (bool) — 모든 rank에서 레이어 입력을 수집할지 여부입니다. 기본값은 True이며, 분산 환경에서는 SVD 계산을 위해 모든 rank에서 레이어 입력을 수집합니다. 비분산 환경에서는 이 인수가 무시됩니다. 분산 환경에서 비분산 dataloader를 사용하는 경우 False로 설정하세요.
  • show_progress_bar (bool) — 진행 표시줄을 표시할지 여부. 기본값은 True입니다.

반환값

eva_state_dict (dict)

각 레이어의 SVD 구성 요소를 포함하는 상태 사전(state dictionary)입니다.

모델의 각 레이어에 대해 SVD를 계산합니다.

이 함수는 모델의 각 레이어에 대해 특이값 분해(SVD)를 계산합니다. 증분 PCA 방법을 사용하여 SVD 구성 요소를 계산합니다. 이 함수는 코사인 유사도를 사용하여 계산된 구성 요소의 수렴 여부도 확인합니다. 각 레이어의 랭크 분포는 설명 분산 비율에 따라 결정됩니다.

LoraGAConfig

클래스 peft.LoraGAConfig

< >

( direction: Literal['ArBr', 'A2rBr', 'ArB2r', 'random'] = 'ArB2r'scale: Literal['stable', 'weight_svd', 'gd_scale', 'unit'] = 'stable'stable_gamma: int = 16 )

매개변수

  • direction (Literal["ArBr", "A2rBr", "ArB2r", "random"]) — lora_A와 lora_B 행렬에 gradient SVD 구성 요소를 분배하는 전략입니다.
    • “ArBr”: 교대 인덱스(A는 홀수, B는 짝수)
    • “A2rBr”: A는 인덱스 [r:2r]를, B는 인덱스 [:r]를 가져옵니다
    • “ArB2r”: A는 인덱스 [:r]를, B는 인덱스 [r:2r]를 가져옵니다 (권장)
    • “random”: 인덱스의 무작위 선택 기본값: “ArB2r”
  • scale (Literal["stable", "weight_svd", "gd_scale", "unit"]) — 어댑터 초기화를 위한 스케일링 전략.
    • “stable”: gamma 매개변수를 사용한 안정적 스케일링
    • “weight_svd”: 가중치 행렬의 특이값에 기반한 스케일
    • “gd_scale”: 경사 하강법 기반 스케일링
    • “unit”: 추가 스케일링 없음 기본값: “stable”
  • stable_gamma (int) — 안정적 스케일링 방법의 Gamma 매개변수. 기본값: 16

LoRA-GA 초기화를 위한 설정을 저장하는 하위 구성 클래스입니다.

LoRA-GA(Gradient Approxation을 사용한 Low-Rank Adaptation)는 초기화 단계에서 기울기 정보를 활용하여 전체 fine-tuning 기울기의 방향과 초기 어댑터 가중치를 정렬함으로써 더 빠른 수렴(2~4배 속도 향상)을 달성합니다.

참고: https://arxiv.org/abs/2407.05000

유틸리티

peft.tuners.lora.loraga.estimate_gradients

< >

( model: Modulelora_config: LoraConfigtrain_step: Callable )

LoRA-GA 초기화를 위한 기울기를 추정합니다.

이 함수는 대상 모듈의 가중치에 대해서만 gradient 계산을 활성화하고 train_step 콜백을 실행합니다. 이는 전역적으로 그래디언트를 활성화하는 것보다 메모리 효율이 더 높습니다.

peft.preprocess_loraga

< >

( model: Modulelora_config: LoraConfigtrain_step: Callablecache_file: str | None = None )

매개변수

  • model (nn.Module) — 전처리할 모델.
  • lora_config (LoraConfig) — 모델의 Lora 구성입니다. lora_config.lora_ga_config가 설정되어 있어야 합니다.
  • train_step (Callable[[], None]) — 그라디언트 추정을 실행하기 위한 콜백입니다. 일반적으로 이 콜백에서 모델의 포워드 패스와 백워드 패스를 실행해야 합니다. 이 콜백 내의 모든 호출에 걸쳐 그라디언트가 누적됩니다.
  • cache_file (Optional[str]) — 그라디언트를 저장/로드하기 위한 캐시 파일의 선택적 경로입니다. 경로가 지정되어 해당 파일이 존재하면 그라디언트를 캐시에서 로드합니다. 그렇지 않으면 그라디언트를 추정하여 해당 경로에 저장합니다.

…

각 선형 레이어에 대해, 제공된 train_step 콜백을 실행하여 그라디언트를 추정합니다. 이 그라디언트는 모듈에 연결된 후 초기화 과정에서 사용됩니다.

완료되면 각 대상 모듈에 다음 필드가 설정됩니다: _peft_loraga_grad (torch.Tensor): 가중치 행렬에 대한 누적 기울기.

침입자 차원 축소

peft.tuners.lora.intruders.reduce_intruder_dimension

< >

( peft_modelold_adapter_name = 'default'new_adapter_name = 'intruder_reduced'top_k = 10threshold_epsilon = 0.5mitigation_lambda = 0.75logging_sink = <built-in function print> )

매개변수

  • peft_model — old_adapter_name에 제공된 이름의 LoRA 어댑터가 로드된 PEFT 모델입니다. 현재 혼합 모델은 지원되지 않습니다.
  • top_k (기본값 — 10) 침입자 탐지를 위해 고려할 상위 k개의 차원을 지정합니다. 값이 클수록 침입자 탐지 분석에 고려되는 차원의 수가 많아지며(그에 따라 오탐의 가능성도 높아질 수 있습니다). 이 매개변수는 베이스 가중치와 어댑터 가중치 사이의 코사인 유사도를 차원의 영향도(특이값 분해로 결정됨) 기준으로 대략 정렬하여 적용하므로, top-k가 10이면 가장 ‘중요한’ 10개의 차원을 살펴봅니다.
  • threshold_epsilon (기본값 — 0.5) 기본 가중치와 어댑터 가중치 사이의 코사인 유사도를 침입자로 간주할 임계값입니다. 논문에서 침입자 차원은 사전 학습된 고유 벡터와 거의 0에 가까운 절대 코사인 유사도를 보인다고 합니다. 이 값이 낮을수록 잠재적 침입자 차원으로 식별되는 경우가 적습니다. 값이 높을수록 잠재적 오탐(false-positive)이 침입자로 간주되는 경우가 많아집니다.
  • mitigation_lambda (기본값 — 0.75) 어댑터의 델타 가중치에서 차감되는 침입자 차원의 상대적 비율입니다. 값이 높을수록 침입자 차원 중 더 많은 부분이 차감되지만, 더 많은 정보가 손실됩니다. 트레이드오프 분석에 대해서는 해당 논문의 Figure 8을 참조하세요.
  • logging_sink (기본값 — print) 완화 과정에 대한 정보를 출력하는 함수입니다. 출력을 원하지 않는 경우 None으로 설정하세요.

https://huggingface.co/papers/2410.21228 (“LoRA vs Full Fine-tuning: An Illusion of Equivalence”)에 기반한 침입 차원 완화

이 메서드는 이미 학습된 저순위(low-rank) 어댑터를 후처리하여 이전 지식을 복원(즉, 망각을 완화)할 수 있습니다. 이는 작업 정확도를 희생하는 대가를 치릅니다. migration_lambda 값을 조정하여 이 두 요인 사이의 트레이드오프를 조절할 수 있습니다.

완화 작업이 완료되면 new_adapter_name에 설정된 이름으로 새 어댑터가 생성되며, 이 어댑터가 현재 활성 어댑터로 설정됩니다. 따라서 완화된 모델에 대한 추론은 수정된 어댑터를 사용합니다. 원래 어댑터로 되돌리려면 peft_model.set_adapter(<old_adapter_name>)을 사용할 수 있습니다.

현재 이 방법이 다른 델타-웨이트 방식으로 일반화되는지 명확하지 않으므로 LoRA만 지원됩니다.

Update on GitHub