Краткое введение в чат-боты:

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

  • Заказ платформ доставки еды, таких как Zomato, Swiggy и т. д., где клиент может передать свои жалобы и претензии, а чат-боты могут позаботиться об этом на элементарном этапе.
  • Портал обслуживания клиентов для мобильных подключений и коммуникаций, таких как Jio, Vodafone и т. д.
  • Веб-сайты с чат-ботами для решения основных вопросов, заданных посетителем.

И так далее….

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

  1. Цель пользователя (Что интересует пользователя?)
  2. Сказал ли пользователь что-нибудь общее или конкретное?
  3. Какие вопросы можно задать, чтобы лучше понять требования пользователя?
  4. Какой может быть наиболее актуальный ответ на вопрос, заданный пользователем?

Эффективность ответа или ответа, данного чат-ботом пользователю, будет сильно зависеть от того, насколько точно чат-бот находит ответы на перечисленные выше вопросы.

Теперь мы рассмотрим два типа используемых чат-ботов:

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

Здесь важно понимать концепцию открытого и закрытого домена по отношению к чат-боту.

  • Открытый домен (универсальные боты): это в основном открытый разговор, в котором пользователь может взять содержание или тему разговора где угодно. Это полностью на усмотрение пользователя. Пространство возможных входов и выходов в этом случае идеально бесконечно или неограниченно. Например: В широком смысле финансовый менеджмент, способы снижения налога на прибыль.
  • Закрытый домен (боты-специалисты): здесь объем или пространство возможных входных и выходных данных несколько ограничены. Система в основном предназначена для достижения очень конкретной цели или набора или целей. Например: Услуга заказа еды из заданного меню в ресторане, где количество вариантов может быть очень большим, но конечным.

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

Процедура:

Первым шагом в любой проблеме науки о данных или машинного обучения является тщательная предварительная обработка или очистка данных, чтобы сделать их готовыми к анализу (также называемая обработкой данных). В большинстве моделей машинного обучения на это уходит около 80–90% времени. Для задач анализа текста и НЛП предварительная обработка еще более важна и сложна. Как только данные готовы для анализа, можно опробовать многочисленные модели, чтобы понять и внедрить лучшую модель.

Очистка/предварительная обработка данных

Очистка данных в этом случае по существу включает следующие шаги:

  • Весь текст сначала преобразуется в нижний регистр для однородности и простоты анализа. Например, «Помощь» и «помощь» по сути имеют одно и то же значение, но когда текст преобразуется в векторы, он будет храниться как 2 разных вектора и обрабатываться отдельно, что увеличивает нагрузку на вычисления и хранение. Преобразование всего текста в нижний регистр (выбирается по соглашению, верхний регистр также может быть выполнен, но, как правило, не выполняется традиционно) значительно снижает вычислительную нагрузку.
  • Следующий шаг включает в себя токенизацию, то есть разбиение заданной строки или текста на отдельные слова или токены. Например: A={'солнце встает на востоке'} разбивается на A= {'the', 'sun', 'rises', 'in', 'the', 'east'}. Суть любой задачи машинного обучения в основном состоит в том, чтобы разбить проблему на наименьшую значимую часть для простоты понимания и микроанализа. Токенизация выполняет эту задачу в случае интеллектуального анализа текста достаточно полно. Предложения в целом трудно анализировать и интерпретировать с помощью компьютера, но когда они разбиты на слова или токены и преобразованы в векторы, они становятся вполне управляемыми.
  • Третий шаг предполагает добавление сгенерированных на предыдущем шаге токенов в словарь, формирующий хранилище слов для сравнения и анализа на последующих шагах.
dir_path = 'chatbot_nlp/data' files_list = os.listdir(dir_path + os.sep) questions = list() answers = list() for filepath in files_list: stream = open( dir_path + os.sep + filepath , 'rb') docs = yaml.safe_load(stream) conversations = docs['conversations'] for con in conversations: if len( con ) > 2 : questions.append(con[0]) replies = con[ 1 : ] ans = '' for rep in replies: ans += ' ' + rep answers.append( ans ) elif len( con )> 1: questions.append(con[0]) answers.append(con[1]) answers_with_tags = list() for i in range( len( answers ) ): if type( answers[i] ) == str: answers_with_tags.append( answers[i] ) else: questions.pop( i ) answers = list() for i in range( len( answers_with_tags ) ) : answers.append( '<START> ' + answers_with_tags[i] + ' <END>' ) tokenizer = preprocessing.text.Tokenizer() tokenizer.fit_on_texts( questions + answers ) VOCAB_SIZE = len( tokenizer.word_index )+1 print( 'VOCAB SIZE : {}'.format( VOCAB_SIZE ))

Подготовка среды для реализации нейросетевой модели:

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

Теперь давайте подумаем с точки зрения чат-бота. Предполагается, что он будет принимать вопросы от пользователя и генерировать ответ или ответ на вопрос с максимальной релевантностью. Поскольку, давая соответствующий ответ, LSTM обеспечивают огромное улучшение по сравнению с обычными RNN.

Для этого подготавливаем следующие массивы:

1) Входные данные кодировщика:

  • Во-первых, введенные вопросы преобразуются в токены.
  • Затем определяется максимальная длина токенизированных вопросов.
  • Затем токенизированные вопросы дополняются до максимальной длины (как было найдено на предыдущем шаге) с использованием пост-заполнения и сохраняются в массиве numpy, сохраняемом как входные данные кодировщика.

2) Входные данные декодера:

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

3) Выходные данные декодера:

Здесь вопрос в контексте. Ответ, предоставляемый чат-ботом, должен удовлетворять основной цели предоставления клиенту максимально релевантной информации. При этом учет фактора времени обязателен. Возьмем пример.

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

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

Важными шагами здесь являются:

  • Токенизация ответа на основе контекста времени для текущего целевого токена.
  • Горячее кодирование дополненных ответов. Горячее кодирование — это четко определенный метод машинного обучения, при котором данные кодируются в виде строк и столбцов по существу двоичных чисел, 1 или 0. Поскольку вычисления внутри машины происходят только с двоичными числами (т. е. даже с текстом, видео, аудио или любые данные, на самых основных уровнях анализа в компьютерах преобразуются в комбинации 0 и 1), горячее кодирование также является важным шагом для анализа.
  • Наконец, выходные данные сохраняются в массиве с именем выходные данные декодера.
from gensim.models import Word2Vec import re vocab=[] for word in tokenizer.word_index: vocab.append(word) def tokenize(sentences): tokens_list=[] vocabulary=[] for sentence in sentences: sentence = sentence.lower() sentence = re.sub( '^a-zA-Z',' ',sentence) tokens = sentence.split() vocabulary+=tokens tokens_list.append(tokens) return tokens_list,vocabulary p=tokenize(questions+answers) model = Word2Vec(p[0]) ''' embedding_matrix = np.zeros((VOCAB_SIZE,100)) for i in range(len(tokenizer.word_index)): embedding_matrix[i] = model[vocab[i]] ''' tokenized_questions = tokenizer.texts_to_sequences(questions) maxlen_questions = max([len(x) for x in tokenized_questions]) padded_questions = preprocessing.sequence.pad_sequences(tokenized_questions, maxlen=maxlen_questions, padding='post') encoder_input_data = np.array( padded_questions ) print(encoder_input_data.shape, maxlen_questions) tokenized_answers = tokenizer.texts_to_sequences(answers) maxlen_answers = max([len(x) for x in tokenized_answers]) padded_answers = preprocessing.sequence.pad_sequences(tokenized_answers, maxlen=maxlen_answers, padding='post') decoder_input_data = np.array(padded_answers) print(decoder_input_data.shape, maxlen_answers) tokenized_answers = tokenizer.texts_to_sequences(answers) for i in range(len(tokenized_answers)): tokenized_answers[i]=tokenized_answers[i][1:] padded_answers = preprocessing.sequence.pad_sequences(tokenized_answers, maxlen=maxlen_answers, padding='post') one_hot_answers = utils.to_categorical(padded_answers, VOCAB_SIZE) decoder_output_data = np.array(one_hot_answers) print(decoder_output_data.shape)

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

Модель LSTM RNN:

Теперь мы переходим к использованию рекуррентной нейронной сети (RNN) с долговременной короткой памятью (LSTM), состоящей из внутренних вентилей.

Мы используем LSTM RNN вместо обычного RNN, чтобы удалить долгосрочные зависимости. В LSTM каждая единица или ячейка в этом слое подразделяется на 2 типа: внутреннее состояние ячейки (сокращенно c) и выходное скрытое состояние (сокращенно h). Есть ворота, которые выводят значение от 0 до 1, 0 означает полное избавление от информации, а 1 означает полное удаление этой информации. Далее, информация для хранения входного слоя определяется совместно сигмовидным и тангенциальным слоями. Подробное описание того, как работает LSTM, можно найти по этой ссылке http://colah.github.io/posts/2015-08-Understanding-LSTMs/.

Базовая конфигурация используемой RNN выглядит следующим образом:

  1. Есть 2 входных слоя, один для входных данных кодировщика, а другой для входных данных декодера.
  2. Слой встраивания: Встраивание слов — это класс подходов к представлению слов и документов с использованием плотных векторных представлений. Здесь он используется для преобразования векторов токенов в плотные векторы фиксированного размера.
  3. Уровень LSTM: предоставление доступа к ячейкам Long-Short Term.
  4. Rmsprop используется в качестве оптимизатора, а категориальная кросс-энтропия используется здесь в качестве функции потерь.
encoder_inputs = tf.keras.layers.Input(shape=( None , )) encoder_embedding = tf.keras.layers.Embedding( VOCAB_SIZE, 200 , mask_zero=True ) (encoder_inputs) encoder_outputs , state_h , state_c = tf.keras.layers.LSTM( 200 , return_state=True )( encoder_embedding ) encoder_states = [ state_h , state_c ] decoder_inputs = tf.keras.layers.Input(shape=( None , )) decoder_embedding = tf.keras.layers.Embedding( VOCAB_SIZE, 200 , mask_zero=True) (decoder_inputs) decoder_lstm = tf.keras.layers.LSTM( 200 , return_state=True , return_sequences=True ) decoder_outputs , _ , _ = decoder_lstm ( decoder_embedding , initial_state=encoder_states ) decoder_dense = tf.keras.layers.Dense( VOCAB_SIZE , activation=tf.keras.activations.softmax ) output = decoder_dense ( decoder_outputs ) model = tf.keras.models.Model([encoder_inputs, decoder_inputs], output ) model.compile(optimizer=tf.keras.optimizers.Adam(), loss='categorical_crossentropy', metrics=['accuracy']) model.summary()
model.fit([encoder_input_data , decoder_input_data], decoder_output_data, batch_size=50, epochs=100 )

Базовая модель последовательности LSTM для последовательности обучена прогнозировать выходные данные декодера с учетом входных данных кодера и входных данных декодера. Входные данные кодировщика поступают на слой Embedding (внедрение кодировщика). Выход слоя Embedding поступает в ячейку LSTM, которая создает 2 вектора состояния (h и c, которые являются состояниями кодировщика, как описано выше). Эти состояния устанавливаются в ячейке LSTM декодера. Входные данные декодера поступают через слой внедрения. Встраивание идет в ячейку LSTM (в которой были состояния) для создания последовательностей.

def make_inference_models(): encoder_model = tf.keras.models.Model(encoder_inputs, encoder_states) decoder_state_input_h = tf.keras.layers.Input(shape=( 200 ,)) decoder_state_input_c = tf.keras.layers.Input(shape=( 200 ,)) decoder_states_inputs = [decoder_state_input_h, decoder_state_input_c] decoder_outputs, state_h, state_c = decoder_lstm( decoder_embedding , initial_state=decoder_states_inputs) decoder_states = [state_h, state_c] decoder_outputs = decoder_dense(decoder_outputs) decoder_model = tf.keras.models.Model( [decoder_inputs] + decoder_states_inputs, [decoder_outputs] + decoder_states) return encoder_model , decoder_model def str_to_tokens( sentence : str ): words = sentence.lower().split() tokens_list = list() for word in words: tokens_list.append( tokenizer.word_index[ word ] ) return preprocessing.sequence.pad_sequences( [tokens_list] , maxlen=maxlen_questions , padding='post')
enc_model , dec_model = make_inference_models() for _ in range(10): states_values = enc_model.predict( str_to_tokens( input( 'Enter question : ' ) ) ) empty_target_seq = np.zeros( ( 1 , 1 ) ) empty_target_seq[0, 0] = tokenizer.word_index['start'] stop_condition = False decoded_translation = '' while not stop_condition : dec_outputs , h , c = dec_model.predict([ empty_target_seq ] + states_values ) sampled_word_index = np.argmax( dec_outputs[0, -1, :] ) sampled_word = None for word , index in tokenizer.word_index.items() : if sampled_word_index == index : decoded_translation += ' {}'.format( word ) sampled_word = word if sampled_word == 'end' or len(decoded_translation.split()) > maxlen_answers: stop_condition = True empty_target_seq = np.zeros( ( 1 , 1 ) ) empty_target_seq[ 0 , 0 ] = sampled_word_index states_values = [ h , c ] print( decoded_translation )

Взаимодействие с созданным чат-ботом:

В наших следующих статьях мы обсудим:

  1. Чат-боты на основе поиска с помощью Python (чат-боты, обученные набору предопределенных ответов.
  2. Создание GUI (графического пользовательского интерфейса), такого как помощь Google, который может быть интегрирован с любым веб-сайтом или приложением.

Ссылка

Понимание LSTM Разница между последовательностями возврата и состояниями возврата для LSTM Рекуррентные слои (LSTM) Введение в режим seq2seq Объяснение чат-ботов

Авторы:

Арпан Сил

Контейо Рой Чоудхури

Первоначально опубликовано на http://mathematicacity.co.in 15 августа 2020 г.