練習問題2の解答例¶

上の例では直感に合うような結果はなかなか得られませんでした。その原因は以下であると仮定します。

  • 対象としたコーパスのサイズが小さい。
  • ユークリッド距離は単語の出現回数に影響を受けやすい。

この仮定に基づいて、コーパスサイズを大きくし(2016年のwikipediaから10000文)、類似度の計算にコサイン類似度を採用してみてください。このようにすると若干ですが、直感に合うような結果が得られるようになると思います。このデータは、2016年時点のwikipediaから10000文を抜粋したもので、文が改行で分けられ、個々の文に番号が振られ、アルファベット順に並んでいます。

In [1]:
# テキストの読み込み
f = open("../samples/eng_wikipedia_2016_10K-sentences.txt","r")
text = f.read()
In [2]:
import re

# テキストを改行で分ける。
text = text.split("\n")

# なぜか文の最後に数字の列(1942など)を登録してくれないので、
# 数字の列が最後にある文は削除
text2 = []

for i in text:
    obj = re.search("¥d¥.$",i)
    if not obj:
        text2.append(i)
In [3]:
# 文番号を削除
text3 = []

for i in text2:
    s,t = i.split("\t")
    text3.append(t)
In [4]:
# 再度文字列に変換
text = " ".join(text2)
In [5]:
# 単語とそのIDの辞書を作成する。

# 使用するパッケージ
from nltk import word_tokenize
import numpy as np

# 単語分割
words = word_tokenize(text.lower())

# 単語にidをふって辞書を作る
# idがkeyでwordがvalueのディクショナリ
# wordがkeyでidがvalueのディクショナリ
word_to_id = {}
id_to_word = {}

# 単語分割したテキストからword_to_id, id_to_wordを作成する
for word in words:
    if word not in word_to_id:
        new_id = len(word_to_id)
        word_to_id[word] = new_id
        id_to_word[new_id] = word
  • このデータは文単位で分割されているので、文ごとに処理します。
In [6]:
vocab_size = len(word_to_id)
co_matrix = np.zeros((vocab_size,vocab_size),dtype=np.int32)

window_size = 1

for i in text2:
    L = []
    words = word_tokenize(i)
    for w in words:
        L.append(word_to_id[w.lower()])
        
    for idx,word_id in enumerate(L):
        for j in range(1, window_size + 1):
            left_idx = idx - 1
            right_idx = idx + 1
                
            if left_idx >= 0:
                left_word_id = L[left_idx]
                co_matrix[word_id,left_word_id] +=1
                    
            if right_idx < len(words):
                right_word_id = L[right_idx]
                co_matrix[word_id,right_word_id] +=1
In [7]:
# scipyのimport
import scipy.spatial.distance as dis
  • 以下で示すように、"book"と"computer"と"bicycle"の関係はWord-netで示される類似度に近い、あるいは、直感に近い結果が得られます。
  • また、"pork"と"pig"、"car"と"automobile"の類似度が1となっていて、かなり妥当な結果と言えます。
In [8]:
dis.cosine(co_matrix[word_to_id["book"]], co_matrix[word_to_id["computer"]])
Out[8]:
np.float64(0.29859492016274536)
In [9]:
dis.cosine(co_matrix[word_to_id["book"]], co_matrix[word_to_id["bicycle"]])
Out[9]:
np.float64(0.46714150842553825)
In [10]:
dis.cosine(co_matrix[word_to_id["computer"]], co_matrix[word_to_id["bicycle"]])
Out[10]:
np.float64(0.6464466094067263)
In [11]:
dis.cosine(co_matrix[word_to_id["pork"]], co_matrix[word_to_id["pig"]])
Out[11]:
np.float64(1.0)
In [12]:
dis.cosine(co_matrix[word_to_id["car"]], co_matrix[word_to_id["automobile"]])
Out[12]:
np.float64(1.0)