Функции, экспонента и логарифм
Функция — это машинка
Функция f берёт вход x и выдаёт выход f(x). Нейросеть — это тоже функция: на вход картинка (64 числа), на выход 10 вероятностей. Просто очень сложная функция, собранная из простых. В этом модуле мы научимся понимать, как выход функции меняется при изменении входа, — это ключ к обучению.
import numpy as np
import vis
x = np.linspace(-3, 3, 200)
vis.line(x, {"2x + 1 (линейная)": 2 * x + 1, "x² (парабола)": x ** 2, "x³": x ** 3},
title="Простые функции", xlabel="x")
- Линейная f(x) = wx + b — прямая: w задаёт наклон, b — сдвиг. Это модель из первого урока.
- Квадратичная x^2 — парабола с минимумом в нуле. Квадрат ошибки, который мы минимизируем, выглядит именно так.
- Кусочные функции задаются разными формулами на разных участках. Самая знаменитая в ML — ReLU:
Экспонента
Экспонента e^x, где e \approx 2.718, — функция, которая растёт со скоростью, равной самой себе. Её свойства:
- e^x > 0 всегда — поэтому её используют, чтобы превратить любое число в положительное (так получаются вероятности в softmax);
- e^0 = 1, e^{a + b} = e^a e^b;
- растёт очень быстро: e^{10} \approx 22\,026, e^{100} \approx 2.7 \cdot 10^{43}, а e^{1000} уже не помещается в float.
import numpy as np
import vis
x = np.linspace(-3, 3, 200)
vis.line(x, {"eˣ": np.exp(x), "2ˣ": 2 ** x, "e⁻ˣ": np.exp(-x)}, title="Экспоненты", xlabel="x")
print(np.exp([0, 1, 10, 100]))
print(np.exp(1000)) # inf — переполнение!
Почему именно e
Число e — то основание, при котором скорость роста функции в каждой точке в точности равна её значению. Наклон графика e^x в точке x равен e^x. Из-за этого формулы с e получаются самыми простыми — увидим это в уроке о производных.
import numpy as np
for n in (1, 10, 100, 10_000, 1_000_000):
print(f"(1 + 1/{n})^{n} = {(1 + 1 / n) ** n:.6f}")
print("e =", np.e)
Логарифм
Натуральный логарифм \ln x — обратная функция к экспоненте: \ln x — это степень, в которую нужно возвести e, чтобы получить x. \ln(e^x) = x, e^{\ln x} = x.
- Определён только для x > 0; \ln 1 = 0; при x \to 0 уходит в -\infty.
- Растёт очень медленно: \ln 1\,000\,000 \approx 13.8.
- Превращает произведения в суммы: \ln(ab) = \ln a + \ln b, и степени — в множители: \ln a^k = k \ln a.
import numpy as np
import vis
x = np.linspace(0.01, 5, 300)
vis.line(x, {"ln x": np.log(x), "log₂ x": np.log2(x)}, title="Логарифмы", xlabel="x")
print(np.log([1, np.e, 10, 1e6]))
print(np.log(2 * 3), np.log(2) + np.log(3))
Зачем логарифм в ML
1. Произведения вероятностей. Вероятность того, что модель права на всём наборе из N объектов, — произведение N вероятностей. При N = 1000 и вероятностях около 0.9 это 0.9^{1000} \approx 10^{-46}, а дальше — просто ноль в компьютере. Логарифм превращает произведение в сумму вполне нормальных чисел:
import numpy as np
p = np.full(5000, 0.9)
print("произведение:", np.prod(p)) # 0.0 — исчезло
print("сумма логарифмов:", np.log(p).sum()) # −526.8 — вполне считаемо
2. Функции потерь. Кросс-энтропия, главная функция потерь для классификации, — это -\ln p, где p — вероятность правильного ответа. Она сильно штрафует уверенные ошибки: при p = 0.01 потеря 4.6, а при p = 0.9 — всего 0.1.
import numpy as np
import vis
p = np.linspace(0.01, 1, 200)
vis.line(p, -np.log(p), title="−ln p: штраф за вероятность правильного ответа", xlabel="p", ylabel="потеря")
3. Шкалы. Потери при обучении часто падают в тысячи раз — их рисуют в логарифмическом масштабе (vis.line(..., logy=True)).
Сигмоида и tanh
Скомбинируем экспоненту с дробью и получим сигмоиду — гладкую «ступеньку» от 0 до 1:
Она превращает любое число в «вероятность»: большие положительные z → почти 1, большие отрицательные → почти 0, \sigma(0) = 0.5. Её близкий родственник tanh — такая же ступенька, но от −1 до 1: \tanh z = 2\sigma(2z) - 1.
import numpy as np
import vis
z = np.linspace(-6, 6, 200)
sigmoid = 1 / (1 + np.exp(-z))
vis.line(z, {"σ(z)": sigmoid, "tanh z": np.tanh(z), "ReLU(z)": np.maximum(0, z) / 6},
title="Функции активации (ReLU сжата в 6 раз для масштаба)", xlabel="z")
Все три — функции активации, та самая нелинейность между слоями нейросети. Подробно сравним их в отдельном уроке.
Композиция функций
Композиция — применение функций одна за другой: h(x) = g(f(x)). Нейросеть — композиция слоёв:
Например, нейрон — это композиция линейной функции и сигмоиды: \sigma(wx + b). Меняя w и b, мы двигаем и растягиваем ступеньку:
import numpy as np
import vis
x = np.linspace(-5, 5, 200)
sig = lambda z: 1 / (1 + np.exp(-z))
vis.line(x, {"σ(x)": sig(x), "σ(3x) — круче": sig(3 * x), "σ(x − 2) — сдвиг": sig(x - 2), "σ(−x) — отражение": sig(-x)},
title="Нейрон σ(w·x + b) при разных w и b", xlabel="x")
Чтобы обучать такие композиции, нам понадобится узнать, как меняется выход при малом изменении входа или параметра, — это производная. Ей посвящён следующий урок.
Итог
- Нейросеть — функция, собранная из простых функций (композиция).
- Экспонента e^x всегда положительна, быстро растёт и легко переполняется.
- Логарифм — обратная к экспоненте, превращает произведения в суммы; на нём построены функции потерь.
- Сигмоида \sigma(z) = 1/(1 + e^{-z}) сжимает любое число в (0, 1); tanh — в (−1, 1); ReLU — \max(0, z).
Практика решено 0 из 7
Чему равно \ln(e^5)?
Чему равно \log_2 1024?
Какие равенства верны (при положительных a, b)?
Какие значения может принимать сигмоида \sigma(z) = \dfrac{1}{1 + e^{-z}}?
Чему равно \sigma(0)?
Напишите функции для чисел (модуль math):
relu(z)— \max(0, z);sigmoid(z)— 1/(1 + e^{-z});tanh(z)— через экспоненты: \dfrac{e^z - e^{-z}}{e^z + e^{-z}} (безmath.tanh).
Какие тесты проверяют код (4)
assert relu(-3) == 0 and relu(2.5) == 2.5 assert sigmoid(0) == 0.5 and abs(sigmoid(2) - 0.8807970779778823) < 1e-12 and abs(sigmoid(-2) + sigmoid(2) - 1) < 1e-12 assert all(abs(tanh(z) - math.tanh(z)) < 1e-12 for z in (-2, -0.5, 0, 1, 3)) import inspect assert "math.tanh" not in inspect.getsource(tanh), "Не используйте math.tanh"
import math
def relu(z):
return max(0.0, z)
def sigmoid(z):
return 1 / (1 + math.exp(-z))
def tanh(z):
return (math.exp(z) - math.exp(-z)) / (math.exp(z) + math.exp(-z))
Логарифм спасает от исчезновения чисел. Модель дала 2000 объектам вероятности правильного
ответа probs (числа от 0 до 1). Нужна вероятность того, что модель права на всех сразу
— произведение вероятностей. Но произведение 2000 чисел меньше 1 превращается в 0.0.
Напишите log_likelihood(probs) — логарифм этого произведения, вычисленный как сумма
логарифмов. Верните float.
Какие тесты проверяют код (2)
assert abs(log_likelihood(np.array([0.5, 0.5])) - np.log(0.25)) < 1e-12 p = np.full(2000, 0.6) ll = log_likelihood(p) assert np.isfinite(ll), "Получилось −∞: произведение обнулилось — суммируйте логарифмы" assert abs(ll - 2000 * np.log(0.6)) < 1e-6
import numpy as np
def log_likelihood(probs):
return float(np.sum(np.log(probs)))
Войдите, чтобы отмечать пройденные уроки.