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

Функции и графики

Что такое функция в математике и в Python, как читать график и как из простых деталей — прямой, ReLU, экспоненты и сигмоиды — собирается нейросеть.

УРОКstage-1/1-0-school-math/01-functions-graphs.mdx1 ч 30 минУрок 1 из 5
После урока вы сможете
  • Объяснить, что такое функция, её аргумент, значение и параметры, и записать формулу как функцию на Python
  • Читать графики линейной и квадратичной функций, модуля и экспоненты; находить наклон прямой по двум точкам
  • Реализовать ReLU и устойчивую сигмоиду и собрать из них нейрон
  • Объяснить, почему нейросеть без нелинейности бесполезно делать глубокой

Любая нейросеть — это функция. Большая языковая модельбольшая языковая модель (БЯМ, LLM)EN large language model (LLM)В китайском «большая модель» («да мосин») — разговорное общее название для LLM и вообще foundation models. вроде Qwen или DeepSeek принимает на вход текст (точнее, номера токенов) и возвращает вероятности следующего токена. Внутри у неё миллиарды чисел, но собрана она из простых деталей: прямых линий, «изломов» и экспонент. Все эти детали есть в школьной программе, просто там они назывались иначе.

Слово «функция» будет встречаться в курсе постоянно: функция активации, функция потерь, функция правдоподобия. В этом уроке мы освежим ту часть школьной математики, которая нужна для ML. Это функция и её график, линейная и квадратичная функции, модуль, кусочные функции (среди них знаменитая ReLU), экспонента и сигмоида. В конце соберём из них маленькую нейросеть. Всё — на числах и в коде на Python, без доказательств.

ФункцияфункцияEN functionВ Python функция — обычный объект, её можно передать как аргумент или вернуть из другой функции. — это правило, которое каждому входу ставит в соответствие ровно один выход. Записывают так: y=f(x)y = f(x). Здесь xx — аргумент (вход), ff — имя функции, yy — значение (выход).

Пример: f(x)=2x+1f(x) = 2x + 1. Подставим x=3x = 3 и получим f(3)=2⋅3+1=7f(3) = 2 \cdot 3 + 1 = 7. В Python всё то же самое:

def f(x):
return 2 * x + 1 # правило: умножить на 2 и прибавить 1
print(f(3)) # 7
print(f(-1)) # -1

Математическая функция и функция в Python — близкие родственники: def задаёт правило, а вызов f(3) подставляет вход. Но математическая функция ничего не делает «по пути»: не печатает, не читает файлы, не помнит прошлых вызовов. На один и тот же вход она всегда даёт один и тот же выход.

Функция может принимать несколько входов. Например, площадь прямоугольника S(a,b)=a⋅bS(a, b) = a \cdot b. Нейросеть принимает тысячи входов сразу.

Для ML важнее другое различие — между входами и параметрамипараметрEN parameter. Возьмём запись y=kx+by = kx + b:

  • xx — вход, он меняется от вызова к вызову;
  • kk и bb — параметры. Для конкретной функции они фиксированы, но если их поменять, получится другая функция из того же семейства.
def line(x, k, b):
return k * x + b
print(line(3, k=2, b=1)) # 7
print(line(3, k=-1, b=10)) # 7 — совсем другая прямая, но и она проходит через точку (3, 7)

Обучение нейросети — это подбор параметров. Архитектура задаёт семейство функций: «все прямые», «все параболы», «все трансформеры с 7 млрд чисел». Обучение ищет в этом семействе функцию, которая лучше всего объясняет данные. Параметры нейросети называют весамивесаEN weights.

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

import math
# Запускайте по одной строке: первая же ошибка останавливает программу
math.sqrt(-1) # ValueError: math domain error
1 / 0 # ZeroDivisionError: division by zero

Множество допустимых входов называют областью определения. Ошибку math domain error вы ещё встретите. Например, когда вероятность округлится до нуля и под логарифмом окажется 0 (следующий урок).

График функцииграфик функцииEN graph of a functionВсе точки (x, f(x)). Таблица значений и график — два способа увидеть одну и ту же функцию. — это все точки (x,f(x))(x, f(x)) на плоскости. Проще всего построить его по таблице значений:

def f(x):
return 2 * x + 1
for x in range(-3, 4): # x = -3, -2, …, 3
print(x, f(x))
-3 -5
-2 -3
-1 -1
0 1
1 3
2 5
3 7

Отметьте эти точки на клетчатой бумаге, и они лягут на прямую. Компьютер делает то же самое, только с сотнями точек. С matplotlib из урока 0.3.3 это выглядит так:

import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-3, 3, 200) # 200 точек от −3 до 3
plt.plot(x, 2 * x + 1, label="2x + 1") # NumPy считает формулу сразу для всех 200 точек
plt.plot(x, x ** 2, label="x²")
plt.axhline(0, color="gray", lw=0.5) # ось x
plt.axvline(0, color="gray", lw=0.5) # ось y
plt.legend()
plt.grid(alpha=0.3)
plt.show()

Вот шесть функций этого урока — запомните их «в лицо»:

Линейнаяy = 2x + 1b = 1Квадратичнаяy = x²минимумМодульy = |x|уголReLUy = max(0, x)изломЭкспонентаy = eˣe⁰ = 1Сигмоидаy = 1/(1+e⁻ˣ)10.5
Шесть функций урока. По горизонтали — x (у сигмоиды от −6 до 6, у остальных примерно от −3 до 3), по вертикали — значение функции.

Глядя на любой график, задавайте три вопроса. Где функция растёт и где убывает? Где она пересекает ноль? Где у неё минимум или максимум? Для ML важнее всего минимум: обучение — это поиск минимума функции потерь.

Поездка на такси: посадка стоит 100 ₽, каждый километр — 30 ₽. Стоимость поездки на xx км равна y=30x+100y = 30x + 100:

Километры xx01510
Цена yy, ₽100130250400

Это линейная функциялинейная функцияEN linear functiony = kx + b. В ML её пишут y = wx + b (w — вес, b — смещение). В китайской школе — тоже y = kx + b., её график — прямая. Оба числа в формуле имеют простой смысл:

  • kk — наклоннаклон (угловой коэффициент)EN slopeНа сколько меняется y, когда x растёт на 1. По двум точкам k = (y₂ − y₁) / (x₂ − x₁).: на сколько изменится yy, если xx увеличить на 1. Здесь каждый километр добавляет 30 ₽.
  • bb — значение при x=0x = 0, то есть точка, где прямая пересекает вертикальную ось. Здесь это 100 ₽ за посадку.

Если k>0k > 0, прямая растёт, если k<0k < 0 — убывает, при k=0k = 0 она горизонтальна.

Наклон можно найти по любым двум точкам прямой:

k=y2−y1x2−x1k = \frac{y_2 - y_1}{x_2 - x_1}

Пример: поездка на 2 км стоила 160 ₽, на 6 км — 280 ₽. Тогда k=280−1606−2=1204=30k = \frac{280 - 160}{6 - 2} = \frac{120}{4} = 30 ₽ за км. Дробь «изменение yy / изменение xx» — зародыш производной, к ней мы вернёмся в модуле 1.2.

Как это выглядит в ML. В нейросетях ту же прямую пишут как y=wx+by = wx + b: ww — вес (weight), bb — смещение (bias). Пример из мира LLM, с условными числами: время ответа модели t=0.02⋅n+0.5t = 0.02 \cdot n + 0.5 секунды. Здесь 0.5 с — задержка до первого токена, а 0.02 с — время на каждый следующий токен, то есть 50 токенов в секунду. Ответ из 50 токенов займёт 1.5 с, из 500 токенов — 10.5 с.

У настоящего нейрона входов много: y=w1x1+w2x2+w3x3+by = w_1 x_1 + w_2 x_2 + w_3 x_3 + b. Это тоже линейная функция, только от нескольких переменных. Как записать её коротко через знак Σ\Sigma, разберём в уроке 1.0.3, а через скалярное произведение векторов — в уроке 1.1.1.

Квадратичная функцияквадратичная функцияEN quadratic functionГрафик — парабола. Квадратичные функции потерь (MSE) похожи на «чашу» с одним дном. y=x2y = x^2 даёт такую таблицу:

xx−3−2−10123
x2x^29410149

Её график — парабола. Она симметрична, отрицательных значений у неё нет, а минимум равен 0 в точке x=0x = 0. Параболу легко сдвигать. Например, y=(x−3)2+1y = (x - 3)^2 + 1 — это та же парабола, у которой минимум равен 1 в точке x=3x = 3. Скобка (x−a)(x - a) сдвигает график вправо на aa, а слагаемое +c+c — вверх на cc.

Почему квадрат так важен в ML. Пусть модель предсказала y^\hat y (читается «игрек с крышкой»), а правильный ответ — yy. Ошибка равна y^−y\hat y - y, а её квадрат растёт гораздо быстрее самой ошибки:

Ошибка y^−y\hat y - y0.51210
Квадрат ошибки0.2514100

Промах в 10 раз больше штрафуется в 100 раз сильнее. Средний квадрат ошибки по всем примерам называют MSEсреднеквадратичная ошибка (MSE)EN mean squared error (MSE)Корень из MSE — RMSE (root mean squared error), измеряется в тех же единицах, что и целевая переменная. (mean squared error). Это главная функция потерьфункция потерьEN loss function для задач, где нужно предсказать число (модуль 2.2).

Теперь посмотрим на ошибку как на функцию параметра. Возьмём модель y^=w⋅x\hat y = w \cdot x и один обучающий пример: вход x=2x = 2, правильный ответ y=6y = 6.

def loss(w):
x, y = 2, 6 # один обучающий пример
y_hat = w * x # предсказание модели с параметром w
return (y_hat - y) ** 2
for w in range(6):
print(w, loss(w))
0 36
1 16
2 4
3 0
4 4
5 16

Получилась парабола L(w)=(2w−6)2L(w) = (2w - 6)^2 с дном при w=3w = 3. Это обучение в миниатюре: найти параметр, при котором функция потерь минимальна. Градиентный спуск (модуль 1.4) «скатывается» по такой чаше на дно. У LLM параметров миллиарды и «чаша» многомерная, но идея та же.

Модульмодуль (абсолютная величина)EN absolute valueРасстояние от числа до нуля, |−3| = 3. В Python — abs(). Основа MAE и нормы L1. ∣x∣|x| — это расстояние от числа до нуля: ∣−3∣=3|-3| = 3, ∣2.5∣=2.5|2.5| = 2.5, ∣0∣=0|0| = 0. График похож на букву V. В Python модуль считает функция abs(-3).

В ML модуль встречается в MAEсредняя абсолютная ошибка (MAE)EN mean absolute error (MAE) — средней абсолютной ошибке. В отличие от квадрата, модуль штрафует промахи линейно: ошибка 10 даёт штраф 10, а не 100, поэтому MAE спокойнее относится к выбросам в данных. Обратите внимание на «угол» графика в нуле: там у функции нет одного наклона. Это пригодится в модуле 1.2.

Кусочная функциякусочная функцияEN piecewise functionРазные формулы на разных участках оси. ReLU = max(0, x) — самая известная кусочная функция в ML. задаётся разными формулами на разных участках. Модуль — уже кусочная функция: слева от нуля это −x-x, справа — xx.

Пример из жизни: мобильный тариф, где первые 100 МБ бесплатны, а дальше каждый мегабайт стоит 2 ₽.

f(x)={0,x≤1002(x−100),x>100f(x) = \begin{cases} 0, & x \le 100 \\ 2(x - 100), & x > 100 \end{cases}

За 50 МБ вы платите 0 ₽, за 150 МБ — 100 ₽, за 600 МБ — 1000 ₽.

Самая знаменитая кусочная функция в ML называется ReLU (rectified linear unit, дословно «выпрямленный линейный элемент»):

ReLU(x)=max⁡(0,x)={x,x>00,x≤0\mathrm{ReLU}(x) = \max(0, x) = \begin{cases} x, & x > 0 \\ 0, & x \le 0 \end{cases}

Отрицательные числа она обнуляет, положительные пропускает без изменений: ReLU(−2)=0\mathrm{ReLU}(-2) = 0, ReLU(0)=0\mathrm{ReLU}(0) = 0, ReLU(2)=2\mathrm{ReLU}(2) = 2. Наш тариф — это 2⋅ReLU(x−100)2 \cdot \mathrm{ReLU}(x - 100), то есть ReLU, сдвинутая вправо на 100 и растянутая вдвое по вертикали. Запомните этот приём, он понадобится в разделе о композиции.

import numpy as np
def relu(x):
return max(0, x) # для одного числа: большее из 0 и x
print(relu(-2), relu(3.5)) # 0 3.5
xs = np.array([-2.0, -1.0, 0.0, 1.0, 2.0])
print(np.maximum(0, xs)) # для массива сразу: [0. 0. 0. 1. 2.]

ReLU популярна, потому что её очень дёшево считать, а положительный сигнал она передаёт без «сжатия». Стандартом глубокого обучения она стала в начале 2010-х годов. В современных LLM чаще используют её гладких родственников: GELU (GPT-2) и SwiGLU (Llama, Qwen, DeepSeek). Их разберём в уроках 3.1.1 и 4.5.3.

Экспонентаэкспонента (показательная функция)EN exponential functioneˣ — всегда положительна, растёт взрывообразно. Внутри softmax и сигмоиды. exe^x строится на особом числе e≈2.718e \approx 2.718. Откуда оно берётся, разберём в следующем уроке, а пока посмотрим на поведение функции:

xx−2−10123
exe^x0.1350.36812.7187.38920.09

У экспоненты три важных свойства. Она всегда положительна. В нуле она равна 1. Вправо она растёт взрывообразно, а влево стремится к нулю, но никогда его не достигает. Именно поэтому softmax использует экспоненту: она превращает любое число, даже отрицательное, в положительное (урок 1.3.2).

Сигмоидасигмоида (логистическая функция)EN sigmoid (logistic function)σ(z) = 1 / (1 + e^(−z)) — превращает любое число в вероятность от 0 до 1. собирается из экспоненты:

σ(x)=11+e−x\sigma(x) = \frac{1}{1 + e^{-x}}

Посчитаем по шагам для x=2x = 2. Сначала e−2≈0.135e^{-2} \approx 0.135, затем 1+0.135=1.1351 + 0.135 = 1.135, и наконец 1/1.135≈0.8811 / 1.135 \approx 0.881.

xx−4−202410
σ(x)\sigma(x)0.0180.1190.50.8810.9820.99995

Сигмоида сжимает любое число в интервал от 0 до 1, поэтому её выход можно читать как вероятность. В нуле она равна ровно 0.5, а ещё она симметрична: σ(−x)=1−σ(x)\sigma(-x) = 1 - \sigma(x). Проверьте по таблице: 0.119+0.881=10.119 + 0.881 = 1. Сигмоида встретится вам в логистической регрессии (урок 2.2.2, «вероятность, что письмо — спам») и внутри функции активации SiLU, x⋅σ(x)x \cdot \sigma(x), на которой построен SwiGLU в Qwen и DeepSeek.

Ловушка переполнения. Число math.exp(1000) не помещается во float, и Python бросает исключение:

import math
def sigmoid_naive(x):
return 1 / (1 + math.exp(-x))
print(sigmoid_naive(2)) # 0.8807970779778823
print(sigmoid_naive(-1000)) # OverflowError: math range error — внутри считается exp(1000)

Выход простой. При x≥0x \ge 0 исходная формула безопасна: e−xe^{-x} не больше 1. А при x<0x < 0 умножим числитель и знаменатель на exe^x и получим равную формулу ex1+ex\frac{e^x}{1 + e^x}. В ней экспонента тоже берётся от отрицательного числа, а значит, не больше 1. Это та же идея, что и у устойчивого softmax в уроке 1.3.2: никогда не считать экспоненту от большого положительного числа. Устойчивую сигмоиду вы напишете в задании.

Композиция: как функции склеиваются в нейросеть

Заголовок раздела «Композиция: как функции склеиваются в нейросеть»

Композициякомпозиция функцийEN function compositionf(g(x)) — сначала g, потом f. Нейросеть — композиция слоёв. f(g(x))f(g(x)) означает: сначала вычислить g(x)g(x), а результат подать в ff. Пусть f(x)=x2f(x) = x^2 и g(x)=x+1g(x) = x + 1. Тогда f(g(2))=f(3)=9f(g(2)) = f(3) = 9, а g(f(2))=g(4)=5g(f(2)) = g(4) = 5. Порядок важен.

Искусственный нейрон — это композиция линейной функции и функции активациифункция активацииEN activation function: neuron(x)=ReLU(wx+b)\mathrm{neuron}(x) = \mathrm{ReLU}(wx + b). Нейросеть — композиция многих таких шагов:

flowchart LR
X["x"] --> L1["w₁·x + b₁"] --> A1["h = ReLU(…)"] --> L2["w₂·h + b₂"] --> S["σ"] --> Y["y от 0 до 1"]
Крошечная нейросеть: линейная функция → ReLU → линейная функция → сигмоида

Составим композицию двух линейных функций: g(x)=3x+1g(x) = 3x + 1 и f(u)=2u+5f(u) = 2u + 5. Получится

f(g(x))=2(3x+1)+5=6x+7.f(g(x)) = 2(3x + 1) + 5 = 6x + 7.

Снова прямая! Сколько линейных слоёв ни ставь друг за другом, в итоге выйдет одна линейная функция. В многомерном случае это одно матричное умножение (урок 1.1.2). Поэтому между слоями нейросети всегда стоит нелинейность — ReLU, сигмоида или их родственники. Подробнее об этом — в уроке 3.1.1.

Приём 1. Модуль собирается из двух ReLU: ∣x∣=ReLU(x)+ReLU(−x)|x| = \mathrm{ReLU}(x) + \mathrm{ReLU}(-x). Проверим при x=−3x = -3: 0+3=30 + 3 = 3. Получилась сеть из двух «нейронов».

Приём 2. Приблизим параболу x2x^2 на отрезке от 0 до 3 тремя ReLU:

g(x)=ReLU(x)+2 ReLU(x−1)+2 ReLU(x−2)g(x) = \mathrm{ReLU}(x) + 2\,\mathrm{ReLU}(x - 1) + 2\,\mathrm{ReLU}(x - 2)

Каждая ReLU «включается» в своей точке и добавляет наклон. На отрезке [0,1][0, 1] наклон равен 1, на [1,2][1, 2] он становится 1+2=31 + 2 = 3, на [2,3][2, 3] — 3+2=53 + 2 = 5. Получается ломаная через точки (0,0)(0, 0), (1,1)(1, 1), (2,4)(2, 4), (3,9)(3, 9). Проверим одну точку руками, x=2.5x = 2.5: ReLU(2.5)+2 ReLU(1.5)+2 ReLU(0.5)=2.5+3+1=6.5\mathrm{ReLU}(2.5) + 2\,\mathrm{ReLU}(1.5) + 2\,\mathrm{ReLU}(0.5) = 2.5 + 3 + 1 = 6.5, а настоящее 2.52=6.252.5^2 = 6.25 — промах всего 0.25.

def relu(x):
return max(0, x)
def g(x):
# три «нейрона» с изломами в точках 0, 1 и 2
return relu(x) + 2 * relu(x - 1) + 2 * relu(x - 2)
for x in [0, 0.5, 1, 1.5, 2, 2.5, 3]:
# {x:<4} — вывести x, выровняв по левому краю в поле шириной 4 символа: так столбцы стоят ровно
print(f"x = {x:<4} x² = {x * x:<5} g(x) = {g(x)}")
x = 0 x² = 0 g(x) = 0
x = 0.5 x² = 0.25 g(x) = 0.5
x = 1 x² = 1 g(x) = 1
x = 1.5 x² = 2.25 g(x) = 2.5
x = 2 x² = 4 g(x) = 4
x = 2.5 x² = 6.25 g(x) = 6.5
x = 3 x² = 9 g(x) = 9
02468100123y = x²g(x) — сумма трёх ReLU
Три ReLU-«нейрона» дают ломаную g(x), которая почти повторяет параболу: наибольшая ошибка 0.25 — посередине между изломами.

Добавьте больше «нейронов» с изломами почаще, и ломаная прижмётся к параболе ещё плотнее. В этом и состоит интуиция теоремы об универсальной аппроксимации: достаточно широкая сеть из ReLU может сколь угодно точно приблизить любую непрерывную функцию на отрезке. Обучение само подбирает, где ставить изломы и с какими коэффициентами.

Реализуйте детали нейросети: линейную функцию, ReLU, устойчивую сигмоиду, нейрон и модуль через ReLU. Кнопка «Запустить» печатает таблицу значений, кнопка «Проверить» запускает тесты.

Линейная функция, ReLU, сигмоида и нейрон6 тестов
import math


def linear(x, k, b):
    """Линейная функция: y = k·x + b.

    Пример: linear(3, 2, 1) → 7, потому что 2·3 + 1 = 7.
    """
    # TODO: одна строка с return
    ...


def relu(x):
    """ReLU: возвращает x, если x > 0, иначе 0.

    Примеры: relu(-2) → 0, relu(3.5) → 3.5.
    """
    # TODO
    ...


def sigmoid(x):
    """Сигмоида: σ(x) = 1 / (1 + e^(−x)). Результат всегда между 0 и 1.

    Пример: sigmoid(0) → 0.5.
    Должна работать и при x = -1000: math.exp(1000) вызывает OverflowError,
    поэтому для отрицательных x используйте равную формулу e^x / (1 + e^x).
    """
    # TODO: if x >= 0: ... else: ...
    ...


def neuron(x, w, b):
    """Один нейрон: сначала линейная функция w·x + b, затем ReLU.

    Пример: neuron(2, 3, -1) → relu(3·2 − 1) = 5.
    Подсказка: используйте уже написанные linear и relu.
    """
    # TODO
    ...


def abs_via_relu(x):
    """Модуль |x|, собранный из двух ReLU — без abs() и без if.

    Пример: abs_via_relu(-3) → 3.
    """
    # TODO
    ...


# Кнопка «Запустить» печатает таблицу значений (пока функции возвращают None):
print("x | linear(x,2,1) | relu | sigmoid | |x|")
for x in [-2, -1, 0, 1, 2]:
    print(x, linear(x, 2, 1), relu(x), sigmoid(x), abs_via_relu(x))

Задание для Jupyter (по желанию). Постройте в matplotlib на одном рисунке x2x^2 и ломаную g(x)g(x) на отрезке от 0 до 3 (для массивов используйте np.maximum(0, x)). Затем сделайте изломы чаще — в точках 0, 0.5, 1, 1.5, 2 и 2.5 — и подберите коэффициенты шести ReLU так, чтобы ломаная проходила через точки (x,x2)(x, x^2). Подсказка: наклон на каждом следующем участке растёт на одно и то же число. Если всё верно, наибольшая ошибка уменьшится в 4 раза, до 0.0625.

Проверь себя6 вопросов
1 Прямая проходит через точки (1,5)(1, 5) и (3,11)(3, 11). Чему равен её наклон kk?
2 Функция потерь L(w)=(2w−6)2L(w) = (2w - 6)^2. При каком ww она минимальна?
3 Какие утверждения про ReLU верны?Можно выбрать несколько ответов
4 Пусть f(x)=x2f(x) = x^2 и g(x)=x+1g(x) = x + 1. Чему равно f(g(2))f(g(2))?
5 Известно, что σ(2)≈0.881\sigma(2) \approx 0.881. Чему равно σ(−2)\sigma(-2)? Округлите до трёх знаков.
6 Зачем между линейными слоями нейросети ставят ReLU или другую нелинейность?
  • Функция — правило «вход → ровно один выход». В ML важно отличать входы от параметров: обучение подбирает параметры.
  • В прямой y=kx+by = kx + b число kk — наклон (изменение yy на единицу xx), а bb — значение в нуле. Квадратичная функция — «чаша» с минимумом, как функция потерь.
  • ReLU =max⁡(0,x)= \max(0, x) — кусочная функция. Сигмоида сжимает любое число в (0,1)(0, 1). Экспоненту от большого положительного числа не считаем — будет переполнение.
  • Нейросеть — композиция линейных функций и нелинейностей. Без нелинейности глубина бесполезна, а с ней из «изломов» собирается любая кривая.

Дальше: степени, экспонента и логарифмы — откуда берётся число ee и почему в ML повсюду log⁡\log.