Много-запросное внимание объяснение

Изысканное и полное объяснение для всех интересующихся

Multi-Query Attention (MQA) является типом механизма внимания, который может ускорить скорость генерации токенов в декодере, при этом гарантируя производительность модели.

Он широко используется в эпоху больших языковых моделей, многие такие модели используют MQA, такие как Falcon, PaLM, StarCoder и другие.

Многоголовое Внимание(MHA)

Прежде чем ввести MQA, давайте сначала рассмотрим механизм внимания по умолчанию в трансформере.

Многоголовое внимание является механизмом внимания по умолчанию в модели трансформера, как показано на Рисунке 1:

Рисунок 1

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

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

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

Ниже приведен процесс декодирования слоя самовнимания в авторегрессивной языковой модели, основанной на декодерах трансформера:

def MHAForDecoder(x, prev_K, prev_V, P_q, P_k, P_v, P_o):    q = tf.einsum("bd, hdk−>bhk", x, P_q)    new_K = tf.concat([prev_K, tf.expand_dims(tf.einsum ("bd, hdk−>bhk", x, P_k), axis = 2)], axis = 2)    new_V = tf.concat([prev_V, tf.expand_dims(tf.einsum("bd, hdv−>bhv", x, P_v), axis = 2)], axis = 2)    logits = tf.einsum("bhk, bhmk−>bhm", q, new_K)    weights = tf.softmax(logits)    O = tf.einsum("bhm, bhmv−>bhv", weights, new_V)    Y = tf.einsum("bhv, hdv−>bd", O, P_o)    return Y, new_K, new_V

Переменные:

  • x: входной тензор на текущем шаге, который является шагом m+1, с формой [b, d]
  • P_q, P_k: тензоры проекции запроса и ключа, с формой [h, d, k]
  • P_v: тензор проекции значения, с формой [h, d, v]
  • P_o: изученные линейные проекции, с формой [h, d, v]
  • Prev_K: тензор Key с предыдущего шага, с формой [b, h, m, k]
  • Prev_V: тензор Value с предыдущего шага, с формой [b, h, m, v]
  • new_K: тензор Key с добавлением текущего шага, с формой [b, h, m+1, k]
  • new_V: тензор Value с добавлением текущего шага, с формой [b, h, m+1, v]

Размерности:

  • m: количество выполненных предыдущих шагов
  • b: размер пакета
  • d: размерность входного и выходного
  • h: количество голов
  • k: другая размерность тензоров Q, K
  • v: другая размерность тензора V

Многозапросное Внимание(MQA)

Multi-Query Attention является вариацией много-головного внимания.

Подход MQA заключается в том, чтобы сохранить первоначальное количество голов для Q, но иметь только одну голову для K и V. Это означает, что все головы Q используют один набор голов K и V, отсюда и название Multi-Query, как показано на рис. 2:

Рисунок 2

Код процесса декодирования для MQA в основном такой же, как код для MHA, за исключением того, что буква “h”, представляющая размер голов, удаляется из уравнения tf.einsum для K, V, P_k и P_v:

def MQAForDecoder(x, prev_K, prev_V, P_q, P_k, P_v, P_o):    q = tf.einsum("bd, hdk−>bhk", x, P_q)    new_K = tf.concat([prev_K, tf.expand_dims(tf.einsum ("bd, dk−>bk", x, P_k), axis = 2)], axis = 2)    new_V = tf.concat([prev_V, tf.expand_dims(tf.einsum("bd, dv−>bv", x, P_v), axis = 2)], axis = 2)    logits = tf.einsum("bhk, bmk−>bhm", q, new_K)    weights = tf.softmax(logits)    O = tf.einsum("bhm, bmv−>bhv", weights, new_V)    Y = tf.einsum("bhv, hdv−>bd", O, P_o)    return Y, new_K, new_V

Производительность

Насколько MQA действительно улучшает скорость? Давайте взглянем на график результатов, представленный в оригинальной статье:

Из таблицы выше видно, что улучшение скорости MQA на кодировщике не очень значительное, но в декодере оно весьма значительное.

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

Почему MQA может достичь ускорения вывода?

Более эффективное использование памяти

В MQA размер тензоров ключа и значения составляет b * k и b * v, в то время как в MHA размер ключа и значения составляет b * h * k и b * h * v, где h представляет количество голов.

Меньшая вычислительная сложность

При использовании кэша KV, вычислительные затраты на вычисление тензора Key и Value на каждом шаге MQA составляют 1/h от MHA, где h представляет количество голов.

Резюме

В целом MQA достигает ускорения вывода за счет следующих методов:

  • Размер кэша KV сокращается в h раз (количество голов), что означает, что тензоры, которые должны храниться в памяти GPU, также уменьшаются. Сэкономленное пространство можно использовать для увеличения размера пакета, тем самым повышая эффективность.
  • Количество данных, считываемых из памяти, уменьшается, что сокращает время ожидания вычислительных устройств и повышает использование вычислительных ресурсов.
  • MQA имеет относительно небольшой кэш KV, который может поместиться в кэш (SRAM). MHA, с другой стороны, имеет больший кэш KV, который не может быть полностью сохранен в кэше и должен считываться из памяти GPU (DRAM), что требует времени.

Заключение

Следует отметить, что MQA была предложена в 2019 году, и ее применение в то время было не столь широким. Это связано с тем, что предыдущие модели не требовали учета этих аспектов, например, LSTM требовала только поддержки одного состояния, без необходимости сохранения какого-либо кэша.

Когда впервые был предложен трансформер, он в основном использовался в задачах Seq2Seq, конкретно в моделях Encoder-Decoder. Однако модели были не очень масштабными, и не было особого практического спроса на них, поэтому MQA не привлекло много внимания.

Позже представительная модель BERT, которая также основана на структуре трансформера-кодировщика, сделала прямой пропуск.

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

Наконец, если здесь есть ошибки или упущения, пожалуйста, не стесняйтесь указывать на них.

Ссылки

Статья MQA: Быстрое декодирование трансформера: вам нужна только одна записывающая головка

Вся ваша эффективность зависит от внимания

https://paperswithcode.com/method/multi-query-attention