Лекция CS231N: как устроены нейросети и алгоритм обратного распространения

Stanford Online 42,8 тыс. 1 ч 16 мин 11 мин 02.09.2025
Главное

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

🔄 Повторение пройденного: функции потерь и оптимизация 1:01

Лекция традиционно начинается с краткого обзора материала прошлых занятий. Ранее были сформулированы целевые функции, известные как функции потерь (loss functions), а также концепция регуляризации. Все вычисления строились вокруг пар данных $(X, Y)$ и линейной функции подсчета очков (scoring function).

Лектор подчеркивает, что Softmax не является единственной доступной функцией потерь. Хотя она широко применяется в глубоком обучении для задач классификации, существуют и другие альтернативы. В качестве примера приводится функция Hinge Loss (исторически известная как SVM loss), которая подробно рассматривалась в дополнительных материалах ко второй лекции.

В отличие от Softmax, Hinge Loss не превращает полученные баллы в вероятности. Она мотивирует сеть увеличивать оценку правильного класса $s_{y_i}$ по сравнению с оценками других классов $s_j$ как минимум на величину заданного зазора (margin), равного единице. Если это условие нарушается, штраф растет пропорционально величине нарушения.

Для поиска оптимальных параметров весов $W$ используется метафора ландшафта потерь (loss landscape), представляющего собой огромную долину. Каждая точка в этой долине соответствует определенному набору весов, и задача оптимизации сводится к поиску минимума. Ключом к решению является вычисление градиента функции потерь по отношению к весам $\nabla_W L$ и пошаговое движение в противоположном градиенту направлении — так устроен алгоритм градиентного спуска (Gradient Descent). Шаг оптимизации регулируется размером шага (step size) или скоростью обучения (learning rate).

В реальной практике вычисление точного аналитического градиента на всем датасете слишком затратно, поэтому данные разбиваются на мини-батчи (mini-batches). Типичный размер батча составляет 32, 64, 128 или 256 примеров. На основе этих подвыборок рассчитываются градиенты для шагов оптимизации.

Помимо классического стохастического градиентного спуска (SGD), лектор напоминает о существовании продвинутых оптимизаторов:

Важным аспектом является настройка расписания скорости обучения (learning rate scheduling). В некоторых оптимизаторах начинают с большого шага, постепенно уменьшая его на определенный коэффициент. Однако в современных алгоритмах, таких как Adam и его вариациях, ручное снижение скорости обучения зачастую не требуется, поскольку этот механизм уже встроен внутрь самого оптимизатора.

⚡ Переход к нейронным сетям и сила нелинейности 9:53

Самая базовая нейросеть представляет собой простую линейную функцию произведения весов на входные данные $f = W \cdot x$. В такой архитектуре есть лишь один слой. Входные данные имеют размерность $D$ (количество признаков), а выходные — размерность $C$ (количество предсказываемых классов).

Чтобы построить двухслойную нейронную сеть, вводится второй набор весов $W_2$. Он применяется к результату первого слоя $W_1 \cdot x$. При этом задается скрытый слой размерностью $H$, определяющий число скрытых нейронов. В формуле также используется функция max, которая выполняет критически важную роль — создание нелинейности между линейными трансформациями первого и второго слоев.

Полная формула принимает вид:

$$f = W_2 \cdot \max(0, W_1 \cdot x)$$

Для простоты изложения в формулах опускается вектор смещения (bias), хотя в реальной практике он обязательно добавляется для обеспечения полноты математической модели.

Без нелинейной операции max построение многослойной сети теряет смысл. Если убрать ее, выражение превратится в $W_2 \cdot W_1 \cdot x$. Матричное произведение двух весов можно легко заменить единой матрицей $W_3$, и вся сеть схлопнется обратно в обычную линейную функцию.

Нелинейность необходима, так как в реальном мире огромное количество данных невозможно разделить одной прямой линией; требуется нелинейное преобразование признакового пространства. Например, перевод декартовых координат $(x, y)$ в полярные $(r, \theta)$ делает классы разделимыми с помощью простой линии.

В литературе сети, где выполняются только последовательные матричные умножения и нелинейные активации слой за слоем, называют полносвязными сетями (fully connected networks) или многослойными перцептронами (MLP). Разработчики могут наслаивать их друг на друга, создавая огромные глубокие архитектуры.

С увеличением количества слоев сеть получает возможность формировать многоуровневые шаблоны. Если линейный классификатор строит всего 10 шаблонов для 10 классов изображений, то скрытый слой, например, со 100 нейронами, позволяет обучить 100 промежуточных шаблонов. На интуитивном уровне это дает сети способность распознавать не объект целиком, а его отдельные составные части (например, глаза, которые есть и у птиц, и у кошек, и у собак).

🧪 Зоопарк функций активации: от ReLU до современных вариантов 17:37

Функция нелинейности в терминологии нейросетей называется функцией активации (activation function). Она играет стержневую роль в обучении моделей. Наиболее популярным выбором долгое время остается ReLU (Rectified Linear Unit) — выпрямленный линейный элемент. Однако у ReLU есть существенный недостаток: она может приводить к появлению «мертвых нейронов», так как полностью зануляет любые отрицательные входные значения.

Чтобы решить проблему «отмирания» нейронов, исследователи разработали альтернативные варианты функций активации:

Классические функции, такие как сигмоида (Sigmoid) и гиперболический тангенс (Tanh), сжимают выходные значения в очень узкий диапазон. По словам лектора, это часто приводит к затуханию градиентов (vanishing gradients), поэтому их практически никогда не используют во внутренних скрытых слоях. Сигмоиду и тангенс обычно оставляют для финальных слоев, где необходимо выполнить бинаризацию выходов или получить вероятности.

Отвечая на вопрос аудитории о принципах выбора функции активации под новую задачу, лектор поясняет, что этот процесс носит преимущественно эмпирический характер. На практике инженеры обычно начинают со стандартной ReLU или берут те функции активации, которые уже успешно зарекомендовали себя в аналогичных архитектурах (например, в CNN или трансформерах). Главным общим свойством всех этих функций является обеспечение нелинейности, дифференцируемость и содействие более быстрой сходимости сети за счет гладкости или центрирования данных.

📐 Размер нейросети и тонкости регуляризации 26:06

Реализация двухслойной нейросети на Python занимает всего около 20 строк кода. В коде фиксируются параметры: количество образцов $N$, размерность входа $D_{in}$, количество нейронов в скрытом слое $H$ и размерность выхода $D_{out}$. Процесс обучения включает прямой проход (forward pass) для расчета предсказаний и вычисления функции потерь, а также обратный проход (backward pass) для расчета аналитических градиентов и обновления весов методом градиентного спуска.

Количество скрытых нейронов напрямую определяет емкость (capacity) модели: чем больше нейронов, тем более сложные и детальные разделяющие границы способна строить сеть. Избыточная емкость может приводить к переобучению (overfitting), когда сеть просто зазубривает обучающую выборку и теряет способность к генерализации на новых данных.

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

Параметр $\lambda$ управляет тем, какой вклад штраф за величину весов вносит в общую функцию потерь. Чем выше значение $\lambda$, тем жестче ограничения, накладываемые на веса $W$, и тем меньше у них свободы. Слишком сильная регуляризация делает разделяющие границы избыточно простыми и размытыми, что приводит к недообучению (underfitting) модели.

Оптимальный регуляризатор всегда ищет баланс между минимизацией ошибки предсказания и удержанием весов в разумных пределах. Поиск идеального количества слоев и нейронов всегда завязан на проведение экспериментов и анализ аналогичных архитектур, созданных для похожих типов данных.

🧠 Биологические аналогии: вдохновение, а не точная копия 35:47

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

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

Тем не менее, биологические нейроны устроены несопоставимо сложнее, чем их математические абстракции. Искусственные нейросети организуются в строгие регулярные структуры и слои главным образом для обеспечения высокой вычислительной эффективности при программной реализации на компьютерах. Лектор отдельно предостерегает студентов от увлечения глубокими аналогиями с работой человеческого мозга, подчеркивая сугубо прикладной математический характер обучаемых моделей.

📊 Вычислительные графы и концепция Backpropagation 39:01

В полносвязных сетях оптимизация параметров $W_1$ и $W_2$ требует вычисления частных производных функции потерь $L$ по отношению к этим весам. Считать аналитические градиенты вручную на бумаге — занятие крайне утомительное и подверженное ошибкам, сопряженное с громоздкими матричными вычислениями. Любое минимальное изменение в архитектуре или функции потерь вынуждает инженера пересчитывать все формулы заново, что делает ручной подход нежизнеспособным при усложнении моделей.

Эффективным решением этой проблемы стали вычислительные графы (computational graphs) и алгоритм обратного распространения ошибки (backpropagation). Вычислительный граф раскладывает сложную функцию на цепочку простых последовательных шагов, где на каждом узле выполняется элементарная математическая операция, а финальным выходом является значение потерь. К графу подходят входные данные $X$ и параметры весов $W$, которые перемножаются для вычисления финального счета, а затем суммируются со штрафом регуляризатора.

Ручной расчет производных для комплексных систем, таких как нейронные машины Тьюринга (Neural Turing Machines), работающие с последовательными данными, был бы абсолютно невозможен без автоматизации через граф.

Для демонстрации работы алгоритма лектор приводит простейший пример функции:

$$f(x, y, z) = (x + y) \cdot z$$

Пусть заданы начальные значения: $x = -2, y = 5, z = -4$. Процесс вычислений разбивается на этапы. Сначала выполняется операция сложения, результат которой обозначается промежуточной переменной $q = x + y$. В числовом выражении $q = -2 + 5 = 3$.

Локальные частные производные для этого узла тривиальны: $\frac{\partial q}{\partial x} = 1$ и $\frac{\partial q}{\partial y} = 1$. Следующим шагом выполняется умножение: $f = q \cdot z$, что дает итоговое значение $3 \cdot (-4) = -12$. Локальные производные этого узла также легко находятся через правила алгебры: $\frac{\partial f}{\partial q} = z = -4$ и $\frac{\partial f}{\partial z} = q = 3$.

🔗 Пошаговый разбор: локальные градиенты и цепное правило 46:28

Чтобы найти производные финальной функции $f$ по отношению ко всем трем исходным переменным $x, y, z$, алгоритм обратного распространения начинает движение с самого конца сети, двигаясь в обратную сторону. Это рекурсивный процесс. Производная функции по отношению к самой себе всегда равна единице: $\frac{\partial f}{\partial f} = 1$.

Двигаясь назад, мы первыми встречаем переменные $z$ и $q$, напрямую связанные с выходом. Градиент для $z$ равен локальной производной, помноженной на единицу, то есть $\frac{\partial f}{\partial z} = q = 3$. Аналогично, градиент для промежучного узла $q$ составляет $\frac{\partial f}{\partial q} = z = -4$.

Сложнее обстоит дело с переменными $x$ и $y$, которые не имеют прямой связи с финальным выходом $f$. Здесь в силу вступает математическое цепное правило (chain rule), позволяющее расщепить вычисление через промежуточную переменную $q$:

$$\frac{\partial f}{\partial y} = \frac{\partial f}{\partial q} \cdot \frac{\partial q}{\partial y}$$

Лектор вводит два ключевых термина, обязательных для понимания backpropagation:

Для переменной $y$ входящий градиент равен $-4$ (значение $\frac{\partial f}{\partial q}$), а локальный градиент равен $1$ (значение $\frac{\partial q}{\partial y}$). Перемножая их, мы получаем финальный исходящий (даунстрим) градиент, равный $-4$.

Точно такая же логика применяется к переменной $x$: локальный градиент $1$ умножается на входящий градиент $-4$, давая на выходе $-4$.

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

🛠️ Модульный подход и типовые математические «ворота» 1:00:47

На основе анализа элементарных операций лектор выделяет несколько устойчивых паттернов поведения узлов (или «ворот», gates) в вычислительном графе, которые полезно запомнить:

Этот паттерн позволяет инженерам создавать чистые модульные программные API с четко определенными методами forward() и backward() для каждого оператора. Метод forward вычисляет выходные значения и кэширует (запоминает) входные данные, так как они понадобятся для вычисления локальных градиентов на обратном пути.

Метод backward принимает входящий градиент и возвращает производные по входам. Именно по такой модульной схеме построены современные библиотеки глубокого обучения, включая PyTorch.

🧮 Масштабирование алгоритма до векторов и матриц 1:04:31

Все рассмотренные ранее примеры оперировали скалярными величинами, однако реальные нейросети обрабатывают данные в виде векторов, матриц и многомерных тензоров. Если в скалярном режиме производная показывает, как изменится число $y$ при минимальном сдвиге числа $x$, то при переходе к векторам размерностью $N$ градиент функции по отношению к вектору сам становится вектором аналогичной длины. В случае отображения вектора в вектор производные выстраиваются в многомерную матрицу Якоби (Jacobian).

При работе с мини-батчами размерностью, например, 64 и скрытыми слоями с размерностью признаков 4096, попытка рассчитать и сохранить матрицу Якоби в явном виде приведет к катастрофической нехватке памяти. Для одного-единственного матричного умножения размер такой промежуточной матрицы превысил бы 256 ГБ.

Чтобы избежать вычисления гигантских разреженных матриц Якоби, в backward-методах библиотек используются оптимизированные правила матричного умножения. Зная, как именно элементы влияют друг на друга, операцию можно свести к двум лаконичным формулам.

Исходящий градиент по отношению к матрице входов $X$ рассчитывается через умножение входящего градиента на транспонированную матрицу весов $W$. Градиент по отношению к весам $W$ находится через перемножение транспонированной матрицы $X$ на входящий градиент. Этот элегантный математический аппарат позволяет эффективно обучать нейросети колоссальных масштабов.

💬 Цитаты

«Softmax не является единственной доступной функцией потерь, хотя она широко применяется в глубоком обучении.»

Лектор курса CS231N 02:28

«Никогда не используйте размер нейросети в качестве регуляризатора.»

Лектор курса CS231N 29:48

«Каждый узел вычислительного графа работает изолированно и модульно, получая апстрим-градиент.»

Лектор курса CS231N 51:55
👥 Спикер
📖 Термины
Backpropagation
Алгоритм обратного распространения ошибки, используемый для вычисления градиентов весов в нейросети.
ReLU
Популярная нелинейная функция активации, которая возвращает ноль для отрицательных входов и само число для положительных.
Вычислительный граф
Графическое представление математических операций, упрощающее пошаговый расчет производных по цепному правилу.
Матрица Якоби
Матрица, составленная из всех частных производных первого порядка для векторной функции.
📊 Цифры
⚖️ Другая сторона
Искусственный интеллект CS231N обратное распространение ошибки вычислительные графы