Декодер-онли и причинная маска (запускаемый пример)
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, без энкодера.
- Маска делает обучение честным и одновременно обеспечивает авторегрессивную генерацию.