NLTK に Sentiment Analysis がやってきた

  • 37
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

NLTK 3.1でSentiment analysis関連が追加されたので試してみました。

* 3.1時点での話だから今後古くなるかも...†

NLTKって?

Natural Language Tool Kit という自然言語処理・テキストマイニングの学習や実験に向いてるPython用ライブラリのこと。
品詞タグ付けや構文解析、情報抽出、意味解析などが簡単にできるようになっていて、オライリーから出ている入門自然言語処理の題材にもなってます。

Sentiment analysisって?

ざっくりいうと文が positive/negative (快/不快あるいは肯定的/否定的) であるかを判定すること。
日本語だと評判分析と呼ばれてたり。

あくまでも2極だけをみるので喜怒哀楽・恥ずかしい・怖いみたいな違いはわからないけど、そこまで細かく知る必要がないときには使えます。

nltk.sentiment の中を覗いてみよう

nltk.sentiment.sentiment_analyzer

ソース冒頭のコメントespecially for teaching and demonstrative
purposes
と書いてある通り、実用性よりも分かりやすさを重視した作りになってる。

使い方の一例

from nltk.classify import NaiveBayesClassifier
from nltk.sentiment import SentimentAnalyzer

# インスタンス生成
sentim_analyzer = SentimentAnalyzer()
# 特徴量抽出関数を登録
# (特徴量抽出関数へのパラメータは後ろにキーワードつけると渡せる)
sentim_analyzer.add_feat_extractor(特徴量抽出関数, パラメ〜タ〜=)
# 対象データセットに先程登録した特徴量抽出関数を適用
training_set = sentim_analyzer.apply_features(学習用テストデ〜タ)
# 好きな学習アルゴリズムをtrainerに入れる
trainer = NaiveBayesClassifier.train
# 学習させる
sentim_analyzer.train(trainer, training_set)

# 判定 (apply_featuresはしなくてよい)
判定したいものたち = [判定したい文1, 判定したい文2, ...]
predicts = sentim_analyzer.classify(判定したいものたち)

特徴量抽出関数はnltk.sentiment.utilに入ってるやつでも自作のでもなんでもOK.

scikit-learnのクラスで学習させたいときは SklearnClassifier で包む必要あり。

from nltk.classify.scikitlearn import SklearnClassifier
from sklearn.svm import LinearSVC

trainer = SklearnClassifier(LinearSVC()).train

nltk.sentiment.util

特徴量抽出の補助関数とデモ用のコードが多いです。

extract_unigram_feats(document, unigrams, handle_negation=False) [source]

特徴量抽出に使うやつ。
document 内に unigrams の語があるかないかを返す。

>>> words = ['ice', 'police', 'riot']
>>> document = 'ice is melting due to global warming'.split()
>>> sorted(extract_unigram_feats(document, words).items())
[('contains(ice)', True), ('contains(police)', False), ('contains(riot)', False)]

extract_bigram_feats(document, bigrams) [source]

特徴量抽出に使うやつ。
↑の2-gram版で、document 内に bigrams のものがあるかないかを返す。

mark_negation(document, double_neg_flip=False, shallow=False) [source]

特徴量抽出に使うやつ。
否定がかかった語の末尾に '_NEG' を付ける。

>>> sent = "I didn't like this movie . It was bad .".split()
>>> mark_negation(sent)
['I', "didn't", 'like_NEG', 'this_NEG', 'movie_NEG', '.', 'It', 'was', 'bad', '.']

nltk.sentiment.vader

VADERというルールベースによるsentiment analysisを実装したもの。

こっちは学習させる必要がないので、わずか3行で極性値を出せる

>>> from nltk.sentiment.vader import SentimentIntensityAnalyzer
>>> vader_analyzer = SentimentIntensityAnalyzer()
>>> vader_analyzer.polarity_scores('I am happy')
FileNotFoundError: [Errno 2] No such file or directory: '/work/venv/py3/lib/python3.5/site-packages/nltk/sentiment/vader_lexicon.txt'

...はずなんだけどモデルのファイルがなかった...😨

とりあえずGitHubからダウンロー

$ curl https://raw.githubusercontent.com/nltk/nltk/develop/nltk/sentiment/vader_lexicon.txt > /work/venv/py3/lib/python3.5/site-packages/nltk/sentiment/vader_lexicon.txt

もっかいチャレンジ!

>>> vader_analyzer.polarity_scores('I am happy')
{'compound': 0.5719, 'neg': 0.0, 'neu': 0.213, 'pos': 0.787}

ゎーぃ

ところでVADERって?

ICWSM-14で提案されたsentiment analysisの手法。

以下の特長があるようです。

  • ソーシャルメディアに出てくるようなテキストに強い
  • 学習データがいらない
  • True/False の2値じゃなくて0.0-1.0の範囲で程度を表す
  • ストリーミングデータに対応できるくらい早い
  • スピードと精度のトレードオフを考えなくてもいい

評価実験では4200ツイートの極性判定でF1値が0.96とのこと。

どんな風にして作られたかざっくり書いてみます。

1. 収録語彙の候補を集める

さまざまな言語資源から収録語彙を収集。

全部で9000くらいになるらしい

2. 人手でスコアリング

Amazon Mechanical Turk (AMT) で人を募って、語のpositive/negativeの度合い[-4, +4]をレーティングする。

このとき、AMTを使うにあたって品質コントロールのためにいろいろやってる。

  • 大学レベル以上の英語読解能力の人に絞る
  • 事前にsentiment ratingのトレーニングなどを受けさせる
  • あらかじめ分布を計算した特定の語 (golden items) よりかけ離れたレートをつける人のデータは使わない
  • 大多数の人が選んだレートと一致してたら報酬を与える

こうしてレーティングされた結果から無効な語彙をカット。

  • 中央値が0の語 (ニュートラルな語)
  • 標準偏差が2.5以上 (文脈によって変わりやすそうな語)

結果、7500語が残ったそう

3. sentiment intensityの特徴をみつける

1.と2.でやったのはpositive/negativeの度合いで、
今度はネガポジ関係ない感情的な強さを示す特徴をみつける。

1万ツイート中からpattern.enのpositive/negativeのスコアが高い順に400ずつツイートを抽出。

人間が各ツイートの強さをレーティングしてそれを分析して5つの特徴を使うことにしている。

  • 感嘆符 (!) の数
  • 全て大文字で書いてる語 (greatをGREATって書いたり)
  • 程度を表す語 (extreamyとかpartlyとか)
  • "but" の後の文は極性をシフトする
  • tri-gram (cut the mustardとか)

おわりに

というわけでNLTK3.1で追加されたSentiment analysisについて紹介しました。(途中からVADER論文の紹介になってたけど......)
ガチで使うには物足りないけど、ちょこっと使ってみる程度にはいいかもと思いました。

過去に日本語を対象にした研究をしてたら途中で海外の方も参加することになって
急いでそこで使われてるsentiment analysis的なものを英語化したりして大変だった記憶があるのですが、
そのときにこれがあれば楽だったなーって悔しいです笑

あと、途中ででてきたpattern.enっていうPythonライブラリも気になりました (まだ試してない)