Создайте повторяющуюся нейронную сеть, которая переводит с английского на французский.

29 августа 2018 г., Thomas Tracey, изначально размещено на Github

В этом посте рассматривается моя работа над финальным проектом программы Udacity Artificial Intelligence Nanodegree. Моя цель - помочь другим студентам и специалистам, которые находятся на ранних этапах развития своей интуиции в области машинного обучения (ML) и искусственного интеллекта (AI).

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

Мое репозиторий Github для этого проекта можно найти здесь. Исходное репозиторий Udacity для этого проекта находится здесь.

Цель проекта

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

Почему машинный перевод так важен

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

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

Чтобы удовлетворить эти потребности, технологические компании вкладывают значительные средства в машинный перевод. Эти инвестиции и недавние достижения в области глубокого обучения привели к значительному повышению качества перевода. Согласно Google, переход на глубокое обучение повысил точность перевода на 60% по сравнению с подходом на основе фраз, который ранее использовался в Google Translate. Сегодня Google и Microsoft могут переводить более 100 различных языков, и точность многих из них приближается к человеческому.

Однако, хотя машинный перевод добился большого прогресса, он все еще не идеален. 😬

Подход к этому проекту

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

Обзор RNN

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

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

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

Настройка RNN

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

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

Слева направо: (1) Стандартный режим обработки без RNN, от ввода фиксированного размера до вывода фиксированного размера (например, классификация изображений). (2) Последовательный вывод (например, при подписании изображения используется изображение и выводится предложение слов). (3) Последовательный ввод (например, анализ тональности, когда данное предложение классифицируется как выражающее положительное или отрицательное настроение). (4) Последовательный ввод и вывод последовательности (например, машинный перевод: RNN читает предложение на английском языке, а затем выводит предложение на французском). (5) Синхронизированный ввод и вывод последовательности (например, классификация видео, в которой мы хотим пометить каждый кадр видео). Обратите внимание, что в каждом случае нет заранее заданных ограничений на последовательности длин, потому что рекуррентное преобразование (зеленый цвет) является фиксированным и может применяться столько раз, сколько мы захотим.

- Андрей Карпатий, Неоправданная эффективность рекуррентных нейронных сетей

Строительство трубопровода

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

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

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

Каркасы

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

Предварительная обработка

Загрузить и изучить данные

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

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

Уборка

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

Примечание. Для других проектов НЛП вам может потребоваться выполнить дополнительные действия, такие как: удаление тегов HTML, удаление стоп-слов, удаление знаков препинания или преобразование в представления тегов, маркировка частей речи или извлечение сущностей.

Токенизация

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

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

Обивка

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

One-Hot Encoding (не используется)

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

Одним из преимуществ OHE является эффективность, поскольку он может работать с более высокой тактовой частотой, чем другие кодировки. Другое преимущество состоит в том, что OHE лучше представляет категориальные данные, где нет порядковой взаимосвязи между различными значениями. Например, предположим, что мы классифицируем животных как млекопитающих, рептилий, рыб или птиц. Если мы закодируем их как 1, 2, 3, 4 соответственно, наша модель может предположить, что между ними существует естественный порядок, которого нет. Бесполезно структурировать наши данные так, чтобы млекопитающие были раньше рептилий и т. Д. Это может ввести нашу модель в заблуждение и привести к плохим результатам. Однако, если мы затем применим одноразовое кодирование к этим целым числам, изменив их на двоичные представления - 1000, 0100, 0010, 0001 соответственно - тогда модель не сможет вывести порядковые отношения.

Но одним из недостатков OHE является то, что векторы могут быть очень длинными и разреженными. Длина вектора определяется словарным запасом, то есть количеством уникальных слов в вашем текстовом корпусе. Как мы видели на этапе проверки данных выше, наш словарный запас для этого проекта очень мал - всего 227 английских слов и 355 французских слов. Для сравнения: Оксфордский словарь английского языка содержит 172 000 слов. Но если мы включим различные имена собственные, времена слов и сленг, в каждом языке могут быть миллионы слов. Например, Google's word2vec обучен словарю из 3 миллионов уникальных слов. Если бы мы использовали OHE в этом словаре, вектор для каждого слова включал бы одно положительное значение (1), окруженное 2 999 999 нулями!

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

Моделирование

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

  1. Входы. Входные последовательности вводятся в модель с одним словом для каждого временного шага. Каждое слово кодируется как уникальное целое число или вектор с горячим кодированием, который отображается в словарь английского набора данных.
  2. Встраивание слоев. Вложения используются для преобразования каждого слова в вектор. Размер вектора зависит от сложности словарного запаса.
  3. Рекуррентные слои (кодировщик). Здесь контекст из векторов слов на предыдущих временных шагах применяется к текущему вектору слов.
  4. Плотные слои (декодер). Это типичные полностью связанные слои, используемые для декодирования закодированного ввода в правильную последовательность преобразования.
  5. Выводы. Выходные данные возвращаются в виде последовательности целых чисел или векторов с горячим кодированием, которые затем могут быть отображены во французском словаре набора данных.

Вложения

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

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

Кодировщик и декодер

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

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

В приведенном ниже примере для кодирования всей входной последовательности требуется четыре временных шага. На каждом временном шаге кодер «считывает» входное слово и выполняет преобразование его скрытого состояния. Затем он передает это скрытое состояние на следующий временной шаг. Имейте в виду, что скрытое состояние представляет собой соответствующий контекст, протекающий по сети. Чем больше скрытое состояние, тем выше обучаемость модели, но также и выше требования к вычислениям. Мы поговорим больше о преобразованиях в скрытом состоянии, когда рассмотрим закрытые повторяющиеся единицы (ГРУ).

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

Также помните, что когда мы ссылаемся на «слово», мы на самом деле имеем в виду векторное представление слова, которое поступает из слоя внедрения.

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

Двунаправленный слой

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

В приведенном выше примере кодировщик имеет только исторический контекст. Но предоставление будущего контекста может привести к повышению производительности модели. Это может показаться нелогичным по сравнению с тем, как люди обрабатывают язык, поскольку мы читаем только в одном направлении. Однако людям часто требуется контекст будущего, чтобы интерпретировать сказанное. Другими словами, иногда мы не понимаем предложение, пока в конце не будет указано важное слово или фраза. Так бывает всякий раз, когда Йода говорит. 😑 🙏

Для этого мы обучаем два уровня RNN одновременно. Первому слою подается входная последовательность как есть, а второму - обратная копия.

Скрытый слой с закрытым рекуррентным блоком (GRU)

А теперь давайте сделаем нашу RNN немного умнее. Что, если бы мы могли быть более избирательными, вместо того, чтобы разрешать всей информации из скрытого состояния течь по сети? Возможно, часть информации более актуальна, а другую информацию следует отбросить. По сути, это то, что делает закрытое рекуррентное подразделение (ГРУ).

В ГРУ есть два ворот: ворота обновления и ворота сброса. Эта статья Симеона Костадинова подробно объясняет это. Подводя итог, можно сказать, что окно обновления (z) помогает модели определить, сколько информации из предыдущих временных шагов необходимо передать в будущее. Между тем, ключ сброса (r) решает, какую часть прошлой информации следует забыть.

Окончательная модель

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

Полученные результаты

Результаты финальной модели можно найти в ячейке 20 записной книжки.

Точность проверки: 97,5%

Время обучения: 23 эпохи

Будущие улучшения

  1. Выполните правильное разделение данных (обучение, проверка, тестирование). В настоящее время нет набора тестов, только обучение и проверка. Очевидно, это не соответствует лучшим практикам.
  2. LSTM + внимание. Это была де-факто архитектура для RNN в течение последних нескольких лет, хотя есть некоторые ограничения. Я не использовал LSTM, потому что уже реализовал его в TensorFlow в другом проекте, и я хотел поэкспериментировать с GRU + Keras для этого проекта.
  3. Обучение работе с большим и разнообразным корпусом текста. Текстовый корпус и словарь для этого проекта довольно малы с небольшими вариациями в синтаксисе. В результате модель получается очень хрупкой. Чтобы создать модель, которая лучше обобщает, вам нужно будет тренироваться на более крупном наборе данных с большей вариативностью грамматики и структуры предложений.
  4. Остаточные слои. Вы можете добавить остаточные слои в глубокую LSTM RNN, как описано в этой статье. Или используйте остаточные слои в качестве альтернативы LSTM и GRU, как описано здесь.
  5. Вложения. Если вы тренируетесь на большом наборе данных, вам обязательно следует использовать предварительно обученный набор встраиваний, таких как word2vec или GloVe. Еще лучше использовать ELMo или BERT.
  • Модель языка встраивания (ELMo). Одним из самых больших достижений в области универсальных вложений в 2018 году стал ELMo, разработанный Институтом ИИ Аллена. Одним из основных преимуществ ELMo является то, что он решает проблему многозначности, когда одно слово имеет несколько значений. ELMo основан на контексте (не на основе слов), поэтому разные значения слова занимают разные векторы в пространстве встраивания. С GloVe и word2vec каждое слово имеет только одно представление в пространстве вложения. Например, слово королева может относиться к матриарху королевской семьи, пчеле, шахматной фигуре или рок-группе 1970-х годов. При традиционных вложениях все эти значения привязаны к одному вектору слова королева. В ELMO это четыре различных вектора, каждый с уникальным набором контекстных слов, занимающих одну и ту же область пространства встраивания. Например, мы ожидаем увидеть такие слова, как ферзь, ладья и пешка в аналогичном векторном пространстве, связанном с игрой в шахматы. И мы ожидаем увидеть матку, улей и мед в другом векторном пространстве, связанном с пчелами. Это обеспечивает значительное повышение семантического кодирования.
  • Представления двунаправленного кодера от Transformers (BERT). Пока что в 2019 году самым большим достижением в области двунаправленного встраивания стал BERT, исходный код которого был открыт от Google. Чем отличается BERT?

Бесконтекстные модели, такие как word2vec или GloVe, генерируют представление встраивания одного слова для каждого слова в словаре. Например, слово банк будет иметь такое же бесконтекстное представление в банковском счете и берегу реки. Вместо этого контекстные модели генерируют представление каждого слова, основанное на других словах в предложении. Например, в предложении Я получил доступ к банковскому счету однонаправленная контекстная модель будет представлять банк на основе Я получил доступ к, но не счет. Однако BERT представляет банк, используя как предыдущий, так и следующий контекст - Я получил доступ к… учетной записи - начиная с самого низа глубокой нейронной сети, что делает ее глубоко двунаправленной.
- Джейкоб Девлин и Мин-Вэй Чанг, Блог Google AI

Контакт

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

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