Residual connections

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

Основная концепция

Пусть (x) — входной тензор блока, а (F(x)) — функция, которую блок должен выучить. В обычной сети выход блока (y) равен просто (F(x)). В residual-блоке вводится связь, пропускающая (x) напрямую:

[ y = F(x) + x]

Здесь (x) называется skip connection или пропускающей связью. Преимущество этого подхода:

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

Реализация residual-блока в ConvNetJS

В ConvNetJS слои создаются с помощью объектов типа Layer. Для создания residual-блока необходимо:

  1. Определить основной путь вычисления (F(x)), состоящий из нескольких слоев (обычно свертки, активации и нормализации).
  2. Суммировать вход (x) с результатом (F(x)).
  3. Передать суммарный результат на следующий слой.

Пример кода residual-блока на ConvNetJS:

// Создание входного слоя
var layer_defs = [];
layer_defs.push({type:'input', out_sx:32, out_sy:32, out_depth:3});

// Первый свёрточный слой внутри residual-блока
layer_defs.push({type:'conv', sx:3, filters:16, stride:1, pad:1, activation:'relu'});

// Второй свёрточный слой блока
layer_defs.push({type:'conv', sx:3, filters:16, stride:1, pad:1, activation:'none'});

// Суммирование входа и выхода блока
layer_defs.push({type:'elemwise', op:'sum', inputs:[0,1]});

// Активация после суммирования
layer_defs.push({type:'relu'});

// Создание сети
var net = new convnetjs.Net();
net.makeLayers(layer_defs);

Пояснения по коду:

  • elemwise с операцией 'sum' позволяет суммировать два тензора поэлементно. В inputs указываются индексы слоев, которые будут суммированы.
  • Активация 'relu' применяется после суммирования для добавления нелинейности.
  • Входной слой должен совпадать по размерности с выходом блока, иначе необходимо использовать проекционную свертку для согласования размеров.

Применение проекционной свертки

Если размеры тензоров на входе и после блока различаются (например, при изменении числа фильтров или размерности), residual connection нельзя просто суммировать. Для решения используется 1x1 свертка, выравнивающая размерность:

layer_defs.push({type:'conv', sx:1, filters:32, stride:1, pad:0, activation:'none', layer_index:0});

После этого суммирование возможно без ошибок размерностей.

Использование residual connections для обучения

Residual connections повышают стабильность обучения глубоких сетей. В ConvNetJS можно наблюдать несколько ключевых эффектов:

  • Быстрая сходимость: сети с остаточными блоками обучаются быстрее, особенно при глубине >10 слоев.
  • Снижение переобучения: благодаря упрощению функции, которую блок должен выучить, вероятность переобучения уменьшается.
  • Устойчивость к исчезновению градиентов: градиенты проходят через skip connections без сильного затухания.

Примеры архитектур с residual connections

  1. Простейший residual-блок: два свёрточных слоя + суммирование + ReLU.
  2. ResNet-стиль: группы residual-блоков с увеличением числа фильтров через проекционную свертку.
  3. Bottleneck блок: три слоя (1x1 → 3x3 → 1x1) с residual connection, оптимизирующий количество параметров.

Рекомендации по построению residual-блоков в ConvNetJS

  • Использовать активацию после суммирования, а не до, чтобы градиент проходил по skip connection линейно.
  • В случаях, когда размеры не совпадают, применять 1x1 свертку для согласования размерностей.
  • Глубокие сети (>20 слоев) требуют регуляризации, например dropout или batch normalization, чтобы избежать переобучения.
  • Экспериментировать с количеством фильтров и глубиной residual-блоков для достижения оптимального соотношения точность/скорость.

Residual connections в ConvNetJS дают возможность строить глубокие и эффективные сверточные сети, значительно облегчая процесс обучения и увеличивая производительность модели.