alterna ai
ALTERNA AI / ГЛАВА 00 · 04

Как выбирается продолжение

На предыдущей странице: когда модель действительно учится.

Не будем требовать весь ответ сразу: сначала разберём выбор одного следующего кусочка.

Модель рассчитывает ответ по числовым настройкам. Программа вокруг неё готовит вход и может вызывать поиск или инструменты.

«Кот»→Оценки продолжений→Доли вариантов→Один выбранный кусочек
Это предварительная карта, а не новый алгоритм, который нужно запомнить сейчас. В следующих главах выясним происхождение векторов, оценок и вероятностей; целую вычисляемую модель соберём позже.

Авторегрессионная факторизация

Пусть текст представлен последовательностью token IDs t₀,…,tₙ₋₁. Авторегрессионная модель оценивает распределение следующего ID:

Pθ(tn=j∣t0,…,tn−1)P_\theta(t_n=j\mid t_0,\ldots,t_{n-1})

для каждого j из vocabulary. Вероятность всей последовательности раскладывается по chain rule:

Pθ(t0,…,tN−1)=∏i=0N−1Pθ(ti∣t<i).P_\theta(t_0,\ldots,t_{N-1}) =\prod_{i=0}^{N-1}P_\theta(t_i\mid t_{<i}).

При обучении следующий token известен из корпуса, поэтому objective часто содержит negative log-likelihood

L(θ)=−∑iln⁡Pθ(ti∣t<i).L(\theta)=-\sum_i\ln P_\theta(t_i\mid t_{<i}).

При генерации target отсутствует: из полученного распределения выбирают token, добавляют его к context и повторяют forward. Учебная модель страницы выполняет только один выбор и не дописывает рассказ.

Контракт игрушки

Модель ниже фиксированная и не обученная. Vocabulary содержит 12 записей, hidden width равна 2, используется одна causal attention head и один FFN. Layer normalization отсутствует. Нет pretrained weights, API и эквивалентности с GPT. Числа выбраны для ручной проверяемости, а не для качества русского текста.

Tokenizer выполняет NFC normalization, lower-case и regex-разбиение на группы букв/чисел. Неизвестная найденная группа получает ID 0; строка только из пунктуации или emoji не создаёт token. Это не BPE. Реальный byte-BPE подробно и без подмены терминов реализован в главе 12 [S12, abstract-only].

Vocabulary в порядке IDs:

Таблица embeddings E имеет shape [12,2]. Lookup выбирает строку E[tᵢ], затем добавляется учебная position vector pᵢ=[i/10,−i/10]:

Xi=E[ti]+pi.X_i=E[t_i]+p_i.

Номер ID не является расстоянием или смыслом. Координаты E назначены вручную. Position vector меняет activation текущей позиции, но не parameter table.

Causal scaled attention

В игрушке W_Q=W_K=W_V=W_o=I₂, поэтому численно Q=K=V=X, хотя роли этих матриц различны. Для query position i и key position j:

Dij=Qi⋅Kj2,Mij={0,j≤i,−∞,j>i,D_{ij}=\frac{Q_i\cdot K_j}{\sqrt2},\qquad M_{ij}=\begin{cases}0,&j\le i,\\-\infty,&j>i,\end{cases}
Aij=exp⁡(Dij+Mij)∑rexp⁡(Dir+Mir),B=AV.A_{ij}=\frac{\exp(D_{ij}+M_{ij})} {\sum_r\exp(D_{ir}+M_{ir})},\qquad B=AV.

Softmax применяется отдельно к каждой строке. Разрешённые coefficients неотрицательны и суммируются в 1; запрещённые будущие позиции получают 0. A вычисляется из текущего input и является activation, а W_Q, W_K, W_V — parameters. Слово «attention weights» не должно скрывать эту разницу [S04; S07].

После выходной проекции выполняется residual R=X+BW_o. Затем покоординатный FFN и второй residual:

F=ReLU⁡(RW1+b1)W2+b2,H=R+F.F=\operatorname{ReLU}(RW_1+b_1)W_2+b_2,\qquad H=R+F.

Здесь W₁=I₂, W₂=.5I₂, biases нулевые. Следовательно, H=R+.5ReLU(R). Residual означает сложение совместимых activations, а не добавление обучающих данных.

Из hidden state в logits

Берём последнюю строку h=H[n−1,:] и применяем независимую output matrix U shape [2,12] и bias c shape [12]:

z=hU+c.z=hU+c.

Каждый zⱼ — logit кандидата. Logits могут быть отрицательными и не суммируются в 1. При temperature T>0:

πj=exp⁡(zj/T)∑rexp⁡(zr/T).\pi_j=\frac{\exp(z_j/T)}{\sum_r\exp(z_r/T)}.

Для numerical stability сначала вычисляют aⱼ=zⱼ/T, вычитают общий максимум a_max, а затем экспоненты exp(aⱼ−a_max). Общий сдвиг не меняет точное распределение, но уменьшает риск overflow [S10]. Temperature не меняет tokenizer, embeddings, attention или logits. При уменьшении T отношение πₐ/π_b=exp((zₐ−z_b)/T) становится более контрастным, если logits не равны.

Отдельный точный пример помогает не путать softmax с магической нормировкой:

Logit Экспонента Probability
0 1 1/6
ln 2 2 1/3
ln 3 3 1/2

CDF равна [1/6,1/2,1]. Число u=.4 попадает во второй интервал, поэтому sampling выбирает второй candidate, хотя третий имеет максимальную probability. Вероятность не является частотой с гарантированным расписанием и тем более не является вероятностью истинности высказывания.

Сквозной trace для «кошка»

Для одной позиции mask разрешает только одну связь:

Стадия Значение Shape
Token IDs [1] [1]
E[1], p₀, X [1,0], [0,0], [1,0] [1,2]
Q, K, V [1,0] [1,2] каждый
Score, mask, A [1/√2], [0], [1] [1,1]
B, R [1,0], [2,0] [1,2]
F, H [1,0], [3,0] [1,2]
z [-3,3,0,3,4,−2.4,−3,0,1.5,−1.5,1.5,−1.3] [12]

Например, logit «окне» равен 3·(−1)+0·1+.6=−2.4. Наибольший logit получает «ковре», но seeded sampling в анимационной модели может выбрать <unk>. Это не баг учебного объяснения: weights действительно не обучены языку.

Sampling строит CDF в порядке IDs и выбирает первый j, для которого u<CDFⱼ. Учебный PRNG Park–Miller использует один draw:

snext=16807s mod 2147483647,u=snext−12147483646.s_{next}=16807s\bmod 2147483647, \qquad u=\frac{s_{next}-1}{2147483646}.

Seed влияет на u, но не на z и π. Раскрытие inspector не должно расходовать дополнительные random draws.

Parameter ledger и вычислительная стоимость

В E содержится 12·2=24 scalar slots. Четыре attention projections дают 4·2·2=16. FFN: 4+2+4+2=12. Независимая U: 24, output bias: 12. Итого:

24+16+12+24+12=88.24+16+12+24+12=88.

Все 88 значений заданы вручную; обученных parameters здесь ноль. JavaScript Number использует binary64, поэтому payload чисел равен 88·8=704 B без overhead. Float32 equivalent — 352 B, но это не фактический формат браузерного массива. DOM, строки, activations и runtime в этот ledger не входят [S11].

Матрица scores имеет n² ячеек, хотя causal mask разрешает n(n+1)/2 связей. При n=12 это 144 ячейки и 78 разрешённых позиций. Из порядка O(n²d) нельзя вывести время в миллисекундах без измерений и учёта остальных операций.

Нижняя лаборатория раскрывает все 13 стадий, matrices и shapes. Измените сначала только temperature, затем только seed: в первом опыте должны сохраниться все states до logits включительно; во втором — ещё и probabilities.

[<unk>, кошка, сидит, на, ковре, окне, собака, спит, рядом, с, дом, улице]

Псевдокод → прозрачный Python

ids = tokenizer(text)
X = lookup(ids) + positions
H = causal_attention_residual_ffn(X)
z = last_row(H) @ U + bias
p = stable_softmax(z / temperature)
next_id = inverse_cdf(p, seeded_random)

Это краткий алгоритм. Ниже раскрыт его механизм: Python без NumPy, без скачивания модели и без API. Числа совпадают с браузерной лабораторией; единичные проекции упрощены явно.

Полный исполняемый пример Python
"""Transparent chapter 00 examples. Python 3; no packages or network."""
from math import exp, log, sqrt
import json
import re
import unicodedata

VOCAB = ['<unk>', 'кошка', 'сидит', 'на', 'ковре', 'окне',
         'собака', 'спит', 'рядом', 'с', 'дом', 'улице']
# Полный исполняемый пример — по ссылке ниже.

Скачать chapter00.py

Запуск: python chapter00.py. Пример проверяется вместе с браузерным математическим ядром. Произвольный код в странице не исполняется.

Код повторяет только что разобранные операции. Полный проверяемыйPython-пример главы доступен отдельным файлом; показанный короткий фрагмент не заменяет его реализацию.

Seed определяет выборку, не веса и не распределение. Разные случайные числа могут попасть в один и тот же интервал CDF.

Что находится рядом с моделью. Как этот вопрос продолжает полученный результат?

Академическая база →