Como construir um bot de análise de sentimento de e-mail: um tutorial de PNL
Publicados: 2022-03-11As tecnologias de processamento de linguagem natural tornaram-se bastante sofisticadas nos últimos anos. De gigantes da tecnologia a amadores, muitos estão correndo para construir interfaces ricas que possam analisar, entender e responder à linguagem natural. Alexa da Amazon, Cortana da Microsoft, Google Home do Google e Siri da Apple visam mudar a maneira como interagimos com os computadores.
A análise de sentimentos, um subcampo do processamento de linguagem natural, consiste em técnicas que determinam o tom de um texto ou fala. Hoje, com aprendizado de máquina e grandes quantidades de dados coletados de mídias sociais e sites de revisão, podemos treinar modelos para identificar o sentimento de uma passagem de linguagem natural com precisão razoável.
Neste tutorial, você aprenderá como criar um bot que pode analisar o sentimento dos e-mails que recebe e notificá-lo sobre e-mails que podem exigir sua atenção imediatamente.
Analisando o sentimento em e-mails
O bot será construído usando uma mistura de desenvolvimento Java e Python. Os dois processos se comunicarão usando o Thrift. Se você não estiver familiarizado com um ou ambos os idiomas, ainda poderá ler, pois os conceitos fundamentais deste artigo também valerão para outros idiomas.
Para determinar se um email precisa de sua atenção, o bot o analisará e determinará se há um tom negativo forte. Em seguida, ele enviará um alerta de texto, se necessário.
Usaremos o Sendgrid para se conectar à nossa caixa de correio e o Twilio para enviar alertas de texto.
Análise de sentimentos: um problema enganosamente simples
Existem palavras que associamos a emoções positivas, como amor, alegria e prazer. E há palavras que associamos a emoções negativas, como ódio, tristeza e dor. Por que não treinar o modelo para reconhecer essas palavras e contar a frequência relativa e a força de cada palavra positiva e negativa?
Bem, há alguns problemas com isso.
Primeiro, há um problema de negação. Por exemplo, uma frase como “O pêssego não é ruim” implica uma emoção positiva usando uma palavra que geralmente associamos a ser negativa. Um modelo simples de saco de palavras não será capaz de reconhecer a negação nesta frase.
Além disso, sentimentos mistos provam ser mais um problema com a análise de sentimentos ingênuos. Por exemplo, uma frase como “O pêssego não é ruim, mas a maçã é realmente terrível” contém sentimentos mistos de intensidades mistas que interagem entre si. Uma abordagem simples não será capaz de resolver os sentimentos combinados, a intensidade diferente ou as interações entre os sentimentos.
Análise de sentimento usando rede tensora neural recursiva
A biblioteca Stanford Natural Language Processing para análise de sentimentos resolve esses problemas usando uma Rede Tensor Neural Recursiva (RNTN).
O algoritmo RNTN primeiro divide uma frase em palavras individuais. Em seguida, constrói uma rede neural onde os nós são as palavras individuais. Finalmente, uma camada tensora é adicionada para que o modelo possa se ajustar adequadamente às interações entre as palavras e frases.
Você pode encontrar uma demonstração visual do algoritmo em seu site oficial.
O grupo Stanford NLP treinou a Recursive Neural Tensor Network usando revisões de filmes IMDB marcadas manualmente e descobriu que seu modelo é capaz de prever sentimentos com precisão muito boa.
Bot que recebe e-mails
A primeira coisa que você quer fazer é configurar a integração de e-mail para que os dados possam ser canalizados para o seu bot.
Há muitas maneiras de fazer isso, mas para simplificar, vamos configurar um servidor web simples e usar o gancho de análise de entrada do Sendgrid para enviar e-mails para o servidor. Podemos encaminhar e-mails para o endereço de análise de entrada do Sendgrid. O Sendgrid enviará uma solicitação POST para nosso servidor web e poderemos processar os dados por meio de nosso servidor.
Para construir o servidor, usaremos o Flask, um framework web simples para Python.
Além de construir o servidor web, queremos conectar o serviço web a um domínio. Por brevidade, vamos pular escrever sobre isso no artigo. No entanto, você pode ler mais sobre isso aqui.
Construir um servidor web no Flask é incrivelmente simples.
Basta criar um app.py
e adicioná-lo ao arquivo:
from flask import Flask, request import datetime app = Flask(__name__) @app.route('/analyze', methods=['POST']) def analyze(): with open('logfile.txt', 'a') as fp_log: fp_log.write('endpoint hit %s \n' % datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')) return "Got it" app.run(host='0.0.0.0')
Se implantarmos este aplicativo atrás de um nome de domínio e atingirmos o endpoint “/analyze”, você deverá ver algo assim:
> >> requests.post('http://sentiments.shanglunwang.com:5000/analyze').text 'Got it'
Em seguida, queremos enviar e-mails para esse endpoint.
Você pode encontrar mais documentação aqui, mas essencialmente deseja configurar o Sendgrid para ser seu processador de e-mail e fazer com que o Sendgrid encaminhe os e-mails para o nosso servidor web.
Aqui está minha configuração no Sendgrid. Isso encaminhará e-mails para @sentibot.shanglunwang.com
como solicitações POST para “http://sentiments.shanglunwang.com/analyze”:
Você pode usar qualquer outro serviço que suporte o envio de e-mails de entrada por webhooks.
Depois de configurar tudo, tente enviar um e-mail para o seu endereço Sendgrid, você deve ver algo assim nos logs:
endpoint hit 2017-05-25 14:35:46
Isso é ótimo! Agora você tem um bot que pode receber e-mails. Isso é metade do que estamos tentando fazer.
Agora, você deseja dar a esse bot a capacidade de analisar sentimentos em e-mails.
Análise de sentimento de e-mail com Stanford NLP
Como a biblioteca Stanford NLP é escrita em Java, queremos construir o mecanismo de análise em Java.
Vamos começar baixando a biblioteca e os modelos de Stanford NLP no Maven. Crie um novo projeto Java, adicione o seguinte às dependências do Maven e importe:
<dependency> <groupId>edu.stanford.nlp</groupId> <artifactId>stanford-corenlp</artifactId> <version>3.6.0</version> </dependency>
O mecanismo de análise de sentimento do Stanford NLP pode ser acessado especificando o anotador de sentimento no código de inicialização do pipeline. A anotação pode então ser recuperada como uma estrutura em árvore.
Para os propósitos deste tutorial, queremos apenas saber o sentimento geral de uma sentença, para que não precisemos analisar a árvore. Nós só precisamos olhar para o nó base.
Isso torna o código principal relativamente simples:
package seanwang; import edu.stanford.nlp.pipeline.*; import edu.stanford.nlp.util.CoreMap; import edu.stanford.nlp.ling.CoreAnnotations; import edu.stanford.nlp.sentiment.SentimentCoreAnnotations; import java.util.*; public class App { public static void main( String[] args ) { Properties pipelineProps = new Properties(); Properties tokenizerProps = new Properties(); pipelineProps.setProperty("annotators", "parse, sentiment"); pipelineProps.setProperty("parse.binaryTrees", "true"); pipelineProps.setProperty("enforceRequirements", "false"); tokenizerProps.setProperty("annotators", "tokenize ssplit"); StanfordCoreNLP tokenizer = new StanfordCoreNLP(tokenizerProps); StanfordCoreNLP pipeline = new StanfordCoreNLP(pipelineProps); String line = "Amazingly grateful beautiful friends are fulfilling an incredibly joyful accomplishment. What an truly terrible idea."; Annotation annotation = tokenizer.process(line); pipeline.annotate(annotation); // normal output for (CoreMap sentence : annotation.get(CoreAnnotations.SentencesAnnotation.class)) { String output = sentence.get(SentimentCoreAnnotations.SentimentClass.class); System.out.println(output); } } }
Tente algumas frases e você deverá ver as anotações apropriadas. Executando as saídas de código de exemplo:
Very Positive Negative
Integrando o bot e o mecanismo de análise
Portanto, temos um programa analisador de sentimentos escrito em Java e um bot de e-mail escrito em Python. Como fazer com que eles conversem entre si?
Existem muitas soluções possíveis para esse problema, mas aqui usaremos o Thrift. Vamos ativar o Sentiment Analyzer como um servidor Thrift e o bot de e-mail como um cliente Thrift.
Thrift é um gerador de código e um protocolo usado para permitir que dois aplicativos, geralmente escritos em linguagens diferentes, possam se comunicar entre si usando um protocolo definido. Equipes poliglotas usam o Thrift para construir redes de microsserviços para aproveitar o melhor de cada linguagem que usam.
Para usar o Thrift, precisaremos de duas coisas: um arquivo .thrift
para definir os terminais de serviço e um código gerado para fazer uso do protocolo definido no arquivo .proto
. Para o serviço do analisador, o sentiment.thrift
se parece com isso:
namespace java sentiment namespace py sentiment service SentimentAnalysisService { string sentimentAnalyze(1:string sentence), }
Podemos gerar código de cliente e servidor usando este arquivo .thrift. Corre:

thrift-0.10.0.exe --gen py sentiment.thrift thrift-0.10.0.exe --gen java sentiment.thrift
Nota: Gerei o código em uma máquina Windows. Você desejará usar o caminho apropriado para o executável do Thrift em seu ambiente.
Agora, vamos fazer as alterações apropriadas no mecanismo de análise para criar um servidor. Seu programa Java deve ficar assim:
SentimentHandler.java
package seanwang; public class SentimentHandler implements SentimentAnalysisService.Iface { SentimentAnalyzer analyzer; SentimentHandler() { analyzer = new SentimentAnalyzer(); } public String sentimentAnalyze(String sentence) { System.out.println("got: " + sentence); return analyzer.analyze(sentence); } }
Este manipulador é onde recebemos a solicitação de análise pelo protocolo Thrift.
SentimentAnalyzer.java
package seanwang; // ... public class SentimentAnalyzer { StanfordCoreNLP tokenizer; StanfordCoreNLP pipeline; public SentimentAnalyzer() { Properties pipelineProps = new Properties(); Properties tokenizerProps = new Properties(); pipelineProps.setProperty("annotators", "parse, sentiment"); pipelineProps.setProperty("parse.binaryTrees", "true"); pipelineProps.setProperty("enforceRequirements", "false"); tokenizerProps.setProperty("annotators", "tokenize ssplit"); tokenizer = new StanfordCoreNLP(tokenizerProps); pipeline = new StanfordCoreNLP(pipelineProps); } public String analyze(String line) { Annotation annotation = tokenizer.process(line); pipeline.annotate(annotation); String output = ""; for (CoreMap sentence : annotation.get(CoreAnnotations.SentencesAnnotation.class)) { output += sentence.get(SentimentCoreAnnotations.SentimentClass.class); output += "\n"; } return output; } }
O Analyzer usa a biblioteca Stanford NLP para determinar o sentimento do texto e produz uma string contendo as anotações de sentimento para cada frase no texto.
SentimentServer.java
package seanwang; // ... public class SentimentServer { public static SentimentHandler handler; public static SentimentAnalysisService.Processor processor; public static void main(String [] args) { try { handler = new SentimentHandler(); processor = new SentimentAnalysisService.Processor(handler); Runnable simple = new Runnable() { public void run() { simple(processor); } }; new Thread(simple).start(); } catch (Exception x) { x.printStackTrace(); } } public static void simple(SentimentAnalysisService.Processor processor) { try { TServerTransport serverTransport = new TServerSocket(9090); TServer server = new TSimpleServer(new Args(serverTransport).processor(processor)); System.out.println("Starting the simple server..."); server.serve(); } catch (Exception e) { e.printStackTrace(); } } }
Observe que não incluí o arquivo SentimentAnalysisService.java
aqui, pois é um arquivo gerado. Você vai querer colocar o código gerado em um lugar onde o resto do seu código possa acessá-lo.
Agora que temos o servidor ativo, vamos escrever um cliente Python para usar o servidor.
cliente.py
from sentiment import SentimentAnalysisService from thrift.transport import TSocket from thrift.transport import TTransport from thrift.protocol import TBinaryProtocol class SentimentClient: def __init__(self, server='localhost', socket=9090): transport = TSocket.TSocket(server, socket) transport = TTransport.TBufferedTransport(transport) protocol = TBinaryProtocol.TBinaryProtocol(transport) self.transport = transport self.client = SentimentAnalysisService.Client(protocol) self.transport.open() def __del__(self): self.transport.close() def analyze(self, sentence): return self.client.sentimentAnalyze(sentence) if __name__ == '__main__': client = SentimentClient() print(client.analyze('An amazingly wonderful sentence'))
Execute isso e você deverá ver:
Very Positive
Excelente! Agora que temos o servidor rodando e conversando com o cliente, vamos integrá-lo ao bot de email instanciando um cliente e canalizando o email para ele.
import client # ... @app.route('/analyze', methods=['POST']) def analyze(): sentiment_client = client.SentimentClient() with open('logfile.txt', 'a') as fp_log: fp_log.write(str(request.form.get('text'))) fp_log.write(request.form.get('text')) fp_log.write(sentiment_client.analyze(request.form.get('text'))) return "Got it"
Agora implante seu serviço Java na mesma máquina em que você está executando o servidor web, inicie o serviço e reinicie o aplicativo. Envie um email para o bot com uma frase de teste e você deverá ver algo assim no arquivo de log:
Amazingly wonderfully positive and beautiful sentence. Very Positive
Analisando o e-mail
Tudo bem! Agora temos um bot de e-mail capaz de realizar análises de sentimentos! Podemos enviar um e-mail e receber uma tag de sentimento para cada frase que enviamos. Agora, vamos explorar como podemos tornar a inteligência acionável.
Para simplificar, vamos nos concentrar em e-mails onde há uma alta concentração de frases negativas e muito negativas. Vamos usar um sistema de pontuação simples e dizer que, se um e-mail contiver mais de 75% de frases de sentimento negativo, marcaremos isso como um e-mail de alarme em potencial que pode exigir uma resposta imediata. Vamos implementar a lógica de pontuação na rota de análise:
@app.route('/analyze', methods=['POST']) def analyze(): text = str(request.form.get('text')) sentiment_client = client.SentimentClient() text.replace('\n', '') # remove all new lines sentences = text.rstrip('.').split('.') # remove the last period before splitting negative_sentences = [ sentence for sentence in sentences if sentiment_client.analyze(sentence).rstrip() in ['Negative', 'Very negative'] # remove newline char ] urgent = len(negative_sentences) / len(sentences) > 0.75 with open('logfile.txt', 'a') as fp_log: fp_log.write("Received: %s" % (request.form.get('text'))) fp_log.write("urgent = %s" % (str(urgent))) return "Got it"
O código acima faz algumas suposições, mas funcionará para fins de demonstração. Envie alguns e-mails para o seu bot e você deverá ver a análise de e-mail nos logs:
Received: Here is a test for the system. This is supposed to be a non-urgent request. It's very good! For the most part this is positive or neutral. Great things are happening! urgent = False Received: This is an urgent request. Everything is truly awful. This is a disaster. People hate this tasteless mail. urgent = True
Enviando um Alerta
Estamos quase terminando!
Criamos um bot de e-mail capaz de receber e-mails, realizar análises de sentimentos e determinar se um e-mail requer atenção imediata. Agora, basta enviar um alerta de texto quando um e-mail for particularmente negativo.
Usaremos o Twilio para enviar um alerta de texto. Sua API Python, que está documentada aqui, é bastante direta. Vamos modificar a rota de análise para enviar uma solicitação quando receber uma solicitação urgente.
def send_message(body): twilio_client.messages.create( to=on_call, from_=os.getenv('TWILIO_PHONE_NUMBER'), body=body ) app = Flask(__name__) @app.route('/analyze', methods=['POST']) def analyze(): text = str(request.form.get('text')) sentiment_client = client.SentimentClient() text.replace('\n', '') # remove all new lines sentences = text.rstrip('.').split('.') # remove the last period before splitting negative_sentences = [ sentence for sentence in sentences if sentiment_client.analyze(sentence).rstrip() in ['Negative', 'Very negative'] # remove newline char ] urgent = len(negative_sentences) / len(sentences) > 0.75 if urgent: send_message('Highly negative email received. Please take action') with open('logfile.txt', 'a') as fp_log: fp_log.write("Received: " % request.form.get('text')) fp_log.write("urgent = %s" % (str(urgent))) fp_log.write("\n") return "Got it"
Você precisará definir suas variáveis de ambiente para as credenciais da sua conta Twilio e definir o número de plantão para um telefone que você possa verificar. Depois de fazer isso, envie um e-mail para o endpoint de análise e você verá um texto sendo enviado para o número de telefone em questão.
E terminamos!
Processamento de linguagem natural facilitado com Stanford NLP
Neste artigo, você aprendeu como criar um bot de análise de sentimento de e-mail usando a biblioteca Stanford NLP. A biblioteca ajuda a abstrair todos os detalhes minuciosos do processamento de linguagem natural e permite que você a use como um bloco de construção para seus aplicativos de PNL.
Espero que este post tenha demonstrado uma das muitas aplicações potenciais surpreendentes da análise de sentimentos, e que isso inspire você a construir um aplicativo de PNL próprio.
Você pode encontrar o código para o bot de análise de sentimento de email neste tutorial de PNL no GitHub.