Image Image Image Image Image Image Image Image Image Image

Интересное о компьютерах | Сегодня: Суббота, 19 августа 2017 года

Scroll to top

Top

No Comments

Как самому написать чат-бота?

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

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

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

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

Анализ фразы, введенной человеком

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

Здесь есть несколько вариантов. Различия в поиске слов во фразе:

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

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

Различие в выводе ответной фразы:

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

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

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

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

Обучение

  1. Не присутствует ни в каких формах. Впрочем, это не значит, что такую программу ничему нельзя научить: в большинстве случаев базы данных таких программ весьма успешно правятся вручную.
  2. Есть специальный режим обучения — отдельно от разговора. Во время обычного разговора программа не обучается (тем не менее, режим обучения часто присутствует и в программах, которые могут обучаться непосредственно во время разговора!).
  3. Каждая фраза пользователя в ходе диалога заносится в базу без анализа (такие программы в ходе обучения очень часто «глупеют», начинают выдавать реплики, не связанные с предыдущей репликой пользователя).
  4. Каждая фраза пользователя заносится в базу после предварительного анализирования (в этом случае, если алгоритм анализа фразы хороший, программа умнеет на глазах, но размер базы катастрофически быстро растет, и очень скоро программа начинает «тормозить»).
  5. После предварительного анализа в базу заносятся не все, а лишь некоторые фразы (например, относящиеся к значимым темам или являющиеся ответом на наиболее часто встречающиеся реплики). В этом случае, даже если алгоритм анализа фразы примитивен, программа умнеет достаточно быстро, а в базу не попадают бессмысленные реплики; но необходимо выработать удачные критерии отбора фраз.

Особого рассмотрения требует добавление фраз человека в базу. Ее структура может быть различна. Например, это может быть просто последовательность строк в файле, или записи с множеством полей, или дерево реплик. Если с фразами и записями все более-менее понятно (дописывай себе новую фразу в конец базы — или в начало, смотря как она реализована), то с деревом все немного сложнее. Трудности возникают с определением того, в какое именно место дерева надо дописать новую фразу. Естественно, если дописывать ее к первой попавшейся ветке, с течением времени программа начнет не умнеть, а, наоборот, глупеть. Есть алгоритмы поиска по дереву и добавления новых элементов, если речь идет о числах или же о строках неосмысленных, то есть когда важно не значение фразы, а лишь ее длина или алфавитный порядок. Здесь же важен в первую очередь именно смысл. Значит, нужно каким-то образом сравнивать фразы друг с другом по смыслу. Поскольку вводимые фразы навряд ли будут полностью совпадать с какими-то эталонными фразами (за исключением совсем коротких реплик «Да», «Нет», «Конечно», «Не знаю» и т.п.), нужно задать критерии «похожести» фраз (с учетом контекста, то есть смысла предыдущих реплик). Как выяснить, на какую из известных фраз больше всего похожа данная фраза? Честно говоря, я не знаю. То есть я могу придумать несколько алгоритмов, но они явно будут неэффективными и недостаточно результативными. Например, если искать максимальное число как можно более длинных последовательностей символов, совпадающих во введенной человеком фразе и во фразах, уже имеющихся в дереве, могут возникнуть случайные совпадения — если во фразах есть очень длинные и очень похожие слова с совершенно разным смыслом. Если взять, например, слова «интенсификация» и «электрификация», то у них совпадают последние 8 букв, что может дать повод программе считать эти слова достаточно близкими (ведь в разговорной речи не так уж часто используются длинные слова!). Если бы эти два слова использовались в разговоре достаточно часто, у программы был бы шанс разобраться, в чем же заключается смысловая разница между ними. Но скорее всего такие слова, промелькнув в разговоре, будут на долгое время забыты, а ошибка всплывет когда-нибудь потом. В одной такой ошибке не было бы ничего страшного, но ведь ошибки накапливаются… К тому же такой алгоритм гарантированно будет спотыкаться на омонимах (словах с одинаковым написанием и разным смыслом) — если не учитывать смысл других слов фразы. Также программы с подобным алгоритмом будут не очень хорошо работать, если человеку вздумается поиграть словами, используя их «не в том смысле». Я не знаю, по какому алгоритму работает программа Chat Master Дмитрия Журавлева, но подозреваю, что там также имеет место анализ фраз на предмет максимальной схожести с уже имеющимися словами и фразами. Так вот, стоило мне в разговоре на компьютерные темы с этой программой ввести какую-то фразу типа «Windows 95 — новый уровень интерактивной эротики» или что-то в этом роде (ради эксперимента я вводила и «нормальные» фразы, и анекдоты, и афоризмы, и цитаты из книг — но все в контексте, то есть человек на такую фразу прореагировал бы адекватно), — так вот, стоило мне ввести в программу такую фразу, как ее «заклинило»: все разговоры о компьютерах стали сводиться почему-то исключительно на секс, и мне пришлось затратить немало сил, чтобы переучить программу.

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

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

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

Итак, как же это сделать и что для этого нужно знать и уметь?

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

Заводим последовательность фраз-ответов, соответствующих этим ключевым словам (в принципе, возможен вариант, когда на каждое ключевое слово существует много разных вариантов ответа, но мы пока рассматриваем простейший случай). Где хранятся ответы — не суть важно: в той же строке файла, что и соответствующее ключевое слово (через разделитель), или в другом файле, или в массиве… Главное — уметь по ключевому слову найти соответствующую реакцию.

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

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

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

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

Есть двумерный массив slova, размер — два на, скажем, десять (количество столбцов можно увеличить — тогда словарный запас программы возрастет), тип элементов — строковые величины. В первой его строке записаны ключевые слова и словосочетания, во второй — ответные реакции. Значения элементов могут быть, например, такими:

slova[1,1] = «ТЕБЯ ЗОВУТ?»

slova[2,1] = «Меня зовут Программа.»

slova[1,2] = «ДЕЛА?»

slova[2,2] = «У меня все хорошо, а у Вас?»

slova[1,3] = «УМЕЕШЬ»

slova[2,3] = «Мои возможности очень ограниченны.»

slova[1,4] = «ЗНАЕШЬ»

slova[2,4] = «К сожалению, я пока очень мало знаю…»

slova[1,5] = «МОЖЕШЬ»

slova[2,5] = «Мои возможности очень ограниченны.»

slova[1,6] = «ЛЮБИШЬ»

slova[2,6] = «У меня нет ярко выраженных предпочтений.»

slova[1,7] = «ПОНИМАЕШЬ»

slova[2,7] = «Я пока понимаю далеко не все, потому что у меня маленькая база.»

slova[1,8] = «?»

slova[2,8] = «У меня недостаточно знаний, чтобы ответить на Ваш вопрос.»

slova[1,9] = «ПРИВЕТ»

slova[2,9] = «Очень приятно Вас видеть.»

slova[1,10] = «ЗДРАВСТВУЙ»

slova[2,10] = «Здравствуйте.»

Еще есть одномерный массив случайных фраз; можно было забить эти фразы в файл, но мы же договорились, что с файлами работать не будем…

Вот какими могут быть эти фразы:

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

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

Итак:

  1. вводим фразу fraza с клавиатуры, заменяем все маленькие буквы на заглавные;
  2. присваиваем переменной otwet значение «» (пустая строка);
  3. в цикле, пока i меньше n (число ключевых слов) и otwet равен пустой строке, делаем следующее: если элемент массива slova[1,i] входит в строку fraza, переменной otwet присваиваем значение slova[2,i] (выходим из цикла, если ключевые слова закончились или если мы уже нашли фразу-реакцию);
  4. если переменная otwet по-прежнему является равной пустой строке, делаем следующее:
  5. выбираем случайное целое число от 1 до m, где m — число «пустых» фраз (в некоторых языках программирования есть для этого специальные функции, а в некоторых есть только случайное число от 0 до 1 — тогда придется записать формулу);
  6. из массива случайных фраз выбираем фразу с таким номером и присваиваем переменной otwet.
  7. выводим значение переменной otwet на экран.

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

Для обработки вводимых фраз нужна процедура поиска ключевых слов во фразе.

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

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

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

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

Как составить базу фраз для эмулятора искусственного интеллекта?

Разумеется, если речь идет о настоящем искусственном интеллекте — тут все просто. Настоящий ИИ трудно написать, но учить его — одно удовольствие. Разговаривай с ним почаще, и рано или поздно твой искусственный интеллект сможет поддерживать беседу на должном уровне, даже если изначально он не знал почти ничего. А вот с эмуляторами дело обстоит хуже. Их, наоборот, довольно легко писать, но трудно учить (если речь идет об осмысленном обучении; тупое занесение всех фраз подряд в один и тот же файл настоящим обучением считаться не может, так как в этом случае программа скоро начнет выдавать бессмысленные реплики). Обычно эмуляторы ИИ создаются с какой-то готовой базой реплик. Эта база может потом пополняться (программно или вручную), но какая-то основа должна быть. В базу заносятся какие-то фразы, слова или части слов, на которые программа изначально должна уметь реагировать правильно, и ответные реплики. Каковы будут эти ответные реплики — пусть подскажет ваша фантазия. Правда, опыт показывает мне, что одни и те же ответные реплики часто кочуют из одной программы в другую. Вообще заимствование десятка-другого подобных удачных фраз (разумеется, без присвоения авторства) плагиатом, на мой взгляд, не является: такие фразы просто становятся своего рода афоризмами, вроде высказываний Фоменко (их я тоже привожу в пример не случайно: они встречаются в базах многих эмуляторов искусственного интеллекта). Немалую пользу приносит также изучение чужой базы на предмет приобретения полезного опыта. Но полное заимствование чьей-то чужой базы — это не есть хорошо, и не только потому, что это плагиат. Кому нужны два десятка собеседников с одним и тем же словарным запасом? Итак, будем поступать честно, в соответствии с заповедью «не укради», и не станем заимствовать чужую базу.

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

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

Откуда брать ответные реакции? Естественно, лучше всего было бы придумать их самим. В большинстве случаев так и делается. Но есть довольно популярные источники реплик для эмуляторов искусственного интеллекта. Это анекдоты, афоризмы, поговорки, фразы Фоменко, цитаты из песен, из любимых фильмов и книг и т.д. Чаще всего такая фраза моментально приходит в голову. А что делать, если не пришла? Можно кропотливо выискивать подходящую к случаю известную фразу. А можно поступить проще. Допустим, нам нужно ответить на вопрос «Что такое деньги?». Можно ответить «Откуда я знаю» или придумать собственное определение — иногда оно получается довольно оригинальным. Но если придуманный ответ оригинальностью не отличается — заходим, например, на Яндекс, и ищем страницы с афоризмами или анекдотами, на которых есть слово «деньги». Среди найденного гарантированно будет парочка фраз, удовлетворяющих вашему взыскательному вкусу. Разумеется, это всего лишь один из множества способов…