Функции и графики
Что такое функция в математике и в Python, как читать график и как из простых деталей — прямой, ReLU, экспоненты и сигмоиды — собирается нейросеть.
Зачем это нужно
Заголовок раздела «Зачем это нужно»Любая нейросеть — это функция. Большая языковая модельбольшая языковая модель (БЯМ, LLM)EN large language model (LLM)В китайском «большая модель» («да мосин») — разговорное общее название для LLM и вообще foundation models. вроде Qwen или DeepSeek принимает на вход текст (точнее, номера токенов) и возвращает вероятности следующего токена. Внутри у неё миллиарды чисел, но собрана она из простых деталей: прямых линий, «изломов» и экспонент. Все эти детали есть в школьной программе, просто там они назывались иначе.
Слово «функция» будет встречаться в курсе постоянно: функция активации, функция потерь, функция правдоподобия. В этом уроке мы освежим ту часть школьной математики, которая нужна для ML. Это функция и её график, линейная и квадратичная функции, модуль, кусочные функции (среди них знаменитая ReLU), экспонента и сигмоида. В конце соберём из них маленькую нейросеть. Всё — на числах и в коде на Python, без доказательств.
Функция: вход → выход
Заголовок раздела «Функция: вход → выход»ФункцияфункцияEN functionВ Python функция — обычный объект, её можно передать как аргумент или вернуть из другой функции. — это правило, которое каждому входу ставит в соответствие ровно один выход. Записывают так: . Здесь — аргумент (вход), — имя функции, — значение (выход).
Пример: . Подставим и получим . В Python всё то же самое:
def f(x): return 2 * x + 1 # правило: умножить на 2 и прибавить 1
print(f(3)) # 7print(f(-1)) # -1Математическая функция и функция в Python — близкие родственники: def задаёт правило, а вызов f(3)
подставляет вход. Но математическая функция ничего не делает «по пути»: не печатает, не читает файлы, не помнит
прошлых вызовов. На один и тот же вход она всегда даёт один и тот же выход.
Входы и параметры
Заголовок раздела «Входы и параметры»Функция может принимать несколько входов. Например, площадь прямоугольника . Нейросеть принимает тысячи входов сразу.
Для ML важнее другое различие — между входами и параметрамипараметрEN parameter. Возьмём запись :
- — вход, он меняется от вызова к вызову;
- и — параметры. Для конкретной функции они фиксированы, но если их поменять, получится другая функция из того же семейства.
def line(x, k, b): return k * x + b
print(line(3, k=2, b=1)) # 7print(line(3, k=-1, b=10)) # 7 — совсем другая прямая, но и она проходит через точку (3, 7)Обучение нейросети — это подбор параметров. Архитектура задаёт семейство функций: «все прямые», «все параболы», «все трансформеры с 7 млрд чисел». Обучение ищет в этом семействе функцию, которая лучше всего объясняет данные. Параметры нейросети называют весамивесаEN weights.
Где функция не определена
Заголовок раздела «Где функция не определена»Не каждую функцию можно вычислить при любом входе. Квадратный корень из отрицательного числа, логарифм нуля, деление на ноль — Python на них ругается:
import math# Запускайте по одной строке: первая же ошибка останавливает программуmath.sqrt(-1) # ValueError: math domain error1 / 0 # ZeroDivisionError: division by zeroМножество допустимых входов называют областью определения. Ошибку math domain error вы ещё встретите.
Например, когда вероятность округлится до нуля и под логарифмом окажется 0 (следующий урок).
График: функция, которую видно
Заголовок раздела «График: функция, которую видно»График функцииграфик функцииEN graph of a functionВсе точки (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 -10 11 32 53 7Отметьте эти точки на клетчатой бумаге, и они лягут на прямую. Компьютер делает то же самое, только с сотнями точек. С matplotlib из урока 0.3.3 это выглядит так:
import numpy as npimport matplotlib.pyplot as plt
x = np.linspace(-3, 3, 200) # 200 точек от −3 до 3plt.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) # ось xplt.axvline(0, color="gray", lw=0.5) # ось yplt.legend()plt.grid(alpha=0.3)plt.show()Вот шесть функций этого урока — запомните их «в лицо»:
Глядя на любой график, задавайте три вопроса. Где функция растёт и где убывает? Где она пересекает ноль? Где у неё минимум или максимум? Для ML важнее всего минимум: обучение — это поиск минимума функции потерь.
Линейная функция y = kx + b
Заголовок раздела «Линейная функция y = kx + b»Поездка на такси: посадка стоит 100 ₽, каждый километр — 30 ₽. Стоимость поездки на км равна :
| Километры | 0 | 1 | 5 | 10 |
|---|---|---|---|---|
| Цена , ₽ | 100 | 130 | 250 | 400 |
Это линейная функциялинейная функцияEN linear functiony = kx + b. В ML её пишут y = wx + b (w — вес, b — смещение). В китайской школе — тоже y = kx + b., её график — прямая. Оба числа в формуле имеют простой смысл:
- — наклоннаклон (угловой коэффициент)EN slopeНа сколько меняется y, когда x растёт на 1. По двум точкам k = (y₂ − y₁) / (x₂ − x₁).: на сколько изменится , если увеличить на 1. Здесь каждый километр добавляет 30 ₽.
- — значение при , то есть точка, где прямая пересекает вертикальную ось. Здесь это 100 ₽ за посадку.
Если , прямая растёт, если — убывает, при она горизонтальна.
Наклон можно найти по любым двум точкам прямой:
Пример: поездка на 2 км стоила 160 ₽, на 6 км — 280 ₽. Тогда ₽ за км. Дробь «изменение / изменение » — зародыш производной, к ней мы вернёмся в модуле 1.2.
Как это выглядит в ML. В нейросетях ту же прямую пишут как : — вес (weight), — смещение (bias). Пример из мира LLM, с условными числами: время ответа модели секунды. Здесь 0.5 с — задержка до первого токена, а 0.02 с — время на каждый следующий токен, то есть 50 токенов в секунду. Ответ из 50 токенов займёт 1.5 с, из 500 токенов — 10.5 с.
У настоящего нейрона входов много: . Это тоже линейная функция, только от нескольких переменных. Как записать её коротко через знак , разберём в уроке 1.0.3, а через скалярное произведение векторов — в уроке 1.1.1.
Квадратичная функция и «чаша» ошибки
Заголовок раздела «Квадратичная функция и «чаша» ошибки»Квадратичная функцияквадратичная функцияEN quadratic functionГрафик — парабола. Квадратичные функции потерь (MSE) похожи на «чашу» с одним дном. даёт такую таблицу:
| −3 | −2 | −1 | 0 | 1 | 2 | 3 | |
|---|---|---|---|---|---|---|---|
| 9 | 4 | 1 | 0 | 1 | 4 | 9 |
Её график — парабола. Она симметрична, отрицательных значений у неё нет, а минимум равен 0 в точке . Параболу легко сдвигать. Например, — это та же парабола, у которой минимум равен 1 в точке . Скобка сдвигает график вправо на , а слагаемое — вверх на .
Почему квадрат так важен в ML. Пусть модель предсказала (читается «игрек с крышкой»), а правильный ответ — . Ошибка равна , а её квадрат растёт гораздо быстрее самой ошибки:
| Ошибка | 0.5 | 1 | 2 | 10 |
|---|---|---|---|---|
| Квадрат ошибки | 0.25 | 1 | 4 | 100 |
Промах в 10 раз больше штрафуется в 100 раз сильнее. Средний квадрат ошибки по всем примерам называют MSEсреднеквадратичная ошибка (MSE)EN mean squared error (MSE)Корень из MSE — RMSE (root mean squared error), измеряется в тех же единицах, что и целевая переменная. (mean squared error). Это главная функция потерьфункция потерьEN loss function для задач, где нужно предсказать число (модуль 2.2).
Теперь посмотрим на ошибку как на функцию параметра. Возьмём модель и один обучающий пример: вход , правильный ответ .
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 361 162 43 04 45 16Получилась парабола с дном при . Это обучение в миниатюре: найти параметр, при котором функция потерь минимальна. Градиентный спуск (модуль 1.4) «скатывается» по такой чаше на дно. У LLM параметров миллиарды и «чаша» многомерная, но идея та же.
Модуль: расстояние до нуля
Заголовок раздела «Модуль: расстояние до нуля»Модульмодуль (абсолютная величина)EN absolute valueРасстояние от числа до нуля, |−3| = 3. В Python — abs(). Основа MAE и нормы L1. — это расстояние от числа до нуля: , , .
График похож на букву V. В Python модуль считает функция abs(-3).
В ML модуль встречается в MAEсредняя абсолютная ошибка (MAE)EN mean absolute error (MAE) — средней абсолютной ошибке. В отличие от квадрата, модуль штрафует промахи линейно: ошибка 10 даёт штраф 10, а не 100, поэтому MAE спокойнее относится к выбросам в данных. Обратите внимание на «угол» графика в нуле: там у функции нет одного наклона. Это пригодится в модуле 1.2.
Кусочные функции и ReLU
Заголовок раздела «Кусочные функции и ReLU»Кусочная функциякусочная функцияEN piecewise functionРазные формулы на разных участках оси. ReLU = max(0, x) — самая известная кусочная функция в ML. задаётся разными формулами на разных участках. Модуль — уже кусочная функция: слева от нуля это , справа — .
Пример из жизни: мобильный тариф, где первые 100 МБ бесплатны, а дальше каждый мегабайт стоит 2 ₽.
За 50 МБ вы платите 0 ₽, за 150 МБ — 100 ₽, за 600 МБ — 1000 ₽.
Самая знаменитая кусочная функция в ML называется ReLU (rectified linear unit, дословно «выпрямленный линейный элемент»):
Отрицательные числа она обнуляет, положительные пропускает без изменений: , , . Наш тариф — это , то есть 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 и сигмоиды. строится на особом числе . Откуда оно берётся, разберём в следующем уроке, а пока посмотрим на поведение функции:
| −2 | −1 | 0 | 1 | 2 | 3 | |
|---|---|---|---|---|---|---|
| 0.135 | 0.368 | 1 | 2.718 | 7.389 | 20.09 |
У экспоненты три важных свойства. Она всегда положительна. В нуле она равна 1. Вправо она растёт взрывообразно, а влево стремится к нулю, но никогда его не достигает. Именно поэтому softmax использует экспоненту: она превращает любое число, даже отрицательное, в положительное (урок 1.3.2).
Сигмоидасигмоида (логистическая функция)EN sigmoid (logistic function)σ(z) = 1 / (1 + e^(−z)) — превращает любое число в вероятность от 0 до 1. собирается из экспоненты:
Посчитаем по шагам для . Сначала , затем , и наконец .
| −4 | −2 | 0 | 2 | 4 | 10 | |
|---|---|---|---|---|---|---|
| 0.018 | 0.119 | 0.5 | 0.881 | 0.982 | 0.99995 |
Сигмоида сжимает любое число в интервал от 0 до 1, поэтому её выход можно читать как вероятность. В нуле она равна ровно 0.5, а ещё она симметрична: . Проверьте по таблице: . Сигмоида встретится вам в логистической регрессии (урок 2.2.2, «вероятность, что письмо — спам») и внутри функции активации SiLU, , на которой построен 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.8807970779778823print(sigmoid_naive(-1000)) # OverflowError: math range error — внутри считается exp(1000)Выход простой. При исходная формула безопасна: не больше 1. А при умножим числитель и знаменатель на и получим равную формулу . В ней экспонента тоже берётся от отрицательного числа, а значит, не больше 1. Это та же идея, что и у устойчивого softmax в уроке 1.3.2: никогда не считать экспоненту от большого положительного числа. Устойчивую сигмоиду вы напишете в задании.
Композиция: как функции склеиваются в нейросеть
Заголовок раздела «Композиция: как функции склеиваются в нейросеть»Композициякомпозиция функцийEN function compositionf(g(x)) — сначала g, потом f. Нейросеть — композиция слоёв. означает: сначала вычислить , а результат подать в . Пусть и . Тогда , а . Порядок важен.
Искусственный нейрон — это композиция линейной функции и функции активациифункция активацииEN activation function: . Нейросеть — композиция многих таких шагов:
flowchart LR X["x"] --> L1["w₁·x + b₁"] --> A1["h = ReLU(…)"] --> L2["w₂·h + b₂"] --> S["σ"] --> Y["y от 0 до 1"]
Почему без нелинейности никак
Заголовок раздела «Почему без нелинейности никак»Составим композицию двух линейных функций: и . Получится
Снова прямая! Сколько линейных слоёв ни ставь друг за другом, в итоге выйдет одна линейная функция. В многомерном случае это одно матричное умножение (урок 1.1.2). Поэтому между слоями нейросети всегда стоит нелинейность — ReLU, сигмоида или их родственники. Подробнее об этом — в уроке 3.1.1.
Из изломов можно собрать любую кривую
Заголовок раздела «Из изломов можно собрать любую кривую»Приём 1. Модуль собирается из двух ReLU: . Проверим при : . Получилась сеть из двух «нейронов».
Приём 2. Приблизим параболу на отрезке от 0 до 3 тремя ReLU:
Каждая ReLU «включается» в своей точке и добавляет наклон. На отрезке наклон равен 1, на он становится , на — . Получается ломаная через точки , , , . Проверим одну точку руками, : , а настоящее — промах всего 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) = 0x = 0.5 x² = 0.25 g(x) = 0.5x = 1 x² = 1 g(x) = 1x = 1.5 x² = 2.25 g(x) = 2.5x = 2 x² = 4 g(x) = 4x = 2.5 x² = 6.25 g(x) = 6.5x = 3 x² = 9 g(x) = 9Добавьте больше «нейронов» с изломами почаще, и ломаная прижмётся к параболе ещё плотнее. В этом и состоит интуиция теоремы об универсальной аппроксимации: достаточно широкая сеть из ReLU может сколь угодно точно приблизить любую непрерывную функцию на отрезке. Обучение само подбирает, где ставить изломы и с какими коэффициентами.
Практика
Заголовок раздела «Практика»Реализуйте детали нейросети: линейную функцию, ReLU, устойчивую сигмоиду, нейрон и модуль через ReLU. Кнопка «Запустить» печатает таблицу значений, кнопка «Проверить» запускает тесты.
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 на одном рисунке и ломаную на отрезке
от 0 до 3 (для массивов используйте np.maximum(0, x)). Затем сделайте изломы чаще — в точках 0, 0.5, 1, 1.5, 2 и 2.5 —
и подберите коэффициенты шести ReLU так, чтобы ломаная проходила через точки . Подсказка: наклон на каждом
следующем участке растёт на одно и то же число. Если всё верно, наибольшая ошибка уменьшится в 4 раза, до 0.0625.
Проверь себя
Заголовок раздела «Проверь себя»- Функция — правило «вход → ровно один выход». В ML важно отличать входы от параметров: обучение подбирает параметры.
- В прямой число — наклон (изменение на единицу ), а — значение в нуле. Квадратичная функция — «чаша» с минимумом, как функция потерь.
- ReLU — кусочная функция. Сигмоида сжимает любое число в . Экспоненту от большого положительного числа не считаем — будет переполнение.
- Нейросеть — композиция линейных функций и нелинейностей. Без нелинейности глубина бесполезна, а с ней из «изломов» собирается любая кривая.
Дальше: степени, экспонента и логарифмы — откуда берётся число и почему в ML повсюду .