欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

RNN循环神经网络实例

程序员文章站 2024-03-14 12:20:58
...

最近又忍不住把RNN这一块儿的东西给过了一遍,感觉还是有一些收获!所以想着给记录下来,因为也看到有人给我提意见说:我写的关于算法的文章太多了,还是要好好搞学术研究,所以就想着最近多更几篇关于深度学习网络方面的文章。

关于RNN循环神经网络的具体概念我就不细讲了,稍微把概念给提下吧,然后会说说其变形模型,以及会给出两个demo讲述其在不同领域的应用,方便大家理解!

1. RNN循环神经网络介绍

RNN循环神经网络实例

上面这张图算是最最最经典的也是最一般的RNN网络结构了。

RNN循环神经网络实例

 因为要讲解步骤,所以用这张图来表示步骤,上图其实是把RNN给展开成我们熟悉的全连接神经网络了,这样我们会比较熟悉。步骤如下:

RNN循环神经网络实例

基于上述步骤可实现一简单的RNN神经网络,核心流程如下demo所示:

import numpy as np
import os
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"

X = [1, 2]
state = [0.0, 0.0]
# 定义不同输入部分的权重
w_cell_state = np.asarray([[0.1, 0.2], [0.3, 0.4]])
w_cell_input = np.asarray([0.5, 0.6])
b_cell = np.asarray([0.1, -0.1])
# 定义输出层的权重
w_output = np.asarray([[0.1], [0.2]])
b_output = 0.1
# 按照时间顺序执行循环神经网络的前向传播过程
for i in range(len(X)):
    before_activation = np.dot(state, w_cell_state) + X[i]*w_cell_input+b_cell
    state = np.tanh(before_activation)
    # 计算当前时刻的最终输出
    final_output = np.dot(state, w_output) + b_output
    # 输出每一时刻的信息
    print("before_activation", before_activation)
    print("state", state)
    print("final_output", final_output)

只说这个最简单的网络,肯定是没有意思的!所以接下来我会分享一个用RNN来识别手写字体的demo。

2. RNN神经网络识别手写字体

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
from tensorflow.contrib import rnn
import os
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"

# 载入数据
mnist = input_data.read_data_sets("MNIST_data", one_hot=True)

# 设置模型的参数
# 对应输入图片的宽
n_inputs = 28
# 对应输入图片的行
max_time = 28
# 隐藏单元,实质上不是神经元,即LSTM单元
lstm_size = 100
# 图片总共有10个分类
n_classes = 10
# 每批次总共有50个样本
batch_size = 50
n_batch = mnist.train.num_examples//batch_size

# 这里的None=50
x = tf.placeholder(tf.float32, [None, 784])
y = tf.placeholder(tf.float32, [None, 10])

# 初始化权值
weights = tf.Variable(tf.truncated_normal([lstm_size, n_classes], stddev=0.1))
# 初始化偏置值
biases = tf.Variable(tf.constant(0.1, shape=[n_classes]))


# 定义RNN网络
def RNN(X, weights, biases):
    inputs = tf.reshape(X, [-1, max_time, n_inputs])
    lstm_cell = rnn.BasicLSTMCell(lstm_size)
    # outputs维度为shape=(?, 28, 100);final_state维度为shape=(?, 100)
    outputs, final_state = tf.nn.dynamic_rnn(lstm_cell, inputs, dtype=tf.float32)
    print("shape(outputs)", outputs)
    print("shape(final_state)", final_state)
    # 如果使用Seq2Seq模型,则调用outputs;反之则使用final_state
    results = tf.nn.softmax(tf.matmul(final_state[1], weights)+biases)
    return results


# 计算RNN的返回结果
prediction = RNN(x, weights, biases)
# 损失函数--交叉熵
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=y, logits=prediction))
# 使用AdamOptimizer进行优化
train_op = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
# 结果放在一个布尔型列表中
crroect_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(prediction, 1))
# 求准确率
accuracy = tf.reduce_mean(tf.cast(crroect_prediction, tf.float32))

# 开始训练模型
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for epoch in range(10):
        for batch in range(n_batch):
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)
            sess.run(train_op, feed_dict={x: batch_xs, y:batch_ys})
        acc = sess.run(accuracy, feed_dict={x: mnist.test.images, y: mnist.test.labels})
        print("Iter "+str(epoch)+", Testing Accuracy="+str(acc))

这个demo里有处地方我需要强调一下:

outputs, final_state = tf.nn.dynamic_rnn(lstm_cell, inputs, dtype=tf.float32)
# 如果使用Seq2Seq模型,则调用outputs;反之则使用final_state
results = tf.nn.softmax(tf.matmul(final_state[1], weights)+biases)

这里面outputs维度为shape=(?, 28, 100);final_state维度为shape=(?, 100),具体结构如下图所示:

RNN循环神经网络实例

在这个demo里我们是没用到output这个LSTM cell的,不是说它没用哈,而是在后面提到的Seq2Seq模型中会用到这个值。

3. RNN神经网络预测下一个字符

不知道大家发现没有,上面讲到的这个最基础的RNN模型中输入数据的“维度”与输出数据的“维度”是一一对应的,即Xt与Ot是相对应的。但是在实际应用中这种情形太少了,跟多的是N->M的关系,即输入数据的维度”与输出数据的“维度”是不同的。比如在机器翻译应用中,将一段汉语翻译成英文。汉语是:我英语不好,翻译成英文是:my English is poor 此处N=5,M=4明显是N与M不相等的情形,此时我们要怎么解决这个模型呢?

RNN循环神经网络实例

那问题就来了,此时我们要怎么解决这个模型呢?

为此得引入一种新的模型,这种结构又叫Encoder-Decoder模型,也可以称之为Seq2Seq模型。如下图所示:

RNN循环神经网络实例

 

还有一种做法是将c当做每一步的输入:

 

RNN循环神经网络实例

按照这个模型,我接下来将会给出能预测下一个字符的RNN模型

import tensorflow as tf
from tensorflow import contrib
import numpy as np
import os
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"

sample = "这是一个基于tensorflow的RNN短句子练习"
# 去重放入列表中
idx2char = list(set(sample))
print("idx2char", idx2char)
# 转换为字典,其中把字母作键,索引作为值
char2idx = {c: i for i, c in enumerate(idx2char)}
# 在字典里取出对应值,因为在idx2char中原sample句子的顺序已经被打乱
sample_idx = [char2idx[c] for c in sample]
x_data = [sample_idx[:-1]]
y_data = [sample_idx[1:]]

# 设置该模型的一些参数
dic_size = len(char2idx)
rnn_hidden_size = len(char2idx)
num_classes = len(char2idx)
batch_size = 1
sequence_length = len(sample) - 1

X = tf.placeholder(tf.int32, [None, sequence_length])
Y = tf.placeholder(tf.int32, [None, sequence_length])
# 将input转化为one-hot类型数据输出,此时X的维度变为[None, sequence_length, num_classes]
X_one_hot = tf.one_hot(X, num_classes)

cell = tf.contrib.rnn.BasicLSTMCell(num_units=rnn_hidden_size, state_is_tuple=True)
initial_state = cell.zero_state(batch_size, tf.float32)
outputs, states = tf.nn.dynamic_rnn(cell, X_one_hot, initial_state=initial_state, dtype=tf.float32)
# 加一层全连接层,相当于加一层深度,使预测更准确
outputs = contrib.layers.fully_connected(inputs=outputs, num_outputs=num_classes, activation_fn=None)
print("outputs", tf.shape(outputs))
weights = tf.ones([batch_size, sequence_length])
# 此处包装了encoder和decoder
sequence_loss = tf.contrib.seq2seq.sequence_loss(logits=outputs, targets=Y, weights=weights)
loss = tf.reduce_mean(sequence_loss)
train = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(loss)

prediction = tf.argmax(outputs, axis=2)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for i in range(3000):
        l, _ = sess.run([loss, train], feed_dict={X: x_data, Y: y_data})
        result = sess.run(prediction, feed_dict={X: x_data})
        # print char using dic
        result_str = [idx2char[c] for c in np.squeeze(result)]
        print(i, "loss:", l, "Prediction:", "".join(result_str))
    print(len(result_str))

模型Run起来的结果如下:

RNN循环神经网络实例

 

RNN循环神经网络实例

可以看到:效果还是不错的!暂时就分享到这儿了,有想到更好的东西再来补充!