加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

基于循环神经网络(RNN)的古诗生成器

发布时间:2020-05-24 19:35:59 所属栏目:Python 来源:互联网
导读:基于循环神经网络(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.组织数据集

编写一个类用于组织数据,方便训练使用。代码很简单,应该不存在什么问题。

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读