Amazon Comprehend(機械学習)で感情分析してみた!
バヅクリ株式会社CTOの合原です。
久々にAWSのサービスを触る機会があったので、まとめたいと思います。
背景
弊社では、オンラインをベースとして、社内イベントなどを行っているのですが、イベント実施時に参加者にアンケートをとっています。
その中には、選択式の設問もある一方、自由記述式の設問もあります。
このアンケート結果は、実際の参加者の声であり、非常に重要なインサイトデータとなります。
課題
選択式の設問は事前に配点をすることで、定量データとして、
結果の分析がしやすいのですが、
一方、自由記述式の回答については、これまで定性的な判断でしかデータ利用ができていませんでした。
端的に言うと、下記の点がネックになっていました。
- データ量も少なくなく、精査するのも手間
- アンケート結果を見る人の解釈が入ってしまう
対策
自由記述式回答結果を定量できないか?と。
そこで、かねてより使ってみたかったAWS Amazon Comprehend(機械学習)を使って見ようと。
Amazon Comprehend自体は、自然言語処理、つまりテキストベースでの分析を行っています。
詳細については下記参照。
やったこと
AWS::Comprehend検証
バヅクリでは、日頃からAWSのサービスを利用しているので、迷わず、Amazon Comprehendを利用できないか検討しました。
幸い、Amazon Comprehendでは、GUIでの、テスト環境があるので、使ってみました。
とりあえず、平易な文で試してみましたところ、👆の通り、
日本語でも、問題なさそう。
...と言うことで、
設計
バヅクリでは、サーバーサイドは、Railsアプリケーションで動かしています。
また、すでにS3など、他のサービスも利用していることから、SDKを利用してサクッと実装することにしました。
gem 'aws-sdk'
すでに、これまでのAWSサービスを利用する部分については、
下記のように、切り分けていたので、今回は、
新しく下記の構成で、modelを追加してみました。
$ tree -N -d -L 1 app/models/aws
app/models/aws
├── comprehend # これを追加
└── s3
実装
class Aws::Comprehend::Manager
class ComprehendArgumentError < StandardError; end
include ActiveModel::Model
ATTRS = %i[
detected_text
]
ATTRS.each { |attr| attr_accessor attr }
def analyze_sentiment
return raise ComprehendArgumentError, "Not found detected_text" if detected_text.blank?
client.detect_sentiment(
text: detected_text, # required
language_code: "ja",
)
end
def resource
@resource ||= Aws::Comprehend::Resource.new(region: region)
end
private
def client
@client ||= resource.client
end
def region
Rails.application.credentials.aws[:region]
end
end
特に凝ったことはせず、シンプルにSDK経由でAWS Comprehend APIへ分析対象テキストを渡しているだけです。
あとは、これを、バッチなり、必要な箇所で呼び出してあげれば、スコアリングができます。
rake taskにしてみた
今回は、適当なテーブルを用意して、Amazon Comprehendの結果をそのままテーブルに入れます。
※弊社の場合、サービスAPP側のテーブル群を、データ分析のできる環境に定期実行で、そのままコピーしているため、redashでもデータが閲覧できるようになります。
ということで、
namespace :analyze_questionnaire do
desc 'all of questionnaires analyzes'
task analyzes: :setup_logger do
Rake::Task['analyze_questionnaire:analyze:participant_questionnaire'].execute
:
end
namespace :analyze do
desc 'analyze_participant_questionnaire'
task participant_questionnaire: :setup_logger do
include SentimentAnalyzeHelper
# スコアリングしたいattribute
free_answers = %i[
satisfaction_level_reason
new_awareness
interactive_points
event_improve_point
]
# 数が多いので並列化
Parallel.each(questionnaires, in_threads: thread_number) do |q|
ActiveRecord::Base.connection_pool.with_connection do
update_params = free_answers.each_with_object({}) do |attr, h|
value = q.send(attr)
next if value.blank?
next if q.send("sentiment_#{attr}").present?
result = sentiment_analyzer(value)
# とりあえず、AWS Comprehendの結果をそのまま入れる前提
h.merge!(
"sentiment_#{attr}": result.sentiment,
"sentiment_score_positive_#{attr.to_s}": result.sentiment_score.positive,
"sentiment_score_negative_#{attr.to_s}": result.sentiment_score.negative,
"sentiment_score_neutral_#{attr.to_s}": result.sentiment_score.neutral,
"sentiment_score_mixed_#{attr.to_s}": result.sentiment_score.mixed,
)
end
q.assign_attributes(update_params)
q.save(validate: false)
end
end
end
:
end
private
task setup_logger: :environment do
Rails.logger = Logger.new(STDOUT)
end
module SentimentAnalyzeHelper
def questionnaires
@questionnaires ||= ::Events::ParticipantQuestionnaire.merge(::Events::ParticipantQuestionnaire.where.not(satisfaction_level_reason: "").or(::Events::ParticipantQuestionnaire.where.not(new_awareness: "")).or(::Events::ParticipantQuestionnaire.where.not(interactive_points: "").or(::Events::ParticipantQuestionnaire.where.not(event_improve_point: ""))))
end
def sentiment_analyzer(detected_text)
return {} if !detected_text
# 前述で作ったmodelを使う
Aws::Comprehend::Manager.new(detected_text: detected_text).analyze_sentiment
end
def thread_number
4
end
end
end
あとは、下記を
$ rake analyze_questionnaire:analyzes
定期実行して、
無事、自由記述回答結果の定量化ができました ⭐️
まとめ
初めてAmazon Comprehendを導入してみましたが、AWS SDKで諸々、用意されているので、
導入のハードルも低く、さすがAWSですね。
肝心のデータ自体の詳細な精査はやっていく必要ありそうですが、
こんなに簡単に機械学習を導入できる、というのもありがたい限り。
まだまだ利用していないAWSサービスはたくさんあるので、
どんどん使っていきたいと思います。
恒例のですが。。。
そんなバヅクリでは、現在Railsエンジニアを募集しております!
気になる方はぜひ👇より!
こちらにコメントでもいいので、カジュアルにご連絡いただけたらな、と思います!
Discussion