Python для НЛП: создание чат-бота на основе правил

Это 12-я статья из моей серии статей о Python для НЛП. В предыдущей статье [/ python-for-nlp-working-with-the-gensim-library-part-2 /] я кратко объяснил различные функции библиотеки Python Gensim [https://pypi.org/project/ gensim /]. До сих пор в этой серии мы рассмотрели почти все наиболее часто используемые библиотеки НЛП, такие как NLTK, SpaCy, Gensim, StanfordCoreNLP, Pattern, TextBlob и т. Д. В этой статье мы не собираемся исследовать какие-либо библиотеки НЛП. Крыса

Это 12-я статья из моей серии статей о Python для НЛП. В предыдущей статье я кратко объяснил различные функции библиотеки Python Gensim. До сих пор в этой серии статей мы рассмотрели почти все наиболее часто используемые библиотеки НЛП, такие как NLTK, SpaCy, Gensim, StanfordCoreNLP, Pattern, TextBlob и т. Д.

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

Что такое чат-бот?

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

Чат-боты, ориентированные на конкретные задачи, предназначены для выполнения определенных задач. Например, чат-бот, ориентированный на задачи, может отвечать на запросы, связанные с бронированием поезда, доставкой пиццы; он также может работать в качестве личного терапевта или личного помощника.

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

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

Подходы к разработке чат-ботов

Подходы к разработке чат-ботов делятся на две категории: чат-боты на основе правил и чат-боты на основе обучения.

Чат-боты на основе обучения

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

Чат-боты на основе поиска учатся выбирать определенный ответ на запросы пользователей. С другой стороны, генеративные чат-боты учатся генерировать ответ на лету.

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

Чат-боты на основе правил

Чат-боты на основе правил довольно просты по сравнению с чат-ботами, основанными на обучении. Есть определенный набор правил. Если запрос пользователя соответствует любому правилу, генерируется ответ на запрос, в противном случае пользователь получает уведомление о том, что ответа на запрос пользователя не существует.

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

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

Разработка чат-бота на основе правил с помощью Python

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

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

Выполните следующие действия, чтобы разработать чат-бота:

Импорт необходимых библиотек

 import nltk 
 import numpy as np 
 import random 
 import string 
 
 import bs4 as bs 
 import urllib.request 
 import re 

Мы будем использовать библиотеку Beautifulsoup4 для анализа данных из Википедии. Кроме того, библиотека регулярных выражений Python re будет использоваться для некоторых задач предварительной обработки текста.

Создание корпуса

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

 raw_html = urllib.request.urlopen('https://en.wikipedia.org/wiki/Tennis') 
 raw_html = raw_html.read() 
 
 article_html = bs.BeautifulSoup(raw_html, 'lxml') 
 
 article_paragraphs = article_html.find_all('p') 
 
 article_text = '' 
 
 for para in article_paragraphs: 
 article_text += para.text 
 
 article_text = article_text.lower() 

Предварительная обработка текста и вспомогательная функция

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

 article_text = re.sub(r'\[[0-9]*\]', ' ', article_text) 
 article_text = re.sub(r'\s+', ' ', article_text) 

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

 article_sentences = nltk.sent_tokenize(article_text) 
 article_words = nltk.word_tokenize(article_text) 

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

Выполните следующий код:

 wnlemmatizer = nltk.stem.WordNetLemmatizer() 
 
 def perform_lemmatization(tokens): 
 return [wnlemmatizer.lemmatize(token) for token in tokens] 
 
 punctuation_removal = dict((ord(punctuation), None) for punctuation in string.punctuation) 
 
 def get_processed_text(document): 
 return perform_lemmatization(nltk.word_tokenize(document.lower().translate(punctuation_removal))) 

В сценарии выше мы первый экземпляр WordNetLemmatizer из NTLK библиотеки. Затем мы определяем функцию perform_lemmatization , которая принимает список слов в качестве входных данных и лемматизирует соответствующий лемматизированный список слов. Список punctuation_removal удаляет знаки препинания из переданного текста. Наконец, get_processed_text принимает предложение в качестве входных данных, токенизирует его, лемматизирует его, а затем удаляет знаки препинания из предложения.

Ответ на приветствия

Поскольку мы разрабатываем чат-бота на основе правил, нам нужно по-разному обрабатывать различные типы пользовательского ввода. Например, для приветствия мы определим специальную функцию. Для обработки приветствий мы создадим два списка: greeting_inputs и greeting_outputs . Когда пользователь вводит приветствие, мы попытаемся найти его в greetings_inputs , если приветствие будет найдено, мы случайным образом выберем ответ из списка greeting_outputs

Взгляните на следующий сценарий:

 greeting_inputs = ("hey", "good morning", "good evening", "morning", "evening", "hi", "whatsup") 
 greeting_responses = ["hey", "hey hows you?", "*nods*", "hello, how you doing", "hello", "Welcome, I am good and you"] 
 
 def generate_greeting_response(greeting): 
 for token in greeting.split(): 
 if token.lower() in greeting_inputs: 
 return random.choice(greeting_responses) 

Здесь метод generate_greeting_response() в основном отвечает за проверку приветствия и генерацию соответствующего ответа.

Ответ на запросы пользователей

Как мы уже говорили ранее, ответ будет сгенерирован на основе косинусного сходства векторизованной формы входного предложения и предложений в корпусах. Следующий скрипт импортирует функции TfidfVectorizer и cosine_similarity :

 from sklearn.feature_extraction.text import TfidfVectorizer 
 from sklearn.metrics.pairwise import cosine_similarity 

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

Взгляните на следующий сценарий:

 def generate_response(user_input): 
 tennisrobo_response = '' 
 article_sentences.append(user_input) 
 
 word_vectorizer = TfidfVectorizer(tokenizer=get_processed_text, stop_words='english') 
 all_word_vectors = word_vectorizer.fit_transform(article_sentences) 
 similar_vector_values = cosine_similarity(all_word_vectors[-1], all_word_vectors) 
 similar_sentence_number = similar_vector_values.argsort()[0][-2] 
 
 matched_vector = similar_vector_values.flatten() 
 matched_vector.sort() 
 vector_matched = matched_vector[-2] 
 
 if vector_matched == 0: 
 tennisrobo_response = tennisrobo_response + "I am sorry, I could not understand you" 
 return tennisrobo_response 
 else: 
 tennisrobo_response = tennisrobo_response + article_sentences[similar_sentence_number] 
 return tennisrobo_response 

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

 word_vectorizer = TfidfVectorizer(tokenizer=get_processed_text, stop_words='english') 
 all_word_vectors = word_vectorizer.fit_transform(article_sentences) 

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

В следующей строке:

 similar_vector_values = cosine_similarity(all_word_vectors[-1], all_word_vectors) 

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

Далее в следующей строке:

 similar_sentence_number = similar_vector_values.argsort()[0][-2] 

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

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

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

Общение с чат-ботом

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

Посмотрите на следующий скрипт, после этого код был объяснен:

 continue_dialogue = True 
 print("Hello, I am your friend TennisRobo. You can ask me any question regarding tennis:") 
 while(continue_dialogue == True): 
 human_text = input() 
 human_text = human_text.lower() 
 if human_text != 'bye': 
 if human_text == 'thanks' or human_text == 'thank you very much' or human_text == 'thank you': 
 continue_dialogue = False 
 print("TennisRobo: Most welcome") 
 else: 
 if generate_greeting_response(human_text) != None: 
 print("TennisRobo: " + generate_greeting_response(human_text)) 
 else: 
 print("TennisRobo: ", end="") 
 print(generate_response(human_text)) 
 article_sentences.remove(human_text) 
 else: 
 continue_dialogue = False 
 print("TennisRobo: Good bye and take care of yourself...") 

В приведенном выше сценарии мы сначала устанавливаем флаг continue_dialogue в значение true. После этого мы печатаем приветственное сообщение пользователю с просьбой ввести какие-либо данные. Затем мы инициализируем цикл while, который продолжается до тех пор, пока continue_dialogue станет истинным. Внутри цикла принимается пользовательский ввод, который затем преобразуется в нижний регистр. Пользовательский ввод хранится в переменной human_text Если пользователь вводит слово «до свидания», continue_dialogue устанавливается в значение false, и пользователю печатается прощальное сообщение.

С другой стороны, если введенный текст не равен «пока», проверяется, содержит ли ввод такие слова, как «спасибо», «спасибо» и т. Д. Или нет. Если такие слова встречаются, выдается ответ «Приветствую». В противном случае, если пользовательский ввод не равен None , generate_response который выбирает пользовательский ответ на основе подобия косинуса, как объяснено в последнем разделе.

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

Результат работы скрипта чат-бота выглядит так:

{.ezlazyload}

Вы можете видеть на изображении выше, что я ввел ввод «roger federer», и полученный ответ:

 however it must be noted that both rod laver and ken rosewall also won major pro slam tournaments on all three surfaces (grass, clay, wood) rosewall in 1963 and laver in 1967. more recently, roger federer is considered by many observers to have the most "complete" game in modern tennis." 

Ответ может быть неточным, но все же имеет смысл.

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

Заключение

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

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

comments powered by Disqus