目標
word2vecをgensimのword2vecを使わずにkerasで実装する。
word2vecの概要
word2vecとはニューラルネットワークを使用して単語を分散表現にする方法である。
分散表現とはベクトル化することである。単語は今までBagOfWordで扱われることが多い。ベクトル化することで、単語間の距離を測ることができる。つまり単語間の類似度を知ることができる。
この類似度というのは意味的な類似度を表している。なぜかというと、分散表現にする際に、「わたしは〇〇に行きたい。」といった文があった時に、〇〇に入る言葉はそれぞれ意味が近いという仮定を置いているからである。この仮定のおかげで、教師なしデータでも学習ができるようになっている。この仮定はあっているかもしれないが、もっと良い仮定を思いつけばより良い分散表現が得られそう。
word2vecの理論
word2vecを実装する際に考えなければならないのは、ニューラルネットの構造と、入力データである。
ニューラルネットの構造に関しては、基本的にはなんでも良いと考えている。というよりも、色々ありすぎてまだ何が良いのかわかっていないというのが本当のところである。
word2vecにも色々な種類がある。
・CBoW
・skip gram
・Globe (厳密にはword2vecとは違う?)
これらの違いはうまく説明できない。
ただ、CBowとskip gramは反対の意味らしい。
ここにCBoWとskipgramの違いについて述べられている記事を載せておく。
今回はskip gramの方を実装していく。
実装
使用するデータ
歌詞テキストを10,000件ほど集めた。
データの読み込み
def text2doc(text):
results = tagger.parse(text).split('\n')
doc = [result.token
for result in map(MecabResult, results)
if not result.POS[0] in ['記号', '助詞', '助動詞', None]]
return doc
docs = [text2doc(song['lyric'], 'mecab') for song in list(db.songs.find())]
これを実行すると、
['わたしは犬になりたい', ...]
>> [['わたし', '犬', 'なり'], ... ]
といった感じになります。これはword2vecを利用できるライブラリのgensimのdocsと同じ形式である。
辞書の作成
dic = Dictionary(docs)
dic.id2token = {val:key for key, val in dic.token2id.items()}
dic.filter_extremes(no_below=3, no_above=0.3)
これは単語をidに変換させるためである。filter_extremesでは頻度3以下の語と、全体の0.3以上を占める語を削除している。
学習データの作成
x = np.zeros((len(dic),len(dic)))
for i in range(len(dic)):
x[i][i] = 1
この処理では、ニューラルネットにinputする時の、データを作成している。len(dic) x len(dic)の対角成分のみ1の行列である。
window_size = 3
y = np.zeros((len(dic), len(dic)))
for doc in docs:
for i, word in enumerate(doc):
_id = dic.token2id.get(word, None)
for j in range(window_size*2+1):
k = i-window_size+j
if k >= 0 and len(doc) >k:
target_id = dic.token2id.get(doc[k], None)
if target_id and _id and k != i:
y[_id][target_id] += 1
else:
pass
_id : 現在注目している単語
target_id : _idの周辺からwindow_size内にある単語のid
をさしていて、y[_id][target_id] += 1で、単語の共起回数を数え上げている。
modelの構築
hidden_num = 200
hidden_layer = 2
from keras.layers import Dense, Activation
model = keras.models.Sequential()
model.add(Dense(hidden_num, input_dim = len(dic)))
model.add(Dense(hidden_num , activation='relu', input_dim=100))
model.add(Dense(len(dic), activation='sigmoid'))
model.compile(optimizer='sgd', loss='mse')
modelの学習
model.fit(x, y, epochs=1, batch_size=32)
疑問・課題
・gloveの概要