Edited at

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

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)です。過去のノックの一覧はこちらからどうぞ。


第10章: ベクトル空間法 (II)


第10章では,前章に引き続き単語ベクトルの学習に取り組む.



95. WordSimilarity-353での評価


94で作ったデータを用い,各モデルが出力する類似度のランキングと,人間の類似度判定のランキングの間のスピアマン相関係数を計算せよ.



出来上がったコード:


main.py

# coding: utf-8

import numpy as np

fname_input = 'combined_out.tab'

with open(fname_input, 'rt') as data_file:

# 類似度の配列作成
human_score = []
my_score = []
N = 0

for line in data_file:
cols = line.split('\t')
human_score.append(float(cols[2]))
my_score.append(float(cols[3]))
N += 1

# ソート
human_index_sorted = np.argsort(human_score)
my_index_sorted = np.argsort(my_score)

# 順位の配列作成
human_order = [0] * N
my_order = [0] * N
for i in range(N):
human_order[human_index_sorted[i]] = i
my_order[my_index_sorted[i]] = i

# スピアマン相関係数算出
total = 0
for i in range(N):
total += pow(human_order[i] - my_order[i], 2)
result = 1 - (6 * total) / (pow(N, 3) - N)

print(result)



実行結果:


問題85の単語ベクトルに対する結果


問題85の単語ベクトルに対する結果

0.22645511508225769



問題90の単語ベクトルに対する結果


問題90の単語ベクトルに対する結果

0.5013384068756902



スピアマンの順位相関係数

スピアマン相関係数は、今回の問題のような2つのランキングに対して、どれくらい相関があるかを示す値です。詳細は「スピアマンの順位相関係数」でググるとたくさん出てくるので割愛しますが、次の式で求めることができます。

$$ \rho = 1 - \frac{6 \sum D^2}{N^3 - N} $$

ここで$ D $は個々のデータに対する2つのランキングの順位の差で、$ N $はデータ数です。最大が1で、値が大きいほど相関があります。今回の問題では値が大きければ大きいほど、人間の判断に近いランキングであると言えます。

$ D $を求めるためには、まず問題94で作った「combined_out.tab」の各行に対して、人間の類似度における順位と単語ベクトルにおける順位を求める必要があります。単純にソートしてしまうと元の行が分からなくなって順位差が求められなくなってしまうので、問題75で使ったNumpy.argsort()でソート後のインデックスを求めて、それを使って新たに各行に対する順位の配列を作る形にしてみました。

結果はword2vecの圧勝です。word2vec、すごいですね。

 

(2017年04月23日の18:01追記)

shiracamusさんにアドバイスいただきました。データをクラス化すれば後からそれぞれの順位を追加できるので、もっとスッキリしたコードになります。他にもtotalの算出部分やべき乗の演算子`
`などアドバイスいただきました。以下、修正いただいたコードです。ありがとうございます!


main2.py(shiracamusさんにアドバイスいただいた版)

# coding: utf-8


fname_input = 'combined_out.tab'

class Data:
def __init__(self, human_score, my_score):
self.human_score = human_score
self.my_score = my_score

def __repr__(self):
return 'Data%s' % repr(self.__dict__)

# データ配列作成
with open(fname_input) as data_file:
def read_data():
for line in data_file:
word1, word2, human_score, my_score = line.split('\t')
yield Data(float(human_score), float(my_score))
data = list(read_data())

# 順位付け
data_sorted_by_human_score = sorted(data, key=lambda data: data.human_score)
for order, d in enumerate(data_sorted_by_human_score):
d.human_order = order

data_sorted_by_my_score = sorted(data, key=lambda data: data.my_score)
for order, d in enumerate(data_sorted_by_my_score):
d.my_order = order

# スピアマン相関係数算出
N = len(data)
total = sum((d.human_order - d.my_order) ** 2 for d in data)
result = 1 - (6 * total) / (N ** 3 - N)

print(result)


 

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


実行結果には、100本ノックで用いるコーパス・データで配布されているデータの一部が含まれます。この第10章で用いているコーパス・データのライセンスはクリエイティブ・コモンズ 表示-継承 3.0 非移植日本語訳)です。また、The WordSimilarity-353 Test Collectionのライセンスはクリエイティブ・コモンズ 表示 4.0 国際日本語訳)です。