Cómo Crear un Bot de Análisis de Sentimientos de Correo Electrónico: Un Tutorial de NLP.

Las tecnologías de procesamiento del lenguaje natural se han vuelto bastante sofisticadas en los últimos años. Desde gigantes tecnológicos hasta aficionados, muchos se apresuran a construir interfaces ricas que puedan analizar, comprender y responder al lenguaje natural. La Alexa de Amazon, la Cortana de Microsoft, la Google Home de Google y la Siri de Apple apuntan a cambiar la forma en que interactuamos con las computadoras.
El análisis del sentimiento, un subcampo del procesamiento del lenguaje natural, consiste en técnicas que determinan el tono de un texto o discurso. Hoy en día, con el aprendizaje automático y la gran cantidad de datos recopilados de las redes sociales y los sitios de revisión, podemos entrenar modelos para identificar el sentimiento de un pasaje en lenguaje natural con una precisión razonable.
Tutorial de Análisis de Sentimiento de Bots de Emails
En este tutorial, aprenderá cómo puede crear un bot que pueda analizar el sentimiento de los correos electrónicos que recibe y notificarle sobre los correos electrónicos que pueden requerir su atención de inmediato.

Análisis del sentimiento en correos electrónicos

El bot se construirá usando una mezcla de Java y Python. Los dos procesos se comunicarán entre ellos usando Thrift. Si no está familiarizado con uno o ambos idiomas, puede seguir leyendo ya que los conceptos fundamentales de este artículo también se aplicarán a otros idiomas.
Para determinar si un correo electrónico necesita su atención, el bot lo analizará y determinará si hay un fuerte tono negativo. Luego enviará una alerta de texto si es necesario.
Usaremos Sendgrid para conectarnos a nuestro buzón de correo y Twilio enviará alertas de texto. ## Análisis de sentimiento: un problema engañosamente simple
Hay palabras que asociamos con emociones positivas, como el amor, la alegría y el placer. Y, hay palabras que asociamos con emociones negativas, como el odio, la tristeza y el dolor. ¿Por qué no entrenar al modelo para reconocer estas palabras y contar la frecuencia relativa y la fuerza de cada palabra positiva y negativa?
Bueno, hay un par de problemas con eso.
Primero, hay un problema de negación. Por ejemplo, una oración como “El melocotón no es malo” implica una emoción positiva usando una palabra que a menudo asociamos con ser negativo. Un modelo simple de bolsa de palabras no podrá reconocer la negación en esta oración.
Además, los sentimientos mixtos demuestran ser otro problema con el análisis ingenuo de sentimientos. Por ejemplo, una oración como “El melocotón no es malo, pero la manzana es realmente terrible” contiene sentimientos mixtos de intensidades mixtas que interactúan entre sí. Un enfoque simple no podrá resolver los sentimientos combinados, la intensidad diferente o las interacciones entre los sentimientos.

Análisis de sentimiento mediante la red recursiva del tensor neuronal

La biblioteca Stanford Natural Language Processing para el análisis del sentimiento resuelve estos problemas usando una Red de tensor neuronal recursivo (RNTN).
RNTN en una oración
El algoritmo RNTN primero divide una oración en palabras individuales. Luego construye una red neuronal donde los nodos son las palabras individuales. Finalmente, se agrega una capa de tensor para que el modelo pueda ajustar adecuadamente las interacciones entre las palabras y frases.
Puede encontrar una demostración visual del algoritmo en su sitio web oficial.
El grupo Stanford NLP entrenó a la Red Recursiva del Tensor Neural usando evaluaciones de películas IMDB etiquetadas manualmente y descubrió que su modelo es capaz de predecir el sentimiento con muy buena precisión.

Bot que recibe correos electrónicos

Lo primero que debes hacer es configurar la integración del correo electrónico para que los datos puedan transferirse a tu bot.
Hay muchas maneras de lograr esto, pero por simplicidad, configuremos un servidor web simple y use el enganche de análisis de entrada de Sendgrid para canalizar correos electrónicos al servidor. Podemos reenviar correos electrónicos a la dirección de análisis entrante de Sendgrid. Sendgrid enviará una solicitud POST a nuestro servidor web, y luego podremos procesar los datos a través de nuestro servidor.
Para construir el servidor, usaremos Flask, un marco web simple para Python.
Además de construir el servidor web, querremos conectar el servicio web a un dominio. Para abreviar, omitiremos escribir sobre esto en el artículo. Sin embargo, puedes leer más sobre esto aquí.
Crear un servidor web en Flask es increíblemente simple.
Simplemente crea un app.py y agrégalo al archivo:
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')
Si implementamos esta aplicación detrás de un nombre de dominio y pulsamos el punto final “/analizar”, debería ver algo como esto:
>>> requests.post ('http://sentiments.shanglunwang.com:5000/analyze') .text
'Lo tengo'

A continuación, queremos enviar correos electrónicos a este punto final.
Puede encontrar más documentación aquí pero básicamente desea configurar Sendgrid para que sea su procesador de correo electrónico y que Sendgrid reenvíe los correos electrónicos a nuestro Servidor web.
Aquí está mi configuración en Sendgrid. Esto reenviará correos electrónicos a @sentibot.shanglunwang.com como solicitudes POST a “http://sentiments.shanglunwang.com/analyze”:
Configuración de SendGrid
Puedes usar cualquier otro servicio que admita el envío de correos entrantes a través de webhooks.
Después de configurar todo, intenta enviar un correo electrónico a tu dirección SendGrid. Deberías ver algo como esto en los registros:
endpoint hit 2017-05-25 14:35:46
¡Eso es genial! Ahora tienes un bot que puede recibir correos electrónicos. Eso es la mitad de lo que estamos tratando de hacer.
Ahora, deseas darle a este bot la capacidad de analizar sentimientos en correos electrónicos.

Email Sentiment Analysis con Stanford NLP

Dado que la biblioteca Stanford NLP está escrita en Java, querremos construir el motor de análisis en Java.
Comencemos descargando la biblioteca y los modelos de PNL de Stanford en Maven. Crea un nuevo proyecto Java, agrega lo siguiente a sus dependencias Maven e importe:
<dependency>
   <groupId>edu.stanford.nlp</groupId>
   <artifactId>stanford-corenlp</artifactId>
   <version>3.6.0</version>
</dependency>
Se puede acceder al motor de análisis de sentimiento de Stanford NLP especificando el anotador de sentimiento en el código de inicialización de la tubería. La anotación se puede recuperar como una estructura de árbol.
Para los fines de este tutorial, solo queremos saber el sentimiento general de una oración, por lo que no será necesario analizar el árbol. Solo tenemos que mirar el nodo base.
Esto hace que el código principal sea relativamente simple:
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 = "Los amigos hermosos increíblemente agradecidos están cumpliendo un logro increíblemente alegre. ¡Qué idea verdaderamente terrible!";
        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);
        }
    }
}
Prueba algunas oraciones y deberías ver las anotaciones apropiadas. Ejecutando los resultados del código de ejemplo:
Muy positivo
Negativo

Integrando el Bot y el Motor de Análisis

Así que tenemos un programa analizador de sentimientos escrito en Java y un bot de correo electrónico escrito en Python. ¿Cómo hacemos que hablen entre ellos?
Hay muchas soluciones posibles para este problema, pero aquí utilizaremos Thrift. Activaremos el Analizador de Sentimientos como un servidor Thrift y el bot de correo electrónico como un cliente Thrift.
Thrift es un generador de código y un protocolo utilizado para habilitar dos aplicaciones, a menudo escritas en diferentes idiomas, para poder comunicarse entre sí utilizando un protocolo definido. Los equipos de Polyglot usan Thrift para construir redes de microservicios para aprovechar lo mejor de cada idioma que usan.
Para usar Thrift, necesitaremos dos cosas: un archivo .thrift para definir los puntos finales del servicio y un código generado para hacer uso del protocolo definido en el archivo.proto. Para el servicio analizador, el sentiment.thrift se ve así:
namespace java sentiment
namespace py sentiment
 
service SentimentAnalysisService
{
        string sentimentAnalyze(1:string sentence),
}
Podemos generar código de cliente y servidor utilizando este archivo .thrift. Corre:
thrift-0.10.0.exe --gen py sentiment.thrift
thrift-0.10.0.exe --gen java sentiment.thrift
Nota: genere el código en una máquina con Windows. Deberá utilizar la ruta adecuada al ejecutable Thrift en su entorno.
Ahora, hagamos los cambios apropiados en el motor de análisis para crear un servidor. Su programa Java debería verse así:
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 manejador es donde recibimos la solicitud de análisis sobre el 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;
    }
}
El analizador utiliza la biblioteca de PNL de Stanford para determinar el sentimiento del texto y produce una cadena que contiene las anotaciones de sentimiento para cada oración en el 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();
        }
    }
}
Ten en cuenta que no incluí el archivo SentimentAnalysisService.java aquí porque es un archivo generado. Deberás poner el código generado en un lugar donde el resto de tu código puedas acceder a él.
Ahora que tenemos el servidor activo, escribamos un cliente de Python para usar el servidor.
client.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'))
Ejecuta esto y deberías ver:
Muy positivo
¡Estupendo! Ahora que tenemos el servidor ejecutándose y hablando con el cliente, integrámoslo con el robot de correo electrónico instanciando un cliente y conectándolo al correo electrónico.
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"
Ahora despliega tu servicio Java en la misma máquina donde estás ejecutando el servidor web, inicia el servicio y reinicia la aplicación. Envía un correo electrónico al bot con una oración de prueba y deberías ver algo como esto en el archivo de registro:
Increíblemente maravillosamente positiva y hermosa frase.
Muy positivo

Analizando el correo electrónico

¡Todo bien! ¡Ahora tenemos un bot de correo electrónico que puede realizar análisis de opinión! Podemos enviar un correo electrónico y recibir una etiqueta de opinión para cada oración que enviamos. Ahora, exploremos cómo podemos hacer que la inteligencia sea procesable.
Para mantener las cosas simples, centrémonos en los correos electrónicos donde hay una alta concentración de oraciones negativas y muy negativas. Usemos un sistema de puntuación simple y digamos que si un correo electrónico contiene más del 75% de oraciones de sentimiento negativo, lo marcaremos como un posible correo electrónico de alarma que puede requerir una respuesta inmediata. Implementemos la lógica de puntuación en la ruta de análisis:
@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"
El código anterior hace algunas suposiciones, pero funcionará con fines de demostración. Envía un par de correos electrónicos a tu bot y deberías ver el análisis del correo electrónico en los registros:
Recibido: Aquí hay una prueba para el sistema. Se supone que es una solicitud no urgente.
¡Es muy bueno! En su mayor parte esto es positivo o neutral. Grandes cosas
están sucediendo!
urgente = Falso
 
Recibido: esta es una solicitud urgente. Todo es realmente horrible Esto es un desastre.
La gente odia este correo insípido.
urgente = True

Envío de una alerta

¡Ya casi hemos terminado!
Hemos creado un bot de correo electrónico que puede recibir correos electrónicos, realizar análisis de sentimientos y determinar si un correo electrónico requiere atención inmediata. Ahora, solo tenemos que enviar una alerta de texto cuando un correo electrónico es particularmente negativo.
Utilizaremos Twilio para enviar una alerta de texto. Su API de Python, que está documentada aquí, es bastante sencilla. Modifiquemos la ruta de análisis para enviar una solicitud cuando reciba una solicitud 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"
Deberás configurar las variables de entorno en sus credenciales de cuenta de Twilio y establecer el número de llamada en un teléfono que pueda verificar. Una vez que hayas hecho eso, envía un correo electrónico al punto final del análisis y verás un mensaje de texto enviado al número de teléfono en cuestión.
¡Y terminamos!

Procesamiento de lenguaje natural simplificado con Stanford NLP

En este artículo, aprendiste a construir un bot de análisis de sentimiento de correo electrónico utilizando la biblioteca Stanford NLP. La biblioteca ayuda a abstraer todos los detalles esenciales del procesamiento del lenguaje natural y te permite usarlo como un bloque de construcción para sus aplicaciones NLP.
Espero que esta publicación haya demostrado una de las muchas aplicaciones potenciales increíbles del análisis de sentimientos, y que esto te inspire a crear tu propia aplicación PNL.
Este articulo fue escrito por Shanglun Wang. Originalmente publicado en Toptal.

Comentarios

Entradas populares