Edited at

素人の言語処理100本ノック:75

More than 1 year has passed since last update.

言語処理100本ノック 2015の挑戦記録です。環境はUbuntu 16.04 LTS + Python 3.5.2 :: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧はこちらからどうぞ。


第8章: 機械学習


本章では,Bo Pang氏とLillian Lee氏が公開しているMovie Review Dataのsentence polarity dataset v1.0を用い,文を肯定的(ポジティブ)もしくは否定的(ネガティブ)に分類するタスク(極性分析)に取り組む.



75. 素性の重み


73で学習したロジスティック回帰モデルの中で,重みの高い素性トップ10と,重みの低い素性トップ10を確認せよ.



出来上がったコード:


main.py

# coding: utf-8

import codecs
import numpy as np

fname_features = 'features.txt'
fname_theta = 'theta.npy'
fencoding = 'cp1252' # Windows-1252らしい

# 素性読み込み
with codecs.open(fname_features, 'r', fencoding) as file_in:
features = list(file_in)

# 学習結果の読み込み
theta = np.load(fname_theta)

# 重みでソートしてインデックス配列作成
index_sorted = np.argsort(theta)

# 上位、下位10件表示
print('top 10')
for index in index_sorted[:-11:-1]:
print('\t{}\t{}'.format(theta[index],
features[index - 1].strip() if index > 0 else '(none)'))

print('worst 10')
for index in index_sorted[0:10:]:
print('\t{}\t{}'.format(theta[index],
features[index - 1].strip() if index > 0 else '(none)'))



実行結果:


実行結果

top 10

2.5513381450571875 refresh
2.3661550679971954 engross
2.1686400756091198 unexpect
1.9558595013013638 examin
1.921611726928927 remark
1.8642762301453122 glorious
1.6736177761639448 quiet
1.6361584092330672 delight
1.6264395695012035 confid
1.6207851665872708 beauti
worst 10
-2.6661835195544206 bore
-2.381809993645082 dull
-2.264732545707236 wast
-2.0944221636736557 fail
-2.043315628825945 flat
-1.9875250134818985 mediocr
-1.921981567258377 worst
-1.9199082235005225 suppos
-1.9103686908457609 routin
-1.8511208689897838 appar


素性の重み

素性の重みは$ \theta $の値そのものです。値が大きければその語が出現した際に仮説関数の値が肯定側へと大きくなりますし、値がマイナスなら否定側へと小さくなります。

ステミングした結果なので単語が途中で切れているものもありますが、トップ10は肯定的な単語、ワースト10は否定的な単語が並んでいるようですので、思った通りに素性の重みが学習できているようです。


ソートした結果のインデックス取得

素性の重みでソートするのは簡単なのですが、少し工夫が必要です。

コード上では素性の重み$ \theta $は行列thetaに格納されていて、対応する素性の文字列配列はfeaturesに格納されています。ここで単純にthetaの値でソートするとthetaだけが並び変わってしまい、トップ10やワースト10の重みの値はわかるものの、対応する素性がわかりません。そのためfeaturesも連動して並び替えないといけません。これはちょっと面倒です。

ここで便利なのがNumpy.argsort()です。これは対象配列をソートするのですが、その結果として返してくれるのは並び替えた値の配列ではなく、並び替えた結果のインデックスの配列になっています。

例えば今回のように普通に昇順でソートすると、戻り値は [最少値の要素のインデックス, 2番目の要素のインデックス, ... 最大値の要素のインデックス] という形になります。そのため、返ってきたインデックスを使って、素性の配列featuresの該当要素を見れば対応する素性がわかります。これは便利!

なお、1つだけ注意しないといけないのは、thetaは3,228要素からなる行列なのに対してfeaturesは3,227要素である点です。問題73のベクトル化で説明したようにthetaの先頭要素は「素性なし」に対応する重みなので、1つずれています。そのため、featuresの該当要素を調べる時はインデックスを-1してください。インデックスが0なら「素性なし」に対応する重みです。

 

76本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。


実行結果には、100本ノックで用いるコーパス・データで配布されているデータの一部が含まれます。