はじめに
はじめまして。オークファン入社歴三年目になりました、 @aucfan-0540 です。業務では主に AWS を用いたインフラ環境の構築等に携わっています。アドベントカレンダーを書くのは三回目です。今までは以下のような記事を書いてきました。
-
無限にホメてくれる VSCode Power Mode で自己肯定感を爆上げする
- とにかく誰かに褒めてもらいたいんだ、という熱い思いから制作しました。
-
Zabbix をバージョンアップしたらメンテナンスの設定を間違えて関係各所をざわつかせてしまった話
- 今でも Zabbix のアラームが鳴り響くと「また何かやっちゃいましたか?」となります。
今回は Amazon Comprehend + Lambda + Slack
でお手軽に Amazon Comprehend
を触りつつ、文章の感情分析をしてみたいと思います。
Amazon Comprehend とは
公式サイト様から引用させていただきます。
Amazon Comprehend は、機械学習を使用して、テキストからインサイトや関係性を発見するための自然言語処理 (NLP) サービスです。
上記の通り、文章を解析して以下のような情報を得ることができるサービスです。
- エンティティ解析
- 固有名詞や日付等の情報を取得します。
- キーフレーズ抽出
- 重要度の高いと思われる単語を抽出します。
- 感情分析
- 感情を読み取り、ポジティブ、ネガティブ、中立、複合のいずれかにカテゴライズします。
- 言語判定
- 用いられている言語を判定します。
今回は「感情分析」の機能を使用します。
構成図
ノートの片隅に書いた落書きみたいになってしまったので、文章でも軽く説明させていただきます。
ユーザーが Slack
へ文章を投稿すると、投稿内容の情報をもったイベントが LambdaFunction
へ送られます。LambdaFunction
はイベントの中から投稿された文章を取り出し Amazon Comprehend
を用いて解析を行った後、結果を Slack
に投稿します。
作成手順
※ 記事内の画像について、諸般の事情でお見せできない部分については弊社のロゴでマスクをしています。
1. Lambda 作成
Slack の設定等は後回しにして、とりあえず Lambda の設定と関数の作成を行っていきたいと思います。
今回ランタイムはPython 3.9
で作成しました。
最終的に使用したコードが以下です。
import json
import boto3
import requests
comprehend = boto3.client('comprehend')
slack_webhook_url = "SlackのWebhookurl"
def lambda_handler(event, context):
body_object = json.loads(event['body'])
# bot による投稿であるかを判定する
if 'subtype' in body_object['event']:
subtype_value = body_object['event']['subtype']
if subtype_value == "bot_message":
print('Posting by bot.')
else:
text_processing(body_object, slack_webhook_url)
def text_processing(event_body, url):
# 投稿された文章を取り出す
submitted_text = event_body['event']['text']
# 感情分析
detect_sentiment_result = comprehend.detect_sentiment(Text=submitted_text, LanguageCode='ja')
sentiment = detect_sentiment_result["Sentiment"]
# それぞれの感情のスコアを取得する
positive_score = detect_sentiment_result["SentimentScore"]["Positive"]
negative_score = detect_sentiment_result["SentimentScore"]["Negative"]
neutral_score = detect_sentiment_result["SentimentScore"]["Neutral"]
mixed_score = detect_sentiment_result["SentimentScore"]["Mixed"]
emotion_score = f"===各感情のスコアを表示します===\nポジティブ:{positive_score}\nネガティブ:{negative_score}\n中立:{neutral_score}\n混合:{mixed_score}\n============================="
# 感情に応じて返すテキストを変える
if sentiment == "POSITIVE":
emotion = "ポジティブな文章です:laughing:"
max_value = positive_score
elif sentiment == "NEGATIVE":
emotion = "ネガティブな文章です:smiling_face_with_tear:"
max_value = negative_score
elif sentiment == "NEUTRAL":
emotion = "中立的な文章です:slightly_smiling_face:"
max_value = neutral_score
elif sentiment == "MIXED":
emotion = "複合的な文章です:melting_face:"
max_value = mixed_score
else:
emotion = "不明な感情です:lying_face:"
max_value = ""
# スコアの値に応じて言葉を追加する
if max_value >= 0.9:
embellishment = "とても"
elif max_value <= 0.6:
embellishment = "どちらかといえば"
else:
embellishment = ""
# Slack にテキストを投稿する
body_text = {"text": f"{embellishment}{emotion}\n{emotion_score}"}
body = json.dumps(body_text)
headers = {
"Content-Type": "application/json"
}
response = requests.post(url, headers=headers, data=body)
if response.status_code == 200:
print("Posted to Slack.")
それぞれの項について解説します。
必要がなければ「関数 URL の発行」まで飛ばしてください
bot の投稿による無限ループ
関数作成中、slack との連携が完了したあと、テストで動かしてみると以下のような状態になりました。
こちらは
- ユーザーが投稿した文章を
Slack
がイベントとしてLambda
へ送る - ユーザーが投稿した文章の解析結果を
Lambda
がSlack
へ投稿する -
Lambda
が投稿した解析結果をSlack
がイベントとしてLambda
へ送る -
Lambda
が投稿した解析結果の解析結果をLambda
がSlack
へ投稿する -
Lambda
が投稿した解析結果の解析結果の解析結果をSlack
がイベントとしてLambda
へ送る -
Lambda
が……
という「不滅のLambda
~無限課金編~」を捕えたものになります。
イベント通知が Lambda
自身による投稿にも反応してしまう可能性をすっかり失念しておりました。
このままでは大量のコストと大量の怒られが発生してしまう……と怯えながら目を皿にして slack
が Lambda
へ送っているイベント内容を見比べたところ、bot による投稿はsubtype":"bot_message"
という要素を持っていることがわかりました。
上記の要素をつかって bot による投稿の場合は処理を終了してもらうようにします。
# bot による投稿であるかを判定する
if 'subtype' in body_object['event']:
subtype_value = body_object['event']['subtype']
if subtype_value == "bot_message":
print('Posting by bot.')
else:
text_processing(body_object, slack_webhook_url)
これでひとまず、当初の想定通りに動くようになりました。
感情分析
Amazon Comprehend
による感情分析は非常に簡単で、以下の一行のみで終了します。
# 感情分析
detect_sentiment_result = comprehend.detect_sentiment(Text=submitted_text, LanguageCode='ja')
当初はただ解析された感情のみを返していましたが、それだけだと味気なかったので追加で各感情のスコアも返すようにし、ついでにスコアの値に応じて文頭に修飾語を付けることにしました。
# それぞれの感情のスコアを取得する
positive_score = detect_sentiment_result["SentimentScore"]["Positive"]
negative_score = detect_sentiment_result["SentimentScore"]["Negative"]
neutral_score = detect_sentiment_result["SentimentScore"]["Neutral"]
mixed_score = detect_sentiment_result["SentimentScore"]["Mixed"]
emotion_score = f"===各感情のスコアを表示します===\nポジティブ:{positive_score}\nネガティブ:{negative_score}\n中立:{neutral_score}\n混合:{mixed_score}\n============================="
# 感情に応じて返すテキストを変える
if sentiment == "POSITIVE":
emotion = "ポジティブな文章です:laughing:"
max_value = positive_score
elif sentiment == "NEGATIVE":
emotion = "ネガティブな文章です:smiling_face_with_tear:"
max_value = negative_score
elif sentiment == "NEUTRAL":
emotion = "中立的な文章です:slightly_smiling_face:"
max_value = neutral_score
elif sentiment == "MIXED":
emotion = "複合的な文章です:melting_face:"
max_value = mixed_score
else:
emotion = "不明な感情です:lying_face:"
max_value = ""
# スコアの値に応じて言葉を追加する
if max_value >= 0.9:
embellishment = "とても"
elif max_value <= 0.6:
embellishment = "どちらかといえば"
else:
embellishment = ""
2. 関数 URL の発行
関数を作成したら、次は Slack
と連携をしてもらうための「関数 URL」を発行する必要があります。
前項で作成した Lambda 関数のコンソール画面を開き、以下の画面から「関数 URL を作成」を選択してください。
作成が完了すると関数 URL が表示されるようになるので、そちらを使って Slack との連携を進めていきます。
3. Slack 設定
今回は専用のアプリを作成するところから始めます。
https://api.slack.com/apps
2. 任意のアプリ名を入力して「Create App」を選択します。
3. アプリが作成できたら、管理画面から「Event Subscriptions」を選択し、有効にしたあと「Request URL」に前項で発行した「関数 URL」を貼り付けます。
無事に認証が通ると「Verified」が表示されます。
4. 同じく管理画面から「Incoming Webhooks」を選択し、有効にします
5. 同画面の下部から「Add New Webhook to Workspace」を選択し、Webhook URL
を発行します。
※個人のアカウントではなく企業管理のワークスペース等を使用する場合は、管理者の承諾がないとアプリの作成およびWebhook URL
の発行ができない場合があります。
6. 発行した URL を関数に貼り付け、変数として利用します。
slack_webhook_url = "SlackのWebhookurl"
これでばっちり動くはずです。
動作確認
Slack
に任意の文章を投稿します。
感想
Amazon Comprehend
はコンソール画面からでもお手軽に操作ができますが、AWS にログインしていない状態でも Slack
からササッと操作できたらもっとお手軽なんじゃないかな~と思い、作ってみました。
アドベントカレンダーの名を借り、業務ではなかなか触れないサービスを好きなように触ることができて楽しかったです
さいごに
株式会社オークファンではエンジニアを募集しております。
ご興味ございましたらぜひ弊社のホームページをご覧ください。