Simon Kozlov:
Так вот, все обещал и все никак не находилось времени.
После прочтения совершенно замечательной статьи Andrej Karpathy про RNN (http://karpathy.github.io/2015/05/21/rnn-effectiveness/ ) мне не давали покоя несколько моментов, которые я наконец понял!
(кстати, статья я так понимаю оказалась важной не только в смысле популяризации баззворда, но и в академии - работ про character-level RNN стало гораздо больше, раньше минимальным юнитом были в основном слова)
Момент первый и самый важный - а как конкретно тренировка-то работает?
Вот есть у нас символы, есть их предсказания, что на одном пассе вычисления делается?
Пишут, что это Stochastic gradient descent, вот мы выбрали в батче случайные символы.
Но ведь результат на них зависит от прошлого.
Одним словом, кругом вопросы.
Ну так вот, работает это следующим образом.
Когда мы берем один сэмпл для обучения, мы внутри этого сэмпла "разворачиваем" RNN на N символов назад.
Т.е. для каждого символа представляем, что его аутпут это не результат пусть двух уровней RNN,
а что это результат очень глубокой сети, которая сначала вычислила стейт N шагов назад, потом на основе его вычислила стейт на шаге N-1 и таким образом дошла до текущего шага.
"разворачиваем" это для каждого сэмпла. У многих сэмплов части пути будут пересекаться, это нормально.
И вот делаем backpropagation сквозь всю эту развернутую цепь,
это называется backpropagation through time.
Т.е. градиенты передаются на прошлые шаги, коих N.
И понятно потом все складываются в градиент для матриц, которые "shared" между этими шагами.
И когда у Karpathy написано "Lets train a 2-layer LSTM with 512 hidden nodes (approx. 3.5 million parameters), and with dropout of 0.5 after each layer. We'll train with batches of 100 examples and truncated backpropagation through time of length 100 characters.".
то это значит, что в батче 100 случайных символов и для каждого из них "разматывается" сетка на 100 шагов назад.
Мигните что-ли кто-нибудь.
Sergey Kishchenko:
Я тут. Это интересно. Т.е., ничем не отличается от просто глубокой нейросетки, которую мы тренируем последовательностями символов по N?
Simon Kozlov:
Да, примерно так.
И у этой глубокой сетки шарятся веса на каждом уровне.
Отчего собственно и наступает хорошая генерализация.
Simon Kozlov:
Момент второй - WTF is LSTM?
Дело в том, что в виде описанном в посте RNN очень нестабильны -
стейт переносится с прошлого шага умножением на некоторую матрицу.
Если эта матрица хоть немного "увеличивает сигнал", то за N шагов стейт экспоненциально улетает в небеса.
Если она наоборот сигнал уменьшает, то за N шагов он почти 0 и градиентам очень тяжело дойти до первых слоев через все уровни backpropagation.
Посему либо saturating gradients, либо взрыв.
LSTM это попытка апдейтить стейт более мягким способом.
Вот как предлагается это делать:
Итак, есть вот память на внутреннем слое, которая хранит массив чисел.
На каждом шаге к ней приходит инпут из значений предыдущего слоя,
и ее аутпут используется как стейт для следующего слоя.
В дополнение к матрице, трансформиующей стейт, вводятся три новых матрицы.
input gate - это матрица, которая получает на вход инпут и прошлый стейт и преобразовывает его с нелинейностью (кажется сигмоид)
И вот изменение стейта, полученное через "стандартный путь" умножается на результат этого input gate и добавляется к стейту.
Кроме этого есть forget gate, которой получает на вход тоже инпут и прошлый стейт и дает вес, на который умножается текущий стейт (т.е. если гейт выдает 0 - то стейт "забывается", а если 1 - целиком сохраняется)
И наконец Output gate, который получает все то же самое и дает вес, на который умножается стейт прежде чем пойти в output.
Т.е. к одному преобразованию добавляется три косвенных, причем все они контролируют насколько далеко имеет смысл отойти от текущего стейта,
и от этого апдейт внутреннего стейта от шага к шагу становится более стабильным.
Обучение всех этих гейтов происходит точно так же, как раньше -
просто считается backpropagation through time.
Фух, вроде все поправил после fact-checking :)
Sergey Kishchenko:
Это прикольно. А можно ли делать какое-то другое замедление, вместо forget gate? Kalman filter, скользящее среднее, например?
Simon Kozlov:
Я уверен, люди пробовали разное.
Вот это работает. Не единственное, но нынче популярное.
Sergey Kishchenko:
Вообще, любопытно. Схема вроде довольно простая.
Спасибо за пояснения!
Михаил Окунев:
Спасибо за рассказ, вроде понятно даже. А есть какое-то объяснение почему это все работает?
То есть, вписан ли этот метод в некий более общий теоретический фреймворк.
Или "оно просто работает"?
Из того, что однажды говорил один товарищ из AI group, я понял, что скорее второе и что уже есть идеи чем LSTM заменять.
Simon Kozlov:
Какой нахер теоретический фреймворк, это ж deep learning :)
Михаил Окунев:
В смысле, LSTM -- это подвид некоторого класса методов (что интересно) или обособленный хак?
Simon Kozlov:
Ну, я так понимаю, основная мотивация - это то, что я описал выше, а то, что работает именно такая конфигурация - вопрос практики.
Хотя, конечно, наверняка есть дополнительное научное объяснение такого выбора.
Но главный критерий скорее практика.