Como avaliar a qualidade de frases de treinamento em intents do Dialogflow

Neste tutorial, você aprenderá como analisar e avaliar a qualidade das frases de treinamento fornecidas às intents do seu agente do Dialogflow (em inglês). A finalidade dessa análise é evitar que o agente se confunda com frases irrelevantes para algumas intents e mais relevantes para outras.

A abordagem adotada é gerar embeddings semânticos das frases de treinamento usando o módulo Universal Sentence Encoder do TensorFlow Hub (tf.Hub). Em seguida, calcule as medidas de coesão e separação com base na semelhança entre embeddings dentro das mesmas intents e em intents diferentes. Neste tutorial, você verá que são identificadas as frases de treinamento “confusas” que, no espaço de embedding, estão mais próximas de intents que são diferentes daquelas que receberam essas frases.

Acesse este notebook do Colab (em inglês) para ver o código usado neste tutorial. Neste artigo, presume-se que você tenha conhecimento básico sobre o Dialogflow. Para saber mais sobre o Dialogflow, consulte este tutorial de várias partes sobre como criar, proteger e escalonar um bot de chat usando o Dialogflow Enterprise Edition no Google Cloud.

Introdução

Com o Dialogflow (em inglês) é possível criar interfaces de conversação em produtos e serviços usando um eficiente mecanismo de processamento de linguagem natural (PLN) para processar e entender entradas de linguagem natural. Veja abaixo alguns casos de uso do Dialogflow:

  • Criar bots de reservas para companhias aéreas, cinemas etc.
  • Simplificar um sistema de pedidos de entrega de fast-food.
  • Viabilizar um atendimento ao cliente eficiente por meio de call-centers semiautomatizados.

É possível implementar fluxos de conversa complexos para processar enunciados dos usuários, mas o Dialogflow executa basicamente as seguintes etapas:

  1. O usuário faz perguntas como "Qual é o total da minha fatura no último mês?"
  2. O agente analisa a entrada e a corresponde a uma intent, como bill_value_inquiry.
  3. O agente também extrai informações de entidades, como "mês passado".
  4. De acordo com a intent das entidades extraídas, o agente invoca um fulfillment para responder à solicitação do usuário.

Os principais conceitos da plataforma Dialogflow são exibidos na tabela a seguir.

Termo Descrição
agente Os agentes são módulos de PLN que podem ser integrados ao sistema. Um agente converte solicitações de usuários em texto ou fala em dados acionáveis, quando a entrada corresponde a uma intent nesse agente.
intent Em uma conversa, as intents fazem o mapeamento das entradas dos usuários nas respostas. Em cada intent, defina exemplos (frases de treinamento) de enunciados do usuário que podem acioná-las, o que extrair de cada enunciado e como responder.
entidades Enquanto as intents permitem que o agente processe a motivação subentendida em uma entrada específica do usuário, as entidades são usadas para captar informações específicas mencionadas pelos usuários. Por exemplo, é possível usar endereços, nomes de produtos ou quantidades com unidades para atender à solicitação do usuário.
fulfillment Com o fulfillment, é possível usar as informações de entidade extraídas pelo agente para gerar respostas dinâmicas ou desencadear ações no back-end, em uma intent de cada vez.

Para mais detalhes sobre os conceitos do Dialogflow, consulte a documentação da solução (em inglês).

As intents são essenciais para um sistema do Dialogflow porque elas fazem a conexão entre as solicitações dos usuários e a lógica de negócios correta para atendê-las. Por exemplo, um sistema Dialogflow para um provedor de serviços de telecomunicações pode ter intents como bill_value_inquiry, pay_bill, upgrade_contract, cancel_contract e add_service. No entanto, para fazer a correspondência entre o enunciado do usuário (texto ou fala) e a intent correta, é necessário que as intents sejam treinadas com um conjunto de frases de treinamento relevantes. Por exemplo, no caso de uma intent de consulta sobre o clima, podemos usar as seguintes frases de treinamento:

  • "Como está o tempo agora?"
  • "Qual será a temperatura no Cairo amanhã?"
  • "Preciso levar um guarda-chuva comigo para Zurique na semana que vem?"

Quando você cria várias intents no sistema, algumas frases fornecidas para uma intent podem ser confusas ou enganosas. Por exemplo, a frase mais relevante para uma determinada intent pode ser usada no treinamento da intent errada. Suponha que você tem um agente do Dialogflow que serve como uma base confiável para uma organização de vendas. Digamos que você tem duas intents para buscar contatos: uma para as equipes de conta internas e outra para o cliente, Você talvez queira chamá-los de get_internal_contacts e get_external_contacts. Uma frase de treinamento típica para cada intent seria como as seguintes:

  • get_internal_contacts: "Quem é o ponto de contato do cliente X?"
  • get_external_contacts: "Como entrar em contato com o cliente X?"

Imagine que os usuários tenham fornecido a seguinte solicitação ao procurar contatos externos: "Contatos do cliente X". Essa solicitação pode confundir o agente do Dialogflow porque é possível fazer correspondência dessa frase com ambas as intents. Se for feita a correspondência com a intent errada, a experiência dos usuários será ruim porque eles precisarão formular a solicitação de modo diferente, o que é irritante e demorado.

Portanto, convém garantir que as frases dentro da mesma intent sejam mais semelhantes e que as frases entre intents diferentes sejam menos semelhantes. No restante do tutorial, você aprenderá como avaliar a qualidade das frases de treinamento fornecidas para cada intent e como identificar frases de treinamento possivelmente confusas.

Abordagem

Neste tutorial, é usada uma abordagem para calcular a semelhança entre duas frases e, consequentemente, calcular a matriz de semelhança para todas as frases de treinamento. Depois de determinar essa matriz, será possível calcular o seguinte:

  • Coesão: o valor médio de semelhança entre cada par de frases na mesma intent. Esse valor é calculado para cada intent. Quanto maior o valor de coesão de uma intent, melhores serão as frases de treinamento dela.
  • Separação: a distância média entre cada par de frases de treinamento em duas intents determinadas.
  • Frases confusas: frases de treinamento de uma intent que são altamente semelhantes a frases de treinamento em outras intents.

Para calcular um valor de semelhança entre duas frases, converta cada uma delas em um vetor de atributo de valor real, que representa a semântica da frase (embeddings). Para ajudar nessa tarefa, usamos o TensorFlow Hub (tf.Hub), uma biblioteca usada para publicação, descoberta e consumo de módulos reutilizáveis de modelos de machine learning. Esses módulos podem ser modelos pré-treinados ou embeddings extraídos de textos, imagens etc. É possível navegar pelos embeddings de texto disponíveis. Neste tutorial, é usado o módulo Universal Sentence Encoder (v2) [em inglês] para codificar texto em vetores com 512 dimensões que podem ser usados para classificação de textos, semelhança semântica, clustering e outras tarefas de processamento de linguagem natural.

Neste tutorial, usamos a semelhança de cossenos (em inglês) como métrica de proximidade entre dois vetores de embedding. Dados dois vetores de valor real, a semelhança de cossenos calcula o cosseno do ângulo entre eles. No nosso exemplo, foram usados dois vetores de embedding extraídos de duas frases de treinamento. O cálculo é feito usando a seguinte fórmula:

$$ \cos(A,B) = \frac{\sum_{i=1}^{n}A_iB_i}{\sqrt{\sum_{i=1}^{n}{A_i^2}}\sqrt{\sum_{i=1}^{n}{B_i^2}}} $$

Nessa fórmula, n é o número de elementos no vetor. Quanto menor for o ângulo entre os vetores, maior será o valor de cosseno desse ângulo, indicando uma maior semelhança. O valor de semelhança de cossenos entre dois vetores sempre está entre 0 e 1.

A figura 1 mostra uma visão geral da abordagem:

Visão geral de como avaliar a coesão e a separação de intents

Figura 1: visão geral de como avaliar a coesão e a separação de intents.

A figura ilustra a seguinte sequência:

  1. Importação das intents e das respectivas frases de treinamento
  2. Gere embeddings para as frases de treinamento usando o módulo pré-treinado do codificador de frases universal tf.Hub.
  3. Criação de uma visualização dos embeddings gerados em um espaço bidimensional.
  4. Cálculo da matriz de semelhança de cossenos dos embeddings que contém os valores de semelhança de pares entre todas as frases de treinamento das diferentes intents
  5. Cálculo das métricas de coesão e separação
  6. Identificação das frases confusas

Objetivos

  • (Opcional) Criar um agente do Dialogflow.
  • Importar intents com frases de treinamento.
  • Executar o notebook do Colab para avaliação da qualidade das intents.

Custos

Neste tutorial, usamos o seguinte componente faturável do Google Cloud:

  • Dialogflow: a versão Standard Edition é gratuita, mas a versão Enterprise Edition oferece suporte empresarial pago. Escolha uma delas ao criar seu agente do Dialogflow. Sua conta pode incluir agentes de ambas as edições. Para mais detalhes, consulte a página de preços do Dialogflow (em inglês).

Antes de começar

  1. Faça login na sua conta do Google.

    Se você ainda não tiver uma, inscreva-se.

  2. No Console do Cloud, na página do seletor de projetos, selecione ou crie um projeto do Cloud.

    Acessar a página do seletor de projetos

  3. Verifique se a cobrança está ativada para o seu projeto do Google Cloud. Saiba como confirmar se a cobrança está ativada para o seu projeto.

  4. Ative a API Dialogflow.

    Ative a API

  5. Crie uma conta de serviço para chamar a API Dialogflow.

    Criar conta de serviço
  6. Na caixa de diálogo Detalhes da conta de serviço, insira o nome e a descrição da conta, conforme mostrado na captura de tela a seguir. Depois, clique em Criar:

    Captura de tela da caixa de diálogo
  7. Defina o papel como Cliente da API Dialogflow e clique em Continuar.

    Captura de tela com a caixa de diálogo

Como concluir o tutorial no notebook do Colab

Nas seções a seguir, você verá orientações sobre as etapas discutidas na seção de abordagem para calcular as métricas de coesão e separação e identificar frases confusas.

Primeiros passos com o notebook do Colab

  1. Acesse o notebook do Colab (em inglês): https://colab.research.google.com/drive/....

  2. Faça uma cópia local no seu Google Drive.

    Como copiar o notebook para seu Google Drive

  3. No Cloud Shell, instale as bibliotecas do Python necessárias para o restante do tutorial, antes de importar as bibliotecas e os módulos necessários.

    !pip install --quiet --upgrade tensorflow dialogflow scipy tensorflow-hub seaborn
    
  4. Defina o PROJECT_ID e o SERVICE_ACCOUNT_EMAIL do Google Cloud que você criou na seção Antes de começar.

    Defina o PROJECT_ID e o SERVICE_ACCOUNT_EMAIL do Google Cloud

  5. Autentique a sessão para criar uma chave da conta de serviço:

    auth.authenticate_user()
    !gcloud config set project {PROJECT_ID}
    !gcloud iam service-accounts keys create sa-key.json \
        --iam-account={SERVICE_ACCOUNT_EMAIL} --project={PROJECT_ID}
    

    Depois que você executar esses comandos, será exibido um link.

  6. Acesse esse link para autenticar sua conta de usuário.

  7. Copie o código de autenticação do página da Web e cole-o no campo Enter verification code no notebook:

    Campo **Enter verification code** no notebook

Como configurar um agente do Dialogflow

Se você já tiver um agente do Dialogflow que queira usar neste tutorial, pule esta etapa. No entanto, se você não tiver um agente ou quiser configurar um novo, faça o download de um arquivo zip com o conteúdo de um agente do Dialogflow exportado, chamado intents-healthcheck. Importe esse agente para sua conta do Dialogflow da seguinte maneira:

  1. Faça o download do arquivo ZIP do agente importado:

    gsutil cp gs://dialogflow-intent-health-check/intent-quality-demo.zip .
    
  2. Acesse https://dialogflow.com/ (em inglês).

  3. Clique no botão Acessar o Console no canto superior direito.

  4. No menu à esquerda, clique em Criar novo agente.

    Criar um agente novo

  5. Digite o nome do agente: intents-healthcheck.

  6. Selecione seu projeto do GCP na lista Projeto do Google.

    • Um projeto do Google Cloud pode ter apenas um agente do Dialogflow. Portanto, se você não encontrar seu projeto do Google Cloud na lista, um agente já estará associado a ele.
    • Se você selecionar Criar um novo projeto, o Dialogflow criará um projeto do Google Cloud com o mesmo nome do agente.
  7. Clique em Criar.

    Como inserir as informações do agente

  8. No menu à esquerda, selecione o novo agente e clique no ícone de Configurações . Depois, no menu no meio da página, selecione Exportar e importar.

    Caixa de diálogo

  9. Clique em Restaurar do arquivo ZIP:

    1. Selecione o arquivo agent-backup.zip que você fez o download na etapa 1.
    2. Digite RESTORE na caixa de texto no final do formulário para confirmar.
    3. Clique em Restaurar.

    Restaurar o agente de um arquivo ZIP

    Depois de restaurar o agente, o Dialogflow criará cinco intents.

  10. Selecione Intents no menu à esquerda para verificar as intents importadas. Você encontrará as intents a seguir:

    Como verificar as intents importadas

Use o agente restaurado no restante do tutorial.

Orientações sobre o código no notebook do Colab

As seções a seguir descrevem o que o código no notebook faz quando executado.

Como buscar as intents

O código a seguir busca intents e frases de treinamento do agente do Dialogflow usando o método fetch_intents_training_phrases. Esse método retorna um dicionário, em que as chaves são as intents nomeadas no seu agente do Dialogflow, e cada valor é uma lista das frases de treinamento em cada entidade. No código, project faz referência ao projeto a que o agente pertence, e service_account_file faz referência ao arquivo criado anteriormente.

def get_intents(service_account_file, project):

    dialogflow_entity_client =  dialogflow.EntityTypesClient.from_service_account_file(service_account_file)
    parent = dialogflow_entity_client.project_agent_path(project)
    entities = list(dialogflow_entity_client.list_entity_types(parent))

    dialogflow_intents_client = dialogflow.IntentsClient.from_service_account_file(service_account_file)
    parent = dialogflow_intents_client.project_agent_path(project)
    intents = list(dialogflow_intents_client.list_intents(
        parent=parent,
        intent_view=dialogflow.enums.IntentView.INTENT_VIEW_FULL))

    entities_name_to_value = {}
    for intent in intents:
        entities_used = {entity.display_name
            for entity in intent.parameters}

        for entity in entities:
            if entity.display_name in entities_used \
                    and entity.display_name not in entities_name_to_value:
                entities_name_to_value[entity.display_name] = np.random.choice(
                    np.random.choice(entity.entities).synonyms, replace=False)

    intent_to_training_phrases = defaultdict(list)
    for intent in intents:
        for training_phrase in intent.training_phrases:
            parts = [entities_name_to_value[part.alias] if part.entity_type else part.text
                for part in training_phrase.parts]
            intent_to_training_phrases[intent.display_name].append("".join(parts))
        # Remove intents with no training phrases
        if not intent_to_training_phrases[intent.display_name]:
            del intent_to_training_phrases[intent.display_name]
    return intent_to_training_phrases
 

O código a seguir verifica as intents recuperadas:

intent_training_phrases = fetch_intents_training_phrases("sa-key.json", project_id)
for intent in intent_training_phrases:
    print("{}:{}".format(intent, len(intent_training_phrases[intent])))

O método fetch_intents_training_phrases retorna a listagem a seguir. Esse snippet de código mostra os intents no agente de demonstração intents-healthcheck, seguidos pela contagem das frases de treinamento disponíveis em cada intent.

start_conversation:4
close_conversation:5
get_internal_contacts:17
request_help:7
get_external_contacts:6

Como gerar embeddings para as frases de treinamento

O código a seguir faz o download do módulo pré-treinado do tf.Hub Universal Sentence Encoder:

embed_module = hub.Module("https://tfhub.dev/google/universal-sentence-encoder/2")

Após o primeiro uso, o módulo é localmente armazenado em cache.

O código a seguir implementa um método que aceita uma lista de frases e retorna uma lista de embeddings com base no módulo tf.Hub:

def make_embeddings_fn():
    placeholder = tf.placeholder(dtype=tf.string)
    embed = embed_module(placeholder)
    session = tf.Session()
    session.run([tf.global_variables_initializer(), tf.tables_initializer()])
    def _embeddings_fn(sentences):
        computed_embeddings = session.run(
            embed, feed_dict={placeholder: sentences})
        return computed_embeddings
    return _embeddings_fn

generate_embeddings = make_embeddings_fn()

Esse método garante que o tf.Session seja criado e que o módulo de incorporação seja carregado apenas uma vez, não sempre que o método for chamado.

O código a seguir gera embeddings para as frases de treinamento nas intents:

{
    intent: {
        training_phrase': [embedding_array]
    }
}

training_phrases_with_embeddings = defaultdict(list)
for intent_name, training_phrases_list in intent_training_phrases.items():
    computed_embeddings = generate_embeddings(training_phrases_list)
    training_phrases_with_embeddings[intent_name] = dict(zip(training_phrases_list, computed_embeddings))

Esse snippet de código cria o dicionário aninhado training_phrases_with_embeddings.

O código a seguir verifica os embeddings gerados:

training_phrases_with_embeddings = defaultdict(list)
for intent_name, training_phrases_list in intent_training_phrases.items():
    computed_embeddings = generate_embeddings(training_phrases_list)
    training_phrases_with_embeddings[intent_name] = dict(zip(training_phrases_list, computed_embeddings))
 

O snippet de código a seguir mostra cada frase de treinamento na intent start_conversation, junto com os cinco primeiros elementos do vetor de embedding de cada frase. O Universal Sentence Encoder gera um vetor de embedding de 512 dimensões para cada frase de treinamento.

Ciao!:[-0.03649221  0.02498418 -0.03456857  0.02827227  0.00471277]
Howdy!:[-0.02732556 -0.00821852 -0.00794602  0.06356855 -0.03726532]
Hello!:[-0.0255452   0.00690543 -0.00611844  0.05633081 -0.0142823 ]
Hi!:[-0.03227544 -0.00985429 -0.01329378  0.06012927 -0.03646606]

Como visualizar os embeddings no espaço bidimensional

O código a seguir reduz de 512 para 2 o número de dimensões dos embedding, usando a análise de componentes principais para calcular esses componentes:

from sklearn.decomposition import PCA
embedding_vectors = None

for intent in training_phrases_with_embeddings:
    embeddings = list(training_phrases_with_embeddings[intent].values())
    if embedding_vectors is None:
        embedding_vectors = embeddings
    else:
        embedding_vectors = np.concatenate((only_embeddings, embeddings))

pca = PCA(n_components=3)
pca.fit(embedding_vectors)

Esse snippet de código usa a classe PCA em sklearn para gerar uma representação 2D dos embeddings de frases de treinamento.

O código a seguir gera uma visualização dos embeddings de frases com número reduzido de dimensões:

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(15,10))
ax = fig.add_subplot(111)

legend = []

for color, intent in enumerate(training_phrases_with_embeddings):
    phrases = list(training_phrases_with_embeddings[intent].keys())
    embeddings = list(training_phrases_with_embeddings[intent].values())
    points = pca.transform(embeddings)
    xs = points[:,0]
    ys = points[:,1]
    ax.scatter(xs, ys, marker='o', s=100, c="C"+str(color))
    for i, phrase in enumerate(phrases):
        ax.annotate(phrase, (xs[i], ys[i]))
    legend.append(intent)

ax.legend(legend)
plt.show()

A figura abaixo mostra a visualização resultante: Como visualizar os embeddings de frases com número reduzido de dimensões

Como calcular a semelhança de pares entre as frases

O código a seguir calcula a semelhança de cossenos em pares para os embeddings de frases de treinamento usando sklearn.metrics.pairwise.cosine_similarity. O código cria um DataFrame, similarity_df, com os valores de similaridade em pares.

from sklearn.metrics.pairwise import cosine_similarity

flatten = []
for intent in training_phrases_with_embeddings:
        for phrase in training_phrases_with_embeddings[intent]:
            flatten.append((intent, phrase, training_phrases_with_embeddings[intent][phrase]))

data = []
for i in range(len(flatten)):
    for j in range(i+1, len(flatten)):
        intent_1 = flatten[i][0]
        phrase_1 = flatten[i][1]
        embedd_1 = flatten[i][2]
        intent_2 = flatten[j][0]
        phrase_2 = flatten[j][1]
        embedd_2 = flatten[j][2]
        similarity = cosine_similarity([embedd_1], [embedd_2])[0][0]
        record = [intent_1, phrase_1, intent_2, phrase_2, similarity]
        data.append(record)

similarity_df = pd.DataFrame(data,
    columns=["Intent A", "Phrase A", "Intent B", "Phrase B", "Similarity"])

O código a seguir mostra exemplos de registros de semelhança:

different_intent = similarity_df['Intent A'] != similarity_df['Intent B']
display(similarity_df[different_intent].sort_values('Similarity',
ascending=False).head(5))

O snippet de código a seguir mostra as frases de treinamento mais semelhantes que não pertencem à mesma intent:

As frases de treinamento mais semelhantes que não pertencem à mesma intent

Frases com intents diferentes que tenham alto valor de semelhança podem ser confusas para o agente do Dialogflow, além de direcionar a entrada do usuário para a intent errada.

Como mensurar a coesão e a separação de intents

O código a seguir calcula o valor de coesão de cada intent, conforme descrito na seção Abordagem.

same_intent = similarity_df['Intent A'] == similarity_df['Intent B']
cohesion_df = pd.DataFrame(similarity_df[different_intent].groupby('Intent A', as_index=False)['Similarity'].mean())
cohesion_df.columns = ['Intent', 'Cohesion']
display(cohesion_df)

O resultado é um valor de coesão para cada intent:

Como calcular o valor de coesão de cada intent

O código a seguir calcula a separação em pares entre intents, conforme descrito na seção Abordagem.

different_intent = similarity_df['Intent A'] != similarity_df['Intent B']
separation_df = pd.DataFrame(similarity_df[different_intent].groupby(['Intent A', 'Intent B'], as_index=False)['Similarity'].mean())
separation_df['Separation'] = 1 - separation_df['Similarity']
del separation_df['Similarity']
display(separation_df.sort_values('Separation'))

O resultado é a separação em pares entre intents:

Como calcular a separação em pares entre intents

Outras melhorias

Para melhorar a qualidade das frases de treinamento de suas intents, pense em adotar as seguintes abordagens:

  • Encontre as frases com alta semelhança em intents diferentes e altere-as ou remova-as.
  • Encontre as frases com mais semelhantes que pertençam a intentes diferentes.
  • Adicione mais frases de treinamento às intents com baixa coesão e investigue as frases de treinamento em intents com baixa separação.

Como limpar

  1. No Console do Cloud, acesse a página Gerenciar recursos:

    Acessar a página "Gerenciar recursos"

  2. Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir .
  3. Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.

A seguir