Перейти к содержимому
Ступень 11.2 Производные и градиенты

Производная как скорость изменения

Что такое производная, как её посчитать численно и по правилам и чему равны производные экспоненты, логарифма, сигмоиды, tanh и SiLU — первый шаг к пониманию того, как обучается нейросеть.

УРОКstage-1/1-2-calculus/01-derivatives.mdx1 ч 40 минУрок 1 из 3
После урока вы сможете
  • Объяснить производную как мгновенную скорость изменения и наклон касательной, записать определение через предел
  • Считать производную численно прямой и центральной разностью и выбирать шаг hh, не попадая в ловушку округления
  • Находить производные по правилам, в том числе для exe^x, ln⁡x\ln x, сигмоиды, tanh, ReLU и SiLU
  • Сделать руками несколько шагов градиентного спуска и проверить производную через autograd в PyTorch

Обучение нейросети — это подкручивание миллиардов «ручек»-параметровпараметрEN parameter так, чтобы функция потерьфункция потерьEN loss function уменьшался. Для каждой ручки нужно знать ответ на один вопрос: если чуть-чуть повернуть её вправо, loss вырастет или упадёт — и насколько сильно? Ответ на этот вопрос и есть производная.

В уроке 1.0.1 вы находили наклон прямой: на сколько растёт yy, когда xx растёт на 1. Производная обобщает наклон на любые кривые: это наклон в одной точке. В этом уроке мы определим её через предел, научимся считать численно (и разберём ловушку, в которую попадают все), выучим правила и найдём производные функций активации, на которых работают нейросети. В конце сделаем первые шаги градиентного спуска — вручную.

Камень падает с башни. За tt секунд он пролетает s(t)=4.9 t2s(t) = 4.9\,t^2 метров. Какова его скорость ровно в момент t=2t = 2?

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

Отрезок времениПуть, мСредняя скорость, м/с
от 2 до 3 с44.1−19.6=24.544.1 - 19.6 = 24.524.5
от 2 до 2.1 с21.609−19.6=2.00921.609 - 19.6 = 2.00920.09
от 2 до 2.01 с19.79649−19.6=0.1964919.79649 - 19.6 = 0.1964919.649
«от 2 до 2 + почти 0»—19.6

Средние скорости приближаются к 19.6 м/с — это мгновенная скорость, то, что показывает спидометр. ПроизводнаяпроизводнаяEN derivativeМгновенная скорость изменения функции — f′(x) = lim (f(x+h) − f(x))/h при h → 0. Геометрически — наклон касательной. — это и есть мгновенная скорость изменения функции.

Геометрически средняя скорость — наклон секущей, прямой через две точки графика. Когда вторая точка приближается к первой, секущая поворачивается и в пределе становится касательной — прямой, которая в этой точке «прижимается» к графику. Производная — наклон касательной.

Секущие → касательнаясекущая при h = 1.2:наклон 3.2при h = 0.25: 2.25касательная:наклон 2Сигмоида и её производная00.250.51σ(x)σ′(x) = σ(1 − σ)σ′(5) ≈ 0.007
Слева: парабола y = x² и точка x = 1; чем меньше шаг h, тем ближе наклон секущей к наклону касательной, то есть к производной. Справа: производная сигмоиды не больше 0.25 и почти исчезает на «хвостах».

Запишем то же самое формулой. Сдвинемся от точки xx на маленький шаг hh и посмотрим, как изменилась функция:

f′(x)=lim⁡h→0f(x+h)−f(x)hf'(x) = \lim_{h \to 0} \frac{f(x + h) - f(x)}{h}

Значок lim⁡h→0\lim_{h \to 0} читается «пределпределEN limitЗначение, к которому сколь угодно близко подходит выражение, когда аргумент стремится к заданному числу. при hh, стремящемся к нулю». Подставить h=0h = 0 нельзя — получится 0/00/0. Но можно смотреть, к какому числу приближается дробь, когда hh становится всё меньше. Для f(x)=x2f(x) = x^2 в точке x=3x = 3:

hh10.10.010.001
(3+h)2−9h\dfrac{(3 + h)^2 - 9}{h}76.16.016.001

Дробь стремится к 6. Алгебра подтверждает: (3+h)2−9h=6h+h2h=6+h\frac{(3 + h)^2 - 9}{h} = \frac{6h + h^2}{h} = 6 + h, и при h→0h \to 0 остаётся 6. Если повторить выкладку для произвольного xx, получится (x2)′=2x(x^2)' = 2x.

Обозначения. Пишут f′(x)f'(x) («эф штрих») или dfdx\frac{df}{dx} (запись Лейбница: «маленькое изменение ff на маленькое изменение xx»). В ML вы постоянно будете видеть dLdw\frac{dL}{dw} — производную loss по весу.

Как читать производную.

  • Знак: f′(x)>0f'(x) > 0 — функция в этой точке растёт, f′(x)<0f'(x) < 0 — убывает, f′(x)=0f'(x) = 0 — график горизонтален. В минимуме и максимуме гладкой функции производная равна нулю.
  • Величина — чувствительность. dLdw=5\frac{dL}{dw} = 5 означает: если увеличить ww на 0.001, loss вырастет примерно на 0.005. Производная — это «курс обмена» маленьких изменений входа на изменения выхода.

Где производной нет. В уроке 1.0.1 мы заметили «угол» у графика ∣x∣\lvert x \rvert в нуле. Слева наклон −1, справа +1, одного наклона нет — производная в этой точке не существует. У ReLU в нуле то же самое: слева наклон 0, справа 1. На практике это не мешает: PyTorch просто считает производную ReLU в нуле равной 0, а ровно в 0 вход попадает редко.

Определение сразу даёт способ посчитать производную на компьютере: взять маленькое hh и не переходить к пределу.

  • Прямая разность: f(x+h)−f(x)h\dfrac{f(x + h) - f(x)}{h}.
  • Центральная разность: f(x+h)−f(x−h)2h\dfrac{f(x + h) - f(x - h)}{2h} — секущая через две точки по обе стороны от xx.

Центральная разностьчисленная производная (конечные разности)EN numerical derivative (finite differences)Приближение производной по значениям функции. Центральная разность (f(x+h) − f(x−h))/2h точнее прямой (f(x+h) − f(x))/h. намного точнее. У функции, которая изгибается вверх (как x2x^2 или exe^x), секущая «справа» завышает наклон, а «слева» — занижает, и в симметричной формуле эти главные ошибки взаимно уничтожаются. Ошибка прямой разности пропорциональна hh, центральной — h2h^2. Проверим на f(x)=exf(x) = e^x в точке x=1x = 1, где точный ответ e≈2.71828e \approx 2.71828:

hhОшибка прямой разностиОшибка центральной разности
0.10.140.0045
0.010.0140.000045
0.0010.00140.00000045

Уменьшили hh в 10 раз — ошибка прямой разности упала в 10 раз, центральной — в 100.

Кажется, что чем меньше hh, тем лучше. Проверим на тех же exe^x и x=1x = 1:

import math
for k in range(1, 13):
h = 10 ** -k
central = (math.exp(1 + h) - math.exp(1 - h)) / (2 * h)
print(f"h = 1e-{k:<2d} ошибка = {abs(central - math.e):.1e}")
h = 1e-1 ошибка = 4.5e-03
h = 1e-2 ошибка = 4.5e-05
h = 1e-3 ошибка = 4.5e-07
h = 1e-4 ошибка = 4.5e-09
h = 1e-5 ошибка = 5.9e-11
h = 1e-6 ошибка = 1.6e-10
h = 1e-7 ошибка = 5.9e-11
h = 1e-8 ошибка = 6.6e-09
h = 1e-9 ошибка = 6.6e-09
h = 1e-10 ошибка = 6.7e-07
h = 1e-11 ошибка = 1.0e-05
h = 1e-12 ошибка = 2.1e-04

Сначала ошибка падает, около h=10−5h = 10^{-5} достигает дна и дальше уже не уменьшается, а начиная с h≈10−8h \approx 10^{-8} быстро растёт. Причина в числах с плавающей точкой (урок 0.0.2): float64 хранит около 16 значащих цифр. Когда hh крошечное, f(x+h)f(x + h) и f(x−h)f(x - h) совпадают почти во всех цифрах, и при вычитании остаются только последние, «шумные». Деление на крошечное hh этот шум раздувает.

Почему нейросети не учат численными производными? Для каждого параметра нужно два вычисления функции. У модели на 7 млрд параметров это 14 млрд прогонов модели на один шаг обучения. Обратное распространение ошибки (следующий урок) получает все производные сразу, примерно за время трёх прогонов: обратный проход стоит около двух прямых. Численная производная остаётся незаменимой для проверки: если ваш код backward ошибается, сравнение с численной производной это покажет (урок 1.2.3).

Считать пределы каждый раз не нужно — есть таблица и правила. Их выводят из определения один раз.

ФункцияПроизводнаяПример
константа cc00(5)′=0(5)' = 0
xnx^nnxn−1n x^{n-1}(x3)′=3x2(x^3)' = 3x^2, в точке 2 это 12
exe^xexe^xпроизводная равна самой функции
ln⁡x\ln x1/x1/xв точке 10 производная 0.1
c⋅f(x)c \cdot f(x)c⋅f′(x)c \cdot f'(x)(4x2)′=8x(4x^2)' = 8x
f+gf + gf′+g′f' + g'(x2+x)′=2x+1(x^2 + x)' = 2x + 1
f⋅gf \cdot gf′g+fg′f' g + f g'(xex)′=ex+xex(x e^x)' = e^x + x e^x
f/gf / gf′g−fg′g2\dfrac{f' g - f g'}{g^2}см. сигмоиду ниже

Как читать таблицу: ff и gg — любые две функции, а f′f' и g′g' — их производные. Например, в строке произведения для xexx e^x берём f=xf = x (производная 1) и g=exg = e^x (производная exe^x): 1⋅ex+x⋅ex1 \cdot e^x + x \cdot e^x. В точке x=1x = 1 это e+e≈5.44e + e \approx 5.44, и центральная разность даёт то же число.

Пример: f(x)=3x2−4xf(x) = 3x^2 - 4x. По правилам f′(x)=6x−4f'(x) = 6x - 4: (3x2)′=3⋅2x=6x(3x^2)' = 3 \cdot 2x = 6x (множитель и степень), (4x)′=4(4x)' = 4 (степень n=1n = 1 даёт 1⋅x0=11 \cdot x^0 = 1), а производная разности — разность производных. В точке x=1x = 1 это 22. Численно: f(1.001)−f(0.999)0.002=2.000\frac{f(1.001) - f(0.999)}{0.002} = 2.000. Совпало.

Экспонента — единственная (с точностью до множителя) функция, равная своей производной. Именно поэтому число ee из урока 1.0.2 так удобно: exe^x растёт со скоростью, равной своему значению.

Логарифм и cross-entropy. В уроке 1.3.2 loss языковой модели — это −ln⁡p-\ln p, где pp — вероятность правильного токена. Производная −ln⁡p-\ln p по pp равна −1/p-1/p:

pp0.90.50.10.01
ddp(−ln⁡p)=−1/p\frac{d}{dp}(-\ln p) = -1/p−1.11−2−10−100

Чем увереннее модель ошибается (маленькое pp), тем сильнее толчок к исправлению. Почти угаданный ответ двигается слабо.

сигмоидасигмоида (логистическая функция)EN sigmoid (logistic function)σ(z) = 1 / (1 + e^(−z)) — превращает любое число в вероятность от 0 до 1. σ(x)=11+e−x\sigma(x) = \frac{1}{1 + e^{-x}} удобно переписать как ex1+ex\frac{e^x}{1 + e^x} (умножили числитель и знаменатель на exe^x, как в уроке 1.0.1). Это дробь, поэтому берём правило частного из таблицы: числитель f=exf = e^x, его производная f′=exf' = e^x; знаменатель g=1+exg = 1 + e^x, его производная g′=exg' = e^x (единица — константа, её производная 0). Подставляем в f′g−fg′g2\frac{f' g - f g'}{g^2}:

σ′(x)=ex(1+ex)−ex⋅ex(1+ex)2=ex(1+ex)2=ex1+ex⏟σ(x)⋅11+ex⏟1−σ(x)\sigma'(x) = \frac{e^x (1 + e^x) - e^x \cdot e^x}{(1 + e^x)^2} = \frac{e^x}{(1 + e^x)^2} = \underbrace{\frac{e^x}{1 + e^x}}_{\sigma(x)} \cdot \underbrace{\frac{1}{1 + e^x}}_{1 - \sigma(x)} σ′(x)=σ(x) (1−σ(x))\sigma'(x) = \sigma(x)\,(1 - \sigma(x))

Производная выражается через само значение функции — при обучении её не нужно пересчитывать, достаточно запомнить σ(x)\sigma(x) после прямого прохода. Числа:

xx02510
σ′(x)\sigma'(x)0.250.1050.00660.000045

Максимум — 0.25, а на «хвостах» производная почти исчезает. Сигмоида насыщается: если вход нейрона большой, сигнал об ошибке через неё почти не проходит. Это одна из причин, почему в глубоких сетях от сигмоиды отказались (урок 3.4.1).

гиперболический тангенсгиперболический тангенсEN hyperbolic tangent (tanh)tanh x = (eˣ − e⁻ˣ)/(eˣ + e⁻ˣ) — «сигмоида» со значениями от −1 до 1. Производная 1 − tanh² x. — родственник сигмоиды со значениями от −1 до 1:

tanh⁡x=ex−e−xex+e−x,tanh⁡′(x)=1−tanh⁡2x\tanh x = \frac{e^x - e^{-x}}{e^x + e^{-x}}, \qquad \tanh'(x) = 1 - \tanh^2 x

В нуле tanh⁡′(0)=1\tanh'(0) = 1 — вчетверо больше, чем у сигмоиды. Вывод формулы удобно сделать с помощью цепного правила — это задание в следующем уроке, а пока проверим численно: tanh⁡′(2)=1−0.9642≈0.0707\tanh'(2) = 1 - 0.964^2 \approx 0.0707, и центральная разность даёт то же число. tanh используется в рекуррентных сетях (рекуррентная нейросетьрекуррентная нейросетьEN recurrent neural network (RNN) и LSTMLSTM (долгая краткосрочная память)EN long short-term memory (LSTM)RNN с ячейкой памяти c_t и тремя гейтами (Hochreiter, Schmidhuber, 1997). Ячейка обновляется сложением c_t = f·c_{t−1} + i·g — «остаточная связь во времени». — архитектуры, которые были до трансформеров, ступень 3) и внутри приближённой формулы GELU в GPT-2.

У ReLU всё просто: производная равна 0 при x<0x < 0 и 1 при x>0x > 0. Сигнал об ошибке либо проходит без изменений, либо полностью блокируется.

Современные LLM — Llama, Qwen, DeepSeek — используют в FFN-блоке (два линейных слоя с активацией между ними, урок 1.1.2) активацию SiLU (её ещё называют Swish): SiLU(x)=x⋅σ(x)\mathrm{SiLU}(x) = x \cdot \sigma(x). В конфигах моделей это строчка "hidden_act": "silu". Производная — по правилу произведения:

SiLU′(x)=1⋅σ(x)+x⋅σ(x)(1−σ(x))=σ(x)(1+x (1−σ(x)))\mathrm{SiLU}'(x) = 1 \cdot \sigma(x) + x \cdot \sigma(x)(1 - \sigma(x)) = \sigma(x)\bigl(1 + x\,(1 - \sigma(x))\bigr)

В точке 0 это σ(0)=0.5\sigma(0) = 0.5, в точке 1 — 0.731+0.197≈0.9280.731 + 0.197 \approx 0.928. В отличие от ReLU, при отрицательных xx производная SiLU по модулю небольшая (около x=−2.4x = -2.4 она даже слегка отрицательна, примерно −0.1-0.1), но не нулевая: «выключенные» нейроны продолжают получать сигнал и могут «ожить».

АктивацияПроизводнаяВ нулеОсобенность
сигмоидаσ(1−σ)\sigma(1 - \sigma)0.25насыщается на хвостах
tanh1−tanh⁡21 - \tanh^21тоже насыщается
ReLU0 или 10 (по соглашению)при x<0x < 0 градиент ровно 0
SiLUσ(1+x(1−σ))\sigma(1 + x(1 - \sigma))0.5гладкая, используется в SwiGLU

Вернёмся к «чаше» ошибки из урока 1.0.1: L(w)=(2w−6)2L(w) = (2w - 6)^2, минимум в w=3w = 3. Раскроем скобки: L(w)=4w2−24w+36L(w) = 4w^2 - 24w + 36, и по правилам L′(w)=8w−24L'(w) = 8w - 24.

В точке w=0w = 0 производная равна −24-24: loss убывает, если увеличивать ww. Значит, шагнём против знака производной: w←w−η⋅L′(w)w \leftarrow w - \eta \cdot L'(w), где η\eta — маленький множитель, скорость обученияскорость обученияEN learning rate. Возьмём η=0.1\eta = 0.1:

ШагwwL′(w)=8w−24L'(w) = 8w - 24Новый w=w−0.1⋅L′(w)w = w - 0.1 \cdot L'(w)
00−242.4
12.4−4.82.88
22.88−0.962.976
32.976−0.1922.9952

За четыре шага мы почти на дне: расстояние до w=3w = 3 каждый раз сокращается в 5 раз. Это и есть градиентный спускградиентный спускEN gradient descent в миниатюре; во всех подробностях — в уроке 1.4.1.

Вручную производные считают, чтобы понимать. На практике их считает autograd — часть PyTorch, которая автоматически находит производные (о PyTorch — заметка в уроке 1.1.2):

import torch
x = torch.tensor(2.0, requires_grad=True) # число 2.0 в виде тензора; «следи за производными по x»
y = torch.sigmoid(x) # y = σ(x)
y.backward() # посчитать dy/dx; ответ появится в x.grad
print(y.item(), x.grad.item()) # 0.8807970285415649 0.10499362647533417
# .item() достаёт из тензора обычное число Python
s = torch.sigmoid(torch.tensor(2.0)).item()
print(s * (1 - s)) # 0.10499362305391458 — наша формула σ(1 − σ)

Числа совпадают до седьмого знака: тензоры по умолчанию хранят числа во float32, где около 7 значащих цифр, а Python — во float64, где около 16 (урок 0.0.2). Как autograd устроен внутри, вы поймёте в следующих двух уроках, а в модуле 3.2 напишете свой.

Реализуйте прямую и центральную разности, аналитические производные сигмоиды, tanh и SiLU и функцию, которая сверяет аналитику с численным ответом. Устойчивая сигмоида из урока 1.0.1 уже есть в заготовке.

Численная производная против аналитической7 тестов
import math


def sigmoid(x):
    """Устойчивая сигмоида из урока 1.0.1 (уже готова)."""
    if x >= 0:
        return 1 / (1 + math.exp(-x))
    e = math.exp(x)
    return e / (1 + e)


def forward_diff(f, x, h=1e-5):
    """Прямая разность: (f(x + h) − f(x)) / h.

    f — это функция, её можно вызывать: f(x + h).
    Пример: для f(x) = x² в точке 3 при h = 0.1 получится (3.1² − 3²) / 0.1 = 6.1.
    """
    # TODO: одна строка с return
    ...


def central_diff(f, x, h=1e-5):
    """Центральная разность: (f(x + h) − f(x − h)) / (2h). Она точнее прямой.

    Пример: для f(x) = x² в точке 3 при h = 0.1 получится (3.1² − 2.9²) / 0.2 = 6.0.
    Осторожно со скобками: «/ 2 * h» поделит на 2 и УМНОЖИТ на h — нужно «/ (2 * h)».
    """
    # TODO: одна строка с return
    ...


def d_sigmoid(x):
    """Аналитическая производная сигмоиды: σ(x) · (1 − σ(x))."""
    # TODO: ваш код
    ...


def d_tanh(x):
    """Аналитическая производная tanh: 1 − tanh²(x). Используйте math.tanh."""
    # TODO: ваш код
    ...


def d_silu(x):
    """Производная SiLU(x) = x · σ(x) — активации из SwiGLU в Qwen и DeepSeek.

    Правило произведения: (u·v)′ = u′·v + u·v′, где u = x, v = σ(x).
    """
    # TODO: ваш код
    ...


def max_error(f, df, xs, h=1e-5):
    """Наибольшее расхождение |df(x) − central_diff(f, x, h)| по всем точкам xs.

    df — аналитическая производная (например, d_sigmoid), f — сама функция (sigmoid).
    Для каждой точки x из xs посчитайте |df(x) − central_diff(f, x, h)| и верните самое большое число.
    """
    # TODO: ваш код
    ...


# Проверьте себя кнопкой «Запустить»:
print(forward_diff(lambda x: x ** 2, 3, h=0.1))   # ≈ 6.1
print(central_diff(lambda x: x ** 2, 3, h=0.1))   # ≈ 6.0
print(d_sigmoid(0), d_tanh(0), d_silu(0))         # 0.25 1.0 0.5
xs = [-3, -1, 0, 0.5, 2, 4]
print(max_error(sigmoid, d_sigmoid, xs))          # порядка 1e-11

Задание для Jupyter (по желанию). Постройте в matplotlib на одном графике ошибку прямой и центральной разности для exe^x в точке 1 как функцию hh от 10−110^{-1} до 10−1510^{-15} в логарифмическом масштабе по обеим осям (plt.loglog). Найдите на графике «дно» для каждой формулы. Потом повторите во float32 (np.float32) и объясните, почему дно сдвинулось.

Проверь себя6 вопросов
1 Найдите производную f(x)=x3f(x) = x^3 в точке x=2x = 2.
2 Посчитайте прямую разность f(x+h)−f(x)h\frac{f(x + h) - f(x)}{h} для f(x)=x2f(x) = x^2 в точке x=1x = 1 при h=0.5h = 0.5.
3 Чему равна производная сигмоиды в нуле?
4 Какие утверждения верны?Можно выбрать несколько ответов
5 Для L(w)=4w2−24w+36L(w) = 4w^2 - 24w + 36 сделайте один шаг градиентного спуска из w=1w = 1 со скоростью обучения η=0.1\eta = 0.1. Чему равен новый ww?
6 Почему большие модели не обучают с помощью численных производных?
  • Производная — мгновенная скорость изменения и наклон касательной: f′(x)=lim⁡h→0f(x+h)−f(x)hf'(x) = \lim_{h \to 0} \frac{f(x+h) - f(x)}{h}. Знак говорит, растёт ли функция, величина — насколько она чувствительна.
  • Численно считаем центральной разностью с h≈10−5h \approx 10^{-5} (float64); слишком маленький шаг портит ответ из-за округления.
  • Правила: (xn)′=nxn−1(x^n)' = nx^{n-1}, (ex)′=ex(e^x)' = e^x, (ln⁡x)′=1/x(\ln x)' = 1/x, правила суммы, произведения и частного.
  • σ′=σ(1−σ)≤0.25\sigma' = \sigma(1 - \sigma) \le 0.25, tanh⁡′=1−tanh⁡2\tanh' = 1 - \tanh^2, ReLU’ — 0 или 1, SiLU’ =σ(1+x(1−σ))= \sigma(1 + x(1 - \sigma)).
  • Шаг против знака производной уменьшает loss — это градиентный спуск.

Дальше: цепное правило и вычислительный граф — как найти производную функции, собранной из сотен звеньев, и посчитать её руками так же, как это делает loss.backward().