電子メール感情分析ボットを構築する方法:NLPチュートリアル

公開: 2022-03-11

自然言語処理技術は、過去数年間で非常に洗練されてきました。 ハイテクの巨人から愛好家まで、多くの人が自然言語を分析、理解、応答できる豊富なインターフェースを構築しようと急いでいます。 AmazonのAlexa、MicrosoftのCortana、GoogleのGoogle Home、AppleのSiriはすべて、コンピューターとのやり取りの方法を変えることを目的としています。

自然言語処理のサブフィールドである感情分析は、テキストまたはスピーチのトーンを決定する手法で構成されています。 今日、機械学習とソーシャルメディアやレビューサイトから収集された大量のデータを使用して、モデルをトレーニングし、自然言語のパッセージの感情をかなり正確に特定できます。

メール感情分析ボットチュートリアル

このチュートリアルでは、受信した電子メールの感情を分析し、すぐに注意が必要な可能性のある電子メールについて通知できるボットを構築する方法を学習します。

メールの感情を分析する

ボットは、JavaとPythonの開発を組み合わせて構築されます。 2つのプロセスは、Thriftを使用して相互に通信します。 これらの言語の一方または両方に精通していない場合でも、この記事の基本的な概念は他の言語にも当てはまるため、読み進めることができます。

電子メールに注意が必要かどうかを判断するために、ボットはそれを解析し、強い否定的なトーンがあるかどうかを判断します。 その後、必要に応じてテキストアラートを送信します。

Sendgridを使用してメールボックスに接続し、Twilioを使用してテキストアラートを送信します。

感情分析:一見単純な問題

愛、喜び、喜びなどのポジティブな感情に関連する言葉があります。 そして、憎しみ、悲しみ、痛みなどの否定的な感情に関連する言葉があります。 これらの単語を認識し、正と負の各単語の相対的な頻度と強さを数えるようにモデルをトレーニングしてみませんか?

さて、それにはいくつかの問題があります。

まず、否定の問題があります。 たとえば、「桃は悪くない」のような文は、私たちが最も頻繁に否定的であると関連付ける単語を使用した肯定的な感情を意味します。 単純なbag-of-wordsモデルでは、この文の否定を認識できません。

さらに、混合感情は、素朴な感情分析のさらに別の問題であることがわかります。 たとえば、「桃は悪くないが、リンゴは本当にひどい」のような文には、互いに相互作用する混合強度の混合感情が含まれています。 単純なアプローチでは、組み合わされた感情、異なる強度、または感情間の相互作用を解決することはできません。

再帰的ニューラルテンソルネットワークを使用した感情分析

感情分析用のスタンフォード自然言語処理ライブラリは、再帰型ニューラルテンソルネットワーク(RNTN)を使用してこれらの問題を解決します。

文のRNTN

RNTNアルゴリズムは、最初に文を個々の単語に分割します。 次に、ノードが個々の単語であるニューラルネットワークを構築します。 最後に、テンソルレイヤーが追加され、モデルが単語とフレーズの間の相互作用を適切に調整できるようになります。

あなたは彼らの公式ウェブサイトでアルゴリズムの視覚的なデモンストレーションを見つけることができます。

スタンフォードNLPグループは、手動でタグ付けされたIMDBムービーレビューを使用して再帰型ニューラルテンソルネットワークをトレーニングし、モデルが非常に高い精度で感情を予測できることを発見しました。

メールを受信するボット

最初に実行したいのは、データをボットにパイプできるように電子メール統合を設定することです。

これを実現する方法はたくさんありますが、簡単にするために、単純なWebサーバーをセットアップし、Sendgridのインバウンド解析フックを使用して電子メールをサーバーにパイプします。 Sendgridのインバウンド解析アドレスにメールを転送できます。 次に、SendgridはPOSTリクエストをWebサーバーに送信し、サーバーを介してデータを処理できるようになります。

サーバーを構築するには、Python用のシンプルなWebフレームワークであるFlaskを使用します。

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

このアプリをドメイン名の背後にデプロイし、エンドポイント「/ analysis」エンドポイントに到達すると、次のように表示されます。

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

次に、このエンドポイントにメールを送信します。

ここでより多くのドキュメントを見つけることができますが、基本的にはSendgridを電子メールプロセッサとして設定し、Sendgridに電子メールをWebサーバーに転送させたいと考えています。

これがSendgridでの私の設定です。 これにより、「http://sentiments.shanglunwang.com/analyze」へのPOSTリクエストとして、メールが@sentibot.shanglunwang.comに転送されます。

Sendgrid構成

Webhookを介した受信メールの送信をサポートする他のサービスを使用できます。

すべてを設定したら、Sendgridアドレスにメールを送信してみてください。ログに次のようなものが表示されます。

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

それは素晴らしいことです! これで、メールを受信できるボットができました。 それは私たちがやろうとしていることの半分です。

ここで、このボットに電子メールの感情を分析する機能を提供したいとします。

スタンフォードNLPによる電子メール感情分析

スタンフォードNLPライブラリはJavaで記述されているため、Javaで分析エンジンを構築する必要があります。

まず、MavenにStanfordNLPライブラリとモデルをダウンロードします。 新しい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を使用します。 Sentiment AnalyzerをThriftサーバーとして起動し、電子メールボットをThriftクライアントとして起動します。

Thriftは、コードジェネレーターであり、多くの場合異なる言語で記述された2つのアプリケーションが、定義されたプロトコルを使用して相互に通信できるようにするために使用されるプロトコルです。 Polyglotチームは、Thriftを使用してマイクロサービスのネットワークを構築し、使用する各言語の長所を活用します。

Thriftを使用するには、サービスエンドポイントを定義するための.thriftファイルと、 .protoファイルで定義されたプロトコルを使用するために生成されたコードの2つが必要になります。 アナライザーサービスの場合、 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プロトコルを介して分析要求を受け取る場所です。

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

アナライザーは、Stanford 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クライアントを作成しましょう。

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

これを実行すると、次のように表示されます。

 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"

次に、Webサーバーを実行しているのと同じマシンにJavaサービスをデプロイし、サービスを開始して、アプリを再起動します。 テスト文を含む電子メールをボットに送信すると、ログファイルに次のようなものが表示されます。

 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"

上記のコードはいくつかの仮定をしていますが、デモンストレーションの目的で機能します。 ボットに2通のメールを送信すると、ログにメール分析が表示されます。

 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を使用してテキストアラートを送信します。 ここに記載されているPythonAPIは、非常に単純です。 緊急リクエストを受信したときにリクエストを送信するように分析ルートを変更してみましょう。

 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で自然言語処理が簡単に

この記事では、StanfordNLPライブラリを使用して電子メール感情分析ボットを構築する方法を学びました。 このライブラリは、自然言語処理の本質的な詳細をすべて抽象化するのに役立ち、NLPアプリケーションの構成要素として使用できるようにします。

この投稿が感情分析の多くの驚くべき潜在的なアプリケーションの1つを示し、これがあなた自身のNLPアプリケーションを構築するようにあなたを刺激することを願っています。

メール感情分析ボットのコードは、GitHubのこのNLPチュートリアルから見つけることができます。

関連:感情分析の精度の4つの落とし穴