LSTMを3段積むことによって、精度改善を図る。イメージは以下のとおり。
model.add(LSTM(self.n_hidden, batch_input_shape=(None, self.maxlen, self.vec_dim),
return_sequences=True,
kernel_initializer=glorot_uniform(seed=20170719),
recurrent_initializer=orthogonal(gain=1.0, seed=20170719)))
model.add(LSTM(self.n_hidden, return_sequences=True,
kernel_initializer=glorot_uniform(seed=20170719),
recurrent_initializer=orthogonal(gain=1.0, seed=20170719)))
model.add(LSTM(self.n_hidden,
kernel_initializer=glorot_uniform(seed=20170719),
recurrent_initializer=orthogonal(gain=1.0, seed=20170719)
))
1段目、2段目のreturn_sequences=Trueが重要である(デフォルトはFalse)。こう指定することで、LSTMの全ての出力系列が次のレイヤーに入力される。LSTMを多段に積むときの必須指定である。
株価予測のときは、多段にしても効果はなかったが、今回は劇的に精度が改善した。段数が多いほど、精度が改善したが、4段以上にしても精度が改善しなかったので、段数を3としてある。
全てのニューラルネット(単語分類用6本、単語推定用7本、計13本)に(つまり前回のリスト5-1のPredictionクラス、さらに今回のリスト5-2のPrediction_freqクラスとPrediction_wordsクラスのすべてに)、この変更を施す。
前段で出現頻度による絞り込みが行われているため、単語推定ニューラルネットでは、全ての単語を出力として想定する必要はない。例えば、頻度10未満と判定された後で動作するニューラルネットは、まさに頻度10未満の単語だけが推定できればよいはずである。すなわち、出力次元を、推定対象に合わせて減少させることができるが、これにより精度の向上が期待できる。
この考え方に立って見直した、単語推定のコードが以下である。訓練データ作成処理では、出力として想定される単語の一覧を作成し、そのインデックスを正解値とするよう、ラベルデータを作成する。
maxlen = 40 # 入力語数
n_upper = 10 # 学習対象単語の出現頻度上限
n_lower = 0 # 学習対象単語の出現頻度下限
mat_urtext = np.zeros((len(mat), 1), dtype=int)
for i in range(0, len(mat)):
#row = np.zeros(len(words), dtype=np.float32)
if mat[i] in word_indices : # 出現頻度の低い単語のインデックスをUNKのそれに置き換え
if word_indices[mat[i]] != 0 : # 0パディング対策
mat_urtext[i, 0] = word_indices[mat[i]]
else :
mat_urtext[i, 0] = len(words)
else:
mat_urtext[i, 0] = word_indices['UNK']
print(mat_urtext.shape)
# 単語の出現数をもう一度カウント:UNK置き換えでwords_indeicesが変わっているため
cnt = np.zeros(len(words)+1)
for j in range (0, len(mat)):
cnt[mat_urtext[j, 0]] += 1
print(cnt.shape)
# 頻度対象内単語のリスト
words_0 = []
for i in range(0, len(words)+1) :
if cnt[i] >= n_lower and cnt[i] < n_upper:
words_0.append(i)
words_0 = sorted(list(set(words_0)))
w0_indices = dict((w, i) for i, w in enumerate(words_0)) # 単語をキーにインデックス検索
indices_w0 = dict((i, w) for i, w in enumerate(words_0)) # インデックスをキーに単語を検索
print(len(words_0))
len_seq = len(mat_urtext) - maxlen
data = []
target = []
for i in range(0, len_seq):
# 答えの単語の出現頻度がlower_limit以上でかつnum_fleq 未満の場合を学習対象にする
if cnt[mat_urtext[i+maxlen, :]] >= n_lower and cnt[mat_urtext[i+maxlen, :]] < n_upper:
#print(mat_urtext[i+maxlen, :])
data.append(mat_urtext[i:i+maxlen, :])
target.append(w0_indices[mat_urtext[i+maxlen, :][0]])
x_train = np.array(data).reshape(len(data), maxlen, 1)
t_train = np.array(target).reshape(len(data), 1)
z = list(zip(x_train, t_train))
nr.seed(12345)
nr.shuffle(z) # シャッフル
x_train, t_train = zip(*z)
x = np.array(x_train).reshape(len(data), maxlen, 1)
t = np.array(t_train).reshape(len(data), 1)
for i in range(0, maxlen):
print(x[2, i, :], indices_word[x[2, i, 0]])
print()
print(t[2, :], indices_word[indices_w0[t[2, 0]]])
x_train = x
t_train = t
print(x_train.shape, t_train.shape)
リストの中ほどに出てくるwords_0が、出力として想定される単語の一覧である。このリストを、前回記事のリスト4の代わりに実行する。
ニューラルネットの定義に変更はない。メイン処理もほとんど変らないが、出力次元output_dimの変更が必要である。
n_pattern = 0
vec_dim = 400
epochs = 100
batch_size = 200
input_dim = len(words)+1
output_dim = len(words_0)
n_hidden = int(vec_dim*1.5) # 隠れ層の次元
prediction = Prediction(maxlen, n_hidden, input_dim, vec_dim, output_dim)
emb_param = 'param_words_'+str(n_pattern)+'_'+str(n_lower)+'_'+str(n_upper)+'.hdf5' # 学習済みパラメーターファイル名の定義
print (emb_param)
row = x_train.shape[0]
x_train = x_train.reshape(row, maxlen)
model = prediction.train(x_train,
np_utils.to_categorical(t_train, output_dim), batch_size, epochs, emb_param)
model.save_weights(emb_param) # 学習済みパラメーターセーブ
score = model.evaluate(x_train.reshape(row, maxlen),
np_utils.to_categorical(t_train, output_dim), batch_size=batch_size, verbose=1)
print("score:", score)
print()
以下の順で実行する。
以上を実行すると、前回の表1に示したパターン0が完了している状態だ。ここでは、残りの7つのパターン、つまりパターン1〜パターン6もここで実行する必要がある。
これにはまず、n_lower/n_upperの値を前回の表1に示したものに置き換えて、リスト4-2を再実行する。
次に、n_patternの値をパターン名の「1」〜「6」に置き換えて、リスト6-2を再実行すればよい。
この2つの再実行をパターン6まで繰り返すことで、7つのパラメーターファイルを再生成する。
以上が単語分類用ニューラルネットのパラメーターファイルになる。頻度分類用ニューラルネットのパラメーターファイルも、ニューラルネットワークの層が増えたので再生成しておこう。こちらは以下の手順を実行する。
今回の表2に示したパターン0が完了している状態なので、残りの5つのパターン、つまりパターン1〜パターン5もここで実行する。
これにはまず、n_lower/n_split/n_upperの値を前掲の表2に示したものに置き換えて、リスト4-1を再実行する。
次に、n_patternの値をパターン名の「1」〜「5」に置き換えて、リスト6-1を再実行すればよい。
この2つの再実行をパターン5まで繰り返すことで、6つのパラメーターファイルを再生成する。
これらの施策の効果は圧倒的で、どのニューラルネットも正解率が99%以上となった。
文書生成の能力がどれくらい上がったかを確認する。出現頻度で分類された単語のグループごとに、インデックスを付ける必要があるので、リスト4を以下のように修正する。
maxlen = 40 # 入力語数
mat_urtext = np.zeros((len(mat), 1), dtype=int)
for i in range(0, len(mat)):
#row = np.zeros(len(words), dtype=np.float32)
if mat[i] in word_indices : # 出現頻度の低い単語のインデックスをunkのそれに置き換え
if word_indices[mat[i]] != 0 : # 0パディング対策
mat_urtext[i, 0] = word_indices[mat[i]]
else :
mat_urtext[i, 0] = len(words)
else:
mat_urtext[i, 0] = word_indices['UNK']
print(mat_urtext.shape)
# 単語の出現数をもう一度カウント:UNK置き換えでwords_indeicesが変わっているため
cnt = np.zeros(len(words)+1)
for j in range (0, len(mat)):
cnt[mat_urtext[j, 0]] += 1
print(cnt.shape)
data = []
target = []
len_seq = len(mat_urtext)-maxlen
#for i in range(0, 10):
for i in range(0, len_seq):
data.append(mat_urtext[i:i+maxlen, :])
target.append(mat_urtext[i+maxlen, :])
x_train = np.array(data).reshape(len(data), maxlen, 1)
t_train = np.array(target).reshape(len(data), 1)
print(x_train.shape, t_train.shape)
# 頻度対象内単語のリスト
words_0 = []
w0_indices = []
indices_w0 = []
n_upper = [10, 28, 100, 300, 2000, 15000, 400000]
n_lower = [0, 10, 28, 100, 300, 2000, 15000]
for j in range(0, 7) :
wk = []
for i in range(0, len(words)+1) :
if cnt[i] >= n_lower[j] and cnt[i] < n_upper[j]:
wk.append(i)
words_0.append(wk)
words_0[j] = sorted(list(set(words_0[j])))
wi = dict((w, i) for i, w in enumerate(words_0[j])) # 単語をキーにインデックス検索
iw = dict((i, w) for i, w in enumerate(words_0[j])) # インデックスをキーに単語を検索
w0_indices.append(wi)
indices_w0.append(iw)
リスト前半のx_trainは訓練用ではなく、文書生成の入力文字列用に作成している。リスト後半が単語分類ごとのインデックス付けで、出現頻度別単語リストがwords_0、単語→インデックス辞書がw0_indices、インデックス→単語辞書がindices_w0である。それぞれ、リストのリストになっている。
また、単語推定の出力次元削減を実施した関係上、リスト7-2とリスト8-1を以下のように修正する。
vec_dim=400
epochs = 100
batch_size=200
input_dim=len(words)+1
n_sigmoid = 1
n_hidden=int(vec_dim*1.5) #隠れ層の次元
#頻度分類用
prediction_freq = Prediction_freq(maxlen,n_hidden,input_dim,vec_dim,n_sigmoid)
print('頻度分類用ニューラルネット_0活性化')
model_classify_freq_0=prediction_freq.create_model()
print('頻度分類用ニューラルネット_1活性化')
model_classify_freq_1=prediction_freq.create_model()
print('頻度分類用ニューラルネット_2活性化')
model_classify_freq_2=prediction_freq.create_model()
print('頻度分類用ニューラルネット_3活性化')
model_classify_freq_3=prediction_freq.create_model()
print('頻度分類用ニューラルネット_4活性化')
model_classify_freq_4=prediction_freq.create_model()
print('頻度分類用ニューラルネット_5活性化')
model_classify_freq_5=prediction_freq.create_model()
print()
output_dim = [0]*7
for i in range(0,7) :
output_dim[i] = len(words_0[i])
#単語予測用
print('単語分類用ニューラルネット(0_10)活性化')
prediction_words = Prediction_words(maxlen,n_hidden,input_dim,vec_dim,output_dim[0])
model_words_0_10=prediction_words.create_model()
print('単語分類用ニューラルネット(10-28)活性化')
prediction_words = Prediction_words(maxlen,n_hidden,input_dim,vec_dim,output_dim[1])
model_words_10_28=prediction_words.create_model()
print('単語分類用ニューラルネット(28-100)活性化')
prediction_words = Prediction_words(maxlen,n_hidden,input_dim,vec_dim,output_dim[2])
model_words_28_100=prediction_words.create_model()
print('単語分類用ニューラルネット(100-300)活性化')
prediction_words = Prediction_words(maxlen,n_hidden,input_dim,vec_dim,output_dim[3])
model_words_100_300=prediction_words.create_model()
print('単語分類用ニューラルネット(300-2000)活性化')
prediction_words = Prediction_words(maxlen,n_hidden,input_dim,vec_dim,output_dim[4])
model_words_300_2000=prediction_words.create_model()
print('単語分類用ニューラルネット(2000-15000)活性化')
prediction_words = Prediction_words(maxlen,n_hidden,input_dim,vec_dim,output_dim[5])
model_words_2000_15000=prediction_words.create_model()
print('単語分類用ニューラルネット(15000-400000)活性化')
prediction_words = Prediction_words(maxlen,n_hidden,input_dim,vec_dim,output_dim[6])
model_words_15000_400000=prediction_words.create_model()
print()
# パラメーターロード
print('頻度分類用ニューラルネット_0パラメーターロード')
model_classify_freq_0.load_weights('param_classify_by_freq_0_0_300_400000.hdf5')
print('頻度分類用ニューラルネット_1パラメーターロード')
model_classify_freq_1.load_weights('param_classify_by_freq_1_0_28_300.hdf5')
print('頻度分類用ニューラルネット_2パラメーターロード')
model_classify_freq_2.load_weights('param_classify_by_freq_2_0_10_28.hdf5')
print('頻度分類用ニューラルネット_3パラメーターロード')
model_classify_freq_3.load_weights('param_classify_by_freq_3_28_100_300.hdf5')
print('頻度分類用ニューラルネット_4パラメーターロード')
model_classify_freq_4.load_weights('param_classify_by_freq_4_300_2000_400000.hdf5')
print('頻度分類用ニューラルネット_5パラメーターロード')
model_classify_freq_5.load_weights('param_classify_by_freq_5_2000_15000_400000.hdf5')
print()
print('単語分類用ニューラルネット(0-10)パラメーターロード')
model_words_0_10.load_weights('param_words_0_0_10.hdf5')
print('単語分類用ニューラルネット(10-28)パラメーターロード')
model_words_10_28.load_weights('param_words_1_10_28.hdf5')
print('単語分類用ニューラルネット(28-100)パラメーターロード')
model_words_28_100.load_weights('param_words_2_28_100.hdf5')
print('単語分類用ニューラルネット(100-300)パラメーターロード')
model_words_100_300.load_weights('param_words_3_100_300.hdf5')
print('単語分類用ニューラルネット(300-2000)パラメーターロード')
model_words_300_2000.load_weights('param_words_4_300_2000.hdf5')
print('単語分類用ニューラルネット(2000-15000)パラメーターロード')
model_words_2000_15000.load_weights('param_words_5_2000_15000.hdf5')
print('単語分類用ニューラルネット(15000-400000)パラメーターロード')
model_words_15000_400000.load_weights('param_words_6_15000_400000.hdf5')
print()
n_init = 6000
x_validation = x_train[n_init, :, :]
x_validation = x_validation.T
row = x_validation.shape[0] # 評価データ数
x_validation = x_validation.reshape(row, maxlen)
text_gen = '' # 生成テキスト
for i in range(0, maxlen) :
text_gen += indices_word[x_validation[0, i]]
print(text_gen)
print()
# 正解データ
text_correct = ''
for j in range(0, 4) :
x_correct = x_train[n_init+j*maxlen, :, :]
x_correct = x_correct.T
#row = x_correct.shape[0] # 評価データ数
x_correct = x_correct.reshape(row, maxlen)
for i in range(0, maxlen) :
text_correct += indices_word[x_correct[0, i]]
print('正解')
print(text_correct)
print()
flag_1 = flag_2 = flag_3 = 0
# 応答文生成
for k in range (0, 100) :
# 単語予測
# 300
ret_0 = model_classify_freq_0.predict(x_validation, batch_size=batch_size, verbose=0) # 評価結果
ret_0 = ret_0.reshape(row, n_sigmoid)
flag_0 = ret_0[0, 0]
# 最大値インデックス
if flag_0 < 0.5 : # 300未満
ret_1 = model_classify_freq_1.predict(x_validation, batch_size=batch_size, verbose=0) # 評価結果
ret_1 = ret_1.reshape(row, n_sigmoid)
flag_1 = ret_1[0, 0]
if flag_1 < 0.5 : # 28未満
ret_2 = model_classify_freq_2.predict(x_validation, batch_size=batch_size, verbose=0) # 評価結果
ret_2 = ret_2.reshape(row, n_sigmoid)
flag_2 = ret_2[0, 0]
if flag_2< 0.5 : # 10未満
pred_freq = 0
ret = model_words_0_10.predict(x_validation, batch_size=batch_size, verbose=0)
ret_w0 = ret.argmax(1)[0]
ret_word = indices_w0[0][ret_w0]
else : # 10以上28未満
pred_freq = 1
ret = model_words_10_28.predict(x_validation, batch_size=batch_size, verbose=0)
ret_w0 = ret.argmax(1)[0]
ret_word = indices_w0[1][ret_w0]
else : # 28以上
ret_3 = model_classify_freq_3.predict(x_validation, batch_size=batch_size, verbose=0) # 評価結果
ret_3 = ret_3.reshape(row, n_sigmoid)
flag_3 = ret_3[0, 0]
if flag_3 <0.5 : # 28以上100未満
pred_freq = 2
ret = model_words_28_100.predict(x_validation, batch_size=batch_size, verbose=0)
ret_w0 = ret.argmax(1)[0]
ret_word = indices_w0[2][ret_w0]
else : # 100以上300未満
pred_freq = 3
ret = model_words_100_300.predict(x_validation, batch_size=batch_size, verbose=0)
ret_w0 = ret.argmax(1)[0]
ret_word = indices_w0[3][ret_w0]
else : # 300以上
ret_4 = model_classify_freq_4.predict(x_validation, batch_size=batch_size, verbose=0) # 評価結果
ret_4 = ret_4.reshape(row, n_sigmoid)
flag_4 = ret_4[0, 0]
if flag_4 <0.5 : # 300以上2000未満
pred_freq = 4
ret = model_words_300_2000.predict(x_validation, batch_size=batch_size, verbose=0)
ret_w0 = ret.argmax(1)[0]
ret_word = indices_w0[4][ret_w0]
else : # 2000以上
ret_5 = model_classify_freq_5.predict(x_validation, batch_size=batch_size, verbose=0) # 評価結果
ret_5 = ret_5.reshape(row, n_sigmoid)
flag_5 = ret_5[0, 0]
if flag_5 < 0.5 : # 2000以上15000未満
pred_freq = 5
ret = model_words_2000_15000.predict(x_validation, batch_size=batch_size, verbose=0)
ret_w0 = ret.argmax(1)[0]
ret_word = indices_w0[5][ret_w0]
else : # 15000以上
pred_freq = 6
ret = model_words_15000_400000.predict(x_validation, batch_size=batch_size, verbose=0)
ret_w0 = ret.argmax(1)[0]
ret_word = indices_w0[6][ret_w0]
print(pred_freq, '\t', indices_word[ret_word])
text_gen += indices_word[ret_word] # 生成文字を追加
#pred_parts = index_parts
x_validation[0, 0:maxlen-1] = x_validation[0, 1:maxlen]
x_validation[0, maxlen-1] = ret_word # 1文字シフト
print()
print(text_gen)
リスト8-1に代えて、これを実行する。
最終的なリストの実行順は、以下のとおり。
文書生成結果は以下のとおり。100発100中である。
まず、「お題」となる入力文字列は以下のとおり。
「はございますまいか。考えて見れば、この世界の、人目につかぬ隅々では、どの様にUNK、恐ろしい事柄が、行われているか、ほんとうに想像の外《ほか》で」
これに続く文章の生成結果は以下のとおり。
「ございます。無論始めの予定では、盗みの目的を果しさえすれば、すぐにもホテルを逃げ出す積《つも》りでいたのですが、世にも奇怪な喜びに、夢中になった私は、逃げ出すどころか、いつまでもいつまでも、椅子の中をUNKのUNKにして、その生活を続けていたのでございます。UNK《UNK》の外出には、注意に注意を加えて、少しも物音を立てず、又人目に触れない様にしていましたので、当然、危険はありませんでしたが、それにしても、数ヶ月という、長い」
ちなみに正解は以下のとおり。
「ございます。無論始めの予定では、盗みの目的を果しさえすれば、すぐにもホテルを逃げ出す積《つも》りでいたのですが、世にも奇怪な喜びに、夢中になった私は、逃げ出すどころか、いつまでもいつまでも、椅子の中をUNKのUNKにして、その生活を続けていたのでございます。UNK《UNK》の外出には、注意に注意を加えて、少しも物音を立てず、又人目に触れない様にしていましたので、当然、危険はありませんでしたが、それにしても、数ヶ月という、長い」
Copyright© Digital Advantage Corp. All Rights Reserved.