Técnicas para entrenar la Inteligencia Artificial de #GoogleAssistant y mejorar las conversaciones
Antes de comenzar con el desarrollo de RIC Escape, no sabía que se debían realizar determinadas tareas para hacer que un bot entendiera diálogos, y mejorara con el tiempo. Hoy sé que eso se llama “entrenar la Inteligencia Artificial”. Y lo haces para que ese bot conversacional entienda bien las frases y mejore. El objetivo de este post es compartir qué técnicas he implementado para ir mejorándola.
Durante estos últimos días he estado desarrollando RIC Escape, una aventura conversacional en formato escape room para Google Assistant (y otros bots).
Es curioso porque algo que quizás comenzó como un juego, o para aprender, me ha llevado a una serie de aprendizajes que no sabía que existían en este ámbito. Además, quizás por el Efecto Ikea del que hablaba hoy la #Bonilista, me estoy empatizando demasiado con el juego y sus jugadores, y me está llevando a “sufrir” mientras aprendo qué y cómo debo hacerlo mejor.
Por ejemplo, me entristece leer las conversaciones de personas que no están teniendo una buena experiencia de usuario en la conversación. Y es que las personas hablan de forma que jamás puedes prever. Aquí van una serie de consejos y funcionalidades que estoy implementando y entrenando para mejorar.
Antes de empezar, me gustaría agradecer a mi pequeña comunidad de betatesters del RIC Escape por todos estos aprendizajes creados a partir de sus consejos. ¡Gracias!
Y si no eres betatester, sabes que si quieres unirte, ¡estás a tiempo!
Si no has leído mi post anterior sobre cómo se desarrolla para Google Assistant con Dialogflow, aquí lo tienes.
El verdadero objetivo
¿Entrenar una IA? En mi caso no pensaba que iba a tener que hacerlo. RIC Escape es una aventura conversacional a la vieja usanza. Solo se pueden usar determinados verbos (mirar, ir, coger, usar, inventario), y las palabras que debes usar de alguna forma u otra el juego te lo indica: Si dices ‘ayuda’, se te indica los verbos que debes usar. Si dices ‘mirar habitación’, por ejemplo, te explica lo que hay alrededor, no tienes por qué inventarte más palabras de las que el juego te sugiere. ¿Verdad? Pues nada más lejos de la realidad.
Resulta que a las personas les gusta hablar como personas.
Somos exigentes. No queremos hablar como robots. “Ir a pasillo norte”, “mirar ordenador” acaban convirtiéndose en “vete al pasillo norte”, “descríbeme el ordenador”… Y si esas frases, que a priori parecen sencillas, no responden a las expectativas, el usuario acaba frustrándose. Y su frustración hace que, instintivamente, acabe valorando el juego como una mala experiencia de usuario, o te señale con una estrella en el market. Algo que quieres evitar.
Por lo tanto, lo que he aprendido es que lo que tengo que hacer a toda costa es evitar la frustración del usuario. Como sea.
¿Quizás violo las reglas del juego? Puede ser. ¿Quizás es culpa del usuario que no sabe que debe decir ‘mirar’ en vez de ‘descríbeme’? Puede ser. Pero yo quiero que el usuario se lo pase bien, no que aprenda los límites de mi lenguaje. Tengo que hacer que el robot entienda la mayoría de las frases del usuario, de cualquier forma, para evitar que el usuario tenga que repetirlas una y otra vez de mil modos diferentes.
¿No te saca de tus casillas cuando dices algo a tu pareja, y ésta te responde con un “Perdona, no te he entendido, puedes repetir”? Pues imagínate que un juego te hace eso una de cada tres frases. Acabas mandando el juego a la mierda. Eso es lo que hay que evitar a toda costa.
¿Cómo se entrena la IA?
Entrenar la Inteligencia Artificial es fácil. En mi caso, lo hago en dos sitios:
- En Dialogflow, en la parte de Training: Allí puedo ver todas las frases, y el sistema ya me propone unas conversaciones para revisar. No sé muy bien cómo funciona el mecanismo de sugerencia, supongo que porque cree que son las más aptas para que sean estudiadas en base al ratio de frases que no ha entendido. Allí se puede ver lo que el usuario dice y lo que la máquina responde. Es muy fácil interpretar qué esperaba recibir el usuario y qué se le dio realmente, y entender así su nivel de frustración. Desde esa misma página se puede reasignar la frase al verbo / intent correcto. En mi caso me enfoco en que recupere correctamente el verbo y el argumento o argumentos enviados.
- En código: también desde código se pueden realizar determinadas acciones. Introducir palabras a ignorar, gestión correcta de los números, etc. Con ello voy generando un backlog de funcionalidades que hacen a la IA más completa. En este artículo doy buena cuenta de ellos.
Por qué me di cuenta que había que entrenar la IA
El primer día que lancé la app en inglés, fue una decepción total. Hay que imaginarse que el usuario se descarga el juego sin ningún contexto. Mientras que en España, los primeros usuarios son conocidos y amigos, que se han ido apuntando a la fase betatesting, porque entiendo que les caigo “majo”, en Estados Unidos y Reino Unido publiqué el juego directamente “a ver qué pasa”. ¿Resultado? 7 valoraciones de una estrella en un día. :( Frente a las 2 que tenía con un cinco (de los amigos del betatesting, claro).
Además, uno de los puntos negativos de Google Assistant a día de hoy (como el Play Store en sus comienzos) es que no se puede contactar con las personas detrás de los votos. No tienes forma de saber quién y por qué te ha valorado así. Solo puedes intuirlo en base a las conversaciones que ves grabadas en tu log. Google, por favor, trabaja en eso…
¿Iba a poder remontarlo? No lo tenía nada claro. Tres días después sigo sin tenerlo muy claro, pero al menos he aprendido y ejecutado algunas cosas, que están dando sus frutos.
Mejorando la experiencia de usuario en la descarga
Esas valoraciones vienen derivadas de la frustración. En el momento que un usuario dice al asistente “vete a la mierda”, no le cuesta nada buscar la app y ponerle un punto negativo. Es importantísimo gestionar bien las expectativas desde el primer momento: hacerle ver que la app tiene sus limitaciones, que ha sido hecho por humanos, explicarle para qué sirve, qué puede hacer y qué no, etc.
Cosas que implementé en ese mismo instante:
- Mejoré la ficha de la app de Google Assistant para explicar que es un juego. Que tiene sus limitaciones, y que está hecho para divertirse. y que no dude en ponerse en contacto conmigo si no lo hace.
- Intenté que el robot RIC cayera mejor. Es tu asistente, tu interlocutor, y por tanto, tiene que caer bien. Así que al principio incluí algo así como “Hola, soy RIC. ¡Espero que nos lo pasemos bien juntos! Dime, ¿Qué quieres hacer?”. Con la clara intención de decirle al usuario “hey, aquí estamos para pasarlo bien, no te mosquees”.
- Tras una breve introducción de dos frases, le explico al usuario lo que puede hacer. Directamente. Sin que lo pida.
- En todas las frases de “fallback” (es decir, cuando no se puede interpretar lo que el usuario dice), explico ahora lo que se puede hacer. “No te he entendido. Me puedes lanzar las siguientes órdenes: etc”.
- Al finalizar el usuario, intenté recuperar al usuario “perdido”, al menos para dar pena, o pedirle que si no le ha gustado el juego, me diga por qué. Así que cuando te desconectas, ahora RIC te dice: I really hope you enjoyed this game. If you did not, please do contact heedrox@gmail.com to let him know what you did not, pretty please. He did this so you really had fun. He would love to know from you and get some feedback, really.
El resultado de esta última acción me dejo alucinado el primer día, pues recibí un mail a las pocas horas de colocar la frase (aunque en dos días más, aún no he recibido otro).
Manteniendo varios idiomas
Dado que las apps de Google Assistant para español no están todavía disponible (por eso os tengo que añadir manualmente como betatesters), traduje la app al inglés inmediatamente, sin saber las implicaciones de mantenimiento que ello iba a requerir.
Las implicaciones son claras: mantenerlo cuesta el doble. Todo lo que tienes que hacer después diariamente, lo tienes que hacer por cada uno de los idiomas en los que tu app está presente. Entrar al dialogflow, ver conversaciones y corregir los verbos, añadir sinónimos a palabras, añadir casos nuevos a gestionar por el juego, etc.
Debido a que el juego es por puro placer personal, es una decisión de la que no me arrepiento; pero si esto fuera una aplicación profesional, habría pasado antes por una fase de betatesting específica por cada uno de los idiomas. Hasta no tener bien pulido un idioma, no habría pasado al siguiente.
Capturar las frases no reconocidas más habituales
Con Dialogflow puedo ver las conversaciones y de allí, extraer conceptos a aplicar, y así estoy elaborando mi backlog. Cada vez que veo que un mismo concepto se aplica a diferentes conversaciones, lo anoto en mi backlog para darle más prioridad.
Es una lástima que Dialogflow no venga con esta habilidad “out-of-the-box”.
Aunque a día de hoy no lo he implementado, creo que hay dos maneras de realizarlo:
- Conectarme a una base de datos firebase, capturar ese fallback, y registrarlo en la base de datos y explotarlo. Aunque a día de hoy es una opción que quiero descartar para no generar más delay al procesado de la consulta, pues estoy evitando toda conexión a una base de datos externa.
- Generar un registro en el log y explotarlo a través de dichos logs. Aunque el visor de firebase de logs no creo que tenga esta habilidad, he visto que Google Cloud Platform tiene detrás algo que se llama Stackdriver. Aunque no he trasteado con él todavía, me huele a que me permitiría hacer algo similar.
Mejorar las órdenes / verbos
Definitivamente no vale un escueto diccionario de verbos. En una aventura conversacional como RIC Escape pensaba que sí, pero aquí van un listado de frases reales que me estoy encontrando. Y eso que el robot especifica claramente que los verbos a utilizar son “mirar”, “ir”, “coger”, “usar” e “inventario”.
- Descríbeme la habitación
- Mírame qué es eso
- Mirar lo que hay en el suelo
- Lee el libro
- Abrir el armario
- Cógelo
- Que lo cojas
- Reprogramar el ordenador
- Cambiar las coordenadas
- Comer la comida
- Qué puedes hacer
- Que me digas qué es lo que puedes hacer
Es curioso además que estas frases no se han realizado en los primeras minutos del usuario, que podría entenderse. Sino cuando el usuario ya ha aprendido la mecánica. Y es que nuestro cerebro, sin querer, tiende a hablar de forma natural con el asistente, aunque ya sepamos que los verbos a usar son unos determinados. Es decir, el usuario tiene que esforzarse en recordar continuamente los verbos válidos. Nosotros, como desarrolladores, si queremos evitar la frustración (recordemos que ése es el objetivo) tenemos el deber de facilitarle esa tarea.
Esta tarea se hace principalmente revisando las frases y asignando a los intents correctos. Por ejemplo “comer la comida” es lo mismo que “usar comida”. “Abrir el armario” es lo mismo que “Usar armario”, “Describeme la habitación” es lo mismo que “mirar”. Etc.
Además, hay otro tipo de patrones que me estoy encontrando y vienen inherentes a la plataforma Google Assistant a la hora de transcribir las frases. Por ejemplo:
- “Luke” en vez de “look”.
- “Inscribe the item“ en vez de “Describe the item”.
- “Rick” en vez de “RIC”.
Aunque puede parecer violar las leyes del propio lenguaje (¡ha dicho “inscribe” en vez de “describe”, que lo pronuncie mejor!), en el fondo, si buscamos reducir la frustración, nos podemos imaginar que con un 99.99% de certeza el usuario buscaba decir “describe”. Por lo que debemos reasignar el verbo “inscribe” como “describe”.
Por lo tanto, no nos tiene que temblar el pulso asignar “Luke” al verbo “Look”, y frases “que suenan parecido” a objetos que, dentro del contexto, deberían haber sido el target adecuado.
Y, desde mi experiencia, es mejor “pasarse de sobra” en este caso. Por ejemplo, quizás el usuario quiso decir “open the shell”, y yo he asignado “shell” a “shelf” porque he visto que en otros casos, la intención sí era “open the shelf”. Inmediatamente el usuario va a recibir un feedback positivo (“yes, the shelf opened”) cuando no se lo esperaba. Mejor eso a lo contrario.
Sinónimos
La necesidad de incorporar sinónimos es evidente. Todo aquello que pueda ser usado como argumento (habitación, item, etc) debe tener sinónimos. En mi caso, gestiono los sinónimos vía código, aunque podría usar las entities de Dialogflow. Pero me siento más cómodo teniendo un repositorio versionado de éstos.
Importante, sobre todo en español: la comparación de textos debe hacerse ignorando las tildes en palabras (puede parecer evidente, pero por si acaso).
Stopwords
Llamo “stopwords” a las palabras que no deben utilizarse a la hora de comparar sinónimos. Tengo una lista en inglés y en español (las reutilizo). Por ejemplo “ventana al exterior” y “ventana hacia el exterior” deben coincidir. Para ello, ignoro palabras como “hacia”, “el”, “a”, “en”, etc.
Plurales
Un caso a gestionar que también puede parecer evidente (pero que de hecho no desarrollé en la primera versión) es la comparación de palabras ignorando los plurales. Es decir, “ventanas hacia el exterior” debe coincidir con “ventana al exterior”.
Para ello, utilicé una librería pluralize en nodejs que fue de gran utilidad y simplificó enormemente este desarrollo. Aunque solo está pensada para inglés, me está dando unos resultados más que satisfactorios para español. De hecho, no encontré librería similar en español y me planteé hacer una adhoc, pero con los resultados actuales, no creo que sea necesario.
Intents con contexto
Actualmente hago poco uso de los contextos en Google Assistant. Los contextos en teoría me permiten saber qué verbo e información ha utilizado anteriormente, y por tanto, añadir esa información al procesado del intent actual.
Un ejemplo muy útil para el cual quiero empezar a plantearlo es para conversaciones de este tipo:
- Usuario: Mirar suelo
- RIC: Veo una cartera
- Usuario: Cógela
- El RIC de hoy: No sé qué quieres decir…
Otro ejemplo:
- Usuario: Mirar diario
- RIC: Es tu diario.
- Usuario: Léelo.
- El RIC de hoy: No sé qué quieres decir…
Puede parecer poco útil, pero veo que muchos intents que han caído al fallback vienen derivado de esto. No costaría mucho recordar el último objeto sobre el que se actuado, y asociar el siguiente intent sobre el anterior argumento.
Capturar lo que no sea explícito para dar una pista a cambio
Ahora mismo, RIC no entiende “Abrir armario”. Efectivamente, puedo asignar el verbo “Abrir XXX” con el intent “Usar”, y así igualar “Abrir armario” a “Usar armario”.
Sin embargo, ese armario en el juego debe abrirse con una llave.
Por lo tanto, esta conversación podría mejorarse:
- Usuario: Abrir armario.
- RIC: No puedo usar el item “armario”.
Para hacerlo, puedo capturar este tipo de acciones y devolver una pista.
- Usuario: Abrir armario.
- RIC: Para abrir el armario necesito usar una llave con el armario.
Quizás este ejemplo puede parecer evidente, pero ¿y este otro?
- Usuario: cambiar rumbo de la nave
- RIC: No sé qué quieres hacer
Cambiar el rumbo de la nave se hace a través del ordenador. Quizás RIC podría saber eso. Por lo que la conversación sería menos frustrante si fuera:
- Usuario: cambiar rumbo de la nave
- RIC: Para cambiar el rumbo de la nave debo usar el ordenador (o incluso que el propio RIC usara el ordenador directamente a través de ese mensaje).
Gestión de los números
Los números son otro de esos problemas que no anticipé. En el juego existe, por ejemplo, un código. Aunque no es el verdadero, digamos que es “código 9876”.
He visto referirse a ese código de mil maneras diferentes:
- Código nueve mil ochocientos setenta y seis
- Código 9.876
- Código 98 76
- Código 9:876
- Código nueve mil ochocientos setenta y 6
Es por tanto que he implementado (y estoy implementando) varias mecánicas:
- Introducción de sinónimos para los números
- Borrado de caracteres extraños entre números a través de expresiones regulares.
- Agrupado de números 98 76 = 9876.
Conclusiones
Como se puede ver, entrenar la IA implica que entienda mejor los verbos, que asocie expresiones naturales a dichos verbos y que las palabras sean más reconocibles.
Para ello, estas son las técnicas que he visto necesarias a día de hoy, y eso que solo han pasado tres días.
Probablemente surjan muchas más posteriormente. Si eso es así, prometo hacer un update de este post.
Y recordad que RIC Escape es una aventura conversacional en formato escape room de 35 minutos creada con todo este mimo para que lo pases bien. Si quieres jugar con ella a través de Google Assistant en español, avísame y te hago betatester.
¡Un saludo!