PEFT documentation

Lily: низко-ранговая адаптация с межслойными связями

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

Lily: низко-ранговая адаптация с межслойными связями

Lily — это метод эффективной дообучения, вводящий обмен весами между слоями для матриц адаптеров. Вместо обучения независимой пары AB для каждого слоя, как в LoRA, Lily использует локально общие адаптеры A (каждый A общий для блока из stride_A последовательных слоёв) и глобально общие эксперты B (небольшой пул из num_B адаптеров B общий для всех слоёв). На каждом прямом проходе лёгкий роутер, зависящий от данных, вычисляет комбинацию экспертов B с весами softmax, чтобы получить эффективный B для данного слоя и входа.

Такой подход позволяет сократить общее количество матриц адаптеров с 2N (стандартный LoRA) до N / stride_A + num_B, высвобождая бюджет параметров для использования значительно большего ранга r — как правило, в 2×–4× раз больше, чем в LoRA. Более высокий ранг и лучшая связанность увеличивают эффективный ранг обновления весов ΔW = A × combined_B, что приводит к улучшению качества адаптации.

Поскольку комбинация B является зависимой от данных (веса маршрутизатора зависят от активаций на входе во время выполнения), merge и unmerge не поддерживаются. Если для вашего развертывания требуется объединение весов, рассмотрите другие методы, такие как LoRA.

В настоящее время у Lily есть следующие дополнительные ограничения:

  • Поддерживаются только слои nn.Linear.
  • Квантованные слои не поддерживаются.

Если эти ограничения не подходят для вашего сценария использования, рассмотрите другие методы.

Аннотация из статьи:

Метод адаптации низкого ранга (LoRA) — это широко используемый параметрически эффективный метод дообучения (PEFT), который обучает обновления весов ΔW = AB для предобученных весов W с помощью адаптеров низкого ранга A и B. Хотя LoRA обеспечивает эффективность использования аппаратных средств, обновления весов низкого ранга ограничивают качество адаптации. В данной статье мы предлагаем метод адаптации низкого ранга с межслойными связями (Lily) — новый метод PEFT, который вводит взаимосвязанную архитектуру с локально общими экспертами A и глобально общими экспертами B. Эта структура устраняет избыточные пары AB для каждого слоя, позволяя получать ΔW более высокого ранга при том же или меньшем количестве параметров. Для повышения выразительности мы используем маршрутизаторы, зависящие от данных, для определения связей между A и B, что предотвращает сходимость экспертов B к одинаковому поведению и улучшает способность представления в различных областях. Эксперименты на разных модальностях, архитектурах и размерах моделей демонстрируют превосходство Lily по производительности и эффективности.

Обзор бенчмарков

API

LilyConfig

класс peft.LilyConfig

< >

( 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 = 32stride_A: int = 1num_B: int = 2scaling: float = 1.0target_modules: Optional[Union[list[str], str]] = Noneexclude_modules: Optional[Union[list[str], str]] = Nonemodules_to_save: Optional[list[str]] = Nonelayers_to_transform: Optional[Union[list[int], int]] = Nonelayers_pattern: Optional[Union[list[str], str]] = Noneinit_weights: bool = True )

Параметры

  • r (int) — ранг Lily. Определяет внутреннее скрытое измерение каждого адаптера и ранг обновления весов A @ B. В Lily количество адаптеров обычно меньше, чем в LoRA, поэтому каждому адаптеру требуется большая ёмкость, и рекомендуется использовать значение r больше, чем в LoRA, — как правило, в 2x, 3x или 4x раз больше ранга LoRA, который вы обычно применяете. Общее количество обучаемых параметров масштабируется по формуле r * (total_layers / stride_A + num_B), поэтому рекомендуемый компромисс — увеличивать r, сохраняя stride_A большим, а num_B — малым.
  • stride_A (int) — Количество последовательных слоёв, которые используют один адаптер A. Например, если stride_A=4, каждые 4 соседних слоя используют один и тот же адаптер A, в результате чего общее количество уникальных адаптеров A составляет total_layers / stride_A Адаптер A сжимает входные данные до представления низкого ранга размера r. stride_A должно быть не меньше
    1. Рекомендуемые значения: 2, 3 или 4 (то есть обмен адаптерами каждые 2, 3 или 4 слоя). Сохранение stride_A большим (меньше различных адаптеров A) и увеличение r вместо этого приводит к лучшей производительности, чем обратный компромисс (малое stride_A, малое r). Установка stride_A=1 означает, что каждый слой имеет собственный адаптер A. Примечание: обмен адаптерами A происходит внутри каждой целевой группы (слои с одинаковым суффиксом target). Например, если ваши target_modules равны ['q_proj', 'v_proj'] и вы установили stride_A=2, то каждые 2 соседних слоя q_proj будут делить один адаптер A, а каждые 2 соседних слоя v_proj — другой адаптер A, но слои q_proj и v_proj не будут делить адаптеры A друг с другом, так как у них разные суффиксы.
  • num_B (int) — Количество общих адаптеров B. В отличие от адаптеров A (которые группируются по слоям), все адаптеры B общие глобально для каждого слоя. При каждом прямом проходе маршрутизатор вычисляет взвешенную комбинацию всех num_B адаптеров B (используя веса, нормированные функцией softmax), чтобы получить единый объединенный адаптер B, который затем проецирует представление низкого ранга обратно в исходное измерение. Рекомендуется устанавливать num_B в том же порядке, что и total_layers / stride_A. Рекомендуемые значения: total_layers / 2, total_layers / 3 или total_layers / 4. Аналогично stride_A, предпочитайте меньшее num_B с большим r, чем большое num_B с малым r. Примечание: для обучения маршрутизатора необходимо как минимум 2 адаптера B (т. е. num_B >= 2), поскольку маршрутизатор учится вычислять взвешенную комбинацию адаптеров B. Примечание: общее использование адаптеров B происходит внутри каждого целевого модуля (слои с одинаковым суффиксом целевого модуля). Например, если ваши target_modules равны ['q_proj', 'v_proj'] и вы установили num_B=4, то будет 4 адаптера B, общих для всех слоев q_proj, и еще 4 адаптера B, общих для всех слоев v_proj, но слои q_proj и v_proj не будут делить адаптеры B между собой, так как у них разные суффиксы.
  • target_modules (Union[List[str], str], необязательный) — Имена модулей, к которым следует применить Lily. Может быть списком строк с именами модулей (например, ['q_proj', 'v_proj']) или регулярным выражением (например, '.*decoder.*(SelfAttention|EncDecAttention).*(q|v)$'). Если не указано, Lily будет применена ко всем поддерживаемым линейным слоям.
  • scaling (float) — Скалярный множитель, применяемый к объединённому выводу адаптера (scaling * A @ combined_B) перед добавлением к результату прямого прохода замороженных весов. В отличие от LoRA, Lily не использует формулировку alpha / r; вместо этого scaling является прямым множителем. Такой подход упрощает перебор значений по логарифмической шкале (например, 0.01, 0.1, 1.0, 10.0). Оптимальное значение зависит от задачи и должно рассматриваться как гиперпараметр. Рекомендуем начинать со значения 1.0.
  • modules_to_save (List[str], необязательный) — Список модулей, помимо слоёв Lily, которые должны быть установлены как обучаемые и сохранены в итоговой контрольной точке. Например, в задачах классификации последовательностей или классификации токенов последний слой classifier/score инициализируется случайным образом, поэтому его необходимо сделать обучаемым и сохранить.
  • exclude_modules (Union[List[str], str], необязательный) — Имена модулей, к которым не следует применять адаптер. При передаче строки выполняется сопоставление по регулярному выражению. При передаче списка строк выполняется точное совпадение или проверяется, заканчивается ли имя модуля каким-либо из переданных строк.
  • layers_to_transform (Union[list[int], int], необязательный) — Индексы слоев для преобразования. Если этот аргумент указан, PEFT преобразует только те индексы слоев, которые перечислены в этом списке. Если передано одно целое число, PEFT преобразует только слой с этим индексом.
  • layers_pattern (Optional[Union[List[str], str]], необязательный) — Имя шаблона слоя, используется только в том случае, если layers_to_transform отличается от None и если шаблон слоя не входит в число общих шаблонов слоёв. Этот параметр должен указывать на nn.ModuleList модели, который часто называется 'layers' или 'h'.
  • init_weights (bool) — Следует ли инициализировать веса адаптера Lily с использованием схемы инициализации по умолчанию: матрицы A инициализируются методом Kaiming uniform, а матрицы B — нулями, что гарантирует нулевой выход адаптера в начале обучения и отсутствие влияния на предобученную модель. Настоятельно рекомендуется оставить это значение равным True, если у вас нет конкретной причины для его изменения.

Это класс конфигурации для хранения конфигурации модели LilyModel.

LilyModel

класс peft.LilyModel

< >

( 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 (LilyConfig) — конфигурация модели Lily.
  • adapter_name (str) — имя адаптера, по умолчанию "default".

Возвращает

torch.nn.Module

Модель Lily PEFT.

Создаёт модель Low-Rank Interconnected Adaptation Across Layers (Lily) на основе предобученной модели transformers.

Метод подробно описан в https://arxiv.org/abs/2407.09946.

Атрибуты:

  • model (PreTrainedModel) — Модель, подлежащая адаптации.
  • peft_config (LilyConfig): конфигурация модели Lily.
Update on GitHub