Rnn что это
Перейти к содержимому

Rnn что это

  • автор:

Стэнфордский курс: лекция 10. Рекуррентные нейронные сети

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

Рекуррентные сети: введение

Мы уже знакомы с основными принципами построения архитектур для самых простых нейросетей. Обычные модели состоят из группы слоёв и принимают на вход объект фиксированного размера — например, изображение. Каждый слой применяет к этому объекту какие-либо преобразования, и на выходе мы получаем результат — для классификации это будет метка класса. Но в некоторых областях машинного обучения нам хотелось бы иметь большую гибкость в типах данных, которые могли бы обрабатывать наши модели.

Именно это позволяют делать рекуррентные нейронные сети (Recurrent Neural Network, RNN). Иногда их делят на несколько разновидностей: “one to one”, “one to many”, “many to one” и “many to many”.

К архитектурам “one to one” можно отнести модели, которые мы рассматривали ранее, — с определённым размером входных и выходных данных. В случае “one to many” при заранее заданном типе и размере входного объекта можно получить вывод разной длины. Такой подход применяется в популярной задаче описания изображений (image captioning). Вариант “many to one” работает ровно наоборот — мы подаём на вход данные нефиксированного размера и получаем их чётко определённые характеристики. Так, например, можно по фрагменту видео определять вид активностей или действия, которые в нём происходят.

И, наконец, “many to many” архитектуры имеют варьирующиеся размеры как входных, так и выходных данных. К решаемым ими задачам относятся машинный перевод (исходная и переведённая фразы могут быть разной длины) и покадровая классификация видео.

Рекуррентные нейросети очень полезны даже при решении задач “one to one”. Рассмотрим популярную проблему распознавания рукописных цифр. Вместо того, чтобы просто сделать один прямой проход и сразу выдать решение, рекуррентная сеть быстро «просматривает» различные части изображения. В терминологии этот процесс называется «проблеск» (glimpse). Сделав несколько таких проблесков, модель принимает окончательное решение о том, какое число изображено на фотографии. Это позволяет существенно повысить точность распознавания и лучше контролировать процесс обучения.

Итак, как же устроена рекуррентная нейросеть? Внутри архитектуры располагается базовая рекуррентная ячейка. Модель принимает некоторые входные данные x и отправляет их в RNN, которая имеет скрытое внутреннее состояние. Это состояние обновляется каждый раз, когда в RNN поступают новые данные. Часто нам необходимо, чтобы RNN генерировала некоторый вывод на каждом временном отрезке. Поэтому после чтения входных данных и обновления скрытого состояния RNN будет создавать выходные данные.

Попробуем подробнее разобраться, что же происходит в загадочном зелёном прямоугольнике с надписью RNN. Внутри него мы вычисляем рекуррентное соотношение с помощью функции f, которая зависит от весов w. Чтобы найти новое состояние ht, мы берём предыдущее скрытое состояние ht⁻1, а также текущий ввод xt. Когда мы отправляем в модель следующие входные данные, полученное нами скрытое состояние ht передаётся в эту же функцию, и весь процесс повторяется.

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

Самая простая реализация рекуррентной сети будет выглядеть следующим образом:

Тангенс здесь используется для введения нелинейности в систему.

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

Возвращение к вычислительным графам

Если вы не знакомы с вычислительными графами, о них можно прочитать в этой лекции.

На первом временном шаге у нас есть первоначальное скрытое состояние h0. Обычно оно инициализируется нулём. Также на вход подаются данные xt и веса W — всё это отправляется в функцию f, которая вычисляет новое состояние h1. И с каждыми новыми входными данными мы повторяем процесс.

Очередное новое состояние даёт нам выходные данные yi, с помощью которых мы можем посчитать потери Li. Просуммировав все Li, мы получим полные потери нашей модели. Такой подход используется в “many to many” архитектурах и наглядно показан на рисунке ниже:

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

В случае, если мы хотим подавать на вход данные фиксированного размера и получать разнообразный вывод (“one to many”), из нашего графа исчезнут разные варианты xi. Модель будет выглядеть следующим образом:

С “many to one” ситуация будет обратная:

Также есть более сложные модели “many to many”, применяющиеся в машинном переводе, — они называются “sequence to sequence”. По сути это комбинация методов “many to one” и “one to many”, которые располагаются друг за другом и называются энкодер и декодер соответственно. Энкодер получает данные различной длины — например, предложение на английском языке. С помощью скрытых состояний он формирует из исходных данных вектор, который затем передаётся в декодер. Последний, в свою очередь, генерирует из полученного вектора выходные данные — исходную фразу, переведённую на другой язык.

Подробнее о том, как выглядят нейросети для машинного перевода и, в частности, Google-переводчик, можно прочитать в статье Google преодолевает барьер между человеческим и машинным переводом. А мы вернёмся к распознаванию изображений и поговорим об image captioning.

Image captioning

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

Чтобы рекуррентная сеть понимала, где именно начинается предложение, во время обучения на её вход подаётся стартовый опознавательный знак ( token). Для построения фразы используется заранее подготовленный словарь, например, из английских слов — и он может быть довольно большим.

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

Обычно подобные архитектуры создаются с помощью контролируемого обучения (supervized learning) — это означает, что в обучающих датасетах уже присутствуют как изображения, так и описания для них. Наиболее популярным и самым большим набором данных является Microsoft COCO. Помимо image captioning он также применяется для сегментации, поиска ключевых точек и даже построения трёхмерной модели человека на основе его позы.

Image captioning + Attention

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

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

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

Существуют также понятия мягкого и жёсткого внимания (soft and hard attention). При мягком внимании мы берём взвешенную комбинацию признаков по всему изображению, тогда как в случае жёсткого внимания мы заставляем модель выбирать только один небольшой участок для обработки на каждом временном шаге. При этом жёсткое внимание, строго говоря, не является дифференцируемой функцией. Поэтому для обучения такой модели необходимо использовать более изощрённые приёмы, чем обычное обратное распространение ошибки. Мы подробнее затронем эту тему в одной из следующих лекций.

Также нейросети, основанные на внимании, повсеместно используются для ответов на визуальные вопросы (Visual Question Answering). Цель этой задачи — обучить модель отвечать на вопрос по изображению. Например, она должна уметь не только называть сами объекты на фотографии, но и считать их, распознавать цвета и оценивать расположение относительно друг друга. Мы уже рассказывали о подобных архитектурах в статье о том, как такие нейросети могут помочь незрячим людям и о нейро-символическом мышлении.

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

С оригинальной лекцией можно ознакомиться на YouTube.

Разнообразие нейронных сетей. Часть вторая. Продвинутые конфигурации

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

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

Разнообразие нейронных сетей. Часть вторая. Продвинутые конфигурации 1

Глубинные свёрточные обратные графические сети (deep convolutional inverse graphics networks, DCIGN) названы слегка некорректно, поскольку они по сути являются вариационными автокодировщиками, кодирующая и декодирующая части которых представлены свёрточной и развёртывающей НС соответственно. Сети такого типа моделируют свойства в виде вероятностей, поэтому их можно научить создавать картинку с собакой и кошкой, даже если сеть видела только картинки, на которых было только одно из животных. Возможно и удаление одного из двух объектов. Также были созданы сети, которые могли менять источник освещения и вращать объект. Сети такого типа обычно обучают методом обратного распространения ошибки.

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

Разнообразие нейронных сетей. Часть вторая. Продвинутые конфигурации 2

Генеративные состязательные сети (generative adversarial networks, GAN) — это сети другого вида, они похожи на близнецов. Такие сети состоят из любых двух (обычно из FF и CNN), одна из которых контент генерирует, а другая — оценивает. Сеть-дискриминатор получает обучающие или созданные генератором данные. Степень угадывания дискриминатором источника данных в дальнейшем участвует в формировании ошибки. Таким образом, возникает состязание между генератором и дискриминатором, где первый учится обманывать первого, а второй — раскрывать обман (похоже на ситуацию “банкир-фальшивомонетчик”). Обучать такие сети весьма тяжело, поскольку нужно не только обучить каждую из них, но и настроить баланс.

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

Разнообразие нейронных сетей. Часть вторая. Продвинутые конфигурации 3

Рекуррентные нейронные сети (recurrent neural networks, RNN) — это сети типа FFNN, но с особенностью: нейроны получают информацию не только от предыдущего слоя, но и от самих себя предыдущего прохода. Это означает, что порядок, в котором вы подаёте данные и обучаете сеть, становится важным. Большой сложностью сетей RNN является проблема исчезающего (или взрывного) градиента, которая заключается в быстрой потере информации с течением времени. Конечно, это влияет лишь на веса, а не состояния нейронов, но ведь именно в них накапливается информация. Обычно сети такого типа используются для автоматического дополнения информации.

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

Разнообразие нейронных сетей. Часть вторая. Продвинутые конфигурации 4

Сети с долгой краткосрочной памятью (long short term memory, LSTM) стараются решить вышеупомянутую проблему потери информации, используя фильтры и явно заданную клетку памяти. У каждого нейрона есть клетка памяти и три фильтра: входной, выходной и забывающий. Целью этих фильтров является защита информации. Входной фильтр определяет, сколько информации из предыдущего слоя будет храниться в клетке. Выходной фильтр определяет, сколько информации получат следующие слои. Ну а забывающий фильтр, каким бы странным не казался, также выполняет полезную функцию: например, если сеть изучает книгу и переходит на новую главу, какие-то символы из старой можно забыть. Такие сети способны научиться создавать сложные структуры, например, писать как Шекспир или сочинять простую музыку, но и ресурсов они потребляют немало.

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

Разнообразие нейронных сетей. Часть вторая. Продвинутые конфигурации 5

Управляемые рекуррентные нейроны (gated recurrent units, GRU) — это небольшая вариация предыдущей сети. У них на один фильтр меньше, и связи реализованы иначе. Фильтр обновления определяет, сколько информации останется от прошлого состояния и сколько будет взято из предыдущего слоя. Фильтр сброса работает примерно как забывающий фильтр.

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

Разнообразие нейронных сетей. Часть вторая. Продвинутые конфигурации 6

Нейронные машины Тьюринга (neural Turing machines, NTM) можно рассматривать как абстрактную модель LSTM и попытку показать, что на самом деле происходит внутри нейронной сети. Ячейка памяти не помещена в нейрон, а размещена отдельно с целью объединить эффективность обычного хранилища данных и мощь нейронной сети. Собственно, поэтому такие сети и называются машинами Тьюринга — в силу способности читать и записывать данные и менять состояние в зависимости от прочитанного они являются тьюринг-полными.

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

Двунаправленные RNN, LSTM и GRU (bidirectional recurrent neural networks, bidirectional long / short term memory networks и bidirectional gated recurrent units, BiRNN, BiLSTM и BiGRU) не показаны в таблице, поскольку они ничем не отличаются от своих однонаправленных вариантов. Разница заключается в том, что эти сети используют не только данные из “прошлого”, но и из “будущего”. Например, обычную сеть типа LSTM обучают угадывать слово “рыба”, подавая буквы по одной, а двунаправленную — подавая ещё и следующую букву из последовательности. Такие сети способны, например, не только расширять изображение по краям, но и заполнять дыры внутри.

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

Разнообразие нейронных сетей. Часть вторая. Продвинутые конфигурации 7

Глубинные остаточные сети (deep residual networks, DRN) — это очень глубокие сети типа FFNN с дополнительными связями между отделёнными друг от друга слоями. Такие сети можно обучать на шаблонах глубиной аж до 150 слоёв — гораздо больше, чем можно было бы ожидать. Однако, было показано, что эти сети мало чем отличаются от рекуррентных, и их часто сравнивают с сетями LSTM.

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

Разнообразие нейронных сетей. Часть вторая. Продвинутые конфигурации 8

Нейронная эхо-сеть (echo state networks, ESN) — это ещё одна разновидность рекуррентных сетей. Её особенностью является отсутствие сформированных слоёв, т.е. связи между нейронами случайны. Соответственно, метод обратного распространения ошибки не срабатывает. Вместо этого нужно подавать входных данные, передавать их по сети и обновлять нейроны, наблюдая за выходными данными.

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

Разнообразие нейронных сетей. Часть вторая. Продвинутые конфигурации 9

Метод экстремального обучения (extreme learning machines, ELM) — это, по сути, сеть типа FFNN, но со случайными связями. Они очень похожи на сети LSM и ESN, но используются как FFNN. Так происходит не только потому, что они не рекуррентны, но и потому, что их можно обучать просто методом обратного распространения ошибки.

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

Разнообразие нейронных сетей. Часть вторая. Продвинутые конфигурации 10

Метод неустойчивых состояний (liquid state machines, LSM) похож на эхо-сеть, но есть существенное отличие: сигмоидная активация заменена пороговой функцией, а каждый нейрон является накопительной ячейкой памяти. Таким образом, при обновлении нейрона его значение не становится равным сумме соседей, а прибавляется само к себе, и при достижении порога сообщается другим нейронам.

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

Разнообразие нейронных сетей. Часть вторая. Продвинутые конфигурации 11

Метод опорных векторов (support vector machines, SVM) находит оптимальные решения задачи оптимизации. Классическая версия способна категоризировать линейно разделяемые данные: например, различать изображения с котом Томом и с котом Гарфилдом. В процессе обучения сеть как бы размещает все данные на 2D-графике и пытается разделить данные прямой линией так, чтобы с каждой стороны были данные только одного класса и чтобы расстояние от данные до линии было максимальным. Используя трюк с ядром, можно классифицировать данные размерности n. Что характерно, этот метод не всегда рассматривается как нейронная сеть.

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

Разнообразие нейронных сетей. Часть вторая. Продвинутые конфигурации 12

И наконец, нейронные сети Кохонена (Kohonen networks, KN), также известные как самоорганизующиеся карты (self organising (feature) maps, SOM, SOFM), завершают наш список. Эти сети используют соревновательное обучение для классификации данных без учителя. Сети подаются входные данные, после чего сеть определяет, какие из нейронов максимально совпадают с ними. После этого эти нейроны изменяются для ещё большей точности совпадения, в процессе двигая за собой соседей. Иногда карты Кохонена также не считаются нейронными сетями.

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

Вот и всё! После прочтения наших статей и прилагающихся материалов вы точно будете уверенно разбираться в видах нейронных сетей ?

Следите за новыми постами по любимым темам

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

Рекуррентные нейронные сети

Рекуррентная нейронная сеть (англ. recurrent neural network, RNN) — вид нейронных сетей, где связи между элементами образуют направленную последовательность.

Описание

Рисунок 1. RNN и ее развернутое представление [1]

Рисунок 2. Схема слоя рекуррентной сети [2]

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

Рисунок 3. RNN с задержкой на скрытом слое

Рисунок 4. Развертка RNN

Обучение RNN аналогично обучению обычной нейронной сети. Мы также используем алгоритм обратного распространения ошибки (англ. Backpropagation), но с небольшим изменением. Поскольку одни и те же параметры используются на всех временных этапах в сети, градиент на каждом выходе зависит не только от расчетов текущего шага, но и от предыдущих временных шагов (рис. 4). Например, чтобы вычислить градиент для четвертого элемента последовательности, нам нужно было бы «распространить ошибку» на 3 шага и суммировать градиенты. Этот алгоритм называется «алгоритмом обратного распространения ошибки сквозь время» (англ. Backpropagation Through Time, BPTT). [3] [4]

Алгоритм обратного распространения ошибки сквозь время:

RNN BPTT GRAD.png

Области и примеры применения

Используются, когда важно соблюдать последовательность, когда важен порядок поступающих объектов.

  • Обработка текста на естественном языке:
    • Анализ текста;
    • Автоматический перевод;
    • Автоматическое распознавание речи;
    • Прогнозирование следующего кадра на основе предыдущих;
    • Распознавание эмоций;
    • Прогнозирование следующего пикселя на основе окружения;
    • Генерация описания изображений.

    Виды RNN

    Один к одному

    Один ко многим

    Многие к одному

    Многие ко многим

    Архитектуры

    Полностью рекуррентная сеть

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

    Рекурсивная сеть

    Рекурсивные нейронные сети (англ. Recurrent neural networks) представляют собой более общий случай рекуррентных сетей, когда сигнал в сети проходит через структуру в виде дерева (обычно бинарные деревья). Те же самые матрицы весов используются рекурсивно по всему графу в соответствии с его топологией.

    Нейронная сеть Хопфилда

    Тип рекуррентной сети, когда все соединения симметричны. Изобретена Джоном Хопфилдом в 1982 году и гарантируется, что динамика такой сети сходится к одному из положений равновесия.

    Двунаправленная ассоциативная память (BAM)

    Вариацией сети Хопфилда является двунаправленная ассоциативная память (BAM). BAM имеет два слоя, каждый из которых может выступать в качестве входного, находить (вспоминать) ассоциацию и генерировать результат для другого слоя.

    Рисунок 5. Сеть Элмана

    Сеть Элмана

    Нейронная сеть Элмана состоит из трёх слоев: [math]x[/math] , [math]y[/math] , [math]z[/math] . Дополнительно к сети добавлен набор «контекстных блоков»: [math]u[/math] (рис. 5). Средний (скрытый) слой соединён с контекстными блоками с фиксированным весом, равным единице. С каждым шагом времени на вход поступает информация, которая проходит прямой ход к выходному слою в соответствии с правилами обучения. Фиксированные обратные связи сохраняют предыдущие значения скрытого слоя в контекстных блоках (до того как скрытый слой поменяет значение в процессе обучения). Таким способом сеть сохраняет своё состояние, что может использоваться в предсказании последовательностей, выходя за пределы мощности многослойного перцептрона.

    [math]h_t = \sigma_h(W_h x_t + U_h h_ + b_h)[/math] ,

    [math]y_t = \sigma_y(W_y h_t + b_y)[/math] ,

    Обозначения переменных и функций:

    • [math]x_t[/math] : вектор входного слоя;
    • [math]h_t[/math] : вектор скрытого слоя;
    • [math]y_t[/math] : вектор выходного слоя;
    • [math]W, U, b[/math] : матрица и вектор параметров;
    • [math]\sigma_h, \sigma_y[/math] : функция активации.

    Сеть Джордана

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

    [math]h_t = \sigma_h(W_h x_t + U_h y_ + b_h)[/math] ,

    [math]y_t = \sigma_y(W_y h_t + b_y)[/math] ,

    Эхо-сети

    Эхо-сеть (англ. Echo State Network, ESN) характеризуется одним скрытым слоем (который называется резервуаром) со случайными редкими связями между нейронами. При этом связи внутри резервуара фиксированы, но связи с выходным слоем подлежат обучению. Состояние резервуара (state) вычисляется через предыдущие состояния резервуара, а также предыдущие состояния входного и выходного сигналов. Так как эхо-сети обладают только одним скрытым слоем, они обладают достаточно низкой вычислительной сложностью.

    Нейронный компрессор истории

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

    Рисунок 6. Схема слоев рекуррентной сети долго-краткосрочной памяти [5]

    Сети долго-краткосрочной памяти

    Сеть долго-краткосрочной памяти (англ. Long short-term memory, LSTM) является самой популярной архитектурой рекуррентной нейронной сети на текущий момент, такая архитектура способна запоминать данные на долгое время (рис. 6). [6]

    Управляемые рекуррентные блоки

    Управляемые рекуррентные блоки (англ. Gated Recurrent Units, GRU) — обладает меньшим количеством параметров, чем у LSTM, и в ней отсутствует выходное управление. При этом производительность в моделях речевого сигнала или полифонической музыки оказалась сопоставимой с LSTM.

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

    Рисунок 7. Двунаправленная рекуррентная сеть [7]

    Двунаправленная рекуррентная сеть (англ. Bidirectional Recurrent Neural Network, biRNN) представляет собой две однонаправленные рекуррентные сети, одна из которых обрабатывает входную последовательность в прямом порядке, а другая — в обратном (рис. 7). Таким образом, для каждого элемента входной последовательности считается два вектора скрытых состояний, на основе которых вычисляется выход сети. Благодаря данной архитектуре сети доступна информация о контексте как из прошлого, так и из будущего, что решает проблему однонаправленных рекуррентных сетей. Для обучения biRNN используются те же алгоритмы, что и для RNN.

    [math]H_t^f = \sigma^f(W_x^f X_t + W_h^f H_^f + b^f)[/math] ,

    [math]H_t^b = \sigma^b(W_x^b X_t + W_h^b H_^b + b^b)[/math] ,

    [math]O_t = W^q H_t + b^q[/math] ,

    где [math]W_x^f[/math] , [math]W_x^b[/math] , [math]W_h^f[/math] , [math]W_h^b[/math] , [math]W^q[/math] , — матрицы весов, [math]b^f[/math] , [math]b^b[/math] , [math]b^q[/math] , — байесы, [math]\sigma^f[/math] , [math]\sigma^b[/math] , — функции активаций, [math]H_t^f[/math] и [math]H_t^b[/math] — выходы однонаправленных рекуррентных сетей, [math]H_t[/math] — их конкатенированный вектор, а [math]O_t[/math] — выход сети на шаге [math]t[/math] .

    Seq-2-seq сети

    Рисунок 8. Seq-2-seq сеть [8]

    Seq-2-seq (Sequence to sequence, Seq2seq) сеть является базовой архитектурой many-to-many RNN и используется для трансляции одной последовательности в другую (рис. 8). Она состоит из двух рекуррентных сетей: кодировщика и декодировщика. Кодировщик вычисляет вектор, кодирующий входную последовательность. Далее данный вектор передается декодировщику, который в свою очередь по полученному скрытому представлению восстанавливает целевую последовательность. При этом каждый посчитанный выход используется для обновления скрытого представления.

    Пример кода

    Пример кода на Python с использованием библиотеки Keras. [9]

    # Импорты import numpy as np from keras.preprocessing import sequence from keras.models import Sequential from keras.layers import Dense, Activation, Embedding from keras.layers import LSTM from keras.datasets import imdb # Устанавливаем seed для обеспечения повторяемости результатов np.random.seed(42) # Указываем количество слов из частотного словаря, которое будет использоваться (отсортированы по частоте использования) max_features = 5000 # Загружаем данные (датасет IMDB содержит 25000 рецензий на фильмы с правильным ответом для обучения и 25000 рецензий на фильмы с правильным ответом для тестирования) (X_train, y_train), (X_test, y_test) = imdb.load_data(nb_words = max_features) # Устанавливаем максимальную длину рецензий в словах, чтобы они все были одной длины maxlen = 80 # Заполняем короткие рецензии пробелами, а длинные обрезаем X_train = sequence.pad_sequences(X_train, maxlen = maxlen) X_test = sequence.pad_sequences(X_test, maxlen = maxlen) # Создаем модель последовательной сети model = Sequential() # Добавляем слой для векторного представления слов (5000 слов, каждое представлено вектором из 32 чисел, отключаем входной сигнал с вероятностью 20% для предотвращения переобучения) model.add(Embedding(max_features, 32, dropout = 0.2)) # Добавляем слой долго-краткосрочной памяти (100 элементов для долговременного хранения информации, отключаем входной сигнал с вероятностью 20%, отключаем рекуррентный сигнал с вероятностью 20%) model.add(LSTM(100, dropout_W = 0.2, dropout_U = 0.2)) # Добавляем полносвязный слой из 1 элемента для классификации, в качестве функции активации будем использовать сигмоидальную функцию model.add(Dense(1, activation = 'sigmoid')) # Компилируем модель нейронной сети model.compile(loss = 'binary_crossentropy', optimizer = 'adam', metrics = ['accuracy']) # Обучаем нейронную сеть (данные для обучения, ответы к данным для обучения, количество рецензий после анализа которого будут изменены веса, число эпох обучения, тестовые данные, показывать progress bar или нет) model.fit(X_train, y_train, batch_size = 64, nb_epoch = 7, validation_data = (X_test, y_test), verbose = 1) # Проверяем качество обучения на тестовых данных (если есть данные, которые не участвовали в обучении, лучше использовать их, но в нашем случае таковых нет) scores = model.evaluate(X_test, y_test, batch_size = 64) print('Точность на тестовых данных: %.2f%%' % (scores[1] * 100))

    Пример на языке Java

    Пример простой рекуррентной нейронной сети, способной генерировать заданную строку по первому символу, с применением библиотеки deeplearning4j .

    См. также

    • Сверточные нейронные сети
    • Нейронные сети, перцептрон
    • Рекурсивные нейронные сети

    Примечания

    1. ↑Understanding LSTM Networks
    2. ↑Understanding LSTM Networks
    3. ↑Backpropagation Through Time
    4. ↑Backpropagation Through Time
    5. ↑Understanding LSTM Networks
    6. ↑Sepp Hochreiter, Jurgen Schmidhuber. Long short-term memory (1997). Neural Computation.
    7. ↑Understanding Bidirectional RNN in PyTorch
    8. ↑Implementation of seq2seq model
    9. ↑Keras RNN

    Рекуррентные нейронные сети (RNN) с Keras

    Перевод руководства по рекуррентным нейросетям с сайта Tensorflow.org. В материале рассматриваются как встроенные возможности Keras/Tensorflow 2.0 по быстрому построению сеток, так и возможности кастомизации слоев и ячеек. Также рассматриваются случаи и ограничения использования ядра CuDNN позволяющего ускорить процесс обучения нейросети.

    Рекуррентные нейронные сети (RNN) — это класс нейронных сетей, которые хороши для моделирования последовательных данных, таких как временные ряды или естественный язык.

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

    Keras RNN API разработан с фокусом на:

    Простоту использования: встроенные слои tf.keras.layers.RNN , tf.keras.layers.LSTM , tf.keras.layers.GRU позволяют вам быстро построить рекуррентные модели без необходимости делать сложные конфигурационные настройки.

    Простота кастомизации: Вы можете также задать собственный слой ячеек RNN (внутреннюю часть цикла for ) с кастомным поведением и использовать его с общим слоем `tf.keras.layers.RNN` (сам цикл `for`). Это позволит вам быстро прототипировать различные исследовательские идеи в гибкой манере, с минимумом кода.

    Установка

    from __future__ import absolute_import, division, print_function, unicode_literals import collections import matplotlib.pyplot as plt import numpy as np import tensorflow as tf from tensorflow.keras import layers

    Построение простой модели

    В Keras есть три встроенных слоя RNN:

    1. tf.keras.layers.SimpleRNN , полносвязная RNN в которой выход предыдущего временного шага должен быть передан в следующий шаг.
    2. tf.keras.layers.GRU , впервые предложен в статье Изучение представлений фраз с использованием кодера-декодера RNN для статистического машинного перевода
    3. tf.keras.layers.LSTM , впервые предложен в статье Долгая краткосрочная память

    Ниже приводится пример Sequential модели которая обрабатывает последовательности целых чисел, вкладывая каждое целое число в 64-мерный вектор, затем обрабатывая последовательности векторов с использованием слоя LSTM .

    model = tf.keras.Sequential() # Добавим слой Embedding ожидая на входе словарь размера 1000, и # на выходе вложение размерностью 64. model.add(layers.Embedding(input_dim=1000, output_dim=64)) # Добавим слой LSTM с 128 внутренними узлами. model.add(layers.LSTM(128)) # Добавим слой Dense с 10 узлами и активацией softmax. model.add(layers.Dense(10)) model.summary()

    Выходы и состояния

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

    Слой RNN может также возвращать всю последовательность выходных данных для каждого элемента (по одному вектору на каждый шаг), если вы укажете return_sequences=True . Размерность этих выходных данных равна (batch_size, timesteps, units) .

    model = tf.keras.Sequential() model.add(layers.Embedding(input_dim=1000, output_dim=64)) # Выходом GRU будет 3D тензор размера (batch_size, timesteps, 256) model.add(layers.GRU(256, return_sequences=True)) # Выходом SimpleRNN будет 2D тензор размера (batch_size, 128) model.add(layers.SimpleRNN(128)) model.add(layers.Dense(10)) model.summary()

    Кроме того, слой RNN может вернуть свое конечное внутреннее состояние (состояния).

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

    Для того чтобы слой RNN возвращал свое внутреннее состояние, установите параметр return_state в значение True при создании слоя. Обратите внимание, что у LSTM 2 тензора состояния, а у GRU только один.

    Чтобы настроить начальное состояние слоя, просто вызовите слой с дополнительным аргументом initial_state .

    Заметьте что размерность должна совпадать с размерностью элемента слоя, как в следующем примере.

    encoder_vocab = 1000 decoder_vocab = 2000 encoder_input = layers.Input(shape=(None, )) encoder_embedded = layers.Embedding(input_dim=encoder_vocab, output_dim=64)(encoder_input) # Возвращает состояния в добавление к выходным данным output, state_h, state_c = layers.LSTM( 64, return_state=True, name='encoder')(encoder_embedded) encoder_state = [state_h, state_c] decoder_input = layers.Input(shape=(None, )) decoder_embedded = layers.Embedding(input_dim=decoder_vocab, output_dim=64)(decoder_input) # Передает 2 состояния в новый слой LSTM в качестве начального состояния decoder_output = layers.LSTM( 64, name='decoder')(decoder_embedded, initial_state=encoder_state) output = layers.Dense(10)(decoder_output) model = tf.keras.Model([encoder_input, decoder_input], output) model.summary() 

    RNN слои и RNN ячейки

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

    Ячейка находится внутри цикла for слоя RNN. Оборачивание ячейки слоем tf.keras.layers.RNN дает вам слой способный обрабатывать пакеты последовательностей, напр. RNN(LSTMCell(10)) .

    Математически, RNN(LSTMCell(10)) дает тот же результат, что и LSTM(10) . Фактически, реализацией этого слоя внутри TF v1.x было лишь создание соответствующей RNN ячейки и оборачивание ее в слой RNN. Однако использование встроенных слоев GRU и LSTM позволяет использовать CuDNN что может дать лучшую производительность.

    Существует три встроенных ячейки RNN, каждая из которых соответствует своему слою RNN.

    • tf.keras.layers.SimpleRNNCell соответствует слою SimpleRNN .
    • tf.keras.layers.GRUCell соответствует слою GRU .
    • tf.keras.layers.LSTMCell соответствует слою LSTM .

    Кросс-пакетное сохранение состояния

    При обработке длинных последовательностей (возможно бесконечных), вы можете захотеть использовать паттерн кросс-пакетное сохранение состояния (cross-batch statefulness).

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

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

    Вы можете сделать это установив в конструкторе `stateful=True`.

    Если у вас есть последовательность `s = [t0, t1,… t1546, t1547]`, вы можете разбить ее например на:

    s1 = [t0, t1, . t100] s2 = [t101, . t201] . s16 = [t1501, . t1547]

    Потом вы можете обработать ее с помощью:

    lstm_layer = layers.LSTM(64, stateful=True) for s in sub_sequences: output = lstm_layer(s)

    Когда вы захотите почистить состояние, используйте layer.reset_states() .

    Примечание: В этом случае, предполагается что пример i в данном пакете является продолжением примера i предыдущего пакета. Это значит, что все пакеты содержат одинаковое количество элементов (размер пакета). Например, если пакет содержит [sequence_A_from_t0_to_t100, sequence_B_from_t0_to_t100] , следующий пакет должен содержать [sequence_A_from_t101_to_t200, sequence_B_from_t101_to_t200] .

    Приведем полный пример:

    paragraph1 = np.random.random((20, 10, 50)).astype(np.float32) paragraph2 = np.random.random((20, 10, 50)).astype(np.float32) paragraph3 = np.random.random((20, 10, 50)).astype(np.float32) lstm_layer = layers.LSTM(64, stateful=True) output = lstm_layer(paragraph1) output = lstm_layer(paragraph2) output = lstm_layer(paragraph3) # reset_states() сбосит кешированное состояние до изначального initial_state. # Если initial_state не было задано, по умолчанию будут использованы нулевые состояния. lstm_layer.reset_states()

    Двунаправленные RNN

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

    Keras предоставляет простой API для создания таких двунаправленных сетей RNN: обертку tf.keras.layers.Bidirectional .

    model = tf.keras.Sequential() model.add(layers.Bidirectional(layers.LSTM(64, return_sequences=True), input_shape=(5, 10))) model.add(layers.Bidirectional(layers.LSTM(32))) model.add(layers.Dense(10)) model.summary()

    Под капотом, Bidirectional скопирует переданный слой RNN layer, и перевернет поле go_backwards вновь скопированного слоя, и таким образом входные данные будут обработаны в обратном порядке.

    На выходе` Bidirectional RNN по умолчанию будет сумма вывода прямого слоя и вывода обратного слоя. Если вам нужно другое поведение слияния, напр. конкатенация, поменяйте параметр `merge_mode` в конструкторе обертки `Bidirectional`.

    Оптимизация производительности и ядра CuDNN в TensorFlow 2.0

    В TensorFlow 2.0, встроенные слои LSTM и GRU пригодны для использования ядер CuDNN по умолчанию, если доступен графический процессор. С этим изменением предыдущие слои keras.layers.CuDNNLSTM/CuDNNGRU устарели, и вы можете построить свою модель, не беспокоясь об оборудовании, на котором она будет работать.

    Поскольку ядро CuDNN построено с некоторыми допущениями, это значит, что слой не сможет использовать слой CuDNN kernel если вы измените параметры по умолчанию встроенных слоев LSTM или GRU. Напр:

    • Изменение функции activation с tanh на что-то другое.
    • Изменение функции recurrent_activation с sigmoid на что-то другое.
    • Использование recurrent_dropout > 0.
    • Установка unroll равным True, что заставляет LSTM/GRU декомпозировать внутренний tf.while_loop в развернутый цикл for .
    • Установка use_bias равным False.
    • Использование масок, когда входные данные не выровнены строго справа (если маска соответствует строго выровненным справа данным, CuDNN может быть все еще использовано. Это наиболее распространенный случай).

    Когда это возможно используйте ядра CuDNN

    batch_size = 64 # Каждый пакет изображений MNIST это тензор размерностью (batch_size, 28, 28). # Каждая входная последовательность размера (28, 28) (высота рассматривается как время). input_dim = 28 units = 64 output_size = 10 # метки от 0 до 9 # Построим RNN модель def build_model(allow_cudnn_kernel=True): # CuDNN доступен только на уровне слоя, а не на уровне ячейки. # Это значит `LSTM(units)` будет использовать ядро CuDNN, # тогда как RNN(LSTMCell(units)) будет использовать non-CuDNN ядро. if allow_cudnn_kernel: # Слой LSTM с параметрами по умолчанию использует CuDNN. lstm_layer = tf.keras.layers.LSTM(units, input_shape=(None, input_dim)) else: # Обертка LSTMCell слоем RNN не будет использовать CuDNN. lstm_layer = tf.keras.layers.RNN( tf.keras.layers.LSTMCell(units), input_shape=(None, input_dim)) model = tf.keras.models.Sequential([ lstm_layer, tf.keras.layers.BatchNormalization(), tf.keras.layers.Dense(output_size)] ) return model 

    Загрузка датасета MNIST

    mnist = tf.keras.datasets.mnist (x_train, y_train), (x_test, y_test) = mnist.load_data() x_train, x_test = x_train / 255.0, x_test / 255.0 sample, sample_label = x_train[0], y_train[0]

    Создайте экземпляр модели и скомпилируйте его

    Мы выбрали sparse_categorical_crossentropy в качестве функции потерь. Выходные данные модели имеют размерность [batch_size, 10] . Ответом модели является целочисленный вектор, каждое из чисел находится в диапазоне от 0 до 9.

    model = build_model(allow_cudnn_kernel=True) model.compile(loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), optimizer='sgd', metrics=['accuracy']) 
    model.fit(x_train, y_train, validation_data=(x_test, y_test), batch_size=batch_size, epochs=5)

    Постройте новую модель без ядра CuDNN

    slow_model = build_model(allow_cudnn_kernel=False) slow_model.set_weights(model.get_weights()) slow_model.compile(loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), optimizer='sgd', metrics=['accuracy']) slow_model.fit(x_train, y_train, validation_data=(x_test, y_test), batch_size=batch_size, epochs=1) # Обучим только за одну эпоху потому что она медленная.

    Как вы можете видеть, модель построенная с CuDNN намного быстрее для обучения чем модель использующая обычное ядро TensorFlow.

    Ту же модель с поддержкой CuDNN можно использовать при выводе в однопроцессорной среде. Аннотация tf.device просто указывает используемое устройство. Модель выполнится по умолчанию на CPU если не будет доступно GPU.

    Вам просто не нужно беспокоиться о железе на котором вы работаете. Разве это не круто?

    with tf.device('CPU:0'): cpu_model = build_model(allow_cudnn_kernel=True) cpu_model.set_weights(model.get_weights()) result = tf.argmax(cpu_model.predict_on_batch(tf.expand_dims(sample, 0)), axis=1) print('Predicted result is: %s, target result is: %s' % (result.numpy(), sample_label)) plt.imshow(sample, cmap=plt.get_cmap('gray')) 

    RNN с входными данными вида список/словарь, или вложенными входными данными

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

    [batch, timestep, ]

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

    [batch, timestep, ]

    В следующем коде построен пример кастомной ячейки RNN которая работает с такими структурированными входными данными.

    Определите пользовательскую ячейку поддерживающую вложенный вход/выход

    NestedInput = collections.namedtuple('NestedInput', ['feature1', 'feature2']) NestedState = collections.namedtuple('NestedState', ['state1', 'state2']) class NestedCell(tf.keras.layers.Layer): def __init__(self, unit_1, unit_2, unit_3, **kwargs): self.unit_1 = unit_1 self.unit_2 = unit_2 self.unit_3 = unit_3 self.state_size = NestedState(state1=unit_1, state2=tf.TensorShape([unit_2, unit_3])) self.output_size = (unit_1, tf.TensorShape([unit_2, unit_3])) super(NestedCell, self).__init__(**kwargs) def build(self, input_shapes): # # ожидает input_shape содержащий 2 элемента, [(batch, i1), (batch, i2, i3)] input_1 = input_shapes.feature1[1] input_2, input_3 = input_shapes.feature2[1:] self.kernel_1 = self.add_weight( shape=(input_1, self.unit_1), initializer='uniform', name='kernel_1') self.kernel_2_3 = self.add_weight( shape=(input_2, input_3, self.unit_2, self.unit_3), initializer='uniform', name='kernel_2_3') def call(self, inputs, states): # входы должны быть в [(batch, input_1), (batch, input_2, input_3)] # состояние должно быть размерностью [(batch, unit_1), (batch, unit_2, unit_3)] input_1, input_2 = tf.nest.flatten(inputs) s1, s2 = states output_1 = tf.matmul(input_1, self.kernel_1) output_2_3 = tf.einsum('bij,ijkl->bkl', input_2, self.kernel_2_3) state_1 = s1 + output_1 state_2_3 = s2 + output_2_3 output = [output_1, output_2_3] new_states = NestedState(state1=state_1, state2=state_2_3) return output, new_states 

    Постройте модель RNN с вложенными входом/выходом

    Давайте построим модель Keras которая использует слой tf.keras.layers.RNN и кастомную ячейку которую мы только определили.

    unit_1 = 10 unit_2 = 20 unit_3 = 30 input_1 = 32 input_2 = 64 input_3 = 32 batch_size = 64 num_batch = 100 timestep = 50 cell = NestedCell(unit_1, unit_2, unit_3) rnn = tf.keras.layers.RNN(cell) inp_1 = tf.keras.Input((None, input_1)) inp_2 = tf.keras.Input((None, input_2, input_3)) outputs = rnn(NestedInput(feature1=inp_1, feature2=inp_2)) model = tf.keras.models.Model([inp_1, inp_2], outputs) model.compile(optimizer='adam', loss='mse', metrics=['accuracy'])unit_1 = 10 unit_2 = 20 unit_3 = 30 input_1 = 32 input_2 = 64 input_3 = 32 batch_size = 64 num_batch = 100 timestep = 50 cell = NestedCell(unit_1, unit_2, unit_3) rnn = tf.keras.layers.RNN(cell) inp_1 = tf.keras.Input((None, input_1)) inp_2 = tf.keras.Input((None, input_2, input_3)) outputs = rnn(NestedInput(feature1=inp_1, feature2=inp_2)) model = tf.keras.models.Model([inp_1, inp_2], outputs) model.compile(optimizer='adam', loss='mse', metrics=['accuracy']) 

    Обучите модель на случайно сгенерированных данных

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

    input_1_data = np.random.random((batch_size * num_batch, timestep, input_1)) input_2_data = np.random.random((batch_size * num_batch, timestep, input_2, input_3)) target_1_data = np.random.random((batch_size * num_batch, unit_1)) target_2_data = np.random.random((batch_size * num_batch, unit_2, unit_3)) input_data = [input_1_data, input_2_data] target_data = [target_1_data, target_2_data] model.fit(input_data, target_data, batch_size=batch_size) 

    Со слоем tf.keras.layers.RNN от вас требуется только определить математическую логику отдельного шага внутри последовательности, а слой tf.keras.layers.RNN будет обрабатывать для вас итерацию последовательности. Это невероятно сильный способ быстрого прототипирования новых видов RNN (напр. вариант LSTM).

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

    • рекуррентная нейронная сеть
    • машинное обучение
    • LSTM
    • keras
    • tensorflow
    • Python
    • Big Data
    • Машинное обучение
    • Искусственный интеллект
    • TensorFlow

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *