Обнаружение взрывного и затухающего градиента

TensorFlow.js предоставляет мощные инструменты для вычисления градиентов и оптимизации нейронных сетей прямо в браузере или на сервере с Node.js. Градиенты — это производные функции потерь по параметрам модели, используемые для обновления весов методом градиентного спуска.

В TensorFlow.js ключевыми объектами являются tf.Tensor, tf.Variable и функции автоматического дифференцирования tf.grad и tf.variableGrads. Тензоры неизменяемы, поэтому для обучения модели используются переменные, которые можно обновлять.

const w = tf.variable(tf.scalar(Math.random()));
const x = tf.tensor([1, 2, 3, 4]);
const f = () => w.square().sum();
const grad = tf.grad(f);
const gradient = grad(w);

В примере вычисляется производная функции потерь по весу w. Это основа, на которой строится обучение нейронных сетей.


Причины возникновения взрывного и затухающего градиента

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

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

Формально, если сеть имеет L слоев, а градиент для веса первого слоя вычисляется как произведение производных всех слоев:

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

Если производные больше 1, градиент растёт экспоненциально (взрывной). Если меньше 1 — затухает.


Практика обнаружения проблем с градиентами

Для анализа градиентов в TensorFlow.js можно использовать прямой вычислительный подход:

const model = tf.sequential();
model.add(tf.layers.dense({units: 50, inputShape: [100], activation: 'relu'}));
model.add(tf.layers.dense({units: 50, activation: 'relu'}));
model.add(tf.layers.dense({units: 10, activation: 'softmax'}));

const x = tf.randomNormal([1, 100]);
const y = tf.oneHot([2], 10);

const optimizer = tf.train.adam(0.01);

for (let i = 0; i < 10; i++) {
  const grads = tf.variableGrads(() => {
    const preds = model.apply(x);
    return tf.losses.softmaxCrossEntropy(y, preds).mean();
  });

  Object.keys(grads.grads).forEach(name => {
    const norm = grads.grads[name].norm().dataSync()[0];
    console.log(`${name}: градиент = ${norm}`);
  });

  optimizer.applyGradients(grads.grads);
}

В этом коде вычисляются нормы градиентов для каждого веса. Резкий рост нормы указывает на взрывной градиент, очень маленькие значения — на затухающий.


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

  1. Обрезка градиента (Gradient Clipping) Ограничение максимальной нормы градиента предотвращает переполнение:
const clippedGrads = {};
Object.keys(grads.grads).forEach(name => {
  clippedGrads[name] = tf.clipByValue(grads.grads[name], -1, 1);
});
optimizer.applyGradients(clippedGrads);
  1. Использование подходящей инициализации весов

    • Xavier (Glorot) и He инициализация уменьшают риск взрыва градиентов. Пример для слоя Dense:
tf.layers.dense({
  units: 50,
  kernelInitializer: 'heNormal',
  activation: 'relu'
});
  1. Понижение скорости обучения Большие значения learning rate усиливают взрыв градиентов. Уменьшение коэффициента стабилизирует обучение.

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

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

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

    • BatchNormalization помогает поддерживать значения активаций в диапазоне, благоприятном для градиентов.
tf.layers.batchNormalization({axis: -1});
  1. Инициализация весов

    • Xavier и He инициализация также полезны для предотвращения затухания, особенно в глубоких сетях.
  2. Глубокие остаточные связи (Residual Connections)

    • В ResNet и других архитектурах градиенты проходят через дополнительные прямые соединения, что предотвращает их исчезновение.

Мониторинг градиентов в процессе обучения

Для устойчивого обучения важно постоянно отслеживать нормы градиентов. TensorFlow.js предоставляет возможность выполнять это через метод tf.variableGrads и вычислять tf.norm. Графики изменения градиентов позволяют определить, на каком слое возникает взрыв или затухание, что упрощает отладку и настройку модели.