こんにちはアドバンストテクノロジー部の@y-matsushitaです。
今回は高度な知識や大量の教師用データが不要で始めることができる、
Google Natural Language APIを使った感情解析を試してみたのでご紹介します!
Google Natural Language APIとは
Googleが提供する自然言語処理の機械学習モデルです。テキストの構造と意味を解析できます。
SNSやECサイトに寄せられたコメントから製品に対するセンチメント(感情)を把握したり、顧客満足度を分析したりすることができます。
動作サンプル
WEB上で動かせるサンプルがあります。
ひとまず「Try the API」の箇所に任意のテキストを入れて試してみましょう。
現在は下記の3つ、Entities, Sentiment, Syntaxが日本語で利用可能です。
(Categories(テキストのカテゴリ分け)のみ2017/10/17時点で未対応)
cloud.google.com
Entities
エンティティ分析:
指定されたテキストに既知のエンティティ(著名人、ランドマークなどの固有名詞、レストラン、競技場などの普通名詞)が含まれているかどうかを調べます。
エンティティ分析を行うには analyzeEntities メソッドを使用します。
Sentiment
感情分析:
指定されたテキストを調べて、そのテキストの背景にある感情的な考え方を分析します。
執筆者の考え方がポジティブか、ネガティブか、ニュートラルかを判断します。
感情分析を行うには analyzeSentiment メソッドを使用します。
Syntax
構文解析:
指定されたテキストから言語情報を抽出し、一連の文とトークン(通常は単語の境界)に分解します。
構文解析を実行するには analyzeSyntax メソッドを使用します。
感情分析
上記3つの中で感情分析に注目します。
感情分析はテキストをネガティブなものとポジティブなものに分類して、
ユーザが何に不満を持っていて何に満足しているかを分析することができます。
Entire Document | Score | Magnitude |
---|---|---|
全体 | 0 | 1.5 |
この商品使い勝手は良いしコスパも良くて最高! | 0.8 | 0.8 |
ただしデザインがダサいしカラーバリエーションも少なすぎ | -0.7 | 0.7 |
上記ではEntrieDocumentの全体のScoreが0となっています。
Scoreは-1.0〜1.0までの値で感情の分析を行い、ポジティブなものほど1.0に近くなります。
逆にネガティブに近づくと-1.0に近くなります。なのでこのテキスト全体では平均的な感情に近いことになります。
ただし各文節ごとの感情をみると、ある観点にはポジティブな感情を出しているが、別の観点にはネガティブな感情を出していることがわかります。
このように文節ごとに、ユーザが何に高評価を出しており、何に不満を持っているか、分析することもできます。
またMagnitudeは感情の振れ幅を示します。
EntrieDocumentのMagnitudeは1.5なので振れ幅としてはそれなりにあります。(初期は0)
つまりMagnitudeによってポジティブな感情とネガティブな感情が混在したテキストという解析結果も得られます。
Google Natural Language APIをPythonから呼び出してみる
前置き長くなりましたが、WEBのサンプルであったNatural Language APIを、
Pythonから呼び出してテキストの感情分析を行ってみます。使用環境はPython3.6.1です。
Google Cloud Platformの登録が済んでいない場合は済ませておいてください。
Cloud Natural Language API | Google Cloud Platform
Google Natural Language APIの課金について
感情分析の場合、1,000件辺り$1ですが、5,000件までは無料で利用可能です。(2017/10/17時点)
無料の範囲内でも、かなりの回数試すことができます!
詳細は下記をご確認ください。
料金 | Google Cloud Natural Language API ドキュメント | Google Cloud Platform
1. GCPの「APIとサービス」から有効化
ダッシュボードの「APIとサービスの有効化」からNatural Language APIを探して有効化させます。
有効化すると以下のようにダッシュボードで確認できます。
2. API キーを発行
APIキーを使っての呼び出しを試してみます。
「認証情報」から「APIキー」を選択してキーを発行します。
リクエストを受け入れるIP addressesに使用するサーバのIPなどを入力して外部から使われないようにしておきます。
発行したAPIキーは外部に公開されないように注意してください。
APIキーを使って感情分析の結果を取得する
gcp_sentiment.py
import sys import requests def main(content, access_token): url = 'https://language.googleapis.com/v1/documents:analyzeSentiment?key={}'.format(access_token) header = {'Content-Type': 'application/json'} body = { "document": { "type": "PLAIN_TEXT", "language": "JA", "content": content }, "encodingType": "UTF8" } response = requests.post(url, headers=header, json=body).json() print(response) if __name__ == '__main__': main("".join(sys.argv[1:]), "取得したAPIキーを設定")
上記、コードを実行します。
$ python gcp_sentiment.py "この商品使い勝手は良いしコスパも良くて最高!ただしデザインがダサいしカラーバリエーションも少なすぎ"
結果は以下のようなJSON形式で取得できます。
文節ごとの感情分析のスコアと総合的な感情分析のスコアが取得できます。
{ "documentSentiment": { "magnitude": 1.5, "score": 0 }, "language": "ja", "sentences": [ { "text": { "content": "この商品使い勝手は良いしコスパも良くて最高!", "beginOffset": 0 }, "sentiment": { "magnitude": 0.8, "score": 0.8 } }, { "text": { "content": "ただしデザインがダサいしカラーバリエーションも少なすぎ", "beginOffset": 66 }, "sentiment": { "magnitude": 0.6, "score": -0.6 } } ] }
結果からdocumentSentimentのScoreのみを取得したい場合は、
以下のような形でresponseから取得することができます。
response = requests.post(url, headers=header, json=body).json() score = response['documentSentiment']['score']
Google Natural Language APIが弱い点と対処法
一般的にはネガティブな表現として扱われるものは当然APIでもネガティブな方へ寄ります。
具体的には弊社の「クラッシュフィーバー」や「バグ上げ」*1などがそれにあたります。
例
・「クラッシュフィーバー」→「クラッシュ」
・「バグマ」、「バグ上げ」→「バグ」
「ついにバグがMAXになった!クラッシュフィーバープレイ中。」
感情分析のスコア:-0.4
{ "documentSentiment": { "magnitude": 1.3, "score": -0.4 }, "language": "ja", "sentences": [ { "text": { "content": "ついにバグがMAXになった!", "beginOffset": 0 }, "sentiment": { "magnitude": 0.7, "score": -0.7 } }, { "text": { "content": "クラッシュフィーバープレイ中。", "beginOffset": 60 }, "sentiment": { "magnitude": 0.1, "score": -0.1 } } ] }
これらのような単語が含まれることが予測される場合は、
以下のように予め当たり障りの無い単語に置換してからAPIを活用すると結果に悪影響を与えずに済みます。
gcp_sentiment.py
import sys import re import requests def main(content, access_token): # 処理前に整形(APIに誤った解釈をされないように置換) content = re.sub('バグ', "限界突破", content) content = re.sub('クラッシュフィーバー', "クラフィ", content) url = 'https://language.googleapis.com/v1/documents:analyzeSentiment?key={}'.format(access_token) header = {'Content-Type': 'application/json'} body = { "document": { "type": "PLAIN_TEXT", "language": "JA", "content": content }, "encodingType": "UTF8" } response = requests.post(url, headers=header, json=body).json() print(response) if __name__ == '__main__': main("".join(sys.argv[1:]), "取得したAPIキーを設定")
以下は整形した後に取得したレスポンスです。
{ "documentSentiment": { "magnitude": 1.4, "score": 0.1 }, "language": "ja", "sentences": [ { "text": { "content": "ついに限界突破がMAXになった!", "beginOffset": 0 }, "sentiment": { "magnitude": 0.6, "score": 0.6 } }, { "text": { "content": "クラフィプレイ中。", "beginOffset": 66 }, "sentiment": { "magnitude": 0.3, "score": 0.3 } } ] }
置換前後の結果比較
原文 | 素で渡した場合 | 置換して渡した場合 ・バグ→限界突破 ・クラッシュフィーバー→クラフィ |
---|---|---|
ついにバグがMAXになった! | -0.7 | 0.6 |
クラッシュフィーバープレイ中。 | -0.1 | 0.3 |
扱えるメソッドまとめ
Methods | |
---|---|
analyzeEntities | POST /v1/documents:analyzeEntities 著名人、ランドマークなどの固有名詞が含まれていないかどうかを調べて、それらのエンティティに関する情報を返す |
analyzeEntitySentiment | POST /v1/documents:analyzeEntitySentiment エンティティ分析と感情分析の両方を組み合わせたもの。テキスト内エンティティの感情(ポジティブかネガティブか)を返す(※日本語非対応2017/10/17時点) |
analyzeSentiment | POST /v1/documents:analyzeSentiment テキストの感情(ポジティブかネガティブか)を返す |
analyzeSyntax | POST /v1/documents:analyzeSyntax テキストの言語情報を抽出し分解し構文解析した結果を返す |
annotateText | POST /v1/documents:annotateText analyzeSentiment、analyzeEntities、analyzeSyntaxが提供するすべての機能をまとめて1回の呼び出しで返す |
*1:クラッシュフィーバーでは同じユニットを合成することをバグを上げるといいます。バグが上がるとクエストで報酬が増えるなどの特典があります。