Antes de tudo, vamos entender que o Processamento de Linguagem Pure (NLP) basicamente é uma área da Inteligência Synthetic que permite aos computadores interpretar e gerar linguagem humana.
É com base nisso que funcionam os chatbots com os quais já estamos acostumados, mas como eles são treinados para serem o que são?
Durante uma interação actual, é comum o chatbot apresentar mensagens como “Desculpe, não entendi, tente novamente” — e isso é completamente regular!
Mas o que vamos falar aqui é sobre o que meu usuário faz depois disso? pode haver muitos caminhos para definir como padrão de comportamento, além de analisar métricas como a nota de satisfação ou o último menu acessado, qual foi o impacto do “não entendimento” no comportamento do usuário? Ou ainda, por que ele abandonou a conversa naquele ponto da interação?”
Como curadores, parte do nosso trabalho consiste em analisar interação por interação — manualmente. Isso exige investigar o comportamento do usuário para encontrar padrões. Mas como fazer isso em uma base com 100 mil interações? Como estabelecer padrões sem viés?
É aqui que o NLP entra em ação, por meio do uso de algoritmos de aprendizado de máquina — como o Random Forest — que auxiliam na previsão de comportamento com base em dados históricos.
“O Random Forest é um algoritmo de machine studying que cria múltiplas árvores de decisão, a partir de subconjuntos dos dados de treinamento. Cada árvore aprende a prever comportamentos com base nos dados que recebe, e o conjunto das árvores — a “floresta” — combina essas previsões para aumentar a precisão last do modelo.” Fonte: ebaconline.com.br
Vamos aplicar esse conceito a um chatbot que responde sobre o standing de pedidos:
Dataset utilizado neste exemplo: random_forest_chatbot
Código completo: https://github.com/AneNegreiros/c-digo_previs-o_random_forest/blob/main/codigo_para_previsao.ipynb
No nosso estudo de caso vamos exportar uma base em excel e, assim que concluído pode ser carregado de volta com a versão atualizada.
import pandas as pd #Manipular dados em formato de tabela (knowledge body)
# scikit-learn: utilitários de machine studying
from sklearn.model_selection import train_test_split # separar os dados em treino e teste
from sklearn.feature_extraction.textual content import CountVectorizer #transformar texto em números (contagem de palavras)
from sklearn.ensemble import RandomForestClassifier #algoritmo de classificação Random Forest
from sklearn.preprocessing import LabelEncoder #converter rótulos (texto) em números
from sklearn.metrics import classification_report #gerar métricas de avaliação do modelo
# NLTK: processamento de linguagem pure
import nltk #baixar recursos e trabalhar com texto
from nltk.corpus import stopwords # lista de “stopwords” (palavras muito comuns)
nltk.obtain('stopwords') # Baixar stopwords em português: somente uma vez
stopwords_pt = stopwords.phrases('portuguese') # Aqui serve para ignorar e carregar palavras comuns em português que devem ser ignoradas (como “de”, “o”, “que”) durante o processamento de texto.
Em sua base em excel, crie uma estrutura com o caminho percorrido por cada usuário de acordo com o fluxo do chatbot — chamada aqui de Jornada Programada — com base em um identificador único (ID) e a também todo o texto sequencial na interação que podemos chamar de “DNA Texto DIgitado “.
Associe ao ID as frases ou palavras que o chatbot não compreendeu (campo NE — Não Entendido).
Make the most of a mesma base de dados e adicione uma nova coluna para categorizar manualmente uma amostra das interações. É recomendável classificar pelo menos 5% a ten% dos registros, com o objetivo de identificar padrões de comportamento após ocorrências de “não entendimento”.
No meu caso de uso, consegui identificar 3 padrões distintos. Esse conjunto de exemplos será utilizado como base de aprendizado para o modelo Random Forest. Nesta etapa, nomeei a nova coluna como “Jornada após NE (handbook)
Após os 3 passos, sua tabela deve ficar como a tabela abaixo:
A partir de agora vamos criar no python, uma nova coluna de informação que uni textos das tabelas em uma so, o que chamo de “texto completo” que combina as informações mais importantes, como o que foi digitado (DNA Texto Digitado) e a jornada programada. O campo “NE” só foi adicionado quando necessário, para identificar quando não houve entendimento sobre ATH. A combinação é feita com um delimitador “ | “ entre elas, o que facilita a leitura e manipulação posterior. Isso faz com que o modelo tenha mais riqueza e qualidade na compreensão de um texto quando combinado com outro:
Obs: é importante unir pelo menos 2 colunas de treinamento.
df["texto_completo"] = df["DNA Texto Digitado"].astype(str) + " | " + df["Jornada Programada"].astype(str)
df["texto_completo"] = df.apply(
lambda row: row["texto_completo"] + " | " + row["NE"]
if pd.notnull(row["NE"]) and row["NE"].strip() != ""
else row["texto_completo"],
axis=1
A visualização desta etapa do código é desta forma no Excel:
O que acontece aqui: O código divide o DataFrame em duas partes:
- df_treino: Contém as linhas em que a coluna “Jornada após NE (handbook)” possui valor (não nulo) e não é igual a um traço (“-”). Esses dados são usados para treinar o modelo, pois já têm uma “jornada” definida que criamos manualmente no passo 3.
- df_prever: Contém as linhas onde a coluna “Jornada após NE (handbook)” é nula ou contém um traço (“-”). Esses dados são usados para prever as jornadas, pois ainda não têm rótulo.
df_treino = df[df["Jornada após NE (manual)"].notna() & (df["Jornada após NE (manual)"].str.strip() != "-")].copy()
df_prever = df[df["Jornada após NE (manual)"].isna() | (df["Jornada após NE (manual)"].str.strip() == "-")].copy()
As interações de atendimento da coluna “Jornada após NE (handbook)” são transformada em valores numéricos considerado ‘texto de resposta correta’, pois os algoritmos de aprendizagem de máquina aprendem através dos números e não dos textos. E através disso, o modelo usa para treinar.
label_encoder = LabelEncoder()
df_treino["label"] = label_encoder.fit_transform(df_treino["Jornada depois do N1 sobre ATH"])
Além disso, também vamos remover da na base de treinamento classificação menor que 10 ocorrências pois o modelo pode não aprender bem sobre ela, por isso ela é removida. Isso ajuda a melhorar a qualidade do treinamento e evitar sobreajuste (overfitting). Mas este valor é de acordo com o seu cenário.
classe_counts = df_treino["label"].value_counts()
classes_validas = classe_counts[classe_counts >= 10].index
df_treino = df_treino[df_treino["label"].isin(classes_validas)]
Da mesma forma que demos valor aos textos da coluna “Jornada após NE (handbook)” vamos fazer o mesmo para a coluna “texto completo” que criamos anteriormente, ou seja, codifica os textos de entrada (inputs) que o modelo vai usar para aprender.
vectorizer = CountVectorizer(max_features=3000, ngram_range=(1, 3), stop_words=stopwords_pt)
X_treino = vectorizer.fit_transform(df_treino["texto_completo"])
y_treino = df_treino["label"]
O que acontece aqui:
- max_features=3000: Limita o número de palavras que o modelo considera, pegando apenas as 3000 mais relevantes.
- ngram_range=(1, 3): Considera não apenas palavras isoladas (unigrams), mas também combinações de 3 palavras (bigrams). Ex: “ quero falar com atendente”
- Os dados transformados são armazenados em X_treino (options) e as labels (rótulos) em y_treino (variável alvo)
Aqui é onde o modelo de aprendizado de máquina é treinado, afinal preparamos todo o materials para este momento. O modelo utilizado é o Random Forest, que é um algoritmo que conhecemos no inicio. Ele aprende a partir dos dados de treino (X_treino e y_treino), onde o modelo tenta prever a jornada de atendimento com base nas interações passadas.
clf = RandomForestClassifier(random_state=42, class_weight="balanced")
clf.match(X_treino, y_treino)
Obs: para visualizar esta arvore no python basta criar o seguinte comando:
from sklearn.tree import plot_tree
import matplotlib.pyplot as plt
plt.determine(figsize=(20, 10))
plot_tree(
clf.estimators_[5], # 5 árvores da floresta
feature_names=vectorizer.get_feature_names_out(),
class_names=label_encoder.classes_,
stuffed=True,
rounded=True,
max_depth=5 # Limita a profundidade para visualização
)
plt.present()
Após o treinamento, o modelo foi utilizado para prever as jornadas das interações que não possuíam rótulo (sem jornada definida). E depois, as previsões geradas foram e então inseridas de volta na base de dados.
Depois de treinar o modelo, usamos os dados incompletos (sem a jornada last handbook) para que o algoritmo preveja a provável sequência de ações do usuário. O texto de cada interação é transformado novamente em números (usando o vectorizer.remodel), e as previsões são feitas usando o modelo treinado (clf.predict). A previsão gerada é revertida para o formato unique de texto com o uso do LabelEncoder (através de inverse_transform), e a coluna “Previsão_Jornada_ATH” é preenchida com o resultado.
Prever:
X_prever = vectorizer.remodel(df_prever["texto_completo"])
y_pred = clf.predict(X_prever)
df_prever["Previsão_Jornada_após_NE"] = label_encoder.inverse_transform(y_pred)
Voltar para a base:
df_saida = df.copy()
df_saida.loc[df_prever.index, "Previsão_Jornada_ATH"] = df_prever["Previsão_Jornada_ATH"]
E por fim, para visualizar o arquivo last, contendo as previsões das jornadas de atendimento salvamos em Excel. Esse arquivo pode ser utilizado para análise da curadoria posterior.
df_saida.to_excel("Base_Teste_Preenchida_com_Prev.xlsx", index=False)
print("Arquivo salvo como Base_Teste_Preenchida_com_Prev.xlsx")
Com esse processo, é possível automatizar decisões, reduzir o abandono e melhorar a experiência do usuário no chatbot, com base em aprendizado prévio.
Espero que este conteúdo tenha ajudado!