iMind Developers Blog

iMind開発者ブログ

GCPのNatural Language APIで感情分析や固有表現抽出

概要

GCPのNatural Language APIを使って文章の感情分析(ポジネガ)を出したり、エンティティ分析(固有表現を抽出)をしてみる。

Google Cloud SDKからの実行とPythonからの実行の2パターン。

コンテンツの分類機能もあるみたいなので使ってみたけど日本語には対応してなかった。いつか対応するといいな。

バージョン情報

  • Google Cloud SDK 268.0.0
  • google-cloud-language==1.3.0

APIの利用料金

サンプルを実行する前に料金や制限について。

https://cloud.google.com/natural-language/pricing

1回のリクエストにつき1000文字までを1ユニットとして計算する。500文字なら1ユニット、1200文字なら2ユニット。2400文字なら3ユニット。

これを書いている時点(2019年10月)ではどのAPIも5000ユニット解析するまでは無料。なので軽く試す分には料金はかからない。

5,000~1,000,000までは1000回につき1ドルとか2ドルとか。うっかり1万回打ってしまっても1000円とか2000円。100万回くらいやってしまうとちょっとした事故。

最大テキストサイズ

https://cloud.google.com/natural-language/quotas

これを書いている時点(2019年10月)では1MBが上限となっている。

トークン(単語の境界とおおむね一致)10万、エンティティの言及数は5000でそれ以上は無視される。

手元で「走れメロス」を形態素解析したら6420語だったので軽い小説くらいなら一発で変換できそう。(文字数は1万文字弱なので10ユニットかかる)

リクエスト量

https://cloud.google.com/natural-language/quotas

1分あたり600回、1日あたり80万回が上限らしい。

1日は86400秒なので秒間9回ずつ打ち続けることができる計算。

インストール

Cloud SDKのインストールは下記参照。

https://cloud.google.com/sdk/downloads?hl=ja

Pythonのライブラリはpipで。

$ pip install --upgrade google-cloud-language

セットアップ

下記の「プロジェクトをセットアップする」を選択する。

https://cloud.google.com/natural-language/docs/quickstart

APIのキーがいるのでGCPのWebコンソールから「APIとサービス」 → 「認証情報」と選び、「認証情報を作成」から「サービスアカウントキー」を作成する。

作成するとjsonファイルがダウンロードできるので、下記の環境変数を設定する。

export GOOGLE_APPLICATION_CREDENTIALS="{ダウンロードしたjsonのパス}"

感情分析(gcloud)

$ gcloud ml language analyze-sentiment \
    --content "俺は怒ったぞ!"

実行結果はjson形式で出力される。scoreがプラスならポジティブ、マイナスならネガティブ。magnitudeは強い感情が入っているとプラス。

今回の「俺は怒ったぞ!」はネガティブで感情が入っているので、scoreがマイナス、magnitudeはプラスになる。

{
  "documentSentiment": {
    "magnitude": 0.6,
    "score": -0.6
  },
  "language": "ja",
  "sentences": [
    {
      "sentiment": {
        "magnitude": 0.6,
        "score": -0.6
      },
      "text": {
        "beginOffset": 0,
        "content": "\u4ffa\u306f\u6012\u3063\u305f\u305e\uff01"
      }
    }
  ]
}

スコアの詳しい解釈については下記を参照。

https://cloud.google.com/natural-language/docs/basics?hl=ja#interpreting_sentiment_analysis_values

スコアがいくつ以上ならポジティブといった定義はユースケースごとに変わるので一概に決めることはできないらしい。

jsonの中にsentencesというキーがあるように、文章全体とセンテンスごとのスコアが取れる。

p$ gcloud ml language analyze-sentiment \
    --content "本日は快晴なり。外出日和。"
$ gcloud ml language analyze-sentiment --content "本日は快晴なり。外出日和。"
{
  "documentSentiment": {
    "magnitude": 0.4,
    "score": 0.2
  },
  "language": "zh",
  "sentences": [
    {
      "sentiment": {
        "magnitude": 0.2,
        "score": 0.2
      },
      "text": {
        "beginOffset": 0,
        "content": "\u672c\u65e5\u306f\u5feb\u6674\u306a\u308a\u3002"
      }
    },
    {
      "sentiment": {
        "magnitude": 0.1,
        "score": 0.1
      },
      "text": {
        "beginOffset": 24,
        "content": "\u5916\u51fa\u65e5\u548c\u3002"
      }
    }
  ]
}

引数にファイルを渡したい場合は --content-file で指定する。

$ echo "俺は怒ったぞ!" > foo.txt
$ gcloud ml language analyze-sentiment \
    --content-file foo.txt

感情分析(Python)

Pythonでは下記のように記述する。

from google.cloud import language
from google.cloud.language import enums
from google.cloud.language import types

client = language.LanguageServiceClient()

text = '俺は怒ったぞ。でも許すぞ。'
document = types.Document(
    content=text,
    type=enums.Document.Type.PLAIN_TEXT)

sentiment = client.analyze_sentiment(document=document) 

# document全体のスコア
ds = sentiment.document_sentiment 
print('score={:.2f}, magnitude={:.2f}'.format(ds.score, ds.magnitude))
    #=> score=0.00, magnitude=1.60

# sentenceごとのスコア
for s in sentiment.sentences:
    score = s.sentiment.score
    magnitude = s.sentiment.magnitude
    text = s.text.content
    print('score={:.2f}, magnitude={:.2f}, {}'.format(
        score, magnitude, text))
    #=> score=-0.70, magnitude=0.70, 俺は怒ったぞ。
    #=> score=0.80, magnitude=0.80, でも許すぞ。

エンティティ分析(gcloud)

エンティティ分析をgcloudから行ってみる。

$ gcloud ml language analyze-entities \
    --content="浅草へ行ってスカイツリーを見てきた"

実行結果(ちょっと整形)

{'entities': [{'mentions': [{'text': {'beginOffset': 0, 'content': '浅草'},
     'type': 'PROPER'}],
   'metadata': {'mid': '/m/01k4r_',
    'wikipedia_url': 'https://en.wikipedia.org/wiki/Asakusa'},
   'name': '浅草',
   'salience': 0.5357579,
   'type': 'LOCATION'},
  {'mentions': [{'text': {'beginOffset': 18, 'content': 'スカイツリー'},
     'type': 'COMMON'}],
   'metadata': {},
   'name': 'スカイツリー',
   'salience': 0.4642421,
   'type': 'OTHER'}],
 'language': 'ja'}

typeの意味はこちら

https://cloud.google.com/natural-language/docs/reference/rest/v1/Entity#Type

PROPERは固有名詞。LOCATIONは地名。COMMONは普通名詞。

DATE(日付)やPRICE(金額)も取れるらしい。

例えば3月10日はこんな風に取れる。

{'mentions': [{'text': {'beginOffset': 0, 'content': '3月10日'},
     'type': 'TYPE_UNKNOWN'}],
   'metadata': {'day': '10', 'month': '3'},
   'name': '3月10日',
   'salience': 0.0,
   'type': 'DATE'}

5000円はこんな感じ。

{'mentions': [{'text': {'beginOffset': 10, 'content': '5000円'},
     'type': 'TYPE_UNKNOWN'}],
   'metadata': {'currency': 'JPY', 'value': '5000.000000'},
   'name': '5000円',
   'salience': 0.0,
   'type': 'PRICE'}

\5000とか5000両は取れない。

エンティティ分析(Python)

from google.cloud import language
from google.cloud.language import enums
from google.cloud.language import types

text = '浅草へ行ってスカイツリーを見てきた'

client = language.LanguageServiceClient()

document = types.Document(
    content=text,
    type=enums.Document.Type.PLAIN_TEXT)

entities = client.analyze_entities(document)

# 言語が取れる
print('language={}'.format(entities.language))
    #=> ja

# エンティティの取得
for e in entities.entities: 
    entity_type = enums.Entity.Type(e.type)
    print('==========')
    print('name={}'.format(e.name))
    print('sentiment={}'.format(e.sentiment))
    print('salience={}'.format(e.salience))
    print('type={}, {}'.format(e.type, entity_type.name))
    print('metadata={}'.format(e.metadata))
    print('mentions={}'.format(e.mentions))

実行結果

==========
name=浅草
sentiment=
salience=0.5357578992843628
type=2, LOCATION
metadata={'mid': '/m/01k4r_', 'wikipedia_url': 'https://en.wikipedia.org/wiki/Asakusa'}
mentions=[text {
  content: "\346\265\205\350\215\211"
  begin_offset: -1
}
type: PROPER
]
==========
name=スカイツリー
sentiment=
salience=0.4642421007156372
type=7, OTHER
metadata={}
mentions=[text {
  content: "\343\202\271\343\202\253\343\202\244\343\203\204\343\203\252\343\203\274"
  begin_offset: -1
}
type: COMMON
]

salienceは高いほど文書の中で重要語になるらしい。

改定履歴

Author: Masato Watanabe, Date: 2019-11-02, 記事投稿