Aidemy Tech Blog

機械学習・ディープラーニング関連技術の活用事例や実装方法をまとめる、株式会社アイデミーの技術ブログです。

【初心者でもわかる】pythonによる簡易自然言語処理(NLP) 超入門【ネガポジ判定】

f:id:tarisaa:20170619232548p:plain

今回の到達点はPythonMeCabを使って2時間前後でAmazonなどのレビューのネガポジ判定をできるプログラムを作るというところにしたいと思います。

自然言語処理
と言うワードを見聞きしたことはありますでしょうか?
この記事はなんとなーく聞いたことあるけど具体的にどうなってるのか、どうやってるのか、知らない。というような人向けの記事になっています。

まず、自然言語処理(NLP:Natural Language Processing)とは、

自然言語処理 - Wikipediaからの引用

自然言語処理(しぜんげんごしょり、英語: natural language processing、略称:NLP)は、人間が日常的に使っている自然言語をコンピュータに処理させる一連の技術であり、人工知能言語学の一分野である。

とあり、具体的には、google翻訳、siri、パソコンの予測変換にも使われていたりするものもあります。
そんな身の回りに溢れた自然言語処理ができてしまえば、いろんなことができるようになります。

例えば、
・長い文章のサマリーを自動で作成してみる。
Twitterなどでツイートのジャンル分けができるようになる。
など、使い方は自分次第です。


と、まあ少し説明をしてみたものの、実際に動かしてみた方が楽しいし、何ができて何が出来ないのかがわかりやすいよね!
ってことで、pythonとか少し触ったことあるよーって人向けにpythonでの自然言語処理の中でも簡単なものの実装をしてみようと思います。
(python触ったことなくても、環境構築さえできれば誰でも試せます!)

(もし、プログラミングとか無理本当ダメって方は下にある全体の流れのフローだけでも読んでいただけると理解が深まるかと思います。)


評価用csvこちらからダウンロードしてください。
github.com

使用する環境、モジュールは

mac OSX10.12.4 Sierra
python 3.6.1
Mecab + natto

大まかにはこのようになっています。
また、他に標準ライブラリをいくつか使用します。

pythonMeCabのインストールはググればたくさん良いサイトが見つかるので、そちらを参考に各自インストールしてください。

私はMeCabのインストールなんかでこのあたりの記事を参考にさせていただきました。
develtips.com
MeCab - 日本語形態素解析システム
MeCab: Yet Another Part-of-Speech and Morphological Analyzer




全体のフローとしては

①評価用のデータの作成。(大量の用言、名詞に点数付けしたリスト)
②評価したいテキストが入ってくる(以下評価テキストとする。)
③1文毎に分解してmecabを使って品詞分解
④用意してあった用言、名詞別リストと照らし合わせ、それぞれの点数を足し合わせていく
⑤ ④の作業を文の数だけ繰り返し、文章全体の点数をだし、判別する。

そして実際にコードを書くに当たって、段階を踏むとすると、

①実際にコードを動かす設定の為のコード
②データやらを読み込む為のコード
③読み込んだデータを処理する為のコード
④処理したデータを出力する為のコード

のように分かれます。

まずは

①実際にコードを動かす設定の為のコード

の部分です。

[getNlp.py]

import codecs
import csv
import re
from natto import MeCab
import os 

上から順に簡単に説明すると

codecs: 文字列のエンコーディングやら諸々をできるようにしてくれるモジュール。
csv:今回文字列評価用のデータをcsvから読み込むのに使う。
re:文字列の正規表現する際に使用するモジュール。今回は文の区切りを判別するのに使う。
from natto import MeCab:MeCabという*形態素解析をしてくれるとても便利なもの。nattoはそれをpythonで使えるようにしてくれるモジュール。
os:自分のpc自体にpythonから干渉できるようにするもの。今回はこのスクリプトが置かれているディレクトリを参照するのに使用。

そして、次に今自分がいるディレクトリをosモジュールを使って読み込む
[getNlp.py]

__location__ = os.path.realpath(
os.path.join(os.getcwd(), os.path.dirname(__file__)))

ここで__location__という変数に今自分のいるpathが指定されます。

次に

②データやらを読み込む為のコード

の部分です。

まず、csvのデータを読み込む部分

[getNlp.py]

def getNegaPosiDic():
    with codecs.open(os.path.join(__location__, "./dataset/yougen.csv"), 'r', 'utf-8') as f_in:
        reader = csv.reader(f_in, delimiter=',', lineterminator='\n')
        negaPosiDic = {}
        for i, x in enumerate(reader):
            y = x[0].split(" ")
            negaPosiDic[y[1]] = y[0]
    with codecs.open(os.path.join(__location__, "./dataset/noun.csv"), 'r', 'utf-8') as f_in:
        reader = csv.reader(f_in, delimiter=',', lineterminator='\n')
        for i, x in enumerate(reader):
            y = x[0].split(" ")
            negaPosiDic[y[1]] = y[0]
    return negaPosiDic

getNefaPosiDicという関数を作成して、csvを読み込んでいます。
具体的には、csv
点数 ワード
点数 ワード
...
といったように保存されているので、それを
ワード 点数
ワード 点数
....
といった形に変形して用言リストと名詞リストを一つのデータにして辞書型というデータ構造にしてpythonで扱いやすいようにして読み込んでます。

次に

③読み込んだデータを処理する為のコード

の部分です。
[getNlp.py]

def nlp(data):
    nm = MeCab() # nmというMeCabクラスのインスタンスを作成
    points = 0 # 文章全体の評価
    negaposi_dic = getNegaPosiDic() # 評価データの読み込み(さっき作った関数を呼び出している。)
    sentenses = re.split("[。!!♪♫★☆>??()w]", data)  # 一文ごとに分ける
    try:
        for sentense in sentenses: # 文の数だけ繰り返す
            negaposi = 0
            result_all = nm.parse(sentense) # 形態素解析して品詞分解をしている。
            result_words = result_all.split("\n")[:-1]  # 単語ごとに分ける
            for word in result_words:
                try:
                    word_toarray = re.split('[\t,]', word)
                    if word_toarray[7] in negaposi_dic:
                            negaposi = int(negaposi_dic[word_toarray[7]])  # その文のネガポジ
        print(word_toarray[7],negaposi_dic[word_toarray[7]], flush=True) # 評価リストに入っていたワードとその評価
                except Exception as e:
                    print('%r' % e, flush=True)
            points += negaposi # 文章全体の評価に加算
    except Exception as e:
        print('%r' % e, flush=True)
        print(data, flush=True)
    return points # 文章全体の値を返す。

大まかな説明はコメントでソースコード中に記載しました。
この中で少し気になるのは、

word_toarray[7]

というのが頻繁に登場することですね。
なぜ、引数が7なのか?それは、MeCabの仕様にあります。

本題に入る前に、"活用形"や"原形"と言う言葉に見覚えはありませんか?
今回は、評価用用言、名詞データはそれぞれの言葉の"原形"で登録されています。
原形というのは、中学校の国語で学んだと思いますが、

活用していない、言葉そのものの形。

ですね。具体的に言うと、

"崩れた","崩れそう","崩れて" は活用形で
"崩れる" は原形ですね。

このように活用形は一つの言葉に対して、活用形は複数あります。
それを全て登録していたのでは「ただでさえデータが多いのに、ものすごい数のデータを登録しなくてはいけなくなる。」
と言うことで、原形のみを登録しています。

しかし、文中で出てくる言葉は活用形の方が多いです。
そこで、Mecabを使います。
MeCabでは入ってきた文章を品詞分解するだけではなく、その活用形や原形なども教えてくれるのです。
そしてその教えてくれる時の形が
以下のようになっています。

['今日', '名詞', '副詞可能', '*', '*', '*', '*', '今日', 'キョウ', 'キョー']
['は', '助詞', '係助詞', '*', '*', '*', '*', 'は', 'ハ', 'ワ']
['月', '名詞', '一般', '*', '*', '*', '*', '月', 'ツキ', 'ツキ']
['が', '助詞', '格助詞', '一般', '*', '*', '*', 'が', 'ガ', 'ガ']
['綺麗', '名詞', '形容動詞語幹', '*', '*', '*', '*', '綺麗', 'キレイ', 'キレイ']
['です', '助動詞', '*', '*', '*', '特殊・デス', '基本形', 'です', 'デス', 'デス']
['ね', '助詞', '終助詞', '*', '*', '*', '*', 'ね', 'ネ', 'ネ']
['。', '記号', '句点', '*', '*', '*', '*', '。', '。', '。']

順番に
'表層形''\t品詞','品詞細分類1','品詞細分類2','品詞細分類3','活用形','活用型','原形','読み','発音'
と、非常にたくさんの情報を単語毎に教えてくれます。
ちなみに品詞の前にある\tと言うのはエスケープシーケンスと言ってタブスペースの代わりに入力される文字列です。

さあ、ここでよくみてもらいたいのですが、
それぞれ左から8番目のところに原形が入っていますね?
これです。これを使う為に

word_toarray[7]

はなんども登場してたんですね。

さらにここで気をつけるのが、8ではなく7ということです。
基本的にコンピュータの世界では数字の始まりは1ではなくて0から始まります。
そうすると普段の私たちの感覚で言う8番目の数字と言うのは
0,1,2,3,4,5,6,7
と、きて7番目ですね。

と言うことで7を使っています。


最後に

④処理したデータを出力する為のコード

の部分です。

ここは他の部分に比べてシンプルです。
[getNlp.py]

if __name__=='__main__':
    data = ""
    while True:
        t = input()
        if t == '':
            break
        data = data + str(t)
    print(nlp(data))

となっています。
ここでは、テキストを受け取りエンターが押されると、処理をして、最後の全体のポイントを出力する。というコードが書かれています。

if __name__=='__main__':

という部分はこのスクリプトが別のプログラムに呼ばれて実行されたかどうかをチェックしており、このプログラムが人の手で実行された時にテキストを受付、処理、出力する。というような動作をするようになっています。

実際にterminalでこのスクリプトが保存されているディレクトリに移動し、
python getNlp.py
を実行し、その後、適当にその辺のAmazonのレビューなどを引っ張ってきて入力してみると
しっかり動いてくれます。

試しに炊飯器のレビューを分析させてみると
[terminal]

$ python getNlp.py
美味しく炊けます。
安定の象印
保温も良く臭くならないので良いです。
食べ過ぎ注意ですね

美味しい 1
良い 1
臭い -1
良い 1
2

と、しっかり出してくれました。

また、このようにレビューのネガポジ判定をしている場合は、テキストと一緒に本当の評価(レビューを書いた人の気持ち)が星でレビューに添えられているので、このプログラムが出した評価と本来の評価とのズレを利用して強化学習などもできるのではないかなと思います。
(強化学習をしようと思った場合大幅に今回のプログラムとは仕組みが変わりますが興味のある方は以下の記事など読んでみて、構想してみてください。)
blog.brainpad.co.jp


ただ、今回のものは本当にシンプルなもので、逆説など、文と文や、言葉と言葉での関わりがあるような文章だとうまく動作してくれません。
なので、次回以降そう言った部分にも少し触れてこうかなとも思います。

今回の全体のソースコードです。
github.com