Перейти к содержимому
Ступень 11.0 Школьная математика — освежаем

Как читать формулы

Греческие буквы, индексы, знаки Σ и Π, векторная запись — и разбор по символам настоящих формул из статей (MSE, softmax, cross-entropy, градиентный шаг, attention) с переводом каждой в код.

УРОКstage-1/1-0-school-math/03-reading-formulas.mdx2 чУрок 3 из 5
После урока вы сможете
  • Читать вслух и понимать обозначения ML-статей — греческие буквы, индексы, y^\hat y, x(i)x^{(i)}, W⊤W^\top, ∈\in, ≈\approx, ∝\propto, arg⁡max⁡\arg\max
  • Переводить формулы со знаками Σ\Sigma и Π\Pi в циклы for и не путаться в индексах «с 1» и «с 0»
  • Разобрать по символам MSE, softmax, cross-entropy, шаг градиентного спуска и attention и написать их в коде

Формула — это сжатый код. Строчка softmax(QK⊤/dk) V\mathrm{softmax}(QK^\top / \sqrt{d_k})\,V заменяет полстраницы текста. На этом языке написана вся литература по LLM — от статьи «Attention Is All You Need» до техотчётов DeepSeek и Qwen. Пока формулы выглядят как шифр, статьи закрыты, и приходится верить пересказам.

Хорошая новость: словарь небольшой. Полтора десятка греческих букв, несколько значков (Σ\Sigma, Π\Pi, ∇\nabla, ∈\in) и правила индексов — этого хватает, чтобы читать почти все формулы этого курса. Главное правило урока: каждую формулу переводим в код. Если вы смогли написать формулу циклом for, значит, вы её поняли.

Греческие буквы в формулах — просто имена переменных, как lr или theta в коде. Но у многих есть устойчивые значения, и их полезно знать заранее:

БукваЧитаетсяЧто обычно значит в MLГде встретится
α\alphaальфакоэффициент; показатель степени в scaling laws1.0.2, 5.1
β\betaбетаβ1,β2\beta_1, \beta_2 в Adam; сила KL-штрафа в DPO и GRPO1.4.2, ступень 6
γ\gammaгаммамасштаб в нормализации; дисконт в RL4.5, ступень 6
δ,Δ\delta, \Deltaдельтамалое изменение, разность: Δx\Delta xмодуль 1.2
ε\varepsilonэпсилонкрошечное число против деления на ноль (1e-8 в Adam); порог клиппинга в PPO и GRPO1.4.2, ступень 6
η\etaэтаlearning rateмодуль 1.4
θ\thetaтетавсе параметры модели разомвезде
λ\lambdaлямбдакоэффициент регуляризации (weight decay)2.1, 1.4.2
μ\muмюсреднееэтот урок, 1.3
σ\sigmaсигмастандартное отклонение; сигмоида σ(x)\sigma(x)1.0.1, этот урок
π\piпичисло 3.14…, а в RL — политика, то есть сама модель πθ\pi_\thetaступень 6
τ\tauтаутемпература (часто пишут и TT)4.1.3
Σ\Sigmaсигма заглавнаясуммаэтот урок
Π\Piпи заглавнаяпроизведениеэтот урок
∇\nablaнаблаградиент1.2.3
∂\partial«дэ круглое»частная производная1.2.3

Латинские буквы тоже имеют привычные роли: xx — вход, yy — правильный ответ, y^\hat y — предсказание, ww и WW — веса, bb — смещение, L\mathcal{L} или LL — loss, nn или NN — число примеров, dd — размерность, tt — позиция токена или шаг обучения, TT — длина последовательности.

Нижний индекснижний индексEN subscript (index)x_i — i-й элемент. В формулах счёт обычно с 1, в Python — с 0. Индекс бывает и просто названием, как k в d_k. xix_i означает «ii-й элемент xx». Пусть x=(5,8,2,7)x = (5, 8, 2, 7). В математике счёт обычно идёт с единицы: x1=5x_1 = 5, x3=2x_3 = 2. В Python — с нуля: x[0] == 5, x[2] == 2. Это главный источник ошибок при переводе формул в код:

В формулеВ Python
x1,x2,…,xnx_1, x_2, \ldots, x_nx[0], x[1], …, x[n-1]
i=1,…,ni = 1, \ldots, nfor i in range(n)
WijW_{ij} — строка ii, столбец jjW[i][j] или в NumPy W[i, j] (с тем же сдвигом: W11W_{11} — это W[0, 0])

Индекс-название. Не каждый нижний индекс — номер. В dkd_k буква kk значит «keys»: это просто имя «размерность ключей». Точно так же β1\beta_1 — «первая бета», πθ\pi_\theta — «политика с параметрами θ\theta», ∇θ\nabla_\theta — «градиент по θ\theta». Подсказка: если по букве в индексе нет суммы и она не меняется в цикле, скорее всего, это название.

Верхние индексы бывают четырёх видов:

ЗаписьЧитаетсяЧто значит
x2x^2икс в квадратестепень (урок 1.0.2)
x(i)x^{(i)}икс с индексом i в скобкахii-й пример датасета, а не степень
W⊤W^\top или WTW^TW транспонированнаястроки и столбцы меняются местами (урок 1.1.2)
WQW^Q, θ∗\theta^*W с индексом Q, тета со звёздочкойметки: «матрица для запросов», «лучшие параметры»

Значки над буквой тоже несут смысл: y^\hat y («с крышкой») — предсказание или оценка, xˉ\bar x («с чертой») — среднее, x~\tilde x («с тильдой») — изменённая или приближённая версия, x′x' («штрих») — другое значение xx или производная.

Знак суммызнак суммы (Σ)EN summation notation (sigma notation)Σ_{i=1}^{n} x_i — «сложить x_i для i от 1 до n»; в коде — цикл for с накоплением суммы от 0. — самый частый значок в ML:

∑i=1nxi=x1+x2+…+xn\sum_{i=1}^{n} x_i = x_1 + x_2 + \ldots + x_n

Читается: «сумма по ii от 1 до nn икс-итых». Под знаком записаны переменная цикла и начало (i=1i = 1), над ним — конец (nn), справа — что складываем (тело цикла). Перевод в Python один к одному:

x = [5, 8, 2, 7]
n = len(x)
total = 0 # сумма начинается с нуля
for i in range(n): # i = 0, 1, …, n−1 (в формуле 1, …, n)
total += x[i] # тело суммы: x_i
print(total) # 22
print(sum(x)) # 22 — то же самое встроенной функцией

Справа от Σ\Sigma может стоять любое выражение. Например, ∑i=1nxi2=25+64+4+49=142\sum_{i=1}^{n} x_i^2 = 25 + 64 + 4 + 49 = 142, в коде sum(xi ** 2 for xi in x).

Среднее и дисперсия. Две формулы, которые встретятся сотни раз:

μ=1n∑i=1nxi,σ2=1n∑i=1n(xi−μ)2\mu = \frac{1}{n} \sum_{i=1}^{n} x_i, \qquad \sigma^2 = \frac{1}{n} \sum_{i=1}^{n} (x_i - \mu)^2

Среднее μ\mu — сумма, делённая на количество: 22/4=5.522 / 4 = 5.5. Дисперсия σ2\sigma^2 — средний квадрат отклонения от среднего. Отклонения равны −0.5,2.5,−3.5,1.5-0.5, 2.5, -3.5, 1.5, их квадраты — 0.25,6.25,12.25,2.250.25, 6.25, 12.25, 2.25, сумма — 21, а σ2=21/4=5.25\sigma^2 = 21 / 4 = 5.25. Стандартное отклонение — корень из дисперсии: σ=5.25≈2.29\sigma = \sqrt{5.25} \approx 2.29. Обратите внимание: формула дисперсии использует результат формулы среднего. В коде сначала считаем mu, потом дисперсию.

Сокращённые записи. Часто пределы суммы не пишут: ∑ixi\sum_i x_i значит «по всем ii». Запись ∑i∈S\sum_{i \in S} значит «по всем ii из множества SS». Двойная сумма ∑i=1m∑j=1nWij\sum_{i=1}^{m} \sum_{j=1}^{n} W_{ij} — это два вложенных цикла: сумма всех элементов матрицы.

Взвешенная сумма — это нейрон. Нейрон из урока 1.0.1 с nn входами записывают так:

z=∑i=1nwixi+bz = \sum_{i=1}^{n} w_i x_i + b
w = [0.5, -1.0, 2.0]
x = [4.0, 1.0, 0.5]
b = 0.1
z = sum(wi * xi for wi, xi in zip(w, x)) + b # 0.5·4 − 1·1 + 2·0.5 + 0.1
print(z) # 2.1

Знак произведениязнак произведения (Π)EN product notation (capital pi)Π_{i=1}^{n} x_i — «перемножить все x_i»; накопление начинается с 1. устроен так же, только числа перемножаются, и накопление начинается с единицы (с нуля всё обнулится):

∏i=1nxi=x1⋅x2⋅…⋅xn\prod_{i=1}^{n} x_i = x_1 \cdot x_2 \cdot \ldots \cdot x_n

Главная формула языкового моделирования (урок 4.1.1) — как раз произведение:

p(x1,…,xT)=∏t=1Tp(xt∣x<t)p(x_1, \ldots, x_T) = \prod_{t=1}^{T} p(x_t \mid x_{<t})

Читаем по символам: «вероятность всей последовательности от x1x_1 до xTx_T равна произведению по tt от 1 до TT вероятностей токена xtx_t при условии всех предыдущих токенов». Вертикальная черта ∣\mid читается «при условии» (урок 1.3.1). Запись x<tx_{<t} означает «все xx с номерами меньше tt», то есть префикс текста.

Из прошлого урока мы знаем, что логарифм превращает произведение в сумму: log⁡∏tpt=∑tlog⁡pt\log \prod_t p_t = \sum_t \log p_t. Поэтому в коде вы почти никогда не увидите Π\Pi — только сумму логарифмов:

import math
probs = [0.5, 0.2, 0.9] # p(x_t | x_<t) для трёх токенов
p = 1.0 # произведение начинается с 1
for q in probs:
p *= q
print(p) # 0.09000000000000001
print(math.log(p)) # -2.407945608651872
print(sum(math.log(q) for q in probs)) # -2.407945608651872 — то же через сумму

Во многих учебниках (например, в d2l) шрифт подсказывает тип объекта:

  • xx — курсив, строчная буква: скаляр (одно число);
  • x\mathbf{x} — жирная строчная: вектор (список чисел);
  • W\mathbf{W} или WW — заглавная: матрица (таблица чисел).

Многие статьи жирный шрифт не используют, и тип приходится угадывать по контексту. Здесь выручает запись с ∈\in — самая полезная строчка в любой статье, потому что она сообщает формы (shape) массивов:

ФормулаЧитаетсяВ NumPy
x∈Rd\mathbf{x} \in \mathbb{R}^{d}x принадлежит эр в степени dx.shape == (d,)
W∈Rm×nW \in \mathbb{R}^{m \times n}W — матрица m на n из вещественных чиселW.shape == (m, n)
w⊤x\mathbf{w}^\top \mathbf{x}w транспонированное на xw @ x — то же, что ∑iwixi\sum_i w_i x_i
WxW\mathbf{x}W на xW @ x
a⊙b\mathbf{a} \odot \mathbf{b}поэлементное произведениеa * b

Буквой R\mathbb{R} обозначают множество вещественных (обычных дробных) чисел. Запись w⊤x\mathbf{w}^\top \mathbf{x} — это сумма ∑iwixi\sum_i w_i x_i из раздела про нейрон, только короче. Так что нейрон пишут ещё и так: z=w⊤x+bz = \mathbf{w}^\top \mathbf{x} + b. Как устроены векторы и матрицы, разберём в модуле 1.1, а пока достаточно уметь их узнавать.

ЗначокЧитаетсяПримерВ коде
:=:=«по определению равно»ReLU(x):=max⁡(0,x)\mathrm{ReLU}(x) := \max(0, x)def relu(x): …
←\leftarrow«присвоить»θ←θ−ηg\theta \leftarrow \theta - \eta gtheta = theta - lr * g
≈\approxприблизительно равноln⁡152 000≈11.9\ln 152\,000 \approx 11.9math.isclose(a, b)
∝\proptoпропорциональноpi∝ezip_i \propto e^{z_i}равно с точностью до общего множителя
∈\inпринадлежитi∈{1,…,n}i \in \{1, \ldots, n\}i in range(n), форма массива
≪\llмного меньшеdc≪dhnhd_c \ll d_h n_h—
max⁡izi\max_i z_iмаксимумmax⁡(3,7,5)=7\max(3, 7, 5) = 7max(z)
arg⁡max⁡izi\arg\max_i z_iаргмакс: номер максимумаarg⁡max⁡(3,7,5)=2\arg\max(3, 7, 5) = 2np.argmax(z) вернёт 1
∣x∣\lvert x \rvertмодуль или размер множества∣V∣\lvert V \rvert — число токенов в словареabs(x), len(V)
∥x∥\lVert \mathbf{x} \rVertнорма (длина) вектора∥x∥2\lVert \mathbf{x} \rVert_2np.linalg.norm(x) (урок 1.1.1)
E\mathbb{E}математическое ожиданиеEx∼D[L]\mathbb{E}_{x \sim D}[\mathcal{L}]среднее по батчу (урок 1.3.1)
∼\sim«выбрано из распределения»x∼Dx \sim Dсэмплирование

Обратите внимание на arg⁡max⁡\arg\max. В математике счёт идёт с единицы, и аргмакс (3,7,5)(3, 7, 5) равен 2. np.argmax считает с нуля и вернёт 1. Это тот же сдвиг на единицу, что и с индексами. Аргмакс понадобится в «жадной» генерации: модель берёт токен с наибольшей вероятностью (урок 4.1.3).

Теперь словаря хватает, чтобы прочитать пять формул, на которых держится весь курс. Для каждой — чтение по символам, пример с числами и код.

MSE=1n∑i=1n(y^i−yi)2\mathrm{MSE} = \frac{1}{n} \sum_{i=1}^{n} \left(\hat y_i - y_i\right)^2

Читаем: «одна nn-я суммы по ii от 1 до nn квадратов разностей предсказания y^i\hat y_i и ответа yiy_i». По частям: 1n\frac{1}{n} — усреднение, Σ\Sigma — цикл по примерам, (y^i−yi)(\hat y_i - y_i) — ошибка на ii-м примере, квадрат — штраф, который растёт быстрее ошибки (урок 1.0.1). Это MSEсреднеквадратичная ошибка (MSE)EN mean squared error (MSE)Корень из MSE — RMSE (root mean squared error), измеряется в тех же единицах, что и целевая переменная., среднеквадратичная ошибка.

Числа: y=(3,5,2)y = (3, 5, 2), y^=(2.5,5,4)\hat y = (2.5, 5, 4). Ошибки равны −0.5,0,2-0.5, 0, 2, квадраты — 0.25,0,40.25, 0, 4. Сумма 4.25, делим на 3 и получаем MSE ≈1.417\approx 1.417.

y_true = [3.0, 5.0, 2.0]
y_pred = [2.5, 5.0, 4.0]
n = len(y_true)
total = 0.0
for i in range(n):
total += (y_pred[i] - y_true[i]) ** 2 # (ŷ_i − y_i)²
print(total / n) # 1.4166666666666667
softmax(z)i=ezi∑j=1Vezj\mathrm{softmax}(\mathbf{z})_i = \frac{e^{z_i}}{\sum_{j=1}^{V} e^{z_j}}

Читаем: «ii-я компонента softmax от вектора z\mathbf{z} равна ee в степени ziz_i, делённому на сумму по jj от 1 до VV экспонент zjz_j». Здесь z\mathbf{z} — логиты: «сырые» оценки, которые модель выдаёт для каждого токена словаря. Это любые числа, в том числе отрицательные. VV — размер словаря (не матрица значений!). Softmax превращает логиты в вероятности: все положительные, в сумме 1.

Главное наблюдение — в формуле два разных индекса. Индекс ii свободный: формула верна для каждого ii, поэтому результат — целый вектор. Индекс jj связанный: он живёт только внутри Σ\Sigma и «исчезает» после суммирования. В коде сумма по jj считается один раз, а потом используется для каждого ii. Эту же формулу часто пишут короче: pi∝ezip_i \propto e^{z_i} — «вероятность пропорциональна экспоненте логита».

Числа: z=(2,1,0.1)\mathbf{z} = (2, 1, 0.1). Экспоненты равны (7.389,2.718,1.105)(7.389, 2.718, 1.105), их сумма — 11.213. Делим каждую на сумму и получаем (0.659,0.242,0.099)(0.659, 0.242, 0.099).

import math
z = [2.0, 1.0, 0.1]
exps = [math.exp(zj) for zj in z] # e^{z_j} для всех j
s = sum(exps) # Σ_j e^{z_j} — считаем один раз
p = [e / s for e in exps] # для каждого i: e^{z_i} / s
print([round(pi, 3) for pi in p]) # [0.659, 0.242, 0.099]

Это «школьная» версия. На логитах вроде 1000 она переполнится. Устойчивую версию вы напишете в уроке 1.3.2.

H(p,q)=−∑i=1Vpilog⁡qiH(p, q) = -\sum_{i=1}^{V} p_i \log q_i

Читаем: «аш от pp и qq равно минус сумме по ii от 1 до VV произведений pip_i на логарифм qiq_i». Здесь pp и qq — два распределения, то есть наборы вероятностей по всем токенам словаря с суммой 1. pp — правильный ответ, qq — предсказание модели (выход softmax), log⁡\log — натуральный логарифм (урок 1.0.2). В языковой модели правильный ответ один — токен tt. Поэтому pt=1p_t = 1, а остальные pi=0p_i = 0 (такой вектор называют one-hot). В сумме выживает одно слагаемое:

H=−log⁡qtH = -\log q_t

Числа: берём q=(0.659,0.242,0.099)q = (0.659, 0.242, 0.099) из softmax выше и правильный токен t=1t = 1 (первый). Тогда H=−ln⁡0.659≈0.417H = -\ln 0.659 \approx 0.417.

import math
q = [0.659, 0.242, 0.099] # из softmax
p = [1, 0, 0] # one-hot: правильный — первый токен
full = -sum(pi * math.log(qi) for pi, qi in zip(p, q))
short = -math.log(q[0]) # индекс 0 в Python = t = 1 в формуле
print(round(full, 3), round(short, 3)) # 0.417 0.417

Это cross-entropyперекрёстная энтропияEN cross-entropy — функция потерь каждой LLM. Почему она именно такая, подробно разобрано в уроке 1.3.2.

θ←θ−η ∇θL(θ)\theta \leftarrow \theta - \eta \, \nabla_\theta \mathcal{L}(\theta)

Читаем: «тета присвоить тета минус эта, умноженное на градиент эл по тета». Здесь θ\theta — все параметры, η\eta — learning rateскорость обученияEN learning rate, а ∇θL\nabla_\theta \mathcal{L} — градиент функции потерь, то есть направление её самого быстрого роста (урок 1.2.3). Минус означает «идём в обратную сторону», вниз по склону. Стрелка ←\leftarrow — это присваивание, как = в коде.

Числа: вернёмся к «чаше» L(w)=(2w−6)2\mathcal{L}(w) = (2w - 6)^2 из урока 1.0.1. Её производная равна 8w−248w - 24 (как её найти, узнаете в модуле 1.2). Начнём с w=2w = 2 и η=0.1\eta = 0.1:

w, lr = 2.0, 0.1
for step in range(4):
grad = 8 * w - 24 # ∇L(w)
w = w - lr * grad # θ ← θ − η·∇L
print(step, round(w, 4))
0 2.8
1 2.96
2 2.992
3 2.9984

С каждым шагом ww подходит к дну чаши w=3w = 3. Именно этот цикл в разных обличьях крутится при обучении любой нейросети (градиентный спускградиентный спускEN gradient descent, модуль 1.4).

Attention(Q,K,V)=softmax ⁣(QK⊤dk)V\mathrm{Attention}(Q, K, V) = \mathrm{softmax}\!\left(\frac{QK^\top}{\sqrt{d_k}}\right) V

Читаем: «attention от QQ, KK, VV равно softmax от QQ, умноженного на KK транспонированное и делённого на корень из dkd_k, и всё это умножить на VV». Это формула (1) из статьи «Attention Is All You Need», сердце трансформера (механизм вниманиямеханизм вниманияEN attention). Разберём каждый символ. Пусть в тексте TT токенов:

СимволФормаСмысл
QQ (queries, запросы)T×dkT \times d_kчто ищет каждый токен
KK (keys, ключи)T×dkT \times d_kчто предлагает каждый токен
VV (values, значения)T×dvT \times d_vкакую информацию токен передаёт
QK⊤QK^\topT×TT \times Tвсе попарные сходства: элемент (i,j)(i, j) равен ∑cQicKjc\sum_c Q_{ic} K_{jc}
dk\sqrt{d_k}числомасштаб, чтобы числа не росли с размерностью (урок 4.4.2)
softmax(⋅)\mathrm{softmax}(\cdot)T×TT \times Tкаждую строку превращает в веса с суммой 1
(…) V(\ldots)\,VT×dvT \times d_vкаждая строка ответа — взвешенная сумма строк VV

Здесь VV — матрица значений, а не размер словаря, как в softmax выше. Снова решает контекст.

flowchart LR
Q["Q (T×d_k)"] --> S["Q·Kᵀ / √d_k<br/>(T×T)"]
K["K (T×d_k)"] --> S
S --> W["softmax по строкам<br/>веса (T×T)"]
W --> O["веса · V<br/>ответ (T×d_v)"]
V["V (T×d_v)"] --> O
Путь данных в формуле attention

Числа: возьмём один запрос q=(1,0)\mathbf{q} = (1, 0) и три токена с ключами (1,0)(1, 0), (0,1)(0, 1), (−1,0)(-1, 0) и значениями (10,0)(10, 0), (0,10)(0, 10), (0,0)(0, 0); dk=2d_k = 2. Сначала распишем формулу циклами — каждое умножение матриц превращается в Σ\Sigma:

import math
q = [1.0, 0.0] # запрос одного токена
K = [[1.0, 0.0], [0.0, 1.0], [-1.0, 0.0]] # ключи трёх токенов
V = [[10.0, 0.0], [0.0, 10.0], [0.0, 0.0]] # значения трёх токенов
d_k = len(q)
scores = []
for j in range(len(K)): # для каждого токена j
s = sum(q[c] * K[j][c] for c in range(d_k)) # Σ_c q_c · K_jc — сходство запроса с ключом j
scores.append(s / math.sqrt(d_k)) # делим на √d_k
print([round(s, 3) for s in scores]) # [0.707, 0.0, -0.707]
exps = [math.exp(s) for s in scores]
weights = [e / sum(exps) for e in exps] # softmax
print([round(w, 3) for w in weights]) # [0.576, 0.284, 0.14]
out = [sum(weights[j] * V[j][c] for j in range(len(V))) for c in range(2)] # Σ_j w_j · v_j
print([round(o, 3) for o in out]) # [5.76, 2.84]

Запрос больше всего похож на первый ключ, поэтому первый токен получил наибольший вес 0.576 и в ответе преобладает его значение. А теперь та же формула для трёх запросов сразу, на NumPy — одна строка на символ:

import numpy as np
Q = np.array([[1.0, 0.0], [0.0, 1.0], [-1.0, 0.0]]) # (T=3, d_k=2)
K = np.array([[1.0, 0.0], [0.0, 1.0], [-1.0, 0.0]]) # (3, 2)
V = np.array([[10.0, 0.0], [0.0, 10.0], [0.0, 0.0]]) # (3, 2)
d_k = Q.shape[1]
scores = Q @ K.T / np.sqrt(d_k) # QKᵀ/√d_k → (3, 3)
weights = np.exp(scores) / np.exp(scores).sum(axis=1, keepdims=True) # softmax по строкам
out = weights @ V # · V → (3, 2)
print(out.round(3))
[[5.76 2.84 ]
[2.483 5.035]
[1.4 2.84 ]]

Первая строка совпала с ответом циклов. Подробно attention разберём в модуле 4.4, а сейчас важно, что вы можете прочитать эту формулу символ за символом.

Шпаргалка: как прочитать незнакомую формулу

Заголовок раздела «Шпаргалка: как прочитать незнакомую формулу»

Пять разборов выше шли по одному и тому же плану. Он работает и для формулы из любой новой статьи:

  1. Что вычисляем? Посмотрите на левую часть: число, вектор или матрица получится в итоге.
  2. Прочитайте абзац «where…» под формулой и выпишите, что значит каждая буква.
  3. Найдите формы. Записи вида ∈Rm×n\in \mathbb{R}^{m \times n} подскажут shape каждого массива.
  4. Разберите индексы. Индекс под Σ\Sigma или Π\Pi — переменная цикла, свободный индекс — «для каждого», индекс-название — просто имя.
  5. Подставьте маленькие числа — два-три элемента, как в наших примерах, — и посчитайте руками.
  6. Напишите циклами, сверьте с ручным счётом и только потом переходите на NumPy.

Если на каком-то шаге застряли, значит, не хватает одного обозначения. Найдите его в таблицах этого урока или в разделе обозначений книги d2l (ссылка в источниках).

Переведите шесть формул в код: среднее, дисперсию, MSE, произведение Π\Pi, скалярное произведение и аргмакс. Формула каждой функции записана в её docstring.

Формулы с Σ и Π → код на Python6 тестов
# Переведите каждую формулу в код. Можно циклом for (так нагляднее),
# можно встроенными sum(...) и генераторами. NumPy здесь не нужен.
# Помните: в формулах индексы идут с 1 до n, а в Python — с 0 до n−1.


def mean(xs):
    """Среднее: μ = (1/n) · Σ_{i=1}^{n} x_i

    mean([2, 4, 6]) → 4.0
    """
    # TODO
    ...


def variance(xs):
    """Дисперсия: σ² = (1/n) · Σ_{i=1}^{n} (x_i − μ)², где μ — среднее.

    variance([2, 4, 4, 4, 5, 5, 7, 9]) → 4.0
    """
    # TODO: сначала посчитайте μ через mean(xs)
    ...


def mse(y_pred, y_true):
    """Среднеквадратичная ошибка: MSE = (1/n) · Σ_{i=1}^{n} (ŷ_i − y_i)²

    mse([2.5, 5.0, 4.0], [3.0, 5.0, 2.0]) → 1.4166…
    """
    # TODO
    ...


def product(xs):
    """Произведение: Π_{i=1}^{n} x_i = x_1 · x_2 · … · x_n

    product([1, 2, 3, 4]) → 24.  Произведение пустого списка равно 1.
    """
    # TODO: с какого числа начинать накопление — с 0 или с 1?
    ...


def dot(w, x):
    """Скалярное произведение: wᵀx = Σ_{i=1}^{n} w_i · x_i

    dot([0.5, -1.0, 2.0], [4.0, 1.0, 0.5]) → 2.0
    """
    # TODO
    ...


def argmax(xs):
    """argmax_i x_i — НОМЕР (индекс с нуля) самого большого элемента, а не сам элемент.

    argmax([0.1, 0.7, 0.2]) → 1.  При равенстве — первый из максимальных.
    """
    # TODO
    ...


print(mean([2, 4, 6]), variance([2, 4, 4, 4, 5, 5, 7, 9]))
print(mse([2.5, 5.0, 4.0], [3.0, 5.0, 2.0]))
print(product([1, 2, 3, 4]), dot([0.5, -1.0, 2.0], [4.0, 1.0, 0.5]))
print(argmax([0.1, 0.7, 0.2]))
Проверь себя6 вопросов
1 Как перевести ∑i=1nxi2\sum_{i=1}^{n} x_i^2 в Python для списка x длины n?
2 Дано x=(4,1,9,2)x = (4, 1, 9, 2), счёт с единицы. Чему равен arg⁡max⁡ixi\arg\max_i x_i?
3 В каких записях верхний индекс НЕ означает возведение в степень?Можно выбрать несколько ответов
4 Шаг θ←θ−η∇θL\theta \leftarrow \theta - \eta \nabla_\theta \mathcal{L} при θ=1.0\theta = 1.0, η=0.5\eta = 0.5 и градиенте 0.40.4. Каким станет θ\theta?
5 В attention матрица QQ имеет форму 10×6410 \times 64 (10 токенов, dk=64d_k = 64). Какой формы QK⊤QK^\top?
6 Как называется значок ∇\nabla, которым обозначают градиент?
  • Греческие буквы — просто имена переменных с устойчивыми ролями: θ\theta — параметры, η\eta — learning rate, μ\mu и σ\sigma — среднее и разброс, ε\varepsilon — крошечное число. Смысл уточняйте в абзаце «where…» под формулой.
  • Σ\Sigma — цикл со сложением, начатым с 0, Π\Pi — цикл с умножением, начатым с 1. В формулах счёт с 1, в Python — с 0.
  • Верхний индекс бывает степенью (x2x^2), номером примера (x(i)x^{(i)}), транспонированием (W⊤W^\top) или меткой (WQW^Q). Запись ∈Rm×n\in \mathbb{R}^{m \times n} сообщает форму массива.
  • Вы прочитали и перевели в код MSE, softmax, cross-entropy, шаг градиентного спуска, attention и даже формулу MLA из статьи DeepSeek.

Дальше: тригонометрия для ML — радианы, синус и косинус как координаты точки на окружности и повороты векторов. На этом стоят позиционные кодировки трансформера и RoPE.