Python para PNL: creación de un chatbot basado en reglas

Este es el artículo número 12 de mi serie de artículos sobre Python para PNL. En el artículo anterior, expliqué brevemente las diferentes funcionalidades de Python's...

Este es el artículo número 12 de mi serie de artículos sobre Python para PNL. En el Artículo anterior, expliqué brevemente las diferentes funcionalidades de la biblioteca Gensim de Python. /pypi.org/project/gensim/). Hasta ahora, en esta serie, hemos cubierto casi todas las bibliotecas de NLP más utilizadas, como NLTK, SpaCy, Gensim, StanfordCoreNLP, Pattern, TextBlob, etc.

En este artículo, no vamos a explorar ninguna biblioteca de PNL. Más bien, desarrollaremos un chatbot basado en reglas muy simple capaz de responder a las consultas de los usuarios sobre el deporte del tenis. Pero antes de comenzar con la codificación real, analicemos brevemente qué son los chatbots y cómo se usan.

¿Qué es un chatbot?

Un chatbot es un agente conversacional capaz de responder a las consultas de los usuarios en forma de texto, voz o a través de una interfaz gráfica de usuario. En palabras simples, un chatbot es una aplicación de software que puede chatear con un usuario sobre cualquier tema. Los chatbots se pueden clasificar en términos generales en dos tipos: Chatbots orientados a tareas y Chatbots de propósito general.

Los chatbots orientados a tareas están diseñados para realizar tareas específicas. Por ejemplo, un chatbot orientado a tareas puede responder consultas relacionadas con la reserva de trenes, la entrega de pizzas; también puede trabajar como terapeuta médico personal o asistente personal.

Por otro lado, los chatbots de propósito general pueden tener discusiones abiertas con los usuarios.

También hay un tercer tipo de chatbots llamados chatbots híbridos que pueden participar en discusiones abiertas y orientadas a tareas con los usuarios.

Enfoques para el desarrollo de chatbots

Los enfoques de desarrollo de chatbots se dividen en dos categorías: chatbots basados ​​en reglas y chatbots basados ​​en aprendizaje.

Chatbots basados ​​en aprendizaje

Los chatbots basados ​​en aprendizaje son el tipo de chatbots que utilizan técnicas de aprendizaje automático y un conjunto de datos para aprender a generar una respuesta a las consultas de los usuarios. Los chatbots basados ​​en aprendizaje se pueden dividir en dos categorías: chatbots basados ​​en recuperación y chatbots generativos.

Los chatbots basados ​​en recuperación aprenden a seleccionar una determinada respuesta a las consultas de los usuarios. Por otro lado, los chatbots generativos aprenden a generar una respuesta sobre la marcha.

Una de las principales ventajas de los chatbots basados ​​en el aprendizaje es su flexibilidad para responder a una variedad de consultas de los usuarios. Aunque es posible que la respuesta no siempre sea correcta, los chatbots basados ​​en el aprendizaje son capaces de responder a cualquier tipo de consulta del usuario. Uno de los principales inconvenientes de estos chatbots es que pueden necesitar una gran cantidad de tiempo y datos para entrenarse.

Chatbots basados ​​en reglas

Los chatbots basados ​​en reglas son bastante sencillos en comparación con los chatbots basados ​​en aprendizaje. Hay un conjunto específico de reglas. Si la consulta del usuario coincide con alguna regla, se genera la respuesta a la consulta; de lo contrario, se notifica al usuario que la respuesta a la consulta del usuario no existe.

Una de las ventajas de los chatbots basados ​​en reglas es que siempre dan resultados precisos. Sin embargo, en el lado negativo, no escalan bien. Para agregar más respuestas, debe definir nuevas reglas.

En la siguiente sección, explicaré cómo crear un chatbot basado en reglas que responderá a consultas simples de los usuarios sobre el deporte del tenis.

Desarrollo de chatbot basado en reglas con Python

El chatbot que vamos a desarrollar será muy sencillo. Primero necesitamos un corpus que contenga mucha información sobre el deporte del tenis. Desarrollaremos dicho corpus raspando el artículo de Wikipedia sobre tenis. A continuación, realizaremos un preprocesamiento en el corpus y luego dividiremos el corpus en oraciones.

Cuando un usuario ingresa una consulta, la consulta se convertirá en forma vectorizada. Todas las oraciones del corpus también se convertirán en sus formas vectorizadas correspondientes. A continuación, se seleccionará la oración con la mayor similitud de coseno con el vector de entrada del usuario como respuesta a la entrada del usuario.

Siga estos pasos para desarrollar el chatbot:

Importación de bibliotecas requeridas

1
2
3
4
5
6
7
8
import nltk
import numpy as np
import random
import string

import bs4 as bs
import urllib.request
import re

Hay muchas formas de analizar datos de Wikipedia. Además, la Biblioteca de expresiones regulares de Python, re, se usará para algunas tareas de preprocesamiento en el texto.

Creando el Corpus

Como dijimos anteriormente, usaremos el artículo de Wikipedia sobre Tenis para crear nuestro corpus. El siguiente script recupera el artículo de Wikipedia y extrae todos los párrafos del texto del artículo. Finalmente, el texto se convierte a minúsculas para facilitar el procesamiento.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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()

Preprocesamiento de texto y función auxiliar

A continuación, debemos preprocesar nuestro texto para eliminar todos los caracteres especiales y espacios vacíos de nuestro texto. La siguiente expresión regular hace eso:

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

Necesitamos dividir nuestro texto en oraciones y palabras, ya que la similitud del coseno de la entrada del usuario se comparará con cada oración. Ejecute el siguiente script:

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

Finalmente, necesitamos crear funciones auxiliares que eliminen la puntuación del texto ingresado por el usuario y también lematicen el texto. La lematización se refiere a reducir una palabra a su forma raíz. Por ejemplo, la lematización de la palabra "ate" devuelve comer, la palabra "arrojar" se convertirá en tirar y la palabra "peor" se reducirá a "malo".

Ejecuta el siguiente código:

1
2
3
4
5
6
7
8
9
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)))

En el script anterior, primero creamos una instancia de WordNetLemmatizer de la biblioteca NTLK. A continuación, definimos una función perform_lemmatization, que toma una lista de palabras como entrada y lematiza la correspondiente lista lematizada de palabras. La lista puntuation_removal elimina la puntuación del texto pasado. Finalmente, el método get_processed_text toma una oración como entrada, la tokeniza, la lematiza y luego elimina la puntuación de la oración.

Responder a los saludos

Dado que estamos desarrollando un chatbot basado en reglas, debemos manejar los diferentes tipos de entradas de los usuarios de manera diferente. Por ejemplo, para los saludos definiremos una función dedicada. Para manejar los saludos, crearemos dos listas: saludo_entradas y saludo_salidas. Cuando un usuario ingresa un saludo, intentaremos buscarlo en la lista saludos_entradas, si se encuentra el saludo, elegiremos aleatoriamente una respuesta de la lista saludos_salidas.

Mira el siguiente guión:

1
2
3
4
5
6
7
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)

Aquí el método generate_greeting_response() es básicamente responsable de validar el mensaje de saludo y generar la respuesta correspondiente.

Responder a las consultas de los usuarios

Como dijimos anteriormente, la respuesta se generará en función de la similitud del coseno de la forma vectorizada de la oración de entrada y las oraciones en los corpus. El siguiente script importa las funciones TfidfVectorizer y cosine_similarity:

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

Ahora tenemos todo configurado para generar una respuesta a las consultas de los usuarios relacionadas con el tenis. Crearemos un método que tome la entrada del usuario, encuentre la similitud del coseno de la entrada del usuario y la compare con las oraciones en el corpus.

Mira el siguiente guión:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
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

Puedes ver que el método generate_response() acepta un parámetro que es una entrada del usuario. A continuación, definimos una cadena vacía tennisrobo_response. Luego agregamos la entrada del usuario a la lista de oraciones ya existentes. Después de eso en las siguientes líneas:

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

Inicializamos el tfidfvectorizer y luego convertimos todas las oraciones en el corpus junto con la oración de entrada en su forma vectorizada correspondiente.

En la siguiente línea:

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

Usamos la función cosine_similarity para encontrar la similitud del coseno entre el último elemento en la lista all_word_vectors (que en realidad es el vector de palabras para la entrada del usuario ya que se agregó al final) y los vectores de palabras para todas las oraciones en el corpus

A continuación, en la siguiente línea:

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

Ordenamos la lista que contiene las similitudes de coseno de los vectores, el penúltimo elemento de la lista en realidad tendrá el coseno más alto (después de ordenar) con la entrada del usuario. El último elemento es la entrada del usuario en sí, por lo tanto, no lo seleccionamos.

Finalmente, aplanamos la similitud del coseno recuperada y verificamos si la similitud es igual a cero o no. Si la similitud del coseno del vector coincidente es 0, eso significa que nuestra consulta no tuvo respuesta. En ese caso, simplemente imprimiremos que no entendemos la consulta del usuario.

De lo contrario, si la similitud del coseno no es igual a cero, eso significa que encontramos una oración similar a la entrada en nuestro corpus. En ese caso, simplemente pasaremos el índice de la oración coincidente a nuestra lista "articulos_oraciones" que contiene la colección de todas las oraciones.

Charlando con el Chatbot

Como paso final, necesitamos crear una función que nos permita chatear con el chatbot que acabamos de diseñar. Para hacerlo, escribiremos otra función auxiliar que seguirá ejecutándose hasta que el usuario escriba "Bye".

Mire el siguiente script, el código se ha explicado después de eso:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
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...")

En el script anterior, primero establecemos el indicador continue_dialogue en verdadero. Después de eso, imprimimos un mensaje de bienvenida al usuario solicitando cualquier entrada. A continuación, inicializamos un ciclo while que continúa ejecutándose hasta que el indicador continue_dialogue sea verdadero. Dentro del ciclo, se recibe la entrada del usuario, que luego se convierte a minúsculas. La entrada del usuario se almacena en la variable human_text. Si el usuario ingresa la palabra "bye", continue_dialogue se establece en falso y se imprime un mensaje de despedida para el usuario.

Por otro lado, si el texto de entrada no es igual a "bye", se comprueba si la entrada contiene palabras como "gracias", "gracias", etc. o no. Si se encuentran tales palabras, se genera una respuesta "Muy bienvenido". De lo contrario, si la entrada del usuario no es igual a Ninguno, se llama al método generate_response, que obtiene la respuesta del usuario en función de la similitud del coseno, como se explica en la última sección.

Una vez que se genera la respuesta, la entrada del usuario se elimina de la colección de oraciones, ya que no queremos que la entrada del usuario forme parte del corpus. El proceso continúa hasta que el usuario escribe "bye". Puede ver por qué este tipo de chatbot se llama chatbot basado en reglas. Hay muchas reglas a seguir y si queremos agregar más funcionalidades al chatbot, tendremos que agregar más reglas.

El resultado del script del chatbot se ve así:

Puede ver en la imagen de arriba que ingresé la entrada "roger federer" y la respuesta generada es:

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

La respuesta puede no ser precisa, sin embargo, todavía tiene sentido.

Es importante mencionar que la idea de este artículo no es desarrollar un chatbot perfecto sino explicar el principio de funcionamiento de los chatbots basados ​​en reglas.

Yendo más lejos: proyecto de extremo a extremo portátil

¿Tu naturaleza inquisitiva te hace querer ir más allá? Recomendamos consultar nuestro Proyecto guiado: ["Subtítulos de imágenes con CNN y Transformers con Keras"](https://wikihtp.com/courses/image-captioning-with-cnns-and -transformadores-con-keras/#cta){target="_blank"}.

En este proyecto guiado, aprenderá a crear un modelo de subtítulos de imágenes, que acepta una imagen como entrada y produce un subtítulo de texto como salida.

Aprenderás a:

  • Preprocesar texto
  • Vectorizar la entrada de texto fácilmente
  • Trabaje con la API tf.data y cree conjuntos de datos de alto rendimiento
  • Cree Transformers desde cero con TensorFlow/Keras y KerasNLP: la adición horizontal oficial a Keras para crear modelos NLP de última generación
  • Cree arquitecturas híbridas donde la salida de una red se codifica para otra

¿Cómo enmarcamos los subtítulos de las imágenes? La mayoría lo considera un ejemplo de aprendizaje profundo generativo, porque estamos enseñando a una red a generar descripciones. Sin embargo, me gusta verlo como una instancia de traducción automática neuronal: estamos traduciendo las características visuales de una imagen en palabras. A través de la traducción, estamos generando una nueva representación de esa imagen, en lugar de simplemente generar un nuevo significado. Verlo como traducción, y solo por generación de extensión, enfoca la tarea bajo una luz diferente y la hace un poco más intuitiva.

Enmarcar el problema como uno de traducción hace que sea más fácil averiguar qué arquitectura querremos usar. Los transformadores solo de codificador son excelentes para comprender el texto (análisis de opinión, clasificación, etc.) porque los codificadores codifican representaciones significativas. Los modelos de solo decodificador son excelentes para la generación (como GPT-3), ya que los decodificadores pueden inferir representaciones significativas en otra secuencia con el mismo significado. La traducción generalmente se realiza mediante una arquitectura de codificador-decodificador, donde los codificadores codifican una representación significativa de una oración (o imagen, en nuestro caso) y los decodificadores aprenden a convertir esta secuencia en otra representación significativa que es más interpretable para nosotros (como una oración).

Conclusión

Los chatbots son agentes conversacionales que participan en diferentes tipos de conversaciones con humanos. Los chatbots están encontrando su lugar en diferentes estratos de la vida, desde asistentes personales hasta sistemas de reserva de boletos y terapeutas fisiológicos. Tener un chatbot en lugar de humanos en realidad puede ser muy rentable. Sin embargo, desarrollar un chatbot con la misma eficiencia que los humanos puede ser muy complicado.

En este artículo, mostramos cómo desarrollar un chatbot simple basado en reglas utilizando la similitud del coseno. En el siguiente artículo, exploramos otro campo de procesamiento del lenguaje natural.