Модели-трансформеры могут иметь множество параметров, что может сделать их точную настройку дорогостоящей и трудоемкой задачей, что иногда даже невозможно на потребительском оборудовании из-за нехватки памяти.
Адаптация низкого ранга (LoRA) весов линейной проекции может помочь решить эти проблемы за счет уменьшения количества параметров в модели, которые необходимо обновить в процессе тонкой настройки.
Чтобы лучше понять, как это работает, давайте сначала наметим шаги, связанные с традиционной тонкой настройкой:
- Прямая передача: вычисление некоторого набора прогнозов путем передачи входных данных вперед через сеть.
- Потери. Сравните прогнозы с реальными данными, чтобы получить оценку потерь или ошибок.
- Обратный проход. Вычислите градиент потерь относительно весов и смещений, работая в обратном направлении по сети.
- Обновить веса. Сдвиньте веса и смещения в направлении, противоположном градиенту, чтобы уменьшить потери.
Если мы сосредоточимся только на весах одной линейной проекции в модели, изменение весов (шаг 4) обычно формулируется следующим образом:
W’ = W + ΔW
где W — матрица весов, ΔW — матрица изменения весов, а W’ — новые веса.
Для LoRA мы собираемся сформулировать это немного по-другому, но все же эквивалентно.
Разница в том, что мы разделяем матрицу весов и матрицу изменения весов на протяжении всего процесса тонкой настройки.
Итак, прямой проход (шаг 1) теперь формулируется следующим образом:
h = W₀x + ΔWx
где W₀ — матрица весов, содержащая веса в начале тонкой настройки (эти веса остаются замороженными и не изменяются), ΔW — матрица изменения весов, а x — входной вектор. Обратите внимание, что x независимо умножается как на W₀, так и на ΔW, а затем эти два произведения складываются.
Поскольку у нас есть это разделение, мы также собираемся внести обновления в матрицу изменения веса (шаг 4), а не в матрицу фиксированного веса.
Для упрощения обозначения давайте также обозначим матрицу изменения веса как Wᵩ и сформулируем ее обновление следующим образом:
Wᵩ’ = Wᵩ + ΔWᵩ
где Wᵩ — новая матрица изменения веса, а ΔWᵩ — изменение матрицы изменения веса.
Причина этой новой формулировки связана с тем, что метод LoRA работает в предположении, что матрица изменения веса имеет низкий внутренний ранг.
Чтобы понять концепцию низкого внутреннего ранга, мы сначала должны немного поговорить о ранге.
ранг матрицы – это максимальное количество линейно независимых строк или столбцов в этой матрице.
Ранг важен, потому что он определяет наименьшее размерное пространство, в котором могут разместиться все столбцы или строки.
Матрицы с недостаточным рангом — то есть матрицы с линейными зависимостями — имеют избыточность. Вещи с избыточностью обычно можно представить более компактно.
Таким образом, низкий внутренний ранг относится к идее о том, что информация, содержащаяся в матрице, может быть представлена с использованием меньшего количества измерений, чем может быть предложено исходной матрицей.
И в этом заключается цель LoRA — взять матрицу изменения веса и аппроксимировать ее произведением двух матриц более низкого ранга.
Он сформулирован следующим образом:
h = W₀x + Wᵩx = W₀x + BAx
где матрица изменения веса Wᵩ теперь представлена произведением двух матриц более низкого ранга B и A.
Обратите внимание на приведенную ниже диаграмму, что размерность r — это гиперпараметр, который мы определяем, чтобы у BA было меньше обучаемых параметров, чем у Wᵩ. Чем меньше значение r, тем более сжато представление.
Прелесть этого метода в том, что B и A параметризованы. Нам не нужно решать их с помощью алгоритмов разложения матриц или чего-то еще. Содержимое B и A изучается в процессе тонкой настройки.
Кроме того, после того, как веса B и A будут изучены, матрица BA может быть объединена обратно с замороженными весами путем простого сложения.
Теперь давайте реализуем все это с помощью кода.
Класс LinearLoRA определяет архитектуру адаптированного линейного уровня низкого ранга.
В этом классе мы утверждаем, что значение r больше 0, поскольку в противном случае соответствующая размерность в матрицах B и A не существовала бы [строка 28].
Обратите внимание, что значение r определяет максимально возможный ранг аппроксимированных матриц. Фактические значения столбцов и строк в этих матрицах изучаются моделью, поэтому в конечном итоге могут возникнуть линейные зависимости, приводящие к ранг даже ниже, чем то, что мы определяем. Но ранг не может быть выше r.
Замороженная предварительно обученная линейная проекция назначается self.pretrained, однако фактические веса конкретного линейного модуля, который мы адаптируем, будут скопированы методом вне этого класса [строка 3].
Мы только замораживаем веса [строка 32]. Смещение сохраняется как обучаемый параметр. То, заморозить предвзятость или нет, не должно иметь большого значения, но с этим можно поэкспериментировать.
Матрица A инициализируется этим универсальным Kaiming, поскольку именно так по умолчанию инициализируются обычные линейные слои в PyTorch [строка 36]. Хотя часто упускается из виду, использование правильных инициализаций веса поможет модели обучаться лучшему потоку градиента.
Матрица B инициализируется всеми нулями, потому что мы хотим, чтобы B, умноженное на A, также было равно нулю и не влияло на матрицу весов для самого первого прямого прохода процесса тонкой настройки [строка 40].
Выходные данные BA масштабируются по альфа/r, прежде чем они добавляются к замороженным предварительно обученным выходным данным [строка 52]. Альфа действует как скорость обучения, чтобы усилить или ослабить степень обновления весов.
Есть несколько дополнительных методов, которые можно найти в сценарии lora_from_scratch.py на GitHub, которые связаны с внедрением этих адаптированных линейных слоев в модель до процесса тонкой настройки и объединением весов после завершения тонкой настройки.
Они будут рассмотрены в следующей части этой серии (Скоро), когда мы перейдем к тонкой настройке нашей модели BERT.
До скорой встречи!