Практический анализ проблем обучения нейронной сети Tensorflow (Nan)

TensorFlow

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

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

1. Феномен

Во время процесса обучения модели Tensorflow, во многих случаях, после обучения в течение определенного периода времени, выходная потеря равна nan, К счастью, это может быть вспышка, а затем обычное обучение, большая часть которого по-прежнему остается nan, а обучение терпит неудачу.

Если вы также выводите градиент (градиент), также возможно, что градиент выводится сначала как nan.

2. Причина

2.1 Лаконичные и популярные принципы

С точки зрения непрофессионала, нейронная единица самого основного узла нейронной сети математически выражается какyi=f(wixi+bi)y_{i}=f(w_{i}x_{i}+b_{i}), как правило, отслеживает, как каждый узел влияет на вывод в режиме обратной деривации. Это обратное распространение с преобладанием ошибок, целью которого является получение оптимальной матрицы глобальных параметров (весовой параметр).

Когда количество слоев нейронной сети велико и количество нейронных узлов велико, числовая устойчивость модели легко ухудшится. Предположим, что слойLLсеть,llЭтажHlH_{l}Параметр весаWlW_{l}, для удобства обсуждения параметр смещения опущенbb. для данногоXXвходная сеть, с.llвывод слояHl=XW1W2...WlH_{l}=XW_{1}W_{2}...W_{l}. В настоящее время,HlH_{l}Он склонен к распаду или взрыву.

Например, появится вывод слоя 30.0.2301×10210.2^{30} \approx 1 \times 10^{-21}, ослабленный.

Когда мы обучаем нейронную сеть, думайте о «потерях» как о функции «параметра веса». Таким образом, появление нан-феноменwixi+bi\sum w_{i}x_{i}+b_{i}в результате пересечения границы.

Итак, что нам нужно сделать, так это предотвратитьwixi+bi\sum w_{i}x_{i}+b_{i}за границами.

Обратное распространение заключается в настройке параметров в направлении отрицательного градиента цели, и параметры обновляются следующим образом:wiwi+Δww_{i} \leftarrow w_{i} + \Delta w.

заданная скорость обученияα\alpha, предполагается:Δw=αLossw\Delta w = -\alpha \frac{\partial Loss}{\partial w}

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

Если значение градиента функции активации вблизи выходного слоя больше 1 после вывода, то при увеличении количества слоев конечный градиент легко растет экспоненциально, то есть градиент взрывается; наоборот, если он меньше 1, то цепное правило в виде умножения легко затухнет до 0, то есть исчезнет градиент.

2.2 Причина

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

  1. Значение параметра или градиента веса узла постепенно увеличивается, пока не превысит границу;
  2. Существуют операции деления на ноль, в том числе деление 0 на 0, что является обычным для предсказания регрессии тренда, или перекрестная энтропия принимает логарифм 0 или отрицательные числа;
  3. Во входных данных есть аномалия, слишком большой/слишком маленький ввод, что приводит к мгновенному NAN.

3. Решения

3.1. Уменьшите скорость обучения

Уменьшение скорости обучения (learning_rate) — самый простой и простой способ.

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

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

3.2 Инициализация параметра веса

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

Соответствующие начальные значения сети не только помогают методу градиентного спуска найти оптимальное значение на хорошей «стартовой точке».

Обычно наша общая инициализация — это инициализация случайного нормального распределения, а инициализация весов и смещений —со средним значением 0 и стандартным отклонением 1Стандартный метод рандомизации нормального распределения. Но лучшая ли это стратегия инициализации?

        #标准差为0.1的正态分布
        def weight_variable(shape,name=None):
            initial = tf.truncated_normal(shape,stddev=0.1)
            return tf.Variable(initial,name=name)
        #0.1的偏差常数,为了避免死亡节点
        def bias_variable(shape,name=None):
            initial = tf.constant(0.1, shape=shape)
            return tf.Variable(initial,name=name)

При использовании relu лучше использовать инициализацию масштабирования дисперсии (he initial).

В TensorFlow метод масштабирования дисперсии записывается как tf.contrib.layers.variance_scaling_initializer(). Согласно нашим экспериментам, этот метод инициализации имеет лучшую производительность обобщения/масштабирования, чем обычная гауссовская инициализация, усеченная гауссовская инициализация и инициализация Ксавьера.

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

        #def weight_variable(shape,name=None):
        #    return tf.get_variable(name, shape ,tf.float32 ,xavier_initializer())
        
        def weight_variable(shape,name=None):
            return tf.get_variable(name, shape ,tf.float32 ,tf.variance_scaling_initializer())

tf.variance_scaling_initializer()

Параметры: (масштаб = 1,0, режим = «fan_in», распределение = «нормальный», seed = нет, dtype = dtypes.float32)

Улучшен тренировочный эффект:

step: 11800, total_loss: 161.1809539794922,accuracy: 0.78125
step: 11850, total_loss: 46.051700592041016,accuracy: 0.9375
step: 11900, total_loss: 92.10340118408203,accuracy: 0.875
step: 11950, total_loss: 23.025850296020508,accuracy: 0.96875

Если в функции активации используются сигмоид и тангенс, то лучше использовать xavir.

Метод xavier_initializer(): этот инициализатор используется для поддержания примерно одинакового размера градиента каждого слоя.

WU[6nj+nj+1,6nj+nj+1]W \sim U [ - \frac {\sqrt{6}} {\sqrt{n_{j}+n_{j+1}}} , \frac{\sqrt{6}}{\sqrt{n_{j}+n_{j+1}}}]

Общие методы инициализации:

  1. tf.constant_initializer() константная инициализация
  2. tf.ones_initializer() все 1 инициализация
  3. tf.zeros_initializer() Все 0 инициализации
  4. tf.random_uniform_initializer() инициализация равномерного распределения
  5. tf.random_normal_initializer() инициализация нормального распределения
  6. tf.truncated_normal_initializer() инициализация усеченного нормального распределения
  7. tf.uniform_unit_scaling_initializer() Входная дисперсия этого метода постоянна.
  8. tf.variance_scaling_initializer() адаптивная инициализация
  9. tf.orthogonal_initializer() генерирует ортогональную матрицу

Иногда, чтобы убедиться, что начальное значение w достаточно мало, общая инициализация |w| намного меньше 1, обычно рекомендуется использовать tf.truncated_normal_initializer(stddev=0.02)

3.3. Обрезка результатов прогнозирования

Значение Nan появляется при расчете потерь перекрестной энтропии, давайте посмотрим на перекрестную энтропию:loss=i=1nyilog(y^i)loss=-\sum_{i=1}^{n} y_{i} log(\hat{y}_{i})

Несмотря на то чтоy^\hat{y}Получается через функцию tf.nn.sigmoid, но когда выходной параметр очень большой или очень маленький, выдаст граничное значение 1 или 0.

Итак, для следующего кода: cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv), name='cost_func') изменить на: cross_entropy = -tf.reduce_sum(y_*tf.log(tf.clip_by_value(y_conv, 1e-10, 1.0)), name='cost_func') # Максимальное значение 1, после нормализации 0~1

            #第二全连接层,即输出层,使用柔性最大值函数softmax作为激活函数
            y_conv = tf.nn.softmax(tf.matmul(h_fc2_drop,W_fc3) + b_fc3, name='y_')
            
            # 使用TensorFlow内置的交叉熵函数避免出现权重消失问题
            cross_entropy = -tf.reduce_sum(y_*tf.log(tf.clip_by_value(y_conv, 1e-10, 1.0)), name='cost_func') # 最大值是1,归一化后0~1
            

3.4 Градиентная обрезка

Для решения проблемы взрыва градиента в тензорном потоке принцип очень прост: обрезка градиента, обрезка производной больше 1 до 1.

Функция обрезки градиента Tensorflow — tf.clip_by_value(A, min, max): Введите тензор A, сожмите значение каждого элемента в A между min и max. Меньше min делает его равным min, а элементы больше max имеют значение, равное max.

            #使用优化器
            lr = tf.Variable(learning_rate,dtype=tf.float32)
            #train_step = tf.train.AdamOptimizer(lr).minimize(cross_entropy)           
            optimizer = tf.train.AdamOptimizer(lr)
            gradients = optimizer.compute_gradients(cross_entropy)
            #梯度优化裁剪   
            capped_gradients = [(tf.clip_by_value(grad, -0.5, 0.5), var) for grad, var in gradients if grad is not None]
            train_step = optimizer.apply_gradients(capped_gradients) 

Кроме того, сеть рекомендует clip_by_global_norm:

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

3.5 Прочее

  1. Уменьшить размер ванны

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

  1. нормализация ввода

Для входных данных класс изображения легче решить и нормализовать 0-255 до 0~1, в то время как другие данные необходимо анализировать и обрабатывать в соответствии с распределением данных, и принимается соответствующая схема нормализации.

  1. Отрегулируйте функцию активации

В настоящее время нейронные сети часто используют relu в качестве функции активации, но проблема Nan может возникнуть, и функцию активации можно изменить в соответствии с реальной ситуацией.

  1. batch normal

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

4. Резюме

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

Начало всего сложно Во-первых, оптимизируйте скорость обучения, начальный вес, функцию активации и т. Д. И минимизируйте промежуточное вмешательство.

Даже в этом случае будут проблемы, такие как Нэн, что мне делать? Прервите тренировку и снова начните новый раунд тренировки.

В связи с ограниченным уровнем автора обратная связь приветствуется.

Ссылаться на:

1. «Выбор Tensorflow LSTM функции активации Relu и инициализации веса, обрезка градиента для решения проблемы практики градиентного взрыва»Наггетс, Сяо Юнвэй, март 2021 г.

2. «Математические принципы и решения задачи исчезновения и взрыва градиента».Блог CSDN, ty44111144ty, 2019.08

3. "О tf.nn.softmax_cross_entropy_with_logits и tf.clip_by_value"Blog Garden, официальный аккаунт WeChat — Resonance Circle, 2017.03

4. «Общие причины и решения Tensorflow NAN»Блог CSDN, Су Рансюй, февраль 2019 г.

5. «Понимание алгоритма обратного распространения в одной статье»Цзянь Шу, Чжан Сяолэй, август 2017 г.

6. «TensorFlow от 0 до 1 — 15 — переосмысление инициализации нейронной сети»Чжиху, Юань Чэнсин, июль 2020 г.

7. «Углубленное понимание глубоких нейронных сетей»Блог CSDN, Сяо Юнвэй, июнь 2020 г.

8. «TensorFlow реализует пакетную нормализацию»Script House, marsjhao, март 2018 г.