言語処理100本ノック 2015年版 (90-95)
前回90番のプログラムを載せ忘れていたのでまずはそこから。
#!/usr/bin/env python from gensim.models import word2vec data = word2vec.Text8Corpus('80.txt') model = word2vec.Word2Vec(data, size=300) voc=model.vocab.keys() if __name__ == "__main__": for x in voc: print(x,end=" ") for y in model[x]: print(y,end=" ") print("")
word2vecに任せるだけなのでプログラムは超カンタンです。
91. アナロジーデータの準備
単語アナロジーの評価データをダウンロードせよ.このデータ中で": "で始まる行はセクション名を表す.例えば,": capital-common-countries"という行は,"capital-common-countries"というセクションの開始を表している.ダウンロードした評価データの中で,"family"というセクションに含まれる評価事例を抜き出してファイルに保存せよ.
単語アナロジーとは何か。例を見てみましょう。
: currency Algeria dinar Angola kwanza
「Algeriaの通貨はdinar。ではAngolaの通貨は?」「kwanza」
というようなデータです。クイズとその回答のようなものですね。
#!/usr/bin/env python import codecs import re fin = codecs.open('questions-words.txt', 'r', 'utf_8') if __name__ == "__main__": for line in fin: #読み込み string = re.split(" ",line[:-1]) if string[0] == ":": if string[1] == "family": family = True else: family = False if family == True: print(line[:-1])
もうちょっとマシな切り出しかたもあろうとは思いますが。もっと行数が多ければ「:family」でなくなった時点で終わらせる処理を入れたほうがいいと思います。
結果
: family boy girl brother sister boy girl brothers sisters boy girl dad mom boy girl father mother (略) uncle aunt son daughter uncle aunt sons daughters uncle aunt stepbrother stepsister uncle aunt stepfather stepmother uncle aunt stepson stepdaughter
92. アナロジーデータへの適用
91で作成した評価データの各事例に対して,vec(2列目の単語) - vec(1列目の単語) + vec(3列目の単語)を計算し,そのベクトルと類似度が最も高い単語と,その類似度を求めよ.求めた単語と類似度は,各事例の末尾に追記せよ.このプログラムを85で作成した単語ベクトル,90で作成した単語ベクトルに対して適用せよ.
残念ながら91で作成した単語の中には85や90で作成した単語ベクトルの中に入っていないものがあります。
それは諦めるようにプログラムを作成。
いちいちファイルを読み込んで比較していくと遅いので単語ベクトルをメモリに持たせました。ベクトルの量が多ければこれは動かなくなると思います。
#!/usr/bin/env python import codecs import re import numpy as np import copy def cos_sim(x, y): #コサイン類似度計算 return np.dot(x, y) / (np.linalg.norm(x) * np.linalg.norm(y)) def getallvec(): #全ベクトル読み込み vecdict = {} fin = codecs.open('85logvec.txt', 'r', 'utf_8') for line in fin: #読み込み string1 = re.split(" ",line[:-1]) a = [float(x) for x in string1[1:301] ] vecdict[string1[0]] = copy.deepcopy(np.array(a)) return vecdict def getsim(v,worddict): #類似度計算 string = "" cos_sim1 = 0 for k,v1 in worddict.items(): npe = v1 cos_sim2=cos_sim(v,npe) if cos_sim2 > cos_sim1: #類似度が大きいものに取り替え cos_sim1 = cos_sim2 string = k print(string,end=" ") print("{0:f}".format(cos_sim1)) if __name__ == "__main__": worddict = getallvec() f91 = codecs.open('91.txt', 'r', 'utf_8') for line in f91: #読み込み string = re.split(" ",line[:-1]) if not string[1] in worddict: print(string[1]," not exist.") continue else: npa = worddict[string[1]] if not string[0] in worddict: print(string[0]," not exist.") continue else: npb = worddict[string[0]] if not string[2] in worddict: print(string[2]," not exist.") continue else: npc = worddict[string[2]] npd = npa - npb + npc print(string[1],"-",string[0],"+",string[2],end=" ") getsim(npd,worddict)
結果
85単語ベクトル
girl - boy + brother brother 0.969367 girl - boy + brothers brothers 0.896437 girl - boy + dad boyfriend 0.873149 girl - boy + father father 0.963852 girl - boy + grandfather grandfather 0.871043 grandpa not exist. girl - boy + grandson grandson 0.778333 girl - boy + groom seemingly 0.622918 girl - boy + he he 0.996097 girl - boy + his his 0.998158 (略)
90単語ベクトル
girl - boy + brother brother 0.946068 girl - boy + brothers brothers 0.905701 girl - boy + dad girl 0.750535 girl - boy + father father 0.946162 girl - boy + grandfather grandmother 0.844906 grandpa not exist. girl - boy + grandson granddaughter 0.816291 girl - boy + groom girl 0.746943 girl - boy + he he 0.967500 girl - boy + his his 0.959634 (略)
あまりうまくいっていません。90単語ベクトルの方が「grandmother」や「granddaughter」をうまく類推できています。
93. アナロジータスクの正解率の計算
92で作ったデータを用い,各モデルのアナロジータスクの正解率を求めよ.
正解率出すんだったら最初っから4列目の単語も入れておきたかったな~。
そもそも単語が存在しなかったケースはどう処理すべきか。
今回は分母を減らすのではなくて、不正解として扱います。
#!/usr/bin/env python import codecs import re f85vec = codecs.open('9285vec.txt', 'r', 'utf_8') f91 = codecs.open('91.txt', 'r', 'utf_8') list1 = [] list2 = [] list3 = [] if __name__ == "__main__": for line in f91: #読み込み string = re.split(" ",line[:-1]) list1.append(string[3]) for line in f85vec: #読み込み string = re.split(" ",line[:-1]) if len(string) > 5: list2.append(string[5]) else: list2.append("") n = 0 x = 0 while n < len(list1): list3.append([list1[n],list2[n]]) if list1[n] == list2[n]: x += 1 n += 1 print(x) print(x/len(list1))
結果
85vec: 0.025691699604743084 (506件中13件正解) 90vec: 0.08695652173913043 (506件中44件正解)
思ったより90vecも低い。
94. WordSimilarity-353での類似度計算
The WordSimilarity-353 Test Collectionの評価データを入力とし,1列目と2列目の単語の類似度を計算し,各行の末尾に類似度の値を追加するプログラムを作成せよ.このプログラムを85で作成した単語ベクトル,90で作成した単語ベクトルに対して適用せよ.
類似度はコサイン類似度でいいのかな?
set1とset2の2つのセットが存在するので、2セット*2ベクトル=4セットのデータが生成されます。
#!/usr/bin/env python import codecs import re import numpy as np import copy def cos_sim(x, y): #コサイン類似度計算 return np.dot(x, y) / (np.linalg.norm(x) * np.linalg.norm(y)) def getallvec(): #全ベクトル読み込み a = [] npxdict = {} fin = codecs.open('85logvec.txt', 'r', 'utf_8') for line in fin: #読み込み string1 = re.split(" ",line[:-1]) a = [float(x) for x in string1[1:301] ] npxdict[string1[0]] = copy.deepcopy(np.array(a)) return npxdict if __name__ == "__main__": worddict = getallvec() n = 0 fset = codecs.open('set2.tab', 'r', 'utf_8') for line in fset: #読み込み if n == 0: #タイトル行を飛ばす n += 1 continue string = re.split("\t",line[:-2]) if not string[0] in worddict: print(string[0]," not exist.") continue else: npa = worddict[string[0]] if not string[1] in worddict: print(string[1]," not exist.") continue else: npb = worddict[string[1]] print(string[1],string[0],end=" ") print("{0:f}".format(cos_sim(npa,npb))) n += 1
結果(set1を85で作成した単語ベクトルに適用したもの)
sex love 0.225515 cat tiger 0.823653 tiger tiger 1.000000 paper book 0.531330 keyboard computer 0.121840 internet computer 0.301095 car plane 0.456288 car train 0.314277 communication telephone 0.137384 radio television 0.724248 (略)
結果(set1を90で作成した単語ベクトルに適用したもの)
sex love 0.559486 cat tiger 0.790749 tiger tiger 1.000000 paper book 0.557259 keyboard computer 0.668568 internet computer 0.634529 car plane 0.604410 car train 0.601514 communication telephone 0.543191 radio television 0.783467 (略)
85vecはコンピューターやキーボードに対する類似度が90vecよりかなり低い。
95. WordSimilarity-353での評価
94で作ったデータを用い,各モデルが出力する類似度のランキングと,人間の類似度判定のランキングの間のスピアマン相関係数を計算せよ.
単語がベクトル内に存在しなくて計算できなかった行があるんですよねー。
欠損値は飛ばすか。
スピアマン相関係数かぁ……R使いたいなあ……(この前からこれしか言ってないような)。
まずは順位を計算するプログラムを作って……。
スピアマンの順位相関係数の計算式は以下のとおり。
1-6×Σ(x-y)^2/(n×(n^2-1))
#!/usr/bin/env python import codecs import re f94 = codecs.open('9490set2.txt', 'r', 'utf_8') fset = codecs.open('set2.tab', 'r', 'utf_8') list1 = [] list2 = [] if __name__ == "__main__": n=0 for line in f94: #94データ読み込み string = re.split(" ",line[:-1]) if len(string) > 4: list1.append([n,string[0],string[3],string[4]]) else: list1.append([n,""]) n += 1 n = 0 for line in fset: #set読み込み if n ==0: n += 1 continue string = re.split("\t",line[:-2]) list1[n-1].append(string[2]) n += 1 for x in list1: #欠損値削除 if not x[1] == "": list2.append(x) #並べ替え(cos類似度) list1_2 = sorted(list2, key=lambda sim: sim[3], reverse=True) #順位設定(1から) n=1 for x in list1_2: list1_2[n-1].append(n) n += 1 #並べ替え(人間判定類似度) list1_3 = sorted(list1_2, key=lambda sim: sim[4], reverse=True) #順位設定(1から) n=1 for x in list1_3: list1_3[n-1].append(n) n += 1 #スピアマン相関係数計算 num = len(list1_3) dis=0 for x in list1_3: dis += (int(x[5])-int(x[6]))**2 spearman = 1 - ( 6 * dis / ( num * (num**2 - 1) )) print(spearman)
結果
85vec set1 0.26828305255447715 90vec set1 0.47424152184541535 85vec set2 0.3813785991508144 90vec set2 0.5302377559438987
んー。やっぱりword2vecの精度は高い……