Open In Colab

Python で日本語文章の感情分析を簡単に試す (with google colab)

感情分析を簡単に試すときに使えるツール一覧

試してみたツールを箇条書きにして以下に示します。

必要なライブラリのインストール

%%capture capt

# MeCabのインストール
!apt install mecab libmecab-dev mecab-ipadic-utf8
!pip install mecab-python3

# mecab-ipadic-NEologdのインストール
!apt install git make curl xz-utils file
!git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git
!echo yes | mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -n -a

# Ref: https://qiita.com/Fulltea/items/90f6ebe6dcceaf64eaef
# Ref: https://qiita.com/SUZUKI_Masaya/items/685000d569452585210c

!ln -s /etc/mecabrc /usr/local/etc/mecabrc
# Ref: https://qiita.com/Naritoshi/items/8f55d7d5cce9ce414395
# 感情分析のためのライブラリ
!pip install -q asari oseti pymlask
# asari==0.0.4 requires Janome==0.3.7
# see https://github.com/Hironsan/asari/issues/9#issuecomment-695706645
!pip install Janome==0.3.7
Requirement already satisfied: Janome==0.3.7 in /usr/local/lib/python3.6/dist-packages (0.3.7)

データの準備

感情分析の入力として使う文章は、青空文庫の

『人形つかい』
ハンス・クリスチャン・アンデルセン Hans Christian Andersen (矢崎源九郎訳)

からピックアップさせていただきました。

list_text = [
             'この人は、この世の中で、いちばんしあわせな人にちがいありません。',
             '芝居小屋もすばらしいし、お客さんもすばらしい人たちでした。',
             'もし中世の時代だったら、おそらく、火あぶりにされたでしょうよ。',
             'みんなのうるさいことといったら、まるで、ハエがびんの中で、ブンブンいっているようでした。',
             'われわれ人間が、こういうことを考えだすことができるとすれば、われわれは、地の中にうめられるまでに、もっと長生きできてもいいはずだが'
]

asari

  • Source code: asari

  • 解説記事: 日本語 Sentiment Analyzer を作ってパッケージ化した話 - Ahogrammer

  • 手法:

    • scikit-learn のみ利用

    • 文章を tf-idf (term frequency–inverse document frequency) でベクトル表現に変換し、それを線形カーネルのサポートベクトルマシンを使って分類問題として文章がポジティヴかネガティヴかを判定している。

    • Deep Learning モデルである BERT による予測と遜色ない性能をしめしたとのこと。

    • どのトレーニングデータセットを使って学習したかが不明なので、どういった種類の文章で適切に判定できるかが不明。

  • ライセンス: MIT

  • デモサイト

%%capture capt

# シンプルな動作確認
from asari.api import Sonar
sonar = Sonar()
res = sonar.ping(text="広告多すぎる♡")
res 
list(map(sonar.ping, list_text))
[{'classes': [{'class_name': 'negative', 'confidence': 0.10382535749585702},
   {'class_name': 'positive', 'confidence': 0.896174642504143}],
  'text': 'この人は、この世の中で、いちばんしあわせな人にちがいありません。',
  'top_class': 'positive'},
 {'classes': [{'class_name': 'negative', 'confidence': 0.035517582235360945},
   {'class_name': 'positive', 'confidence': 0.964482417764639}],
  'text': '芝居小屋もすばらしいし、お客さんもすばらしい人たちでした。',
  'top_class': 'positive'},
 {'classes': [{'class_name': 'negative', 'confidence': 0.5815274190768989},
   {'class_name': 'positive', 'confidence': 0.41847258092310113}],
  'text': 'もし中世の時代だったら、おそらく、火あぶりにされたでしょうよ。',
  'top_class': 'negative'},
 {'classes': [{'class_name': 'negative', 'confidence': 0.2692695045573754},
   {'class_name': 'positive', 'confidence': 0.7307304954426246}],
  'text': 'みんなのうるさいことといったら、まるで、ハエがびんの中で、ブンブンいっているようでした。',
  'top_class': 'positive'},
 {'classes': [{'class_name': 'negative', 'confidence': 0.050528495655525495},
   {'class_name': 'positive', 'confidence': 0.9494715043444746}],
  'text': 'われわれ人間が、こういうことを考えだすことができるとすれば、われわれは、地の中にうめられるまでに、もっと長生きできてもいいはずだが',
  'top_class': 'positive'}]

「みんなのうるさいことといったら、まるで、ハエがびんの中で、ブンブンいっているようでした。」という文章は直感的にはネガティヴな印象であるが、ポジティヴとの判定になった。

他の例については妥当な判定が出ていそう。

oseti

# シンプルな動作確認
import oseti

analyzer = oseti.Analyzer()
analyzer.analyze('天国で待ってる。')
[1.0]
list(map(analyzer.analyze, list_text))
[[0.0], [1.0], [0], [0], [1.0]]

2番めの文章「芝居小屋もすばらしいし、お客さんもすばらしい人たちでした。」

5番目の文章「われわれ人間が、こういうことを考えだすことができるとすれば、われわれは、地の中にうめられるまでに、もっと長生きできてもいいはずだが」

のみポジティヴ (+1) 判定で、他の文章に関しては ニュートラルの判定。

やはり、辞書ベースだと、辞書に含まれていない単語には弱いという印象。

pymlask

パッケージの作者は oseti と同じ。

# シンプルな動作確認
import mlask
emotion_analyzer = mlask.MLAsk()
emotion_analyzer.analyze('彼のことは嫌いではない!(;´Д`)')
# => {'text': '彼のことは嫌いではない!(;´Д`)',
#     'emotion': defaultdict(<class 'list'>,{'yorokobi': ['嫌い*CVS'], 'suki': ['嫌い*CVS']}),
#     'orientation': 'POSITIVE',
#     'activation': 'NEUTRAL',
#     'emoticon': ['(;´Д`)'],
#     'intension': 2,
#     'intensifier': {'exclamation': ['!'], 'emotikony': ['´Д`', 'Д`', '´Д', '(;´Д`)']},
#     'representative': ('yorokobi', ['嫌い*CVS'])
#     }
{'activation': 'NEUTRAL',
 'emoticon': ['(;´Д`)'],
 'emotion': defaultdict(list, {'suki': ['嫌い*CVS'], 'yorokobi': ['嫌い*CVS']}),
 'intensifier': {'emotikony': ['´Д`', 'Д`', '´Д', '(;´Д`)'],
  'exclamation': ['!']},
 'intension': 2,
 'orientation': 'POSITIVE',
 'representative': ('yorokobi', ['嫌い*CVS']),
 'text': '彼のことは嫌いではない!(;´Д`)'}
# せっかくなので、neologd 辞書を使ってみる

# mecab-ipadic-neologd のインストール先を調べる
import subprocess

cmd='echo `mecab-config --dicdir`"/mecab-ipadic-neologd"'
path = (subprocess.Popen(cmd, stdout=subprocess.PIPE,
                           shell=True).communicate()[0]).decode('utf-8')
                           
emotion_analyzer = mlask.MLAsk('-d {0}'.format(path))  # Use other dictionary

list(map(emotion_analyzer.analyze, list_text))
[{'activation': 'NEUTRAL',
  'emoticon': None,
  'emotion': defaultdict(list, {'yorokobi': ['しあわせ']}),
  'intensifier': {},
  'intension': 0,
  'orientation': 'POSITIVE',
  'representative': ('yorokobi', ['しあわせ']),
  'text': 'この人は、この世の中で、いちばんしあわせな人にちがいありません。'},
 {'emotion': None, 'text': '芝居小屋もすばらしいし、お客さんもすばらしい人たちでした。'},
 {'emotion': None, 'text': 'もし中世の時代だったら、おそらく、火あぶりにされたでしょうよ。'},
 {'emotion': None, 'text': 'みんなのうるさいことといったら、まるで、ハエがびんの中で、ブンブンいっているようでした。'},
 {'emotion': None,
  'text': 'われわれ人間が、こういうことを考えだすことができるとすれば、われわれは、地の中にうめられるまでに、もっと長生きできてもいいはずだが'}]

こちらの手法も、辞書にある単語(しあわせ)があるとポジティヴと判定があるが、辞書にはないと判定が不可能。

いまいちな結果という印象。

huggingface の bert-base-japanese-sentiment

# 必要なライブラリのインストール
!pip install -q transformers
     |████████████████████████████████| 2.1MB 8.7MB/s 
     |████████████████████████████████| 3.2MB 41.5MB/s 
     |████████████████████████████████| 890kB 52.9MB/s 
?25h  Building wheel for sacremoses (setup.py) ... ?25l?25hdone
%%capture capt

from transformers import BertTokenizer, BertForSequenceClassification, pipeline

tokenizer = BertTokenizer.from_pretrained("daigo/bert-base-japanese-sentiment")
model = BertForSequenceClassification.from_pretrained("daigo/bert-base-japanese-sentiment")
# シンプルな動作確認

print(pipeline("sentiment-analysis",model=model, tokenizer=tokenizer)("私は幸福である。"))
[{'label': 'ポジティブ', 'score': 0.9934489130973816}]
sentiment_analyzer = pipeline("sentiment-analysis",model=model, tokenizer=tokenizer)

list(map(sentiment_analyzer, list_text))
[[{'label': 'ポジティブ', 'score': 0.9596116542816162}],
 [{'label': 'ポジティブ', 'score': 0.6044701933860779}],
 [{'label': 'ポジティブ', 'score': 0.8851077556610107}],
 [{'label': 'ポジティブ', 'score': 0.6943467855453491}],
 [{'label': 'ポジティブ', 'score': 0.5758240222930908}]]
# 上記すべてポジティヴ判定になってしまったので、他の例でも試してみる

list(map(sentiment_analyzer, ['最悪だ', '今日は暑い', 'こんにちは', 'ふつう']))
[[{'label': 'ネガティブ', 'score': 0.9891454577445984}],
 [{'label': 'ネガティブ', 'score': 0.8046908974647522}],
 [{'label': 'ポジティブ', 'score': 0.9862995147705078}],
 [{'label': 'ポジティブ', 'score': 0.9928306937217712}]]

用意したすべての例文すべてがポジティヴ判定になってしまったので、他の例でも試してみた。

わかりやすいネガティヴな文章は、ネガティヴと判定されるよう。

ニュートラルな文章は、ポジティヴに判定されやすい傾向がありそう。

まとめ

日本語文章の感情分析を簡単にできるツールを試してみました。

こういったツールを公開していただけていることに感謝ですね。

真面目に感情分析やって、もっと妥当な結果を出そうとすると、トレーニングデータセットを充実させたり、さらに工夫が必要になりそうです。

(手法ごとの感情分析の結果を定量的に評価するためのデータセット、いいのないだろうか。。。)