ユーザーレビューを感情分析するBotについて

この記事はJXAdvent Calendarの17日目の記事です。 こんにちは、以前秩父での開発合宿の記事を書いたぬまっち(@nuMatch) です。
弊社のSlackにはユーザーからのコメントが届くチャンネルがあります。
その内訳は

Android-Review      : Google Playに投稿されるユーザーレビューコメント
iOS-Review          : iTunes Storeに投稿されるユーザーレビューコメント

Android-Feedback    :
iOS-Feedback        : NewsDigestの「ご意見・ご要望」画面から投稿されるフィードバックコメント

の4種類で、日々開発の参考にさせて頂いております。

頂いたご意見の中には当然好意的な意見と否定的な意見両方があるのですが、
その度合いを数値として残しておければ分析の役に立つのではと思い、Botを作成したので記事として公開します。

(そしてAndroid開発以外に手を出すのも、初めての挑戦です)

今回作るBotにやらせてみたいのは
- 好意的な意見と否定的な意見の比率は施策によって遷移するのかを分析したい - Slackチャンネルで流れていくだけでは勿体無いので、コメントを蓄積してしたい

の二点になります。

SlackBotをHerokuにデプロイさせた理由

という事でBotを作ろうと思ったのですが、正直自分はAndroid開発以外のことは皆無でして、どこからどう始めたらいいのか分かりません。 そこで考えたのは今想定されるユースケースと自分の環境から最適解を見つけ出していこう、というものでした。

  • 言語選定 ここは弊社のサーバサイドエンジニアに知見を伺いたい事情もあったので、 Python を選択しました。
    その道のエキスパートが沢山いらっしゃるので、ここは相談しやすさを重視です。

  • SlackBot 前述の通り、コメントはSlackに流れてくるのですからBotはSlackから効率良く取得できるものでありたいです。 Python3系をサポートしていることから、Slackbotライブラリがチョイスされました。

  • Heroku 作ったBotはデプロイしないことには動いてくれません。当たり前ですがいつまでもローカル環境で動かしていては自宅の電気代も勿体無いです。 ここはCTOに相談したところHerokuを推して頂きました。PaaSの中で選ぶならAWSの知見が社内に溜まっているとは思いましたが、
    「デプロイ以外の細かなセキュリティ周りの設定だとかで躓く可能性が高い、初めての体験はなるべく躓かないほうが良い」
    という助言を頂いたところで、今回のBotは Heroku環境 で動作させる事が確定です。

感情分析をどう扱うのか(APIの選定)

次はある意味心臓部にあたる、感情分析をどうするか、という点について。
Google感情解析API(CLOUD NATURAL LANGUAGE API)というGoogle Cloud Platformのサービスがあります。 簡単に説明すると、事前に用意された機械学習モデルにREST APIでアクセスする事が出来て、感情分析結果を取得する事が出来るものです。

  • 感情分析の範囲
    0.1 ~ 1.0 ポジティブ
    0.0 ニュートラル
    -0.1 ~ -1.0 ネガティブ

コメントを投げると上記の範囲でレスポンスを返却してくれるので、ネガポジの度合いを絶対値で判断する事が出来ます。

ユーザーコメントが届く度にコメントをAPIに乗せて投げてあげれば、感情分析はGoogle Cloud Platform側で担保する。これでレビュー解析Botは形になりそうです。

大まかな流れ

f:id:numatch-jx:20181217173133p:plain

①レビューコメントの投稿

デプロイしたBotをレビューが投稿されるSlackチャンネルに招待し、 コメントの通知を受け取れるようにデコーダを実装させます。

from slackbot.bot import listen_to

@listen_to(r'.*')
def mention_func(message):
    body = message.body

SlackBotライブラリに用意されている@listen_toデコーダで投稿されたMessage全体を受け取ります。 その中からbodyを部分を取り出して感情分析にかけたいユーザーコメントを取得します。

  • Androidレビュー/フィードバック
  • iOSレビュー/フィードバック

の4パターンのコメントが投稿されるので、取得したbodyの内容で判別の上、適切な形で取り出したうえでGoogle感情解析APIに投稿します。

②コメントの感情分析 / ③ネガポジスコアの返却

Google Cloud PlatformにてGoogle Natural Language API(感情分析API)の登録を完了させるとAPIキーを発行する事が出来ます。
Heroku側でダッシュボード内でAPIキーを環境変数として登録しておく事で、Botがデプロイ環境で感情分析APIを扱うことが出来るようになります。

環境変数の登録
f:id:numatch-jx:20181216202549j:plain
from google.cloud import language

# 環境変数からAPIキーを取得
info = json.loads(os.environ.get('環境変数に登録したKey'))
credentials = service_account.Credentials.from_service_account_info(info)
client = language.LanguageServiceClient(credentials=credentials)

# 感情分析APIにリクエストを投げる
sentiment = client.analyze_sentiment(document=document).document_sentiment

sentiment.scoreで感情分析のスコアを取り出すことが出来ます。

④スコアの投稿

スコアは-1.0 ~ 1.0の範囲で返却されますが、端数になりがちなので小数点以下で四捨五入しておきます。

ounded_score = round(sentiment.score, 3) 

message.reply()で投稿されたSlackメッセージに対してスレッド返信を行えます。 また、スコアが正の値ならポジティブ、負の値ならネガティブなので対応した絵文字でSlackのユーザーコメントにリアクションをしてみます。

message.reply('NegaPosi Score: {}'.format(rounded_score), in_thread=True)
message.react(negaposi(rounded_score))

解析結果

ポジティブ ネガティブ
f:id:numatch-jx:20181217150910j:plain f:id:numatch-jx:20181217150938p:plain
  • ポジティブコメント
    「地震情報が素早い世界中の情報が分かる」はポジティブな意見そうですが、実際のスコアが0.7と判定されており、感情分析結果としてもポジティブと捉えられています。
  • ネガティブコメント 「天気予報は出ないのですか?」は残念ながらネガティブそうです。スコアを確認すると-0.6ですね。APIのレスポンスでもネガティブとして判断されているようです。

  • 絵文字とスレッド 絵文字がリアクションとして投稿されるので、スレッドを開かなくてもネガポジの解析結果を判断する事が出来ます。 スレッド返信されておりSlack上でスコアを確認することも出来て良さそうです!

データベース登録と、その結果

最初にBotを作ろうとした理由のうち、

  • 好意的な意見と否定的な意見の比率は施策によって遷移するのかを分析したい

は実現が出来たので、

  • Slackチャンネルで流れていくだけでは勿体無いので、コメントを蓄積してしたい

も実現するべく、次はデータベースへのInsert機能をつけたいと思います。 PostgreSQLはHerokuが公式でサポートしているので都合が良さそうです。

Add-onsでHerokuにPostgreSQLを追加するとダッシュボードから確認出来るようになります。

Herokuが自動的に環境変数に登録してくれる
f:id:numatch-jx:20181216202758j:plain

PostgreSQLのDBに接続するためのパスワード等は、Herokuの環境変数へと自動で登録されるのでラクチンです。

  • 保存しておきたい4つのプロパティ

今後スコアを分析するの必要そうなプロパティは事前に考えておきました。
下記の4つの値を保持しておくと、今後役立ちそうです。

comment : 感情分析にかけたユーザーコメント
score : ネガポジスコア
type : OSの種別・レビュー/フィードバックの種別
created_at : Insert日時 

後からレビュー分析に使えるようなColumn情報を定義してCREATE TABLE(今回のテーブル名はreviews)しておきます。 PythonからDB接続するためにはドライバが必要になるので、psycopg2をimportしたうえでコネクションオブジェクトの取得を行います。

def get_connection():
    dsn = os.environ.get('Herokuが登録してくれた環境変数Key')
    return psycopg2.connect(dsn)

コネクションオブジェクトからDBカーソルを取得したら、必要なプロパティに対してInsertを実行します。

def insert_review(comment, score, type):
    with get_connection() as conn:
        with conn.cursor() as cur:
            cur.execute('INSERT INTO reviews (comment,score,type,created_at) VALUES (%s,%s,%s,%s)',
                        (comment, score, type, datetime.now().strftime("%Y/%m/%d %H:%M:%S")))
            conn.commit()

レビューコメントや感情分析スコアが無事にDB保存されている事を確認できたら、今回の目標は達成となります!

感情分析スコアDB
f:id:numatch-jx:20181217151025j:plain

作ってみて

今回、ユーザーレビューを感情分析してSlackに投稿する/DBに保存する機能のBotを作りました。 Android開発以外は門外漢で通ってきた自分ですが、質問に快くレスをくれた弊社の開発チームのおかげで無事にデプロイまで漕ぎ着ける事が出来たので感謝しかありません。 こういったチャレンジに取り組みやすい環境が弊社には揃っていると思います。

ところですが、Botに愛着を持ってもらう為に Cook-Tool-Reviewと名付けました。(個人的にですが)

なんでCook-Toolなのか。 それは弊社開発チームのプロジェクトは基本的にお菓子の名前で呼ばれている事から発想しました。 お菓子の調理を手伝う調理器具みたいな役割、Botがそんな風に役に立ったらいいなって気持ちを込めて名付けました。 今後もサービス開発の助けになるようなCook-Tool-Botを増やしていければいいなって思います。