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

Определение проблемы

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

Итак, следуя общепринятым стандартам, мы будем называть наши веса для L [1] как w [1], веса для L [2] как w [2] аналогично вплоть до весов для слоя L [n] как w [ п]. Далее, чтобы упростить задачу на этом этапе, предположим, что мы используем функцию линейной активации, такую ​​чтоg(z) = z. А пока давайте все также проигнорируем b и скажем, что b [l] = 0 для alll ∈ (1, n).

В этом случае теперь мы можем просто написать:

Теперь давайте сделаем здесь несколько замечательных наблюдений, так как b = 0,

Итак, давайте теперь сделаем выражение для [2], так что [2] будет

Итак, с этим и предыдущим уравнением мы получаем, что a [2] = w [1] w [2] X, продолжая это a [3] = w [1] w [2] w [3] X и так далее.

Взрывающиеся градиенты

Давайте теперь просто скажем, что все наши w [l] просто больше, чем личность. Поскольку наши w, конечно, являются матрицами, было бы лучше поместить их просто больше, чем единичная матрица, за исключением w [l], которая имела бы другие размеры, чем остальные w. То, что мы только что определили как нечто большее, чем единичная матрица, назовем C = 1.2 ⋅ I, где I - единичная матрица 2 X 2. Теперь мы можем легко упростить приведенное выше выражение до:

если L был большим, как это бывает для очень глубокой нейронной сети, y-шляпа будет очень большой. На самом деле он просто растет экспоненциально, растет как 1,2 в пересчете на количество слоев. Итак, если у вас очень глубокая нейронная сеть, значение y просто взорвется.

Исчезающие градиенты

Теперь и наоборот, если мы заменим здесь 1,2 на 0,5, что будет меньше 1. Это сделает наше C = 0,5 I. Итак, теперь у вас снова 0,5ˡ⁻¹. Таким образом, при этом значения активации будут экспоненциально уменьшаться в зависимости от количества слоев L сети. Таким образом, в очень глубокой сети количество активаций уменьшается экспоненциально.

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

Зачем заботиться?

В сегодняшнюю эпоху для многих этих задач вам может потребоваться глубокая нейронная сеть с сетью из 152 или 256 слоев, в этом случае эти значения могут быть очень большими или очень маленькими. Что сильно повлияет на вашу тренировку и сделает ее намного сложнее. Просто рассмотрите этот сценарий, ваши градиенты экспоненциально меньше, чем L, тогда градиентный спуск будет делать крошечные маленькие шаги и потребует много времени для градиентного спуска, чтобы узнать что-либо или сходиться. Вы только что видели, как глубокие сети страдают от проблем с исчезновением или взрывом градиентов. Теперь было бы замечательно и посмотреть, как мы могли бы решить эту проблему с помощью тщательной инициализации весов, давайте посмотрим, как мы это сделаем.

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

Оказывается, решение для этого, которое очень помогает, - это более тщательный выбор случайной инициализации для вашей нейронной сети. Давайте сначала разберемся с этим с помощью одного нейрона. Что-то вроде этого-

Итак, у вас есть входы от x1 до x4, а затем некоторая функция a = g(z), которая выводит ваш y. Примерно так обстоит дело с любым нейроном. Итак, позже, когда мы продолжим обобщать это, x будут заменены некоторым слоем a [l], но пока мы будем придерживаться x. Не что-то новое, но давайте просто составим формулу, чтобы прояснить наше понимание. Обратите внимание, что сейчас мы просто игнорируем b, устанавливая b = 0.

Итак, чтобы z не взорвался или не стал слишком маленьким, вы замечаете, что чем больше n, тем меньше вы хотите, чтобы ваш w[i] был, поскольку z - это сумма w[i] и x[i], где n - количество входных функций, входящих в ваш нейрон, который находится в этом примере 4. И когда вы добавляете много этих терминов, которые являются большими n, вы хотите, чтобы каждый член был меньше. Теперь вы можете сделать разумную вещь здесь, а именно установить отклонение (примечание: отклонение здесь представлено в соответствии с общепринятыми стандартами как var()) w[i], которое должно быть обратным вашему n. Что просто означает var(w[i]) = 1/n. Итак, что вы бы сделали на практике:

Обратите внимание, что S здесь относится к форме матрицы.

Поскольку n[l-1] - это количество входов, которые получит нейрон на слое l. Если вы используете наиболее распространенную функцию активации ReLu, можно заметить, что вместо того, чтобы устанавливать отклонение равным 1/n, вы должны вместо этого установить отклонение равным 2/n. Итак, давайте резюмируем это -

Итак, если вы знакомы со случайными величинами, теперь вы можете увидеть, как все это собрано вместе. Если вы возьмете гауссову случайную величину и умножите ее на квадратный корень из 2/n[l-1], я получу дисперсию, равную 2/n. И если вы заметили, что раньше мы говорили в терминах n, а теперь перешли к n[l-1], чтобы обобщить его для любого слоя l, так как слой l будет иметь n[l-1] входных характеристик для каждой единицы или каждого нейрона в этом слое.

И теперь у вас есть стандартная дисперсия, равная 1, и это приведет к тому, что ваш z тоже будет в аналогичной шкале. При этом ваш w[l] будет не намного больше 1 и не меньше 1. Теперь, это определенно поможет вашим градиентам не исчезнуть или взорваться слишком быстро.

Варианты

Мы только что предположили функцию активации ReLu или линейную функцию активации. Итак, давайте посмотрим, что произойдет, если вы воспользуетесь какой-либо другой функцией активации.

  • танх

Есть замечательная статья, которая показывает, что когда вы используете гиперболический загар или просто загар, было бы лучше умножить его на корень из 1/n[l-1], а не на 2/n[l-1]. Я имею в виду, что у вас будет примерно такое уравнение:

Однако мы не будем здесь доказывать этот результат. Это чаще называют инициализацией Xavier или инициализацией Glorot по имени его первооткрывателей.

Также был вариант, который снова имеет теоретическое обоснование и также может быть замечен в нескольких статьях -

Однако я бы порекомендовал вам использовать Формулу (2), когда вы используете ReLu, которая является наиболее распространенной функцией активации, если вы используете tanh, вы можете попробовать и то, и другое.

Делаем это лучше

Есть еще одна идея, которая может помочь вам сделать вашу инициализацию еще лучше. Рассмотрим формулу (2) и добавим к ней параметр γ, не путать с гиперпараметром SVM γ. Мы умножим это на нашу исходную формулу -

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

Реализация с помощью TensorFlow

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

  • Глорот нормальный или Ксавье нормальный
import tensorflow as tf
tf.keras.initializers.glorot_normal(seed=None)

Он отбирает выборки из усеченного нормального распределения с центром в 0 с помощью stddev = sqrt(2 / (fan_in + fan_out)), где fan_in - количество входных единиц в тензоре весов, а fan_out - количество выходных единиц в тензоре весов.

  • Униформа Glorot или форма Xavier
import tensorflow as tf
tf.keras.initializers.glorot_uniform(seed=None)

Он извлекает выборки из равномерного распределения в пределах [-limit, limit], где limit равно sqrt(6 / (fan_in + fan_out)), где fan_in - количество входных единиц в тензоре весов, а fan_out - количество выходных единиц в тензоре весов.

Оба этих фрагмента кода возвращают объект инициализатора, и вы можете очень легко его использовать. Вы также видели, как легко это было сделать с помощью TensorFlow.

Заключение

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

Обо мне

Всем привет, я Ришит Дагли

LinkedIn - l inkedin.com/in/rishit-dagli-440113165/

Сайт - rishit.tech

Если вы хотите задать мне несколько вопросов, сообщить о любой ошибке, предложить улучшения, оставить отзыв, вы можете сделать это, отправив мне письмо по адресу -