Chatbot на нейронных сетях
Недавно набрел на такую статью. Как оказалось некая компания с говорящим названием «наносемантика» объявила конкурс русских чатботов помпезно назвав это «Тестом Тьюринга». Лично я отношусь к подобным начинаниям отрицательно — чатбот — программа для имитации разговора — создание, как правило, не умное, основанное на заготовленных шаблонах, и соревнования их науку не двигают, зато шоу и внимание публики обеспечено. Создается почва для разных спекуляций про разумные компьютеры и великие прорывы в искусственном интеллекте, что крайне далеко от истины. Особенно в данном случае, когда принимаются только боты написанные на движке сопоставления шаблонов, причем самой компании «Наносемантика».Впрочем, ругать других всегда легко, а вот сделать что-то работающее бывает не так просто. Мне стало любопытно, можно ли сделать чатбот не ручным заполнением шаблонов ответа, а с помощью обучения нейронной сети на образцах диалогов. Быстрый поиск в Интернете полезной информации не дал, поэтому я решил быстро сделать пару экспериментов и посмотреть что получится.
Обычный чатбот ограничен поверхностными ответами, которые определены заданными шаблонами. Обучаемый чатбот (есть и такие) ищет ответы на похожие вопросы в своей базе диалогов, и иногда даже создавать новые шаблоны. Но все равно «ум» его обычно строго ограничен сверху заданным алгоритмом. А вот бот с нейронной сетью, работающей по принципу вопрос на входе — ответ на выходе — теоретически ничем не ограничен. Есть работы, в которых показано, что нейронные сети могут выучивать правила логического вывода получая на вход предложения на естественном языке, а также отвечать на вопросы касающиеся различных текстов. Но мы не будем пока столь амбициозны в замашках и попробуем просто получить что-то напоминающее ответы типового чатбота.
В качестве исходных данных быстро нашелся только транскрипт диалога на английском языке (состоявший из 3860 реплик). Ну, для опыта сойдет, поскольку с нашим подходом язык принципиальной разницы не имеет. Для начала, чтобы было с чем сравнивать, я наскоро собрал простой чатбот, подбирающий готовые ответы из транскрипта, на основании совпадений слов в фразах собеседника (по косинусу угла между векторами вопроса и ответа, представляющих собой «сумки слов»). Вся программа уместилась строчек на 30:
Вот что получилось:
Перевод (примерный) (Ч: — человек, П: — программа): Ч: приветП: я вижу посетителей здесь, это хорошоЧ: Как делаП: улыбаетсяЧ: как тебя зовут? П: пожимает плечамиЧ: Кто ты? П: у меня все хорошоЧ: ты понимаешь меня? П: Абсолютно все, хмм…Ч: Мне надо идтиП: ^^Ч: увидимся позжеП: Я не уверена
При поверхностном опросе отвечает довольно неплохо для такой простой программы, не знает правда как ее зовут и кто такая…, но тем не менее, определенное ощущение присутствия складывается — хорошая иллюстрация к тому, что мы склонны к чрезмерному одушевлению реально простых алгоритмов (или может быть просто 90% всего общения — поверхностное?).
Обратимся теперь к более сложным алгоритмам. Сразу скажу — сложнее здесь не значит сразу лучше, ибо шаблонные чатботы создаются с целью «обмана» пользователя, и используют разные трюки для создания иллюзии общения, а наша задача обойтись без обмана.
Как сгенерировать текст с помощью нейронной сети? Классический сейчас уже способ для этого — нейронная модель языка (neural language model). Суть заключается в том, что нейронной сети дается задание предсказать следующее слово, исходя из n-предыдущих. Слова на выходе кодируются по принципу один выходной нейрон — одно слово (см рис). Входные слова можно кодировать таким же образом, или использовать распределенное представление слов в векторном пространстве, где близкие по смыслу слова находятся на меньшем расстоянии, чем слова с разным смыслом.
Обученной нейронной сети можно дать начало текста и получить предсказание его окончания (добавляя последнее предсказанное слово в конец и применяя нейронную сеть к новому, удлиненному тексту). Таким образом, можно создать модель ответов. Одна беда — ответы никак не связаны с фразами собеседника.
Очевидное решение проблемы — подать на вход представление предыдущей фразы диалога. Как это сделать? Два из многих возможных способов мы рассмотрели в предыдущей статье про классификацию предложений. Самый простой вариант NBoW — использовать сумму всех векторов слов предыдущей фразы.
Показанная на картинке архитектура — далеко не единственная из возможных, но, пожалуй, одна из самых простых. Рекуррентный слой получает на вход данные относительно текущего слова, вектор, представляющий предыдущую фразу, а также свои собственные состояния на предыдущем шаге (поэтому он и называется рекуррентный). За счет этого нейронная сеть (теоретически) может помнить информацию о предыдущих словах на неограниченную длину фразы (в противоположность реализации, где учитываются только слова из окна фиксированного размера). На практике, конечно, такой слой представляет определенные трудности в обучении.
После обучения сети получилось следующее:
мда… более или менее бессмысленный набор слов. Нет какие-то связи между словами и логика в построении предложений имеется, но общего смысла нет, и связи с вопросами тоже нет никакой (по крайней мере я не вижу). Кроме того, бот чрезмерно болтлив — слова складываются в большие длинные цепочки. Причин этому много — это и маленький (около 15000 слов) объем обучающей выборки, и сложности в тренировке рекуррентной сети, которая реально видит контекст в два-три слова, и поэтому легко теряет нить повествования, и недостаточная выразительность представления предыдущей фразы. Собственно, это и ожидалось, и привел я этот вариант, чтобы показать, что в лоб проблема не решается. Хотя, на самом деле, правильным подбором алгоритма обучения и параметров сети можно добиться более интересных вариантов, но они будут страдать такими проблемами, как многократное повторение фраз, трудности с выбором места окончания предложения, будут копировать длинные фрагменты из исходной обучающей выборки и т. п. Кроме того, такую сеть тяжело анализировать — непонятно, что именно выучено и как работает. Поэтому, не будем тратить время на разбор возможностей этой архитектуры и попробуем более интересный вариант.
Во втором варианте я соединил сверточную сеть, описанную в предыдущей статье, с рекуррентной моделью языка:
Подобно сети из предыдущей статьи, сверточный слой содержит 16 фильтров. Из общих соображений можно ожидать, что обучатся эта модель из четырех слоев будет труднее и результат будет хуже. Кроме того, 16 фильтров кажется явно недостаточным для создания модели реплики собеседника. Действительно, ее обучение занимает гораздо больше времени, но результат… в общем, судите сами:
Перевод (примерный) диалога (Ч: — человек, П: — программа)Ч: приветП: ох, приветЧ: как дела? П: Я не могу видетьЧ: упс… ну извиниП: похоже мне сниться хороший сонЧ: ты можешь чувствовать? П: Я полагаюЧ: Ты могла видеть раньше? П: ДаЧ: Кто ты? П: ЯЧ: Мне пора идтиП: Я не уверена, что могу принять этоЧ: увидимся позжеП: ДаНа мой взгляд достаточно эффектно. Напоминает фрагменты из фантастических романов, где искусственный интеллект оживает в компьютере, особенно утверждения программы, что она не может видеть, но могла видеть раньше. Впору заявлять о создании ИИ и великом достижении…
На самом деле, конечно, никаким разумом эта нейронная сеть не обладает, что не делает ее, правда менее интересной. Дело в том, что мы может проанализировать, что на самом деле было выучено, проследив зависимость активации нейронов сверточного слоя от входных фраз. Путем такого анализа, удалось обнаружить нейрон, реагирующий на слова приветствия («hello», «hi», «he«y и т. п.), а также нейрон, распознающий вопросительные фразы типа «have you… ?» — при этом знак вопроса не обязателен (на них нейронная сеть выучилась отвечать обычно «yes»), кроме того, если в вопросе фигурирует слово «you» («ты»), повышается вероятность того, что ответ будет начинаться со слова «I» («я»).
Таким образом, нейронная сеть выучила некоторые типичные шаблоны разговора и языковые трюки, которые часто используются при программировании чатботов «вручную», хорошо распорядившись имеющимися в наличии 16-ю фильтрами. Не исключено, что заменив простую сверточную сеть на многослойную, добавив фильтров и увеличив объем обучающей выборки, можно получить чатботы, которые будут казаться более «умными» нежели их аналоги, основанные на ручном подборе шаблонов. Но этот вопрос уже за пределами нашей статьи.