PEFT documentation

ShadowPEFT

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

ShadowPEFT

ShadowPEFT enrichit un modèle de base décodeur uniquement, figé, avec un réseau shadow léger et pré-entraînable qui s’exécute en parallèle avec l’architecture principale. Une petite architecture principale shadow produit un état shadow initial s^(0), qui suit ensuite la boucle de décodage du modèle de base : à chaque bloc ciblé, l’écart entre les états cachés de base et l’état shadow est réinjecté dans l’entrée du bloc (une correction de rang faible), et l’état shadow est mis à jour par une mise à jour résiduelle à porte calculée à partir de la sortie du bloc. Seuls les composants shadow sont entraînés ; le modèle de base reste figé.

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)

L'adaptation étant une trajectoire dépendante des entrées dans l'espace des couches (l'état ombre évolue avec les données) et non un delta statique dans l'espace des poids, ShadowPEFT ne peut pas être fusionné dans les poids de base. L'appel de merge, merge_adapter ou merge_and_unload lève une erreur explicite. Pour les modèles de langage Transformers, vous pouvez obtenir le réseau ombre léger de manière autonome via model.base_model.unload_shadow(), qui renvoie un DetachedShadowModel indépendant. Le déchargement autonome n'est pas pris en charge pour les modèles Diffusers, car la reconstruction d'un débruiteur complet dépend de l'architecture.

L’ajout de plusieurs adaptateurs, le passage de l’un à l’autre avec set_adapter, leur suppression ainsi que leur activation/désactivation fonctionnent de la même manière que pour les autres méthodes PEFT. Un seul adaptateur peut être actif à la fois, car l’état fantôme correspond à un unique parcours à travers le réseau.

Le backbone d'ombre peut être construit de deux manières, contrôlées par ShadowConfig.shadow_model :

  • "mirror" (par défaut) : un backbone d’ombre plus petit est créé automatiquement. Les modèles de langage utilisent une copie réduite de l’architecture de base. Les architectures Diffusers avec un backend enregistré utilisent un modèle d’architecture similaire réduit, initialisé à partir de poids de base sélectionnés ; les architectures compatibles sans backend basculent sur un MLP résiduel par jeton. Lorsque la taille cachée d’ombre diffère de celle de base, une projection entraînée comble l’écart.
  • un identifiant de modèle ou un chemin local : le backbone est chargé en tant que modèle pré-entraîné plus petit. Les modèles Transformers utilisent AutoModel ; les backends Diffusers enregistrés définissent leur propre chargement de checkpoint compatible.

La prise en charge des Diffusers tenant compte de l’architecture est sélectionnée automatiquement à partir de la classe du modèle. Flux2 dispose actuellement d’un backend enregistré ; les autres modèles Diffusers compatibles basés sur des transformateurs utilisent le repli générique MLP. L’appel autonome de unload_shadow() reste non pris en charge pour tous les modèles Diffusers, car la reconstruction d’un débruiteur complet est spécifique à l’architecture.

Par rapport aux méthodes de type LoRA, ShadowPEFT ajoute davantage de paramètres et de calculs (il exécute un réseau parallèle et encapsule des blocs décodeurs entiers), mais l'adaptateur est un réseau autonome qui peut être entraîné de manière centralisée, réutilisé entre différentes tâches et initialisé à partir d'un petit modèle pré-entraîné. Une perte auxiliaire optionnelle (auxiliary_loss_weight) applique la tête de tâche à l'état d'ombre initial s^(0) et l'ajoute à la perte de la tâche, encourageant le chemin d'ombre détachable à résoudre la tâche de manière autonome. Pour les LM causaux, la tête de sortie de base est réutilisée ; incluez "lm_head" dans modules_to_save pour l'entraîner et l'enregistrer via le mécanisme PEFT standard.

Cache KV

ShadowPEFT prend en charge le décodage incrémental avec un cache KV dual : l’un pour le modèle de base figé et l’autre pour l’ossature d’ombre. L’injection et la mise à jour sont locales au jeton, de sorte qu’un nouveau jeton n’a besoin que de son propre état d’ombre s ; la causalité conserve la validité des clés/valeurs de préfixe de base (calculées sous injection). L’objet apparié est un ShadowCache, renvoyé en tant que past_key_values lorsque use_cache=True. Vous pouvez passer use_cache=True à generate() comme d’habitude.

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 fonctionne toujours et reprocesse la séquence complète à chaque étape (utile pour le débogage).

Utilisation

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)

Pour initialiser l'ossature d'ombre à partir d'un modèle pré-entraîné plus petit, passez son identifiant ou son chemin en tant que shadow_model :

config = ShadowConfig(shadow_model="Qwen/Qwen3-0.6B", task_type="CAUSAL_LM")
model = get_peft_model(base_model, config)

Évaluation du chemin d'ombre

Par défaut, la sortie du modèle (logits) est celle du modèle de base adapté par ombre : les corrections d’ombre sont injectées dans les états cachés du modèle de base à chaque couche, de sorte que logits reflète déjà ShadowPEFT (utilisez model.disable_adapter() pour obtenir le modèle de base brut à des fins de comparaison). La perte auxiliaire entraîne en outre le chemin d’ombre à résoudre la tâche de manière autonome.

Pour évaluer le réseau d'ombre autonome d'un modèle de langage Transformers (le modèle détachable et léger — l'analogue ShadowPEFT de merge_and_unload), utilisez unload_shadow(). Il renvoie head(projection(backbone(x))) en tant que modèle de tâche standard que vous pouvez évaluer comme n'importe quel autre : pour une tâche de LM causal, il s'agit d'un LM causal capable de génération (prend en charge generate() et le cache KV), et pour une tâche de classification de séquence, il agrège le dernier jeton et renvoie les logits de classe. L'appel de cette méthode pour un modèle Diffusers lève 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)

Par défaut (copy=False), le modèle retourné partage ses modules avec le modèle PEFT, et un squelette d’ombre qui partage les embeddings d’entrée figés de la base y accède via une référence qui n’est pas un sous-module. Cela convient pour l’évaluation, mais cela signifie que save_pretrained écrirait un point de contrôle sans le tableau d’embeddings. Passez copy=True si vous souhaitez enregistrer ou publier le modèle autonome :

shadow = model.base_model.unload_shadow(copy=True)
shadow.save_pretrained("standalone-shadow")

API

ShadowConfig

classe peft.ShadowConfig

< >

( 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 )

Paramètres

  • target_modules (Optional[Union[list[str], str]]) — Les blocs de transformer à envelopper avec le mécanisme d’ombre (blocs de décodeur entiers, et non couches linéaires). Peut être une liste de noms de modules, ou une expression régulière, par ex. r'.*\.layers\.\d+$' pour cibler chaque bloc de décodeur. La valeur par défaut est None, auquel cas chaque bloc de décodeur du modèle de base est enveloppé. Notez que les blocs enveloppés doivent être contigus, car l’état d’ombre suit la boucle du décodeur depuis le premier bloc enveloppé jusqu’au dernier.
  • exclude_modules (Optional[Union[list[str], str]]) — Les noms des modules à ne pas envelopper avec le mécanisme d’ombre, fournis sous forme de liste ou d’expression régulière. La valeur par défaut est None.
  • r (int) — Le rang du goulot d’étranglement d’injection de faible rang W_down / W_up. La valeur par défaut est 8.
  • shadow_alpha (float) — L'intensité de la correction injectée ajoutée à l'entrée du bloc (Éq. 4). Valeur par défaut : 0.1.
  • shadow_dropout (float) — Probabilité de dropout appliquée au signal de divergence avant le goulot d'étranglement (Éq. 3). Valeur par défaut : 0.2.
  • init_weights (bool) — Indique s'il faut initialiser W_up à zéro afin que l'injection soit une opération nulle au début de l'entraînement (en suivant la convention B=0 de LoRA). Ne modifiez pas ce paramètre à moins de savoir exactement ce que vous faites. La valeur par défaut est True.
  • shadow_model (str) — Méthode de construction du backbone d’ombre. "mirror" (par défaut) construit un nouveau backbone de la même architecture que le modèle de base, mais avec moins de couches ou des couches plus petites (voir les substitutions shadow_* ci-dessous). Les architectures Diffusers disposant d’un backend Shadow enregistré utilisent un modèle d’architecture réduite similaire ; les autres transformeurs de diffusion compatibles basculent sur un MLP générique par jeton. Toute autre chaîne est interprétée comme un identifiant de modèle ou un chemin local. Un checkpoint d’ombre Transformers « projeté » (model_type == "causal_lm_with_hidden_projection", par ex. shadow-llm/Qwen3-0.6B-H8B) charge son backbone pré-entraîné ainsi que sa projection entraînée des états cachés d’ombre vers les états cachés de base. Les checkpoints Diffusers explicites nécessitent un backend enregistré ; actuellement, Flux2 est pris en charge.
  • shadow_num_hidden_layers (Optional[int]) — La profondeur de l’ossature d’ombre auto-construite ("mirror"). Pour Flux2, cela crée le nombre de blocs double-flux demandé et deux fois plus de blocs mono-flux ; pour le repli générique de Diffusers, il s’agit du nombre de blocs MLP résiduels. La valeur par défaut est None (1 couche).
  • shadow_hidden_size (Optional[int]) — Taille cachée du backbone d’ombre construit automatiquement ; peut différer de la taille cachée de base (une projection est insérée automatiquement). Les backends d’architecture peuvent imposer des contraintes supplémentaires ; les réductions Flux2 doivent préserver la dimension de tête d’attention de base. Valeur par défaut : None (identique au modèle de base).
  • shadow_num_attention_heads (Optional[int]) — Le nombre de têtes d'attention de l'ossature d'ombre construite automatiquement. La valeur par défaut est None (identique à celle du modèle de base). Cette option est utilisée par les backends prenant en compte l'architecture, mais pas par le repli MLP générique de Diffusers.
  • shadow_intermediate_size (Optional[int]) — Largeur du réseau de propagation avant de l'ossature d'ombre construite automatiquement. La valeur par défaut est None (identique à sa taille cachée pour le repli sur le MLP générique de Diffusers).
  • share_embeddings (bool) — Indique s’il faut réutiliser les embeddings d’entrée gelés de la base pour alimenter l’ossature ombre (via inputs_embeds) plutôt que la table d’embeddings propre à l’ossature ombre. La valeur par défaut est True.
  • update_hidden_size (Optional[int]) — La largeur cachée des MLP de mise à jour T (candidat) et G (porte). La valeur par défaut est None (utilise r).
  • auxiliary_loss_weight (float) — Le poids lambda de la perte d’ombre auxiliaire (Éq. 8-9) qui est ajoutée à la perte de la tâche lorsque labels sont transmis. Les boucles d’entraînement de diffusion peuvent utiliser la même valeur pour la perte de débruitage détachée. Définissez 0 pour la désactiver. Valeur par défaut : 0.05.
  • layers_to_transform (Optional[Union[list[int], int]]) — Les indices de blocs à transformer. Si une liste est transmise, le mécanisme d'ombre est appliqué aux blocs situés à ces indices. Si un entier unique est transmis, il est appliqué uniquement à cet index. La valeur par défaut est None (chaque bloc correspondant est transformé).
  • layers_pattern (Optional[Union[list[str], str]]) — Le nom du motif de couche, utilisé uniquement si layers_to_transform est différent de None. Il s’agit du nom de la nn.ModuleList qui contient les blocs décodeurs (souvent "layers" ou "h"). La valeur par défaut est None.
  • modules_to_save (Optional[list[str]]) — Les modules supplémentaires à définir comme entraînables et à enregistrer dans le point de contrôle final (par exemple "lm_head" ou une tête de classification). La valeur par défaut est None.

Classe de configuration pour ShadowModel (ShadowPEFT).

ShadowPEFT enrichit un modèle de base décodeur uniquement, figé, avec un petit réseau parallèle shadow entraînable. Une colonne vertébrale shadow produit un état shadow initial s^(0) qui suit ensuite la boucle de décodage du modèle de base : à chaque bloc ciblé, l'écart entre les états cachés de base et l'état shadow est réinjecté dans l'entrée du bloc (Éq. 2-4), et l'état shadow est mis à jour par une mise à jour résiduelle à porte à partir de la sortie du bloc (Éq. 5-7). Seuls les composants shadow sont entraînés ; le modèle de base reste figé. Comme l'adaptation est une trajectoire dépendante de l'entrée dans l'espace des couches plutôt qu'un delta de poids statique, ShadowPEFT ne peut pas être fusionné dans les poids de base.

ShadowModel

classe peft.ShadowModel

< >

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

Crée un modèle ShadowPEFT à partir d'un modèle transformers pré-entraîné.

ShadowPEFT enrichit un modèle de base décodeur uniquement, figé, avec un petit réseau parallèle shadow entraînable. Une colonne vertébrale shadow produit un état shadow initial qui suit la boucle du décodeur de base ; à chaque bloc ciblé, l'écart entre les états cachés de base et l'état shadow est injecté dans le bloc, et l'état shadow est mis à jour par une mise à jour résiduelle à porte. Seuls les composants shadow sont entraînés. Consultez ShadowConfig pour la configuration.

La méthode ne peut pas être fusionnée dans les poids de base (l'adaptation est une trajectoire dépendante de l'entrée, et non un delta de poids statique) ; utilisez ShadowModel.unload_shadow() pour obtenir le réseau d'ombre autonome à la place.

shadow_auxiliary_loss

< >

( labels: Tensorattention_mask: torch.Tensor | None = None )

La perte de tâche propre au chemin d’ombre, CE(shadow_head(s^(0)), labels) (non pondérée ; forward applique le poids).

La perte est calculée sur l’état d’ombre initial s^(0) (la sortie du tronc d’ombre, projetée) — exactement ce que le modèle autonome unload_shadow() calcule sous la forme head(projection(backbone(x))). C’est ce qui rend le réseau d’ombre détaché utilisable de manière autonome ; l’entraîner sur s^(L) (l’état final, qui dépend des sorties par couche du modèle de base et qui n’existe pas de manière autonome) laisserait le modèle détaché non entraîné.

unload_shadow

< >

( adapter_name: str | None = Nonecopy: bool = False )

Paramètres

  • adapter_name (str, facultatif) — L’adaptateur dont le réseau d’ombre doit être déchargé. Par défaut, l’adaptateur actif.
  • copy (bool, facultatif, par défaut False) — Si True, effectue une copie profonde du modèle retourné afin qu’il soit indépendant de celui-ci (utilise plus de mémoire). Si False (par défaut), partage les modules — similaire à merge_and_unload, qui réutilise les modules plutôt que de les cloner. Modifier un modèle affecte alors l’autre.

Retourner l'architecture principale d'ombre (+ tête) en tant que modèle autonome, sans le modèle de base.

L’analogue ShadowPEFT de merge_and_unload : là où celui-ci renvoie le modèle de base avec l’adaptation intégrée, celui-ci ne renvoie que le réseau d’ombre léger pour une inférence efficace / sur périphérique. Il exécute head(projection(backbone(x))) — les mises à jour par bloc nécessitent les sorties de base et n’existent donc pas de manière autonome. Pour les modèles de langage, le résultat se comporte comme un LM causal normal (prend en charge generate() et le cache KV). Les modèles Diffusers ne sont pas pris en charge, car la reconstruction d’un débruiteur autonome complet est spécifique à chaque architecture de diffusion.

Affectez le résultat à une variable et utilisez-le ; avec copy=False, les modules restent partagés avec ce modèle.

Passez copy=True si vous prévoyez d'save_pretrained le modèle autonome. Un backbone d'ombre qui partage les embeddings d'entrée figés de la base (la configuration par défaut "mirror") y accède via une référence qui n'est pas un sous-module du modèle retourné, de sorte qu'un point de contrôle copy=False est manquant du tableau d'embeddings ; copy=True ré-attache une copie privée et enregistre un point de contrôle complet.

ShadowCache

classe peft.tuners.shadow.ShadowCache

< >

( base: Any = Noneshadow: Any = None )

Caches KV appariés pour le décodage incrémentiel ShadowPEFT.

La génération autorégressive nécessite un cache du modèle de base (clés/valeurs calculées sous injection d’ombre) et un cache de modèle d’ombre distinct (pour faire avancer s^(0) token par token). Il s’agit d’une sous-classe de Cache de Transformers : les opérations qui affectent la disposition du lot ou de la séquence (reset, reorder, crop, repeat et select) sont appliquées aux deux caches, tandis que les métadonnées d’attention et les mises à jour sont déléguées au cache de base. Les attributs de cache inconnus sont également délégués au cache de base pour assurer la compatibilité avec les implémentations de cache spécifiques à l’architecture.

Le wrapper n'est pas compilable par conception, car les deux caches peuvent avoir des dispositions de couches et des tailles de vecteurs cachés différentes. La conversion de tuple hérité n'est pas prise en charge, car un seul tuple hérité ne peut pas représenter les deux chemins. La moitié ombre est dépaquetée avant le forward de base et re-paquétée à la sortie — voir les hooks de ShadowModel.

Update on GitHub