Al analizar una expresión del usuario, tenemos que identificar la intención del usuario (es decir, lo que quiere), pero también extraer los parámetros del texto que serán necesarios para responder a la petición. Estos parámetros se denominan entidades en el mundo de la NLP. El proceso de extracción de entidades del texto se conoce como reconocimiento de entidades con nombre o NER, por sus siglas en inglés.
Las entidades pueden ser nombres de lugares, personas, fechas, divisas, lugares,…, como se muestra en la imagen destacada que muestra los resultados de la diplomacia en un texto de muestra. Pero también pueden ser cualquier lista de valores relevantes para su dominio (por ejemplo, los tipos de pizzas que ofrece su restaurante). En este caso, hablamos de NER personalizado.
En nuestro post anterior, explicamos cómo construir tu propio clasificador de intenciones para chatbot, veamos ahora cómo podemos extenderlo para que soporte NERs personalizados. Recuerda que este motor Xatkit NLU que estamos construyendo es totalmente de código abierto.
Tabla de contenidos
Representar las NER personalizadas como parte de la definición de nuestro chatbot
El primer paso es permitir a los diseñadores de bots declarar las entidades personalizadas que deben ser reconocidas al ejecutar el chatbot. Para ello, hemos ampliado nuestro módulo dsl.py
con clases adicionales.
class Entity:
«»»An entity to be recognized as part of the matching process»»»
def __init__(self, name: str):
self.name: str = name
class CustomEntityEntry:
«»»Each one of the entries (and its synonyms) a CustomEntity consists of»»»
def __init__(self, value: str, synonyms: list[str] = None):
self.value: str = value
self.synonyms: list[str] = synonyms
class CustomEntity(Entity):
«»» A custom entity, adhoc for the bot «»»
def __init__(self, name: str, entries: list[CustomEntityEntry] = None):
super().__init__(name)
if (entries is not None):
self.entries: list[CustomEntityEntry] = entries
else:
self.entries = []
class EntityReference:
"""A parameter of an Intent, representing an entity that is expected to be matched"""
def __init__(self, name: str, fragment:str, entity: Entity):
self.entity: Entity = entity # Entity type to be matched
self.name: str = name # name of the parameter
self.fragment: str = fragment # fragment of the text representing the entity ref in a training sentence
class Intent:
"""A chatbot intent"""
def __init__(self, name: str, training_sentences: list[str]):
self.name: str = name
self.training_sentences: list[str] = training_sentences
self.processed_training_sentences: list[str] = []
self.training_sequences: list[int] = []
# list of references to entities used in the Intent
# we are going to assume that two intents in the same context do not have parameters with the same name unless they refer to the same entity type
self.entity_parameters: list[EntityReference] = []
Ahora una intención puede incluir varios parámetros de entidad, cada uno de los cuales hace referencia a una entidad personalizada. La referencia se identifica como un fragmento de texto específico en las frases de entrenamiento. Cada entidad personalizada se define como un conjunto de valores más una lista de posibles sinónimos para cada valor. Para aquellos que utilizan Xatkit como una plataforma de chatbot completa (y no sólo jugando con este servidor NLU como un componente independiente), esto correspondería al concepto de mapeo de entidades.
El siguiente gist muestra un ejemplo sencillo de una intención meteorológica con un NER de ciudad personalizado. Como parte de la definición de la intent, estamos diciendo que el fragmento correspondiente a mycity
debe coincidir con un valor de ciudad de la lista de posibles ciudades en city_entity
. Evidentemente, si se desea que coincida con cualquier ciudad, lo mejor sería reutilizar una lista predefinida de ciudades de todo el mundo en lugar de crear manualmente nuestra propia lista, pero como ejemplo sencillo, seguro que se entiende la idea.
def test_intent_with_ner_initialization():
entity: CustomEntity = CustomEntity(‘city_entity’, [CustomEntityEntry(‘Barcelona’, [‘BCN’]), CustomEntityEntry(‘Madrid’)])
intent: Intent = Intent(‘intent_name’, [‘what is the weather like in mycity’, ‘forecast for mycity’, ‘is it sunny?’], [EntityReference(‘city’, ‘mycity’, entity)])
Comparación de los NER personalizados
Esta parte es bastante sencilla, ya que simplemente utilizamos una iteración directa en todos los NER personalizados posibles e intentamos hacer coincidir sus valores con el enunciado del usuario y recopilamos todas las coincidencias si las hay. A continuación, se devuelve un diccionario de las coincidencias con los pares junto con las predicciones de coincidencia de intención.
Así, si el usuario dice algo como «¿Cuál es la previsión para Barcelona?», le informaremos de que hemos podido encontrar una referencia a una city_entity
en el enunciado (Barcelona en este caso).
Uso de NER personalizados durante la predicción de la concordancia de intenciones
Hasta ahora hemos podido identificar las entidades, pero no forman parte del proceso de comparación de intenciones. ¿Deben influir en la predicción? ¿Es importante la información de los NER para reforzar las posibilidades de coincidencia de una determinada intención? Creemos que sí.
Por lo tanto, si el parámetro use_ner_in_prediction
se establece en true, antes de ejecutar la predicción del modelo, sustituiremos cualquier referencia a la entidad por el nombre del propio tipo de entidad. Esto hace que la entrada del usuario se asemeje más a las sentencias de entrenamiento de los intentos que hacen referencia a esa misma entidad, ya que ahora ambos estarán utilizando el nombre de la entidad (en lugar de cualquiera de sus valores concretos). Esta coincidencia adicional empujará a la red neuronal a aumentar la probabilidad de que esos intents coincidan.
Incluso podría ir un paso más allá al procesar la combinación de predicción de intención + NERs coincidentes para determinar la mejor intención candidata. En lugar de ir sólo a por la intención con mayor probabilidad, podría filtrar primero (o dar más peso) a las intenciones para las que haya podido hacer coincidir las intenciones referenciadas. Imagínese que el motor NLP le dice que la mejor coincidencia para el texto del usuario es la intención «Previsión meteorológica de la ciudad», pero no se ha encontrado ninguna referencia a la ciudad en el proceso.
Por lo tanto, tiene que responder a la previsión de una ciudad que no conoce. Si el parámetro de la ciudad fuera opcional (por ejemplo, si se quiere preguntar por la ciudad en el siguiente estado de la conversación), no hay problema. Pero si no era opcional (es decir, todas las sentencias de entrenamiento para la intención hacían referencia a la entidad), la elección de esa intención probablemente acabará provocando un error cuando el cliente Xatkit la busque para construir la respuesta.
Especialmente cuando dos intenciones tienen un valor de probabilidad muy similar, puede ser una buena idea elegir aquel para el que hayamos sido capaces de acertar todos los parámetros esperados.