データの取得、管理で行った青空文庫の取得データ群から、指定の作家の文章を学習させて、
その後、その作者風の文章を生成させます。
以下の工程では、
# -*- coding:utf-8 -*-
#<meta charset='utf-8'>
import argparse
import csv
import os
import sys
import zipfile
import pandas as pd
import re
# 青空文庫より得たデータ群
DEFAULT_INDEX_FILE_PATH = '../data/raw/aozorabunko/index_pages/list_person_all_extended_utf8.zip'
DEFAULT_INPUT_DIR = '../data/parsed/aozorabunko/morph'
DEFAULT_AUTHOR_NAME = '江戸川乱歩' # 学習対象の作者 他の例→DEFAULT_AUTHOR_NAME = '芥川竜之介'
DEFAULT_OUTPUT_DIR = 'data/prepared/aozorabunko/generate/morph_3' # 学習データを格納するフォルダ
DEFAULT_OUTPUT_PATH = DEFAULT_OUTPUT_DIR + "/" +'train_3.csv' # 学習データを記憶するファイル
ENCODING = 'utf-8' # 上記ファイルのキャラクタセット
z = zipfile.ZipFile(DEFAULT_INDEX_FILE_PATH)
with z.open(z.filelist[0]) as i_:
index_file = pd.read_csv(i_)
print(DEFAULT_INDEX_FILE_PATH,'からpandas.core.frame.DataFrameに読み込みました。')
print(index_file)
print(index_file.info())
print(index_file[['作品ID','作品名','姓','名']][67:68])
print("'作品ID'の名前の'.txt'ファイルが作品内容で、「"+DEFAULT_INPUT_DIR +"」の所に存在する")
content_path = index_file['作品ID'].map(lambda x: '{}/{}.txt'.format(DEFAULT_INPUT_DIR,x))
print(content_path)
exists_only=index_file[content_path.map(os.path.exists)]
author_name = exists_only['姓'] + exists_only['名']
authors_only = exists_only[author_name == DEFAULT_AUTHOR_NAME]
paths = content_path[authors_only.index]
print(paths)
'''上記の実行例
作品ID 作品名 作品名読み ... XHTML/HTMLファイル符号化方式 XHTML/HTMLファイル文字集合 XHTML/HTMLファイル修正回数
0 56078 駅伝馬車 えきでんばしゃ ... ShiftJIS JIS X 0208 0.0
1 56033 クリスマス・イーヴ クリスマス・イーヴ ... ShiftJIS JIS X 0208 0.0
2
... ...
15927 45210 純粋経済学要論 じゅんすいけいざいがくようろん ... ShiftJIS JIS X 0208 0.0
[15928 rows x 55 columns]
'''
print("以上が",DEFAULT_AUTHOR_NAME,"の作品のパスです")
def rnn_data_generator(paths):
prog = re.compile('[0-9]+')
for path in paths:
print("ファイル=====",path)
with open(path, encoding=ENCODING) as i_:
for line in i_:
line = line.strip()
if line == '' :continue
if prog.match(line) :continue
yield line
os.makedirs(DEFAULT_OUTPUT_DIR, exist_ok=True)
with open(DEFAULT_OUTPUT_PATH, 'w', encoding=ENCODING) as o_:
generator = rnn_data_generator(list(paths.values))
for line in generator:
o_.write(line)
print(line)
def test():
g = rnn_data_generator(['../data/parsed/aozorabunko/morph/57105.txt'])
for line in g:
print(line)
#test()
# -*- coding: utf-8 -*-
# 参考 TensorFlowの活用ガイド(技術評論社)
# 参考 https://deepinsider.jp/tutor/introtensorflow/whatisrnn
#<meta charset='utf-8'>
import argparse
import os
import sys
import numpy as np
import tensorflow as tf
from tensorflow.python.layers.core import Dense
sys.path.append('..') # 親ディレクトリのモージュールを利用
import utils
#拡張子が.csvであるが、単なるテキストファイル
DEFAULT_DATA_PATH = 'data/prepared/aozorabunko/generate/morph/train.csv'
EMB_SIZE = 128
#EMB_SIZE = 64 #★
VOCAB_SIZE = 50000
VOCAB_SIZE = 40000 #★
HIDDEN_SIZE = 256
HIDDEN_SIZE = 96 #★
HIDDEN_SIZE = 192 #★
N_LAYERS = 2
BATCH_SIZE = 64
#BATCH_SIZE = 16 #★
MAX_LEN = 32
#MAX_LEN = 16 #★
STEPS = 100000
LOG_DIR = 'log'
MODEL_DIR = 'models/generate_rnn'
ENCODING = 'utf-8'
# http://testpy.hatenablog.com/entry/2017/05/07/122323
# 上記を参考にして、メモリアロケーションのサイズを減らして実行
def rnn_model(inputs, num_steps, rnn_cell, initial_state, embedding_size, vocab_size):
"""
RNNによる文章生成モデル.学習時と推定時でネットワーク構造が異なる。
Args:
inputs (Tensor): [batch_size, max_sequence_length] の入力データ.
num_steps (int): 出力データの最大長.
rnn_cell (RNNCell): RNNセル.
intitial_state (Tensor or tupple of Tensor): RNNの初期状態(型はRNNセルに依存する).
embedding_size (int): 単語の埋め込み空間のサイズ.
vocab_size (int): 語彙数(単語IDの最大値).
Returns:
学習時の出力、推定時の出力
"""
with tf.variable_scope('decoder'):
# 分類器と同様に単語の埋め込みをおこなう
with tf.variable_scope('emb'):
emb_w = tf.get_variable(
'w',
shape=(vocab_size, embedding_size),
initializer=tf.contrib.layers.xavier_initializer()
)
emb = tf.nn.embedding_lookup(emb_w, inputs)
# 学習用のネットワーク定義
with tf.variable_scope('decoder'):
# seq2seq.Decoder では、 output_layerを経由して入力値を調整する
output_layer = Dense(vocab_size)
# 学習時は TrainingHelper を用いる
train_helper = tf.contrib.seq2seq.TrainingHelper(
emb,
tf.ones_like(inputs[:, 0])*num_steps
)
train_decoder = tf.contrib.seq2seq.BasicDecoder(
rnn_cell,
train_helper,
initial_state,
output_layer
)
train_outputs, _, _ = tf.contrib.seq2seq.dynamic_decode(
train_decoder,
impute_finished=True,
maximum_iterations=num_steps
)
# 推定(文章生成)用のネットワーク定義
with tf.variable_scope('decoder', reuse=True):
# Helperを選ぶことで「次の単語として何を使うか」が決まる。
inference_helper = tf.contrib.seq2seq.GreedyEmbeddingHelper(
emb_w,
inputs[:, 0],
0
)
inference_decoder = tf.contrib.seq2seq.BasicDecoder(
rnn_cell,
inference_helper,
initial_state,
output_layer
)
inference_outputs, _, _ = tf.contrib.seq2seq.dynamic_decode(
inference_decoder,
impute_finished=True,
maximum_iterations=num_steps
)
return train_outputs, inference_outputs
# text の文章内からランダムに始点を決めて、「batch_size個の語彙で構成される文」を抽出するジェネレータを返す。
# inputs、outputs の2つのnumpy配列(語彙に対応する番号)で、
# outputsの文は inputsの文に対いて、1つ語彙だけ後ろにずらした文になるようにしている。
def build_batch_generator(text, batch_size, max_len):
n_tokens = len(text)
while True:
inputs = np.zeros((batch_size, max_len), dtype=np.int32)
outputs = np.zeros((batch_size, max_len), dtype=np.int32)
for e in range(batch_size):
idx = np.random.randint(n_tokens - max_len)
inputs[e] = text[idx: idx + max_len]
outputs[e] = text[idx + 1: idx + max_len + 1]
yield inputs, outputs
# 文章ファイルを読み込んで、textに記憶する。
with open(DEFAULT_DATA_PATH, encoding=ENCODING) as i_:
text = i_.read()
print(text[0:100])
print("・・・・・省略・・・・・・・")
print(text[-100:])
print('以上が、{}のテキストファイル内容で、{}個の文字数'.format(DEFAULT_DATA_PATH, len(text)))
'''上記の実行例
ある に ち よう 日 の ご ご 、 丹下 サト子 ちゃん と 、 木村 ミドリ ちゃん と 、 野崎 サユリ ちゃん の 三 人 が 、 友だち の ところ へ あそび に 行っ た か えり に
・・・・・省略・・・・・・・
がり まし た 。 そして 、 みんな は おどりあがる よう に し て 叫ぶ の でし た 。「 少年 探偵 団 ばん ざあい ......。」「 チンピラ 別働隊 ばん ざあい ......。」
以上が、data/prepared/aozorabunko/generate/morph/train.csvのテキストファイル内容で、5598339個の文字数
'''
#sys.exit()
text_encoder = utils.TextEncoder()
text_encoder.build_vocab([text], VOCAB_SIZE) # text_encoder内に辞書を構築
text_encoder.print_info() #情報表示
'''上記の実行例
build_vocabメソッドで文字列からトークンを構築します。
['、', 'の', 'て', 'た', 'に', '。', 'は', 'を', 'が', 'と', 'で', 'です', 'も', 'まし', 'し', 'な', 'だ', 'い', 'ない', 'か']
・・・省略・・・・
['黙想', '黙礼', '黙祷', '黙許', '黙認', '黯痣', '黴', '鼎', '鼬', '鼻たかだか', '鼻声', '鼻孔', '鼻毛', '鼻汁', '鼻炎', '鼻茸', '齎さ', '齎し', '齟齬', '龍之介']
上記[self.vocab]は、 で、この出現数の順で番号を付けた。
その[self.vocab_reversed]を以下で検証。(この番号はself.vocabの添え字)
0:、:0
1:の:1
2:て:2
3:た:3
4:に:4
5:。:5
6:は:6
・・・省略・・・・
39995:龍之介:39995
39994:齟齬:39994
39993:齎し:39993
--------------print_info 以上
'''
# エンコーダとデコーダを検証
txt_enc = text_encoder.encode("今 ごろ あの 紳士 は どうして ゐる かしら 。")
print(type(txt_enc), txt_enc)
print(text_encoder.decode( txt_enc ))
#sys.exit()
# [188, 801, 75, 755, 10, 317, 9813, 504, 9]
# 今 ごろ あの 紳士 は どうして ゐる かしら 。
txt_enc = text_encoder.encode(text)
print("最後の一部を出力したコー:",text_encoder.decode( txt_enc[-20:] ))
'''上記の実行例
最後の一部を出コード: よう に し て 叫ぶ の でし た 。「 少年 探偵 団 ばん ざあい ......。」「 チンピラ 別働隊 ばん ざあい ......。」
'''
gen = build_batch_generator(
txt_enc,
BATCH_SIZE,
MAX_LEN
)
print("build_batch_generator :生成:", gen)
# build_batch_generator :生成: <generator object build_batch_generator at 0x000001E95F1F5360>
#inputs, output = next(gen)
#print( "実験batch inputs[0]***************" , inputs[0],text_encoder.decode( inputs[0] ) )
#print( "実験batch output[0]***************" , output[0],text_encoder.decode( output[0] ) )
#sys.exit()
'''上記を実行した場合の実行例(これが)
実験batch inputs[0]*************** [ 665 12 59 7 414 8 4 18139 5 39 11 575
11 3710 6 588 1295 7 9323 5 243 11 341 6
21 7 9 18139 8 10 2000 12] 相違 が あっ た 為 に 、 沙漠 の 中 を 円 を 描い て 歩き 続け た 旅人 の 話 を 聞い て い た 。 沙漠 に は 雲 が
実験batch output[0]*************** [ 12 59 7 414 8 4 18139 5 39 11 575 11
3710 6 588 1295 7 9323 5 243 11 341 6 21
7 9 18139 8 10 2000 12 6846] が あっ た 為 に 、 沙漠 の 中 を 円 を 描い て 歩き 続け た 旅人 の 話 を 聞い て い た 。 沙漠 に は 雲 が はれ
'''
inputs = tf.placeholder(tf.int32, (None, None), name='inputs')
print("input用プレイスフォルダ生成:",inputs)
#input用プレイスフォルダ生成: Tensor("inputs:0", shape=(?, ?), dtype=int32)
print("tf.contrib.rnn.MultiRNNCellの生成")
rnn_cell = tf.contrib.rnn.MultiRNNCell([
tf.contrib.rnn.GRUCell(HIDDEN_SIZE)
for _ in range(N_LAYERS)
])
print("rnn_cell:", type(rnn_cell))
#rnn_cell: <class 'tensorflow.python.ops.rnn_cell_impl.MultiRNNCell'>
initial_state = rnn_cell.zero_state(BATCH_SIZE, dtype=tf.float32)
print("initial_state=", initial_state)
#initial_state=(<tf.Tensor 'MultiRNNCellZeroState/GRUCellZeroState/zeros:0' shape=(64, 192) dtype=float32>,
# <tf.Tensor 'MultiRNNCellZeroState/GRUCellZeroState_1/zeros:0' shape=(64, 192) dtype=float32>)
print("rnn_model の生成")
train_outputs, inference_outputs = rnn_model(
inputs, #[batch_size, max_sequence_length] (Tensor)の入力データ
MAX_LEN, #(int): 出力データの最大長
rnn_cell, # RNNセル
initial_state, #RNNセルに依存する型の初期状態
EMB_SIZE,#(int): #単語の埋め込み空間のサイズ
VOCAB_SIZE# (int): #語彙数(単語IDの最大値)
)
print("\n学習時の出力train_outputs:", train_outputs)
#学習時の出力train_outputs: BasicDecoderOutput(rnn_output=<tf.Tensor 'decoder_1/decoder/transpose:0' shape=(64, ?, 40000) dtype=float32>,
# sample_id=<tf.Tensor 'decoder_1/decoder/transpose_1:0' shape=(64, ?) dtype=int32>)
print("\n推定時の出力inference_outputs:", inference_outputs)
#推定時の出力inference_outputs: BasicDecoderOutput(rnn_output=<tf.Tensor 'decoder_2/decoder/transpose:0' shape=(64, ?, 40000) dtype=float32>,
# sample_id=<tf.Tensor 'decoder_2/decoder/transpose_1:0' shape=(64, ?) dtype=int32>)
targets = tf.placeholder(tf.int32, (None, None), name='targets')
print("\n targets用プレイスフォルダ生成:",targets)
# targets用プレイスフォルダ生成: Tensor("targets:0", shape=(?, ?), dtype=int32)
with tf.variable_scope('loss'):
masks = tf.ones_like(targets, dtype=tf.float32)
loss = tf.contrib.seq2seq.sequence_loss(
train_outputs.rnn_output,
targets,
masks
)
print("\n masks=tf.ones_like(targets):",masks)
# masks=tf.ones_like(targets): Tensor("loss/ones_like:0", shape=(?, ?), dtype=float32)
print("\n loss 交差エントロピー誤差(損失関数):",loss)
# loss 交差エントロピー誤差(損失関数): Tensor("loss/sequence_loss/truediv:0", shape=(), dtype=float32)
with tf.variable_scope('opt'):
optimizer = tf.train.AdamOptimizer() # 最適化
train_op = optimizer.minimize(loss) # 損失関数結果が最小になる訓練指示
print("\n optimizer 最適化:",optimizer)
# optimizer 最適化: <tensorflow.python.training.adam.AdamOptimizer object at 0x0000020BB58A1358>
print("\n train_op 損失関数結果が最小になる訓練指示\n",train_op)
''' train_op 損失関数結果が最小になる訓練指示
name: "opt/Adam"
op: "NoOp"
input: "^opt/Adam/update_decoder/emb/w/group_deps"
input: "^opt/Adam/update_decoder/decoder/multi_rnn_cell/cell_0/gru_cell/gates/kernel/ApplyAdam"
input: "^opt/Adam/update_decoder/decoder/multi_rnn_cell/cell_0/gru_cell/gates/bias/ApplyAdam"
input: "^opt/Adam/update_decoder/decoder/multi_rnn_cell/cell_0/gru_cell/candidate/kernel/ApplyAdam"
input: "^opt/Adam/update_decoder/decoder/multi_rnn_cell/cell_0/gru_cell/candidate/bias/ApplyAdam"
input: "^opt/Adam/update_decoder/decoder/multi_rnn_cell/cell_1/gru_cell/gates/kernel/ApplyAdam"
input: "^opt/Adam/update_decoder/decoder/multi_rnn_cell/cell_1/gru_cell/gates/bias/ApplyAdam"
input: "^opt/Adam/update_decoder/decoder/multi_rnn_cell/cell_1/gru_cell/candidate/kernel/ApplyAdam"
input: "^opt/Adam/update_decoder/decoder/multi_rnn_cell/cell_1/gru_cell/candidate/bias/ApplyAdam"
input: "^opt/Adam/update_decoder/decoder/dense/kernel/ApplyAdam"
input: "^opt/Adam/update_decoder/decoder/dense/bias/ApplyAdam"
input: "^opt/Adam/Assign"
input: "^opt/Adam/Assign_1"
'''
checkpoint_path = os.path.join(MODEL_DIR, 'model.ckpt')
print("パラメタ保存パス checkpoint_path:" + checkpoint_path)
#パラメタ保存パス checkpoint_path:models/generate_rnn\model.ckpt
saver = tf.train.Saver()
print(" saver = tf.train.Saver():", saver)
# saver = tf.train.Saver(): <tensorflow.python.training.saver.Saver object at 0x0000026AFCA446D8>
print(" セッションの実行開始")
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
sum_loss = 0
for step in range(1, STEPS + 1):
inputs_, outputs_ = next(gen)
loss_, _ = sess.run(
[loss, train_op],
feed_dict={
inputs: inputs_,
targets: outputs_
}
)
sum_loss += loss_
if step <= 2 :
print("----STEP:" + str(step), "結果のloss_:", loss_ , _)
print(" inputs_ :", inputs_.shape, inputs_ )
print(" outputs_:", outputs_.shape, outputs_)
if step % 100 == 0:
saver.save(sess, checkpoint_path, global_step=step)
print("save--------STEP:" + str(step), "sum_loss/100: ",sum_loss/100)
sum_loss = 0
# 直前の input_ に対する推定結果を得て、表示
inferences = sess.run(
inference_outputs.sample_id,
feed_dict={ inputs: inputs_[:, :1] }
)
for _ in range(3):
print('---- SEED ----')
print(text_encoder.decode(inputs_[_, :1]))
print('---- OUTPUT ----')
print(text_encoder.decode(inferences[_]))
セッションの実行結果の例
----STEP:1 結果のloss_: 10.596933 None inputs_ : (64, 32) [[ 75 40 11 ... 10 1130 5] [1536 32 19 ... 32 8 18] [ 158 8 10 ... 655 8 1237] ... [ 6 21 7 ... 32 8 18] [ 11 1911 497 ... 32 14 36] [ 39 24 4 ... 350 15 344]] outputs_: (64, 32) [[ 40 11 16 ... 1130 5 17854] [ 32 19 115 ... 8 18 17] [ 8 10 4 ... 8 1237 6] ... [ 21 7 87 ... 8 18 6] [ 1911 497 49 ... 14 36 9] [ 24 4 149 ... 15 344 37]] ----STEP:2 結果のloss_: 10.594896 None inputs_ : (64, 32) [[ 5 121 12 ... 1315 37 5] [ 228 4 189 ... 8 8062 11] [1136 22 31 ... 6681 19 63] ... [ 5 9366 361 ... 54 40 8] [ 96 17 7 ... 6 4 36] [ 41 9 103 ... 10 4 4090]] outputs_: (64, 32) [[ 121 12 4 ... 37 5 394] [ 4 189 14 ... 8062 11 2892] [ 22 31 596 ... 19 63 14] ... [9366 361 15 ... 40 8 10] [ 17 7 9 ... 4 36 72] [ 9 103 4 ... 4 4090 12]] 'models/generate_rnn\\model.ckpt-100' save--------STEP:100 sum_loss/100: 7.081988639831543 ---- SEED ---- に ---- OUTPUT ---- 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 ---- SEED ---- あたり ---- OUTPUT ---- 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 ---- SEED ---- を ---- OUTPUT ---- 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 'models/generate_rnn\\model.ckpt-200' save--------STEP:200 sum_loss/100: 6.246347107887268 ---- SEED ---- 。「 ---- OUTPUT ---- 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 ---- SEED ---- いる ---- OUTPUT ---- 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 ---- SEED ---- は ---- OUTPUT ---- 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 'models/generate_rnn\\model.ckpt-300' save--------STEP:300 sum_loss/100: 6.23431734085083 ---- SEED ---- 、 ---- OUTPUT ---- 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 ---- SEED ---- 中 ---- OUTPUT ---- 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 ---- SEED ---- では ---- OUTPUT ---- 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 、 ・・・・・省略・・・・・・