如何构建电子邮件情绪分析机器人:NLP 教程

已发表: 2022-03-11

在过去的几年里,自然语言处理技术已经变得相当复杂。 从科技巨头到爱好者,许多人都在争先恐后地构建可以分析、理解和响应自然语言的丰富界面。 亚马逊的 Alexa、微软的 Cortana、谷歌的 Google Home 和苹果的 Siri 都旨在改变我们与计算机交互的方式。

情感分析是自然语言处理的一个子领域,由确定文本或语音语气的技术组成。 今天,通过机器学习以及从社交媒体和评论网站收集的大量数据,我们可以训练模型以相当准确的方式识别自然语言段落的情绪。

电子邮件情绪分析机器人教程

在本教程中,您将学习如何构建一个机器人来分析它收到的电子邮件的情绪,并通知您可能需要立即注意的电子邮件。

分析电子邮件中的情绪

该机器人将使用 Java 和 Python 开发的混合构建。 这两个进程将使用 Thrift 相互通信。 如果您不熟悉其中一种或两种语言,您仍然可以继续阅读,因为本文的基本概念也适用于其他语言。

为了确定一封电子邮件是否需要您的注意,机器人将对其进行解析并确定是否存在强烈的负面语气。 然后,如果需要,它将发送文本警报。

我们将使用 Sendgrid 连接到我们的邮箱并使用 Twilio 发送文本警报。

情绪分析:一个看似简单的问题

有些词我们与积极的情绪相关联,例如爱、快乐和快乐。 而且,有些词与负面情绪相关联,例如仇恨、悲伤和痛苦。 为什么不训练模型来识别这些单词并计算每个正负单词的相对频率和强度呢?

嗯,有几个问题。

首先,有一个否定的问题。 例如,像“The peach is not bad”这样的句子使用我们最常联想到消极的词来暗示积极的情绪。 一个简单的词袋模型将无法识别这句话中的否定。

此外,混合情绪被证明是幼稚情绪分析的另一个问题。 例如,像“桃子还不错,但苹果真的很糟糕”这样的句子包含了相互影响的混合强度的混合情绪。 简单的方法将无法解决组合的情绪、不同的强度或情绪之间的相互作用。

使用递归神经张量网络进行情感分析

用于情感分析的斯坦福自然语言处理库使用递归神经张量网络 (RNTN) 解决了这些问题。

RNTN上的一句话

RNTN 算法首先将一个句子分成单个单词。 然后它构建一个神经网络,其中节点是单个单词。 最后,添加了一个张量层,以便模型可以针对单词和短语之间的交互进行适当的调整。

您可以在他们的官方网站上找到该算法的可视化演示。

斯坦福 NLP 小组使用手动标记的 IMDB 电影评论训练了递归神经张量网络,发现他们的模型能够非常准确地预测情绪。

接收电子邮件的机器人

您要做的第一件事是设置电子邮件集成,以便将数据通过管道传输到您的机器人。

有很多方法可以做到这一点,但为了简单起见,让我们设置一个简单的 Web 服务器并使用 Sendgrid 的入站解析挂钩将电子邮件传送到服务器。 我们可以将电子邮件转发到 Sendgrid 的入站解析地址。 然后 Sendgrid 将向我们的 Web 服务器发送一个 POST 请求,然后我们将能够通过我们的服务器处理数据。

为了构建服务器,我们将使用 Flask,一个用于 Python 的简单 Web 框架。

除了构建 Web 服务器之外,我们还希望将 Web 服务连接到域。 为简洁起见,我们将跳过本文中有关此的内容。 但是,您可以在此处阅读有关它的更多信息。

在 Flask 中构建 Web 服务器非常简单。

只需创建一个app.py并将其添加到文件中:

 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')

如果我们将此应用程序部署在域名后面并点击端点“/analyze”端点,您应该会看到如下内容:

 > >> requests.post('http://sentiments.shanglunwang.com:5000/analyze').text 'Got it'

接下来,我们要向此端点发送电子邮件。

您可以在此处找到更多文档,但您基本上希望将 Sendgrid 设置为您的电子邮件处理器,并让 Sendgrid 将电子邮件转发到我们的 Web 服务器。

这是我在 Sendgrid 上的设置。 这会将电子邮件转发到@sentibot.shanglunwang.com ,作为对“http://sentiments.shanglunwang.com/analyze”的 POST 请求:

发送网格配置

您可以使用任何其他支持通过 webhook 发送入站电子邮件的服务。

设置完所有内容后,尝试向您的 Sendgrid 地址发送电子邮件,您应该会在日志中看到如下内容:

 endpoint hit 2017-05-25 14:35:46

那太棒了! 您现在拥有一个能够接收电子邮件的机器人。 这是我们正在努力做的一半。

现在,您想让这个机器人能够分析电子邮件中的情绪。

斯坦福 NLP 的电子邮件情绪分析

由于斯坦福 NLP 库是用 Java 编写的,我们将希望用 Java 构建分析引擎。

让我们从在 Maven 中下载斯坦福 NLP 库和模型开始。 创建一个新的 Java 项目,将以下内容添加到您的 Maven 依赖项中,然后导入:

 <dependency> <groupId>edu.stanford.nlp</groupId> <artifactId>stanford-corenlp</artifactId> <version>3.6.0</version> </dependency>

可以通过在管道初始化代码中指定情感注释器来访问斯坦福 NLP 的情感分析引擎。 然后可以将注释检索为树结构。

出于本教程的目的,我们只想知道一个句子的一般情绪,所以我们不需要解析树。 我们只需要查看基本节点。

这使得主代码相对简单:

 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); } } }

尝试一些句子,您应该会看到相应的注释。 运行示例代码输出:

 Very Positive Negative

集成机器人和分析引擎

所以我们有一个用 Java 编写的情绪分析器程序和一个用 Python 编写的电子邮件机器人。 我们如何让他们互相交谈?

这个问题有很多可能的解决方案,但这里我们将使用 Thrift。 我们将启动情绪分析器作为 Thrift 服务器,并将电子邮件机器人作为 Thrift 客户端。

Thrift 是一种代码生成器和协议,用于使通常以不同语言编写的两个应用程序能够使用定义的协议相互通信。 Polyglot 团队使用 Thrift 构建微服务网络,以充分利用他们使用的每种语言。

要使用 Thrift,我们需要两件事:一个.thrift文件来定义服务端点,以及生成代码来使用.proto文件中定义的协议。 对于分析器服务, sentiment.thrift如下所示:

 namespace java sentiment namespace py sentiment service SentimentAnalysisService { string sentimentAnalyze(1:string sentence), }

我们可以使用这个 .thrift 文件生成客户端和服务器代码。 跑步:

 thrift-0.10.0.exe --gen py sentiment.thrift thrift-0.10.0.exe --gen java sentiment.thrift

注意:我在 Windows 机器上生成了代码。 您将需要在您的环境中使用 Thrift 可执行文件的适当路径。

现在,让我们对分析引擎进行适当的更改以创建服务器。 您的 Java 程序应如下所示:

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); } }

这个处理程序是我们通过 Thrift 协议接收分析请求的地方。

情绪分析器.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; } }

Analyzer 使用斯坦福 NLP 库来确定文本的情绪,并生成一个包含文本中每个句子的情绪注释的字符串。

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(); } } }

请注意,我没有在此处包含SentimentAnalysisService.java文件,因为它是一个生成的文件。 您需要将生成的代码放在其他代码可以访问的地方。

现在我们已经启动了服务器,让我们编写一个 Python 客户端来使用服务器。

客户端.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'))

运行这个,你应该看到:

 Very Positive

伟大的! 现在我们已经让服务器运行并与客户端通信,让我们通过实例化客户端并将电子邮件通过管道将其与电子邮件机器人集成。

 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"

现在将 Java 服务部署到运行 Web 服务器的同一台机器上,启动服务,然后重新启动应用程序。 向机器人发送一封带有测试语句的电子邮件,您应该在日志文件中看到如下内容:

 Amazingly wonderfully positive and beautiful sentence. Very Positive

分析电子邮件

好的! 现在我们有了一个能够执行情绪分析的电子邮件机器人! 我们可以发送一封电子邮件,并为我们发送的每个句子接收一个情绪标签。 现在,让我们探讨如何使情报具有可操作性。

为简单起见,让我们关注负面和非常负面句子高度集中的电子邮件。 让我们使用一个简单的评分系统,如果一封电子邮件包含超过 75% 的负面情绪句子,我们会将其标记为可能需要立即响应的潜在警报电子邮件。 让我们在分析路由中实现评分逻辑:

 @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"

上面的代码做了一些假设,但将用于演示目的。 向您的机器人发送几封电子邮件,您应该会在日志中看到电子邮件分析:

 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

发出警报

我们快完成了!

我们已经构建了一个电子邮件机器人,它能够接收电子邮件、执行情绪分析并确定电子邮件是否需要立即关注。 现在,我们只需要在电子邮件特别负面时发送文本警报。

我们将使用 Twilio 发送文本警报。 他们在此处记录的 Python API 非常简单。 让我们修改分析路由,使其在收到紧急请求时发出请求。

 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"

您需要将环境变量设置为您的 Twilio 帐户凭据,并将待命号码设置为您可以检查的电话。 完成此操作后,向分析端点发送一封电子邮件,您应该会看到一条文本正在发送到相关电话号码。

我们完成了!

斯坦福 NLP 让自然语言处理变得简单

在本文中,您学习了如何使用斯坦福 NLP 库构建电子邮件情绪分析机器人。 该库有助于抽象出自然语言处理的所有细节,并允许您将其用作 NLP 应用程序的构建块。

我希望这篇文章已经展示了情感分析的众多惊人的潜在应用之一,并且这会激发您构建自己的 NLP 应用程序。

您可以从 GitHub 上的此 NLP 教程中找到电子邮件情绪分析机器人的代码。

相关:情绪分析准确性的四个陷阱