
Nesse artigo eu vou explicar como utilizei a similaridade por cosseno para criar grupos de frases similares.
Foi me dado o seguinte problema: o chatbot não consegue entender todas as frases. Então, é preciso pegar todas as frases que não foram entendidas e agrupa-lás por contexto, para depois adicionar respostas para cada contexto e, assim, o chatbot conseguir ter uma resposta para essas novas situações.
Inicialmente, pensei: “até que fim chegou a hora de usar todo aquele conhecimento de machine learning, inteligência artificial e todas essas buzzwords de data science que eu estudado nos últimos anos!”
Comecei a ler bastante sobre algoritmos de similaridades de texto, achei alguns que tinham todas as buzzwords que eu estava querendo utilizar, mas não era tão simples de aplicar. O problema em si não era por ser difícil, mas sim por demorar mais para testar e só depois saber se realmente estava usando o algoritmo certo. Como estou trabalhando numa startup, o tempo ainda é um ponto crucial, então não dava simplesmente para gastar semanas testando algoritmos de machine learning para descobrir qual que realmente iria resolver meu problema. Por isso, eu resolvi segurar minha vontade de usar “rocket science” e pensei em coisas mais simples, fazer um MVP disso e botar para rodar em produção, se depois sentirmos necessidade de fazer algo mais robusto eu salvei os links dos algoritmos de machine learning… vai que né… kkkkk
Similaridade por cosseno
O algoritmo de similaridade por cosseno foi o que utilizei para comparar 2 frases. Ele compara as palavras de 2 textos, ignorando a ordem, e cria um vetor com as palavras mais repetidas. Depois é só aplicar a fórmula e “tá pronto”. Vamos a um exemplo:
Por favor, qual o valor da diária?
Por favor, qual o valor da bebida?
Quebramos em palavras, então teremos o seguinte vetor:
[“por”, “favor,”, “qual”, “o”, “valor”, “da”, “diária”, “bebida”]
Agora iremos contar quantas palavras do vetor tem em cada frase

Com isso criamos 2 vetores:
1: [1, 1, 1, 1, 1, 1, 1, 0]
2: [1, 1, 1, 1, 1, 1, 0, 1]
E agora é só aplicar a fórmula:
Parafraseando meu professor de cálculo: “Não preciso demonstrar o cálculo, porque claramente dá para ver que o resultado é…” uma similaridade de 0,85.
Realmente dá para ver que as frases são sintaticamente parecidas, mas semanticamente querem dizer coisas completamente diferentes. Por isso vamos para um passo bem importante antes de aplicar o cálculo de similaridade…
Limpeza das frases
A primeira vez que botei para rodar o script, eu vi vários grupos de frases parecidas, fiquei bem feliz. Mas, analisando profundamente, eu vi que a maioria não fazia sentido estarem juntas, porque, por mais que elas fossem bem parecidas, elas queriam dizer coisas diferentes e não teria como eu dar uma resposta similar para todo aquele grupo de frases. Foi preciso fazer um pré-processamento das frases para fazer uma limpeza nas frases e garantir um agrupamento melhor. Segue abaixo o código deste pré-processamento e aqui o link do gist.:
Lembrando que a ordem de execução de cada função é importante, pois se deixar para remover os acentos ou deixar as palavras em minúsculo só depois da etapa de filtragem da “wordExcluded” pode dar errado, já que “Por” é diferente de “por” que por sua vez é diferente de “pôr”. Nesse caso essas 2 últimas palavras ainda estariam na frase, mas não deveriam. Lembre-se também de adicionar palavras escritas certas e erradas porque você tem que comparar com o que o usuário escreve… então terá todas as formas possíveis e inimagináveis de escrever uma mesma palavra.
Agora limpando a frase antes de executar a similaridade por cosseno temos o seguinte resultado:
const str1 = 'Por favor, qual o valor da reserva?'const str2 = 'Por favor, qual o valor da diária?'const str3 = clearText(str1) // reservaconst str4 = clearText(str2) // diariaCosinesimilarity(str1, str2) // 0.857142857142857Cosinesimilarity(str3, str4) // 0.0
Depois de utilizar a limpeza do texto tivemos um similaridade de 0% entre as duas frases. Para o contexto em que estou trabalhando essas 2 frases realmente não são similares. Dependendo do contexto a palavra ‘valor’ pode fazer sentido, então retirando ela das “wordExcluded” temos uma similaridade de 50%. Por isso é preciso entender bem do contexto em que está trabalhando.
Qual a similaridade aceitável?
Assim que eu comecei a fazer os testes, eu usava uma similaridade de 80% e parecia que tinha um humano lendo todas a frases e agrupando-as. No entanto, muitas frases que tinham contexto similares, não estavam sendo agrupadas. Por isso, eu fui regredindo de 5% em 5% até chegar um ponto que já estava vindo muitas frases sem sentido e no meu contexto o valor ideal foi 60%. Com 50% eu tinha um grupo grande de frases que eram similares, mas ainda tinha muito “lixo” dentro dos grupos. Com 55% diminuiu bastante, mas foi com 60% que achei mais seguro de se trabalhar, pois eu tinha uma grande quantidade de frases por grupos e todas de alta qualidade. Com 80% era de uma qualidade incrível, mas poucas frases por grupo. Para um grupo ser válido e ele ir para produção, teria que ter pelo menos 5 frases por grupo e, nesse caso, era bem difícil ter um grupo com 5 ou mais frases.
Lembrando sempre esses valores são no contexto em que eu estou trabalhando, talvez em algum outro um valor aceitável seja 90% e em outro pode ser 50%.
Uma das coisas que me motivou a escrever esse post é que atualmente tudo é machine learning, inteligência artificial, data science, data mining e nem sempre precisamos fazer algo tão complexo para resolver um problema que “aparentemente” precisa de algo complexo. Não sei quem me falou isso mas se você está fazendo algo muito complexo para resolver um problema, provavelmente está errado. Além disso, também não valia a pena eu demorar semanas desenvolvendo essa feature usando o estado da arte da tecnologia sem saber se os meus usuários realmente vão parar pelo menos 1x por mês para ver as frases que não foram entendidas e adicionar uma resposta a elas. Se esse MVP der certo e depois eu precisar de algumas “buzzwords”, escreverei um novo post sobre como melhorar o algoritmo de similaridade de texto.
Se tiverem sugestões e/ou críticas comentem abaixo :)
