PEFT documentation
ShadowPEFT
ShadowPEFT
ShadowPEFT는 동결된 base decoder-only 모델에 경량이고 사전 학습 가능한 shadow 네트워크를 추가하여 백본과 병렬로 실행되도록 합니다. 작은 shadow 백본이 초기 shadow 상태 s^(0)를 생성하며, 이 상태는 base 모델의 decoder 루프를 따라 이동합니다. 각 대상 블록에서 base hidden states와 shadow 상태 간의 차이를 블록 입력에 주입(저랭크 보정)하고, 블록 출력에서 계산된 게이트드 잔차 업데이트로 shadow 상태를 진행시킵니다. shadow 구성 요소만 학습되며, base 모델은 동결된 상태로 유지됩니다.
Input
├──► Shadow backbone (small, trainable) ──► s^(0)
└──► Base model (frozen, large)
block_0 ◄── inject(h, s) ─► h_0 ──► update ─► s_1
block_1 ◄── inject(h, s) ─► h_1 ──► update ─► s_2
... (the (hidden, shadow) pair rides the loop together)적응은 정적인 가중치 공간 델타가 아니라 레이어 공간에서의 입력 의존적 궤적(섀도 상태가 데이터와 함께 진화함)이므로, ShadowPEFT는 베이스 가중치에 병합할 수 없습니다. merge, merge_adapter, 또는 merge_and_unload를 호출하면 명시적인 오류가 발생합니다. Transformers 언어 모델의 경우, model.base_model.unload_shadow()를 사용하여 경량 섀도 네트워크를 단독으로 가져올 수 있으며, 이는 독립적인 DetachedShadowModel을 반환합니다. 완전한 디노이저 재구성이 아키텍처에 따라 달라지기 때문에 Diffusers 모델에서는 단독 언로드가 지원되지 않습니다.
여러 어댑터를 추가하고, set_adapter로 전환하며, 삭제하고, 활성화/비활성화하는 것은 다른 PEFT 방법과 동일하게 작동합니다. 네트워크를 통한 단일 궤적(single trajectory)인 shadow state가 하나만 존재하기 때문에, 한 번에 하나의 어댑터만 활성화될 수 있습니다.
Shadow backbone은 ShadowConfig.shadow_model으로 제어되는 두 가지 방식으로 구축할 수 있습니다:
"mirror"(기본값): 더 작은 shadow backbone이 자동으로 생성됩니다. 언어 모델은 기본 아키텍처의 축소된 복사본을 사용합니다. 등록된 백엔드가 있는 Diffusers 아키텍처는 선택된 기본 가중치로 초기화된 축소된 유사 아키텍처 모델을 사용하며, 백엔드가 없는 호환 아키텍처는 토큰 단위 residual MLP로 대체됩니다. shadow hidden size가 기본값과 다르면 학습된 projection이 이 차이를 연결합니다.- 모델 ID 또는 로컬 경로: 백본은 더 작은 사전 학습된 모델로 로드됩니다. Transformers 모델은
AutoModel을 사용하며, 등록된 Diffusers 백엔드는 자체 호환 체크포인트 로딩을 정의합니다.
Architecture-aware Diffusers 지원은 모델 클래스에서 자동으로 선택됩니다. Flux2에는 현재 등록된 백엔드가 있으며, 기타 호환되는 transformer 기반 Diffusers 모델은 범용 MLP 폴백을 사용합니다. 완전한 denoiser를 재구성하는 것은 아키텍처에 따라 다르기 때문에, 모든 Diffusers 모델에서 독립적인 unload_shadow()는 여전히 지원되지 않습니다.
LoRA 방식의 방법론과 비교하면, ShadowPEFT는 더 많은 매개변수와 계산량이 필요합니다(병렬 네트워크를 실행하고 전체 디코더 블록을 래핑하기 때문). 그러나 어댑터는 중앙에서 학습할 수 있고, 작업 간에 재사용할 수 있으며, 사전 학습된 소형 모델에서 초기화할 수 있는 자체 완결형 네트워크입니다. 선택적인 보조 손실(auxiliary_loss_weight)은 작업 헤드를 초기 섀도 상태 s^(0)에 적용하여 작업 손실에 더함으로써, 분리 가능한 섀도 경로가 자체적으로 작업을 해결하도록 장려합니다. 인과적 LM의 경우, 기본 출력 헤드가 재사용됩니다. 표준 PEFT 메커니즘을 통해 이를 학습하고 저장하려면 modules_to_save에 "lm_head"를 포함하세요.
KV 캐시
ShadowPEFT는 dual KV 캐시를 통한 증분 디코딩을 지원합니다. 하나는 동결된 베이스 모델용이고, 다른 하나는 shadow backbone용입니다. Inject/update는 토큰 로컬(token-local)이므로, 새로운 토큰은 자신의 shadow 상태 s만 필요로 합니다. 인과성(causality) 덕분에 prefix의 base 키/값(injection 하에 계산된 것)이 유효하게 유지됩니다. 페어링된 객체는 ShadowCache이며, use_cache=True일 때 past_key_values로 반환됩니다. 평소처럼 generate()에 use_cache=True를 전달할 수 있습니다.
out = model.generate(input_ids, max_new_tokens=32) # dual KV cache enabled by default
# or explicitly:
out = model.generate(input_ids, use_cache=True, max_new_tokens=32)use_cache=False는 여전히 작동하며 각 단계마다 전체 시퀀스를 다시 처리합니다(디버깅에 유용합니다).
사용
from transformers import AutoModelForCausalLM
from peft import ShadowConfig, get_peft_model
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-0.6B")
config = ShadowConfig(r=8, shadow_num_hidden_layers=1, task_type="CAUSAL_LM")
model = get_peft_model(model, config)
model.print_healthcheck()
out = model.generate(input_ids, max_new_tokens=32)더 작은 사전 학습된 모델에서 shadow backbone을 초기화하려면, 해당 모델의 id 또는 경로를 shadow_model로 전달하세요:
config = ShadowConfig(shadow_model="Qwen/Qwen3-0.6B", task_type="CAUSAL_LM")
model = get_peft_model(base_model, config)섀도 경로 평가
기본적으로 모델 출력(logits)은 shadow-adapted base model입니다. shadow 보정이 모든 레이어의 base model hidden states에 주입되므로 logits에는 이미 ShadowPEFT가 반영되어 있습니다(비교를 위해 순수한 base model을 얻으려면 model.disable_adapter()를 사용하세요). 보조 손실(auxiliary loss)은 shadow path가 자체적으로 작업을 해결하도록 추가 학습을 수행합니다.
Transformers 언어 모델의 독립형 shadow network(분리 가능한 경량 모델 — merge_and_unload의 ShadowPEFT 대응물)를 평가하려면 unload_shadow()를 사용합니다. 이 메서드는 head(projection(backbone(x)))를 일반 태스크 모델로 반환하며, 다른 모델과 동일하게 평가할 수 있습니다. causal-LM 태스크의 경우 생성이 가능한 causal LM(generate() 및 KV 캐싱 지원)을, 시퀀스 분류 태스크의 경우 마지막 토큰을 풀링하여 클래스 로지트를 반환합니다. Diffusers 모델에 대해 이 메서드를 호출하면 NotImplementedError가 발생합니다.
shadow = model.base_model.unload_shadow() # a DetachedShadowModel (a PreTrainedModel)
shadow.eval()
# causal LM:
out = shadow.generate(input_ids, max_new_tokens=32)
# sequence classification:
logits = shadow(input_ids=input_ids, attention_mask=attention_mask).logits # (batch, num_labels)기본적으로(copy=False) 반환된 모델은 PEFT 모델과 모듈을 공유하며, 동결된 기본 입력 임베딩을 공유하는 shadow backbone은 서브모듈이 아닌 참조를 통해 해당 모듈에 도달합니다. 이는 평가에는 문제가 없으나, save_pretrained를 호출하면 임베딩 테이블이 없는 체크포인트가 작성됨을 의미합니다. 독립적인 모델을 저장하거나 push하려면 copy=True를 전달하세요:
shadow = model.base_model.unload_shadow(copy=True)
shadow.save_pretrained("standalone-shadow")벤치마크 개요
API
ShadowConfig
클래스 peft.ShadowConfig
< source >( 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 = Falsetarget_modules: list[str] | str | None = Noneexclude_modules: list[str] | str | None = Noner: int = 8shadow_alpha: float = 0.1shadow_dropout: float = 0.2init_weights: bool = Trueshadow_model: str = 'mirror'shadow_num_hidden_layers: int | None = Noneshadow_hidden_size: int | None = Noneshadow_num_attention_heads: int | None = Noneshadow_intermediate_size: int | None = Noneshare_embeddings: bool = Trueupdate_hidden_size: int | None = Noneauxiliary_loss_weight: float = 0.05layers_to_transform: list[int] | int | None = Nonelayers_pattern: list[str] | str | None = Nonemodules_to_save: list[str] | None = None )
매개변수
- target_modules (
Optional[Union[list[str], str]]) — shadow 메커니즘으로 감싸는 transformer 블록(선형 레이어가 아닌 전체 decoder 블록)입니다. 모듈 이름의 리스트나 정규식, 예: 모든 decoder 블록을 대상으로 하려면r'.*\.layers\.\d+$'등을 사용할 수 있습니다. 기본값은None이며, 이 경우 base model의 모든 decoder 블록이 감싸집니다. shadow state가 첫 번째 감싸진 블록부터 마지막 블록까지 decoder 루프를 따라 이동하므로, 감싸는 블록은 연속이어야 한다는 점에 유의하세요. - exclude_modules (
Optional[Union[list[str], str]]) — shadow 메커니즘으로 감싸지 않을 모듈의 이름입니다. 리스트 또는 정규식으로 지정합니다. 기본값은None입니다. - r (
int) — 저순위 주입 병목W_down/W_up의 랭크입니다. 기본값은8입니다. - shadow_alpha (
float) — 블록 입력에 추가되는 주입된 보정의 강도(식 4). 기본값은0.1입니다. - shadow_dropout (
float) — bottleneck(Eq. 3) 이전에 discrepancy signal에 적용되는 dropout 확률입니다. 기본값은0.2입니다. - init_weights (
bool) — 학습 시작 시 주입이 no-op이 되도록W_up을 0으로 초기화할지 여부(LoRA의B=0규약을 따름). 자신이 무엇을 하는지 확실히 아는 경우에만 이 값을 변경하십시오. 기본값은True입니다. - shadow_model (
str) — shadow backbone를 구축하는 방법입니다."mirror"(기본값)는 base model과 동일한 아키텍처를 가지되 레이어 수가 적거나 작은 새로운 backbone을 구축합니다(아래의shadow_*오버라이드를 참조). 등록된 Shadow backends를 사용하는 Diffusers 아키텍처는 축소된 유사 아키텍처 모델을 사용하며, 기타 호환 가능한 diffusion transformers는 범용 token-wise MLP로 폴백됩니다. 기타 문자열은 model id 또는 로컬 경로로 처리됩니다. “projected” Transformers shadow checkpoint(model_type == "causal_lm_with_hidden_projection", 예:shadow-llm/Qwen3-0.6B-H8B)는 사전 학습된 backbone과 함께 학습된 shadow-hidden -> base-hidden 투영을 로드합니다. 명시적인 Diffusers checkpoint는 등록된 backends를 필요로 하며, 현재 Flux2가 지원됩니다. - shadow_num_hidden_layers (
Optional[int]) — 자동 생성되는("mirror") shadow backbone의 깊이입니다. Flux2의 경우 요청된 수의 double-stream 블록과 그 두 배의 single-stream 블록을 생성하며, 일반적인 Diffusers 폴백의 경우 residual MLP 블록의 수를 의미합니다. 기본값은None(1레이어)입니다. - shadow_hidden_size (
Optional[int]) — 자동 생성된 shadow backbone의 hidden size입니다. base hidden size와 다를 수 있으며, 이 경우 투영이 자동으로 삽입됩니다. 아키텍처 백엔드에 따라 추가 제약이 있을 수 있으며, Flux2 축소 시 base attention head dimension을 유지해야 합니다. 기본값은None(base 모델과 동일)입니다. - shadow_num_attention_heads (
Optional[int]) — 자동 생성된 shadow backbone의 attention head 수입니다. 기본값은None(기본 모델과 동일)입니다. 이 옵션은 아키텍처 인식 백엔드에서 사용되지만, 범용 Diffusers MLP 폴백에서는 사용되지 않습니다. - shadow_intermediate_size (
Optional[int]) — 자동 생성된 shadow backbone의 feed-forward 폭입니다. 기본값은None입니다(일반적인 Diffusers MLP 폴백의 경우 hidden size와 동일). - share_embeddings (
bool) — shadow backbone의 자체 임베딩 테이블 대신, 동결된 베이스 입력 임베딩을 재사용하여 shadow backbone에 공급(inputs_embeds을 통해)할지 여부입니다. 기본값은True입니다. - update_hidden_size (
Optional[int]) —T(candidate) 및G(gate) 업데이트 MLP의 숨겨진 폭입니다. 기본값은None이며(r사용)입니다. - auxiliary_loss_weight (
float) —labels가 전달될 때 작업 손실에 추가되는 보조 섀도 손실(식 8-9)의 가중치lambda입니다. Diffusion 학습 루프는 동일한 값을 detached denoising loss에 사용할 수 있습니다. 비활성화하려면0으로 설정합니다. 기본값은0.05입니다. - layers_to_transform (
Optional[Union[list[int], int]]) — 변환할 블록의 인덱스입니다. 리스트가 전달되면 해당 인덱스의 블록에 shadow 메커니즘이 적용됩니다. 단일 정수가 전달되면 해당 인덱스에만 적용됩니다. 기본값은None이며, 이 경우 모든 일치하는 블록이 변환됩니다. - layers_pattern (
Optional[Union[list[str], str]]) —layers_to_transform가None과 다르면만 사용되는 레이어 패턴 이름입니다. 이는 디코더 블록을 포함하는nn.ModuleList의 이름으로, 보통"layers"또는"h"입니다. 기본값은None입니다. - modules_to_save (
Optional[list[str]]) — 최종 체크포인트에 학습 가능 상태로 설정하고 저장할 추가 모듈(예:"lm_head"또는 분류 헤드)입니다. 기본값은None입니다.
ShadowModel(ShadowPEFT)의 설정 클래스입니다.
ShadowPEFT는 동결된 기본(decoder-only) 모델에 작고 학습 가능한 병렬 shadow 네트워크를 추가합니다. shadow backbone은 초기 shadow 상태 s^(0)를 생성하며, 이 상태는 기본 모델의 디코더 루프를 따라 이동합니다. 대상 블록마다 기본 은닉 상태와 shadow 상태 간의 차이를 블록 입력에 다시 주입(Eq. 2-4)하고, 블록 출력에서 게이트된 잔차 업데이트를 통해 shadow 상태를 진행시킵니다(Eq. 5-7). 학습되는 것은 shadow 구성 요소뿐이며, 기본 모델은 동결된 상태로 유지됩니다. 적응이 정적인 가중치 델타가 아니라 레이어 공간에서 입력에 의존하는 궤적이므로, ShadowPEFT는 기본 가중치에 병합할 수 없습니다.
ShadowModel
클래스 peft.ShadowModel
< source >( modelpeft_config: Union[PeftConfig, dict[str, PeftConfig]]adapter_name: strlow_cpu_mem_usage: bool = Falsestate_dict: Optional[dict[str, torch.Tensor]] = None )
ShadowPEFT 모델을 사전 학습된 transformers 모델에서 생성합니다.
ShadowPEFT는 동결된 기본 decoder-only 모델에 작고 학습 가능한 병렬 shadow 네트워크를 추가합니다. shadow backbone은 기본 decoder 루프를 따라가는 초기 shadow 상태를 생성합니다. 대상 블록마다 기본 hidden states와 shadow 상태 간의 차이를 해당 블록에 주입하고, gated residual 업데이트를 통해 shadow 상태를 진행시킵니다. 학습 대상은 shadow 구성 요소만입니다. 구성은 ShadowConfig를 참조하세요.
해당 메서드는 베이스 가중치에 병합할 수 없습니다(적응은 입력에 의존하는 궤적이며 정적인 가중치 델타가 아니므로). 대신 독립적인 섀도 네트워크를 얻으려면 ShadowModel.unload_shadow()를 사용하세요.
Shadow path 자체의 task loss, CE(shadow_head(s^(0)), labels) (가중치 없음; forward가 가중치를 적용합니다).
손실은 초기 shadow 상태 s^(0)(투사된 shadow backbone 출력)에 대해 계산됩니다. 이는 독립적인 unload_shadow() 모델이 head(projection(backbone(x)))로 계산하는 값과 정확히 일치합니다. 이 점이 분리된 shadow 네트워크를 단독으로 사용할 수 있게 해줍니다. 최종 상태인 s^(L)(베이스 모델의 레이어별 출력에 의존하며 단독으로 존재하지 않음)에 대해 학습을 수행하면 분리된 모델은 학습되지 않은 상태가 됩니다.
unload_shadow
< source >( adapter_name: str | None = Nonecopy: bool = False )
베이스 모델 없이 그림자 백본(+ 헤드)를 독립된 모델로 반환합니다.
merge_and_unload의 ShadowPEFT 대응 함수입니다. merge_and_unload가 적응이 내장된 베이스 모델을 반환하는 반면, 이 함수는 고효율/엣지 추론을 위해 경량화된 shadow 네트워크만 반환합니다. head(projection(backbone(x)))를 실행하며, 블록별 업데이트는 베이스 출력에 의존하므로 독립적으로 존재하지 않습니다. 언어 모델의 경우, 결과는 일반적인 causal LM처럼 동작하며(generate() 및 KV 캐싱 지원) Diffusers 모델은 각 diffusion 아키텍처마다 완전한 독립적 denoiser를 재구성하는 방식이 다르므로 지원하지 않습니다.
결과를 변수에 할당하고 사용하세요. copy=False인 경우 모듈은 이 모델과 공유된 상태로 유지됩니다.
독립 모델을 save_pretrained로 저장할 계획이라면 copy=True를 전달하세요. 동결된 기본 입력 임베딩을 공유하는 섀도 백본(기본 "mirror" 구성)은 반환된 모델의 서브모듈이 아닌 참조를 통해 해당 임베딩에 접근하므로, copy=False 체크포인트에는 임베딩 테이블이 누락됩니다. copy=True는 사본을 다시 연결하여 완전한 체크포인트를 저장합니다.
ShadowCache
증분적 ShadowPEFT 디코딩을 위한 페어링된 KV 캐시
Autoregressive generation에는 base-model cache(shadow injection 하에서 계산된 keys/values) 및 별도의 shadow model cache(s^(0)를 토큰 단위로 진행하기 위한)가 필요합니다. 이는 Transformers의 Cache 하위 클래스입니다. 배치 또는 시퀀스 레이아웃에 영향을 주는 작업(reset, reorder, crop, repeat, select)은 두 cache 모두에 적용되며, attention 메타데이터 및 업데이트는 base cache에 위임됩니다. 아키텍처별 cache 구현과의 호환성을 위해 알려지지 않은 cache 속성도 base cache에 위임됩니다.
두 캐시의 레이어 레이아웃과 hidden size가 다를 수 있으므로 wrapper는 의도적으로 compileable하지 않습니다. 단일 legacy tuple로는 두 경로를 모두 표현할 수 없기 때문에 legacy tuple 변환은 지원하지 않습니다. shadow half는 base forward 전에 unpack되고, 반환 시 다시 pack됩니다. ShadowModel hooks를 참조하세요.