acf domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/planetac/desa.planetachatbot.com/wp-includes/functions.php on line 6170all-in-one-seo-pack domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/planetac/desa.planetachatbot.com/wp-includes/functions.php on line 6170wp-user-avatar domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/planetac/desa.planetachatbot.com/wp-includes/functions.php on line 6170The post K-Means text clustering en Python first appeared on Planeta Chatbot.
]]>Es un algoritmo no supervisado de tipo geométrico traducido como “K medias” donde buscamos encontrar un número K (previamente definido) de agrupaciones generadas en base a similitudes entre los atributos de las observaciones del dataset. Para medir esta similitud se utiliza la clásica distancia euclídea:


El proceso de generación de estos grupos o clústers consiste en inventar K observaciones (centroides) hipotéticas e ir ajustándolas hasta que sean las más representativas, es decir, aquellas que contengan los atributos más característicos de K agrupaciones. Las observaciones de un clúster deberán ser lo más homogéneas posibles entre sí y lo más heterogéneas posible con respecto a las de los otros clústers.
Por tanto, si consideramos que la similitud entre estos centroides y el resto de observaciones es proporcional a la distancia que los separa, entonces buscamos minimizar las distancias de las observaciones con respecto a sus centroides asociados. El proceso de deducción de grupos es el siguiente:
Estos pasos se repiten hasta que se cumple un determinado criterio: iteraciones máximas o cambio mínimo entre las dos últimas iteraciones.
Nota: Aunque el conjunto de datos que he utilizado en este artículo es de tipo texto, el código que expondré en apartados posteriores es extrapolable a otros datasets de tipo estructurado, a excepción de la carga de datos y vectorización que adjunto a continuación (transformación de datos no estructurados en vectores interpretables por el algoritmo). Para mayor información sobre esta transformación puedes echar un ojo a este artículo.
Para saber el número K ideal de clases en los que deberíamos agrupar los datos sin utilizar el “ground truth”, es decir, sin conocer las clases asigandas a cada observación, deberemos basarnos en la cohesión intra-clúster (distancias entre las observaciones de un mismo cluster) o la separación inter-clúster (distancias entre las observaciones de un cluster con respecto a las observaciones del resto de clústers):
Coeficiente de Silhouette (Silhouette score): Su resultado está limitado entre -1 y 1:
La siguiente fórmula calcula el coeficiente de Silhouette para una sola observación del conjunto de datos:

Este proceso se repite para todas las observaciones del conjunto de datos.
Para dar con la K óptima entrenaremos K veces el modelo, calculando la puntuación de Silhouette para cada diferente valor de K.

Los valores óptimos de K vienen dados por la curvatura de mayor convergencia de la función, marcada por el círculo amarillo; el número ideal de K ronda las 15–20 agrupaciones.
La idea es que los primeros grupos generados agregarán mucha información (varianza), pero una vez que el número de grupos excede el número real u óptimo de grupos la información agregada cae bruscamente, ya que solo está subdividiendo los grupos reales. Este hecho se manifiesta en la representación gráfica en forma de “codo” o cambio más brusco de la pendiente.
Método de error de inercia (Inertia error method): Cuanto más variedad haya entre las observaciones del dataset mayor serán sus distancias respecto a sus centroides asociados. La inercia o intertia, en el contexto del K-Means, es la suma de todas las distancias de las observaciones de un clúster a su centroide. Suponiendo que el objetivo es reducir la suma de distancias (cuadrados) de puntos con sus respectivos centroides, cuanto menor sea esta suma total, mejor, ya que indicará una mayor homogeneidad en las observaciones pertenecientes a los clústers creados.


Vemos, de nuevo, que el “codo” (elbow) de la curva parece ubicarse en K ∈ [10–20]. En este caso es más complicado de observar ya que la pendiente es mucho más suave. Aunque dicha distancia parece reducirse indefinidamente, no hace falta decir que acabará pecando de overfitting, ajustándose en exceso a los atributos de las muestras y perdiendo la capacidad de generalizar adecuadamente la pertenencia de una muestra a un determinado grupo, por tanto, buscamos un punto de equilibrio.
Para evaluar el desempeño también podemos recurrir al “ground truth”, es decir, a las clases asignadas de manera supervisada. Como es lógico, no es habitual contar con ellas en los conjuntos de datos empleados para algoritmos no supervisados, pero en caso de tenerlas existen métricas que evalúan el resultado mediante una comparativa de las etiquetas predichas con las etiquetas reales:
Del mismo modo que en las anteriores métricas, vamos a iterar un entrenamiento y acumular el error de cada una de estas puntuaciones para cada valor de K.

A continuación, entrenamos el algoritmo no supervisado con el número de grupos K = 20 y echamos un ojo a la distribución de observaciones por cada clúster generado.
Para atenuar la variabilidad del resultado usaremos la validación cruzada. Si no conoces este concepto puedes echar un ojo a este artículo.
El método “score” para el caso de K-Means con la librería Sklearn calcula la media de las distancias de todas las observaciones del dataset a sus respectivos clústers, en negativo, como criterio para evaluar el modelo. Este cálculo es conocido como distorsión o distortion y es similar al error de inercia, con la salvedad de que realiza un promedio en vez de acumular las distancias de cada observación a su respectivo centroide.
Accuracy per fold: [-71.14549414 -72.67791944 -71.04928676 -71.97185517 -72.90843598 -69.61543214 -70.40156191 -69.85335418 -72.07096672 -72.12241503] Mean accuracy: -71.382 Std. deviation: +- 2.188

Si te ha gustado el artículo puedes clicar
tantas veces como quieras. Además, puedes seguir mi cuenta para estar al tanto de futuras publicaciones.
The post K-Means text clustering en Python first appeared on Planeta Chatbot.
]]>The post Clasificación de texto para múltiples clases en Python first appeared on Planeta Chatbot.
]]>Detrás del módulo de entendimiento del lenguaje natural o Natural Language Understanding de un chatbot no hay mucho más que un algoritmo de clasificación de texto en el que los datos de entrenamiento son las frases de los usuarios y la etiqueta su correspondiente intención.
El objetivo de la clasificación desarrollada en este artículo es deducir qué permiso retribuido está solicitando un empleado al describir su situación. En cualquier caso, el código es extrapolable a cualquier otro dataset de texto supervisado.
Antes de nada, será necesario que importes las librerías implicadas en el desarrollo:
'''Text classification: TFIDF (ELI5) with Sklearn logistic regression'''from sklearn.model_selection import train_test_split from sklearn.model_selection import cross_val_score, StratifiedKFold from sklearn.pipeline import Pipeline from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report import numpy as np import seaborn as sns import pandas as pd import eli5__author__= "https://medium.com/@guzman.gp"
Lo primero será volcar el dataset desde un fichero CSV, cuyo lenguaje natural ha sido previamente procesado, con el siguiente formato:
file_alias = "<nombre de tu fichero>"
path_to_data = f'tu/ruta/relativa/{file_alias}.csv'try:
df = pd.read_csv(path_to_data, low_memory=False)
except FileNotFoundError:
raise FileNotFoundError(f"File {file_alias} at path {path_to_data} doesn't exist.")
Visualizamos rápidamente la distribución de observaciones según su clase, para asegurarnos de no sesgar el modelo posteriormente al haber aprendido en exceso sobre unas situaciones concretas:
df['labels'].value_counts().plot.bar();
En el eje de abscisas podéis ver las etiquetas correspondientes a los diferentes permisos retribuidos y en el eje de ordenadas el número de frases de ejemplo por permiso.

Después, dividimos el conjunto de datos de manera que podamos probar el desempeño del modelo con unos datos diferentes a los utilizados para entrenarlo, de manera que mantenga la máxima objetividad posible. Así sabremos si ha aprendido bien a generalizar el conocimiento que tratamos de trasmitirle, o si en cambio, se ha centrado en peculiaridades irrelevantes que predominaban en las frases de entrenamiento.
La disivisón será: un 90% de observaciones (frases) para entrenar y un 10% para probarlo. Además, especificaremos una “semilla” (número fijo al random_state) de manera que la distribución 9:1 escogida en las muestras se mantenga siempre idéntica. Esto nos permitirá saber si el modelo ha mejorado al ajustar sus parámetros, o si realmente ha sido una cuestión de azar, al haber dado con una combinación de subconjuntos que se complementaban especialmente bien en sus respectivos roles de entrenamiento y prueba.

X_train, X_test, y_train, y_test = train_test_split(df['text'], df['labels'], test_size=0.1, random_state=12)
Pero, ¿y si justo la distribución de frases dada por el random_state escogido es una de esas combinaciones atípicas?
En efecto esto puede ocurrir, por ello recurriremos al siguiente método de validación cruzada: Stratified K Fold. El cual repite el entrenamiento del algoritmo el número de veces (K) especificado en el parámetro n_splits, permitiendo reducir la varianza en el resultado.

Es probable que ya hayas oído hablar de esta técnica como K Fold a secas; el muestreo estratificado simplemente se asegura de que la asignación de las frases en cada uno de nuestros K grupos sea proporcional a su predominancia en el conjunto de datos inicial, de manera que la distribución se mantenga lo más natural posible. Así, no condicionamos el desempeño del algoritmo en ninguno de sus K entrenamientos por una posible distribución desbalanceada.

cv = StratifiedKFold(n_splits=10, shuffle=True) # En la imagen anterior hay 5 subgrupos, ¡yo pondré 10!
Como ya sabemos, los algoritmos solo saben interpretar números. Por tanto, antes de entregarle las frases hay que “vectorizarlas”; cada palabra queda representada por un número y cada frase (documento) por el vector resultante de la composición de dichos números. Esta técnica se conoce como Bag of words.
Los vectores resultantes comparten una dimensión equivalente a la cantidad de palabras distintas que componen el corpus. Por ello, cuanto más reducimos este vocabulario más facilitamos al algoritmo la agrupación y posterior clasificación de los vectores de entrada, ya que estaríamos reduciendo la varianza del modelo entrenado. Como es lógico, este filtrado debe mantener las palabras que contienen el significado esencial del texto y descartar el resto (con el uso de stop words, stemming, lemmatization, etc.).
El valor de los números que representan cada palabra encontrada en el corpus de entrenamiento puede escogerse con diferentes criterios. El más simple es la vectorización binaria (one-hot encoder), en el que simplemente asignamos un “1” a aquellas palabras que aparecen en la observación (frase de entrada) y un “0” a las que no.

También podemos optar por los vectores de frecuencia (count vectorizer). Esta técnica va un paso más hallá asignando un número equivalente a la frecuencia de aparición de dicha palabra en todos los textos de entrenamiento utilizados (corpus). De esta manera, el algoritmo dará más peso a las palabras que más se repitan en el contexto estudiado.

Ahora bien, en determinados dominios de conocimiento hay palabras que se repiten en prácticamente todas las frases y que influyen muy poco en el entendimiento del mensaje. Por ejemplo, en el contexto tratado en este caso, las palabras “día/s” son comunes a casi todas las circunstancias. Sin embargo, la presencia de palabras como “matrimonio”, “mudo” y “hospitalizado” en un mensaje suelen ser determinantes en la predicción:
Para resolver este problema se utiliza la técnica TF-IDF (Term Frecuency — Inverse Document Frecuency), en la que el peso que tiene cada palabra (Wi,j) es directamente proporcional a las veces que aparece en las frases de entrenamiento de la clase actual (tfi,j) e inversamente proporcional a las veces que aparece en las frases de entrenamiento de todas las clases (dfi,j).

tfidf_vectorizer = TfidfVectorizer(smooth_idf=True, norm='l2', sublinear_tf=True)
Para el entrenamiento multiclase vamos a escoger la regresión logística de Sklearn:
lgr_model = LogisticRegression(C=25, solver='saga', max_iter=2000)
Para concatenar ambos pasos utilizaremos las “pipelines” de Sklearn:
lgr_pipeline = Pipeline([('tfidf', tfidf_vectorizer),('clf', lgr_model)])
A continuación procesamos los dos pasos descritos en el pipeline en el paso anterior:
lgr_pipeline.fit(X_train, y_train)
Podemos comprobar qué palabras (features) y qué cantidad tiene nuestro corpus:
len(lgr_pipeline[0].get_feature_names()) # 888 en mi caso
Veamos qué desempeño ha conseguido el algoritmo calculando la media de los “K folds” entrenados y cuánto se han dispersado sus puntuaciones:
scores = cross_val_score(lgr_pipeline, X_train, y_train, cv=cv)
sc_mean = scores.mean()
sc_dev = scores.std()*2
print(f'''Accuracy per fold: {scores}
Mean accuracy: {round(scores.mean(),3)}
Std. deviation: +- {round(scores.std()*2,3)}''')
¡Nada mal!
Accuracy per fold: [0.96296296 0.95061728 0.98765432 0.95061728 0.95061728 0.96296296 1. 0.975 0.9875 0.9625 ] Mean accuracy: 0.969 Std. deviation: +- 0.034
Con el siguiente código podemos ver la correspondencia entre los pesos de las palabras y los utilizados por el clasificador son equivalentes.
eli5.show_weights(lgr_pipeline, vec=lgr_pipeline[0], top=20, feature_filter=lambda x: x != '<BIAS>') # He seleccionado las 20 palabras más importante de cada clase
Este esquema nos ayudará muchísimo a ver si hemos abusado de alguna palabra o expresión en las frases de entrenamiento… por ejemplo, quizás la palabra “pareja” tiene una importancia excesiva en la intención “pareja de hecho”. Es normal que la palabra “pareja” predomine en las situaciones de esta clase. Esto se podría solucionar concatenando las palabras “pareja de hecho”, de manera que el algoritmo la considerase como una palabra (feature) a parte: “parejadehecho”. De esta manera este sabría cuándo hablas de tu pareja a secas. Por ejemplo en “quiero acompañar a mi pareja al médico” o “voy a contraer matrimonio con mi pareja” frente a “voy a formalizarme como parejadehecho”.


El caso comentado es solo un ejemplo cualquiera de la infinidad de matices que podemos apreciar a simple vista con esta herramienta de visualización.
Además, puedes ampliar la información para evaluar el algoritmo calculando el resto de métricas y la matriz de confusión:
y_pred = lgr_pipeline.predict(X_test) confs_matrix = pd.crosstab(y_test, y_pred) sns.heatmap(confs_matrix, annot=True, cbar=True) y_test = list(y_test) X_test = list(X_test) mistakes = [X_test[i] for i in range(len(y_pred)) if y_test[i] != y_pred[i]] print(classification_report(y_test, y_pred))
Obteniendo los siguientes resultados:

La escasa presencia de números fuera de la diagonal de la matriz nos informa de que las intenciones no se están confundiendo entre sí prácticamente. Es decir, que las clases están bien escogidas y no hay solapamientos en sus respectivas temáticas. En cuanto a las siguientes métricas, su análisis dependerá de las preferencias que quieras aplicar a tu predicción…
precision recall f1-score support consultas_medicas 1.00 0.95 0.97 19 examenes 1.00 1.00 1.00 6 fallecimiento 1.00 1.00 1.00 12 hospitalización 0.86 0.86 0.86 7 matrimonio 0.95 0.95 0.95 20 pareja_hecho 0.90 0.90 0.90 10 preparacion_parto 1.00 1.00 1.00 5 traslado_domicilio 0.92 1.00 0.96 11 accuracy 0.96 90 macro avg 0.95 0.96 0.95 90 weighted avg 0.96 0.96 0.96 90
En mi caso, me quedo con la accuracy o exactitud media de 96% de aciertos como indicador global del desempeño.
Ahora, vamos a indagar un poco más en los errores, a ver qué palabras están confundiendo al algoritmo en su predicción. Para ello imprimo el error seguido de su etiqueta predicha (erróneamente):
print(f'{len(mistakes)} mistakes from {len(y_test)} validation samples:')
for i,f in enumerate(mistakes):
print(f"X {f} -> {y_test[i]}")
Resultando en…
4 mistakes from 90 validation samples: X necesito enviar empresa algun documento cambio proximamente civil soltero casado -> pareja_hecho X dias permiso retribuido acompañar hospital familiar grado consanguinidad -> matrimonio X ingresado pareja urgencias -> traslado_domicilio X equivalentes uniones matrimonio -> consultas_medicas
Sí, las frases parecen redactadas por el mismísimo Yoda. ¡Recuerda que están preprocesadas, de ahí su redacción telegráfica! El NLP da para un largo artículo, así que podemos resumir que estas frases están filtradas y les faltan palabras comunes sin significado que suelen servir de nexo sintáctico (conocidas como stop words). Así evitamos generar vectores kilométricos…

Y pensar que todo este tiempo Yoda solo estaba aplicando NLP al hablar…
Ahora vamos a estudiar a fondo qué palabras está interpretando mal el algoritmo para reescribir, añadir o quitar determinadas frases de entrenamiento.
Por ejemplo, escogiendo el primer fallo (mistakes[0]): “necesito enviar empresa algun documento cambio próximamente civil soltero casado”, quedaría:
eli5.show_prediction(lgr_pipeline[1], doc=mistakes[0], vec=lgr_pipeline[0])


Ya te habrás dado cuenta de que cada columna (clase) de la tabla contiene la frase errónea pasada como parámetro descompuesta en un ranking. El cual está encabezado por la palabra con más peso y terminado con la de menos peso. En la cabecera de la tabla encontramos la intención correspondiente, con su afinidad (probabilidad de ocurrencia) y peso total acumulado.
Alguna de las palabras de la frase no aparecen, por ejemplo: “soltero”, “documento”, “próximamente”, etc. Esto se debe a que no estaban incluidas en el corpus que generó el vector bag-of-words y deberíamos pensar en incluir algunos ejemplos con ellas en los textos de entrenamiento.
Vemos que en este caso el origen del error es la palabra “cambio”, la cual ha inclinado totalmente la balanza hacia la intención de “traslado de domicilio”, probablemente por su alta frecuencia de ocurrencia en sus frases de entrenamiento en comparación con el resto de clases. ¡Tocará disminuir su frecuencia en la clase equivocada y compensar su ausencia en el resto de clases! 
The post Clasificación de texto para múltiples clases en Python first appeared on Planeta Chatbot.
]]>The post Chatbots: algoritmos, intenciones, entidades e historias first appeared on Planeta Chatbot.
]]>En primer lugar, conviene entender lo que es un algoritmo de clasificación y cómo funciona para poder interiorizar íntegramente la mecánica que hay detrás de las interfaces conversacionales.
Recuerda que en esencia un chatbot está compuesto por dos grandes partes:
Ambas partes están generalmente basadas en algoritmos de clasificación para llevar a cabo estas tareas. Estos forman parte del conocido grupo aprendizaje automático, y dentro de este, pertenecen al subgrupo de los algoritmos de tipo supervisado. A grandes rasgos, este tipo de algoritmos se pueden entender como una función que recibe diferentes variables de entrada y devuelve una variable de salida.
Se les acuña con el término “supervisado” porque extraen conocimiento de forma automática a partir del procesado de una serie de muestras de entrenamiento que han sido previamente vinculadas a una determinada clase bajo supervisión de una persona.
Una vez finalizada esta fase de entrenamiento, el algoritmo podrá predecir (calcular) la clase (variable de salida) a la que pertenece una muestra de entrada no utilizada para entrenar al algoritmo, con un alto grado de fiabilidad.

En este caso, las muestras con las que entrenamos el algoritmo (variables de entrada) son frases de ejemplo que esperamos recibir por parte del usuario.
La salida, una vez entrenado el algoritmo, será la intención del usuario con mayor afinidad a la frase que ha recibido como entrada, según el criterio del algoritmo.

Desde un punto de vista matemático, estas frases se convierten a un vector para que el algoritmo las pueda interpretar.
Además, cada una de las clases o intenciones con las que se “etiqueta” el mensaje del usuario, también se “traducen” a un vector.

De esta manera, el entrenamiento para cualquier algoritmo geométrico supervisado, consiste en un ajuste de los parámetros orientados a reducir la distancia o error entre el vector de entrada (frase) y el vector de salida (intención).

Entrenamiento para el caso de una red neuronal: ajuste de parámetros para obtener el mínimo “coste” o error.
Probablemente al ver la viñeta anterior, entre el hombre de uniforme y el robot, has pensado: «Bueno, el mensaje del hombre es un poco ambiguo. Es decir, podría estar finalizando la conversación… sí, pero también podría quedar algún asunto pendiente y querer continuar la conversación».
Es cierto, hay multitud de frases ambiguas que pueden requerir una respuesta u otra en función del contexto que rodea a la conversación en ese instante. Es en este aspecto donde el CORE ayuda al NLU.
En primer lugar, el NLU predice la intención. En este caso “Despedir”, ya que el bot entiende que el humano está cerrando la conversación.
En segundo lugar, pasa la intención predicha al algoritmo del flujo de diálogo. Este, procede a analizar el contexto para escoger la acción idónea con la que responder al humano. Para ello, a parte de la intención predicha por el NLU, se fija en otras cosas como palabras clave utilizadas dentro del mensaje del usuario (entidades), en el estado de las variables de algún otro proceso contextual y en las últimas acciones respondidas por el bot.
En este caso, vamos a suponer que el objetivo de la conversación es realizar un formulario. Por tanto, podemos resumir el contexto en dos alternativas: “Formulario incompleto” y “Formulario completo”.
En la imagen de abajo, si observamos el segundo pensamiento de la viñeta de arriba. Nos damos cuenta de que antes de responder, el bot comprueba si queda algún asunto pendiente en el formulario. Al darse cuenta de que sí, devuelve una frase de cierre y continuación de diálogo. Si por el contrario comprueba que ya no quedan más pasos, responde con un comentario que finaliza la conversación.

Resumiendo, las muestras de entrada con las que entrenamos este algoritmo son palabras clave, otras variables contextuales y las acciones que se han ido escogiendo en los mensajes inmediatamente anteriores.
La salida, una vez entrenado el algoritmo, será la acción más apropiada con la que responder al usuario.
Recuerda: como comenté en el artículo anterior, no es obligatorio basar el flujo de diálogo en un algoritmo.
Algunos servicios de interfaces conversacionales, como IBM Watson Assistant, construyen el flujo de diálogo por reglas, a partir de una ramificación de nodos que contemplan todas las variantes que pueden tener lugar en una conversación.
Estos nodos son los estados u “hojas” dentro de ese “árbol”, los cuales son activados cuando se cumple una determinada condición compuesta por una combinación de las mencionadas entidades, variables de contexto e intenciones.
A cada nodo se le configura previamente una determinada respuesta, como un mensaje, un salto a otro nodo, una imagen, una URL o cualquier otro servicio que pudiera darte una página web, de la misma manera que se le vincula a cada acción predicha en el caso del algoritmo.

Ejemplo de flujo de diálogo basado en estructura jerárquica de IBM Watson Assistant
Hemos aludido a las intenciones (o en inglés intents) en numerosas ocasiones. Ya sabemos que son el resultado predicho por el algoritmo del NLU. En concreto, para un mensaje de entrada, el NLU calculará el porcentaje de afinidad o confianza (confidence) con el que considera que se aproxima a cada intención para las que ha sido entrenado a identificar.
Por ejemplo, imagina un chatbot que asiste a los usuarios sobre servicios técnicos en el departamento de una empresa. Supongamos que este chatbot está diseñado para atender a los trabajadores en 2 dominios diferentes: “Resolver incidencia” y “Solicitar un dispositivo”.
Por tanto, tendría dos intenciones (intents). Una por cada conocimiento del contexto que queremos que domine.
Continuando con el ejemplo, si el usuario abre el canal de conversación y dice: “Mi ordenador no se conecta con la impresora” (variables de entrada) el chatbot debería predecir la intención “Resolver incidencia” (variable de salida o clasificación).

1ª figura: Datos/frases/vectores etiquetados según clases A y B. 2ª figura: Clasificación una vez entrenado el algoritmo
En el ejemplo anterior el usuario podría haber utilizado muchas variantes: “El ordenador no detecta a la impresora”, “La impresora no responde al ordenar la impresión”, “No encuentro la impresora en las listadas en la configuración de impresión.”, etc.
Los matices de todas estas muestras irán almacenándose en los parámetros del algoritmo utilizado, por tanto, cuantas más frases de ejemplo, el bot tendrá una mayor probabilidad de acertar la intención del usuario.
Estas frases de entrenamiento deben ser las más representativas de una determinada intención. Es decir, las peticiones más frecuentes que esperamos recibir por parte del usuario.
Aquí va un fragmento de la creación de una intención del ejemplo utilizado anteriormente generado con la interfaz del chatbot de IBM.

Por otro lado, si recuerdas en la explicación del CORE apareció el concepto de entidad (entity). Estas son palabras o expresiones clave dentro del mensaje del usuario que el chatbot está programado para reconocer y que activan determinadas acciones cuando se detecta su presencia.
Además, para cubrir todas aquellas posibles variantes de esas entidades que puede escribir el usuario para referirse a lo mismo, estas se puede complementar con, o bien un grupo de sinónimos si aluden a palabras o bien con expresiones regulares para códigos alfanuméricos como un número de teléfono, DNI, etc.
Siguiendo con la línea de ejemplos anteriores, imagina que un usuario dice al chatbot: “Mi navegador ha volcado un error 403 cuando he introducido la página web, ¿a qué se debe?”.
En primer lugar el NLU predecirá la intención “Resolver incidencia”. Después, comprobará si reconoce alguna entidad en el mensaje.
Supongamos que previamente hemos creado estas dos entidades:
1. “Navegador”: junto con otras palabras con significados similares.
2. “Error”: con expresión regular de todos los códigos que pueden aparecer en el navegador (en este caso regex de 3 dígitos).

Así quedarían estas entidades creadas dentro de la interfaz de IBM:
Por último, el CORE, recibirá la tupla “intención-entidades” , considerará cuales han sido las últimas acciones devueltas por el bot y lo introducirá en su algoritmo para predecir la acción que corresponde.
Las historias también es un recurso utilizado por el CORE. En concreto, es la información de la que se nutre el algoritmo a la hora de entrenarse.
Estas aúnan todos los posibles contextos por los que se contempla que se desarrolle la conversación. En otras palabras, son los esqueletos de todas las conversaciones previstas entre el usuario y el chatbot. Estos esqueletos están compuestos por intenciones, entidades u otras variables contextuales (slots).
De momento, solo he visto su aplicación en el framework Rasa. En la imagen inferior se puede observar un ejemplo del formato utilizado para recopilar estas historias. Con ello, el algoritmo podrá proceder con el entrenamiento.

La interpretación es la siguiente: el nombre de la historia (##) es “camino feliz”, las intenciones (*) son “saludo”, “contactar ventas” y “agradecimiento” . Las respuestas (–) pueden ser texto (utter) o acciones, en este caso en forma de cuestionario (form) junto con las entidades o slots ({}) que debe identificar y almacenar.
Si te ha gustado el artículo puedes clicar
tantas veces como quieras. Además, puedes seguir mi cuenta para estar al tanto de futuras publicaciones
Si quieres pasar a la acción, puedes echar un vistazo a este ejemplo de NLU sencillo en Python:
The post Chatbots: algoritmos, intenciones, entidades e historias first appeared on Planeta Chatbot.
]]>