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

ResNet и skip connections

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

ResNet и skip connections 🧬

В прошлом уроке ты прошёл путь от LeNet до VGGNet — путь, на котором главным рецептом успеха было «сделай сеть глубже». LeNet — пять слоёв, AlexNet — восемь, VGG-19 — девятнадцать, и с каждым шагом точность на ImageNet росла. Логичный следующий вопрос: а что, если взять VGG-подобную архитектуру и сделать её ещё глубже — не 19 слоёв, а 56? Интуиция подсказывает: больше слоёв — больше вычислительной мощности — выше качество. Эту интуицию исследователи Microsoft Research проверили экспериментально в 2015 году, и результат оказался обескураживающим.

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

Решение, которое предложили Кайминг Хэ и его коллеги, поражает своей простотой формулировки при глубине последствий: вместо того чтобы заставлять слой напрямую выучивать сложное отображение $H(x)$, дай ему выучить только «остаток» — разницу между желаемым выходом и входом, $F(x) = H(x) - x$. А сам выход слоя собери как сумму этого остатка и немодифицированного входа: $H(x) = F(x) + x$. Технически это означает добавление «короткого пути» в архитектуре — соединения, которое пропускает вход слоя (или блока слоёв) в обход самих слоёв напрямую к их выходу, где он складывается с тем, что вычислили обычные слои. Именно это соединение и называется skip connection (перепрыгивающая связь) или остаточной связью (residual connection).

План сегодняшнего урока: сначала разберёмся, что именно означает «деградация» глубоких сетей и почему это не переобучение, затем построим остаточный блок с нуля и разберём на конкретных числах, как именно градиент «протекает» через skip connection в обход слоя, потом посмотрим, почему тождественное отображение — тривиально достижимая цель для остаточного блока, хотя для обычного слоя это на удивление трудная задача, а в конце — архитектуру ResNet-50/101/152 и то, как одна идея из 2015 года стала фундаментом практически всех современных архитектур глубокого обучения, включая блок внимания трансформеров, который встретится тебе позже в курсе.

История

К 2014 году гонка глубины CNN шла полным ходом: VGGNet (урок 336) довела число слоёв до 19 и показала, что глубина при аккуратной архитектуре (маленькие фильтры 3×3, регулярное удвоение числа каналов) действительно помогает. Естественным следующим шагом казалось просто наращивать число слоёв и дальше. Команда исследователей Microsoft Research — Кайминг Хэ, Сянъюй Чжан, Шаоцин Рен и Цзянь Сунь — поставила контролируемый эксперимент: взяли простую сверточную архитектуру (без каких-либо специальных трюков) и обучили две её версии — на 20 слоёв и на 56 слоёв, — на одном и том же датасете, с одинаковыми гиперпараметрами. По всем интуитивным ожиданиям более глубокая сеть должна была работать не хуже: в худшем случае дополнительные 36 слоёв могли бы просто выучить тождественное отображение и ничего не испортить. На практике 56-слойная сеть показала более высокую ошибку и на обучении, и на тесте — причём разница была не пограничной, а существенной.

Это наблюдение прямо противоречило тогдашнему объяснению неудач глубоких сетей через затухающий и взрывающийся градиент (урок 330): к 2015 году проблема затухающего градиента формально считалась во многом решённой — благодаря пакетной нормализации (batch normalization), продуманной инициализации весов и повсеместному переходу на ReLU вместо сигмоиды (урок про многослойные сети). Обучение обеих сетей — и 20-слойной, и 56-слойной — сходилось: градиенты не «взрывались» и не обнулялись до состояния полного паралича. Но 56-слойная сеть сходилась к существенно худшему решению. Авторы статьи «Deep Residual Learning for Image Recognition» (2015 год) назвали это явление проблемой деградации (degradation problem) — она отличается от классического затухающего градиента тем, что связана не с вычислением градиентов как таковым, а с самим ландшафтом функции потерь: чем глубже сеть, тем труднее оптимизатору отыскать в этом ландшафте путь к действительно хорошему решению, даже когда градиенты формально не затухают до нуля.

Ответом стала архитектура ResNet (сокращение от residual network, «остаточная сеть») с остаточными блоками, о которых пойдёт речь в этом уроке. Результат превзошёл все ожидания: ResNet с глубиной 152 слоя не просто обучалась — она обучалась лучше, чем более мелкие версии той же архитектуры, и выиграла конкурс ILSVRC (ImageNet Large Scale Visual Recognition Challenge) 2015 года с большим отрывом от конкурентов, а заодно взяла первые места в конкурсах по детекции и сегментации объектов COCO того же года. Впервые стало возможным обучать сети глубиной в сотни слоёв и получать от этого реальный прирост качества, а не деградацию. Статья Хэ, Чжана, Рена и Суня стала одной из самых цитируемых работ во всей истории машинного обучения, а сама идея skip connection вышла далеко за пределы компьютерного зрения — сегодня она есть практически в любой современной глубокой архитектуре, вплоть до трансформеров, речь о которых пойдёт позже в курсе.

Проблема деградации: не переобучение, а проблема оптимизации

Интуиция

Представь себе два спортивных плана. По первому плану ты тренируешься 20 недель. По второму — 56 недель, но структура тренировок скопирована с первого плана и просто повторена почти три раза подряд. Логично ожидать, что 56-недельный план — раз он «содержит в себе» 20-недельный как частный случай (можно же просто «застыть» на 20-й неделе и продолжать поддерживающие, ничего не меняющие тренировки оставшиеся 36 недель) — приведёт как минимум к тому же результату, а скорее всего к лучшему. Если результат вдруг оказывается хуже, дело не в том, что «тело перетренировано под конкретный сценарий» (это было бы похоже на переобучение — специализация под частности в ущерб общему результату), а в том, что сам процесс следования такому длинному плану оказался сложнее осуществить на практике: пропущенные тренировки, накопленная усталость, потерянная мотивация где-то на середине пути. Проблема не в конечной цели плана, а в самом процессе его выполнения.

Именно так стоит понимать проблему деградации глубоких сетей. Формально более глубокая сеть не может быть хуже более мелкой в принципе — это строгий математический факт, а не предположение: возьми обученную 20-слойную сеть, добавь после неё 36 слоёв, каждый из которых настроен на тождественное отображение (то есть выдаёт на выходе ровно то же самое, что получил на входе, без изменений), — и 56-слойная сеть будет вычислять функцию, идентичную 20-слойной, а значит покажет ту же самую ошибку, ни на йоту не хуже. Значит, если экспериментально 56-слойная сеть работает хуже 20-слойной, вина лежит не на архитектуре как таковой (она принципиально способна воспроизвести результат мелкой сети или превзойти его), а на процессе обучения градиентным спуском: он просто не находит этого решения (даже такого простого, как «первые 20 слоёв — как в маленькой сети, последние 36 — тождественное отображение») в разумное время при стандартной инициализации весов.

Формула

Проблема деградации (Хэ и др., 2015). Пусть $\mathrm{err}_{\text{train}}(L)$ — ошибка сети глубины $L$ на обучающей выборке. Экспериментально для простых (не остаточных) архитектур на определённом диапазоне глубин наблюдается

$$\mathrm{err}_{\text{train}}(L_2) > \mathrm{err}_{\text{train}}(L_1), \qquad L_2 > L_1$$

хотя теоретически всегда возможна конструкция, при которой $\mathrm{err}_{\text{train}}(L_2) \le \mathrm{err}_{\text{train}}(L_1)$ — а именно, если дополнительные $L_2-L_1$ слоёв реализуют тождественное отображение $g(x)=x$. Проблема не в теоретической выразимости, а в том, что градиентный спуск не находит эту (или лучшую) конфигурацию весов на практике.

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

Пример 1 (численная иллюстрация парадокса на реальных цифрах статьи). В оригинальном эксперименте Хэ и коллег на датасете CIFAR-10 простая сверточная сеть (без остаточных связей) глубиной 20 слоёв показала ошибку на обучающей выборке около $6\%$, а её версия глубиной 56 слоёв — около $10\%$ ошибки на той же обучающей выборке при том же числе эпох обучения. Раз речь про обучающую ошибку, а не про валидационную, объяснение «56-слойная сеть переобучилась под тренировочные данные и потому хуже обобщает» здесь просто не работает — переобучение по определению означает низкую ошибку на трейне и высокую на валидации, а тут высокая ошибка именно на трейне. Единственное разумное объяснение — 56-слойная сеть попросту не смогла как следует обучиться, застряв на существенно худшем решении, чем достижимо в принципе.

Пример 2 (конструктивное доказательство, что глубина не может навредить — «сеть внутри сети»). Возьмём обученную сеть $f_{20}$ глубиной 20 слоёв с ошибкой $6\%$. Построим сеть $f_{56}$ явно: первые 20 слоёв — точная копия весов $f_{20}$, оставшиеся 36 слоёв каждый настроены на тождественное отображение (для полносвязного слоя это матрица весов, равная единичной матрице $I$, и нулевое смещение: $a = I \cdot x + 0 = x$). Тогда $f_{56}(x) = f_{20}(x)$ для любого входа $x$ — обе сети вычисляют одну и ту же функцию, а значит имеют одинаковую ошибку $6\%$, не больше. Это не гипотеза, а прямое построение: такая конфигурация весов для $f_{56}$ существует и достижима «в теории». Экспериментальный факт в том, что стандартный градиентный спуск, стартуя со случайной инициализации, эту (или сравнимую по качеству) конфигурацию не находит — он сходится куда-то в существенно худшую область пространства весов.

Пример 3 (почему выучить точное тождественное отображение обычным слоем неожиданно трудно). Возьмём один полносвязный слой с ReLU-активацией: $a = \mathrm{ReLU}(Wx+b)$. Чтобы этот слой в точности реализовал тождественное отображение $a=x$ для произвольного $x\in\mathbb{R}^n$ (включая отрицательные компоненты), матрица весов должна быть единичной ($W=I$), смещение нулевым ($b=0$), а сама ReLU не должна ничего «отрезать» — но ReLU обнуляет любой отрицательный вход, то есть $\mathrm{ReLU}(x)=x$ выполняется только когда все компоненты $x$ неотрицательны. Для входа, скажем, $x=(2,-3,1)$ получим $\mathrm{ReLU}(I\cdot x)=\mathrm{ReLU}(2,-3,1)=(2,0,1)\ne x$ — тождественное отображение через такой слой в принципе невозможно точно для отрицательных значений. Даже отбросив эту техническую деталь и предположив линейный слой без активации, стандартная случайная инициализация весов (например, Xavier или He-инициализация, урок про инициализацию весов) специально устроена так, чтобы веса были небольшими случайными числами, а вовсе не единичной матрицей, — то есть отправная точка градиентного спуска находится далеко от тождественного отображения, и его придётся «доучивать» с нуля, слой за слоем, сквозь всю глубину сети.

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

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

Остаточный блок: $F(x) + x$ вместо $H(x)$

Интуиция

Ключевая идея ResNet — не менять то, что теоретически может выучить сеть, а переформулировать, что именно сеть учит на практике, чтобы приблизить эту цель к тому, с чего сеть стартует при случайной инициализации. Вместо того чтобы заставлять группу слоёв выучивать желаемое отображение $H(x)$ напрямую «с нуля» — как это делает обычный (не остаточный) слой, — остаточный блок добавляет прямой путь для входа $x$ в обход этих слоёв, а сами слои внутри блока учат только разницу, «остаток» между желаемым выходом и входом: $F(x) = H(x) - x$. Финальный выход блока собирается простым сложением: то, что вычислили внутренние слои ($F(x)$), плюс то, что пришло на вход без изменений ($x$). Технически «в обход» реализуется предельно просто: значение $x$ запоминается перед тем, как войти в блок слоёв, и складывается поэлементно с выходом этих слоёв в самом конце — именно эта стрелка «в обход» и называется skip connection.

Почему это меняет дело? Если желаемое отображение $H(x)$ близко к тождественному (то есть блок в целом должен почти не менять свой вход — а именно это нужно в примере 2 предыдущего раздела, где дополнительные слои должны просто «пропускать» сигнал дальше без искажений), то для обычного слоя это означает необходимость выучить нетривиальную матрицу весов, близкую к единичной, — цель, далёкая от случайной инициализации. А для остаточного блока это означает, что внутренним слоям достаточно выучить $F(x)\approx 0$ — то есть просто ничего не делать, оставаясь близко к своей начальной точке (небольшие случайные веса и так дают выход, близкий к нулю после нормализации). Задача «оставаться около нуля» устроена для оптимизации значительно проще, чем задача «сформировать единичную матрицу с нуля».

Формула

Остаточный блок (residual block). Пусть $x$ — вход блока, а $\mathcal{F}(x, \{W_i\})$ — отображение, вычисляемое несколькими слоями внутри блока (например, два свёрточных слоя с активацией между ними) с весами $\{W_i\}$. Тогда выход блока

$$y = \mathcal{F}(x, \{W_i\}) + x$$

Если размерности входа и выхода $\mathcal{F}$ совпадают, сложение выполняется поэлементно напрямую; если нет (например, меняется число каналов при переходе между стадиями сети), к $x$ применяется линейное проецирующее преобразование $W_s$ (обычно свёртка $1\times1$):

$$y = \mathcal{F}(x, \{W_i\}) + W_s x$$

Слои внутри блока при этом учат не итоговое отображение $H(x)=y$ напрямую, а остаток $\mathcal{F}(x)\approx H(x)-x$ — отсюда название «остаточное обучение» (residual learning).

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

Пример 1 (полный численный forward pass, то есть прямой проход, остаточного блока с двумя слоями). Возьмём упрощённый остаточный блок с одним скалярным «каналом» (для наглядности вычислений — на практике это были бы векторы активаций по каналам свёртки) из двух последовательных линейных преобразований с ReLU между ними: $z_1=\mathrm{ReLU}(w_1x+b_1)$, $F(x)=w_2z_1+b_2$, и выходом блока $y=F(x)+x$. Пусть $w_1=0{,}5$, $b_1=-0{,}2$, $w_2=0{,}3$, $b_2=0{,}1$, вход $x=2$. Считаем: $z_1=\mathrm{ReLU}(0{,}5\cdot2-0{,}2)=\mathrm{ReLU}(0{,}8)=0{,}8$; $F(x)=0{,}3\cdot0{,}8+0{,}1=0{,}24+0{,}1=0{,}34$; итоговый выход блока $y=F(x)+x=0{,}34+2=2{,}34$. Для сравнения посчитаем, что было бы без skip connection, то есть если бы блок вычислял просто $H(x)=F(x)$ без прибавления $x$: $H(x)=0{,}34$ — совсем другое число, гораздо дальше от входа $x=2$. Именно skip connection «удерживает» выход близко ко входу, а внутренние слои лишь корректируют его небольшой добавкой.

Пример 2 (демонстрация, что тождественное отображение — тривиальный частный случай остаточного блока). Возьмём тот же блок, но теперь явно подберём веса так, чтобы $F(x)=0$ для любого входа: положим $w_2=0$ (веса второго слоя обнулены), $b_2=0$. Тогда независимо от $z_1$ имеем $F(x)=0\cdot z_1+0=0$, и выход блока $y=F(x)+x=0+x=x$ — блок в точности реализует тождественное отображение, каким бы ни был вход. Сравни это с примером 3 предыдущего раздела: там для обычного слоя (без skip connection) получить точное тождественное отображение мешала сама структура ReLU и требовалась ровно единичная матрица весов. Здесь же достаточно одной простой конфигурации — обнулить веса последнего слоя блока, — и притом это ничуть не противоречит поведению ReLU внутри блока: сама ReLU по-прежнему может отсекать отрицательные значения $z_1$, но раз итоговый вклад $F(x)$ всё равно умножается на $w_2=0$, это уже не имеет значения для итогового выхода. Обнулить (или сделать очень маленькими) веса последнего слоя — это именно то направление, к которому небольшая случайная инициализация и L2-регуляризация (урок про регуляризацию) естественно подталкивают сеть без специальных усилий, то есть тождественное отображение оказывается не просто достижимым, а буквально «ближайшим» состоянием к типичной точке старта.

Пример 3 (как градиент «перепрыгивает» через слой — вычисление производной по цепному правилу). Возьмём выход блока $y=F(x)+x$ и функцию потерь $L$, для которой уже известен градиент $\dfrac{\partial L}{\partial y}$, пришедший с более поздних слоёв сети при обратном проходе. По правилу дифференцирования суммы:

$$\frac{\partial L}{\partial x} = \frac{\partial L}{\partial y}\cdot\frac{\partial y}{\partial x} = \frac{\partial L}{\partial y}\cdot\left(\frac{\partial F(x)}{\partial x} + 1\right) = \frac{\partial L}{\partial y}\cdot\frac{\partial F(x)}{\partial x} + \frac{\partial L}{\partial y}$$

Слагаемое $\dfrac{\partial L}{\partial y}\cdot\dfrac{\partial F(x)}{\partial x}$ — это обычный путь градиента через слои блока, который может затухать (множители меньше единицы на каждом слое перемножаются, как в обычной глубокой сети без skip connection, урок 330). А слагаемое $\dfrac{\partial L}{\partial y}$ — это градиент, прошедший напрямую, в обход всех слоёв блока, ровно с коэффициентом $1$, без единого умножения на веса или производные активаций. Подставим конкретные числа: пусть $\dfrac{\partial L}{\partial y}=0{,}8$, а множитель $\dfrac{\partial F(x)}{\partial x}$ (произведение производных внутри блока, которое в глубокой сети без skip connection могло бы быть, например, $0{,}05$ из-за нескольких сигмоид или маленьких весов подряд). Тогда $\dfrac{\partial L}{\partial x}=0{,}8\cdot0{,}05+0{,}8=0{,}04+0{,}8=0{,}84$ — итоговый градиент почти целиком определяется «прямым» слагаемым $0{,}8$, а вклад через слои блока ($0{,}04$) почти не важен. Даже если бы внутренние слои полностью «погасили» градиент до нуля ($\partial F/\partial x = 0$), итоговый градиент по-прежнему остался бы равен $0{,}8$ — ровно тому, что пришло сверху, без потерь.

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

Слагаемое «$+1$» в производной $\dfrac{\partial y}{\partial x}=\dfrac{\partial F(x)}{\partial x}+1$ — это математическое сердце всей идеи ResNet. В обычной глубокой сети градиент, проходя обратно через $L$ последовательных слоёв, умножается на $L$ множителей подряд (производные каждого слоя), и если каждый из этих множителей чуть меньше единицы, произведение экспоненциально уменьшается с ростом глубины — это и есть затухающий градиент из урока 330 в его наиболее разрушительной форме для очень глубоких сетей. Skip connection добавляет гарантированный путь с коэффициентом ровно $1$ на каждом блоке, который не зависит от того, что происходит внутри блока с его весами и активациями. При прохождении через $N$ последовательных остаточных блоков подряд этот прямой путь превращается не в произведение $N$ множителей, а в сумму, где как минимум одно слагаемое (полностью «прямой» путь, минуя вообще все блоки) остаётся равным единице независимо от $N$. Именно поэтому глубина сети, обёрнутая в остаточные блоки, перестаёт быть врагом обучаемости: градиент физически может «дотечь» от последнего слоя до первого без экспоненциального затухания, что и открывает дорогу к сетям в сотни слоёв.

Почему тождественное отображение — «путь наименьшего сопротивления»

Интуиция

Этот раздел объединяет две идеи предыдущего блока в одно целостное объяснение того, почему ResNet вообще решает проблему деградации, а не просто добавляет ещё один архитектурный трюк. Смысл в следующем: задача «выучить полезное преобразование $H(x)$, которое в худшем случае оказывается близким к тождественному» разбивается остаточным блоком на задачу «выучить добавку $F(x)$, которая в худшем случае близка к нулю». А выучить, что «ничего не делать — нормально», оптимизатору принципиально легче, чем выучить, что «делать ровно то же самое, что было на входе» через сложную нелинейную цепочку слоёв.

Формула

Асимметрия задачи обучения. Обычный слой (или блок слоёв) должен подобрать веса так, чтобы $H(x)\approx x$ — то есть выучить нетривиальное преобразование, эквивалентное тождественному, отталкиваясь от случайной инициализации, которая обычно далека от такого решения. Остаточный блок должен подобрать веса так, чтобы

$$F(x) = H(x) - x \approx 0$$

Если $H(x)\approx x$ действительно оптимально (сеть слишком глубока, дополнительные слои не нужны), задача сводится к $F(x)\approx0$ — а нулевые (или близкие к нулю) веса типичны для случайной инициализации и регуляризации, то есть решение уже находится вблизи начальной точки оптимизации, а не где-то далеко от неё.

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

Пример 1 (сравнение «расстояния» до решения в пространстве весов). При инициализации по схеме Хе (He-инициализация, стандартная для ReLU-сетей) веса слоя обычно выбираются как случайные числа с небольшой дисперсией, порядка $\mathrm{Var}(W)=2/n_{in}$, что для слоя с $n_{in}=256$ входами даёт типичное отклонение веса от нуля около $\sqrt{2/256}\approx0{,}088$ — то есть веса стартуют маленькими и близкими к нулю. Для обычного слоя, которому нужно выучить $H(x)\approx x$ (эквивалент единичной матрицы, где диагональные элементы равны $1$, а не $0$), это означает, что путь от точки старта до цели проходит через изменение каждого диагонального веса примерно на $1-0{,}088\approx0{,}91$ в среднем — существенное перемещение в пространстве весов. Для остаточного блока, которому нужно выучить $F(x)\approx0$, точка старта и цель почти совпадают с самого начала: расстояние в пространстве весов, грубо говоря, порядка тех же $0{,}088$, а не $0{,}91$. Оптимизатору попросту нужно пройти в разы более короткий путь, чтобы достичь состояния «слой не портит вход».

Пример 2 (что происходит, если слой действительно не нужен — «мёртвый» остаточный блок против «мёртвого» обычного слоя). Представь сеть настолько глубокую, что часть блоков ближе к концу оказываются избыточными для конкретной задачи — качество решения уже достигнуто более ранними слоями. В архитектуре ResNet такой блок обучается к $F(x)\to0$: он не мешает и не помогает, выход блока становится примерно равен входу, $y\approx x$, и сеть по сути «пропускает» этот блок, как будто его нет. В архитектуре без skip connection аналогичный избыточный слой должен выучить $H(x)\approx x$ через полноценное нелинейное преобразование — если оптимизация не находит точно этого решения (а как показано в примере 3 раздела про деградацию, для ReLU-слоя точное тождественное отображение недостижимо для отрицательных входов в принципе), слой начинает вносить небольшие, но систематические искажения в сигнал. Помножь такое искажение на десятки лишних слоёв подряд — и накопленная ошибка объясняет наблюдаемую на практике деградацию качества у глубоких сетей без остаточных связей.

Пример 3 (эмпирическое подтверждение из статьи — сравнение норм откликов слоёв). В оригинальной статье про ResNet авторы измеряли типичную величину (стандартное отклонение) откликов $F(x)$ внутри обученных остаточных блоков на разных стадиях сети и обнаружили, что более глубокие блоки (те, что находятся ближе к концу сети) в среднем демонстрируют меньшие по величине отклики $F(x)$, чем более ранние блоки, — то есть более поздние блоки на практике действительно выучивают близкие к нулю поправки, что напрямую подтверждает интуицию «блок предпочитает быть близким к тождественному отображению, если существенной работы для него не находится». Это не гипотеза «в теории могло бы быть так», а эмпирически измеренный эффект после реального обучения сети — остаточные блоки на практике ведут себя ровно так, как предсказывает математика остаточного обучения.

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

Эта асимметрия — не просто удобное объяснение постфактум, а прямой ответ на вопрос, поставленный в начале урока: почему более глубокая сеть с skip connections не деградирует, даже когда дополнительные слои избыточны для задачи. Каждый остаточный блок получает «путь наименьшего сопротивления» к состоянию «не мешать» — и если этого состояния достаточно для хорошего решения, оптимизатор находит его без труда, поскольку оно физически близко к точке старта. Дополнительная глубина в ResNet становится практически бесплатной опцией: если слои нужны — они выучивают полезное преобразование $F(x)$; если не нужны — они естественным образом «отключаются», стремясь к $F(x)\approx0$, вместо того чтобы портить сигнал неудачно выученным приближением к тождественному отображению. Именно поэтому у ResNet добавление глубины перестаёт быть риском и становится почти чистым выигрышем.

Архитектура ResNet: остаточные блоки, ResNet-50/101/152

Интуиция

Одного остаточного блока недостаточно — нужна цельная архитектура, которая берёт входное изображение, прогоняет его через последовательность остаточных блоков, постепенно наращивая число каналов и уменьшая пространственное разрешение (аналогично VGG из урока 336), и заканчивается классификационной головой. ResNet построен как несколько «стадий», внутри каждой из которых подряд идёт несколько одинаковых по структуре остаточных блоков, а между стадиями число каналов удваивается, а пространственный размер карты признаков уменьшается вдвое (обычно свёрткой с шагом 2). Именно повторяемость и однородность блоков — один из секретов того, почему ResNet удалось довести до 152 слоёв и не утонуть в архитектурных изысках: вместо придумывания сотен разных типов слоёв инженеры просто штампуют одинаковые остаточные блоки один за другим.

Формула

Стадии ResNet. Сеть строится из входного свёрточного слоя (обычно $7\times7$, шаг $2$) и слоя пулинга, за которыми следуют четыре стадии остаточных блоков с числом каналов $64\to128\to256\to512$ (для ResNet-18/34) или $256\to512\to1024\to2048$ (для ResNet-50/101/152, где используется «бутылочный» блок, см. ниже), и завершается глобальным усредняющим пулингом (global average pooling) и одним полносвязным слоем-классификатором. Общее число слоёв конкретной версии сети (например, «50» в ResNet-50) — это подсчёт всех слоёв с обучаемыми весами (свёрточных и полносвязных) по всем стадиям вместе с входным слоем.

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

Пример 1 (обычный остаточный блок против «бутылочного» — BasicBlock и Bottleneck). Для не слишком глубоких версий (ResNet-18, ResNet-34) остаточный блок состоит из двух свёрток $3\times3$ подряд: вход $\to$ свёртка $3\times3\to$ ReLU $\to$ свёртка $3\times3\to$ сложение со входом $\to$ ReLU. Для глубоких версий (ResNet-50/101/152) используется более экономный «бутылочный» блок (bottleneck block) из трёх свёрток: сначала свёртка $1\times1$ сжимает число каналов (например, с $256$ до $64$), затем свёртка $3\times3$ работает уже с этим уменьшенным числом каналов, а финальная свёртка $1\times1$ снова расширяет число каналов обратно до $256$ перед сложением со входом. Посчитаем экономию параметров для входа с $256$ каналами и выхода с $256$ каналами: обычный блок из двух свёрток $3\times3\times256\times256$ дал бы $2\times(3\times3\times256\times256)=1\,179\,648$ параметров (без bias), тогда как бутылочный блок ($1\times1$: $256\to64$, затем $3\times3$: $64\to64$, затем $1\times1$: $64\to256$) даёт $256\times64 + 3\times3\times64\times64 + 64\times256 = 16\,384+36\,864+16\,384=69\,632$ параметра — почти в 17 раз меньше при сопоставимой выразительной способности. Именно бутылочные блоки сделали практически осуществимым обучение версий глубиной 50, 101 и 152 слоя без взрывного роста числа параметров.

Пример 2 (сложение размерностей, когда каналы не совпадают — проекция $1\times1$). Между стадиями число каналов меняется (скажем, с $256$ на $512$) и пространственный размер карты признаков уменьшается вдвое. Прямое сложение $F(x)+x$ здесь невозможно буквально — размерности не совпадают. В этом случае к $x$ применяется отдельная свёртка $1\times1$ с шагом $2$ (проекция $W_s$ из формулы остаточного блока), которая одновременно меняет число каналов с $256$ на $512$ и уменьшает пространственный размер вдвое, приводя $x$ к точно той же форме, что и выход $F(x)$ основной ветки блока, — после чего складывание становится корректным. Такой блок называют блоком с проекцией (projection shortcut) в отличие от обычного блока с тождественной связью (identity shortcut), где $x$ пробрасывается вообще без изменений.

Пример 3 (счёт слоёв на конкретной версии — откуда взялось число 50). Разберём происхождение числа 50 в имени ResNet-50: входной слой — $1$ свёрточный слой; далее четыре стадии из бутылочных блоков в количестве $3, 4, 6, 3$ блока соответственно (итого $16$ блоков), каждый из которых содержит $3$ свёрточных слоя — это даёт $16\times3=48$ слоёв; и наконец $1$ полносвязный классификационный слой в конце. Суммарно: $1+48+1=50$ слоёв с обучаемыми весами — отсюда и число в названии архитектуры. ResNet-101 и ResNet-152 получаются из той же самой схемы простым увеличением числа блоков в третьей стадии (с $6$ блоков до $23$ для ResNet-101 и до $36$ для ResNet-152), без изменения общей концепции: тот же самый бутылочный блок, повторённый больше раз подряд, — прямое доказательство того, что архитектура ResNet масштабируется по глубине почти «бесплатно», просто добавлением ещё одинаковых блоков.

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

Однородная, повторяемая структура остаточных блоков — не только удобство для проектирования архитектуры, но и прямое следствие всего, что было показано выше в уроке: раз каждый блок способен без вреда «отключиться» (выучив $F(x)\approx0$), добавление ещё одного блока в стадию — это операция с ограниченным риском ухудшения и потенциальным выигрышем в качестве, если для нового блока найдётся полезная работа. Именно это свойство и позволило исследователям масштабировать одну и ту же идею от ResNet-18 до ResNet-152, просто повторяя один и тот же строительный блок больше раз, — то, что было немыслимо для архитектур без skip connections, где каждый добавленный слой рисковал внести деградацию, показанную в начале урока.

Влияние ResNet на все последующие архитектуры

Интуиция

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

Формула

Обобщённый остаточный блок для произвольного типа слоя. Пусть $\mathrm{Layer}(x)$ — произвольное преобразование (свёртка, полносвязный слой, блок самовнимания в трансформере, рекуррентная ячейка) с обучаемыми весами. Обобщённая остаточная обёртка вокруг любого такого слоя:

$$y = x + \mathrm{Layer}(\mathrm{Norm}(x))$$

где $\mathrm{Norm}$ — слой нормализации (пакетная нормализация в CNN или послойная нормализация, layer normalization, в трансформерах), применяемый перед основным преобразованием. Именно эта обёртка (с вариациями в порядке нормализации) лежит в основе трансформерного блока, включая блок самовнимания (self-attention), который будет разобран в одном из следующих уроков курса.

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

Пример 1 (ResNeXt и DenseNet — прямые архитектурные потомки). Уже в 2016–2017 годах появились архитектуры ResNeXt (расширяющая идею бутылочного блока за счёт параллельных «путей» внутри блока, объединённых суммированием — то есть множественные остаточные ветви одновременно) и DenseNet (где каждый слой соединён skip connection не только с непосредственным соседом, а со всеми предыдущими слоями блока сразу, через конкатенацию, а не сложение). Обе архитектуры напрямую наследуют центральную идею ResNet — короткий путь для градиента в обход слоёв, — но модифицируют то, как именно организована связь (сумма против конкатенации, один путь против множества параллельных).

Пример 2 (трансформерный блок как остаточная сеть). Каждый блок трансформера (архитектуры, лежащей в основе современных больших языковых моделей и будет отдельно разобрана позже в курсе) устроен как последовательность из двух остаточных подблоков: $x_1 = x + \mathrm{SelfAttention}(\mathrm{Norm}(x))$, затем $x_2 = x_1 + \mathrm{FeedForward}(\mathrm{Norm}(x_1))$. Сравни эту запись с формулой обобщённого остаточного блока выше — это буквально та же конструкция $y=x+\mathrm{Layer}(\cdot)$, применённая дважды подряд внутри одного трансформерного блока, только вместо свёртки внутри стоит механизм самовнимания и полносвязная сеть. Современные трансформерные модели состоят из десятков и сотен таких блоков подряд (у крупных языковых моделей — счёт может идти на многие десятки слоёв), и без остаточных связей градиент попросту не дошёл бы от последнего блока до первого при такой глубине — ровно та же проблема, что была продемонстрирована в разделе про затухающий градиент этого урока, только для другого типа архитектуры.

Пример 3 (свёрточные сети следующего поколения — от EfficientNet до современных backbone-сетей). Архитектуры, появившиеся после ResNet, — EfficientNet, RegNet, ConvNeXt и десятки других — практически без исключения включают остаточные связи как базовый строительный элемент, споря друг с другом уже не о том, «нужны ли skip connections», а о деталях: как масштабировать глубину, ширину и разрешение одновременно (тема следующего урока про Inception и EfficientNet), как организовать блок внимания внутри свёрточной сети, как оптимизировать число параметров под конкретное вычислительное бюджет. Сам вопрос о skip connections в этих обсуждениях уже не поднимается — они стали настолько базовым, самим собой разумеющимся элементом архитектуры, что vanilla-сеть без остаточных связей на такой глубине сегодня выглядела бы архитектурным анахронизмом, а не смелым решением.

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

То, что придумывалось в 2015 году как решение узкой, конкретной проблемы — деградации качества при обучении очень глубоких сверточных сетей для классификации изображений, — оказалось общим принципом, применимым к любой достаточно глубокой нейросетевой архитектуре независимо от типа задачи и типа слоя внутри неё. Это редкий случай, когда архитектурная идея не просто улучшила метрику на конкретном бенчмарке, а стала частью базового «словаря» глубокого обучения — таким же фундаментальным строительным блоком, как сама операция $a=f(Wx+b)$ из урока про многослойные сети. Понимание механики skip connection — почему она устраняет затухание градиента и почему тождественное отображение достижимо тривиально — напрямую пригодится тебе при разборе трансформеров дальше в курсе: там ты увидишь буквально ту же формулу $y=x+\mathrm{Layer}(\mathrm{Norm}(x))$, просто с другим содержимым внутри $\mathrm{Layer}$.

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

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

Задание 1: Для остаточного блока $F(x)=0{,}2x^2$ найди выход блока $y=F(x)+x$ при $x=3$.


Задание 2: При каком условии на веса $w_2, b_2$ последнего слоя остаточного блока $F(x)=w_2z_1+b_2$ выполняется $F(x)=0$ для любого $x$?


Задание 3: Остаточный блок с $F(x)=0$ получает вход $x=7$. Чему равен выход блока $y$?


Задание 4: По цепному правилу для $y=F(x)+x$ найди общий вид производной $\dfrac{\partial y}{\partial x}$.


Задание 5 (машинное обучение): Почему более высокая ошибка 56-слойной сети по сравнению с 20-слойной на обучающей выборке не объясняется переобучением?


Задание 6: Остаточный блок $F(x)=1{,}5x-4$. Найди выход $y=F(x)+x$ при $x=0$ и при $x=4$.


Задание 7: Свёрточный слой $1\times1$ переводит $256$ каналов в $64$ канала (без bias). Сколько параметров у этого слоя?


Задание 8: Чему равен $\sigma(0)$ и какое значение принимает производная $\dfrac{\partial F(x)}{\partial x}$ в примере 3 раздела про остаточный блок, если $\dfrac{\partial L}{\partial y}=1{,}0$ и $\dfrac{\partial F(x)}{\partial x}=0$ (внутренние слои полностью «погасили» градиент)?


Задание 9: В обычном (не остаточном) блоке из трёх слоёв подряд с производными $0{,}3$, $0{,}4$ и $0{,}2$ найди суммарный множитель градиента, проходящего через весь блок по цепному правилу.


Задание 10: Сеть глубины $20$ показывает ошибку на трейне $6\%$, сеть глубины $56$ (та же архитектура, без skip connections) — $10\%$ на трейне. Можно ли построить сеть глубины $56$ с ошибкой не хуже $6\%$ в принципе? Как?


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

Задание 11: Бутылочный блок ($1\times1$: $512\to128$, затем $3\times3$: $128\to128$, затем $1\times1$: $128\to512$) — посчитай число параметров (без bias).


Задание 12: Сравни число параметров бутылочного блока из задания 11 с гипотетическим обычным блоком из двух свёрток $3\times3$ ($512\to512$ каждая, без bias).


Задание 13: Блок переводит вход с $128$ каналов, размер карты признаков $56\times56$, в выход с $256$ каналами, размер $28\times28$. Опиши, какое преобразование нужно применить к $x$ для skip connection, и почему нельзя просто сложить $F(x)+x$ напрямую.


Задание 14: ResNet-34 использует обычные (не бутылочные) блоки из двух свёрток $3\times3$ каждый на всех четырёх стадиях, с $3, 4, 6, 3$ блоками соответственно (как и в ResNet-50, но блоки другие). Посчитай число свёрточных слоёв внутри всех блоков.


Задание 15: Используя результат задания 14, объясни, откуда берётся число «34» в названии ResNet-34 (с учётом входного слоя и финального полносвязного слоя).


Задание 16: Остаточный блок с $F(x)=-0{,}9x$. Найди $y=F(x)+x$ при $x=10$. Что происходит с сигналом при таком $F$?


Задание 17: Для трансформерного блока $x_1=x+\mathrm{SelfAttention}(\mathrm{Norm}(x))$ определи, что здесь играет роль $F(x)$ из формулы обычного остаточного блока $y=F(x)+x$.


Задание 18: Сеть без skip connections имеет $10$ последовательных слоёв, каждый с производной активации $0{,}6$. Найди множитель, на который умножается градиент, дойдя от последнего слоя до первого.


Задание 19: Для той же сети из задания 18, но с $20$ слоями вместо $10$, найди множитель градиента. Сравни с ответом задания 18 и сделай вывод о влиянии глубины.


Задание 20: Остаточный блок $F(x)=0{,}1\sin(x)$ (внутренние слои дают небольшой, но не нулевой вклад). При $x=\pi/2\approx1{,}571$ найди $F(x)$ и итоговый выход блока $y$.


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

Задание 21 (машинное обучение): Объясни своими словами, почему выучить $F(x)\approx0$ оптимизатору легче, чем выучить $H(x)\approx x$ напрямую, если оба варианта в конечном счёте дают одинаковый итоговый выход блока.


Задание 22: Бутылочный блок ResNet-50 на первой стадии: вход $256$ каналов, промежуточные $64$ канала, выход $256$ каналов, карта признаков $56\times56$. Посчитай, сколько всего чисел (элементов тензора) содержит выход блока (без учёта batch-размерности).


Задание 23: ResNet-152 использует те же бутылочные блоки, что и ResNet-50, но с $3, 8, 36, 3$ блоками по стадиям вместо $3, 4, 6, 3$. Посчитай суммарное число блоков и суммарное число свёрточных слоёв внутри них (по $3$ свёртки на блок).


Задание 24: Почему тот факт, что затухающий и взрывающийся градиент формально считался «решённым» к 2015 году (через batch normalization и ReLU), не означал автоматического решения проблемы деградации глубоких сетей?


Задание 25: Остаточный блок с проекцией: $F(x)$ имеет форму $512\times28\times28$, вход $x$ имеет форму $256\times56\times56$. Опиши шаги приведения $x$ к форме $F(x)$ и посчитай число параметров нужной свёртки $1\times1$ с шагом $2$ (без bias).


Задание 26: Сравни поведение градиента в сети из $50$ обычных (не остаточных) слоёв с производной активации $0{,}9$ каждый и в сети из $50$ остаточных блоков, где для каждого блока $\dfrac{\partial F(x)}{\partial x}=0{,}9$. Найди итоговый множитель градиента для обоих случаев (для остаточных блоков используй формулу $\dfrac{\partial y}{\partial x}=\dfrac{\partial F(x)}{\partial x}+1$ и приближение, что множители блоков перемножаются последовательно).


Задание 27: Почему DenseNet (где каждый слой соединён skip connection'ами со всеми предыдущими слоями блока через конкатенацию) можно рассматривать как развитие идеи ResNet, а не как принципиально другой подход?


Задание 28: Сеть содержит $34$ последовательных остаточных блока. Известно, что для конкретной задачи только первые $10$ блоков вносят существенный вклад ($F(x)$ заметно отличается от нуля), а оставшиеся $24$ блока выучили $F(x)\approx0$. Опиши, чему эквивалентна такая сеть по итоговому вычисляемому отображению, и объясни, почему это не вредит качеству.


Задание 29: Два блока подряд: первый $F_1(x)=0{,}5x-1$, второй $F_2(x)=-0{,}2x+3$ (второй блок применяется к выходу первого). Вход в первый блок $x=2$. Найди итоговый выход после обоих блоков.


Задание 30: ResNet-50 имеет стадии с $3, 4, 6, 3$ бутылочными блоками, где каждый блок содержит $3$ свёрточных слоя, плюс входной свёрточный слой и финальный полносвязный слой. Отдельно посчитай число блоков с проекцией (по одному на стадию, на первом блоке каждой стадии, где меняется число каналов) и число блоков с тождественной связью (все остальные).

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

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

  • Путают skip connection с обычным «пропуском» слоя или удалением его из сети. Слои внутри остаточного блока продолжают полноценно вычисляться на каждом forward pass — skip connection не отключает и не обходит вычисление $F(x)$, а лишь добавляет параллельный прямой путь для $x$, который затем складывается с результатом $F(x)$ (задание 3).

  • Думают, что skip connection решает проблему затухающего градиента исключительно за счёт того, что делает градиенты внутри слоёв блока больше. На самом деле градиенты внутри $F(x)$ могут оставаться сколь угодно маленькими — skip connection добавляет отдельное гарантированное слагаемое $+1$ к общей производной блока (задание 8), которое не зависит от качества градиента внутри $F(x)$ вовсе.

  • Забывают о необходимости проекции $W_s$, когда размерности входа и выхода блока не совпадают. При смене числа каналов или пространственного размера между стадиями сети прямое сложение $F(x)+x$ технически невозможно без предварительного приведения $x$ к нужной форме через свёртку $1\times1$ (задания 13, 25) — попытка сложить тензоры разной формы приведёт к ошибке размерности в коде.

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

  • Путают бутылочный блок (bottleneck) с обычным двухслойным остаточным блоком, используемым в ResNet-18/34. Это два разных внутренних устройства блока — бутылочный блок из трёх свёрток ($1\times1$, $3\times3$, $1\times1$) применяется в более глубоких версиях (ResNet-50/101/152) именно ради экономии параметров (задание 12), тогда как более мелкие версии используют обычный блок из двух свёрток $3\times3$.

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

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

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

  • Остаточный блок переформулирует задачу обучения: вместо $H(x)$ напрямую слои учат остаток $F(x)=H(x)-x$, а выход блока собирается как $y=F(x)+x$ через прямую (skip) связь входа с выходом.

  • Тождественное отображение тривиально достижимо в остаточном блоке — достаточно, чтобы веса последнего слоя внутри блока стали близки к нулю ($F(x)\approx0$), что физически близко к типичной точке случайной инициализации весов.

  • Производная выхода блока по входу равна $\dfrac{\partial y}{\partial x}=\dfrac{\partial F(x)}{\partial x}+1$ — слагаемое «$+1$» гарантирует, что градиент проходит через блок в обход слоёв $F(x)$ без затухания, даже если внутренний градиент $\dfrac{\partial F(x)}{\partial x}$ близок к нулю.

  • Бутылочный блок (свёртки $1\times1\to3\times3\to1\times1$) экономит параметры по сравнению с обычным блоком из двух свёрток $3\times3$ — именно он используется в глубоких версиях ResNet-50/101/152, что и сделало такую глубину практически осуществимой.

  • Название версии ResNet (число в имени архитектуры) — это подсчёт всех слоёв с обучаемыми весами: входной свёрточный слой, все свёрточные слои внутри остаточных блоков по всем стадиям, финальный полносвязный слой.

  • Когда размерности входа и выхода блока не совпадают (смена числа каналов или пространственного размера между стадиями), к входу применяется проекция — свёртка $1\times1$, приводящая его форму к форме $F(x)$ перед сложением.

  • ResNet выиграла ImageNet 2015 года с глубиной до 152 слоёв — впервые продемонстрировав, что рост глубины сети без ограничений даёт реальный прирост качества, а не деградацию.

  • Skip connection стала универсальным архитектурным элементом далеко за пределами CNN — включая блок трансформера, где формула $y=x+\mathrm{Layer}(\mathrm{Norm}(x))$ применяется как для самовнимания, так и для полносвязной части каждого блока.

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

Этот урок напрямую опирается на урок 330 про backpropagation, где впервые разбиралась проблема затухающего градиента: при обратном проходе через много последовательных слоёв производные перемножаются, и если каждая из них меньше единицы, произведение экспоненциально уменьшается с глубиной сети (см. задания 18–19 этого урока, где показано, как удвоение глубины возводит затухание в квадрат). Разница в том, что урок 330 рассматривал эту проблему для сети в целом, а сегодняшний урок показал конкретное архитектурное решение: skip connection добавляет к производной каждого блока гарантированное слагаемое «$+1$» (формула $\dfrac{\partial y}{\partial x}=\dfrac{\partial F(x)}{\partial x}+1$), которое структурно не позволяет общему множителю градиента падать ниже определённого предела, сколько бы блоков ни стояло подряд, — это прямой, инженерный ответ именно на ту проблему затухающего градиента, которая была сформулирована математически в уроке 330.

Урок также продолжает урок 336 про классические CNN-архитектуры: там ResNet упоминалась как финальная точка эволюции от LeNet к VGG, сегодняшний урок разобрал её устройство детально — вплоть до конкретных численных примеров того, как считается forward и backward pass через остаточный блок. Идея асимметрии между выразимостью модели (может ли архитектура в принципе представить нужную функцию) и обучаемостью (находит ли оптимизатор эту функцию на практике) перекликается с темой теоремы универсальной аппроксимации из урока про многослойные сети: там теорема доказывала существование подходящих весов для приближения любой функции, но ничего не говорила о том, как их найти обучением, — ровно та же логика применима и к проблеме деградации глубоких сетей.

Дальше в курсе, при разборе трансформеров, ты снова встретишь ту же самую формулу остаточной связи $y=x+\mathrm{Layer}(\mathrm{Norm}(x))$ — на этот раз обёрнутую вокруг блока самовнимания и полносвязной сети внутри каждого слоя трансформера. Понимание того, почему эта конструкция решает проблему затухающего градиента (слагаемое «$+1$» в производной) и почему тождественное отображение оказывается тривиально достижимым, напрямую перенесётся на понимание того, как обучаются модели с десятками и сотнями слоёв внимания, включая современные большие языковые модели.

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

  • Статья Кайминга Хэ и коллег «Deep Residual Learning for Image Recognition» (2015 год) — одна из самых цитируемых научных работ во всей истории машинного обучения, с числом цитирований, исчисляемым уже десятками тысяч, что отражает масштаб влияния идеи skip connection далеко за пределами первоначальной задачи классификации изображений.

  • ResNet-152, выигравшая ImageNet 2015 года, содержала примерно в 8 раз больше слоёв, чем VGG-19 (урок 336), но при этом требовала меньше вычислительных операций благодаря экономичным бутылочным блокам — глубина и вычислительная стоимость оказались не жёстко связаны между собой, если архитектура спроектирована аккуратно.

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

  • Помимо ResNet, в 2015 году независимо появилась архитектура Highway Networks (Шмидхубер и коллеги), использующая похожую идею прямых путей для сигнала, но с дополнительным обучаемым «шлюзом» (gate), решающим, сколько сигнала пропускать напрямую, а сколько — через слои; ResNet оказалась проще (без обучаемого шлюза, просто прямое сложение) и в итоге получила куда более широкое распространение в индустрии и исследованиях.

Лайфхаки

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

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

  • При выборе между обычным блоком (две свёртки $3\times3$) и бутылочным блоком (три свёртки $1\times1\to3\times3\to1\times1$) для собственной глубокой архитектуры ориентируйся на глубину: для сетей до 30–40 слоёв разница в эффективности параметров не критична, а для более глубоких версий бутылочный блок практически обязателен, чтобы не допустить взрывного роста числа параметров.

  • При отладке forward pass через остаточный блок в коде сверяй формы тензоров перед сложением $F(x)+x$ отдельным assert-выражением — рассинхронизация размерностей между основной веткой и веткой skip connection (особенно после добавления или изменения слоя внутри $F$) является одной из самых частых ошибок при ручной реализации ResNet-подобных архитектур.

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

  • Не полагайся на интуицию «глубже — всегда лучше» даже с skip connections: прирост качества от добавления новых блоков убывает с глубиной, и после определённой точки для конкретной задачи и объёма данных дополнительные блоки почти не меняют результат (как в задании 28) — прежде чем наращивать глубину дальше, проверяй, действительно ли задаче нужна дополнительная выразительная способность.

Сегодня ты разобрал одну из тех немногих идей в глубоком обучении, которые меняют не одну конкретную архитектуру, а сам способ проектирования глубоких сетей в принципе. Проблема деградации выглядела парадоксом — более способная модель работала хуже, — но за этим парадоксом стояла конкретная, разрешимая проблема оптимизации, а не фундаментальный тупик. Остаточная связь $y=F(x)+x$ решает её не хитрым трюком, а честной математикой: гарантированным слагаемым «$+1$» в производной, которое физически не даёт градиенту затухнуть на пути через сотни слоёв, и тривиально достижимым тождественным отображением, которое избавляет оптимизатор от необходимости заново «изобретать» то, что уже есть на входе. Эта же формула — вход плюс скорректированный вход — ждёт тебя в блоке трансформера уже совсем скоро в курсе. А в следующем уроке ты увидишь, как идеи ResNet и более ранние архитектурные приёмы объединяются в Inception и EfficientNet — архитектурах, которые оптимизируют уже не только глубину, но и ширину, и разрешение сети одновременно.

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

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

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