Редактировать: Rasa - это фреймворк с открытым исходным кодом для построения диалогового ИИ.

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

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

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

Ищу дешевое мексиканское заведение в центре.

Чтобы отреагировать на это, вам нужно сделать две вещи:

я. Поймите намерение пользователя: он ищет ресторан, а не говорит «привет», «до свидания» или «спасибо».

II. Извлеките дешевые, мексиканские и центральные как поля, которые можно использовать для выполнения запроса.

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

1. Три причины сделать это самому

Во-первых, если вы действительно хотите построить бизнес на основе диалогового программного обеспечения, вероятно, не стоит передавать все, что говорят вам пользователи, Facebook или Microsoft. Во-вторых, я просто не верю, что веб-API - это решение всех проблем в программировании. Вызовы https выполняются медленно, и вы всегда ограничены вариантами дизайна, которые используются в конечных точках API. С другой стороны, библиотеки можно взломать. В-третьих, есть большая вероятность, что вы сможете создать что-то, что лучше работает с вашими данными и сценариями использования. Помните, что универсальные API-интерфейсы должны хорошо справляться со каждой проблемой, вам нужно только справляться с вашей.

2. Word2vec + эвристика - сложность = рабочий код

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

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

Мы собираемся использовать векторы слов, которые представляют собой векторы из нескольких десятков или сотен чисел с плавающей запятой, которые в некоторой степени отражают значение слова. Тот факт, что это вообще возможно, и способ обучения этих моделей очень интересны. Для наших целей это означает, что тяжелая работа за нас уже проделана: вложения слов, такие как word2vec или GloVe, являются мощным способом представления текстовых данных. Я решил использовать для этих экспериментов Перчатку. Вы можете скачать предварительно обученные модели из их репозитория, и я взял самую маленькую (50-мерную) предварительно обученную модель.
Ниже я вставил код, основанный на примерах Python из репозитория GloVe. Все, что это делает, - загружает в память векторы слов для всего словаря.

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

Давайте попробуем это на примере предложения.

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

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

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

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

3. То, что вы действительно можете использовать

Есть много вещей, которые мы можем сделать лучше. Например, преобразование блока текста в токены требует большего, чем просто разделение на пробелы. Один из подходов - использовать комбинацию SpaCy / textacy для очистки и синтаксического анализа вашего текста и scikit-learn для построения ваших моделей. Здесь я воспользуюсь хорошей библиотекой под названием MITIE (MIT Information Extraction), которая делает все, что нам нужно, и имеет привязки к Python.

Есть два класса, которые мы можем использовать напрямую. Во-первых, классификатор текста:

Во-вторых, распознающий субъект:

Библиотека MITIE довольно сложна и использует спектральные вложения слов, а не GloVe. Классификатор текста - это простая SVM, тогда как средство распознавания сущностей использует структурную SVM. Если вам интересно, в репозитории есть ссылки на соответствующую литературу.

Как и следовало ожидать, использование такой библиотеки (или комбинации SpaCy и вашей любимой библиотеки ML) дает гораздо лучшую производительность, чем хакерский код, который я опубликовал в начале. Фактически, по моему опыту, вы можете очень быстро превзойти производительность остроумия или LUIS для вашей конкретной проблемы просто потому, что вы можете настроить параметры для своего набора данных.

Заключение

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

Спасибо Алексу, Кейт, Норману и Джоуи за чтение черновиков!

Хотите присоединиться к нам, чтобы создавать ботов, похожих на людей? Мы нанимаем!

Приложение по разреженному восстановлению: как это может работать?

Так как же возможно, что вы можете сложить (или усреднить) векторы слов в предложении и каким-то образом сохранить значение? Это похоже на то, как если бы класс из 10 учеников набрал в среднем 75% на тесте, и пытались выяснить, каковы были индивидуальные баллы. Ну, почти. Оказывается, это одна из тех нелогичных вещей о многомерной геометрии.

Если вы выберете 10 векторов из набора из 1000, вы действительно сможете узнать, какие из них вы выбрали, просто зная среднее значение, если векторы имеют достаточно высокую размерность (скажем, 300). Это сводится к тому факту, что в R ³⁰⁰ много места, и поэтому, если вы выбираете пару векторов случайным образом, вы можете ожидать, что они будут (почти) линейно независимыми.

Нас не интересует длина векторов слов (они нормализованы в приведенном выше коде), поэтому мы можем рассматривать векторы как точки на единичной сфере в ^ d. Скажем, у нас есть набор из N векторов V ⊂ ^ d, которые являются i.i.d на единичной d-сфере. Вопрос в том, учитывая некоторое подмножество S из V, насколько большим должно быть d, чтобы мы могли восстановить все S, имея только x = Σ v _i, сумму векторы в S? Это возможно, если скалярное произведение между x и любым вектором v` ∉ S очень мало, и только векторы в S имеют v · x ~ 1.
Мы можем использовать результат, называемый «концентрацией меры», который говорит нам, что именно нам нужно. Для i.i.d точек на единичной d-сфере математическое ожидание скалярного произведения между любыми двумя, E (v · w) = 1 / √d. А вероятность того, что скалярное произведение больше, чем a, равна P (v · w ›a) ≤ (1-a²) ^ (d / 2). Таким образом, мы можем записать вероятность ε того, что некоторый вектор v` ∉ S слишком близок к одному из векторов v ∈S с точки зрения размерности пространства. Это дает результат, что для уменьшения вероятности отказа до ε нам потребуется d ›S log (NS / ε). Поэтому, если мы хотим восстановить подмножество 10 векторов из 1000 с отказоустойчивостью 1%, мы можем сделать это в 138 измерениях или выше.

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