PEFT documentation
ShadowPEFT
ShadowPEFT
ShadowPEFT дополняет замороженную базовую модель, работающую только в режиме декодирования, лёгкой, обучаемой теневой сетью, которая работает параллельно с основным каркасом. Небольшой теневой каркас формирует начальное теневое состояние s^(0), которое затем проходит по циклу декодера базовой модели: на каждом целевом блоке расхождение между скрытыми состояниями базовой модели и теневым состоянием добавляется обратно во вход блока (низкоранговая коррекция), а теневое состояние обновляется по гейтированному остаточному обновлению, вычисленному на основе выхода блока. Обучаются только теневые компоненты; базовая модель остаётся замороженной.
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. Одновременно может быть активен только один адаптер, поскольку теневое состояние представляет собой единственную траекторию через сеть.
Теневой базовый блок можно построить двумя способами, которые контролируются параметром ShadowConfig.shadow_model:
"mirror"(по умолчанию): автоматически создаётся более лёгкий теневой backbone. Языковые модели используют уменьшенную копию базовой архитектуры. Архитектуры Diffusers с зарегистрированным backend используют уменьшенную модель, аналогичную архитектуре, инициализированную из выбранных базовых весов; совместимые архитектуры без backend откатываются к поэлементному residual MLP. Если размер скрытого слоя теневой модели отличается от базового, обученная проекция компенсирует эту разницу.- идентификатор модели или локальный путь: базовая модель загружается как более мелкая предобученная модель. Модели Transformers используют
AutoModel; зарегистрированные бэкенды Diffusers определяют собственную совместимую загрузку контрольных точек.
Поддержка Diffusers с учётом архитектуры выбирается автоматически на основе класса модели. Для Flux2 в настоящее время зарегистрирован отдельный бэкенд; другие совместимые трансформерные модели Diffusers используют универсальный fallback на основе MLP. Самостоятельный вызов unload_shadow() по-прежнему не поддерживается для всех моделей Diffusers, поскольку восстановление полного денойзера зависит от конкретной архитектуры.
В отличие от методов в стиле LoRA, ShadowPEFT добавляет больше параметров и вычислений (он запускает параллельную сеть и оборачивает целые блоки декодера), но адаптер представляет собой автономную сеть, которую можно обучать централизованно, переиспользовать для разных задач и инициализировать из предварительно обученной небольшой модели. Необязательный вспомогательный потерь (auxiliary_loss_weight) применяет головку задачи к начальному теневому состоянию s^(0) и добавляет её к потере задачи, побуждая отключаемый теневой путь решать задачу самостоятельно. Для каузальных LM переиспользуется базовая головка вывода; включите "lm_head" в modules_to_save, чтобы обучить и сохранить её через стандартный механизм PEFT.
Кэш KV
ShadowPEFT поддерживает инкрементальное декодирование с dual KV-кэшем: один для замороженной базовой модели и один для теневой основы. Инжекция и обновление локальны по токенам, поэтому для нового токена требуется только его собственное теневое состояние s; причинность сохраняет валидность ключей и значений префикса базовой модели (вычисленных при инжекции). Сопоставленный объект — ShadowCache, который возвращается как past_key_values, если use_cache=True. Вы можете передать use_cache=True в generate() как обычно.
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)Чтобы инициализировать теневой backbone из более мелкой предобученной модели, передайте её id или путь как shadow_model:
config = ShadowConfig(shadow_model="Qwen/Qwen3-0.6B", task_type="CAUSAL_LM")
model = get_peft_model(base_model, config)Оценка теневой траектории
По умолчанию выход модели (logits) — это базовая модель с теневой адаптацией: теневые корректировки внедряются в скрытые состояния базовой модели на каждом слое, поэтому logits уже отражают ShadowPEFT (чтобы получить чистую базовую модель для сравнения, используйте model.disable_adapter()). Дополнительный вспомогательный loss дополнительно обучает теневой путь решать задачу самостоятельно.
Для оценки автономной теневой сети языковой модели Transformers (отделяемая, легковесная модель — аналог ShadowPEFT для merge_and_unload) используйте 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, а теневой базовый блок, разделяющий замороженные входные эмбеддинги, получает к ним доступ через ссылку, которая не является подмодулем. Это нормально для оценки, но это означает, что save_pretrained запишет контрольную точку без таблицы эмбеддингов. Передайте 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 (целые декодерные блоки, а не линейные слои). Может быть списком имён модулей или регулярным выражением, напримерr'.*\.layers\.\d+$', чтобы захватить каждый декодерный блок. По умолчаниюNone, в этом случае оборачиваются все декодерные блоки базовой модели. Обратите внимание, что оборачиваемые блоки должны быть непрерывными, поскольку shadow-состояние передаётся по циклу декодера от первого оборачиваемого блока до последнего. - exclude_modules (
Optional[Union[list[str], str]]) — Имена модулей, которые не следует оборачивать с помощью теневой механики, заданные в виде списка или регулярного выражения. По умолчаниюNone. - r (
int) — Ранг бутылочного горлышка низкорангового внедренияW_down/W_up. По умолчанию8. - shadow_alpha (
float) — Сила вводимой коррекции, добавляемой к входу блока (формула 4). По умолчанию0.1. - shadow_dropout (
float) — Вероятность dropout, применяемая к сигналу расхождения перед бутылочным горлышком (формула 3). По умолчанию0.2. - init_weights (
bool) — Определяет, следует ли обнулять инициализациюW_up, чтобы инъекция не влияла на модель в начале обучения (по аналогии с конвенциейB=0в LoRA). Не изменяйте это значение, если точно не знаете, что делаете. По умолчаниюTrue. - shadow_model (
str) — Способ построения теневой базовой модели."mirror"(по умолчанию) строит новую базовую модель той же архитектуры, что и исходная модель, но с меньшим количеством/размером слоёв (см. переопределенияshadow_*ниже). Архитектуры Diffusers с зарегистрированным теневым бэкендом используют модель с уменьшенной архитектурой; другие совместимые диффузионные трансформеры откатываются к универсальному посимвольному MLP. Любая другая строка рассматривается как идентификатор модели или локальный путь. «Проецированная» теневая контрольная точка Transformers (model_type == "causal_lm_with_hidden_projection", напримерshadow-llm/Qwen3-0.6B-H8B) загружает её предобученную базовую модель вместе с обученным проекционным слоем теневых скрытых состояний -> скрытых состояний базовой модели. Явные контрольные точки Diffusers требуют зарегистрированного бэкенда; в настоящее время поддерживается Flux2. - shadow_num_hidden_layers (
Optional[int]) — Глубина автоматически создаваемого ("mirror") теневого каркаса. Для Flux2 это определяет требуемое количество двухпоточных блоков и вдвое больше однопоточных блоков; для универсального fallback в Diffusers — это количество остаточных MLP-блоков. По умолчаниюNone(1слой). - shadow_hidden_size (
Optional[int]) — Размер скрытого слоя автоматически создаваемого теневого базового блока; может отличаться от базового размера скрытого слоя (проекция вставляется автоматически). Бэкенды архитектуры могут накладывать дополнительные ограничения; для сокращений Flux2 необходимо сохранять базовое измерение головы внимания. По умолчаниюNone(как у базовой модели). - shadow_num_attention_heads (
Optional[int]) — Количество голок внимания в автоматически создаваемом теневом базовом блоке. По умолчаниюNone(как в базовой модели). Этот параметр используется архитектура-зависимыми бэкендами, но не универсальным фолбэком Diffusers на основе MLP. - shadow_intermediate_size (
Optional[int]) — Ширина полносвязного слоя автоматически создаваемого теневого каркаса. По умолчаниюNone(совпадает с размером скрытого слоя для универсального резервного варианта MLP в Diffusers). - share_embeddings (
bool) — Определяет, следует ли повторно использовать замороженные входные эмбеддинги базовой модели для подачи в теневой ствол (черезinputs_embeds) вместо собственной таблицы эмбеддингов теневого ствола. По умолчаниюTrue. - update_hidden_size (
Optional[int]) — Скрытая ширина MLP-слоёв обновленияT(кандидат) иG(шлюз). По умолчаниюNone(используетсяr). - auxiliary_loss_weight (
float) — Весlambdaвспомогательной тени потери (формулы 8–9), который добавляется к потере задачи при передачеlabels. Циклы обучения диффузии могут использовать то же значение для отсоединённой потери денойзинга. Установите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 дополняет замороженную базовую модель только с декодером небольшой обучаемой параллельной shadow-сетью. Shadow-каркас формирует начальное shadow-состояние s^(0), которое затем проходит по циклу декодера базовой модели: на каждом целевом блоке расхождение между скрытыми состояниями базовой модели и shadow-состоянием подмешивается обратно во вход блока (формулы 2–4), а shadow-состояние обновляется по выходу блока с помощью гейтированного остаточного обновления (формулы 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 дополняет замороженную базовую модель только с декодером небольшой обучаемой параллельной shadow-сетью. Shadow-каркас формирует начальное shadow-состояние, которое проходит по циклу базового декодера; на каждом целевом блоке расхождение между скрытыми состояниями базовой модели и shadow-состоянием вводится в блок, а shadow-состояние обновляется по гейтированному остаточному обновлению. Обучаются только shadow-компоненты. См. ShadowConfig для конфигурации.
Метод не может быть объединён с базовыми весами (адаптация представляет собой зависящую от входных данных траекторию, а не статическую дельту весов); вместо этого используйте ShadowModel.unload_shadow(), чтобы получить автономную теневую сеть.
Собственная функция потерь задачи теневого пути, CE(shadow_head(s^(0)), labels) (без весов; forward применяет вес).
Потери вычисляются на начальном теневом состоянии s^(0) (выход теневой базовой сети, проецированный) — ровно то, что автономная модель unload_shadow() вычисляет как head(projection(backbone(x))). Именно это позволяет использовать отсоединённую теневую сеть самостоятельно; обучение её на s^(L) (конечное состояние, которое зависит от выходов базовой модели по слоям и не существует автономно) оставило бы отсоединённую модель необученной.
unload_shadow
< source >( adapter_name: str | None = Nonecopy: bool = False )
Параметры
- adapter_name (
str, необязательный) — Адаптер, теневую сеть которого нужно выгрузить. По умолчанию используется активный адаптер. - copy (
bool, необязательный, по умолчаниюFalse) — ЕслиTrue, выполняется глубокое копирование возвращаемой модели, чтобы она была независима от текущей (требует больше памяти). ЕслиFalse(по умолчанию), модули разделяются — аналогичноmerge_and_unload, который переиспользует модули, а не клонирует их. Изменение одной модели влияет на другую.
Возвращает теневой backbone (+ head) как самостоятельную модель, без базовой модели.
Аналог merge_and_unload в ShadowPEFT: если тот возвращает базовую модель с «впечённой» адаптацией, то этот метод возвращает только легковесную теневую сеть для высокоэффективного / edge-инференса. Он выполняет head(projection(backbone(x))) — обновления по блокам требуют выходов базовой модели и поэтому не существуют автономно. Для языковых моделей результат ведёт себя как обычная каузальная LM (поддерживает generate() и кэширование KV). Модели Diffusers не поддерживаются, поскольку реконструкция полного автономного денойзера специфична для каждой архитектуры диффузии.
Присвойте результат переменной и используйте его; при copy=False модули остаются общими с этой моделью.
Передавайте copy=True, если вы планируете сохранить автономную модель с помощью save_pretrained. Теневой базовый блок, который разделяет замороженные входные эмбеддинги базовой модели (настройка по умолчанию "mirror"), обращается к ним через ссылку, которая не является подмодулем возвращаемой модели, поэтому чекпоинт с copy=False не содержит таблицу эмбеддингов; copy=True привязывает приватную копию и сохраняет полный чекпоинт.
ShadowCache
Связанные кэши KV для инкрементного декодирования ShadowPEFT.
Для авторегрессионной генерации требуется кэш базовой модели (ключи/значения, вычисленные при теневой инъекции) и отдельный кэш теневой модели (для продвижения токена s^(0) по одному токену). Это подкласс Cache из Transformers: операции, влияющие на структуру батча или последовательности (сброс, переупорядочивание, обрезка, повторение и выбор), применяются к обоим кэшам, тогда как метаданные внимания и обновления делегируются базовому кэшу. Неизвестные атрибуты кэша также делегируются базовому кэшу для обеспечения совместимости с реализациями кэша, специфичными для архитектуры.
Обёртка намеренно не компилируется, поскольку два кэша могут иметь различные структуры слоёв и размеры скрытых состояний. Преобразование в устаревшие кортежи не поддерживается, так как один устаревший кортеж не может представить оба пути. Теневая половина распаковывается перед базовым прямым проходом и упаковывается обратно при выходе — см. хуки ShadowModel.