Функции активации в RNN. Двунаправленные (bidirectional) RNN-слои

Смотреть материал на YouTube | RuTube

В программах наших занятий мы в рекуррентном слое использовали функцию активации tanh – гиперболический тангенс. И это довольно частый выбор. Однако когда рассматривали полносвязные и сверточные нейронные сети, то там рекомендовалось применять другую функцию активации – ReLU. Возникает вопрос, почему в рекуррентных слоях мы не следуем этому правилу сетей прямого распространения? Давайте разберемся.

Как мы уже знаем, любой рекуррентный слой можно развернуть во времени и представить в виде следующего графа вычислений:

Также мы знаем, что на каждой итерации выходное значение вектора скрытого состояния вычисляется по формуле:

Теперь, если формально расписать порядок вычисления производной для некоторой выбранной функции потерь:

по матрице весовых коэффициентов W, получим выражение:

Что из него следует? Смотрите, если выбрать в качестве функции активации ReLU:

То ее производная будет либо 0, либо 1. Пусть, для простоты, производная от ReLU у нас всюду получается равной 1. Тогда в формуле вычисления производной от функции потерь в слагаемых появляется умножение на матрицу V в степени  на каждом k-м рекуррентном шаге:

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

Хорошо, давайте теперь посмотрим, чем гиперболический тангенс будет качественно отличаться от ReLU. Его график вместе с производной выглядит следующим образом:

А производная функции потерь будет содержать такие слагаемые:

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

Ну а что насчет сигмоидной функции активации:

Производная функции потерь будет иметь вид:

А график производной сигмоиды следующим образом:

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

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

Как бороться со взрывом градиентов, мы с вами уже говорили. Хорошей эвристикой здесь является ограничение их величины некоторым пороговым уровнем. А вот побороть их быстрое затухание, задача куда сложнее. Для ее решения были предложены более сложные схемы рекуррентных слоев LSTM и GRU, о которых мы еще будем говорить.

Двунаправленные рекуррентные сети

Но есть еще один подход, позволяющий учесть больше информации в векторе скрытого состояния даже при использовании простого элмановского рекуррентного слоя. Что это за подход? Смотрите, не редко бывают задачи, когда известна вся последовательность целиком и ее нужно обработать. Например, разделить короткие фразы по семантической окраске: негативное или позитивное высказывание. Или же понять гендерную принадлежность фразы: мужская, женская. И так далее. Что дает нам наличие всех элементов обрабатываемой последовательности? Очевидно то, что можно сначала пройти по всем ее элементам в одном (прямом) направлении, а затем, в обратном, формируя два вектора скрытого состояния. И уже на основе этих двух векторов вычислять выходные значения. Приходим к идее двунаправленных рекуррентных слоев.

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

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

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

с начальным значением . Здесь используются свои матрицы преобразований  и смещения .

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

А затем, он пропускается через полносвязный слой, формируя итоговое выходное значение для текущего шага рекурсии:

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

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

Видео по теме