tl;dr

  • RNNS отлично работает с текстом, но свертки могут сделать это быстрее
  • Любая часть предложения может влиять на семантику слова. По этой причине мы хотим, чтобы наша сеть сразу видела весь ввод.
  • Получение такой большой восприимчивости может привести к исчезновению градиентов и отказу наших сетей
  • Мы можем решить проблему исчезающего градиента с помощью DenseNets или Dilated Convolutions.
  • Иногда нам нужно сгенерировать текст. Мы можем использовать «деконволюцию» для генерации произвольно длинных выходных данных.

вступление

За последние три года область НЛП пережила огромную революцию благодаря глубокому обучению. Лидером этой революции была рекуррентная нейронная сеть и, в частности, ее проявление как LSTM. Одновременно с этим область компьютерного зрения была изменена за счет сверточных нейронных сетей. В этом посте рассказывается, что мы, «пишущие людям», можем узнать от наших друзей, занимающихся видением.

Общие задачи НЛП

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

Классификация

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

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

Маркировка последовательности

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

Генерация последовательности

Возможно, самые впечатляющие результаты недавнего НЛП были достигнуты в переводе. Трансляция - это отображение одной последовательности в другую без каких-либо гарантий длины выходного предложения. Например, первые слова Библии с иврита на английский переводятся так: בראשית = «В начале».

В основе этого успеха лежит структура Sequence to Sequence (AKA encoder decoder), методология «сжатия» последовательности в код, а затем декодирования ее в другую последовательность. Известные примеры включают перевод (кодирование иврита и декодирование на английский), подписи к изображениям (кодирование изображения и декодирование текстового описания его содержимого)

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

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

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

Требования от архитектуры НЛП

Что общего у всех рассмотренных реализаций, так это то, что они используют повторяющуюся архитектуру, обычно LSTM (если вы не уверены, что это такое, вот - отличное введение). Стоит отметить, что ни одна из задач не имела повторяющихся имен в своем имени и ни в одной из них не упоминались LSTM. Имея это в виду, давайте на минутку поразмышляем, что предоставляют RNN и особенно LSTM, что делает их настолько повсеместными в NLP.

Произвольный размер ввода

Стандартная нейронная сеть с прямой связью имеет параметр для каждого входа. Это становится проблематичным при работе с текстом или изображениями по нескольким причинам.

  1. Это ограничивает размер ввода, который мы можем обработать. Наша сеть будет иметь конечное количество входных узлов и не сможет увеличиваться до большего числа.
  2. Мы теряем много общей информации. Рассмотрим предложения «Я очень люблю пить пиво» ​​и «Я люблю пить много пива». Сеть с прямой связью должна узнавать о концепции «много» дважды, поскольку она каждый раз появляется в разных входных узлах.

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

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

Долгосрочные зависимости

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

Теория RNN обещает нам дальнодействующие зависимости из коробки. Практика немного сложнее. Когда мы обучаемся через обратное распространение, нам нужно распространить сигнал через все рекуррентное отношение. Дело в том, что на каждом этапе мы умножаем на число. Если эти числа обычно меньше 1, наш сигнал быстро упадет до 0. Если они больше 1, то наш сигнал взорвется.

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

Преимущества сверток

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

Один ответ: «Потому что мы можем».

Но есть две другие веские причины использовать свертки, скорость и контекст.

Parrelalisation

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

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

Когда я намеревался написать это, у меня был только собственный опыт и Google ByteNet, подтверждающие это утверждение. Буквально на этой неделе Facebook опубликовал свою полностью сверточную модель перевода и сообщил о 9-кратном ускорении по сравнению с моделями на основе LSTM.

Просмотреть весь ввод сразу

LSTM читают свой ввод слева направо (или справа налево), но иногда нам нужно, чтобы контекст конца предложения влиял на мысли сети о его начале. Например, у нас может быть предложение типа «Я бы хотел купить ваш продукт. Нет!" и мы хотим, чтобы отрицание в конце повлияло на все предложение.

С помощью LSTM мы достигаем этого путем запуска двух LSTM, одного слева направо, а другого справа налево, и объединения их выходных данных. Это хорошо работает на практике, но удваивает нашу вычислительную нагрузку.

С другой стороны, свертки увеличивают «восприимчивое поле» по мере того, как мы складываем все больше и больше слоев. Это означает, что по умолчанию каждый «шаг» в представлении свертки просматривает все входные данные в своем принимающем поле до и после него. Мне не известны какие-либо окончательные аргументы в пользу того, что это по своей сути лучше, чем LSTM, но это дает нам желаемый эффект управляемым образом и с низкими вычислительными затратами.

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

Практические свертки для текста

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

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

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

Чтобы конкретизировать идею «Текст = изображение высотой 1», давайте посмотрим, как мы будем использовать двумерную сверточную операцию в Tensorflow для последовательности встроенных токенов.

Итак, что мы делаем здесь, это изменяем форму ввода с помощью tf.expand_dims, чтобы он стал «изображением высоты 1». После запуска оператора 2-й свертки мы выжимаем лишнее измерение.

Иерархия и восприимчивые поля

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

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

Увеличивающееся восприимчивое поле

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

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

Большие фильтры

Первый, наиболее очевидный способ - увеличить размер фильтра, то есть выполнить свертку [1x5] вместо [1x3]. В моей работе с текстом я не добился хороших результатов, и я предложу свои предположения относительно того, почему.

В своей области я в основном занимаюсь вводом на уровне персонажа и текстами, которые морфологически очень богаты. Я думаю (по крайней мере, о первых) слоях свертки как об обучении n-грамм, так что ширина фильтра соответствует биграммам, триграммам и т.д. «ab» в тексте встречается больше, чем «abb».

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

Добавление слоев

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

Добавление слоев имеет два различных, но связанных эффекта. Часто всплывает то, что модель научится создавать абстракции более высокого уровня над входными данными, которые она получает (Пиксели = ›Края =› Глаза = ›Лицо). Во-вторых, рецептивное поле растет с каждым шагом.

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

Компромисс между градиентным и восприимчивым полями

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

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

Два решения проблемы исчезающего градиента

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

Остаточные соединения

2016 год был еще одним отличным годом для людей, занимающихся видением, когда появились как минимум две очень популярные архитектуры, ResNets и DenseNets (статья DenseNet, в частности, исключительно хорошо написана и заслуживает того, чтобы ее прочитать) . Оба они решают одну и ту же проблему: Как сделать мою сеть очень глубокой, не теряя градиентного сигнала?

Артур Джулиани написал фантастический обзор Resnet, DenseNets и Highway сетей для тех из вас, кто ищет подробности и сравнение. Я кратко коснусь DenseNets, которые доводят основную концепцию до крайности.

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

DenseNets делают это особенно интересным способом. Они объединяют выходные данные каждого слоя со своими входными данными таким образом, чтобы:

  1. Мы начинаем с встраивания наших входных данных, скажем, размерности 10.
  2. Наш первый слой рассчитывает 10 карт характеристик. Он выводит 10 карт функций, объединенных в исходное вложение.
  3. Второй слой получает в качестве входных данных 20 размерных векторов (10 из входных данных и 10 из предыдущего слоя) и вычисляет еще 10 карт характеристик. Таким образом, он выводит 30 размерных векторов.

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

Я хотел бы отметить еще две вещи.

  1. Я ранее упоминал, что верхние уровни имеют представление об исходном вводе, которое может быть нарушено уровнями абстракции. Одним из основных моментов объединения выходных данных каждого слоя является то, что исходный сигнал достигает следующих слоев в неизменном виде, так что все слои имеют прямой обзор объектов нижнего уровня, по существу удаляя некоторую дымку.
  2. Трюк с остаточным соединением требует, чтобы все наши слои имели одинаковую форму. Это означает, что нам нужно заполнить каждый слой так, чтобы его вход и выход имели одинаковые пространственные размеры [1Xwidth]. Это означает, что сама по себе такая архитектура будет работать для задач маркировки последовательностей (где входные и выходные данные имеют одинаковые пространственные измерения), но потребует дополнительной работы для задач кодирования и классификации (где нам нужно уменьшить входные данные до вектор фиксированного размера или набор векторов). В статье DenseNet это действительно делается, так как их цель - провести классификацию, и мы подробнее остановимся на этом позже.

Расширенные свертки

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

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

Основная идея состоит в том, чтобы ввести «дыры» в каждый фильтр, чтобы он не воздействовал на соседние части ввода, а скорее пропускал их к более удаленным частям. Обратите внимание, что это отличается от применения свертки с шагом ›1. Когда мы шагаем по фильтру, мы пропускаем части ввода между приложениями свертки. С расширенными свертками мы пропускаем части входных данных за одно применение свертки. Путем грамотной организации растущих расширений мы можем добиться обещанного экспоненциального роста рецептивных полей.

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

Лично мне больше всего нравится статья Нейронный машинный перевод в линейном времени. Он соответствует структуре кодера-декодера, о которой мы говорили в начале. У нас пока нет всех инструментов, чтобы поговорить о декодере, но мы можем увидеть кодировщик в действии.

А вот ввод на английском

Режиссер Джон Фавро, который в настоящее время работает над предстоящим фильмом Диснея «Книга джунглей», сказал веб-сайту Hollywood Reporter: «Я думаю, что времена меняются».

И его перевод, доставленный вам расширенными извилинами

Regisseur Jon Favreau, der zur Zeit an Disneys kommendem jungle Book Film arbeitet, hat der Website Hollywood Reporter gesagt: «Ich denke, die Zeiten andern sich».

И в качестве бонуса помните, что звук подобен тексту в том смысле, что он имеет только одно пространственное / временное измерение. Попробуйте DeepMind Wavenet, который использует расширенные извилины (и много другой магии) для создания человеческой речи и фортепианной музыки.

Получение информации из вашей сети

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

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

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

  • Мы можем пометить письмо как спам на основании его содержания и / или темы.
  • Предсказать, является ли предложение саркастичным или нет

В этих случаях мы можем следовать традиционным подходам специалистов по видению и дополнять нашу сеть сверточными слоями, не имеющими заполнения и / или использующими операции объединения.

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

Эта задача влечет за собой две проблемы.

  • Как повысить дискретизацию с помощью сверток?
  • Как нам сделать именно то, что нужно для повышения дискретизации?

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

Повышение дискретизации с деконволюцией

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

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

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

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

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

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

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

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

Несколько вещей, о которых стоит подумать

  1. Если вы посмотрите на эти рисунки снизу вверх, они окажутся стандартными полосатыми свертками, где мы просто добавили воображаемые отверстия на выходных слоях (белые блоки)
  2. На практике каждый «вход» - это не одно число, а вектор. В мире изображений это может быть трехмерное значение RGB. В тексте это может быть вложение 300-мерного слова. Если вы (де) сворачиваете в середине своей сети, каждая точка будет вектором любого размера, вышедшего из последнего слоя.
  3. Я указываю на это, чтобы убедить вас, что их информации на входном слое деконволюции достаточно, чтобы она распространилась по нескольким точкам на выходе.
  4. На практике мне удалось запустить несколько сверток с сохранением длины отступов после деконволюции. Я полагаю, хотя и не доказал, что это действует как перераспределение информации. Я думаю об этом как о том, как дать стейку отдохнуть после приготовления на гриле, чтобы соки перераспределились.

Резюме

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

Большинство задач, с которыми я столкнулся с текстом, в конечном итоге связаны с тем же требованием архитектуры: максимизировать воспринимающее поле при сохранении адекватного потока градиентов. Мы видели, как для этого использовались как DenseNets, так и расширенные свертки.

Наконец, иногда мы хотим расширить последовательность или вектор в более крупную последовательность. Мы рассматривали деконволюцию как способ «передискретизации» текста и как бонус по сравнению с добавлением свертки после того, как дать стейку отдохнуть и перераспределить его соки.

Мне бы хотелось узнать больше о ваших мыслях и опыте работы с такими моделями. Поделитесь в комментариях или напишите мне в твиттере @thetalperry