Рубрики
Без рубрики

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

Автор оригинала: Usman Malik.

Это 12-я статья в моей серии статей по Python для НЛП. В предыдущей статье я кратко объяснил различные функциональные возможности библиотеки Python Gensim . До сих пор в этой серии мы рассматривали почти все наиболее часто используемые библиотеки НЛП, такие как NLTK, Space, 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)

Наконец, нам нужно создать вспомогательные функции, которые удалят знаки препинания из вводимого пользователем текста, а также лемматизируют текст. Лемматизация относится к приведению слова к его корневой форме. Например, лемматизация мира “are” возвращается, слово “throwing” станет throw, а слово “worse” сведется к “bad”.

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

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() принимает один параметр, который является пользовательским вводом. Далее мы определяем пустую строку tennis robo_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 . Если пользователь вводит слово “bye”, то continue_dialogue устанавливается в false и пользователю выводится сообщение goodbye.

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

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

Вывод скрипта чат-бота выглядит следующим образом:

Вы можете видеть на приведенном выше изображении, что я ввел ввод “Роджер Федерер”, и сгенерированный ответ таков::

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."

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

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

Вывод

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

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