rnnlm.py
・ライブラリのインポート
#!/usr/bin/env python
import numpy as np
import chainer
from chainer import cuda, Function, gradient_check, Variable, \
optimizers, serializers, utils
from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L
・ファイル読み込み、前処理
def load_data(filename):
global vocab
words = open(filename).read().replace('\n', '<eos>').strip().split()
dataset = np.ndarray((len(words),), dtype=np.int32)
for i, word in enumerate(words):
if word not in vocab:
vocab[word] = len(vocab)
dataset[i] = vocab[word]
return dataset
train_data = load_data(train_path)
eos_id = vocab['<eos>']
- words :文章を読み込み、文末に
<eos>
タグを追加、行頭と行末の改行コードを削除する - vocab :単語を重複無しで追加し、インデックスを付けて保存
- dataset :単語のインデックス番号を配列として保存
・クラス定義
・--init--
部
class MyRNN(chainer.Chain):
def __init__(self, v, k):
super(MyRNN, self).__init__(
embed = L.EmbedID(v, k), # Word embedding layer
H = L.Linear(k, k), # L1 layer
W = L.Linear(k, v), # L2 layer
)
・--init--
部は、モデルのセット時に1度だけ用いられる。
・v
,k
を引数として受取り、レイヤを定義する。
- input layer : 語彙数、
len{vocab}
より、439(?) - hidden layer :
demb
より100 - output layer : input layer と同じ
・__call__
部
def __call__(self, s):
accum_loss = None
v, k = self.embed.W.data.shape
h = Variable(np.zeros((1,k), dtype=np.float32)) # define variable h [ it means x = Variable(x_data, dtype=np.float32) ]
for i in range(len(s)): # iterate word in sentenc s = [0,1,2,3] -> 4 iterate
next_w_id = eos_id if (i == len(s) - 1) else s[i+1] # if next word is in here, else next word is <eos>
tx = Variable(np.array([next_w_id], dtype=np.int32)) # tx is next word ID ( = ground truth data )
x_k = self.embed(Variable(np.array([s[i]], dtype=np.int32))) #x_k ==Wx
h = F.tanh(x_k + self.H(h)) # sekf.H(h) is W'h
loss = F.softmax_cross_entropy(self.W(h), tx)
accum_loss = loss if accum_loss is None else accum_loss + loss
return accum_loss, x_k, self.W(h)
・__call__
部は、モデルの定義完了後に実行される。
v,k: __init__
で定義したembedレイヤの重みWの行列を定義。v:行、k:列の次元に対応している。
h:中間層のユニット数を定義、中間層は今回は100ユニットとしている。
s
には受け取った文章のインデックスが、単語ごとに配列として格納されている。これをfor i in range(len(s)):
ループ内で単語ごとに分割し、処理を行う。
・for内の処理
・next_w_id
に$t+1$ステップでの出現単語のインデックス番号を格納する。
もし次の単語が存在しない場合、<eos>
のインデックスが格納される。
その後、tx
として変数化される。x_k
では、今ステップで参照している単語のインデックスを変数化し、word embeddingを行う。
・word embedding
- 辞書の該当単語IDを取得し、そのID部分のみが
1
、それ以外を0
とするような1-hotベクトル$x_t$を作成する。- また、コード内のh (=$h_t$)は、$t-1$ステップの$h_{t-1}$と$x_k$を用いて以下の式で求まる。加えて$W_{embed}$を用いて$x_k$(=
x_k
)を得る。$$x_t:dim(x_t)=len(vocab)$$- $$x_k = W_{embed} * x_k, dim(x_k)=demb$$$$h_t=tanh(W_{embed} * x_t + W_h * h_{t-1})$$
- $$=tanh(x_k+ W_h*h_{t-1})$$
・F.softmax_cross_entropy(self.W(h), tx)
により、モデルの尤度が求まる。詳細は以下を参照
交差エントロピー:neuralnetの日記
・メモ
観測データ$self.W(h).data=z$, 教師データ$t_x=t$とすると
$$z=[0.166479, 0.060454, 3.714621, ..., -0.407060], sum(z)≠ 1$$$$y =softmax(z) = \frac{exp(z_i)}{\sum_iexp(z_i)}, sum(y)=1$$であり、$softmax()$を通す事で、総和が1になるように正規化される。(=確率とみなせる)
但し、クラスが多い場合、値が極めて小さくなり、アンダーフローの可能性が発生する。logを通す事で、値をそこそこ大きく取る事ができる(?)
クロスエントロピーを通す事で、モデルの損失を測ることができる。$t,y$について$$t=[0, 0, ..., 0, 1, 0, ... , 0], len(y)=len(t)=len(vocab)$$
$$crossentropy(y,t) = -\sum_it_i*log_2(y_i)=\sum_i\frac{t_i}{log_2(y_i)}$$
教師データ$t$は1-hotベクトルであるから、正解クラス$t_i=t_k$以外の値は0となる。よって、式は以下のように変形可能となる。$$crossentropy(y',t)=\frac{t_k}{log_2(y'_k)}$$
モデルの損失を文章単位で累積し、累積誤差accum_loss
を返す。
main
for epoch in range(1):
s = []
for pos in range(25): # train_data = [0, 1, 2, 3, 4, 5, 6, 3, 7, 8, 9, 3]
id = train_data[pos]
s.append(id)
if (id == eos_id):
## s = [0,1,2,3] ( index '3' is <eos> )
loss, embedlist,tmp = model(s) ## loss.data.size=1, embed.data.size=100
model.zerograds()
loss.backward()
optimizer.update()
s = []
if (pos % 100 == 0):
print(pos, "/", len(train_data)," finished")
outfile = "myrnn-" + str(epoch) + ".model"
serializers.save_npz(outfile, model)
for epoch in range(1):
s = []
for pos in range(25): # train_data = [0, 1, 2, 3, 4, 5, 6, 3, 7, 8, 9, 3]
id = train_data[pos]
s.append(id)
if (id == eos_id):
## s = [0,1,2,3] ( index '3' is <eos> )
loss, embedlist,tmp = model(s) ## loss.data.size=1, embed.data.size=100
model.zerograds()
loss.backward()
optimizer.update()
s = []
if (pos % 100 == 0):
print(pos, "/", len(train_data)," finished")
outfile = "myrnn-" + str(epoch) + ".model"
serializers.save_npz(outfile, model)
for epoch in range(n)
: エポックの繰り返し回数n
for pos in range(len(train_data))
: 文章のwordの数だけ繰り返す
Author And Source
この問題について(rnnlm.py), 我々は、より多くの情報をここで見つけました https://qiita.com/goodboop68/items/c2a9d3291b4a4ef7f6e2著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .