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. Изначально он был разработан для больших языковых моделей, но стал чрезвычайно популярным методом обучения для диффузионных моделей благодаря своей эффективности и результативности.

Метод адапции низкого ранга (LoRA) — это метод PEFT, который разлагает большую матрицу на две более мелкие матрицы низкого ранга. Это значительно сокращает количество параметров, требующих дообучения.

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

Важная парадигма обработки естественного языка заключается в крупномасштабном предобучении на данных общей предметной области и адаптации к конкретным задачам или доменам. По мере того как мы предобучаем более крупные модели, полная дообучка, при которой пересчитываются все параметры модели, становится всё менее осуществимой. Возьмём, например, GPT-3 175B: развёртывание независимых экземпляров дообученных моделей, каждая из которых содержит 175 млрд параметров, является prohibitively expensive. Мы предлагаем Low-Rank Adaptation, или LoRA, которая замораживает веса предобученной модели и внедряет обучаемые матрицы разложения с низким рангом в каждый слой архитектуры Transformer, что значительно сокращает количество обучаемых параметров для downstream-задач. По сравнению с GPT-3 175B, дообученным с помощью Adam, LoRA позволяет сократить количество обучаемых параметров в 10 000 раз, а требования к памяти GPU — в 3 раза. LoRA демонстрирует качество модели, сопоставимое с полным дообучением или превосходящее его, на RoBERTa, DeBERTa, GPT-2 и GPT-3, несмотря на меньшее количество обучаемых параметров, более высокую пропускную способность обучения и, в отличие от адаптеров, отсутствие дополнительных задержек при инференсе. Мы также проводим эмпирическое исследование ранговой недостаточности при адаптации языковых моделей, что проливает свет на эффективность LoRA. Мы выпускаем пакет, облегчающий интеграцию LoRA с моделями PyTorch, и предоставляем наши реализации и контрольные точки моделей для RoBERTa, DeBERTa и GPT-2 по этой ссылке.

LoRA представляет собой обновления весов ΔW\Delta W с помощью двух матриц меньшего размера (называемых матрицами обновления) ) посредством разложения низкого ранга. Эти новые матрицы можно обучать для адаптации к новым данным, сохраняя общее количество параметров на низком уровне. Исходная матрица весов остаётся замороженной и не получает дальнейших обновлений. Для получения окончательных результатов исходные и дополнительные адаптированные веса объединяются. Также можно объединить веса адаптера с базовой моделью, чтобы устранить задержку при выводе.

Этот подход имеет ряд преимуществ:

  • LoRA делает дообучение более эффективным, значительно сокращая количество обучаемых параметров.
  • Исходные предобученные веса остаются замороженными, что позволяет создавать множество лёгких и переносимых моделей LoRA для различных задач, построенных на их основе.
  • LoRA ортогональна другим параметрически эффективным методам и может сочетаться со многими из них.
  • Производительность моделей, дообученных с использованием LoRA, сопоставима с производительностью полностью дообученных моделей.

В принципе, LoRA можно применять к любому подмножеству матриц весов в нейронной сети, чтобы сократить количество обучаемых параметров. Однако ради простоты и дальнейшей эффективности по параметрам LoRA обычно применяется только к блокам внимания в моделях Transformer — возможно, стоит также обратить внимание на другие слои. Итоговое количество обучаемых параметров в модели LoRA зависит от размера матриц обновления, который определяется в основном рангом r и формой исходной матрицы весов.

Вы можете инициализировать матрицы низкого ранга с учётом различных задач — осведомлённость о задаче (CorDA, EVA, Astra), более быстрая сходимость (PiSSA), смягчение эффектов квантования (LoftQ) — это лишь некоторые примеры. Подробнее о различных способах инициализации читайте ниже. Инициализация по умолчанию настроена так, чтобы LoRA изначально не изменяла модель (no-op), позволяя постепенно обучать новое поведение, минимально вмешиваясь в работу существующей модели.

Использование

Размер матриц низкорангового обновления определяется параметром rank или r. Чем выше ранг, тем больше параметров модели подлежит обучению, но и тем выше её способность к обучению. В следующем примере вы нацеливаетесь на матрицы query и value блоков внимания. Другие важные параметры, которые необходимо задать: lora_alpha (коэффициент масштабирования), bias (обучать ли none, all или только параметры смещения LoRA), и 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 управляется параметром init_lora_weights в LoraConfig. По умолчанию PEFT инициализирует веса LoRA методом Kaiming-uniform для веса A и нулями для веса B, что приводит к тождественному преобразованию (как в эталонной реализации).

Для слоёв transformers.Conv1D, таких как проекции в GPT-2, используйте fan_in_fan_out=True, поскольку их веса хранятся в формате (in_features, out_features), а не в раскладке nn.Linear. Поддерживаемые инициализаторы на основе весов используют раскладку Linear для декомпозиции и восстанавливают исходный формат хранения при обновлении базовых весов. Масштабирование "stable" в LoRA-GA также использует выходное измерение независимо от формата хранения.

Также можно передать init_lora_weights="gaussian". Как следует из названия, это инициализирует веса A с помощью гауссова распределения, а веса B — нулями (именно так Diffusers инициализирует веса LoRA).

from peft import LoraConfig

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

Также существует вариант установить init_lora_weights=False, который полезен для отладки и тестирования. Это должен быть единственный случай, когда вы используете этот параметр. При выборе этого варианта веса LoRA инициализируются таким образом, что они не приводят к тождественному преобразованию.

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 применяет эту нормализацию только при инициализации с помощью init_lora_weights="nora" в LoraConfig: нормализуются столбцы случайно инициализированного веса LoRA A, а вес LoRA B инициализируется нулями. Обе матрицы остаются обучаемыми, а базовые веса не изменяются.

Для линейного слоя обновление весов адаптера вычисляется как $\Delta W = sBA$, где AA имеет форму (r, in_features), BB имеет форму (out_features, r), а ss — это коэффициент масштабирования LoRA. NoRA начинает с случайного веса A, созданного линейным слоем LoRA, и нормализует каждый столбец по размерности ранга: 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 равен нулю, обновление весов адаптера до обучения также равно нулю. При bias="none" и lora_bias=False адаптер изначально сохраняет выход базовой модели. NoRA не требует калибровочного набора данных или разложения весов.

Для NoRA рекомендуемая настройка — lora_alpha, равный r (соотношение alpha/rank 1:1), например, r=8, lora_alpha=8. При значении по умолчанию use_rslora=False коэффициент масштабирования остаётся равным 1; при включении rank-stabilized LoRA он меняется на lora_alpha / sqrt(r).

В следующем примере NoRA применяется к двум линейным слоям в небольшой модели PyTorch:

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"], если такие имена присутствуют в модели.

NoRA использует существующие конфигурации, API обучения, сохранения и загрузки LoRA, поэтому отдельные классы NoraConfig или NoraModel отсутствуют. Общие API конфигурации и модели описаны в LoraConfig и LoraModel.

NoRA поддерживает целевые модули torch.nn.Linear или параметры nn.Parameter, переданные напрямую с помощью target_parameters. Поддержка эмбеддингов, свёрточных слоёв и т. д. отсутствует.

Обучение

В этом разделе показано, как обрабатывать более сложные сценарии обучения, а не просто применять низко-ранговый адаптер к модели и подавать данные.

Обучение в стиле QLoRA

Настройки LoRA по умолчанию в PEFT добавляют обучаемые веса в слои запросов и значений каждого блока внимания. Однако QLoRA, который добавляет обучаемые веса во все линейные слои трансформерной модели, способен обеспечить производительность, сопоставимую с полностью дообученной моделью. Чтобы применить LoRA ко всем линейным слоям, как в QLoRA, установите target_modules="all-linear" (это проще, чем указывать отдельные модули по имени, которое может различаться в зависимости от архитектуры).

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

Для получения дополнительной информации о том, как применить квантование к адаптерам PEFT, обратитесь к руководству по квантованию.

Эффективное по памяти дублирование слоёв с LoRA

Один из подходов к повышению производительности моделей заключается в расширении модели путём дублирования слоёв для построения более крупной модели на основе предобученной модели заданного размера. Например, увеличение модели 7B до 10B, как описано в статье SOLAR. PEFT LoRA поддерживает такой тип расширения с высокой эффективностью использования памяти, позволяя выполнять дальнейшую дообучение с помощью адаптеров LoRA, привязанных к слоям после их дублирования. Дублированные слои не требуют дополнительной памяти, поскольку они разделяют базовые веса, поэтому единственная дополнительная память, необходимая для этого, — это память для весов адаптеров. Для использования этой функции необходимо создать конфигурацию с аргументом layer_replication.

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

Допустим, исходная модель содержала 5 слоёв [0, 1, 2 ,3, 4]. В этом случае будет создана модель с 7 слоями, расположенными в порядке [0, 1, 2, 3, 2, 3, 4]. Это соответствует конвенции сквозного слияния (pass through merge) в mergekit, согласно которой последовательности слоёв, заданные кортежами «начало включительно, конец исключая», складываются для построения итоговой модели. Каждый слой в итоговой модели получает собственный уникальный набор адаптеров LoRA.

Fewshot-Metamath-OrcaVicuna-Mistral-10B — это пример модели, обученной с использованием этого метода на базе Mistral-7B, расширенной до 10B. Файл adapter_config.json содержит пример конфигурации адаптера LoRA, применяющей этот метод для дообучения.

Тонкий контроль над рангами и альфа (масштабирование)

По умолчанию все слои, для которых применяется LoRA, имеют одинаковый ранг r и одинаковое значение lora_alpha (которое определяет масштабирование LoRA), в соответствии с тем, что указано в LoraConfig. Однако в некоторых случаях может потребоваться задать разные значения для разных слоёв. Это можно сделать, передав аргументы rank_pattern и alpha_pattern в LoraConfig. Эти аргументы должны быть словарями, где ключом является имя слоя, а значением — значение ранга/альфы. Ключами могут быть регулярные выражения (regex). Все слои LoRA, которые явно не указаны в rank_pattern и alpha_pattern, будут использовать значения по умолчанию 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. Если вы сомневаетесь, не пытайтесь усложнять с помощью регулярных выражений — просто передайте полное имя каждого модуля с другим рангом/альфой, предваряя его префиксом ^, и всё будет в порядке.

Автоматическое обнаружение подходящих целевых модулей

peft.helpers.KappaTuneSelector реализует стратегию выбора целевых слоёв на основе числа обусловленности, описанную в статье KappaTune. Она просматривает каждый модуль nn.Linear и, для моделей, в которых веса экспертов MoE хранятся как объединённые 3D-тензоры nn.Parameter (например, Llama-4, Qwen3-MoE), также эти параметры, вычисляет для каждого число обусловленности матрицы κ = σ_max / σ_min и выбирает наиболее изотропные слои (с наименьшим κ). Эти изотропные слои являются идеальными кандидатами для дообучения, поскольку их высокая энтропия позволяет им легче усваивать новую информацию, оставляя специализированные анизотропные слои нетронутыми для снижения катастрофического забывания при непрерывном обучении.

Используйте peft.helpers.find_kappa_target_modules() как однострочник, чтобы получить оптимальные target_modules для LoraConfig:

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. Однако в некоторых случаях это невозможно. Например, во многих слоях mixture of expert (MoE) в HF Transformers вместо nn.Linear используется nn.Parameter. PEFT обычно переопределяет метод forward для LoRA, но для nn.Parameter такого метода нет. Поэтому, чтобы применить LoRA к этому параметру, его необходимо указать с помощью target_parameters. В качестве примера, для Llama4 можно передать: target_parameters=['feed_forward.experts.gate_up_proj', 'feed_forward.experts.down_proj].

Обратите внимание, что при работе с параметрами экспертов PEFT может добавлять значительные накладные расходы во время выполнения. Причина в том, что PEFT всегда материализует вклад LoRA для каждого эксперта, даже если требуется лишь небольшое количество экспертов. Во время обучения это менее критично, поскольку в течение последовательности, как правило, активируется значительная доля экспертов хотя бы один раз. Однако во время инференса обычно используется KV-кэш, и нам нужно вычислять только последний токен, что означает активацию лишь небольшого числа экспертов. Следовательно, использование LoRA на слоях MoE может привести к существенному замедлению во время инференса. Поэтому рекомендуется объединить веса (model.merge_adapter() или model = model.merge_and_unload()). Это устраняет накладные расходы PEFT.

Более подробное исследование этой проблемы можно найти в этом pull request по оптимизации MoE.

Ограничения

  • На данный момент этот аргумент позволяет указывать nn.Parameter с 2 или 3 измерениями. Предполагается, что в случае параметра с 3 измерениями нулевое измерение является измерением экспертов.
  • Поддерживается несколько адаптеров LoRA (добавленных через model.add_adapter или model.load_adapter), использующих target_parameters, но все они должны нацеливаться на один и тот же набор параметров.

Параметры экспертов MoE и vLLM

Некоторые MoE-модели в Transformers хранят веса экспертов как тензоры nn.Parameter (часто 3D), а не как модули nn.Linear. Чтобы применить 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 на уровне, сопоставимом с плотными слоями (см. LoRA Without Regret от Schulman и др.). Модули, не являющиеся экспертами, используют ранг по умолчанию r.

Ускоренный вывод с помощью дообученной модели возможен, например, с использованием vLLM, который поддерживает объединённые слои экспертов MoE начиная с версии v0.11.2.

Эффективное обучение токенов вместе с 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
[...]

Веса токенов сохраняются как часть состояния адаптера (adapter state dict) наряду с весами LoRA. При полной дообучении и сохранении матрицы эмбеддингов файл был бы значительно больше.

Чтобы дать представление о том, сколько VRAM можно сэкономить, было проведено примитивное сравнение приведённого выше примера: обучение матрицы эмбеддингов полностью (modules_to_save=["embed_tokens"]), использование LoRA для матрицы эмбеддингов (target_modules=[..., "embed_tokens"], ранг 32) и обучаемых токенов (trainable_token_indices=[...], 6 токенов):

Обучаемые токеныLoRAПолная дообучение
VRAM15,562 МБ15,581 МБ~16,500 МБ
Влияние6 токеноввсе токенывсе токены

Связывание весов

Многие каузальные LM используют weight tying, при котором две или более весовые матрицы разделяют одни и те же базовые параметры. В наиболее распространённом случае веса входных эмбеддингов (embed_tokens) и веса выходной проекции (lm_head) используют один и тот же тензор. Это позволяет сократить количество параметров и, как правило, сохраняет качество модели.

Не всегда очевидно, как PEFT обрабатывает эти связанные веса, когда они являются целью дообучения. Для LoRA параметр ensure_weight_tying в LoraConfig определяет, должен ли PEFT явно сохранять связь обновлений на стороне адаптера для этих слоёв. На практике это может влиять на modules_to_save, target_modules и trainable_token_indices. Обратите внимание, что эта логика частично опирается на соглашения о наименовании слоёв ("embed_tokens", "lm_head"), и корректная работа не гарантируется, если эти соглашения не соблюдаются.

Таблицы ниже обобщают ожидаемое поведение.

modules_to_save

Веса базовой модели связаныensure_weight_tyingФорма LoraConfigПоведение
Нет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"]Оберните связанные слои и сохраните связь между обёртками
Нет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_tyingФорма LoraConfigПоведение
НетFalsetarget_modules=["embed_tokens"] или ["lm_head"]Добавление LoRA только на выбранный слой
Нет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 и эмбеддинги связаны, как пользователь я могу захотеть дообучить разные токены на них. Поэтому в таблице ниже мы различаем дообучение одних и тех же токенов и дообучение разных токенов.

Веса базовой модели связаныensure_weight_tyingФорма LoraConfigПоведение
Нет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.
  • Если веса базовой модели не связаны, ensure_weight_tying=True не может принудительно установить связь и лишь выводит предупреждение.
  • Для trainable_token_indices связанные слои должны использовать одни и те же индексы токенов, если ensure_weight_tying=True.

Оптимизаторы

Обучение LoRA может опционально включать специализированные оптимизаторы. В настоящее время PEFT поддерживает LoRA-FA, LoRA+ и Riemannian-preconditioned LoRA.

Оптимизатор LoRA-FA

Обучение с использованием LoRA может быть более эффективным и производительным с применением LoRA-FA, как описано в LoRA-FA. LoRA-FA снижает потребление памяти для активаций, фиксируя матрицу A и настраивая только матрицу B. Во время обучения градиент B оптимизируется так, чтобы приближать градиент полной параметрической донастройки. Кроме того, потребление памяти в LoRA-FA не зависит от ранга (поскольку активация $A$ стирается), поэтому производительность можно повысить, увеличивая lora rank, без роста потребления памяти.

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

LoRA с римановым предусловием

Обучение LoRA можно улучшить с помощью римановского преобразователя, как описано в Riemannian Preconditioned LoRA. На каждом шаге оптимизатора градиенты матриц LoRA AA и BB умножаются на r×rr \times r преобразователь, который масштабирует евклидов градиент в направлении риманова (масштабированного градиента) на многообразии матриц низкого ранга, что улучшает обусловленность обновления и может повысить сходимость. Поскольку преобразователь имеет размер $r \times r$, его накладные расходы на память и время выполнения невелики при ранге LoRA. create_riemannian_optimizer оборачивает любой базовый класс оптимизатора (например, torch.optim.AdamW или torch.optim.SGD): параметры, не относящиеся к LoRA, обновляются базовым оптимизатором без изменений, а только nn.Linear-слои LoRA формыlora_A / lora_B) преобразуются с использованием преобразователя.

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 имеет (почти) вырожденный ранг.

Постобучение

В этом разделе представлены возможные методы постобработки для обученных адаптеров.

Слияние весов 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. Если вам нужно сохранить копию весов, чтобы позже отменить слияние адаптера или удалить его и загрузить другой, следует использовать функцию 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 происходит повторная квантизация базовых весов, а многократные циклы merge и unmerge могут накапливать ошибку округления. См. Merging adapters into quantized weights.

Если активно несколько адаптеров, а объединены только некоторые из них, необъединённые адаптеры молча не применяются. Это поведение изменится в PEFT v1.0. См. Объединение только части активных адаптеров.

Функция add_weighted_adapter() полезна для объединения нескольких LoRA в новый адаптер на основе схемы весов, заданной пользователем в параметре weights. Ниже приведён пример «от и до».

Сначала загрузите базовую модель:

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. Подробности см. в документации. Обратите внимание, что значение “svd” для combination_type не поддерживается при использовании torch.float16 или torch.bfloat16 в качестве типа данных.

Теперь выполните инференс:

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

Для смягчения проблемы будет взята модель PEFT с загруженным LoRA и создан новый изменённый адаптер, который загружается вместе с существующим адаптером и становится активным.

Пример использования:

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

Существует несколько гиперпараметров, которые можно использовать для настройки эффективности митигации, однако, как показано на рисунке 8 в статье, всегда будет существовать компромисс между точностью задачи, которую изучает адаптер, и забыванием знаний базовой модели. Митигация удаляет информацию из адаптера, чтобы снизить влияние на забывание предыдущих знаний, но это также означает, что часть информации о задаче, изученной адаптером, также теряется.

По умолчанию параметры настроены так, чтобы обеспечить хороший баланс между двумя факторами, однако не гарантируется, что эти значения будут оптимальными для вашего адаптера, модели и данных, поэтому разумно заранее подготовить бенчмарк для измерения эффекта.

Загрузка адаптеров

Адаптеры можно загрузить в предобученную модель с помощью 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")

Параллелизм тензоров

LoRA поддерживает Tensor Parallelism (TP), предоставляемый Transformers. Когда базовая модель загружается с tp_plan, PEFT автоматически определяет конфигурацию TP для каждого целевого модуля и добавляет соответствующие хуки к весам адаптера LoRA, чтобы они корректно участвовали в тензорно-параллельных вычислениях.

Поддержка Tensor Parallelism для LoRA требует transformers >= 5.4.0. API Tensor Parallelism на основе DTensor требует peft >= 0.21.1 и transformers >= 5.17.0.

Использование идентично стандартному рабочему процессу LoRA — просто загрузите базовую модель с tp_plan перед её обёртыванием с помощью PEFT:

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-плана, например, с помощью parallelize_module из PyTorch, PEFT задаёт размеры весов LoRA для шардированного nn.Linear в соответствии с его локальным шардом, если размерность device mesh веса называется "tp", а в противном случае — в соответствии с полной формой слоя (как в случае с FSDP2). PEFT не добавляет TP-хуки к этим весам LoRA и не собирает их при сохранении. Если имя размерности вашей TP-сетки отличается, добавьте его перед созданием модели PEFT:

from peft.utils.integrations import TpMeshDimRegistry

TpMeshDimRegistry.register("tp_dim")

Вывод

В этом разделе показано, что можно сделать на этапе инференса с LoRA, например, отсоединить адаптер.

Активированный LoRA (aLoRA)

Активированный LoRA (aLoRA) — это архитектура адаптеров низкого ранга для каузальных языковых моделей, которая повторно использует существующий кэш KV базовой модели для более эффективного вывода. Этот подход лучше всего подходит для конвейеров вывода, которые полагаются на базовую модель для большинства задач/генераций, но используют адаптер(ы) aLoRA для выполнения специализированных задач в цепочке. Например, для проверки или исправления сгенерированных выходов базовой модели. В таких сценариях время вывода может быть сокращено на порядок или более. Для получения дополнительной информации об aLoRA и множества примеров использования см. статью об aLoRA.

Этот метод находит последнее вхождение последовательности вызова (alora_invocation_tokens) в каждом входном сообщении (она может состоять всего из 1 токена). Веса адаптера активируются на токенах, начиная с начала последовательности вызова. Все входные данные после последовательности вызова также адаптируются, и все сгенерированные токены будут использовать адаптированные веса. Веса на предыдущих токенах остаются без адаптации, что делает кэш для этих токенов взаимозаменяемым с кэшем базовой модели благодаря маске причинного внимания в причинных языковых моделях. Использование очень похоже на стандартный LoRA. Главное отличие состоит в том, что последовательность вызова должна быть указана при создании адаптера:

from peft import LoraConfig

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

alora_invocation_tokens` — это список целочисленных идентификаторов токенов. Для заданной строки вызова это можно получить следующим образом:

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

Токенизатор — это токенизатор базовой модели. Используйте add_special_tokens=False, чтобы избежать добавления токенов SOS/EOS в строку поиска (что с высокой вероятностью приведёт к неудаче поиска).

Примечания

  • aLoRA поддерживается только для задач task_type=CAUSAL_LM в связи с акцентом на повторное использование кэша.
  • Поскольку веса адаптируются на меньшем количестве токенов, aLoRA часто (но не всегда) требует более высокий ранг (r), чем LoRA. r=32 может быть хорошей отправной точкой.
  • Веса aLoRA по определению не могут быть объединены с базовой моделью, поскольку веса адаптера применяются выборочно к подмножеству токенов. Попытки выполнить объединение приведут к ошибкам.
  • Поиск по лучам пока не поддерживается.
  • Как правило, не рекомендуется добавлять в токенизатор новые токены, отсутствующие в базовой модели. Это может усложнить целевой сценарий использования, в котором базовая модель и адаптерная модель работают с пересекающимся контекстом. Чтобы обойти эту проблему, можно добавить обучаемые токены в базовую модель до обучения адаптера.

Выбор последовательности вызовов и проектирование 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.

Если для строки вызова используются собственные строки, убедитесь, что начало и конец строки представляют собой специальные токены, чтобы избежать проблем с токенизацией на границах.

Чтобы понять, почему это важно, представьте, что ‘a’, ‘b’, ‘c’ и ‘ab’ — это токены вашего токенизатора (с номерами 1, 2, 3 и 4 соответственно). Допустим, ваш alora_invocation_tokens = [2, 3]. Теперь представьте, что входная строка — это “abc”. Поскольку “ab” является токеном, эта строка будет токенизирована как [4,3]. В результате alora_invocation_tokens не будут найдены, несмотря на то, что строка “bc” в ней присутствует. Однако, если начало и конец строки вызова — это специальные токены, такой сбой никогда не произойдет, поскольку специальные токены никогда не объединяются с другими символами в один и тот же токен.

Использование (и повторное использование) кэша для генерации

Основная цель aLoRA — сделать кэш KV взаимозаменяемым между базовой моделью и моделями с адаптерами aLoRA до начала последовательности вызовов, поскольку значения KV базовой модели и адаптированной модели несовместимы. В частности, ключи и значения, сохранённые во время одной генерации модели, могут использоваться в последующих генерациях, чтобы избежать дорогостоящих операций prefill для токенов контекста. При обмене кэшем между базовой моделью и адаптерами aLoRA существуют два основных паттерна:

  1. Базовая модель сгенерировала некоторый результат, после чего вызывается адаптер aLoRA для выполнения последующей генерации. Например, базовая модель отвечает на вопрос, а aLoRA, обученный обнаруживать галлюцинации, проверяет ответ базовой модели.
  2. Адаптер aLoRA сгенерировал некоторый результат, и базовая модель или другой адаптер aLoRA вызываются для последующей генерации, где контекст частично пересекается с контекстом исходного aLoRA. Например, пользователь предоставляет запрос, а aLoRA переписывает его, чтобы сделать более самодостаточным и улучшить извлечение в RAG-системе. Затем документы извлекаются и загружаются в контекст, aLoRA проверяет, являются ли эти документы релевантными для вопроса, и после этого базовая модель генерирует ответ.

Для демонстрации описанного выше поведения при использовании кэширования мы применяем DynamicCache из transformers. Следите за тем, чтобы адаптированные значения кэша не смешивались с базовыми значениями кэша. В частности, при частичном пересечении контекста (паттерн 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) с частичным перекрытием контекста Здесь мы предварительно заполняем общий контекст с помощью базовой модели, а затем выполняем генерацию.

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 — один для французского, другой для немецкого. Как правило, нам приходится разделять партии так, чтобы каждая из них содержала примеры только одного языка; объединять разные языки в одной партии нельзя.

К счастью, можно использовать разные адаптеры LoRA в одном батче, передавая аргумент adapter_name. Ниже мы показываем пример того, как это работает на практике. Сначала загрузим базовую модель, английский адаптер, а также адаптеры для французского и немецкого языков:

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

Теперь мы хотим сгенерировать текст на выборке, содержащей все три языка: первые три примера на английском, следующие три — на французском, а последние три — на немецком. Мы можем использовать аргумент adapter_names, чтобы указать, какой адаптер использовать для каждого примера. Поскольку базовая модель используется для английского, для этих примеров мы применяем специальную строку "__base__". Для следующих трёх примеров мы указываем имя адаптера французской LoRA-дообученной модели, в данном случае "adapter_fr". Для последних трёх примеров мы указываем имя адаптера немецкой 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.
  • Нельзя передавать adapter_names, если веса некоторых адаптеров были объединены с базовыми весами с помощью метода merge_adapter. Сначала отмените объединение всех адаптеров, вызвав model.unmerge_adapter(). Обратите внимание, что объединение только части активных адаптеров имеет аналогичное ограничение вне контекста adapter_names.
  • По очевидным причинам это нельзя использовать после вызова merge_and_unload(), поскольку в этом случае все адаптеры LoRA будут объединены с базовыми весами.
  • В настоящее время эта функция не работает с DoRA, поэтому установите use_dora=False в вашем LoraConfig, если вы хотите её использовать.
  • Возможность modules_to_save в настоящее время поддерживается только для слоёв типов Linear, Embedding, Conv2d и Conv1d.
  • При выводе с использованием adapter_names, ожидается дополнительное время выполнения, особенно если в батче много разных адаптеров. Это происходит потому, что размер батча фактически уменьшается до количества образцов на адаптер. Если производительность во время выполнения для вас в приоритете, попробуйте следующее:
    • Увеличьте размер пакета.
    • Старайтесь избегать наличия большого количества различных адаптеров в одном батче, предпочитая однородные батчи. Этого можно достичь, буферизуя примеры с одним и тем же адаптером и выполняя инференс только с небольшим набором различных адаптеров.
    • Ознакомьтесь с альтернативными реализациями, такими как LoRAX, punica или S-LoRA, которые оптимизированы для работы с большим количеством различных адаптеров.

Составление и повторное использование адаптеров LoRA

Arrow

Arrow — это модульный алгоритм маршрутизации, разработанный для объединения нескольких предварительно обученных адаптеров LoRA, специализированных на конкретных задачах, с целью решения поставленной задачи. Он похож на Polytropon, но не требует дообучения. Вместо наивного слияния всех адаптеров Arrow вводит механизм маршрутизации mixture-of-experts (MoE) на уровне токенов, не требующий градиентов. На этапе инференса для каждого адаптера LoRA сначала вычисляется прототип путём извлечения главного правого сингулярного вектора из его SVD-разложения. Затем представления каждого токена сравниваются с этими прототипами с помощью косинусного сходства для получения коэффициентов маршрутизации. Токены назначаются k наиболее релевантным адаптерам LoRA, коэффициенты нормализуются с помощью softmax, а их выходные значения линейно комбинируются. Это позволяет эффективно переиспользовать существующие модули LoRA для новых задач и обеспечивает более сильную обобщающую способность в режиме zero-shot.

В PEFT Arrow включается с помощью [`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_router" для вывода, чтобы использовать Arrow. Обратите внимание, что если вы добавите новый адаптер LoRA после create_arrow_model и хотите его дообучить, вам нужно явно установить новый адаптер как активный, поскольку "arrow_router" активируется по умолчанию в create_arrow_model.

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 расширяет Arrow, очищая специфичные для задачи адаптеры LoRA перед маршрутизацией. Ключевая идея заключается в вычитании общих знаний, закодированных в пространстве LoRA, на основе принципа забывания через отрицание, чтобы адаптеры задач стали более изолированными и сфокусированными на сигналах, релевантных задаче. Конкретно, GenKnowSub оценивает низкоразмерное «общее» подпространство на основе набора общих (не специфичных для задачи) адаптеров LoRA и удаляет эту компоненту из обновления LoRA каждого адаптера задачи перед посимвольной маршрутизацией Arrow. Как правило, это улучшает композиционность и снижает интерференцию при объединении множества адаптеров задач.

В PEFT включите GenKnowSub, установив use_gks=True в ArrowConfig и передав general_adapter_paths в create_arrow_model:

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-битным квантованием (через 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 («ранг»).
  • target_modules (Optional[Union[List[str], str]]) — Имена модулей, к которым следует применить адаптер. Если это значение указано, будут заменены только модули с указанными именами. При передаче строки выполняется сопоставление по регулярному выражению. При передаче списка строк выполняется точное совпадение или проверяется, заканчивается ли имя модуля каким-либо из переданных строк. Если это значение указано как ‘all-linear’, выбираются все линейные/Conv1D модули (если модель является PreTrainedModel, выходной слой исключается). Если это значение не указано, модули выбираются в соответствии с архитектурой модели. Если архитектура неизвестна, будет выброшена ошибка — в этом случае следует указать целевые модули вручную. Чтобы не выбирать ни один модуль (поскольку вы хотите применить target_parameters), установите target_modules=[].
  • exclude_modules (Optional[Union[List[str], str]]) — Имена модулей, к которым не следует применять адаптер. При передаче строки выполняется сопоставление по регулярному выражению. При передаче списка строк выполняется точное совпадение или проверяется, заканчивается ли имя модуля каким-либо из переданных строк.
  • lora_alpha (int) — Параметр alpha для масштабирования Lora.
  • lora_dropout (float) — Вероятность dropout для слоёв Lora.
  • fan_in_fan_out (bool) — Установите это значение в True, если заменяемый слой хранит веса в формате (fan_in, fan_out). Например, gpt-2 использует Conv1D, который хранит веса в формате (fan_in, fan_out), поэтому это значение должно быть установлено в True.
  • bias (str) — Тип смещения для LoRA. Может принимать значения ‘none’, ‘all’ или ‘lora_only’. Если указано ‘all’ или ‘lora_only’, соответствующие смещения будут обновляться во время обучения. Обратите внимание, что это означает: даже при отключении адаптеров модель не будет выдавать тот же результат, что и базовая модель без адаптации.
  • 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 не будет выполнять никаких действий. Установка инициализации в False приводит к случайной инициализации LoRA A и B, то есть LoRA не является no-op до обучения; этот параметр предназначен для отладки. Передача 'gaussian' приводит к гауссовой инициализации, масштабированной рангом LoRA для линейных и embedding-слоёв. Передача 'loftq' для использования инициализации LoftQ. Передача 'eva' приводит к инициализации на основе данных Explained Variance Adaptation. EVA инициализирует LoRA на основе SVD активаций входных данных слоя и достигает SOTA-результатов благодаря способности адаптироваться к данным дообучения. Передача 'olora' для использования инициализации OLoRA. Передача 'pissa' приводит к инициализации Principal Singular values and Singular vectors Adaptation (PiSSA), которая сходится быстрее, чем LoRA, и в конечном итоге обеспечивает более высокую производительность. Кроме того, PiSSA снижает ошибку квантования по сравнению с QLoRA, что приводит к дальнейшему улучшению. Передача 'pissa_niter_[number of iters]' запускает инициализацию PiSSA на основе Fast-SVD, где [number of iters] указывает количество итераций подпространства для выполнения FSVD и должно быть неотрицательным целым числом. При [number of iters] = 16 инициализация модели 7B может быть завершена за несколько секунд, а эффект обучения примерно эквивалентен использованию SVD. Передача 'corda' приводит к инициализации Context-Oriented Decomposition Adaptation, которая сходится даже быстрее, чем PiSSA, в режиме Instruction-Previewed Mode и лучше сохраняет мировые знания в режиме Knowledge-Preserved Mode. Передача 'astra' приводит к инициализации Activation-Space Tail-Eigenvector Low-Rank Adaptation, которая проецирует базовые веса на собственное пространство выходных активаций, оцененное на основе небольшого калибровочного набора данных. Передача "orthogonal" приводит к ортогональной инициализации LoRA A и B; в этом она напоминает "olora", но базовые веса остаются неизменными (требуется, чтобы r было чётным, пока поддерживается только для линейных слоёв). Передача "mica" приводит к инициализации Minor Component Adaptation (MiCA), которая инициализирует B из r левых сингулярных векторов базовых весов, соответствующих наименьшим сингулярным значениям, устанавливает A в ноль и замораживает B во время обучения; обновляется только A. В настоящее время поддерживается для линейных и embedding-слоёв. Передача "nora" приводит к инициализации Normalized Low-Rank Adaptation (NoRA), которая нормализует каждый столбец случайно инициализированного веса LoRA A до единичной L2-нормы и устанавливает вес LoRA B в ноль. Нормализация применяется только при инициализации; базовые веса остаются неизменными, и оба A и B остаются обучаемыми. Рекомендуемая настройка — lora_alpha, равное r, с use_rslora=False, что сохраняет соотношение 1:1 и коэффициент масштабирования 1. В настоящее время поддерживается для линейных слоёв и для nn.Parameters, указанных в target_parameters.

    Principal Singular values and Singular vectors Adaptation (PiSSA), которая сходится быстрее, чем LoRA и в итоге обеспечивает более высокую производительность. Кроме того, PiSSA снижает ошибку квантования по сравнению с QLoRA, что приводит к дальнейшим улучшениям. Передача 'pissa_niter_[number of iters]' запускает инициализацию PiSSA на основе Fast-SVD, где [number of iters] указывает количество итераций подпространства для выполнения FSVD и должно быть неотрицательным целым числом. Если [number of iters] установлено в 16, инициализация модели 7B может быть завершена за несколько секунд, а эффект обучения примерно эквивалентен использованию SVD. Передача 'corda' приводит к инициализации Context-Oriented Decomposition Adaptation, которая сходится ещё быстрее, чем PiSSA, в режиме Instruction-Previewed Mode, и лучше сохраняет мировые знания по сравнению с LoRA в режиме Knowledge-Preserved Mode. Передача 'astra' приводит к инициализации Activation-Space Tail-Eigenvector Low-Rank Adaptation, которая проецирует базовые веса на собственное пространство выходных активаций, оценённое на основе небольшого калибровочного набора данных. Передача "orthogonal" приводит к ортогональной инициализации LoRA A и B; в этом она напоминает "olora", но базовые веса остаются неизменными (требуется, чтобы r было чётным, на данный момент поддерживаются только линейные слои). Передача "mica" приводит к инициализации Minor Component Adaptation (MiCA), которая инициализирует B из r левых сингулярных векторов базовых весов, соответствующих наименьшим сингулярным значениям, устанавливает A в ноль и замораживает B во время обучения; обновляется только A. В настоящее время поддерживается для линейных и embedding-слоёв. Передача "nora" приводит к инициализации Normalized Low-Rank Adaptation (NoRA), которая нормализует каждый столбец случайно инициализированного веса LoRA A до единичной L2-нормы и устанавливает вес LoRA B в ноль. Нормализация применяется только при инициализации; базовые веса не изменяются, и оба A и B остаются обучаемыми. Рекомендуемая настройка — lora_alpha, равное r при use_rslora=False, что сохраняет соотношение 1:1 и коэффициент масштабирования 1. В настоящее время поддерживается для линейных слоёв и для nn.Parameter, указанных с помощью 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. Например, {'^model.decoder.layers.0.encoder_attn.k_proj': 16}.
  • alpha_pattern (dict) — Соответствие между именами слоев или регулярными выражениями и значениями alpha, отличными от значения alpha по умолчанию, заданного параметром lora_alpha. Например, {'^model.decoder.layers.0.encoder_attn.k_proj': 16}.
  • megatron_config (Optional[dict]) — Аргументы TransformerConfig для Megatron. Используется для создания параллельного линейного слоя LoRA. Получить его можно так: core_transformer_config_from_args(get_args()), где обе эти функции берутся из Megatron. Аргументы используются для инициализации TransformerConfig в Megatron. Этот параметр необходимо указать, если вы хотите применить LoRA к слоям ColumnParallelLinear и RowParallelLinear в megatron.
  • 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 требует use_orig_params=True, чтобы избежать проблем с неравномерным requires_grad.
  • 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 (используйте тот же датасет, что и для дообучения).
  • corda_config (Optional[CordaConfig]) — Конфигурация CorDA. Если это значение не равно None, то CorDA будет использоваться для построения адаптерных слоёв. Также передайте init_lora_weights='corda'.
  • astra_config (Optional[AstraConfig]) — Конфигурация Astra. Если это значение не равно None, то Astra будет использоваться для построения адаптерных слоёв. Также передайте init_lora_weights='astra' и вызовите preprocess_astra перед get_peft_model в этом случае.
  • lora_ga_config (Optional[LoraGAConfig]) — Конфигурация LoRA-GA. Если передано, то для инициализации адаптерных слоёв будет использоваться LoRA-GA. В этом случае также установите init_lora_weights='lora_ga'.
  • use_dora (bool) — Включает ‘Weight-Decomposed Low-Rank Adaptation’ (DoRA). Этот метод разделяет обновления весов на две части: величину и направление. Направление обрабатывается обычным LoRA, а величина — отдельным обучаемым параметром. Это может повысить эффективность LoRA, особенно при низких рангах. В настоящее время DoRA поддерживает только линейные и Conv2D слои. DoRA вносит больший оверхед, чем чистый LoRA, поэтому для инференса рекомендуется объединять веса. Подробнее см. https://huggingface.co/papers/2402.09353.
  • velora_config (Optional[VeloraConfig]) — Включает VeLoRA, передав объект VeloraConfig. VeLoRA заменяет стандартный обратный проход для проекции LoRA A на собственный, который сохраняет сжатые активации вместо полных входных активаций.
  • kasa_config (Optional[KasaConfig]) — Включает KaSA (Knowledge-aware Singular-value Adaptation), передав объект KasaConfig. KaSA отбрасывает r наименьших сингулярных компонента замороженных весов базовой модели с помощью однократного SVD и вставляет обучаемую диагональ сингулярных значений между факторами LoRA A и B. В настоящее время поддерживаются только линейные слои.
  • alora_invocation_tokens (List[int]) — Если не None, включает ‘Activated LoRA’ (aLoRA), где alora_invocation_tokens представляет собой токенизированную строку вызова для адаптера (должна присутствовать во всех входных строках модели). Этот метод выборочно активирует веса адаптера только для токенов во время и после alora_invocation_tokens. При использовании в CausalLM это означает, что KV-кэш до момента вызова совместим с KV-кэшем базовой модели (и других адаптеров aLoRA, работающих аналогичным образом). В результате, в конвейерах вывода, предполагающих переключение между выводом базовой модели и выводом с адаптером (например, агентные конвейеры, см. примеры в статье), достигается значительная экономия (по сравнению с LoRA) за счет сохранения операций prefill. Общее ускорение вывода с адаптером может составлять порядок величины и более на vLLM, в зависимости от длины общего контекста. Обратите внимание, что слияние невозможно из-за выборочного применения весов.
  • 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). Если передана, Monteclora будет использоваться для добавления вариационного Монте-Карло-сэмплирования поверх адаптеров LoRA. См. MontecloraConfig для подробностей о конкретных гиперпараметрах.
  • layer_replication (List[Tuple[int, int]]) — Создаёт новый стек слоёв, складывая слои исходной модели в соответствии с указанными диапазонами. Это позволяет расширять (или сжимать) модель без дублирования весов базовой модели. К каждому новому слою будут подключены отдельные адаптеры LoRA.
  • runtime_config (LoraRuntimeConfig) — Конфигурации времени выполнения (не сохраняются и не восстанавливаются).
  • lora_bias (bool) — По умолчанию False. Определяет, следует ли включать смещение (bias) для параметра B в LoRA. Как правило, эту опцию следует отключать. Основное применение — случай, когда веса LoRA были извлечены из полностью дообученных параметров, чтобы учесть смещение этих параметров.
  • target_parameters (List[str], необязательный) — Список имён параметров, которые следует заменить на LoRA. Этот аргумент работает аналогично target_modules, за исключением того, что здесь нужно передавать имя параметра. Как правило, для указания модуля (например, nn.Linear) следует использовать target_modules. Однако в некоторых случаях это невозможно. Например, во многих слоях mixture of expert (MoE) в HF Transformers вместо nn.Linear используется nn.Parameter. PEFT обычно переопределяет метод forward для LoRA, но для 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]) — Включает BD-LoRA (Block-Diagonal LoRA) путем предоставления BdLoraConfig. Эта техника использует блочно-диагональные матрицы для факторов 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) — Создаёт пустые веса адаптера на meta-устройстве. Полезно для ускорения процесса загрузки.

Возвращает

torch.nn.Module

Модель Lora.

Создаёт модель Low Rank Adapter (LoRA) на основе предобученной модели transformers.

Метод подробно описан в 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)

Атрибуты:

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

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. Если значение не указано, будет использован максимальный ранг объединяемых адаптеров.
  • svd_clamp (float, необязательный) — Квантильная граница для ограничения (clamping) результата разложения SVD. Если указано значение None, ограничение не выполняется. По умолчанию: None.
  • svd_full_matrices (bool, необязательный) — Определяет, вычислять полный или сокращённый 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 )

Эта функция может вычислять обновления PiSSA/CorDA/OLoRA, сравнивая параметры адаптера PiSSA/CorDA/OLoRA в output_state_dict с начальными значениями PiSSA/CorDA/OLoRA в adapter_name, тем самым преобразуя 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 (Вывод dW для LoRA
  • ΔW=AB−A0B0\Delta W = AB - A_0 B_0

Утилиты

ArrowConfig

класс peft.ArrowConfig

< >

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

Это класс подконфигурации для хранения конфигурации алгоритмов Arrow и GenKnowSub. Arrow — это алгоритм маршрутизации для объединения обученных модулей LoRA с целью решения новых задач, предложенный в работе https://huggingface.co/papers/2405.11157. GenKnowSub — это метод уточнения обученных модулей перед их объединением с помощью Arrow, описанный в работе https://aclanthology.org/2025.acl-short.54/

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) — Модель, веса которой необходимо заменить. Должна быть квантованной PEFT-моделью с слоями LoRA.
  • model_path (Optional[str]) — Путь к файлу safetensors модели. Если модель является моделью Hugging Face, этот путь будет определён из конфигурации модели. В противном случае его необходимо указать.
  • adapter_name (str) — Имя адаптера, веса которого будут заменены. Имя адаптера по умолчанию — “default”.
  • callback (Optional[Callable[[PeftModel, str], bool]]) — Функция обратного вызова, которая будет вызвана после замены каждого модуля. Функция обратного вызова должна принимать в качестве входных данных модель и имя текущего модуля и возвращать логическое значение, указывающее, следует ли сохранить замену. Если функция обратного вызова возвращает False, замена будет отменена. Это может быть очень полезно для подтверждения того, что инициализация LoftQ действительно снижает ошибку квантования модели. В качестве примера эта функция обратного вызова может генерировать логиты для заданного входа и сравнивать их с логитами исходной неквантованной модели при том же входе, возвращая True только в случае улучшения. Поскольку это жадная оптимизация, возможно, что многократный вызов этой функции приведет к постепенному улучшению.

Замените веса LoRA модели, квантованной с помощью bitsandbytes, используя технику LoftQ.

Замена выполняется на лету путём загрузки неоквантованных весов из локально хранящегося файла модели safetensors и инициализации весов LoRA таким образом, чтобы минимизировать ошибку квантования между исходными и оквантованными весами.

Так как ленивая загрузка невозможна с pickle, обычные файлы контрольных точек PyTorch не поддерживаются.

В зависимости от размера модели вызов этой функции может занять некоторое время.

LoftQConfig

класс 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) — Значение Rho для перераспределения в EVA (>= 1.0). Максимальный ранг для слоя равен lora_r * rho. По умолчанию 2.0, что означает, что максимальный допустимый ранг для слоя составляет 2r. Увеличение rho позволяет более сильно перераспределять ранги между слоями. Некоторые предобученные модели могут быть более чувствительны к перераспределению рангов. Поэтому, если производительность ниже ожидаемой, может быть полезно попробовать rho=1.0 (без перераспределения).
  • tau (float) — Порог косинусного сходства для раннего завершения. Сравнивает косинусное сходство правых сингулярных векторов между двумя последовательными шагами SVD. Если косинусное сходство превышает этот порог, итерация SVD останавливается. По умолчанию 0.99.
  • use_label_mask (bool) — Использовать маску меток для инициализации EVA. Это означает, что позиции, где labels=label_mask_value, игнорируются при вычислении SVD. В большинстве случаев предпочтительнее устанавливать use_label_mask=True, что особенно полезно для многоходовых диалогов. Значение по умолчанию — True. Отфильтровывание элементов на основе маски меток иногда приводит к малому размеру батча и, как следствие, к нестабильности вычислений 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) — модель peft, для которой вычисляется SVD.
  • dataloader (Optional[Iterable]) — Загрузчик данных, используемый для прямого прохода. Если значение равно None, необходимо указать eva_state_dict.
  • eva_state_dict (Optional[dict]) — Словарь состояния (state_dict) для загрузки в модель. Если значение равно None, необходимо предоставить загрузчик данных (dataloader), и словарь состояния будет вычислен с помощью get_eva_state_dict.
  • forward_fn (Callable) — Функция прямого прохода, используемая для прямого прохода. Принимает два аргумента: model и inputs. Поведение по умолчанию — return model(**inputs)
  • prepare_model_inputs_fn (Optional[Callable]) — Эта функция принимает входные данные модели и peft_config и передаёт результат в prepare_layer_inputs_fn. Может использоваться для изменения входных данных для вычисления SVD на основе исходных входных данных модели. Например, в задачах языкового моделирования маска внимания используется для определения индексов, соответствующих токенам padding, которые не должны использоваться при вычислении 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, и должна возвращать 2D-тензор. Логика по умолчанию описана в 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) — Следует ли собирать входные данные слоя со всех рангов. По умолчанию True, что означает, что в распределённой среде входные данные слоя собираются со всех рангов для вычисления SVD. В нераспределённых средах этот аргумент игнорируется. Установите False, если вы используете нераспределённый dataloader в распределённой среде.
  • show_progress_bar (bool) — Показывать ли индикатор прогресса. По умолчанию True.

Возвращает

model (torch.nn.Module)

Модель с инициализированными весами LoRA.

Инициализируйте веса слоёв LoRA с использованием метода EVA.

Эта функция инициализирует веса слоёв LoRA с использованием метода EVA. Она вычисляет 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) — загрузчик данных, используемый для прямого прохода.
  • peft_config (Optional[LoraConfig]) — Конфигурация для слоёв LoRA. Требуется только в том случае, если model не является PeftModel.
  • forward_fn (Callable) — Функция прямого прохода, используемая для прямого прохода. Принимает два аргумента: model и inputs. Поведение по умолчанию — return model(**inputs)
  • prepare_model_inputs_fn (Optional[Callable]) — Эта функция принимает входные данные модели и peft_config и передаёт результат в prepare_layer_inputs_fn. Может использоваться для изменения входных данных для вычисления SVD на основе исходных входных данных модели. Например, в задачах языкового моделирования маска внимания используется для определения индексов, соответствующих токенам padding, которые не должны использоваться при вычислении 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, и должна возвращать 2D-тензор. Логика по умолчанию описана в 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) — Следует ли собирать входные данные слоя со всех рангов. По умолчанию True, что означает, что в распределённой среде входные данные слоя собираются со всех рангов для вычисления SVD. В нераспределённых средах этот аргумент игнорируется. Установите False, если вы используете нераспределённый dataloader в распределённой среде.
  • show_progress_bar (bool) — Показывать ли индикатор прогресса. По умолчанию True.

Возвращает

eva_state_dict (dict)

Словарь состояний, содержащий компоненты SVD для каждого слоя.

Вычислите SVD для каждого слоя в модели.

Эта функция вычисляет разложение по сингулярным значениям (SVD) для каждого слоя модели. Для вычисления компонент SVD используется метод инкрементального PCA. Функция также проверяет сходимость вычисленных компонент с помощью косинусного сходства. Распределение ранга для каждого слоя определяется на основе отношения объяснённой дисперсии.

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"]) — Стратегия распределения компонент градиентного SVD по матрицам lora_A и lora_B.
    • «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) — Параметр гамма для метода стабильного масштабирования. По умолчанию: 16

Это класс подконфигурации для хранения конфигурации инициализации LoRA-GA.

LoRA-GA (Low-Rank Adaptation with Gradient Approximation) использует информацию о градиентах на этапе инициализации, чтобы достичь более быстрой сходимости (ускорение в 2–4 раза), выравнивая начальные веса адаптера с направлением градиентов полной дообучения.

Ссылка: https://arxiv.org/abs/2407.05000

Утилиты

peft.tuners.lora.loraga.estimate_gradients

< >

( model: Modulelora_config: LoraConfigtrain_step: Callable )

Оценка градиентов для инициализации LoRA-GA.

Эта функция позволяет вычислять градиенты ТОЛЬКО для весов целевого модуля и запускает callback 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]) — Необязательный путь к файлу кэша для сохранения/загрузки градиентов. Если указан и файл существует, градиенты будут загружены из кэша. В противном случае градиенты будут оценены и сохранены по этому пути.

Сформируйте необходимые поля LoRA-GA для модели, оценив градиенты.

Для каждого линейного слоя градиенты будут оценены путём выполнения предоставленного колбэка train_step. Затем эти градиенты привязываются к модулям и используются во время инициализации.

По завершении для каждого целевого модуля устанавливаются следующие поля: _peft_loraga_grad (torch.Tensor): Накопленный градиент для матрицы весов.

Сокращение размерности Intruder

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 — Модель PEFT с загруженным адаптером LoRA, имя которого указано в old_adapter_name. В настоящее время смешанные модели не поддерживаются.
  • top_k (по умолчанию — 10) Рассматривает топ-k измерений для обнаружения чужеродных элементов. Чем больше значение, тем больше измерений будет учтено при анализе для обнаружения чужеродных элементов (и тем выше вероятность ложных срабатываний). Работает на основе косинусного сходства между базовыми весами и весами адаптера, отсортированных примерно по влиянию измерения (определяемому с помощью сингулярного разложения), поэтому значение top_k, равное 10, означает анализ 10 наиболее «важных» измерений.
  • threshold_epsilon (по умолчанию — 0.5) Пороговое значение, при котором косинусная сходимость между базовым весом и весом адаптера считается вторжением. Согласно статье, измерения вторжения демонстрируют близкую к нулю абсолютную косинусную сходимость с предобученными сингулярными векторами. Чем ниже это значение, тем меньше потенциальных измерений вторжения идентифицируется. Чем выше значение, тем больше потенциальных ложных срабатываний рассматриваются как вторжения.
  • mitigation_lambda (по умолчанию — 0.75) Относительная доля размерности интрузера, которая вычитается из дельта-веса адаптера. Чем выше значение, тем больше размерности интрузера вычитается, но тем больше информации теряется. См. рисунок 8 в статье для анализа компромиссов.
  • logging_sink (по умолчанию — print) Функция, выводящая информацию о процессе смягчения. Установите None, если не требуется вывод.

Митигация размерности вторжения на основе https://huggingface.co/papers/2410.21228 («LoRA vs Full Fine-tuning: An Illusion of Equivalence»).

Этот метод позволяет восстановить ранее полученные знания (то есть смягчить эффект забывания) путём постобработки уже обученных адаптеров низкого ранга. Это достигается ценой точности на задаче — значение migration_lambda можно настраивать, чтобы находить баланс между этими двумя факторами.

После завершения митигации будет создан новый адаптер с именем, заданным в new_adapter_name, который также станет текущим активным адаптером. Следовательно, инференс на митигированной модели будет использовать изменённый адаптер. Чтобы переключиться обратно на исходный адаптер, можно использовать peft_model.set_adapter(<old_adapter_name>).

В настоящее время поддерживается только LoRA, так как неясно, распространяется ли этот метод на другие методы дельта-весов.

Update on GitHub