1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

2022年版「Cloud Natural Language」活用 第2回|エンティティ分析の方法と成果物を説明する

Last updated at Posted at 2022-07-25

はじめに

ヴェネクト株式会社のディレクター 小峰です。
今回の記事では、”エンティティ分析”と”エンティティ感情分析”を扱います。インプットした文章で言及されているトピックを抽出し、その「重要度」と「感情表現の方向性」と「感情表現の強さ」を取得できます。
エンティティ分析を通して文章中のトピック(何が述べられているか、主題)を取得することで、文章で言及されている内容を取得できます。その結果を利用して弊社内では、大量のレビューに対し「レビュー文章でどのようなトピックが言及されているか」を機械的に集計しています。そのほかにも、文章のカテゴリ分け/ タグ付けに活用したり、言語入力型のインターフェイスで入力内容の識別に利用することができます。
一言でまとめると、「文章中の主題」を集計するための処理になります。

GCP|エンティティ分析
https://cloud.google.com/natural-language/docs/analyzing-entities?hl=ja

プログラム実行環境

Pythonの実行環境

今回はPythonを利用します。Versionは3.7を採用します。
先に必要なPackageを書き出すと下記の通りになります。

# DataFrame変数を処理に活用するため、Importします
import pandas as pd
import numpy as np

# Excelで取得したデータを処理するため、Importします
import xlrd

# 以下はGCPに接続し、APIでやり取りをするため、各種GCP関連のPackageをImportします
from google.cloud import language_v1
import os
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from googleapiclient import discovery
from google.cloud import storage

GCPの「Natural Language API」の利用

また、自然言語処理はGCPの「Natural Language API」を利用します。そのため、GCPのプロジェクトを立ち上げ、課金を有効にし、API利用の認証取得と「Natural Language API」の有効化が必要です。
実際の設定方法ですが、公式のHelpを参考にしてください。

GCP|すべてのクイックスタート
https://cloud.google.com/natural-language/docs/quickstarts?hl=ja
GCP|クイックスタート: Natural Language API の設定
https://cloud.google.com/natural-language/docs/setup?hl=ja

インプットするデータ

取得データの解説

サンプルとして、レビューサイトに記載されている、化粧品の商品レビューを対象にします。レビュー文章だけでなく、レビュー記入者の名前とレビューが記入された日付を取得します。レビューが記載されたExcelをPythonで読み込み、PandasのData Frame変数に変換し、分析を実行します。
スクリーンショット 2022-04-18 20.10.00.png

インプットに向けた加工

エンティティ分析はレビュー文1つずつ実行します。そのため、Excelからデータを読み取り、レビューの一覧を格納したData Frame変数を、レビュー1文づつに分解します。成果物を扱いやすくするために、Dict変数を採用します。Keyにレビュー記入者の名前とレビューが記載された日付を設定し、各レビューをユニークに識別できるようにし、Valueにレビュー文を導入します。
下記のScriptを実行し、インプットするDict変数を得ます。

Reviews = {}
for DF in ListOfReviews:
 for Index, Row in DF.iterrows():
   R = Row["内容"]
   R = R.replace('\n', '')#改行文字\nを削除する
   R = R.replace('\u3000', '')
   I = Index
   Reviews[I] = R
  
Reviews

サンプルコード

サンプルコード例

説明の前にサンプルコードを記載します。

def AccmodateEntities(Input, Dict, Threshold):
 #解析対象の言語特性を定義する
 Type = language_v1.Document.Type.PLAIN_TEXT
 Language = "ja"
 Encoding_type = language_v1.EncodingType.UTF8
 
 
 #各Reviewの分析結果を集計するためのList変数を定義する
 ListEntities_PerReviews = []
 
 #各レビューに対し処理を実行する
 for Key, Value in zip(Input.keys(), Input.values()):
   #各レビューのEntityのみを抜き出し格納するためのList変数を定義する
   ListEntities_ForDictionary = []
 
 
   #分析対象のレビューを取得し、UTF-8に変換した上で解析処理を行う
   Text = Value.encode('utf-8')
   Document = {"content":Text , "type_": Type, "language": Language}
   Response = Client.analyze_entity_sentiment(request = {'document': Document, 'encoding_type': Encoding_type})
        
  
   #各Entityの分析結果を集計するためのList変数を定義する
   ListEntities_PerEntities = []
 
  
   # 各レビューのEntityを取得し、併せてSalience, Sentimentも取得する
   for Entity in Response.entities:
     Name = format(Entity.name)
     Salience = float(format(Entity.salience))
     ET = str(format(Entity.type_.name))
     Sentiment = Entity.sentiment
     SentimentScore = format(Sentiment.score)
     SentimentMagnitude = format(Sentiment.magnitude)
 
    
     #取得したEntityを一覧化したList変数を作成する
     #重要度がThreasold以下のEntityを削除する
     if Salience < Threshold:
       None
     else:
       #Entityの判別結果のMentionが取得対象のListに合致しなければ除外する
       if ET in EntityList:
         #Entity登録の重複を避ける
         if Name in ListEntities_ForDictionary:
           None
         else:
           ListEntities_ForDictionary.append(Name)
       else:
         None
    
 
     #Entity感情分析結果をData Frameに格納する処理を行う
     EntityEmotionResult = pd.DataFrame.from_dict({
         "Date":Key[0],
         "ReviewerName":Key[1],
         "BrandName":Key[2],
         "ReputationFromReviewer":Key[3],
         "Age":Key[4],
         "SkinCondition":Key[5],
         "Review":Value,
         "EntityName":Name,
         "EntitySalience":Salience,
         "EntityCategory":ET,
         "EntitySentimentScore":SentimentScore,
         "EntitySentimentMagnitude":SentimentMagnitude
     },
     orient="index").T
 
     #各Entity毎の処理結果をListEntities_PerEntitiesに格納する
     ListEntities_PerEntities.append(EntityEmotionResult)
  
 
   #個別のEntity出力結果を縦に結合し、レビュー毎の集計結果をまとめる
   EntityReview = pd.concat(ListEntities_PerEntities)
 
   #各Review毎の処理結果をListEntities_PerReviewsに格納する
   ListEntities_PerReviews.append(EntityReview)
 
   #各レビュー毎に出現したEntityを格納するためのDictinnary変数を定義し出力結果のLIstに格納させる
   Dict[Key] = ListEntities_ForDictionary
 
 #個別のReview毎の出力結果を縦に結合し、レビュー毎の集計結果をまとめる
 EntityResult = pd.concat(ListEntities_PerReviews)
 
 #最後に各列のデータ型を定義する
 EntityResult = EntityResult.astype({
     "Date":"object",
     "ReviewerName":"object",
     "BrandName":"object",
     "ReputationFromReviewer":"int",
     "Age":"object",
     "SkinCondition":"object",
     "Review":"object",
     "EntityName":"object",
     "EntitySalience":"float128",
     "EntityCategory":"object",
     "EntitySentimentScore":"float128",
     "EntitySentimentMagnitude":"float128"
 })
 
 return Dict, EntityResult

Entities  = {}
Entities, EntityResult = AccmodateEntities(Reviews, Entities, 0)

解説

上記のスクリプトを実行することで、Keyにレビュー識別用の名前と日付が入力され、Valueに取得したEntityを格納したDict変数が得られます。
Entitiesは、Keyにレビュー情報(レビュー者の名前や日付など)、Valueにエンティティを格納しています。結果、「対象のレビューのエンティティは何か」を格納しています。
EntityResultは、DataFrame変数を採用し、レビュー情報とエンティティ、そのエンティティの特性(SalienceやSentimentなど)を一覧表として格納しています。以降の集計処理で活用します。

最初に「Natural Language API」に送信する言語設定を定義します。今回は日本語(ja)を利用し、文字コードはUTF-8を採用します。

Type = language_v1.Document.Type.PLAIN_TEXT
Language = "ja"
Encoding_type = language_v1.EncodingType.UTF8

「Natural Language API」への送信は下記の処理で実行します。エンティティ感情分析を実行することで、通常のエンティティ分析で得られる成果も取得できるため、エンティティ感情分析を採用し処理を行います。

Text = Value.encode('utf-8')
Document = {"content":Text , "type_": Type, "language": Language}
Response = Client.analyze_entity_sentiment(request = {'document': Document, 'encoding_type': Encoding_type})

結果はJsonで返されます。中身を解説すると下記になります。
まず、第一階層ではentitiesとlanguageが返されます。

{
  "entities": [
    {
      object (Entity)
    }
  ],
  "language": string
}

ここで重要なのはentitiesなので詳しく中身を見ます。

{
  "name": string,
  "type": enum(Type),
  "metadata": {
    string: string,
    ...
  },
  "salience": number,
  "mentions": [
    {
      object(EntityMention)
    }
  ],
  "sentiment": {
    object(Sentiment)
  }
}

各要素について解説します。

  • Name
    • 取得したエンティティです。
    • 入力した文章のトピックが記載されています。
  • type
    • エンティティタイプです。(人名や地名、イベントなど、エンティティ種別が出力されます)
    • エンティティの大まかな種類がわかるため、不要なEntityのフィルタリングに利用できます。
    • 下記のリンクより、詳細をご確認ください。

      GCP|Entity Type
      https://cloud.google.com/natural-language/docs/reference/rest/v1/Entity?hl=ja#Type

  • metadata
    • エンティティに関連付けられるメタデータが記載されます。
    • 英語版であればWikipediaのリンクなどが表示されますが、現時点では日本語は非対応で満足な結果が得られません。そのため無視して構いません。
  • salience
    • エンティティの重要度です。対象のエンティティ文章の中心に位置し、重要だと判断されれば点数が高くなります。
    • [0-1.0]で表現されます。1ほど重要度が高く、0は重要度が低いです。
  • mentions
    • エンティティの文章中での表現です。
    • ただし、現時点では日本語は対応していないため、無視して構いません。
  • sentiment
    • エンティティの感情表現です。
    • その内部はさらに2つの要素を含みます。
      • magnitude
        • 感情表現の強さです。感情的な表現がどれだけ出現し、強い表現であったかを数値化した値です。
        • 0から+∞の間の値です。感情表現が強いほど点数が高くなります。
      • score
        • 感情表現の方向性です。ポジティブな表現であるか、ネガティブな表現であるかを判断できます。
        • -1から+1の間の値で表現されます。-1に近いほどネガティブであり、+1に近いほどポジティブです。0は中立的な表現です。

まとめると、”Name”、 "salience”、”magnitude”、”score”の4者を取得すればOKです。
そのため、下記の処理でJsonから必要な要素を取得できます。

   for Entity in Response.entities:
     Name = format(Entity.name)
     Salience = float(format(Entity.salience))
     ET = str(format(Entity.type_.name))
     Sentiment = Entity.sentiment
     SentimentScore = format(Sentiment.score)
     SentimentMagnitude = format(Sentiment.magnitude)

GCP|Method: documents.analyzeEntitySentiment
https://cloud.google.com/natural-language/docs/reference/rest/v1/documents/analyzeEntitySentiment?hl=ja
GCP|Entity
https://cloud.google.com/natural-language/docs/reference/rest/v1/Entity?hl=ja
GCP|Sentiment
https://cloud.google.com/natural-language/docs/reference/rest/v1/Sentiment?hl=ja

アウトプット

弊社内では2つの形式で成果物を取得します。
まず、レビュー毎にどのようなトピックが出現しているか、格納したDict変数を取得します。
このDict変数を活用すれば、各レビューで言及されるトピックを機械的に取得できます。
スクリーンショット 2022-04-18 20.20.24.png

次に各Entityの特性を分析するためのData Frame変数を作成します。
Entity毎にSalienceとSntimentを取得し、レビュー内でどのように言及されているか、機械的に集計した結果の一覧表を得ます。より分析の完成度を高めるために、レビュー内に記載されている年齢や肌質も取得しています。
スクリーンショット 2022-04-18 20.20.47.png

上記2者を組み合わせて活用することで、ビジネス上の目的に対し回答を得ることが出来ます。

どのように活用できるか

弊社内では商品毎に言及されているトピックを集計し、 「各商品の利用者はどのような点に関心があるか/どのような点を評価しているか」 を商品間で比較することで、マーケティング上で有益な考察を得るために活用しています。
大量のレビューを読むのはマンパワーの負担も大きく、読む人の主観にも左右されやすいですが、自然言語解析を活用することで、効率的かつ客観的な分析を行うことができます。

それ以外にも文章中で言及されているトピックを取得できるため、そのトピックを利用して 文章をカテゴリ分け/ タグ付けする ことに活用も出来ます。ニュース記事や投稿内容のトピックを判断し、機械的な処理に結びつけることができます。
他にもトピックの抽出とそのポジティブ/ネガティブの判別もできるため、 言語入力型のインターフェイスのリクエストの識別 にも活用できるし、顧客対応を目指し、ネガティブな投稿の監視にも役立てられると思います。

次回について

全4回の記事を投稿しておりますので、下記リンクから参照ください

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?