Проблема затухающего и взрывного градиента

Основы градиентного спуска

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

[ W W - ]

где () — скорость обучения. Вычисление градиентов осуществляется методом обратного распространения ошибки (backpropagation), который применяет цепное правило для последовательных слоёв:

[ = _{k=l}^{L-1} ]

Здесь (a^{(l)}) — активация на слое (l). Именно этот многоступенчатый продукт производных создаёт условия для затухающего или взрывного градиента.

Причины возникновения

Затухающий градиент возникает, когда производные функции активации меньше единицы. Например, для сигмоиды:

[ ’(x) = (x)(1-(x)) ]

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

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

Методы борьбы с затухающими и взрывными градиентами

1. Инициализация весов

В TensorFlow.js доступны разные схемы инициализации весов через tf.initializers. Правильный выбор уменьшает вероятность взрывных или затухающих градиентов:

const model = tf.sequential();
model.add(tf.layers.dense({
  units: 128,
  inputShape: [784],
  activation: 'relu',
  kernelInitializer: 'heNormal' // Для ReLU
}));
  • glorotNormal или glorotUniform подходит для сигмоид и tanh.
  • heNormal или heUniform рекомендуется для ReLU и его вариантов.

2. Нормализация слоёв

Batch Normalization стабилизирует распределение активаций:

model.add(tf.layers.batchNormalization());

Это позволяет градиентам сохранять умеренные значения и ускоряет обучение.

3. Ограничение нормы градиентов

В TensorFlow.js можно применять gradient clipping, чтобы градиенты не превышали заданный порог:

const optimizer = tf.train.adam(0.001, undefined, undefined, undefined, {
  clipNorm: 5.0
});
  • clipNorm ограничивает L2-норму градиента.
  • Есть также clipValue, ограничивающий значения отдельных компонентов.

4. Выбор функций активации

  • ReLU и его варианты (LeakyReLU, ELU) минимизируют проблему затухающего градиента.
  • Сигмоида и tanh чувствительны к глубине сети, требуют дополнительной нормализации и аккуратной инициализации.

5. Использование коротких связей (Residual Connections)

ResNet-подобные архитектуры позволяют градиентам проходить напрямую через слои:

const input = tf.input({shape: [128]});
const dense1 = tf.layers.dense({units: 128, activation: 'relu'}).apply(input);
const dense2 = tf.layers.dense({units: 128}).apply(dense1);
const output = tf.layers.add().apply([dense2, input]); // residual connection

Это предотвращает затухание сигнала в глубоких сетях.

Практическая демонстрация в TensorFlow.js

Создание модели с возможностью наблюдать эффект затухающего градиента:

const model = tf.sequential();
model.add(tf.layers.dense({units: 256, inputShape: [784], activation: 'sigmoid', kernelInitializer: 'glorotNormal'}));
for (let i = 0; i < 10; i++) {
  model.add(tf.layers.dense({units: 256, activation: 'sigmoid'}));
}
model.add(tf.layers.dense({units: 10, activation: 'softmax'}));

model.compile({
  optimizer: tf.train.adam(),
  loss: 'categoricalCrossentropy',
  metrics: ['accuracy']
});

Сигмоида и глубокая архитектура наглядно демонстрируют затухание градиентов: веса первых слоёв обновляются крайне медленно, что приводит к медленной сходимости. Замена активации на ReLU и добавление batch normalization решают проблему.

Влияние на обучение и выбор архитектуры

  • Глубокие сети с небольшими градиентами требуют более медленного обучения и часто нестабильны.
  • Сильные градиенты могут привести к взрывному росту весов и NaN-результатам.
  • Правильная комбинация инициализации, нормализации и ограничений градиентов позволяет строить стабильные глубокие модели в TensorFlow.js.

Мониторинг градиентов

TensorFlow.js позволяет анализировать значения градиентов через tf.variableGrads:

const {value, grads} = tf.variableGrads(() => loss(predictions, labels));
console.log(grads['dense/kernel'].dataSync());

Отслеживание градиентов помогает выявить моменты затухания или взрыва и корректировать архитектуру или оптимизатор.