Amazon Comprehend Sentiment Analysisを本当のNo-Codeにしてみる

# サマリ

### 問題意識

* Amazon Comprehendは素晴らしいサービスだけど、コードを書けない人間には結構厳しいサービスでは？

### ビジョン

* コードを書くスキルがない人にも、Comprehendのパワーを与えて、データサイエンス・アナリティクスの裾野を広げていく
* 引いては、Bizユーザーが自律的・自立的にdata-drivenな意思決定を進めるインフラを作っていきたい

### ソリューション

* プロトタイプとして、Lambdaを使って、「CSVをアップロードするだけでComprehendが走る環境」を作ってみる
* 将来的には、①CSVアップロード→ ②GUIで処理したい列を選択、③想定結果の確認、④処理実行&結果ファイルダウンロードボタンのクリック、というステップでComprehendの分析処理をすべての人に機能開放したい

# Amazon Comprehendが絶妙に使いにくい

Amazon Comprehendは、Pythonを使えるユーザー、[Boto3のドキュメント](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/comprehend.html) を読める人間からすると、大変面白い & とても使いやすいサービスです。<br>
一方、Pythonには不慣れだけど、comprehendを使ってみたいというユーザーとしては、以下の理由から使いにくいのではないかと思います。

* GUIベースでAnalysis Jobを作成しても、JSON Linesを返すため、どう扱っていいかわからない
* Boto3を使って処理するほどスキルはないため、どう進めていいかわからない

(~~玄人的には、ブラックボックスなので、逆に使いにくいみたいな話もあるかと。素人も使いにくければ、玄人も使えないって話だったら、誰向けなのかイマイチわかんねえ~~)

<br>

そこで、どんな人でもAmazon Comprehendの機能の事前トレーニング済みの分析処理をお試しできるようなツール・環境を作ってみましょう。

# アウトプットイメージ

### 最終的なアウトプットイメージ

処理データ: EXCELやCSVファイルで、何行もテキスト・センテンスデータが並んでいるデータ。
アンケートの回答とかを想定している。

|  ID  |  AGE  | GENDER | COMMENT |
| ---- | ---- | ---- | ---- |
|  1  |  20 | M | AAABBBCCC... |
|  2  |  30 | F | XXXYYYZZZ... |

ユーザーの体験: ボタンをクリックしているだけで、Sentiment analysisやKey Phrase Detection　jobが走っているイメージ。

<br>

**想定するユーザー体験**

* なにかwebサイトがある
* `Upload File`みたいなボタンがあって、そこから処理したいExcel or CSVファイルを選択
* データの読み込み、および出力
* Comprehendで処理したい列を選択
* 処理後の結果のサンプルとして、冒頭の5行のみの出力結果を提示。<br>ユーザーに対して問題がないかを確認。
* 問題がなければ `PROCEED`みたいなボタンをクリックさせる
* 処理結果を得る`DOWNLOAD`みたいなボタンを押すと、処理結果のCSVを取得

### 本記事でのプロトタイプとしてのアウトプットイメージ

Amazon Comprehendには様々なサービスがあるけど、一旦はSentiment Analysisに限定
以下のような処理が走るLambdaを作成する

![スクリーンショット 2023-04-13 22.08.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656439/893af1f0-48ae-ca78-5583-732850b2089e.png)

#### Input Data (data.csv)

* data.csvという名前でFIX
* ID, COMMENTの2列のみ
* あるバケットの中のInputというS3フォルダに格納される

|  ID  | COMMENT |
| ---- | ---- |
|  1  |  AAABBBCCC... |
|  2  |  XXXYYYZZZ... |

#### Output Data (\<timestamp\>_sentiment_analysis.csv)

* あるバケットの中のOutputというS3フォルダに格納される
* 出力ファイル名は時刻 + _sentiment_analysis.csv (i.e., 202304132230_sentiment_analysis)
* 以下のように、dominantなsentimentとそのscoreが出る

|  ID  | COMMENT | SENTIMENT | SCORE |
| ---- | ---- | ---- | ---- |
|  1  |  AAABBBCCC... | Positive | 0.90 | 
|  2  |  XXXYYYZZZ... | MIXED | 0.85 |

# プロトタイプとしてのコード

Lambdaに以下を突っ込みます。

* 環境: Python 3.9
* pandasのレイヤーを設定
* タイムアウト時間を15minに設定 (15minもいらないですけど)
* Role: S3へのRead&Write, Comprehendの権限を付与
* トリガー: 以下の通り、あるバケットの内の `input` プレフィックスに `data.csv` がアップロードされると処理が走る設定とする

![スクリーンショット 2023-04-13 22.21.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656439/ff56fd72-766a-e34d-6af7-f65df132615d.png)

* コード

**lambda_function.py**

```python
import json
import boto3
import pandas as pd 
import io
import datetime as dt

from comprehend_sentiment_analysis import sentiment_analysis

def lambda_handler(event, context):
    s3 = boto3.client('s3')
    comprehend = boto3.client('comprehend')
    bucket_name='poc-comprehend-sentiment-analysis'
    
    response = s3.get_object(Bucket=bucket_name, Key='input/data.csv')
    d0 = pd.read_csv(io.BytesIO(response['Body'].read()))
    d0['Comment'] = d0['Comment'].str.replace('\n', '')
    d0['Comment'] = d0['Comment'].str.replace('\u3000', '')
    print('read data completed')
    
    # 1) remove empty records, 2) implement sentiment analysis, 3) merge data, and 4) save it as csv file
    data_to_analysis = d0.dropna(subset=['Comment']).reset_index(drop=True)
    d1=sentiment_analysis(data_to_analysis)
    df_return = pd.merge(d0,d1,on='ID',how='left')
    df_return.to_csv('/tmp/df_return.csv', index=False)

    # upload s3
    current_time = dt.datetime.now(dt.timezone(dt.timedelta(hours=9))).strftime('%Y%m%d%H%M') # Japanese time
    file_key = 'output/' + current_time + '_sentiment_analysis.csv'
    s3.upload_file('/tmp/df_return.csv', bucket_name, file_key)
    print('s3 upload completed')
    
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Amazon Comprehend Sentiment Analysis Completed')
    }
```

**comprehend_sentiment_analysis.py**

```python
import boto3
import pandas as pd
import numpy as np

def sentiment_analysis(data):
    '''
    Input: コメントが入った行のみのデータフレーム (回答なしのデータを除外済み)
    Output: 各コメントのSentimentとそのScoreの列が追加されたデータフレーム
    '''
    comprehend = boto3.client('comprehend')
    
    sentiment_list = []
    
    for comment in data['Comment']:
        # identify dominant language
        language = comprehend.detect_dominant_language(Text=comment)
        
        # detect sentiment
        comment_sentiment = comprehend.detect_sentiment(Text=comment, LanguageCode=language['Languages'][0]['LanguageCode'])
        sentiment_list.append(comment_sentiment)
        
    print('comprehend job completed')

    df_sentiment = pd.json_normalize(sentiment_list)
    df_sentiment = df_sentiment.iloc[:, 0:4] # select main sentiment and each score (positive/negative/mixed/neutral)
    df_sentiment['Score'] = df_sentiment.max(axis='columns')
    df_sentiment = df_sentiment[['Sentiment', 'Score']]
    
    data = pd.concat([data, df_sentiment], axis=1)
    df_return = data[['ID', 'Sentiment', 'Score']]

    return df_return
```

(あとはよしなにしてください、PoCなので大変雑にコードを書いています)

# やってみた

csvをinputフォルダに突っ込むと...

![スクリーンショット 2023-04-13 22.28.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656439/edccd95f-f6ed-3050-2ece-5ab5922162d4.png)

outputフォルダに解析結果が出た！

![スクリーンショット 2023-04-13 22.28.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656439/cf83c2f8-e2cc-1cef-062a-b9bd35d1c25a.png)

出力結果はこんな感じ

![スクリーンショット 2023-04-13 22.31.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656439/d43172c5-2b0a-bb29-5c74-4d371de9dfc1.png)

# まとめ

Lambdaを使って、CSVをアップロードするとComprehend Sentiment Analysisの処理が走るツールを作りました。正直言ってそれほど便利な気はしないし、子供だまし感も否めないです。

けど、「組織におけるデータによる意思決定」って全員が使える・理解できるを出発点にしなくちゃならないと思っています。そういう意味では、このあたりを試金石として、データ分析の取り組みを全社・組織として広げるきっかけにするのが良いオチなんでしょうね、という印象です。納得感とやりやすさこそ一番大事。

チームで掲げるビジョンと責任を取ってくれる上司をもとに、組織を前に動かしていきたいですね (白目)

そんじゃーね！