如何構建電子郵件情緒分析機器人: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 教程中找到電子郵件情緒分析機器人的代碼。

相關:情緒分析準確性的四個陷阱