作者:凌逆战

博客园地址:https://www.cnblogs.com/LXP-Never/p/11543152.html

char RNN代码来源于https://github.com/hzy46/Char-RNN-TensorFlow


前言

本人在学习char RNN的过程中,遇到了很多的问题,但是依然选择一行代码一行代码的啃下来,并且注释好,我在啃代码的过程中,就想要是有一位大神在我旁边就好了,我在看代码的过程中,不懂那里,就问那里,可是现实中并没有,所有问题都要自己解决,今日我终于把代码全部弄懂了,也把代码分享给下一位想要学习char RNN的人。开源才能进步,中国加油。觉有有用希望大家可以点个赞,关注我,这将给我莫大的动力。如果我文中有错误的地方,欢迎指出,我也需要学习和进步。多一点包容,多一点努力。

RNN原理不熟悉的同学建议参考这篇博客:循环神经网络(RNN)原理

代码基础不熟悉的同学建议参考这篇博客: TensorFlow中实现RNN,彻底弄懂time_step

详细代码注释

train.py

# -*- coding:utf-8 -*-
import tensorflow as tf
from read_utils import TextConverter, batch_generator
from model import CharRNN
import os
import codecs FLAGS = tf.flags.FLAGS tf.flags.DEFINE_string('name', 'default', '模型名')
tf.flags.DEFINE_integer('num_seqs', 32, '一个batch里面的序列数量') #
tf.flags.DEFINE_integer('num_steps', 26, '序列的长度') #
tf.flags.DEFINE_integer('lstm_size', 128, 'LSTM隐层的大小')
tf.flags.DEFINE_integer('num_layers', 2, 'LSTM的层数')
tf.flags.DEFINE_boolean('use_embedding', False, '是否使用 embedding')
tf.flags.DEFINE_integer('embedding_size', 128, 'embedding的大小')
tf.flags.DEFINE_float('learning_rate', 0.001, '学习率')
tf.flags.DEFINE_float('train_keep_prob', 0.5, '训练期间的dropout比率')
tf.flags.DEFINE_string('input_file', '', 'utf8编码过的text文件')
tf.flags.DEFINE_integer('max_steps', 10000, '一个step 是运行一个batch, max_steps固定了最大的运行步数')
tf.flags.DEFINE_integer('save_every_n', 1000, '每隔1000步会将模型保存下来')
tf.flags.DEFINE_integer('log_every_n', 10, '每隔10步会在屏幕上打出曰志')
# 使用的字母(汉字)的最大个数。默认为3500 。程序会自动挑选出使用最多的字,井将剩下的字归为一类,并标记为<unk>
tf.flags.DEFINE_integer('max_vocab', 10000, '最大字符数量')
# python train.py --use_embedding --input_file data/poetry.txt --name poetry --learning_rate 0.005 --num_steps 26 --num_seqs 32 --max_steps 10000 # python train.py \
# --use_embedding \
# --input_file data/poetry.txt \
# --name poetry \
# --learning_rate 0.005 \
# --num_steps 26 \
# --num_seqs 32 \
# --max_steps 10000 def main(_):
model_path = os.path.join('model', FLAGS.name)
if os.path.exists(model_path) is False:
os.makedirs(model_path)
with codecs.open(FLAGS.input_file, encoding='utf-8') as f: # 打开训练数据集poetry.txt
text = f.read()
converter = TextConverter(text, FLAGS.max_vocab) # 最大字符数量10000
converter.save_to_file(os.path.join(model_path, 'converter.pkl')) arr = converter.text_to_arr(text)
g = batch_generator(arr, FLAGS.num_seqs, FLAGS.num_steps) # 句子数量、句子长度
print(converter.vocab_size) #
model = CharRNN(converter.vocab_size,
num_seqs=FLAGS.num_seqs,
num_steps=FLAGS.num_steps,
lstm_size=FLAGS.lstm_size,
num_layers=FLAGS.num_layers,
learning_rate=FLAGS.learning_rate,
train_keep_prob=FLAGS.train_keep_prob,
use_embedding=FLAGS.use_embedding,
embedding_size=FLAGS.embedding_size)
model.train(g, FLAGS.max_steps, model_path, FLAGS.save_every_n, FLAGS.log_every_n) if __name__ == '__main__':
tf.app.run()

生成英文

将使用的训练文件shakespeare.txt保存在项目的data/文件夹下,对应的训练的命令为:

python train.py --input_file data/shakespeare.txt --name shakespeare --num_steps 50 --num_seqs 32 --learning_rate 0.01 --max_steps 20000

--input_file:用于训练的文本数据。程序要求训练的文本必须为使用utf-8编码的文件。

--name:指定模型的名称,该名称决定了模型的保存位置。如这里指定模型的名称为shakespeare,那么训练中模型的保存位置是在model/shakespeare目录下。

--num_steps、--num_seqs:这两个参数决定了一个batch内序列的个数(相当于batch_size)和单个序列的长度。其中--num_steps对应序列长度,--num_seqs对应序列个数。

--learning_rate:训练时使用的学习率。

--max_steps:一个step是运行一个batch,--max_steps固定了最大的运行步数。

运行后,模型会被保存在model/shakespeare目录下。

用机器写诗

对应的训练的命令为:

python train.py --use_embedding --input_file data/poetry.txt --name potry --learning_rate 0.005 --num_steps 26 --num_seqs 32 --max_steps 10000

--use_embedding:为输入数据加入一个embedding层。默认是使用独热编码而不使用embedding的,这里对汉字生成加入embedding层,可以获得更好的效果。

C代码生成

对应的训练的命令为:

python train.py --input_file data/linux.txt --num_steps 100 --name linux --learning_rate 0.01 --num_seqs 32 --max_steps 20000

这里使用了更大的序列长度100(即num_steps参数)。对于代码来说,依赖关系可能在较长的序列中才能体现出来(如函数的大括号等)。代码同样采用单个字母或符号输入,因此没有必要使用embedding层。

model.py

# coding: utf-8
import os
import time
import numpy as np
import tensorflow as tf def pick_top_n(preds, vocab_size, top_n=5):
p = np.squeeze(preds)
# p[np.argsort(p)]将p从小到大排序
p[np.argsort(p)[:-top_n]] = 0 # 将除了top_n个预测值的位置都置为0
p = p / np.sum(p) # 归一化概率
# 以p的概率从vocab_size中随机选取一个字符,p是列表,vocab_size也是列表,p代表vocab_size中每个字的概率
c = np.random.choice(vocab_size, 1, p=p)[0]
return c class CharRNN:
def __init__(self, num_classes, num_seqs=32, num_steps=26, lstm_size=128, num_layers=2, learning_rate=0.001,
grad_clip=5, sampling=False, train_keep_prob=0.5, use_embedding=False, embedding_size=128):
if sampling is True: # 如果是测试
num_seqs, num_steps = 1, 1
else:
num_seqs, num_steps = num_seqs, num_steps self.num_classes = num_classes # 一共分3501类,每个字是一类,判断下一个字出现的概率,是下一个类的概率,分类任务
self.num_seqs = num_seqs # 一个batch里面句子的数量32
self.num_steps = num_steps # 句子的长度26
self.lstm_size = lstm_size # 隐藏层大小 (batch_size, state_size)
self.num_layers = num_layers # LSTM层数量
self.learning_rate = learning_rate # 学习率
self.grad_clip = grad_clip
self.train_keep_prob = train_keep_prob
self.use_embedding = use_embedding
self.embedding_size = embedding_size # embedding的大小128 tf.reset_default_graph()
self.build_inputs()
self.build_lstm()
self.build_loss()
self.build_optimizer()
self.saver = tf.train.Saver() def build_inputs(self):
with tf.name_scope('inputs'):
# shape = (batch_size, num_steps) = (句子数量,句子长度)=(32, 26)
self.inputs = tf.placeholder(tf.int32, shape=(self.num_seqs, self.num_steps), name='inputs')
# 输出shape=输入shape,内容是self.inputs每个字母对应的下一个字母(32, 26)
self.targets = tf.placeholder(tf.int32, shape=(self.num_seqs, self.num_steps), name='targets')
self.keep_prob = tf.placeholder(tf.float32, name='keep_prob') # 对于汉字生成,使用embedding层会取得更好的效果。
# 英文字母没有必要用embedding层
if self.use_embedding is False:
self.lstm_inputs = tf.one_hot(self.inputs, self.num_classes)
else:
with tf.device("/cpu:0"):
# 先定义一个embedding变量,embedding才是我们的训练数据(字的总类别,每个字的向量)=(3501, 128)
embedding = tf.get_variable('embedding', [self.num_classes, self.embedding_size])
# 使用tf.nn.embedding lookup查找embedding,让self.input从embedding中查数据
# 请注意embedding变量也是可以训练的,因此是通过训练得到embedding的具体数值。 # embedding.shape=[self.num_classes, self.embedding_size]=(3501, 128)
# self.inputs.shape=(num_seqs, num_steps)=(句子数量,句子长度)=(32, 26)
# self.lstm_inputs是直接输入LSTM的数据。
# self.lstm_inputs.shape=(batch_size, time_step, input_size)=(num_seqs, num_steps, embedding_size)=(句子数量,句子长度,词向量)=(32, 26, 128)
self.lstm_inputs = tf.nn.embedding_lookup(embedding, self.inputs) def build_lstm(self):
"""定义多层N vs N LSTM模型""" # 创建单个cell函数
def get_a_cell(lstm_size, keep_prob):
lstm = tf.nn.rnn_cell.BasicLSTMCell(lstm_size)
drop = tf.nn.rnn_cell.DropoutWrapper(lstm, output_keep_prob=keep_prob)
return drop # 将LSTMCell进行堆叠
with tf.name_scope('lstm'):
cell = tf.nn.rnn_cell.MultiRNNCell(
[get_a_cell(self.lstm_size, self.keep_prob) for _ in range(self.num_layers)])
# 隐藏层的初始化 shape=batch_size,计入笔记中,你的博客漏掉了
self.initial_state = cell.zero_state(self.num_seqs, tf.float32) # (batch_size, state_size)
print("self.initial_state.shape", self.initial_state)
# (LSTMStateTuple(
# c= <tf.Tensor 'lstm/MultiRNNCellZeroState/DropoutWrapperZeroState/BasicLSTMCellZeroState/zeros:0' shape = (32, 128) dtype = float32 >,
# h = < tf.Tensor 'lstm/MultiRNNCellZeroState/DropoutWrapperZeroState/BasicLSTMCellZeroState/zeros_1:0' shape = (32, 128) dtype = float32 >),
# LSTMStateTuple(
# c= < tf.Tensor 'lstm/MultiRNNCellZeroState/DropoutWrapperZeroState_1/BasicLSTMCellZeroState/zeros:0' shape = (32, 128) dtype = float32 >,
# h = < tf.Tensor 'lstm/MultiRNNCellZeroState/DropoutWrapperZeroState_1/BasicLSTMCellZeroState/zeros_1:0' shape = (32, 128) dtype = float32 >)) # 将我们创建的LSTMCell通过dynamic_rnn对cell展开时间维度,不然只是在时间上走"一步"
# inputs_shape = (batch_size, time_steps, input_size)
# initial_state_shape = (batch_size, cell.state_size)
# output_shape=(batch_size, time_steps, cell.output_size)=(32, 26, 128) time_steps步里所有输出,是个列表
self.lstm_outputs, self.final_state = tf.nn.dynamic_rnn(cell, self.lstm_inputs, initial_state=self.initial_state)
# 通过lstm_outputs得到概率
seq_output = tf.concat(self.lstm_outputs, 1) # 合并所有time_step得到输出,lstm_outputs只有一个,因此还是原shape=32, 26, 128)
x = tf.reshape(seq_output, [-1, self.lstm_size]) # (batch_size*time_steps, cell.output_size)=(32*26, 128) # softmax层
with tf.variable_scope('softmax'):
softmax_w = tf.Variable(tf.truncated_normal([self.lstm_size, self.num_classes], stddev=0.1))
softmax_b = tf.Variable(tf.zeros(self.num_classes)) self.logits = tf.matmul(x, softmax_w) + softmax_b # 预测值
self.proba_prediction = tf.nn.softmax(self.logits, name='predictions') # 变成下一个词出现的概率 def build_loss(self):
with tf.name_scope('loss'):
y_one_hot = tf.one_hot(self.targets, self.num_classes)
y_reshaped = tf.reshape(y_one_hot, self.logits.get_shape())
loss = tf.nn.softmax_cross_entropy_with_logits(logits=self.logits, labels=y_reshaped)
self.loss = tf.reduce_mean(loss) def build_optimizer(self):
# 使用截断梯度下降 clipping gradients
tvars = tf.trainable_variables()
grads, _ = tf.clip_by_global_norm(tf.gradients(self.loss, tvars), self.grad_clip)
train_op = tf.train.AdamOptimizer(self.learning_rate)
self.optimizer = train_op.apply_gradients(zip(grads, tvars)) def train(self, batch_generator, max_steps, save_path, save_every_n, log_every_n):
self.session = tf.Session()
with self.session as sess:
sess.run(tf.global_variables_initializer())
# Train network
step = 0
new_state = sess.run(self.initial_state)
for x, y in batch_generator:
step += 1
start = time.time()
feed = {self.inputs: x,
self.targets: y,
self.keep_prob: self.train_keep_prob,
self.initial_state: new_state}
batch_loss, new_state, _ = sess.run([self.loss, self.final_state, self.optimizer], feed_dict=feed) end = time.time()
# control the print lines
if step % log_every_n == 0:
print('step: {}/{}... '.format(step, max_steps),
'loss: {:.4f}... '.format(batch_loss),
'{:.4f} sec/batch'.format((end - start)))
if step % save_every_n == 0:
self.saver.save(sess, os.path.join(save_path, 'model'), global_step=step)
if step >= max_steps:
break
self.saver.save(sess, os.path.join(save_path, 'model'), global_step=step) def sample(self, n_samples, prime, vocab_size):
"""
:param n_samples: 生成多少词
:param prime: 开始字符串
:param vocab_size: 一共有多少字符
"""
samples = [c for c in prime] # [6, 14]=[风, 水]
sess = self.session
new_state = sess.run(self.initial_state)
preds = np.ones((vocab_size,)) # for prime=[]
for c in prime:
print("输入的单词是:", c)
x = np.zeros((1, 1))
# 输入单个字符
x[0, 0] = c
feed = {self.inputs: x,
self.keep_prob: 1.,
self.initial_state: new_state}
# preds是概率,
preds, new_state = sess.run([self.proba_prediction, self.final_state], feed_dict=feed) c = pick_top_n(preds, vocab_size)
print("预测出的词是", c) # 18-->中
samples.append(c) # 添加字符到samples中 # 不断生成字符,直到达到指定数目
for i in range(n_samples): #
x = np.zeros((1, 1))
x[0, 0] = c
feed = {self.inputs: x,
self.keep_prob: 1.,
self.initial_state: new_state}
preds, new_state = sess.run([self.proba_prediction, self.final_state], feed_dict=feed) c = pick_top_n(preds, vocab_size) # c 为词索引
samples.append(c) return np.array(samples) def load(self, checkpoint):
self.session = tf.Session()
self.saver.restore(self.session, checkpoint)
print('Restored from: {}'.format(checkpoint))

model.py

下面我挑重点讲

  模型的定义主要放在model.py文件中

输入数据的定义

def build_inputs(self):
with tf.name_scope('inputs'):
# shape = (batch_size, num_steps) = (句子数量,句子长度)=(32, 26)
self.inputs = tf.placeholder(tf.int32, shape=(self.num_seqs, self.num_steps), name='inputs')
# 输出shape=输入shape,内容是self.inputs每个字母对应的下一个字母(32, 26)
self.targets = tf.placeholder(tf.int32, shape=(self.num_seqs, self.num_steps), name='targets')
self.keep_prob = tf.placeholder(tf.float32, name='keep_prob') # 对于汉字生成,使用embedding层会取得更好的效果。
# 英文字母没有必要用embedding层
if self.use_embedding is False:
self.lstm_inputs = tf.one_hot(self.inputs, self.num_classes)
else:
with tf.device("/cpu:0"):
# 先定义一个embedding变量,embedding才是我们的训练数据(字的总类别,每个字的向量)=(3501, 128)
embedding = tf.get_variable('embedding', [self.num_classes, self.embedding_size])
# 使用tf.nn.embedding lookup查找embedding,让self.input从embedding中查数据
# 请注意embedding变量也是可以训练的,因此是通过训练得到embedding的具体数值。 # embedding.shape=[self.num_classes, self.embedding_size]=(3501, 128)
# self.inputs.shape=(num_seqs, num_steps)=(句子数量,句子长度)=(32, 26)
# self.lstm_inputs是直接输入LSTM的数据。
# self.lstm_inputs.shape=(batch_size, time_step, input_size)=(num_seqs, num_steps, embedding_size)=(句子数量,句子长度,词向量)=(32, 26, 128)
self.lstm_inputs = tf.nn.embedding_lookup(embedding, self.inputs)

self.inputs:外部传入的一个batch内的输入数据,它的形状为(self.num_seqs, self.num_steps)

self.lstm_inputs:是直接输入LSTM的数据

self.num_seqs:一个batch内句子的个数(相当于batch_size)

self.num_steps:每个句子的长度

self.targets是self.inputs对应的训练目标,它的形状和self.inputs相同,内容是self.inputs每个字母对应的下一个字母。

self.keep_prob:后面的模型中有Dropout层,这里的self.keep_prob控制了Dropout层所需要的概率。在训练时,使用self.keep_prob=0.5,在测试时,使用self.keep_prob=1.0。

  对于单个的英文字母,一般不使用embedding层,而对于汉字生成,使用embedding层会取得更好的效果。程序中用self.use_embedding参数控制是否使用embedding。

  • 当不使用embedding时,会直接对self.inputs做独热编码得到self.lstm_inputs;
  • 当使用embedding时,会先定义一个embedding变量,接着使用tf.nn.embedding_lookup查找embedding。

注意:embedding变量也是可以训练的,因此是通过训练得到embedding的具体数值。

定义多层LSTM模型

下面的函数定义了多层的 N VS N LSTM 模型

def build_lstm(self):
"""定义多层N vs N LSTM模型""" # 创建单个cell函数
def get_a_cell(lstm_size, keep_prob):
lstm = tf.nn.rnn_cell.BasicLSTMCell(lstm_size)
drop = tf.nn.rnn_cell.DropoutWrapper(lstm, output_keep_prob=keep_prob)
return drop # 将LSTMCell进行堆叠
with tf.name_scope('lstm'):
cell = tf.nn.rnn_cell.MultiRNNCell(
[get_a_cell(self.lstm_size, self.keep_prob) for _ in range(self.num_layers)])
# 隐藏层的初始化 shape=batch_size,计入笔记中,你的博客漏掉了
self.initial_state = cell.zero_state(self.num_seqs, tf.float32) # (batch_size, state_size)
print("self.initial_state.shape", self.initial_state)
# (LSTMStateTuple(
# c= <tf.Tensor 'lstm/MultiRNNCellZeroState/DropoutWrapperZeroState/BasicLSTMCellZeroState/zeros:0' shape = (32, 128) dtype = float32 >,
# h = < tf.Tensor 'lstm/MultiRNNCellZeroState/DropoutWrapperZeroState/BasicLSTMCellZeroState/zeros_1:0' shape = (32, 128) dtype = float32 >),
# LSTMStateTuple(
# c= < tf.Tensor 'lstm/MultiRNNCellZeroState/DropoutWrapperZeroState_1/BasicLSTMCellZeroState/zeros:0' shape = (32, 128) dtype = float32 >,
# h = < tf.Tensor 'lstm/MultiRNNCellZeroState/DropoutWrapperZeroState_1/BasicLSTMCellZeroState/zeros_1:0' shape = (32, 128) dtype = float32 >)) # 将我们创建的LSTMCell通过dynamic_rnn对cell展开时间维度,不然只是在时间上走"一步"
# inputs_shape = (batch_size, time_steps, input_size)
# initial_state_shape = (batch_size, cell.state_size)
# output_shape=(batch_size, time_steps, cell.output_size)=(32, 26, 128) time_steps步里所有输出,是个列表
self.lstm_outputs, self.final_state = tf.nn.dynamic_rnn(cell, self.lstm_inputs, initial_state=self.initial_state)
# 通过lstm_outputs得到概率
seq_output = tf.concat(self.lstm_outputs, 1) # 合并所有time_step得到输出,lstm_outputs只有一个,因此还是原shape=32, 26, 128)
x = tf.reshape(seq_output, [-1, self.lstm_size]) # (batch_size*time_steps, cell.output_size)=(32*26, 128) # softmax层
with tf.variable_scope('softmax'):
softmax_w = tf.Variable(tf.truncated_normal([self.lstm_size, self.num_classes], stddev=0.1))
softmax_b = tf.Variable(tf.zeros(self.num_classes)) self.logits = tf.matmul(x, softmax_w) + softmax_b # 预测值
self.proba_prediction = tf.nn.softmax(self.logits, name='predictions') # 变成下一个词出现的概率

在这段代码中,定义了一个多层的BasicLSTMCell。并且在这里对每个BasicLSTMCell使用了tf.nn.rnn_cell.DropoutWrapper函数,即加入了一层Dropout,以减少过拟合。

  定义了cell后,使用tf.nn.dynamic_rnn函数展开了时间维度。tf.nn.dynamic_rnn的输入为cell、self.lstm_inputs、self.initial_state。其中cell已经解释过了,self.inputs是在上一节中的输出层定义的,self.initial_state是通过调用cell.zero_state得到的一个全0的Tensor,表示初始的隐层状态。

  tf.nn.dynamic_rnn的输出为self.outputs和self.final_state。self.outputs是多层LSTM的隐层h,因此需要得到最后的分类概率,还需要再定义一层Softmax层才可以。这里经过一次类似于Wx+b的变换后得到self.logits,再做Softmax处理,输出为self.proba_prediction。

定义损失

  得到self.proba_prediction后,可以使用它和self.targets的独热编码做交叉熵得到损失。另外,也可以使用tf.nn.softmax_cross_entropy_with_logits函数,通过self.logits直接定义损失,对应的代码如下:

def build_loss(self):
with tf.name_scope('loss'):
y_one_hot = tf.one_hot(self.targets, self.num_classes)
y_reshaped = tf.reshape(y_one_hot, self.logits.get_shape())
loss = tf.nn.softmax_cross_entropy_with_logits(logits=self.logits, labels=y_reshaped)
self.loss = tf.reduce_mean(loss)

sample.py

# Author:凌逆战
# -*- coding:utf-8 -*-
import tensorflow as tf
from read_utils import TextConverter
from model import CharRNN
import os FLAGS = tf.flags.FLAGS tf.flags.DEFINE_integer('lstm_size', 128, 'size of hidden state of lstm')
tf.flags.DEFINE_integer('num_layers', 2, 'number of lstm layers')
tf.flags.DEFINE_boolean('use_embedding', False, 'whether to use embedding')
tf.flags.DEFINE_integer('embedding_size', 128, 'size of embedding')
tf.flags.DEFINE_string('converter_path', '', 'model/name/converter.pkl')
tf.flags.DEFINE_string('checkpoint_path', '', 'checkpoint path')
tf.flags.DEFINE_string('start_string', '', 'use this string to start generating')
tf.flags.DEFINE_integer('max_length', 30, 'max length to generate')
# --use_embedding --start_string "风水" --converter_path model/poetry/converter.pkl --checkpoint_path model/poetry/ --max_length 30 def main(_):
FLAGS.start_string = FLAGS.start_string
converter = TextConverter(filename=FLAGS.converter_path)
if os.path.isdir(FLAGS.checkpoint_path):
FLAGS.checkpoint_path = tf.train.latest_checkpoint(FLAGS.checkpoint_path) model = CharRNN(converter.vocab_size,
sampling=True,
lstm_size=FLAGS.lstm_size,
num_layers=FLAGS.num_layers,
use_embedding=FLAGS.use_embedding,
embedding_size=FLAGS.embedding_size) model.load(FLAGS.checkpoint_path) start = converter.text_to_arr(FLAGS.start_string)
arr = model.sample(FLAGS.max_length, start, converter.vocab_size)
print("arr装的是每个单词的位置", arr)
print(converter.arr_to_text(arr)) if __name__ == '__main__':
tf.app.run()

莎士比亚诗集测试命令

python sample.py --converter_path model/shakespeare/converter.pkl --checkpoint_path model/shakespeare/ --max_length 1000

对应参数的含义为:

--converter_path:其实神经网络生成的是字母类别id,并不是字母。这些类别id在输入模型时是通过一个converter转换的,程序会自动把converter保存在model/shakespeare目录下。在输出时需要使用converter将类别id转换回字母。

--checkpoint_path:模型的保存路径。

--max_length:生成的序列长度。

唐诗测试命令

python sample.py --use_embeding --converter_path model/poetry/converter.pkl --checkpoint_path model/poetry/ --max_length 300

因为在训练时使用了--use_embedding。所以在测试时也必须使用--use_embedding,这样才能成功载入参数。生成诗歌的效果为:

C代码测试命令

python sample.py --converter_path model/lonux/converter.pkl --checkpoint_path model/linux --max_length 1000

多次运行测试命令可以生成完全不一样的程序段。

read_utils.py

# Author:凌逆战
# -*- coding:utf-8 -*-
import numpy as np
import copy
import pickle def batch_generator(arr, n_seqs, n_steps):
"""
:param arr: 训练集数据
:param n_seqs:一个batch的句子数量,32
:param n_steps: 句子长度,26
:return: x, y 的生成器
"""
arr = copy.copy(arr) # 把数据备份一份
batch_size = n_seqs * n_steps # 一个batch的句子数量*句子长度=一个batch的总字数
n_batches = int(len(arr) / batch_size) # 取到了batch的整数
arr = arr[:batch_size * n_batches] # [:n_seqs * n_steps * n_batches]
arr = arr.reshape((n_seqs, -1)) # # [n_seqs: n_steps * n_batches]
while True:
np.random.shuffle(arr)
# 每次循环是一次batch
for n in range(0, arr.shape[1], n_steps):
x = arr[:, n:n + n_steps] # 一个句子,句子的每个词
y = np.zeros_like(x)
# y[:, -1]所有行的最后一列=x[:, 0] 所有行的第0列
y[:, :-1], y[:, -1] = x[:, 1:], x[:, 0]
yield x, y class TextConverter(object):
def __init__(self, text=None, max_vocab=5000, filename=None):
if filename is not None:
with open(filename, 'rb') as f:
self.vocab = pickle.load(f)
else:
vocab = set(text) # 变成集和,去重
print("数据集总共用到了多少词", len(vocab)) #
# max_vocab_process
# 计算每个词出现的次数
vocab_count = {}
for word in vocab:
vocab_count[word] = 0
for word in text:
vocab_count[word] += 1 vocab_count_list = [] # [(词,词数量), (词,词数量)...]
for word in vocab_count: # 字典循环,得到的是键
vocab_count_list.append((word, vocab_count[word]))
vocab_count_list.sort(key=lambda x: x[1], reverse=True) # 按照词数量倒序 大-->小
if len(vocab_count_list) > max_vocab:
vocab_count_list = vocab_count_list[:max_vocab]
vocab = [x[0] for x in vocab_count_list]
self.vocab = vocab # 装载所有词的列表 self.word_to_int_table = {c: i for i, c in enumerate(self.vocab)}
self.int_to_word_table = dict(enumerate(self.vocab)) # {(索引,单词),(索引,单词)...}
for item in list(self.int_to_word_table.items())[:50]: # 遍历字典中的元素
print(item)
# (0, ',')
# (1, '。')
# (2, '\n')
# (3, '不')
# (4, '人')
# (5, '山')
# (6, '风')
# (7, '日')
# (8, '云')
# (9, '无')
# (10, '何')
# (11, '一')
# (12, '春')
# (13, '月')
# (14, '水')
# (15, '花') @property
def vocab_size(self):
return len(self.vocab) + 1 def word_to_int(self, word):
if word in self.word_to_int_table:
return self.word_to_int_table[word] # 返回这是第几个词
else:
return len(self.vocab) def int_to_word(self, index):
if index == len(self.vocab):
return '<unk>'
elif index < len(self.vocab):
return self.int_to_word_table[index] # 返回第几个词所对应的词
else:
raise Exception('Unknown index!') def text_to_arr(self, text):
arr = []
for word in text:
arr.append(self.word_to_int(word)) # text中的词,出现在vocab中的索引
return np.array(arr) def arr_to_text(self, arr):
words = []
for index in arr:
words.append(self.int_to_word(index))
return "".join(words) def save_to_file(self, filename):
with open(filename, 'wb') as f:
pickle.dump(self.vocab, f)

因为在训练时使用了--use_embedding。所以在测试时也必须使用--use_embedding,这样才能成功载入参数。

这可能是国内最全面的char RNN注释的更多相关文章

  1. 干货,比较全面的c#.net公共帮助类

    比较全面的c#帮助类 比较全面的c#帮助类,日常工作收集,包括前面几家公司用到的,各式各样的几乎都能找到,所有功能性代码都是独立的类,类与类之间没有联系,可以单独引用至项目,分享出来,方便大家,几乎都 ...

  2. 最全面的iOS和Mac开源项目和第三方库汇总

    标签: UI 下拉刷新 EGOTableViewPullRefresh – 最早的下拉刷新控件. SVPullToRefresh – 下拉刷新控件. MJRefresh – 仅需一行代码就可以为UIT ...

  3. 【Linux开发】全面的framebuffer详解

    全面的framebuffer详解 一.FrameBuffer的原理 FrameBuffer 是出现在 2.2.xx 内核当中的一种驱动程序接口. Linux是工作在保护模式下,所以用户态进程是无法象D ...

  4. 超全面的.NET GDI+图形图像编程教程

    本篇主题内容是.NET GDI+图形图像编程系列的教程,不要被这个滚动条吓到,为了查找方便,我没有分开写,上面加了目录了,而且很多都是源码和图片~ (*^_^*) 本人也为了学习深刻,另一方面也是为了 ...

  5. 非常全面的SQL Server巡检脚本来自sqlskills团队的Glenn Berry 大牛

    非常全面的SQL Server巡检脚本来自sqlskills团队的Glenn Berry 大牛 Glenn Berry 大牛会对这个脚本持续更新 -- SQL Server 2012 Diagnost ...

  6. 最全面的 C++ 资源、框架大全

    转载自   http://www.codeceo.com/article/cpp-resource-framework.html#0-tsina-1-99850-397232819ff9a47a7b7 ...

  7. 比较全面的gdb调试命令

    from:http://blog.csdn.net/xiajun07061225/article/details/8960332 http://blog.csdn.net/cjfeii/article ...

  8. [置顶] echarts x轴文字显示不全(xAxis文字倾斜比较全面的3种做法值得推荐)

    echarts x轴标签文字过多导致显示不全 如图: 解决办法1:xAxis.axisLabel 属性 axisLabel的类型是object ,主要作用是:坐标轴刻度标签的相关设置.(当然yAxis ...

  9. 国内最全的Spring Boot系列之二

    历史文章 <国内最全的Spring Boot系列之一> 视频&交流平台 SpringBoot视频:http://t.cn/R3QepWG Spring Cloud视频:http:/ ...

随机推荐

  1. Mybatis学习笔记之---动态sql中标签的使用

    动态Sql语句中标签的使用 (一)常用标签 1.<if> if标签通常用于WHERE语句中,通过判断参数值来决定是否使用某个查询条件, 他也经常用于UPDATE语句中判断是否更新某一个字段 ...

  2. MVC+EF Core 完整教程20--tag helper详解

    之前我们有一篇:“动态生成多级菜单”,对使用Html Helper做了详细讲述,并且自定义了一个菜单的 Html Helper: https://www.cnblogs.com/miro/p/5541 ...

  3. 第三方登录之QQ

    public class MainActivity extends AppCompatActivity { private Button btn; private TextView tv; priva ...

  4. 前端模板引擎doT.js的用法

    简介 一款简单好用的前端模板引擎 用法 <script type="text/javascript" src="js/doT.min.js">< ...

  5. SQL语句完成Excel数据导入数据库表中流程方法及注意事项

    第一步:先查看数据库是否安装AccessDatabaseEngine_X64.exe, 如下图查看: 如果未安装先下载脚本之家下载地址 https://www.jb51.net/softs/29150 ...

  6. thinkphp 框架两种模式 两种模式:开发调试模式、线上生产模式

    define(‘APP_DEBUG’,true/false);

  7. File Compression and Archiving in linux (linux 中文件的归档)

    1. Compressing Files at the Shell Prompt Red Hat Enterprise Linux provides the bzip2, gzip, and zip ...

  8. caddy(四)Run详解

    caddy(四)Run详解 前言 平时我们使用 caddy 都是使用 它的 二进制 分发文件,我们现在来分析 caddy 的 Run 函数.从最外层抽象的看它都做了些什么. Caddy Run 我们来 ...

  9. [WPF自定义控件库] 给WPF一个HyperlinkButton

    1. 在WPF怎么在UI上添加超级链接 这篇文章的目的是介绍怎么在WPF里创建自定义的HyperlinkButton控件.很神奇的,WPF居然连HyperlinkButton都没有,不过它提供了另一种 ...

  10. Sqlserver2012 评估期已过问题

    sql server 2012提示评估期已过的解决方法: 第一步:进入SQL2012配置工具中的安装中心. 第二步:再进入左侧维护选项界面,然后选择选择版本升级. 第三步:进入输入产品密钥界面,输入相 ...