極性辞書を使用した感情分析に挑戦【自然言語処理の基礎】

こんにちは、@Yoshimiです。

私たち人間には「好き」「嫌い」「ポジティブ」「ネガティブ」などの感情・気持ちがあります。そのような感情・気持ちまでもAIで判断できるようになってきました(多分)。今回は、感情分析、別名ネガポジ分析の方法・コーディングを紹介します。

極性辞書とは

自然言語処理では、文章の単語(キーワード)、文脈などを解析します。ポジティブ(ネガティブ)な文面であれば、特有の単語が含まれているであろうという考えから集められた単語リストのことです。

例えば「笑顔」という単語であればポジティブに入り、「不満」という単語であればネガティブという具合です。どういう基準でポジティブ、ネガティブと判断しているのかは、スコア(−1~1)の形で表現しております。

公開されている有名な極性辞書は以下の2つです。

感情分析とは

人間には「好き」「嫌い」「ポジティブ」「ネガティブ」などの感情があります。その分析をすることを感情分析とよんでいます。よくネガポジ分析など耳にすると思いますが、それです。

昨今、Twitterなど気軽言葉を発信できるようになりました。ニュース(出来事)や製品への意見・クレーム(もちろんポジティブな意見もあり)なども集めるのが容易になり、より消費者に近づくことができるようになりました。そのようなデータを有効活用することに注目が集まっています。

他にもビジネスシーンで考えるとすれば、滅多にないですが、社内チャットでの何かしらへの批判や意見です。お客様の声もそうです。従業員であれば退職につながってしまう、または事件が起きそうなどの判定にも利用できるかもしれません。お客様の声はダイレクトに企業へひびきます。

このようなデータから人々は何を感がているのかを突き止めるのに役立ちます。

感情分析のデータセット(自然言語編)

※文章パクリなので修正する
Twitter日本語評判分析データセット: Twitterの日本語評判分析データセットは、ツイートの評判情報をクラウドソーシングにより分析され、分析結果が公開されております。

OpinRankデータセット: トリップアドバイザーと車の情報サイト、エドマンズのウェブサイトから車やホテルの完全なレビューをトータルで30万件収集したデータセット。

レストランレビューデータセット: ニューヨーク地域のレストランの52,000件のレビューをレーティング付きで収集。

センチメント140: 16万件のツイートから顔文字を取り除き、6種類のフイールドで構成したデータセット。特に、ブランドマネジメントや世論調査に役立つ。

論文レビューデータセット: 学術論文レビューの意見を予測するために作成されたデータセット。コンピューティングに関するカンファレンスからスペイン語と英語のレビューを収集。

実装してみる

早速、実装してみます。livedoorニュースコーパスの内、トピックニュースーをデータセットとして使います。(提供元:株式会社ロンウイット)ありがとうございます!

2012年9月上旬 ダウンロード(通常テキスト):ldcc-20140209.tar.gz ダウンロードします。解答するといくつかのディレクトリが確認できますが、topic-newsディレクトリ配下の.txtデータを使っていきます。

適当にデータの中身を確認します。

http://news.livedoor.com/article/detail/5913367/
2011-10-05T11:53:00+0900
韓国がハブられたiPhone4S発表会
 米アップルが4日に発表したiPhone4S。iPhone5への期待を裏切り、一時株価にまで影響を与えたこの発表会において、ひそかにもうひとつ話題になっていることがある。・・・・

1行目はURL(リンク)、2行目は日付、3行目がタイトル、4行目以降が記事本文となっていることが確認できます。分析に不必要な単語(キーワード)は削除していきます。1行目、2行目など不要対象です。データクリーニングもしっかり行います。

必要なライブラリを読み込んでおきます。

import glob
import re
import pandas as pd

livedoorの記事を読み込んでリスト化します。globメソッドでディレクトリにあるファイル一覧を読み取ります。for文内部では、データクレンジングなどを行います。分析対象のテキスト(記事本文)のみを抽出します。

# livedoorトピックニュースの文章リスト
texts = []

# livedoorトピックニュースのファイル名一覧を取得する
paths = glob.glob('./txt_data/topic-news/topic-news-*.txt')

for path in paths:
    with open(path, 'r') as f:
        original_text = f.read()
        
        # 先頭2行は不要なメタ情報のため、削除
        text = re.sub(r'^.*\n.*\n', '', original_text) 
        # "【関連"や"■関連"以降は削除
        result = re.search(r'(【|■)関連', text)
        if result is not None:
            text = text[:result.start()]
            
        texts.append(text)

# 最初の1件を表示
print(texts[0])

分析する本文ですが、英文のように単語単語の間がスペースで区切られていたりするのであれば比較的容易に分析が可能です。しかし、日本語は単語単語のスペースがなく、句読点で区切られていたりいなかったり、さらに、名詞、動詞、形容詞など文章の中で混在しています。その混在を分けるために分かち書きという技術を使います。

分かち書きとは 英語のようにことばのくぎりに空白をいれる書きかたです。

分かち書きで単語単語に区切ります。

日本語で有名なライブラリはMeCabやJUMAN++を使うことで精度の高い形態素解析が可能です。Mecabは辞書のダウンロードなども必要でちょっと手間です。その手間を包括しているのがjanomeというライブラリです。

janomeはjanome.tokenizer.Tokenizerを使うと分かち書きされ、品詞ラベル(名詞、動詞、助詞・・・)も付与してくれるという優れものです。

from janome.tokenizer import Tokenizer

class CorpusElement:
    def __init__(self, text='', tokens=[], pn_scores=[]):
        self.text = text # テキスト本文
        self.tokens = tokens # 構文木解析されたトークンのリスト
        self.pn_scores = pn_scores # 感情極性値(後述)


# CorpusElementのリスト
naive_corpus = []

naive_tokenizer = Tokenizer()

for text in texts:
    tokens = naive_tokenizer.tokenize(text)
    element = CorpusElement(text, tokens)
    naive_corpus.append(element)

# 最初の1文章の形態素解析結果を表示
for token in naive_corpus[0].tokens:
    print(token)

一部抜粋します。

河本  名詞,固有名詞,人名,姓,*,*,河本,カワモト,カワモト
の  助詞,連体化,*,*,*,*,の,ノ,ノ
生活  名詞,サ変接続,*,*,*,*,生活,セイカツ,セイカツ
保護  名詞,サ変接続,*,*,*,*,保護,ホゴ,ホゴ
問題  名詞,ナイ形容詞語幹,*,*,*,*,問題,モンダイ,モンダイ
で  助詞,格助詞,一般,*,*,*,で,デ,デ
、  記号,読点,*,*,*,*,、,、,、
オリ  名詞,固有名詞,一般,*,*,*,オリ,オリ,オリ
ラジ  名詞,固有名詞,一般,*,*,*,ラジ,ラジ,ラジ
・  記号,一般,*,*,*,*,・,・,・
藤森  名詞,固有名詞,人名,姓,*,*,藤森,フジモリ,フジモリ

上記をみると、名詞、助詞など様々な品詞に分かち書きされたことがわかります。

さて、この分かち書きされた単語には「良い(positive)」「悪い(negative)」のスコアが振られています。前述した極性辞書にリスト化されています。スコア領域は-1~1の間です。

この極性辞書を使い、livedoorトピックニュースの極性値を算出します。

load_pn_dict関数:単語をキー、極性値を値とする辞書

  • pn_dic[‘良い’] の値は 0.999995
  • pn_ja.dicはシフトJISなので、codecsをインポート

get_pn_scores関数:janome.tokenizer.Tokenリストから対応する極性値リストを返す

  • Token.part_of_speechで 名詞,一般,*,* などの品詞情報が取得できます(後は”,”で分けて0番目の要素を取得すれば、品詞がわかります)
  • 活用形による違いを吸収して対応表にヒットさせやすいようにするため、Token.base_formプロパティで原型を取り出しています
  • pn_ja.dicは名詞、動詞、形容詞、副詞のみのため、それ以外の品詞は除外しています
import codecs


# pn_ja.dicファイルから、単語をキー、極性値を値とする辞書を得る
def load_pn_dict():
    dic = {}
    
    with codecs.open('./pn_ja.dic', 'r', 'shift_jis') as f:
        lines = f.readlines()
        
        for line in lines:
            # 各行は"良い:よい:形容詞:0.999995"
            columns = line.split(':')
            dic[columns[0]] = float(columns[3])
            
    return dic


# トークンリストから極性値リストを得る
def get_pn_scores(tokens, pn_dic):
    scores = []
    
    for surface in [t.surface for t in tokens if t.part_of_speech.split(',')[0] in ['動詞','名詞', '形容詞', '副詞']]:
        if surface in pn_dic:
            scores.append(pn_dic[surface])
        
    return scores


# 感情極性対応表のロード
pn_dic = load_pn_dict()
print(pn_dic['良い'])
# 0.999995

# 各文章の極性値リストを得る
for element in naive_corpus:
    element.pn_scores = get_pn_scores(element.tokens, pn_dic)

# 1件目の文章の極性値を表示する
print(naive_corpus[0].pn_scores)

全体的にネガティブな値が算出されました。

[-0.937753, -0.608183, -0.716755, -0.544844, -0.903573, -0.937753, -0.608183, 0.969083, -0.649968, -0.301489, -0.145898, -0.191821, -0.516111, -0.974571, -0.291937, -0.649968, -0.457258, -0.177296, -0.145898, -0.123772, -0.658478, 0.207418, 0.0834557, -0.895241, -0.199786, -0.424823, -0.338876, -0.443555, -0.496628, -0.810152, -0.649968, 0.207418, -0.758135, 0.0834557, -0.895241, -0.199786, -0.937753, -0.608183, 0.969083, -0.547411, 0.921866, -0.339703, -0.974571, -0.424823, -0.339703, -0.453675, -0.785235, -0.649968, -0.547411, -0.457258, -0.877473, -0.939987, -0.392227, -0.93136, -0.199786, -0.585094, -0.985854, -0.940497, -0.675635, -0.824814, -0.80819, -0.877473, -0.447796, -0.93584, -0.312943, -0.824814, 0.00838344, -0.518585, -0.904511, -0.999334, -0.99444, -0.984295, -0.522181, -0.895241, -0.199786, -0.419754, -0.572353, -0.254232, -0.552305, -0.974571, -0.922327, -0.443555, -0.199786, 0.0834557, -0.924931, -0.339703, 0.238599, 0.0008502, 0.238599, -0.426941, -0.424823, -0.573711, -0.86477, -0.979874, -0.339703, -0.351288, -0.424823, -0.339703, -0.785235, -0.93584, 0.956891, -0.457258]
import io

# 平均値が最も高い5件を表示
for element in sorted(naive_corpus, key=lambda e: sum(e.pn_scores)/len(e.pn_scores), reverse=True)[:5]:
    print('Average: {:.3f}'.format(sum(element.pn_scores)/len(element.pn_scores)))
    print('Title: {}'.format(io.StringIO(element.text).readline()))

print('=' * 10)
    
# 平均値が最も低い5件を表示
for element in sorted(naive_corpus, key=lambda e: sum(e.pn_scores)/len(e.pn_scores))[:5]:
    print('Average: {:.3f}'.format(sum(element.pn_scores)/len(element.pn_scores)))
    print('Title: {}'.format(io.StringIO(element.text).readline()))

上位5つを見てみると「祝福」「笑い」などポジティブ要素が含まれていますが、「離婚」というキーワードも気になります。平均値が低い5つは「死亡」「賠償」「DV」など明らかにネガティブな単語です。

Average: -0.077 Title: 結婚した亀田興毅の評価がネット掲示板で急上昇
Average: -0.194 Title: ゆずの北川悠仁、結婚後初のメディア出演!!大勢のリスナーからの祝福メールに大感謝!!
Average: -0.259 Title: 慶應大学だけ書籍の売れ筋が「おかしい」と話題に
Average: -0.267 Title: 西岡剛選手と離婚報道の徳澤直子に「笑いがとまらないだろう」
Average: -0.272 Title: 2011年・大学ミスコンの美女たちを一挙公開
==========
Average: -0.680 Title: チェルノブイリ再来か、福島で恐れていた事態が遂に…
Average: -0.673 Title: “金総書記死亡”に対する韓国での反応
Average: -0.672 Title: フジテレビの姿勢に非難殺到…番組で″火渡り″させ、老人が歩行不能の大火傷
Average: -0.662 Title: 「東電からの賠償額がわずか333円」に非難の嵐
Average: -0.659 Title: 黒田勇樹のDV騒動 ネット掲示板では冷ややかな声も

各文章の単語数、品詞数なども調べると気がつかなったことも気がつけるかもしれませんね。

考え所

極性辞書を使用した感情分析に挑戦してみました。いろいろなブログや研究を参考にしてみましたが、もちろんデメリットもあるようです。

  • 極性辞書にない単語は判定できない
  • 文脈によって異なる単語は誤って判定される

例えば、「悪くない」というフレーズは「悪く」「ない」に別れます。辞書からスコアを算出すると「悪く=ネガティブ」「ない=ネガティブ」もネガティブなワードと算出される可能性があります。本来であれば、この「悪くない」は「良い」ということなのですが、これが日本語の難しさです。また、「やばい」という単語もそれに該当します。「このアイスクリームやばいね!」という文章です。この会話は、きっと「このアイスクリーム非常に美味しい」という意味なはずです。皆さんも「やばい」という言葉を「すごい」とかのポジティブ表現として使ったことはあると思います。しかし、極性辞書は「やばい」は-0.990268で登録されていますので、常にネガティブだと判定されてしまうのです。

難しい!

機械学習の敷居が下がってきたということで機械学習による感情分析が主流になってきています。機械学習を用いることでアルゴリズムが学習に基づいて文脈から感情を判定することも可能になるので、大局的にポジティブなのか、ネガティブなのかを判定することが可能になるということです。

「このアイスクリームやばいね!みんなにお勧めしたいね。」→ポジティブ
「やばい、頭が痛い」→ネガティブ

難しい!

最後に

正直今回は、自力で解析したという物ではありません。ohke (id:ohke)さんのブログの写経です。取得したコーパスをデータフレームに入れたり、Twitter APIを使い感情分析、ネガポジ分析にチャレンジしていきたいと思います。

参考:https://ohke.hateblo.jp/entry/2017/10/27/230000


なりたい自分になれる
スキルアップならUdemy

私も利用し、高収入エンジニアになったのよ。未経験から機械学習、データサイエンティスト、アプリ開発エンジニアを目指せるコンテンツが多数あります。優秀な講師が多数!割引を利用すれば1,200円〜から動画購入可能です。!

ABOUTこの記事をかいた人

大学卒業して、キラキラしていたのでIT業界にはいりましたが、中身はブラックでした!!だから、投資技術を磨いて早くリタイヤしたいです。株価、Python、機械学習をもうもう勉強中です。経済的自由を手に入れて農家やりたい!