🔴 Сложный ⏱️ 60 минут

Многослойные сети

📋 Содержание урока

Многослойные сети 🕸️

В уроке 327 ты разобрал персептрон и упёрся в его фундаментальное ограничение: он умеет рисовать только одну прямую линию, разделяющую два класса, и поэтому не способен решить задачу XOR (исключающее ИЛИ) — там, где точки одного класса лежат по диагонали друг напротив друга, и никакая прямая линия не может отделить их от точек другого класса. Это не мелкий технический недостаток, а строгое математическое ограничение: XOR линейно неразделим, а персептрон по конструкции является линейным классификатором.

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

Но локальное решение одной конкретной задачи — только повод для куда более сильного результата. Оказывается, сеть с одним достаточно широким скрытым слоем и нелинейной активацией способна приблизить не только XOR, а вообще любую непрерывную функцию с любой наперёд заданной точностью. Это теорема универсальной аппроксимации — один из немногих по-настоящему фундаментальных теоретических результатов во всём машинном обучении, который объясняет не «как обучить конкретную сеть», а «почему нейросети как класс моделей в принципе способны решать настолько разные задачи — от распознавания рукописных цифр до генерации текста».

Сегодняшний урок — первый в блоке, полностью посвящённом многослойным сетям и глубокому обучению. План такой: сначала явно решаем XOR добавлением скрытого слоя, затем разбираем теорему универсальной аппроксимации, потом — архитектуру полносвязной сети и её компактную матричную запись, затем сравниваем две ключевые функции активации (сигмоиду и ReLU) и в конце проходим forward pass — пошаговое вычисление выхода сети слой за слоем. Сам процесс обучения весов через алгоритм backpropagation (обратное распространение ошибки) — тема следующих уроков блока (урок 330), а урок 329 разберёт функции активации ещё подробнее. Сегодня же тебе нужно твёрдо понять, как сеть вообще устроена и как она вычисляет ответ при уже заданных весах — без этого понимания обучение будет выглядеть чистой магией.

История

Марвин Минский и Сеймур Пейперт в своей книге 1969 года «Перцептроны» строго доказали, что одиночный персептрон не может решить XOR, и это доказательство на полтора десятилетия заморозило финансирование и интерес к нейросетевым исследованиям — период, который историки науки называют «зимой нейросетей» (AI winter). Примечательно, что сами Минский и Пейперт прекрасно понимали: многослойная сеть теоретически способна преодолеть это ограничение. Но у них не было ответа на куда более практичный вопрос — как обучить веса скрытых слоёв, если ты видишь ошибку только на выходе сети? Если выходной нейрон ошибся, как понять, какой из сотен весов скрытого слоя виноват и насколько сильно его нужно поправить? Эта проблема известна как проблема присвоения кредита (credit assignment problem), и именно отсутствие её решения, а не недостаток идей, держало многослойные сети в тени полтора десятилетия.

Перелом случился в 1986 году, когда Дэвид Румельхарт, Джеффри Хинтон и Рональд Уильямс опубликовали в журнале Nature статью «Learning representations by back-propagating errors» («Обучение представлений с помощью обратного распространения ошибки»). Статья популяризировала алгоритм backpropagation — эффективный способ, основанный на цепном правиле дифференцирования, точно вычислить вклад каждого веса в каждом слое сети, включая скрытые, в итоговую ошибку. Это и было решением проблемы присвоения кредита: теперь можно было не гадать, а математически строго рассчитать, в какую сторону и насколько сдвинуть любой вес любого слоя, чтобы уменьшить ошибку сети. Именно эта статья считается ключевой искрой, вернувшей многослойные сети в центр внимания исследователей после многолетнего забвения.

Интересно, что backpropagation не был открыт единожды и сразу. Похожую математическую идею описал Пол Вербос в своей диссертации 1974 года, независимо к близким результатам пришли Дэвид Паркер и Ян Лекун в 1985 году — то есть один и тот же алгоритм был переоткрыт как минимум трижды за двенадцать лет, прежде чем закрепился в науке под именем Румельхарта, Хинтона и Уильямс. Спустя всего пару-тройку лет после их статьи появилось и строгое теоретическое обоснование того, почему многослойные сети настолько мощны: в 1989 году Джордж Цибенко доказал теорему универсальной аппроксимации для сетей с сигмоидной активацией, а в 1991 году Курт Хорник обобщил результат на куда более широкий класс нелинейных функций активации. Так практический алгоритм обучения (backpropagation) и строгое теоретическое обоснование мощности архитектуры (универсальная аппроксимация) сомкнулись в течение одного десятилетия — и именно эта комбинация заложила фундамент всего современного глубокого обучения.

Как один скрытый слой решает XOR

Интуиция

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

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

Формула

Многослойный персептрон (MLP) с одним скрытым слоем. Пусть вход $x\in\mathbb{R}^n$, скрытый слой имеет $k$ нейронов с весами $W_1\in\mathbb{R}^{k\times n}$ и смещениями $b_1\in\mathbb{R}^k$, а выходной слой — один нейрон с весами $v\in\mathbb{R}^k$ и смещением $c$. Тогда сеть вычисляет

$$h = f(W_1 x + b_1), \qquad y = g(v^\top h + c)$$

где $f$ — нелинейная функция активации скрытого слоя, $g$ — активация выходного слоя (может быть линейной, сигмоидой или иной, в зависимости от задачи).

Разбор примеров

Пример 1 (минимальное решение — один скрытый нейрон с квадратичной активацией). Возьмём один-единственный скрытый нейрон с весами $w_1=1$, $w_2=-1$, $b=0$, так что его линейная часть равна $z=x_1-x_2$. В качестве нелинейной активации возьмём необычную для практики, но абсолютно законную нелинейную функцию — возведение в квадрат: $h=z^2$. Выходной нейрон просто пропускает это значение без изменений: $y=h$.

$x_1$ $x_2$ $z=x_1-x_2$ $h=z^2$ XOR
$0$ $0$ $0$ $0$ $0$
$0$ $1$ $-1$ $1$ $1$
$1$ $0$ $1$ $1$ $1$
$1$ $1$ $0$ $0$ $0$

Сеть с одним скрытым нейроном и нелинейной активацией $f(z)=z^2$ решает XOR точно, без единой ошибки на всех четырёх комбинациях входов. Это не случайность: для бинарных $x_1,x_2\in\{0,1\}$ верно алгебраическое тождество $(x_1-x_2)^2 = x_1^2-2x_1x_2+x_2^2 = x_1-2x_1x_2+x_2$ (поскольку для $0$ и $1$ квадрат числа равен самому числу), а выражение $x_1-2x_1x_2+x_2$ — это в точности формула XOR для бинарных переменных. Важная оговорка: активация «квадрат» практически не используется в реальных архитектурах — этот пример специально выбран предельно минимальным (один нейрон!), чтобы явно показать: решающим фактором является именно нелинейность как таковая, а не конкретный выбор функции. На практике для XOR и подобных задач используют куда более стандартные активации, что мы и сделаем в следующем примере.

Пример 2 (практическое решение — два скрытых нейрона с ReLU). Возьмём стандартную для современных сетей активацию ReLU$(z)=\max(0,z)$ и два скрытых нейрона: $h_1=\mathrm{ReLU}(x_1+x_2)$ (веса $w=(1,1)$, $b=0$) и $h_2=\mathrm{ReLU}(x_1+x_2-1)$ (веса $w=(1,1)$, $b=-1$). Выходной нейрон линейно комбинирует их: $y=h_1-2h_2$ (веса $v=(1,-2)$, $c=0$, без активации на выходе).

$x_1$ $x_2$ $h_1=\mathrm{ReLU}(x_1+x_2)$ $h_2=\mathrm{ReLU}(x_1+x_2-1)$ $y=h_1-2h_2$ XOR
$0$ $0$ $0$ $0$ $0$ $0$
$0$ $1$ $1$ $0$ $1$ $1$
$1$ $0$ $1$ $0$ $1$ $1$
$1$ $1$ $2$ $1$ $0$ $0$

Снова полное совпадение с XOR на всех четырёх входах, но теперь исключительно с помощью ReLU — той самой активации, которую ты встретишь в подавляющем большинстве современных архитектур. Обрати внимание на механику: $h_1$ «срабатывает» (становится положительным), когда хотя бы один из входов равен $1$, а $h_2$ «срабатывает» только когда оба входа равны $1$ одновременно — разность $h_1-2h_2$ устроена так, что вклад $h_2$ ровно компенсирует лишнюю единицу, которую $h_1$ даёт на паре $(1,1)$.

Пример 3 (почему без нелинейности глубина бесполезна). Возьмём сеть с одним скрытым слоем из двух линейных (без активации) нейронов и линейным выходом: $W_1=\begin{pmatrix}2&1\\1&3\end{pmatrix}$, $b_1=(0,0)$, $W_2=(1,-1)$, $b_2=5$. Подставим одно в другое: $y = W_2(W_1x+b_1)+b_2 = (W_2W_1)x + (W_2b_1+b_2)$. Вычислим эффективную матрицу: $W_2W_1 = (1\cdot2+(-1)\cdot1,\ 1\cdot1+(-1)\cdot3) = (1,-2)$, а эффективное смещение $W_2b_1+b_2 = 0+5=5$. Значит вся двухслойная сеть эквивалентна одному линейному выражению $y=x_1-2x_2+5$ — обычному персептрону без всякой активации. Это не особенность конкретных чисел: для любых матриц $W_1,W_2$ и смещений $b_1,b_2$ композиция линейных слоёв $W_2(W_1x+b_1)+b_2$ всегда сворачивается в одну аффинную функцию $W'x+b'$ с $W'=W_2W_1$ и $b'=W_2b_1+b_2$. Сколько бы линейных слоёв подряд ты ни поставил — хоть десять, хоть сто, — результат останется той же самой прямой линией, которую рисует один персептрон, и решить XOR такая сеть не сможет никогда, независимо от подбора весов.

Почему это важно

Пример 3 объясняет, почему в определении многослойной сети нелинейность — не опциональное украшение, а обязательный компонент: без неё «глубина» — это иллюзия, дополнительные слои не добавляют модели ни капли выразительной силы. А примеры 1 и 2 прямо показывают решение задачи, поставленной ещё в уроке 327: персептрон не мог провести кривую границу вокруг диагональных точек XOR, а сеть даже с единственным скрытым нейроном, вооружённым нелинейной активацией, делает это точно. Именно эта комбинация — «скрытый слой плюс нелинейность» — и есть минимальный работающий рецепт многослойной сети, из которого вырастают все более сложные архитектуры дальше в курсе.

Теорема универсальной аппроксимации

Интуиция

Предыдущий раздел решил одну конкретную задачу — XOR. Возникает естественный вопрос: а насколько далеко распространяется эта идея? Можно ли таким же образом — скрытый слой плюс нелинейность — приблизить вообще любую разумную функцию, не только XOR? Ответ, доказанный математически, — да, причём с ошеломляющей общностью: одного скрытого слоя, если сделать его достаточно широким, хватает, чтобы приблизить абсолютно любую непрерывную функцию с любой наперёд заданной точностью.

Формула

Теорема универсальной аппроксимации (Цибенко, 1989; Хорник, 1991). Пусть $f$ — произвольная непрерывная функция на компактном множестве $K\subset\mathbb{R}^n$, и $\varepsilon>0$ — сколь угодно малая требуемая точность. Тогда для широкого класса нелинейных функций активации $\sigma$ (включая сигмоиду, гиперболический тангенс и ReLU) существует натуральное число $N$ и сеть с одним скрытым слоем шириной $N$ вида

$$F(x) = \sum_{i=1}^N v_i\,\sigma(w_i^\top x + b_i) + c$$

такая, что $\sup_{x\in K}\left|F(x)-f(x)\right| < \varepsilon$.

Формулировка доказывает только существование подходящих весов $w_i, v_i, b_i, c$ — она ничего не говорит о том, как их найти, и не даёт верхней границы на требуемую ширину $N$.

Разбор примеров

Пример 1 (сигмоидами можно собрать прямоугольный импульс). Возьмём два скрытых сигмоидных нейрона и построим из них «импульс» высотой $h$ на интервале $(a,b)$: $\mathrm{bump}(x) = h\bigl[\sigma(k(x-a)) - \sigma(k(x-b))\bigr]$, где $k$ — большое число, делающее сигмоиду почти ступенькой. Возьмём $a=2$, $b=5$, $h=3$, $k=10$. При $x=0$: $\sigma(10\cdot(-2))=\sigma(-20)\approx0$, $\sigma(10\cdot(-5))=\sigma(-50)\approx0$, значит $\mathrm{bump}(0)\approx3\cdot(0-0)=0$. При $x=3{,}5$: $\sigma(10\cdot1{,}5)=\sigma(15)\approx1$, $\sigma(10\cdot(-1{,}5))=\sigma(-15)\approx0$, значит $\mathrm{bump}(3{,}5)\approx3\cdot(1-0)=3$. При $x=7$: $\sigma(50)\approx1$ и $\sigma(20)\approx1$, значит $\mathrm{bump}(7)\approx3\cdot(1-1)=0$. Двумя скрытыми нейронами мы точно воспроизвели прямоугольный импульс высотой $3$ ровно на интервале $(2,5)$ и нулевые значения вне его.

Пример 2 (ReLU-сетью можно собрать треугольный «шатёр»). Тремя ReLU-нейронами строится треугольная («шатровая») функция, равная нулю везде, кроме отрезка $(0,2)$, с пиком $1$ в точке $x=1$: $\mathrm{hat}(x) = \mathrm{ReLU}(x) - 2\,\mathrm{ReLU}(x-1) + \mathrm{ReLU}(x-2)$. Проверим по точкам: $x=0{,}5$: $0{,}5-2\cdot0+0=0{,}5$; $x=1$: $1-2\cdot0+0=1$ (пик); $x=1{,}5$: $1{,}5-2\cdot0{,}5+0=0{,}5$; $x=3$: $3-2\cdot2+1=0$. Такие треугольные «шатры», расставленные плотно вдоль оси и с разной высотой, в сумме дают кусочно-линейную аппроксимацию любой непрерывной кривой — чем больше таких шатров (чем шире скрытый слой), тем точнее приближение к произвольной функции, включая гладкие кривые вроде синуса.

Пример 3 (существование — не то же самое, что практическая обучаемость). Теорема гарантирует, что подходящая сеть существует, но не говорит, что градиентный спуск (метод, которым сети обучаются на практике, урок 330) обязательно её найдёт. Для сложных функций может понадобиться экспоненциально большая ширина $N$ — теорема вообще не даёт для неё оценки, — а обучение такой гигантской сети может застревать в локальных минимумах, требовать непомерного количества данных или попросту не укладываться в разумное время. Именно из-за этого разрыва между «существует» и «практично найти» современные архитектуры почти никогда не строят как один экстремально широкий слой — вместо этого используют несколько слоёв умеренной ширины (углубляют, а не расширяют сеть), что мы подробнее разберём в следующих уроках блока.

Почему это важно

Теорема универсальной аппроксимации — это не про конкретную архитектуру и не про рецепт обучения, а про принципиальные возможности. Она отвечает на вопрос, который иначе выглядел бы чисто эмпирическим наблюдением («нейросети почему-то часто хорошо работают») строгим математическим фактом: сеть с одним скрытым слоем и нелинейной активацией в принципе способна выразить любую непрерывную зависимость между входом и выходом. Это фундамент, на котором строится доверие ко всему семейству нейросетевых моделей — от простого MLP этого урока до сверточных сетей и трансформеров, которые появятся дальше в курсе: все они лишь по-разному организуют один и тот же теоретически универсальный строительный блок.

Архитектура полносвязной сети и матричная форма

Интуиция

В многослойной сети каждый нейрон одного слоя соединён абсолютно со всеми нейронами предыдущего слоя — отсюда название «полносвязный слой» (англ. fully connected layer, также называемый dense layer). Вместо того чтобы расписывать вычисление каждого отдельного нейрона отдельной формулой, весь слой целиком удобно записать одной матричной операцией: умножение матрицы весов на вектор входа, прибавление вектора смещений и применение нелинейности поэлементно к результату.

Формула

Полносвязный (dense) слой. Пусть слой принимает $n$ чисел на входе и содержит $m$ нейронов. Матрица весов $W\in\mathbb{R}^{m\times n}$ (строка $j$ — веса $j$-го нейрона), вектор смещений $b\in\mathbb{R}^m$. Тогда

$$z = Wx + b, \qquad z_j = \sum_{i=1}^n W_{ji}x_i + b_j, \qquad a = f(z)$$

где $f$ — функция активации, применяемая к каждой координате $z_j$ независимо и одинаково.

Разбор примеров

Пример 1 (полный численный forward одного слоя, 3 входа → 4 нейрона). Пусть

$$W=\begin{pmatrix}0{,}2&-0{,}5&1{,}0\\0{,}1&0{,}3&-0{,}2\\-1{,}0&0{,}4&0{,}6\\0{,}0&0{,}7&-0{,}3\end{pmatrix},\quad b=\begin{pmatrix}0{,}1\\-0{,}2\\0{,}05\\0{,}3\end{pmatrix},\quad x=\begin{pmatrix}1\\2\\-1\end{pmatrix}$$

Считаем построчно: $z_1=0{,}2\cdot1+(-0{,}5)\cdot2+1{,}0\cdot(-1)+0{,}1=0{,}2-1{,}0-1{,}0+0{,}1=-1{,}7$; $z_2=0{,}1\cdot1+0{,}3\cdot2+(-0{,}2)\cdot(-1)-0{,}2=0{,}1+0{,}6+0{,}2-0{,}2=0{,}7$; $z_3=-1{,}0\cdot1+0{,}4\cdot2+0{,}6\cdot(-1)+0{,}05=-1{,}0+0{,}8-0{,}6+0{,}05=-0{,}75$; $z_4=0{,}0\cdot1+0{,}7\cdot2+(-0{,}3)\cdot(-1)+0{,}3=0+1{,}4+0{,}3+0{,}3=2{,}0$. Итого $z=(-1{,}7;\ 0{,}7;\ -0{,}75;\ 2{,}0)$. Применим ReLU поэлементно: $a=\mathrm{ReLU}(z)=(0;\ 0{,}7;\ 0;\ 2{,}0)$.

Пример 2 (счёт параметров на реалистичном примере — слой MNIST). Классический учебный датасет рукописных цифр MNIST состоит из изображений $28\times28$ пикселей, которые при подаче в полносвязный слой разворачивают в вектор из $784$ чисел. Если первый скрытый слой содержит $128$ нейронов, число обучаемых весов равно $784\times128=100\,352$, плюс $128$ смещений (по одному на нейрон) — итого $100\,480$ параметров только в одном этом слое. Уже на этом простом примере видно, откуда у современных сетей берутся миллионы и миллиарды параметров: каждый новый полносвязный слой умножает число входов на число нейронов.

Пример 3 (та же операция в коде — torch.nn.Linear). В PyTorch ровно эта матричная операция реализована слоем nn.Linear:

import torch
import torch.nn as nn

layer = nn.Linear(in_features=3, out_features=4)
x = torch.tensor([1.0, 2.0, -1.0])

z = layer(x)          # эквивалент W @ x + b из примера 1
a = torch.relu(z)      # поэлементная нелинейность применяется отдельно

Внутри nn.Linear хранит веса в виде матрицы формы (out_features, in_features) — то есть ровно (4, 3), как матрица $W$ из примера 1, — и вычисляет x @ W.T + b, что математически то же самое умножение матрицы на вектор с прибавлением смещения. Активация (torch.relu) в PyTorch — отдельный, самостоятельный вызов, а не часть nn.Linear, что прямо иллюстрирует вывод из примера 3 предыдущего раздела: линейный слой сам по себе линеен, нелинейность появляется только благодаря отдельно применяемой функции активации.

Почему это важно

Матричная запись $a=f(Wx+b)$ — не просто удобное сокращение записи, а буквально тот вычислительный блок, из которого построены все современные архитектуры глубокого обучения. Свёрточный слой — это тот же самый принцип с ограниченной (разреженной, локально связанной) матрицей весов; слой внимания в трансформере — тот же принцип с весами, которые динамически вычисляются самой сетью. Но в основании и того, и другого лежит ровно то умножение матрицы на вектор с последующей нелинейностью, которое ты только что посчитал вручную в примере 1. Понимание этого блока на уровне конкретных чисел — необходимая база для всего, что будет дальше в курсе.

Функции активации: сигмоида против ReLU

Интуиция

Раздел про XOR доказал, что нелинейность обязательна. Но какую именно нелинейность выбрать? Исторически первой и долгое время главной функцией активации была сигмоида — гладкая S-образная кривая, сжимающая любое число в интервал $(0,1)$. Однако у неё есть серьёзный практический недостаток, который стал очевиден при обучении по-настоящему глубоких сетей: на краях (при больших по модулю значениях входа) сигмоида почти плоская, а значит её производная почти равна нулю. Сегодня в подавляющем большинстве архитектур для скрытых слоёв используется ReLU — предельно простая функция, отсекающая отрицательные значения в ноль и пропускающая положительные без изменений, которая решает эту проблему для положительной части входа, но порождает собственную — нейроны иногда «умирают».

Формула

Сигмоида. $\sigma(z) = \dfrac{1}{1+e^{-z}}$, область значений $(0,1)$, производная $\sigma'(z) = \sigma(z)\bigl(1-\sigma(z)\bigr)$.

ReLU (Rectified Linear Unit, «выпрямленный линейный элемент»). $\mathrm{ReLU}(z) = \max(0,z)$, производная $\mathrm{ReLU}'(z) = \begin{cases}1, & z>0\\0, & z<0\end{cases}$ (в точке $z=0$ производная формально не определена, на практике полагается равной $0$ или $1$).

Разбор примеров

Пример 1 (затухающий градиент сигмоиды на краях). Посчитаем производную сигмоиды в нескольких точках. В нуле: $\sigma(0)=0{,}5$, $\sigma'(0)=0{,}5\cdot0{,}5=0{,}25$ — максимум производной. При $z=5$: $\sigma(5)\approx0{,}9933$, $\sigma'(5)=0{,}9933\cdot(1-0{,}9933)\approx0{,}0067$ — уже почти в $37$ раз меньше максимума. При $z=10$: $\sigma(10)\approx0{,}99995$, $\sigma'(10)\approx0{,}99995\cdot0{,}00005\approx0{,}0000454$ — меньше максимума почти в $5\,500$ раз. Это и есть затухающий градиент (vanishing gradient): для входов, далёких от нуля, сигмоида почти не реагирует на изменение входа, а значит при обучении (урок 330) сигнал ошибки, проходя через такой нейрон, почти обнуляется — и чем больше в сети слоёв с сигмоидой, тем сильнее перемножение множества таких маленьких чисел гасит градиент до состояния, в котором обучение практически останавливается.

Пример 2 (умирающий ReLU). Возьмём нейрон с весами $w=(-3,-2)$ и смещением $b=-1$, на вход которому всегда подаются неотрицательные числа $x_1,x_2\ge0$ (обычная ситуация: например, входные признаки уже нормализованы в диапазон $[0,1]$, либо это выход предыдущего ReLU-слоя, который по определению не может быть отрицательным). Тогда для любых допустимых входов $z=-3x_1-2x_2-1\le-1<0$ — значение $z$ всегда строго отрицательно, значит $\mathrm{ReLU}(z)=0$ на всём датасете без единого исключения, а производная $\mathrm{ReLU}'(z)=0$ тоже всегда равна нулю. При обучении градиент, проходящий через этот нейрон, обнуляется навсегда: веса этого нейрона больше никогда не обновятся, каким бы длинным ни было дальнейшее обучение, — нейрон буквально «умер» и перестал участвовать в работе сети.

Пример 3 (сравнительная таблица значений).

$z$ $-5$ $-2$ $-1$ $0$ $1$ $2$ $5$
$\sigma(z)$ $0{,}0067$ $0{,}1192$ $0{,}2689$ $0{,}5$ $0{,}7311$ $0{,}8808$ $0{,}9933$
$\mathrm{ReLU}(z)$ $0$ $0$ $0$ $0$ $1$ $2$ $5$

Таблица показывает разницу наглядно: сигмоида остаётся ограниченной в $(0,1)$ на всём диапазоне и никогда не достигает точно $0$ или $1$, а лишь сколь угодно близко подходит к этим значениям — расплачиваясь за это почти нулевым градиентом на краях. ReLU не ограничена сверху (значение растёт линейно вместе с входом), а её градиент равен ровно $1$ для любого положительного входа, каким бы большим он ни был, — никакого затухания на положительной стороне. Цена — вся отрицательная половина области определения даёт ровно нулевой выход и нулевой градиент.

Почему это важно

Именно этот компромисс объясняет, почему за последнее десятилетие ReLU практически полностью вытеснила сигмоиду из скрытых слоёв большинства современных архитектур: для глубоких сетей с десятками и сотнями слоёв затухающий градиент сигмоиды делает обучение крайне медленным или вовсе невозможным, тогда как ReLU сохраняет полноценный градиент для всех активных нейронов. Сигмоида при этом не исчезла — она осталась стандартным выбором для выходного слоя в задачах бинарной классификации, где выход должен интерпретироваться как вероятность в диапазоне $(0,1)$. Риск умирающих нейронов у ReLU — не повод от неё отказываться, а повод контролировать инициализацию весов и скорость обучения; более подробный разбор вариантов активации (включая способы борьбы с умирающими ReLU) — тема следующего урока 329.

Forward pass: как сеть вычисляет предсказание

Интуиция

Прежде чем сеть сможет чему-либо научиться, она должна уметь вычислить хоть какой-то ответ на заданный вход — пусть даже совершенно неправильный при случайных начальных весах. Это вычисление называется прямым проходом (forward pass): вход последовательно проходит через каждый слой сети, на каждом слое вычисляется взвешенная сумма и применяется активация, и так до самого последнего, выходного слоя. Без forward pass не с чем сравнивать истинный ответ и не от чего отталкиваться, вычисляя ошибку, — поэтому это первый, обязательный шаг, предшествующий любому обучению.

Формула

Прямой проход (forward pass). Для сети из $L$ слоёв обозначим вход как $a^{(0)}=x$. Для каждого слоя $l=1,\dots,L$ последовательно вычисляется

$$z^{(l)} = W^{(l)}a^{(l-1)} + b^{(l)}, \qquad a^{(l)} = f^{(l)}\bigl(z^{(l)}\bigr)$$

Итоговое предсказание сети — выход последнего слоя: $\hat y = a^{(L)}$.

Разбор примеров

Пример 1 (полная трассировка сети с двумя скрытыми слоями). Возьмём сеть архитектуры $2\to3\to2\to1$: два входа, скрытый слой из трёх ReLU-нейронов, второй скрытый слой из двух ReLU-нейронов, выходной слой с сигмоидой. Веса:

$$W^{(1)}=\begin{pmatrix}1&-1\\0{,}5&0{,}5\\-2&1\end{pmatrix},\ b^{(1)}=\begin{pmatrix}0\\-0{,}2\\0{,}3\end{pmatrix},\quad W^{(2)}=\begin{pmatrix}1&0&-1\\0{,}5&-0{,}5&2\end{pmatrix},\ b^{(2)}=\begin{pmatrix}0{,}1\\-0{,}3\end{pmatrix},\quad W^{(3)}=(2,-1),\ b^{(3)}=-0{,}5$$

Вход $x=(2,1)$.

Слой 1: $z^{(1)}_1=1\cdot2+(-1)\cdot1+0=1$; $z^{(1)}_2=0{,}5\cdot2+0{,}5\cdot1-0{,}2=1{,}3$; $z^{(1)}_3=-2\cdot2+1\cdot1+0{,}3=-2{,}7$. После ReLU: $a^{(1)}=(1;\ 1{,}3;\ 0)$.

Слой 2: $z^{(2)}_1=1\cdot1+0\cdot1{,}3+(-1)\cdot0+0{,}1=1{,}1$; $z^{(2)}_2=0{,}5\cdot1+(-0{,}5)\cdot1{,}3+2\cdot0-0{,}3=0{,}5-0{,}65+0-0{,}3=-0{,}45$. После ReLU: $a^{(2)}=(1{,}1;\ 0)$.

Слой 3 (выход): $z^{(3)}=2\cdot1{,}1+(-1)\cdot0-0{,}5=2{,}2-0{,}5=1{,}7$. После сигмоиды: $\hat y=\sigma(1{,}7)\approx0{,}8456$.

Три слоя, три последовательных матричных умножения плюс нелинейности — и в итоге получено единственное число $\hat y\approx0{,}8456$, которое сеть выдаёт как предсказание для входа $(2,1)$.

Пример 2 (вычислительная стоимость forward pass — счёт операций). Для сети из примера 1 посчитаем число умножений с накоплением (MAC, англ. multiply-accumulate): слой 1 — $3$ нейрона $\times$ $2$ входа $=6$ операций; слой 2 — $2\times3=6$; слой 3 — $1\times2=2$. Итого $14$ операций умножения-сложения за один forward pass для одного примера. Масштабируем на реалистичную сеть для MNIST архитектуры $784\to128\to64\to10$: $784\times128 + 128\times64 + 64\times10 = 100\,352+8\,192+640=109\,184$ операций MAC на каждое отдельное изображение. При обработке пакета (batch) из тысячи изображений это уже больше ста миллионов операций — именно поэтому эффективность forward pass (то есть время работы уже обученной модели, инференс) — отдельная инженерная задача, важная не менее, чем скорость самого обучения.

Пример 3 (forward pass как обязательное условие для backpropagation). Чтобы вычислить ошибку сети, нужно сравнить предсказание $\hat y$ с истинным ответом $y$ — но $\hat y$ появляется только в результате forward pass, описанного выше. Более того, алгоритм backpropagation (урок 330), которым сеть обучается, будет применять цепное правило дифференцирования и использовать для этого именно те промежуточные значения $z^{(l)}$ и $a^{(l)}$, которые вычислены на forward pass, — например, чтобы посчитать градиент по весам скрытого слоя, потребуется знать активацию $a^{(1)}$, полученную именно на этом проходе. Иными словами, каждое значение, вычисленное сегодня вручную в примере 1, в следующем уроке окажется ровно тем «сырьём», из которого строится обучение сети.

Почему это важно

Forward pass — это именно та операция, которая происходит каждый раз, когда уже обученная модель делает предсказание в реальном приложении: распознаёт объект на фотографии, оценивает кредитный риск, генерирует следующее слово в тексте. В коде это буквально один вызов — model(x) в PyTorch или model.predict(x) в других библиотеках — но за этим вызовом стоит именно та последовательность матричных умножений и нелинейностей, которую ты сегодня прошёл вручную. И одновременно это первый обязательный шаг перед любым обучением: без forward pass нет предсказания, без предсказания нет ошибки, без ошибки нечего распространять назад по сети — именно с этого мы продолжим в следующих уроках блока.

Практика: 30 заданий

Базовые задания (1–10)

Задание 1: Вычисли $\mathrm{ReLU}(-3)$, $\mathrm{ReLU}(0)$, $\mathrm{ReLU}(5)$.


Задание 2: Вычисли $\sigma(0)$ для сигмоиды $\sigma(z)=\dfrac{1}{1+e^{-z}}$.


Задание 3: Нейрон с весами $w=(2,-1)$, смещением $b=0{,}5$, вход $x=(1,3)$. Найди $z$ и $\mathrm{ReLU}(z)$.


Задание 4: Для нейрона из задания 3 ($z=-0{,}5$) найди выход с активацией сигмоида вместо ReLU.


Задание 5 (машинное обучение): Почему для решения XOR одиночному персептрону нужен хотя бы один скрытый слой?


Задание 6: Для всех четырёх комбинаций $x_1,x_2\in\{0,1\}$ вычисли $(x_1-x_2)^2$ и сравни с XOR.


Задание 7: Посчитай число обучаемых параметров (веса + смещения) полносвязного слоя с $10$ входами и $5$ нейронами.


Задание 8: Полносвязный слой с матрицей $W=\begin{pmatrix}1&0\\0&1\end{pmatrix}$, $b=(0,0)$, вход $x=(3,-2)$. Найди $z$, затем $\mathrm{ReLU}(z)$.


Задание 9: Дан вектор $z=(-2,0,3)$. Найди $\mathrm{ReLU}(z)$ покоординатно.


Задание 10 (машинное обучение): Верно ли, что глубокая сеть из линейных слоёв без функций активации эквивалентна одному линейному слою? Обоснуй.

Средние задания (11–20)

Задание 11: $W=\begin{pmatrix}1&2&-1\\0&1&1\end{pmatrix}$, $b=(0,-1)$, $x=(1,0,2)$. Найди $z=Wx+b$.


Задание 12: Примени ReLU к результату задания 11.


Задание 13: Найди производную сигмоиды в точке $z=2$.


Задание 14: Скрытый нейрон с весами $w_1=1$, $w_2=-1$, $b=0$ и квадратичной активацией $h=z^2$. Найди $h$ для входа XOR $(1,0)$.


Задание 15 (машинное обучение): ReLU-нейрон с весами $w=(-1,-2)$, $b=-3$ получает на вход только неотрицательные $x_1,x_2\ge0$ (выход предыдущего ReLU-слоя). Жив ли этот нейрон?


Задание 16: Слой со входом $a=(1,-1,2)$, весами $W=\begin{pmatrix}2&0&-1\\1&-1&1\end{pmatrix}$, $b=(1,0)$. Найди $z$, затем $\mathrm{ReLU}(z)$.


Задание 17: Используя результат задания 16 как вход выходного нейрона с весами $v=(1,-2)$, смещением $c=0{,}5$ и сигмоидой на выходе, найди итоговый $y$.


Задание 18 (машинное обучение): Сколько всего обучаемых параметров в сети архитектуры $784\to256\to10$ (два полносвязных слоя, с учётом смещений)?


Задание 19: Проверь на всех четырёх комбинациях входов, что сеть $h=(x_1-x_2)^2$, $y=h$ точно решает XOR.


Задание 20 (машинное обучение): Почему теорема универсальной аппроксимации не гарантирует, что обучение (градиентный спуск) найдёт нужные веса?

Продвинутые задания (21–30)

Задание 21: Сеть: слой 1 $W_1=\begin{pmatrix}1&1\\1&-1\end{pmatrix}$, $b_1=(0,0)$, ReLU; выходной слой $v=(1,1)$, $c=-1$, сигмоида. Вход $x=(2,-1)$. Найди итоговый $y$.


Задание 22 (машинное обучение): Почему одного скрытого ReLU-нейрона (без второго, как в примере урока $h_1-2h_2$) недостаточно, чтобы решить XOR?


Задание 23: Сеть архитектуры $2\to3\to1$. Посчитай общее число параметров.


Задание 24: Аппроксимация ступеньки двумя сигмоидами: $\mathrm{bump}(x)=h[\sigma(k(x-a))-\sigma(k(x-b))]$, $a=1$, $b=4$, $h=2$, $k=8$. Найди приближённые значения в точках $x=0$, $x=2{,}5$, $x=6$.


Задание 25 (машинное обучение): При обучении MLP ты замечаешь, что 40% ReLU-нейронов первого слоя выдают ровно $0$ на всём датасете. Что произошло и как это исправить?


Задание 26: Все веса выходного сигмоидного нейрона умножили на $10$, смещение не меняли. Как это повлияет на выход при больших по модулю входах и на градиент?


Задание 27 (машинное обучение): Почему в PyTorch/TensorFlow слой называется «Linear»/«Dense», если вся сеть в целом нелинейна?


Задание 28: Треугольная «шатровая» функция $\mathrm{hat}(x)=\mathrm{ReLU}(x)-2\,\mathrm{ReLU}(x-1)+\mathrm{ReLU}(x-2)$. Найди значения в точках $x=-1$, $x=0{,}5$, $x=1$, $x=1{,}5$, $x=3$.


Задание 29 (машинное обучение): Почему теорема универсальной аппроксимации сама по себе не объясняет, зачем на практике используют глубокие (многослойные), а не просто очень широкие однослойные сети?


Задание 30: Сеть: слой 1 $W_1=\begin{pmatrix}2&-1\\0&3\end{pmatrix}$, $b_1=(-1,0)$, ReLU; выходной слой $v=(1,-1)$, $c=0{,}2$, ReLU на выходе. Вход $x=(1,-2)$. Найди итоговый выход.

Частые ошибки

  • Думают, что просто увеличение числа слоёв без функций активации уже даёт более мощную нелинейную модель. Как показано в примере 3 раздела про XOR, композиция линейных слоёв всегда сворачивается в один линейный слой — без нелинейной активации между слоями глубина не добавляет модели никакой выразительной силы, сколько бы слоёв ни было добавлено (задание 10).

  • Считают, что теорема универсальной аппроксимации гарантирует нахождение нужных весов при обучении. Теорема доказывает лишь существование подходящих весов, но ничего не говорит о том, найдёт ли их градиентный спуск за разумное время и не потребуется ли для этого неприемлемо большая ширина скрытого слоя (задание 20).

  • Используют сигмоиду по умолчанию во всех скрытых слоях глубокой сети. Как показано в разделе про функции активации, сигмоида быстро приводит к затухающим градиентам вдали от нуля, что особенно губительно в глубоких сетях. Сегодня стандартный выбор для скрытых слоёв — ReLU, а сигмоиду оставляют преимущественно для выходного слоя, где нужна интерпретация как вероятность.

  • Не проверяют «умирающие» ReLU-нейроны и удивляются, почему часть сети как будто не обучается. Если у нейрона предвызов $z$ отрицателен на всём датасете, его выход и градиент навсегда равны нулю (задания 15, 25) — это стоит диагностировать напрямую, отслеживая долю нулевых активаций по слоям.

  • Путают полносвязный (Linear/Dense) слой с целой нейросетью, считая, что он сам по себе делает что-то нелинейное. Как разобрано в задании 27, сам слой действительно линеен — нелинейность появляется только благодаря отдельно применяемой функции активации после него.

  • Считают forward pass (прямой проход) финальным этапом работы с сетью, как будто раз сеть выдала число — на этом всё закончено. Прямой проход — лишь первый шаг: чтобы сеть чему-то научилась, нужен ещё этап сравнения предсказания с истинным ответом и обратного распространения ошибки, которому посвящены следующие уроки блока.

Главное запомнить

  • Один скрытый слой с нелинейной активацией решает задачу XOR, неразрешимую для одиночного персептрона (урок 327) — линейная граница персептрона превращается в границу, собранную из нескольких линий.

  • Нелинейность между слоями обязательна: без неё любая глубина сети математически сворачивается в один линейный слой (пример 3 раздела про XOR) — композиция аффинных функций снова аффинна.

  • Теорема универсальной аппроксимации (Цибенко, 1989; Хорник, 1991): сеть с одним достаточно широким скрытым слоем и нелинейной активацией способна приблизить любую непрерывную функцию на компактном множестве сколь угодно точно.

  • Теорема доказывает лишь существование подходящих весов, не даёт способа их найти и не ограничивает требуемую ширину слоя — именно поэтому на практике используют глубокие, а не экстремально широкие сети.

  • Полносвязный слой целиком вычисляется одной матричной операцией: $a=f(Wx+b)$, где $W$ — матрица весов, $b$ — вектор смещений, $f$ применяется поэлементно.

  • Сигмоида $\sigma(z)=1/(1+e^{-z})$ страдает от затухающих градиентов на краях (производная стремится к нулю при больших $|z|$); ReLU $\max(0,z)$ решает эту проблему для положительных входов ценой риска «умирания» нейронов при постоянно отрицательных входах.

  • Сегодня ReLU — активация по умолчанию для скрытых слоёв в большинстве современных архитектур; сигмоида чаще используется на выходе для получения вероятностей.

  • Forward pass — последовательное вычисление $z^{(l)}=W^{(l)}a^{(l-1)}+b^{(l)}$, $a^{(l)}=f(z^{(l)})$ по всем слоям сети — обязательный первый шаг перед любым обучением: без предсказания $\hat y$ нечего сравнивать с истинным ответом.

  • Каждый нейрон слоя соединён со всеми нейронами предыдущего слоя (полносвязность) — этот же строительный блок torch.nn.Linear плюс активация лежит в основе всех современных архитектур, включая свёрточные сети и трансформеры.

  • Обучение весов сети через алгоритм backpropagation (обратное распространение ошибки) — тема следующих уроков блока; сегодняшний forward pass (прямой проход) лишь фиксирует, как сеть вычисляет предсказание при уже заданных весах.

Связь с темами курса

Этот урок напрямую продолжает урок 327: там персептрон упёрся в строгое ограничение — невозможность решить линейно неразделимую задачу XOR, а сегодня это ограничение снято добавлением скрытого слоя с нелинейной активацией. Связь с уроком 326 тоже прямая: там были введены базовые понятия искусственного нейрона и функций активации в общих чертах, сегодня эти понятия собраны в полноценную многослойную архитектуру с точной матричной записью и строгим теоретическим обоснованием (теоремой универсальной аппроксимации) того, почему такая архитектура вообще работает.

Матричная форма записи слоя $a=f(Wx+b)$ опирается на умножение матрицы на вектор из курса линейной алгебры, а сравнение сигмоиды и ReLU по поведению производной на краях напрямую связано с проблемой затухающих и взрывающихся градиентов, которая в полную силу проявится при разборе алгоритма обратного распространения ошибки.

Урок 329 продолжит тему функций активации значительно подробнее — там будут разобраны гиперболический тангенс, Leaky ReLU и другие современные варианты, решающие проблему умирающих нейронов, обозначенную сегодня лишь пунктиром. А урок 330 — прямое и самое важное продолжение сегодняшнего материала: там будет показано, как именно вычисляются градиенты по весам каждого слоя сети через backpropagation (обратное распространение ошибки), чтобы сеть не просто вычисляла forward pass (прямой проход) с фиксированными весами (как сегодня), а подбирала эти веса самостоятельно на основе данных.

Интересные факты

  • Алгоритм backpropagation (обратное распространение ошибки) был переоткрыт независимо как минимум трижды за двенадцать лет — Полом Вербосом в диссертации 1974 года, Дэвидом Паркером и Яном Лекуном в 1985 году — прежде чем закрепился в науке под именами Румельхарта, Хинтона и Уильямс благодаря их статье 1986 года в Nature.

  • Марвин Минский, чья книга 1969 года «Перцептроны» считается главной причиной пятнадцатилетнего затишья в исследованиях нейросетей, сам прекрасно понимал, что многослойные сети теоретически способны преодолеть доказанные им ограничения одиночного персептрона, — просто на тот момент не существовало эффективного способа обучить веса скрытых слоёв.

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

  • Современные языковые модели с миллиардами параметров в своей вычислительной основе по-прежнему построены из того же самого блока $a=f(Wx+b)$, который разобран в этом уроке, — просто повторённого и скомбинированного огромное количество раз; принципиально нового математического строительного блока со времён статьи 1986 года так и не появилось.

Лайфхаки

  • При отладке сети, которая «застряла» и не обучается, в первую очередь проверяй статистику активаций по слоям (долю нулевых выходов для ReLU, степень насыщения для сигмоиды) — это быстро диагностирует затухающий градиент или умирающие нейроны ещё до анализа кривой ошибки.

  • Прежде чем запускать обучение на реальных данных, прогони forward pass (прямой проход) вручную на крошечном примере с заранее зафиксированными весами (как в примерах этого урока) — если числа в коде не совпадают с расчётом на бумаге, ошибка в архитектуре сети, а не в данных или гиперпараметрах.

  • Выбирай ReLU по умолчанию для скрытых слоёв, если нет специальной причины иначе (сигмоида в глубоких сетях почти всегда проигрывает из-за затухающего градиента), и оставляй сигмоиду для выходного слоя, где действительно нужна интерпретация выхода как вероятности в $(0,1)$.

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

  • При подсчёте параметров сети не забывай отдельно прибавлять смещения по каждому слою — забытый bias-вектор часто становится источником ошибки при подсчёте размера модели.

  • Используй форму хранения весов nn.Linear (out_features, in_features) как быструю проверку архитектуры: если формы матриц соседних слоёв не согласуются, ошибка обнаружится ещё до первого обучающего шага, а не где-то глубоко внутри цикла обучения.

Сегодня ты закрыл главный теоретический пробел, оставшийся после урока о персептроне: одна прямая линия — это лишь частный, самый простой случай того, что способна выучить нейронная сеть. Добавь скрытый слой и нелинейность — и та же самая по духу конструкция превращается в универсальный аппроксиматор, способный в принципе приблизить любую непрерывную зависимость. Ты уже умеешь вручную посчитать forward pass через несколько слоёв и объяснить, почему без нелинейности глубина бесполезна, а с ней — почти всесильна. Следующий шаг — научить сеть находить эти веса самостоятельно, а не подставлять их вручную, как сегодня. Именно этим мы и займёмся дальше в блоке.

Понял тему? Закрепи в боте! 🚀

Попрактикуйся на задачах и получи персональные рекомендации от AI

💪 Начать тренировку
💬 Есть вопрос? Спроси бота!