Декодер-онли и причинная маска (запускаемый пример)

GPT, Claude и почти все чат-модели — декодер-онли с причинной маской. Этот урок объясняет, почему токену нельзя «подглядывать в будущее» и как это реализовано.

Причинная (каузальная) маска — ограничение внимания, при котором токен может смотреть только на себя и предыдущие токены, но не на будущие.

Почему нельзя смотреть в будущее

Модель учится предсказывать следующий токен. Представьте, что при предсказании 4-го токена ей разрешено видеть 5-й, 6-й и далее — тогда задача стала бы тривиальной и бессмысленной: «предскажи то, что уже видишь». Чтобы обучение было честным, при предсказании позиции i модель должна опираться только на токены 0..i. Этого и добивается причинная маска.

Как работает маска

Технически: перед softmax в матрице похожестей все позиции «из будущего» заменяют на минус бесконечность. После softmax их вес становится нулём — будто этих токенов и нет. Посмотрим вживую, как для каждого токена обнуляется внимание к тем, что правее.

import math

tokens = ["Я", "люблю", "писать", "код"]
n = len(tokens)

# Допустим, "сырые" похожести запрос-ключ уже посчитаны (упрощённо одинаковые).
def softmax(xs):
    m = max(v for v in xs if v != float("-inf")) if any(v != float("-inf") for v in xs) else 0.0
    e = [math.exp(x - m) if x != float("-inf") else 0.0 for x in xs]
    s = sum(e)
    return [v / s for v in e]

print("Причинная маска: токен i видит только токены 0..i")
for i in range(n):
    scores = []
    for j in range(n):
        if j <= i:
            scores.append(1.0)         # реальная похожесть (тут просто 1.0)
        else:
            scores.append(float("-inf"))  # будущее закрываем -inf
    w = softmax(scores)
    weights_str = ", ".join(f"{tokens[j]}={w[j]:.2f}" for j in range(n))
    print(f"  '{tokens[i]}' смотрит на -> {weights_str}")

Вывод:

Причинная маска: токен i видит только токены 0..i
  'Я' смотрит на -> Я=1.00, люблю=0.00, писать=0.00, код=0.00
  'люблю' смотрит на -> Я=0.50, люблю=0.50, писать=0.00, код=0.00
  'писать' смотрит на -> Я=0.33, люблю=0.33, писать=0.33, код=0.00
  'код' смотрит на -> Я=0.25, люблю=0.25, писать=0.25, код=0.25

Первый токен «Я» видит только себя. Каждый следующий — себя и всё, что слева, но ничего справа. Получается «лестница»: чем правее токен, тем больше он видит. В реальной модели веса там, конечно, не равные (мы для наглядности взяли одинаковые похожести), но нули в будущем — именно из-за маски.

Декодер-онли архитектура

Модели вроде GPT называют декодер-онли: у них нет отдельного энкодера, есть только стопка блоков с маскированным self-attention. Вся последовательность — системный промпт, вопрос, уже сгенерированный ответ — это один поток токенов, и каждый токен видит лишь левый контекст. Такая схема идеально совпадает с задачей «продолжи текст слева направо».

Зачем это при генерации

Причинная маска не только делает обучение честным — она же делает возможной авторегрессивную генерацию. Раз каждый токен зависит только от левых, мы можем дописывать ответ по одному токену: добавили токен → он стал частью левого контекста для следующего → предсказываем дальше. Будущего ещё не существует, и смотреть на него не нужно — что идеально согласуется с маской.

Связь обучения и инференса

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

Итог

  • Причинная маска запрещает токену смотреть на будущие токены — только на себя и левые.
  • Реализуется заменой «будущих» похожестей на −∞ перед softmax (их вес обнуляется).
  • Декодер-онли (GPT, Claude) — стопка блоков с маскированным self-attention, без энкодера.
  • Маска делает обучение честным и одновременно обеспечивает авторегрессивную генерацию.
Проверьте себя
1. Что запрещает причинная (каузальная) маска?
AИспользовать softmax
BТокену смотреть на будущие токены — разрешены только он сам и левые
CПрименять несколько голов внимания
DОбучать модель
2. Как технически реализуется причинная маска?
AУдалением будущих токенов из словаря
BЗаменой похожестей с будущими токенами на −∞ перед softmax, так их вес становится нулём
CСортировкой токенов
DОтключением FFN
3. Почему причинная маска удобна для генерации?
AОна ускоряет токенизацию
BРаз токен зависит только от левых, текст можно наращивать по одному токену слева направо
CОна увеличивает словарь
DОна убирает необходимость в обучении
Поддержать проект