基于循环神经网络(RNN)的古诗生成器
|
基于循环神经网络(RNN)的古诗生成器,具体内容如下 之前在手机百度上看到有个“为你写诗”功能,能够随机生成古诗,当时感觉很酷炫= = 在学习了深度学习后,了解了一下原理,打算自己做个实现练练手,于是,就有了这个项目。文中如有瑕疵纰漏之处,还请路过的诸位大佬不吝赐教,万分感谢! 使用循环神经网络实现的古诗生成器,能够完成古体诗的自动生成。我简单地训练了一下,格式是对上了,至于意境么。。。emmm,呵呵 举一下模型测试结果例子: 1.生成古体诗 示例1:
示例2:
2.生成藏头诗(以“神策”为例) 示例1:
示例2:
下面记录项目实现过程(由于都是文本处理方面,跟前一个项目存在很多类似的内容,对于这部分内容,我就只简单提一下,不展开了,新的东西再具体说): 1.数据预处理 数据集使用四万首的唐诗训练集,可以点击这里进行下载。 数据预处理的过程与前一个项目TensorFlow练手项目一:使用循环神经网络(RNN)实现影评情感分类大同小异,可以参考前一个项目,这里就不多说了,直接上代码。
# -*- coding: utf-8 -*-
# @Time : 18-3-13 上午11:04
# @Author : AaronJny
# @Email : Aaron__7@163.com
import sys
reload(sys)
sys.setdefaultencoding('utf8')
import collections
ORIGIN_DATA = 'origin_data/poetry.txt' # 源数据路径
OUTPUT_DATA = 'processed_data/poetry.txt' # 输出向量路径
VOCAB_DATA = 'vocab/poetry.vocab'
def word_to_id(word,id_dict):
if word in id_dict:
return id_dict[word]
else:
return id_dict['<unknow>']
poetry_list = [] # 存放唐诗的数组
# 从文件中读取唐诗
with open(ORIGIN_DATA,'r') as f:
f_lines = f.readlines()
print '唐诗总数 : {}'.format(len(f_lines))
# 逐行进行处理
for line in f_lines:
# 去除前后空白符,转码
strip_line = line.strip().decode('utf8')
try:
# 将唐诗分为标题和内容
title,content = strip_line.split(':')
except:
# 出现多个':'的将被舍弃
continue
# 去除内容中的空格
content = content.strip().replace(' ','')
# 舍弃含有非法字符的唐诗
if '(' in content or '(' in content or '<' in content or '《' in content or '_' in content or '[' in content:
continue
# 舍弃过短或过长的唐诗
lenth = len(content)
if lenth < 20 or lenth > 100:
continue
# 加入列表
poetry_list.append('s' + content + 'e')
print '用于训练的唐诗数 : {}'.format(len(poetry_list))
poetry_list=sorted(poetry_list,key=lambda x:len(x))
words_list = []
# 获取唐诗中所有的字符
for poetry in poetry_list:
words_list.extend([word for word in poetry])
# 统计其出现的次数
counter = collections.Counter(words_list)
# 排序
sorted_words = sorted(counter.items(),key=lambda x: x[1],reverse=True)
# 获得出现次数降序排列的字符列表
words_list = ['<unknow>'] + [x[0] for x in sorted_words]
# 这里选择保留高频词的数目,词只有不到七千个,所以我全部保留
words_list = words_list[:len(words_list)]
print '词汇表大小 : {}'.format(words_list)
with open(VOCAB_DATA,'w') as f:
for word in words_list:
f.write(word + 'n')
# 生成单词到id的映射
word_id_dict = dict(zip(words_list,range(len(words_list))))
# 将poetry_list转换成向量形式
id_list=[]
for poetry in poetry_list:
id_list.append([str(word_to_id(word,word_id_dict)) for word in poetry])
# 将向量写入文件
with open(OUTPUT_DATA,'w') as f:
for id_l in id_list:
f.write(' '.join(id_l) + 'n')
2.模型编写 这里要编写两个模型,一个用于训练,一个用于验证(生成古体诗)。两个模型大体上一致,因为用途不同,所以有些细节有出入。当进行验证时,验证模型读取训练模型的参数进行覆盖。 注释比较细,就不多说了,看代码。对于两个模型不同的一些关键细节,我也用注释进行了说明。
# -*- coding: utf-8 -*-
# @Time : 18-3-13 下午2:06
# @Author : AaronJny
# @Email : Aaron__7@163.com
import tensorflow as tf
import functools
import setting
HIDDEN_SIZE = 128 # LSTM隐藏节点个数
NUM_LAYERS = 2 # RNN深度
def doublewrap(function):
@functools.wraps(function)
def decorator(*args,**kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
return function(args[0])
else:
return lambda wrapee: function(wrapee,*args,**kwargs)
return decorator
@doublewrap
def define_scope(function,scope=None,**kwargs):
attribute = '_cache_' + function.__name__
name = scope or function.__name__
@property
@functools.wraps(function)
def decorator(self):
if not hasattr(self,attribute):
with tf.variable_scope(name,**kwargs):
setattr(self,attribute,function(self))
return getattr(self,attribute)
return decorator
class TrainModel(object):
"""
训练模型
"""
def __init__(self,data,labels,emb_keep,rnn_keep):
self.data = data # 数据
self.labels = labels # 标签
self.emb_keep = emb_keep # embedding层dropout保留率
self.rnn_keep = rnn_keep # lstm层dropout保留率
self.global_step
self.cell
self.predict
self.loss
self.optimize
@define_scope
def cell(self):
"""
rnn网络结构
:return:
"""
lstm_cell = [
tf.nn.rnn_cell.DropoutWrapper(tf.nn.rnn_cell.BasicLSTMCell(HIDDEN_SIZE),output_keep_prob=self.rnn_keep) for
_ in range(NUM_LAYERS)]
cell = tf.nn.rnn_cell.MultiRNNCell(lstm_cell)
return cell
@define_scope
def predict(self):
"""
定义前向传播
:return:
"""
# 创建词嵌入矩阵权重
embedding = tf.get_variable('embedding',shape=[setting.VOCAB_SIZE,HIDDEN_SIZE])
# 创建softmax层参数
if setting.SHARE_EMD_WITH_SOFTMAX:
softmax_weights = tf.transpose(embedding)
else:
softmax_weights = tf.get_variable('softmaweights',shape=[HIDDEN_SIZE,setting.VOCAB_SIZE])
softmax_bais = tf.get_variable('softmax_bais',shape=[setting.VOCAB_SIZE])
# 进行词嵌入
emb = tf.nn.embedding_lookup(embedding,self.data)
# dropout
emb_dropout = tf.nn.dropout(emb,self.emb_keep)
# 计算循环神经网络的输出
self.init_state = self.cell.zero_state(setting.BATCH_SIZE,dtype=tf.float32)
outputs,last_state = tf.nn.dynamic_rnn(self.cell,emb_dropout,scope='d_rnn',dtype=tf.float32,initial_state=self.init_state)
outputs = tf.reshape(outputs,[-1,HIDDEN_SIZE])
# 计算logits
logits = tf.matmul(outputs,softmax_weights) + softmax_bais
return logits
@define_scope
def loss(self):
"""
定义损失函数
:return:
"""
# 计算交叉熵
outputs_target = tf.reshape(self.labels,[-1])
loss = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=self.predict,labels=outputs_target,)
# 平均
cost = tf.reduce_mean(loss)
return cost
@define_scope
def global_step(self):
"""
global_step
:return:
"""
global_step = tf.Variable(0,trainable=False)
return global_step
@define_scope
def optimize(self):
"""
定义反向传播过程
:return:
"""
# 学习率衰减
learn_rate = tf.train.exponential_decay(setting.LEARN_RATE,self.global_step,setting.LR_DECAY_STEP,setting.LR_DECAY)
# 计算梯度,并防止梯度爆炸
trainable_variables = tf.trainable_variables()
grads,_ = tf.clip_by_global_norm(tf.gradients(self.loss,trainable_variables),setting.MAX_GRAD)
# 创建优化器,进行反向传播
optimizer = tf.train.AdamOptimizer(learn_rate)
train_op = optimizer.apply_gradients(zip(grads,self.global_step)
return train_op
class EvalModel(object):
"""
验证模型
"""
def __init__(self,rnn_keep):
self.data = data # 输入
self.emb_keep = emb_keep # embedding层dropout保留率
self.rnn_keep = rnn_keep # lstm层dropout保留率
self.cell
self.predict
self.prob
@define_scope
def cell(self):
"""
rnn网络结构
:return:
"""
lstm_cell = [
tf.nn.rnn_cell.DropoutWrapper(tf.nn.rnn_cell.BasicLSTMCell(HIDDEN_SIZE),output_keep_prob=self.rnn_keep) for
_ in range(NUM_LAYERS)]
cell = tf.nn.rnn_cell.MultiRNNCell(lstm_cell)
return cell
@define_scope
def predict(self):
"""
定义前向传播过程
:return:
"""
embedding = tf.get_variable('embedding',HIDDEN_SIZE])
if setting.SHARE_EMD_WITH_SOFTMAX:
softmax_weights = tf.transpose(embedding)
else:
softmax_weights = tf.get_variable('softmaweights',shape=[setting.VOCAB_SIZE])
emb = tf.nn.embedding_lookup(embedding,self.data)
emb_dropout = tf.nn.dropout(emb,self.emb_keep)
# 与训练模型不同,这里只要生成一首古体诗,所以batch_size=1
self.init_state = self.cell.zero_state(1,HIDDEN_SIZE])
logits = tf.matmul(outputs,softmax_weights) + softmax_bais
# 与训练模型不同,这里要记录最后的状态,以此来循环生成字,直到完成一首诗
self.last_state = last_state
return logits
@define_scope
def prob(self):
"""
softmax计算概率
:return:
"""
probs = tf.nn.softmax(self.predict)
return probs
3.组织数据集 编写一个类用于组织数据,方便训练使用。代码很简单,应该不存在什么问题。 (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
