Chainer とはニューラルネットの実装を簡単にしたフレームワークです。
言語モデルとはある単語が来たときに次の単語に何が来やすいかを予測するものです。
言語モデルにはいくつか種類があるのでここでも紹介しておきます。
ニューラル言語モデル
リカレントニューラル言語モデル
以下では、このChainerを利用しデータを準備するところから実際に言語モデルを構築し学習・評価を行うまでの手順を解説します。
もしGPUを使用したい方は、以下にまとめてあるのでご参考ください。
Chainerの言語処理では多数のライブラリを導入します。
In [ ]:
import time
import math
import sys
import pickle
import copy
import os
import re
import numpy as np
from chainer import cuda, Variable, FunctionSet, optimizers
import chainer.functions as F
`導入するライブラリの代表例は下記です。
numpy
: 行列計算などの複雑な計算を行なうライブラリchainer
: Chainerの導入
In [ ]:
#-------------Explain7 in the Qiita-------------
n_epochs = 30
n_units = 641
batchsize = 200
bprop_len = 40
grad_clip = 0.3
gpu_ID = 0
data_dir = "data_hands_on"
checkpoint_dir = "cv"
xp = cuda.cupy if gpu_ID >= 0 else np
#-------------Explain7 in the Qiita-------------
学習用にダウンロードしたファイルをプログラムに読ませる処理を関数化しています
学習データ、単語の長さ、語彙数を取得しています。 上記をそれぞれ行列データとして保持しています。
In [ ]:
# input data
#-------------Explain1 in the Qiita-------------
def source_to_words(source):
line = source.replace("¥n", " ").replace("¥t", " ")
for spacer in ["(", ")", "{", "}", "[", "]", ",", ";", ":", "++", "!", "$", '"', "'"]:
line = line.replace(spacer, " " + spacer + " ")
words = [w.strip() for w in line.split()]
return words
def load_data():
vocab = {}
print ('%s/angular.js'% data_dir)
source = open('%s/angular_full_remake.js' % data_dir, 'r').read()
words = source_to_words(source)
freq = {}
dataset = np.ndarray((len(words),), dtype=np.int32)
for i, word in enumerate(words):
if word not in vocab:
vocab[word] = len(vocab)
freq[word] = 0
dataset[i] = vocab[word]
freq[word] += 1
print('corpus length:', len(words))
print('vocab size:', len(vocab))
return dataset, words, vocab, freq
#-------------Explain1 in the Qiita-------------
if not os.path.exists(checkpoint_dir):
os.mkdir(checkpoint_dir)
train_data, words, vocab, freq = load_data()
for f in ["frequent", "rarely"]:
print("{0} words".format(f))
print(sorted(freq.items(), key=lambda i: i[1], reverse=True if f == "frequent" else False)[:50])
RNNLM(リカレントニューラル言語モデルの設定を行っています)
In [ ]:
#-------------Explain2 in the Qiita-------------
class CharRNN(FunctionSet):
def __init__(self, n_vocab, n_units):
super(CharRNN, self).__init__(
embed = F.EmbedID(n_vocab, n_units),
l1_x = F.Linear(n_units, 4*n_units),
l1_h = F.Linear(n_units, 4*n_units),
l2_h = F.Linear(n_units, 4*n_units),
l2_x = F.Linear(n_units, 4*n_units),
l3 = F.Linear(n_units, n_vocab),
)
for param in self.parameters:
param[:] = np.random.uniform(-0.08, 0.08, param.shape)
def forward_one_step(self, x_data, y_data, state, train=True, dropout_ratio=0.7):
x = Variable(x_data, volatile=not train)
t = Variable(y_data, volatile=not train)
h0 = self.embed(x)
h1_in = self.l1_x(F.dropout(h0, ratio=dropout_ratio, train=train)) + self.l1_h(state['h1'])
c1, h1 = F.lstm(state['c1'], h1_in)
h2_in = self.l2_x(F.dropout(h1, ratio=dropout_ratio, train=train)) + self.l2_h(state['h2'])
c2, h2 = F.lstm(state['c2'], h2_in)
y = self.l3(F.dropout(h2, ratio=dropout_ratio, train=train))
state = {'c1': c1, 'h1': h1, 'c2': c2, 'h2': h2}
return state, F.softmax_cross_entropy(y, t)
def predict(self, x_data, state):
x = Variable(x_data, volatile=True)
h0 = self.embed(x)
h1_in = self.l1_x(h0) + self.l1_h(state['h1'])
c1, h1 = F.lstm(state['c1'], h1_in)
h2_in = self.l2_x(h1) + self.l2_h(state['h2'])
c2, h2 = F.lstm(state['c2'], h2_in)
y = self.l3(h2)
state = {'c1': c1, 'h1': h1, 'c2': c2, 'h2': h2}
return state, F.softmax(y)
def make_initial_state(n_units, batchsize=50, train=True):
return {name: Variable(np.zeros((batchsize, n_units), dtype=np.float32),
volatile=not train)
for name in ('c1', 'h1', 'c2', 'h2')}
#-------------Explain2 in the Qiita-------------
RNNLM(リカレントニューラル言語モデルの設定を行っています)
In [ ]:
# Prepare RNNLM model
model = CharRNN(len(vocab), n_units)
if gpu_ID >= 0:
cuda.check_cuda_available()
cuda.get_device(gpu_ID).use()
model.to_gpu()
optimizer = optimizers.RMSprop(lr=2e-3, alpha=0.95, eps=1e-8)
optimizer.setup(model)
In [ ]:
whole_len = train_data.shape[0]
jump = whole_len // batchsize
epoch = 0
start_at = time.time()
cur_at = start_at
state = make_initial_state(n_units, batchsize=batchsize)
cur_log_perp = 0
if gpu_ID >= 0:
accum_loss = Variable(cuda.zeros(()))
for key, value in state.items():
value.data = cuda.to_gpu(value.data)
else:
accum_loss = Variable(xp.zeros((), dtype=np.float32))
逐次尤度の計算も行っている。
適宜学習データのパープレキシティも計算している
バックプロパゲーションでパラメータを更新する。
In [ ]:
for i in range(int(jump * n_epochs)):
#-------------Explain4 in the Qiita-------------
x_batch = np.array([train_data[(jump * j + i) % whole_len]
for j in range(batchsize)])
y_batch = np.array([train_data[(jump * j + i + 1) % whole_len]
for j in range(batchsize)])
if gpu_ID >= 0:
x_batch = cuda.to_gpu(x_batch)
y_batch = cuda.to_gpu(y_batch)
state, loss_i = model.forward_one_step(x_batch, y_batch, state, dropout_ratio=0.7)
accum_loss += loss_i
cur_log_perp += loss_i.data
if (i + 1) % bprop_len == 0: # Run truncated BPTT
now = time.time()
cur_at = now
# print('{}/{}, train_loss = {}, time = {:.2f}'.format((i + 1)/bprop_len, jump, accum_loss.data / bprop_len, now-cur_at))
optimizer.zero_grads()
accum_loss.backward()
accum_loss.unchain_backward() # truncate
accum_loss = Variable(np.zeros((), dtype=np.float32))
if gpu_ID >= 0:
accum_loss = Variable(cuda.zeros(()))
else:
accum_loss = Variable(np.zeros((), dtype=np.float32))
optimizer.clip_grads(grad_clip)
optimizer.update()
if (i + 1) % 10000 == 0:
perp = math.exp(cuda.to_cpu(cur_log_perp) / 10000)
print('iter {} training perplexity: {:.2f} '.format(i + 1, perp))
fn = ('%s/charrnn_epoch_%i.chainermodel' % (checkpoint_dir, epoch))
pickle.dump(copy.deepcopy(model).to_cpu(), open(fn, 'wb'))
cur_log_perp = 0
if (i + 1) % jump == 0:
epoch += 1
#-------------Explain4 in the Qiita-------------
sys.stdout.flush()
In [ ]:
# load model
#-------------Explain6 in the Qiita-------------
model = pickle.load(open("cv/charrnn_epoch_22.chainermodel", 'rb'))
#-------------Explain6 in the Qiita-------------
n_units = model.embed.W.shape[1]
if gpu_ID >= 0:
cuda.check_cuda_available()
cuda.get_device(gpu_ID).use()
model.to_gpu()
# initialize generator
state = make_initial_state(n_units, batchsize=1, train=False)
if gpu_ID >= 0:
for key, value in state.items():
value.data = cuda.to_gpu(value.data)
# show vocababury
ivocab = {}
ivocab = {v:k for k, v in vocab.items()}
In [ ]:
# initialize generator
index = np.random.randint(0, len(vocab), 1)[0]
sampling_range = 5
prev_char = np.array([0], dtype=np.int32)
if gpu_ID >= 0:
prev_char = cuda.to_gpu(prev_char)
for i in range(1000):
if ivocab[index] in ["}", ";"]:
sys.stdout.write(ivocab[index] + "\n")
else:
sys.stdout.write(ivocab[index] + " ")
#-------------Explain7 in the Qiita-------------
state, prob = model.predict(prev_char, state)
index = np.argmax(cuda.to_cpu(prob.data))
#index = np.random.choice(prob.data.argsort()[0,-sampling_range:][::-1], 1)[0]
#-------------Explain7 in the Qiita-------------
prev_char = np.array([index], dtype=np.int32)
if gpu_ID >= 0:
prev_char = cuda.to_gpu(prev_char)
print
In [ ]:
In [ ]: