TensorFlow构建循环神经网络

简介: 前言 前面在《循环神经网络》文章中已经介绍了深度学习的循环神经网络模型及其原理,接下去这篇文章将尝试使用TensorFlow来实现一个循环神经网络,该例子能通过训练给定的语料生成模型并实现对字符的预测。

前言

前面在《循环神经网络》文章中已经介绍了深度学习的循环神经网络模型及其原理,接下去这篇文章将尝试使用TensorFlow来实现一个循环神经网络,该例子能通过训练给定的语料生成模型并实现对字符的预测。这里选择使用最原始的循环神经网络RNN模型。

语料库的准备

这里就简单用纪伯伦的《On Friendship》作为语料吧。

RNN简要说明

用下面两张图简要说明下,RNN模型有多个时刻的输入,从第一个图中看到输入x、隐层s和输出o都与时刻t有关,可以看到上一时刻的隐含层会影响到当前时刻的输出,这也就使循环神经网络具有记忆功能。

为了更加清晰理解,看第二个图,从神经元来看RNN,输入层有3个神经元,隐含层有4个神经元,输出层有2个神经元,保留当前时刻状态参与下一时刻计算。

创建词汇

def create_vocab(text):
    unique_chars = list(set(text))
    print(unique_chars)
    vocab_size = len(unique_chars)
    vocab_index_dict = {}
    index_vocab_dict = {}
    for i, char in enumerate(unique_chars):
        vocab_index_dict[char] = i
        index_vocab_dict[i] = char
    return vocab_index_dict, index_vocab_dict, vocab_size

处理字符首先就是需要创建包含语料中所有的词的词汇,需要一个从字符到词汇位置索引的词典,也需要一个从位置索引到字符的词典。

词汇保存及读取

def save_vocab(vocab_index_dict, vocab_file):
    with codecs.open(vocab_file, 'w', encoding='utf-8') as f:
        json.dump(vocab_index_dict, f, indent=2, sort_keys=True)

def load_vocab(vocab_file):
    with codecs.open(vocab_file, 'r', encoding='utf-8') as f:
        vocab_index_dict = json.load(f)
    index_vocab_dict = {}
    vocab_size = 0
    for char, index in iteritems(vocab_index_dict):
        index_vocab_dict[index] = char
        vocab_size += 1
    return vocab_index_dict, index_vocab_dict, vocab_size

第一次创建词汇后我们需要将它保存下来,后面在使用模型预测时需要读取该词汇,如果不保存而每次都创建的话则可能导致词汇顺序不同。

批量生成器

class BatchGenerator(object):
    def __init__(self, text, batch_size, seq_length, vocab_size, vocab_index_dict):
        self._text = text
        self._text_size = len(text)
        self._batch_size = batch_size
        self.vocab_size = vocab_size
        self.seq_length = seq_length
        self.vocab_index_dict = vocab_index_dict

        segment = self._text_size // batch_size
        self._cursor = [offset * segment for offset in range(batch_size)]
        self._last_batch = self._next_batch()

    def _next_batch(self):
        batch = np.zeros(shape=(self._batch_size), dtype=np.float)
        for b in range(self._batch_size):
            batch[b] = self.vocab_index_dict[self._text[self._cursor[b]]]
            self._cursor[b] = (self._cursor[b] + 1) % self._text_size
        return batch

    def next(self):
        batches = [self._last_batch]
        for step in range(self.seq_length):
            batches.append(self._next_batch())
        self._last_batch = batches[-1]
        return batches

创建一个批量生成器用于将文本生成批量的训练样本,其中text为整个语料,batch_size为批大小,vocab_size为词汇大小,seq_length为序列长度,vocab_index_dict为词汇索引词典。生成器的生成结构大致如下图,按文本顺序竖着填进矩阵,而矩阵的列大小为batch_size。

这里写图片描述

构建图

cell_fn = tf.contrib.rnn.BasicRNNCell
cell = cell_fn(hidden_size)
cells = [cell]
for i in range(rnn_layers - 1):
    higher_layer_cell = cell_fn(hidden_size)
    cells.append(higher_layer_cell)
multi_cell = tf.contrib.rnn.MultiRNNCell(cells)
self.zero_state = multi_cell.zero_state(self.batch_size, tf.float32)

这里我们仅仅使用基础的RNN,所以直接实例化一个BasicRNNCell对象,需要指定隐含层的神经元数hidden_size,另外因为单层的RNN学习能力有限,这里可以设置网络的层数rnn_layers来增强神经网络的学习能力,最终用MultiRNNCell封装起来。

接着需要给我们的multi cell进行初始化状态,初始状态全设为0,即用multi_cell.zero_state来实现。这里需要传入一个batch_size参数,它会生成rnn_layers层的(batch_size ,hidden_size)个初始状态。

self.initial_state = create_tuple_placeholders_with_default(multi_cell.zero_state(self.batch_size, tf.float32), shape=multi_cell.state_size)
self.input_data = tf.placeholder(tf.int64, [self.batch_size, self.seq_length], name='inputs')
self.targets = tf.placeholder(tf.int64, [self.batch_size, self.seq_length], name='targets')

def create_tuple_placeholders_with_default(inputs, shape):
    if isinstance(shape, int):
        result = tf.placeholder_with_default(
            inputs, list((None,)) + [shape])
    else:
        subplaceholders = [create_tuple_placeholders_with_default(
            subinputs, subshape)
            for subinputs, subshape in zip(inputs, shape)]
        t = type(shape)
        if t == tuple:
            result = t(subplaceholders)
        else:
            result = t(*subplaceholders)
    return result

接着我们开始创建占位符,有三个占位符需要创建,分别为初始状态占位符、输入占位符和target占位符。首先看初始状态占位符,这个主要是根据multi_cell.zero_state(self.batch_size, tf.float32)的结构使用tf.placeholder_with_default创建。其次看输入占位符,与批大小和序列长度相关的结构[batch_size, seq_length]。最后是target占位符,结构与输入占位符是一样的。为更好理解这里给输入和target画个图,如下:

这里写图片描述

这里写图片描述

self.embedding = tf.get_variable('embedding', [vocab_size, embedding_size])
inputs = tf.nn.embedding_lookup(self.embedding, self.input_data)

一般我们会需要一个嵌入层将词汇嵌入到指定的维度空间上,维度由embedding_size指定。同时vocab_size为词汇大小,这样就可以将所有单词都映射到指定的维数空间上。嵌入层结构如下图,通过tf.nn.embedding_lookup就能找到输入对应的词空间向量了,这里解释下embedding_lookup操作,它会从词汇中取到inputs每个元素对应的词向量,inputs为2维的话,通过该操作后变为3维,因为已经将词用embedding_size维向量表示了。

这里写图片描述

sliced_inputs = [tf.squeeze(input_, [1]) for input_ in
                         tf.split(axis=1, num_or_size_splits=self.seq_length, value=inputs)]
outputs, final_state = tf.contrib.rnn.static_rnn(multi_cell, sliced_inputs, initial_state=self.initial_state)

上面得到的3维的嵌入层空间向量,我们无法直接传入循环神经网络,需要一些处理。需要根据序列长度切割,通过split后再经过squeeze操作后得到一个list,这个list就是最终要进入到循环神经网络的输入,list的长度为seq_length,这个很好理解,就是由这么多个时刻的输入。每个输入的结构为(batch_size,embedding_size),也即是(20,128)。注意这里的embedding_size,刚好也是128,与循环神经网络的隐含层神经元数量一样,这里不是巧合,而是他们必须要相同,这样嵌入层出来的矩阵输入到神经网络才能刚好与神经网络的各个权重完美相乘。最终得到循环神经网络的输出和最终状态。

flat_outputs = tf.reshape(tf.concat(axis=1, values=outputs), [-1, hidden_size])
flat_targets = tf.reshape(tf.concat(axis=1, values=self.targets), [-1])

softmax_w = tf.get_variable("softmax_w", [hidden_size, vocab_size])
softmax_b = tf.get_variable("softmax_b", [vocab_size])
self.logits = tf.matmul(flat_outputs, softmax_w) + softmax_b

loss = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=self.logits, labels=flat_targets)
mean_loss = tf.reduce_mean(loss)

经过2层循环神经网络得到了输出outputs,但该输出是一个list结构,我们要通过tf.reshape转成tf张量形式,该张量结构为(200,128)。同样target占位符也要连接起来,结构为(200,)。接着构建softmax层,权重结构为[hidden_size, vocab_size],偏置项结构为[vocab_size],输出矩阵与权重矩阵相乘并加上偏置项得到logits,然后使用sparse_softmax_cross_entropy_with_logits计算交叉熵损失,最后求损失平均值。

count = tf.Variable(1.0, name='count')
sum_mean_loss = tf.Variable(1.0, name='sum_mean_loss')
update_loss_monitor = tf.group(sum_mean_loss.assign(sum_mean_loss + mean_loss), count.assign(count + 1),
                                       name='update_loss_monitor')
with tf.control_dependencies([update_loss_monitor]):
    self.average_loss = sum_mean_loss / count
self.global_step = tf.get_variable('global_step', [], initializer=tf.constant_initializer(0.0))

这里逻辑比较清晰了,用于计算平均损失,另外global_step变量用于记录训练的全局步数。

tvars = tf.trainable_variables()
grads, _ = tf.clip_by_global_norm(tf.gradients(mean_loss, tvars), max_grad_norm)
optimizer = tf.train.AdamOptimizer(self.learning_rate)
self.train_op = optimizer.apply_gradients(zip(grads, tvars), global_step=self.global_step)

最后使用优化器对损失函数进行优化。为了防止梯度爆炸或梯度消失需要用clip_by_global_norm对梯度进行修正。

所以最后构建的图可以用下面的图大致描述。
这里写图片描述

创建会话

with tf.Session(graph=graph) as session:
tf.global_variables_initializer().run()
for i in range(num_epochs):
    model.train(session, train_size, train_batches)

def train(self, session, train_size, train_batches):
    epoch_size = train_size // (self.batch_size * self.seq_length)
    if train_size % (self.batch_size * self.seq_length) != 0:
        epoch_size += 1
    state = session.run(self.zero_state)
    start_time = time.time()
    for step in range(epoch_size):
        data = train_batches.next()
        inputs = np.array(data[:-1]).transpose()
        targets = np.array(data[1:]).transpose()
        ops = [self.average_loss, self.final_state, self.train_op, self.global_step, self.learning_rate]
        feed_dict = {self.input_data: inputs, self.targets: targets,
                     self.initial_state: state}
        average_loss, state, __, global_step, lr = session.run(ops, feed_dict)

创建会话开始训练,设置需要训练多少轮,由num_epochs指定。epoch_size为完整训练一遍语料库需要的轮数。运行self.zero_state得到初始状态,通过批量生成器获取一批样本数据,因为当前时刻的输入对应的正确输出为下一时刻的值,所以用data[:-1]和data[1:]得到输入和target。组织ops并将输入、target和状态对应输入到占位符上,执行。

预测

module_file = tf.train.latest_checkpoint(restore_path)
model_saver.restore(session, module_file)
start_text = 'your'
length = 20
print(model.predict(session, start_text, length, vocab_index_dict, index_vocab_dict))

将前面训练的模型保存下来后就可以加载该模型并且进行RNN预测了。

github

https://github.com/sea-boat/DeepLearning-Lab

========广告时间========

鄙人的新书《Tomcat内核设计剖析》已经在京东销售了,有需要的朋友可以到 https://item.jd.com/12185360.html 进行预定。感谢各位朋友。

为什么写《Tomcat内核设计剖析》

=========================

欢迎关注:

这里写图片描述

目录
相关文章
|
11天前
|
机器学习/深度学习 TensorFlow API
TensorFlow与Keras实战:构建深度学习模型
本文探讨了TensorFlow和其高级API Keras在深度学习中的应用。TensorFlow是Google开发的高性能开源框架,支持分布式计算,而Keras以其用户友好和模块化设计简化了神经网络构建。通过一个手写数字识别的实战案例,展示了如何使用Keras加载MNIST数据集、构建CNN模型、训练及评估模型,并进行预测。案例详述了数据预处理、模型构建、训练过程和预测新图像的步骤,为读者提供TensorFlow和Keras的基础实践指导。
142 59
|
11天前
|
机器学习/深度学习 算法 TensorFlow
深度学习基础:神经网络原理与构建
**摘要:** 本文介绍了深度学习中的神经网络基础,包括神经元模型、前向传播和反向传播。通过TensorFlow的Keras API,展示了如何构建并训练一个简单的神经网络,以对鸢尾花数据集进行分类。从数据预处理到模型构建、训练和评估,文章详细阐述了深度学习的基本流程,为读者提供了一个深度学习入门的起点。虽然深度学习领域广阔,涉及更多复杂技术和网络结构,但本文为后续学习奠定了基础。
38 5
|
22小时前
|
机器学习/深度学习 PyTorch 算法框架/工具
RNN、LSTM、GRU神经网络构建人名分类器(三)
这个文本描述了一个使用RNN(循环神经网络)、LSTM(长短期记忆网络)和GRU(门控循环单元)构建的人名分类器的案例。案例的主要目的是通过输入一个人名来预测它最可能属于哪个国家。这个任务在国际化的公司中很重要,因为可以自动为用户注册时提供相应的国家或地区选项。
|
22小时前
|
机器学习/深度学习
RNN、LSTM、GRU神经网络构建人名分类器(二)
这个文本描述了一个使用RNN(循环神经网络)、LSTM(长短期记忆网络)和GRU(门控循环单元)构建的人名分类器的案例。案例的主要目的是通过输入一个人名来预测它最可能属于哪个国家。这个任务在国际化的公司中很重要,因为可以自动为用户注册时提供相应的国家或地区选项。
|
11天前
|
机器学习/深度学习 人工智能 安全
网络安全基础:防御是最佳进攻——构建坚实的网络安全防线
【6月更文挑战第12天】网络安全至关重要,防御是最佳进攻。本文探讨基础概念、关键策略及未来趋势。防火墙、入侵检测、加密技术、身份认证、访问控制、漏洞管理是防御关键。未来,人工智能、机器学习将增强威胁防御,零信任架构普及,隐私保护和数据安全成为焦点。构建坚实防线,持续学习改进,共同应对网络安全挑战。
|
11天前
|
存储 SQL 安全
构建数字世界的堡垒:网络安全与信息安全探究
在当今数字化时代,网络安全与信息安全已成为不可忽视的重要议题。本文从网络安全漏洞、加密技术以及安全意识等方面展开探讨,旨在为读者提供对这一领域的深入了解和实用知识。
13 2
|
19天前
|
机器学习/深度学习 算法 TensorFlow
【图像识别】谷物识别系统Python+人工智能深度学习+TensorFlow+卷积算法网络模型+图像识别
谷物识别系统,本系统使用Python作为主要编程语言,通过TensorFlow搭建ResNet50卷积神经算法网络模型,通过对11种谷物图片数据集('大米', '小米', '燕麦', '玉米渣', '红豆', '绿豆', '花生仁', '荞麦', '黄豆', '黑米', '黑豆')进行训练,得到一个进度较高的H5格式的模型文件。然后使用Django框架搭建了一个Web网页端可视化操作界面。实现用户上传一张图片识别其名称。
55 0
【图像识别】谷物识别系统Python+人工智能深度学习+TensorFlow+卷积算法网络模型+图像识别
|
4天前
|
存储 安全 网络安全
云计算与网络安全:构建安全可靠的数字化未来
随着信息技术的快速发展,云计算和网络安全成为保障数字化未来的重要技术领域。本文将探讨云服务、网络安全和信息安全等关键技术,并重点介绍如何构建安全可靠的数字化未来。
10 0
|
8天前
|
存储 安全 网络安全
云计算与网络安全的交汇点:构建安全可靠的云服务
在数字化转型的浪潮中,云计算技术以其强大的计算能力和灵活的资源调度优势,迅速成为各行业信息化建设的核心。然而,随着云计算应用的普及,网络安全问题也日益凸显。本文探讨云计算与网络安全的交汇点,分析当前云服务的安全挑战,并提出相应的安全策略,以助力企业构建安全、可靠的云计算环境。
16 0
|
15天前
|
安全 数据安全/隐私保护 Docker
Docker 容器连接:构建安全高效的容器化网络生态
Docker 容器连接:构建安全高效的容器化网络生态

热门文章

最新文章