6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

オークファングループAdvent Calendar 2023

Day 5

Amazon Comprehend + Lambda + Slack でお手軽に感情分析をする

Last updated at Posted at 2023-12-04

はじめに

はじめまして。オークファン入社歴三年目になりました、 @aucfan-0540 です。業務では主に AWS を用いたインフラ環境の構築等に携わっています。アドベントカレンダーを書くのは三回目です。今までは以下のような記事を書いてきました。

今回は Amazon Comprehend + Lambda + Slack でお手軽に Amazon Comprehend を触りつつ、文章の感情分析をしてみたいと思います。:grin:

Amazon Comprehend とは

公式サイト様から引用させていただきます。

Amazon Comprehend は、機械学習を使用して、テキストからインサイトや関係性を発見するための自然言語処理 (NLP) サービスです。

上記の通り、文章を解析して以下のような情報を得ることができるサービスです。

  • エンティティ解析
    • 固有名詞や日付等の情報を取得します。
  • キーフレーズ抽出
    • 重要度の高いと思われる単語を抽出します。
  • 感情分析
    • 感情を読み取り、ポジティブ、ネガティブ、中立、複合のいずれかにカテゴライズします。
  • 言語判定
    • 用いられている言語を判定します。

今回は「感情分析」の機能を使用します。

構成図

image.png

ノートの片隅に書いた落書きみたいになってしまったので、文章でも軽く説明させていただきます。

ユーザーが 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 との連携が完了したあと、テストで動かしてみると以下のような状態になりました。

スクリーンショット 2023-12-01 153358.png

こちらは

  1. ユーザーが投稿した文章を Slack がイベントとして Lambda へ送る
  2. ユーザーが投稿した文章の解析結果を LambdaSlack へ投稿する
  3. Lambdaが投稿した解析結果を Slack がイベントとして Lambda へ送る  :innocent: :interrobang:
  4. Lambdaが投稿した解析結果の解析結果を LambdaSlack へ投稿する
  5. Lambdaが投稿した解析結果の解析結果の解析結果を Slack がイベントとして Lambda へ送る
  6. Lambdaが……

という「不滅のLambda~無限課金編~」を捕えたものになります。
イベント通知が Lambda 自身による投稿にも反応してしまう可能性をすっかり失念しておりました。

このままでは大量のコスト大量の怒られが発生してしまう……と怯えながら目を皿にして slackLambda へ送っているイベント内容を見比べたところ、bot による投稿はsubtype":"bot_message"という要素を持っていることがわかりました。

ユーザーによる投稿
image.png

bot による投稿
image.png

上記の要素をつかって 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)

これでひとまず、当初の想定通りに動くようになりました。

スクリーンショット 2023-12-01 154022.png

感情分析

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 を作成」を選択してください。

Image 1.png

認証タイプを「NONE」にして作成します。
Image 1.png

作成が完了すると関数 URL が表示されるようになるので、そちらを使って Slack との連携を進めていきます。
スクリーンショット 2023-12-01 161954.png

3. Slack 設定

今回は専用のアプリを作成するところから始めます。
https://api.slack.com/apps

1.Create New App」を選択してください。
Image 1.png

2. 任意のアプリ名を入力して「Create App」を選択します。
Image 1.png

3. アプリが作成できたら、管理画面から「Event Subscriptions」を選択し、有効にしたあと「Request URL」に前項で発行した「関数 URL」を貼り付けます。
Image 1.png
無事に認証が通ると「Verified」が表示されます。

4. 同じく管理画面から「Incoming Webhooks」を選択し、有効にします
Image 1.png

5. 同画面の下部から「Add New Webhook to Workspace」を選択し、Webhook URL を発行します。
Image 1.png

※個人のアカウントではなく企業管理のワークスペース等を使用する場合は、管理者の承諾がないとアプリの作成およびWebhook URLの発行ができない場合があります。

6. 発行した URL を関数に貼り付け、変数として利用します。

slack_webhook_url = "SlackのWebhookurl"

これでばっちり動くはずです。

動作確認

Slack に任意の文章を投稿します。

名作を解析してみたり
Image 1.png

意味もなく漫画のセリフを入れてみたり
Image 1.png

ボジョレーの評価もわかります:thumbsup:
スクリーンショット 2023-12-01 171516.png

感想

Amazon Comprehendはコンソール画面からでもお手軽に操作ができますが、AWS にログインしていない状態でも Slack からササッと操作できたらもっとお手軽なんじゃないかな~と思い、作ってみました。
アドベントカレンダーの名を借り、業務ではなかなか触れないサービスを好きなように触ることができて楽しかったです :grin:

さいごに

株式会社オークファンではエンジニアを募集しております。
ご興味ございましたらぜひ弊社のホームページをご覧ください。

6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?