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

**こちらの記事は少し古くなっています。修正した記事を下記の URL へ移転しました。**

https://jupyterbook.hnishi.com/language-models/easy_try_sentiment_analysis.html

<a href="https://colab.research.google.com/gist/hnishi/c3e7fbf352864aaffcef20e3bb4d5f9a/20200808_try_sentiment_analysis.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>

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

* 感情分析をお手軽に試したいときに使えるツールをまとめました。
* 日本語文章の感情分析の手法については本記事では詳しく触れませんが、以下の記事にわかりやすくまとまっていると思います。
    * [【自然言語処理】感情分析の進め方＆ハマりやすいポイント - Qiita](https://qiita.com/toshiyuki_tsutsui/items/604f92dbe6e20a18a17e)
    * [ディープラーニングを使って転職会議の企業クチコミデータを感情分析してみる - Qiita](https://qiita.com/terrierscript/items/54daa5aedde599e2637c)

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

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

- [asari](https://github.com/Hironsan/asari)
    - [日本語 Sentiment Analyzer を作ってパッケージ化した話 - Ahogrammer](https://hironsan.hatenablog.com/entry/japanese-sentiment-analyzer)
    - sklearnのTfidfVectorizerとLinearSVCを使っている
    - BERT による予測と遜色ない性能
    - トレーニングデータセットが不明
    - MIT ライセンス
    - [デモサイト](https://asari-sentiment.herokuapp.com/)
- [oseti](https://github.com/ikegami-yukino/oseti)
    - [日本語評価極性辞書を利用したPython用Sentiment Analysisライブラリ oseti を公開しました - Qiita](https://qiita.com/yukinoi/items/46aa016d83bb0e64f598)
    - 辞書ベース
    - 実装はシンプル
    - MIT ライセンス
- [pymlask](https://github.com/ikegami-yukino/pymlask)
    - [ML-Askでテキストの感情分析 - Qiita](https://qiita.com/yukinoi/items/ef6fb48b5e3694e9659c)
    - a Python version of ML-Ask (eMotive eLement and Expression Analysis system)
    - 2,100語の辞書によるパターンマッチングで{喜, 怒, 哀, 怖, 恥, 好, 厭, 昂, 安, 驚}の10種類の感情を推定
    - The BSD 3-Clause License
- [huggingface の bert-base-japanese-sentiment](https://huggingface.co/daigo/bert-base-japanese-sentiment)
    - [bert-base-japanese-sentiment モデル作者の BERT HANDSON 資料](https://github.com/ydaigo/BERT_HANDSON/blob/master/BERT_HANDS_ON.ipynb)
    - ネガポジ判定 (ポジティヴとネガティヴの binary classification)
    - bert-base-japanese-char-whole-word-masking というモデルを自作データセットでファインチューニング

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


```python
%%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
```


```python
# 感情分析のためのライブラリ
!pip install -q asari oseti pymlask
```

## データの準備

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

[『人形つかい』  
ハンス・クリスチャン・アンデルセン　Hans Christian Andersen
（矢崎源九郎訳）](https://www.aozora.gr.jp/cards/000019/files/60068_71441.html)

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


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

## asari

- Source code: [asari](https://github.com/Hironsan/asari)
- 解説記事: [日本語 Sentiment Analyzer を作ってパッケージ化した話 - Ahogrammer](https://hironsan.hatenablog.com/entry/japanese-sentiment-analyzer)
- 手法:
    - scikit-learn のみ利用
    - 文章を tf-idf (term frequency–inverse document frequency) でベクトル表現に変換し、それを線形カーネルのサポートベクトルマシンを使って分類問題として文章がポジティヴかネガティヴかを判定している。
    - Deep Learning モデルである BERT による予測と遜色ない性能をしめしたとのこと。
    - どのトレーニングデータセットを使って学習したかが不明なので、どういった種類の文章で適切に判定できるかが不明。
- ライセンス: MIT
- [デモサイト](https://asari-sentiment.herokuapp.com/)


```python
%%capture capt

# シンプルな動作確認
from asari.api import Sonar
sonar = Sonar()
res = sonar.ping(text="広告多すぎる♡")
res 
```


```python
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

- Source code: [oseti](https://github.com/ikegami-yukino/oseti)
- 解説記事: [日本語評価極性辞書を利用したPython用Sentiment Analysisライブラリ oseti を公開しました - Qiita](https://qiita.com/yukinoi/items/46aa016d83bb0e64f598)
- 手法:
    - [日本語評価極性辞書](http://www.cl.ecei.tohoku.ac.jp/index.php?Open%20Resources%2FJapanese%20Sentiment%20Polarity%20Dictionary) ベース
    - 否定表現が文末にある場合は、ネガティヴとポジティヴを反転させている ([code](https://github.com/ikegami-yukino/oseti/blob/1a5dc38ea5688b927fbc4593ffeacad11b2ab491/oseti/oseti.py#L57))
    - 「あるじゃない」という文末が考慮されていて面白い ([code](https://github.com/ikegami-yukino/oseti/blob/1a5dc38ea5688b927fbc4593ffeacad11b2ab491/oseti/oseti.py#L30-L31))
- ライセンス: MIT


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

analyzer = oseti.Analyzer()
analyzer.analyze('天国で待ってる。')
```




    [1.0]




```python
list(map(analyzer.analyze, list_text))
```




    [[0.0], [1.0], [0], [0], [1.0]]



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

と

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

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

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

## pymlask

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

- Source code: [pymlask](https://github.com/ikegami-yukino/pymlask)
- 解説記事: [ML-Askでテキストの感情分析 - Qiita](https://qiita.com/yukinoi/items/ef6fb48b5e3694e9659c)
- 手法:
    - [ML-Ask (eMotive eLement and Expression Analysis system)](http://arakilab.media.eng.hokudai.ac.jp/~ptaszynski/repository/mlask.htm) というライブラリを python で使えるようにしたパッケージ
    - 2,100語の辞書によるパターンマッチングで{喜, 怒, 哀, 怖, 恥, 好, 厭, 昂, 安, 驚}の10種類の感情を推定
- ライセンス: The BSD 3-Clause License


```python
# シンプルな動作確認
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': '彼のことは嫌いではない！(;´Д`)'}




```python
# せっかくなので、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

- [huggingface の bert-base-japanese-sentiment](https://huggingface.co/daigo/bert-base-japanese-sentiment)
    - 解説記事: [bert-base-japanese-sentiment モデル作者の BERT HANDSON 資料](https://github.com/ydaigo/BERT_HANDSON/blob/master/BERT_HANDS_ON.ipynb)
    - ネガポジ判定 (ポジティヴとネガティヴの binary classification)
    - ライセンス: Apache-2.0 License


```python
# 必要なライブラリのインストール
!pip install -q transformers
```


```python
%%capture capt

from transformers import AutoTokenizer, AutoModelForSequenceClassification
from transformers import pipeline

tokenizer = AutoTokenizer.from_pretrained("daigo/bert-base-japanese-sentiment")

model = AutoModelForSequenceClassification.from_pretrained("daigo/bert-base-japanese-sentiment")
```


```python
# シンプルな動作確認

print(pipeline("sentiment-analysis",model="daigo/bert-base-japanese-sentiment",tokenizer="daigo/bert-base-japanese-sentiment")("私は幸福である。"))
```

    [{'label': 'ポジティブ', 'score': 0.9843042492866516}]



```python
sentiment_analyzer = pipeline("sentiment-analysis",model="daigo/bert-base-japanese-sentiment",tokenizer="daigo/bert-base-japanese-sentiment")

list(map(sentiment_analyzer, list_text))
```




    [[{'label': 'ポジティブ', 'score': 0.6794141530990601}],
     [{'label': 'ポジティブ', 'score': 0.972648024559021}],
     [{'label': 'ポジティブ', 'score': 0.8007588386535645}],
     [{'label': 'ポジティブ', 'score': 0.7873095870018005}],
     [{'label': 'ポジティブ', 'score': 0.9143434166908264}]]




```python
# 上記すべてポジティヴ判定になってしまったので、他の例でも試してみる

list(map(sentiment_analyzer, ['最悪だ', '今日は暑い', 'こんにちは', 'ふつう']))
```




    [[{'label': 'ネガティブ', 'score': 0.9896144866943359}],
     [{'label': 'ネガティブ', 'score': 0.8293251991271973}],
     [{'label': 'ポジティブ', 'score': 0.9862995147705078}],
     [{'label': 'ポジティブ', 'score': 0.9928306937217712}]]



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

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

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

## まとめ

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

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

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

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

## 参考

### 解説、まとめ記事

- [【27個掲載】文章、表情、音声の感情分析に使えるデータセットまとめ | Lionbridge AI](https://lionbridge.ai/ja/datasets/15-free-sentiment-analysis-datasets-for-machine-learning/)
    - リソースや極性辞書へのリンクなど
- [筑波大学 乾先生の感情分析リンクまとめ](http://www.nlp.mibel.cs.tsukuba.ac.jp/~inui/SA/links.html)
- [【自然言語処理】感情分析の進め方＆ハマりやすいポイント - Qiita](https://qiita.com/toshiyuki_tsutsui/items/604f92dbe6e20a18a17e)
- [日本語 Sentiment Analyzer を作ってパッケージ化した話 - Ahogrammer](https://hironsan.hatenablog.com/entry/japanese-sentiment-analyzer)
- [ディープラーニングを使って転職会議の企業クチコミデータを感情分析してみる - Qiita](https://qiita.com/terrierscript/items/54daa5aedde599e2637c)
- [小説「天気の子」を丸ごと一冊、感情分析してみた☔️ - Qiita](https://qiita.com/toshiyuki_tsutsui/items/10f52c30fe1504b83ba1)
- [日本語評価極性辞書を利用したPython用Sentiment Analysisライブラリ oseti を公開しました - Qiita](https://qiita.com/yukinoi/items/46aa016d83bb0e64f598)
- [ML-Askでテキストの感情分析 - Qiita](https://qiita.com/yukinoi/items/ef6fb48b5e3694e9659c)
- [bert-base-japanese-sentiment モデル作者の BERT HANDSON 資料](https://github.com/ydaigo/BERT_HANDSON/blob/master/BERT_HANDS_ON.ipynb)

### 感情分析日本語データセット

- [SNOW D18:日本語感情表現辞書 - 長岡技術科学大学 自然言語処理研究室](http://www.jnlp.org/SNOW/D18)
    - 長岡技術科学大学 自然言語処理研究室
    - 約2,000表現を収録し、各表現に対して我々が独自に定義した48分類の感情を付与