La creación de un chatbot de Juego de Tronos para Slack: Parte 1 Entender el lenguaje

Source: Deep Learning on Medium


Lecciones aprendidas aplicando el aprendizaje profundo a la comprensión del lenguaje natural e incorporando preguntas y respuestas.

El verano pasado decidí probar mis habilidades del PLN y emprender la construcción de un chatbot. Como gran fan de Juego de Tronos, decidí crear un chatbot para Juego de Tronos. Inicialmente, mi objetivo era proporcionar una manera de obtener fácilmente varios tipos de noticias GOT de lugares como Reddit, Watchers on the Wall, los Siete Rinos y Twitter. Sin embargo, rápidamente decidí expandirme a otras tareas y me intrigó particularmente cómo integrar los enfoques modernos de PLN para enriquecer mi chatbot. Este primer artículo cubrirá los componentes de comprensión del lenguaje de aprendizaje natural y preguntas; y el segundo artículo hablará más sobre la plataforma y la arquitectura.

Si deseas utilizar el chatbot, puedes unirte a la comunidad de Citadel en Slack (aunque, como explicaré, aún no he agregado todas las funciones descritas en la versión del producto real. Ademas el bot sólo entiende inglés). En el futuro planeo agregar soporte para instalarlo en tu espacio de trabajo. Ten en cuenta también que algunos de los ejemplos de este artículo y el contenido del propio bot contienen información hasta la temporada 7.

Entendimiento del lenguaje natural (NLU)

Aprendizaje profundo para NLU: una introducción

Dónde y cómo integrar el aprendizaje profundo en un chatbot es en realidad una pregunta un tanto difícil. En el nivel básico del mundo chatbot puedes usar métodos basados en reglas, aprendizaje automático o alguna combinación de ambos. Con los métodos basados en reglas, por lo general, tienes una garantía de que el chatbot responderá correctamente a las consultas de los usuarios, siempre que los usuarios escriban de forma específica y limitada. Con el aprendizaje automático (o incluso los métodos estadísticas de LPN) puedes romper las fórmulas rígidas y permitir a los usuarios escribir de forma más natural. Sin embargo, al hacer esto, también introduces incertidumbre (incluso con los mejores modelos). Además, incluso los modelos SOTA generalmente solo funcionan para limitados tipos de diálogos. Por ejemplo, un modelo entrenado para el diálogo orientado a objetivos generalmente se descompone si el usuario comienza a participar en chit-chat. Por supuesto, el aprendizaje profundo también requiere datos y en esta situación a menudo tenemos un problema al comenzar. Por lo tanto, a menudo necesitas escribir datos de muestra de lo que crees preguntarán. Este proceso requiere mucho tiempo y, a menudo, es inexacto.

El sistema formulado

Antes de ver por qué podemos necesitar modelos basados en aprendizaje automático, veamos algunas de las limitaciones de usar métodos basados en reglas. Con la fórmula para el chatbot podríamos escribir en el código algo similar a esto:

Ten en cuenta que esto se simplifica a propósito. En mi bot real para mis métodos basados en reglas, por lo general utilizo un dictado para asignar palabras a acciones para evitar largas afirmaciones como estas. También obviamente tendrías que manejar las operaciones de DB, etc.
Ejemplos de búsquedas de los usuarios y respuestas con la sistema de las fórmulas

Ahora los usuarios tienen que escribir de una manera muy específica para usar el chatbot. Tienen que escribir de forma exacta Frase Jon Snow o Newa Reddit. Esto está bien si solo quieres incluir consultas simples. Pero ¿qué pasa si queremos apoyar la funcionalidad de frases como Quote about Jon Snow o incluso Quote from Jon Snow when he is talking about Stannis? ¿Si? Podríamos obligar a los usuarios a hacer las cosas de una manera formulada, pero eso se vuelve rápidamente complicado y engorroso para los usuarios. Pasa algo similar con las noticias, el soporte de consultas complejas como Get news from the past 24 Hours on Reddit o incluso News about Jon Snow in Season 8 se vuelve complicado en el mejor de los casos e imposible en el peor.

Slot Filling y detección de la intención

Esto nos lleva al machine learning para rellenar slot y la detección de intentos. Un área clave para usar el Deep learning en los chatbots es tomar automáticamente la cadena ingresada por un usuario y asignar los tokens relevantes a las ranuras para una API (esto se conoce como slot filling o SLU). Un área relacionada es la detección de la intención que se enfoca en mapear la expresión hacia una intención. Aquí hay un ejemplo de un SLU anotado:

Frase sobre Jon Snow
0 0 B-focus-character I-focus-character
Intent: Quote 0
Frase de Jon Nieve estoy hablando de Stannis
0 0 B-speaker I-speaker 0 0 0 I-focus-character
Intent: Quote 0

El slot filling en cierto sentido es una versión más detallada del reconocimiento de entidades nombradas (NER). Por ejemplo, en una configuración NER pura, Jon Snow puede que siempre tenga la etiqueta “carácter” mientras que para rellenar la slot, la etiqueta cambiará según la slot que deba ocupar. El formato de la anotación se llama IOB, esto significa inicio-exterior-principio. Está destinado a mostrar “trozos” de los tokens juntos.

Como la respuesta del bot dependerá tanto de las ranuras como del objetivo del usuario, muchos documentos se centran en el llenado de ranuras conjuntas y la detección del intent. Además muchas bibliotecas NLU, como el marco Rasa-NLU, proporcionan una detección conjunta de intenciones de SLU.

Una vez que tengamos las slots llenas, todavía necesitaremos construir la consulta real. La construcción de la consulta dependerá de cómo esté configurada tu base de datos. Como tal, en la mayoría de los casos este código lo escribes manualmente tú mismo. Sin embargo, hay algunos modelos que aprenden una asignación directa de la expresión a una consulta SQL. La gran mayoría de las veces, sin embargo, tendrás una API existente o querrás construir una. Así que veamos cómo podrías convertir esto en una simple solicitud de API:

def process_user_text(user_text, model):
# Let's assume model.predict() returns
# intent:str ents:dict (e.g. {"focus_character":"Jon Snow"})
intent, ents = model.predict(user_text)
# Assume this function combines multi-token ents and
# normalizes them to how they appear in the database
ents = combine_normalize_ents(ents)
url = "https://random_site.com/" + intent
requests.post(url, data=ents)
Ten en cuenta que aunque este código se parece al código de la API de GOT-Bot, no lo he probado personalmente. Planeo hacerlo en los próximos días. Pero si te encuentras con errores en el ínterim, házmelo saber.

Ahora podíamos usar esta sencilla API de Flask para manejar estas solicitudes.

Volviendo a nuestro ejemplo de noticias anteriores, etiquetaríamos los datos en el siguiente formato para trabajar con la API:

Noticias sobre Jon Snow en temporada 8
0 0 B-character I-character 0 B-season I-season
Intent: News 1

Como puedes ver, este formato nos permite construir llamadas a la API y consultas SQL mucho más fáciles. Ahora podríamos definir una función (asumiendo que ya habíamos ejecutado un NER y etiquetado las noticias).

Escenarios de datos limitados

El problema de este enfoque, y por supuesto el Deep learning en general, es la necesidad de grandes cantidades de datos de entrenamiento etiquetados. Un enfoque que estoy investigando actualmente es el uso del meta-aprendizaje en muchos conjuntos de datos de diálogos anotados para permitir que el modelo se adapte rápidamente a unos pocos ejemplos.

La alineación de la slot es otro enfoque interesante (aunque algo limitado). En Hacia el análisis semántico de Zero-Shot Frame para la escala de dominios, un artículo de 2017 de los investigadores de Google, se describe el uso de los nombres de las slot y/ o la documentación de las ranuras en la API para realizar un llenado efectivo de cero tomas. La idea es que si el modelo ya estaba capacitado para reservar una aerolínea, entonces también debería poder reservar un autobús, ya que las slots deberían superponerse (es decir, ambas tendrían un start_city, destination_city). Llevando esta idea un paso más allá, un sistema de diálogo basado en un restaurante puede tener un restaurant_city (es decir, reservarme un restaurante en Chicago) y un hotel hotel_city. Al explotar semánticas similares entre frases, un modelo podría aprender a llenar restaurant_city efectivamente, a pesar de que solo fue entrenado en los datos de reserva de la aerolínea. Por supuesto, este enfoque también tiene limitaciones: (1) no puede funcionar en dominios drásticamente diferentes con poca o ninguna superposición; (2) en algunos casos, en realidad podría haber una transferencia negativa (por ejemplo, se comportó pero en la reserva de taxis; se confundió drop_off y pickup_spot porque dependen del contexto; aunque podrían alinearse start_city y dependen del contexto; aunque podrían alinearse start_city y destination_city su representación no es similar). Para mí caso de uso, este enfoque probablemente no funcionará, ya que hay pocas slots semánticas superpuestas entre los grandes conjuntos de datos públicos de llenado de ranura y mi chatbot GOT.

Más allá del llenado de ranuras conjuntas y modelos de detección de intenciones

Pero incluso los modelos de NLU conjuntos tienen sus limitaciones, ya que no utilizan el contexto. Por ejemplo, supongamos que el usuario declaró Frase Robert Baratheon y luego dijo Una otra frase de él. En este escenario, uno de los modelos de NLU descritos anteriormente no sabrá qué hacer, ya que no utiliza el historial de conversaciones. De manera similar, un usuario podría hacer la que Who is Jon Snow’s mother? Y el bot (con suerte) devolvería Lyanna Stark si entonces el usuario le preguntara When did she run off with Rhaegar? Probablemente ni si quiera le lanzaría a la ranura her. También puede haber ocasiones en las que necesitemos actualizar o solicitar información adicional sobre ciertas ranuras. Por ejemplo, si el usuario solicitó News from the past 24 hours about Season 8? la API requeriría que se especificara la fuente de esas noticias, ¿el bot podría responder From what source? O de lo contrario, si el usuario indicó que Get the scene from episode 2?, el bot podría responder from what season?

Los modelos de diálogo de end-to-end deben poder manejar estas tareas. Un desafío que se creó para medir el progreso en esta tarea fue el Seguimiento del estado del diálogo. En particular, DSTC2, la segunda versión del desafío, midió qué tan bien los modelos podrían emitir y actualizar las llamadas a la API y solicitar información adicional al usuario cuando sea necesario. Uno de los primeros modelos en salir adelante en este desafío fue la adopción de las Redes de Memoria para el diálogo orientado a objetivos. Esto fue realizado por investigadores de Facebook en el diálogo de aprendizaje de extremo a extremo orientado a objetivos. Demostraron que las Redes de Memoria superaron a otros métodos de aprendizaje automático por un amplio margen.

Más recientemente, ha habido artículos como Mem2Seq que incorporan activamente el historial de diálogos con la base de conocimientos y los utilizan en la generación de respuestas. Específicamente, Mem2Seq tiene dos partes, un codificador de memoria que codifica el historial de diálogos y un decodificador que usa el diálogo codificado / KB para generar una respuesta del usuario. Mem2Seq logró los resultados SOTA en el desafío DSTC2, BABI y el conjunto de datos Standford en el automóvil.

La architectura del Mem2Seq.

Para entrenar realmente Mem2Seq para GOT-Bot se requieren tres cosas: una base de conocimientos, intentos anotados e historiales de diálogo anotados en la slot. Esto hace que sea más difícil adaptarse a GOT-Bot ya que la KN debe convertirse en tres como (persona, persona2, relación).

Respondiendo a la pregunta

La línea entre dónde comienza la respuesta a la pregunta y dónde termina el llenado de espacios es a menuda bastante borroso. En términos de investigación generalmente vemos que el control de calidad se refiere a la respuesta de una pregunta basada en datos textuales no estructurados. (También puede basarse en una base de conocimientos estructurada, pero en este caso es particularmente confuso dónde termina exactamente el llenado de ranuras y comienza el control de calidad). En el primero, esto generalmente significa buscar y extraer la respuesta a partir de datos textuales en lugar de averiguar qué slots se debe llenar para consultar una base de datos. En el contexto del bot de Game of Thrones, significa tomar una pregunta del usuario, buscar los índices adecuados en ElasticSearch y luego extraer la respuesta correcta de los resultados devueltos. Antes de entrar en cómo vamos a ver exactamente los diferentes tipos de preguntas que un usuario puede hacer:

En esencia, hay tres categorías de preguntas:

  1. Preguntas que pueden responderse consultando el gráfico de conocimiento

A quiénes ha matado Sandor Clegane?

Quién es el padre de Jon Nieve?

Qué es el lema de casa Glover?

A quién fue casado Margeary?

En que región se encuentra Harrenhal?

Todas estas preguntas tienen respuestas conocidas que se pueden encontrar en un gráfico de conocimiento estructurado. El problema es que tenemos que convertir la consulta del usuario en SQL o una solicitud de API. Esto es similar a lo que necesitamos hacer con el llenado de espacios. En muchos casos, podemos convertir esto en un problema de llenado de slots a través de preguntas de frases como otra interacción. Por ejemplo:

A quienes ha matado Sandor Hound 
0 0 0 I-attribute B-focus-character I-focus_character
Intent kb_question

O en el caso de la siguiente pregunta:

What region is Harrenhall in?
0 I-location-region 0 I-focus_castle 0
Intent región

Entonces podríamos construir una solicitud de API de una manera similar. Sin embargo, hay una gran cantidad de conjuntos de datos con preguntas para SQL, por lo que en este caso podría tener sentido usar uno de esos conjuntos de datos.

2. Las preguntas no se encuentran en el gráfico de conocimiento pero aún tienen respuestas conocidas y se pueden extraer de las páginas de MediaWiki u otros sitios de GOT.

Cómo comenzó la guerra de los cincos reyes?

Qué pasó durante el torneo de Harrenhal?

Qué fue la guerra del los cinco reyes?

Cómo terminó la rebelión de Robert ?

A quien ayudó los Lannisters obtener la alianza con los Tyrells?

Los conjuntos de datos/ modelos más relevantes para esta tarea son conjuntos de datos como MS MARCO y TriviaQA. Aunque muchos investigadores evalúan en SQUAD, en realidad casi nunca se le dará el párrafo de contexto exacto. Esto hace que los modelos que funcionan bien en MS MARCO sean ideales, ya que reciben una lista completa de resultados clasificados y tienen que extraer la respuesta correcta.

El conjunto de datos de QuAC o la pregunta y la respuesta en contexto son similares a los modelos de diálogo “de extremo a extremo” mencionados anteriormente para preguntas y respuestas. Contiene preguntas y preguntas de seguimiento que involucran múltiples giros de diálogos. Los modelos como FlowQA pueden funcionar bien en esta tarea de control de calidad conversacional, ya que agregan el historial de diálogos al modelo base.

3. Preguntas donde la respuesta es subjetiva o especulativa y que requieren encontrar preguntas similares o alternativas a realizar inferencias de múltiples saltos.

Por que Sansa confia Joffery?

Quienes sobrevivir temporara 8 and por qué?

Sí Robb Stark no hubiera roto su compromiso que los Freys habrían traicionado?

Quién matara a Cersei?

Es Jon el principe que fue prometido?

Estas preguntas no tienen respuestas definitivas y requieren análisis o especulación. Por lo tanto, la mejor solución es encontrar preguntas similares que ya hayan sido respondidas. Esto se puede hacer a través del índice de Quora. Sin embargo, aquí no utilizaremos un modelo de control de calidad sino un modelo de similitud de preguntas. La similitud de la pregunta se puede hacer usando una variedad de métodos. Mi modelo actual en producción utiliza un ElasticSearch básico y luego vuelve a clasificar los resultados utilizando el codificador de oraciones universales. + Similitud del coseno. Con el fin de recopilar más datos para mejorar la clasificación, actualmente el bot muestra al usuario todos los 10 primeros resultados. Entonces podemos volver a entrenar el modelo basado en las elecciones del usuario. Sin embargo, hay varios problemas con este enfoque. Primero, en muchos casos, el ElasticSearch inicial a menudo no devuelve buenas preguntas. En segundo lugar, los usuarios pueden devolver otra respuesta interesante que no responde directamente a su pregunta. Aun así, esta “supervisión débil” significa que uno puede anotar manualmente los ejemplos mucho más rápido.

Ejemplo de preguntas y respuestas. En el primer panel (desde la izquierda), la respuesta correcta se devuelve como (1) cuando debería devolverse como 0 (es probable que haya algún tipo de error, ya que esa pregunta parece estar fuera de lugar). En 2/3, ElasticSearch no siquiera encuentra las respuestas correctas, pero los resultados están relacionados.

Creando un buen proceso de onboarding

Crear un buen proceso de onboarding también es esencial para conseguir usuarios. Tu bot debe causar una impresión positiva inmediatamente o, de lo contrario, la gente seguirá no continuará. Por este motivo, para poder realizar un buen proceso de incorporación decidí escribir una conversación basada en reglas. El bot se presenta primero con un mensaje directo de bienvenida a la Ciudadela. A lo largo del proceso de incorporación, se realiza un seguimiento del estado del usuario en Redin. Al final de cada respuesta, el estado del usuario se actualiza. Aquí decidí usar diccionarios simples para asignar el estado del usuario a las acciones con el fin de evitar sentencias SI… largas.

El proceso de incorporación apunta a que los usuarios se familiaricen con las características básicas del bot de una manera divertida y amigable.

Mensaje del Maester boy a nuevos usuarios

Un problema con las reglas definidas manualmente es que si el usuario dice algo inesperado o incluso ligeramente diferente de lo que has codificado, el bot fallará. Descubrí esto de la manera más difícil cuando accidentalmente dejé que un error pasara por mis pruebas de unidad y mis pruebas manuales. Esperaba que los usuarios respondieran yes a la pregunta Would you like me to show you around the Citadel. Sin embargo, los usuarios a menudo respondían con cosas como yes thanks o yes please, un error muy simple que no detecté. Es por eso que recomiendo tener una variedad de personas que realicen una prueba beta de su chatbot porque puede que te pierdas algunas cosas sin percatarte de ello.

Generación de respuesta

No he hablado demasiado sobre la generación de respuesta real en este artículo. En su mayor parte, esto se hace mediante la recombinación de las respuestas de los elementos descritos anteriormente con frases básicas. Por supuesto, existen muchos métodos más sofisticados para generar respuestas que son únicas y cambian con el tiempo. Sin embargo, en este momento todavía estoy usando frases simples para combinar los resultados de las llamadas NLU desde la API.

¿Qué pasa con el chat y las interacciones no orientadas a objetivos?

Este es un área que no he investigado demasiado, pero espero poder sumergirme en las ediciones posteriores. Esencialmente, esto es cuando el usuario no quiere realizar una tarea específica, solo quiere conversar sobre los elementos de Juego de Tronos en general y escuchar una respuesta ingeniosa/interesante del bot.

El estado actual del bot y futuras mejoras

Actualmente, el chatbot todavía está en el fase de pruebas. No he podido anotar suficientes datos de entrenamiento o incorporar el aprendizaje meta / no supervisado con la suficiente eficacia para que el llenado de ranuras funcione de manera constante. Sin embargo, mis modelos entrenados están mejorando y espero lanzar pronto una actualización que los incorpore. También estoy estudiando el entrenamiento de Mem2Seq para manejar todo el proceso de diálogo a través del meta-aprendizaje, sin embargo, esto está en un futuro más lejano.

En términos de preguntas y respuestas, la búsqueda del índice Quora sigue siendo muy diferente y no hay soporte para consultar la base de conocimientos. Espero mejorar la clasificación de la pregunta de control de calidad del índice. Quora con el BERT Reranker que se formó previamente en MS MARCO. Espero volver a escribir el sistema de noticias para que puedas pedir cosas como “las últimas novedades de la temporada 8” o “los nuevos memes de Jon Snow de Reddit”.Finalmente, estoy agregando algunos flujos de diálogo basados en reglas para secuencias de chat más realistas. En la segunda parte de esta serie, abordaré aspectos más prácticos del chatbot, como las plataformas y las herramientas utilizadas.

Versión original en inglés