文章目录
一、RNN
RNN是循环神经网络的缩写,是一种用于处理序列数据的神经网络。RNN具有独特的结构,可以维护先前输入的内部记忆,并使用它们来预测序列中下一个输入。这使它们特别适用于语言模型、语音识别和机器翻译等任务。RNN通常用于自然语言处理(NLP)任务,其中单词在句子中的顺序很重要并且必须被考虑进去。
二、RNN原理和使用步骤
RNN存在的问题如下:
-
梯度消失:当RNN进行反向传播时,梯度会随着时间步骤的增加而指数级衰减,这会导致网络无法学习长期依赖关系。
-
梯度爆炸:有时候梯度会随着时间步骤的增加而指数级增加,这会导致数值不稳定,网络无法收敛。
-
训练速度慢:RNN训练速度相对较慢,因为每个时间步骤都需要进行反向传播。
-
长序列处理困难:对于较长的序列,RNN的计算成本会变得很高,而且长期依赖关系的学习也变得更加困难。
-
信息遗忘:由于RNN只能通过隐藏状态传递信息,因此网络可能会忘记过去的信息,这会导致模型性能下降。
引入LSTM
三、LSTM原理
LSTM(全称为Long Short-Term Memory)是一种循环神经网络(RNN)的变体,用于处理序列数据中的长期依赖性问题。LSTM通过引入一种称为“门”的机制,可以选择性地记忆或遗忘输入数据。下面是一张LSTM的原理图:
可以看成主线剧情和支线剧情推动故事发展,有用的支线被保留了部分,没用的就不要了,但是故事本身的主线是由每一级本身的输入产生,保留着前面有用的支线剧情的影响,最后形成了特定的故事
在LSTM中,有三个门:输入门(input gate)、遗忘门(forget gate)和输出门(output gate)。其作用如下:
- 输入门:决定有多少信息需要传递到记忆单元中。它包含一个sigmoid激活函数,它将输入数据和前一个时间步的隐藏状态作为输入,并产生介于0和1之间的输出。
- 遗忘门:决定有多少旧的信息需要保留在记忆单元中。它包含一个sigmoid激活函数,它将输入数据和前一个时间步的隐藏状态作为输入,并产生介于0和1之间的输出。
- 输出门:决定从记忆单元中选择多少信息来输出。它包括一个sigmoid激活函数和一个tanh激活函数。sigmoid激活函数决定保留多少输出,而tanh激活函数产生一个介于-1和1之间的向量。
除了三个门之外,LSTM还包含一个记忆单元(memory cell),用于存储序列中的信息。记忆单元受输入门、遗忘门和输出门的控制,可以选择性地存储或删除信息。这些门和记忆单元的组合使得LSTM能够捕捉序列中长期依赖性的信息。
四、LSTM的代码和相关使用
以下是LSTM结构在TensorFlow中实现MNIST数据集分类的代码:
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# 加载MNIST数据集
mnist = input_data.read_data_sets("MNIST_data", one_hot=True)
# 设置超参数
learning_rate = 0.001
training_steps = 10000
batch_size = 128
display_step = 100
# 设置网络参数
n_inputs = 28 # 输入层的维度
n_steps = 28 # LSTM展开的步长
n_hidden_units = 128 # 隐藏层神经元数量
n_classes = 10 # 输出层的维度
# 创建输入占位符
x = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
y = tf.placeholder(tf.float32, [None, n_classes])
# 定义LSTM单元
lstm_cell = tf.contrib.rnn.BasicLSTMCell(n_hidden_units)
# 初始化LSTM状态
init_state = lstm_cell.zero_state(batch_size, tf.float32)
# 将输入数据转换为LSTM可以处理的形式
inputs = tf.unstack(x, n_steps, 1)
# 定义网络结构
outputs, states = tf.contrib.rnn.static_rnn(lstm_cell, inputs, initial_state=init_state)
# 定义输出层
weights = tf.Variable(tf.random_normal([n_hidden_units, n_classes]))
biases = tf.Variable(tf.random_normal([n_classes]))
output = tf.matmul(outputs[-1], weights) + biases
# 定义损失函数和优化器
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=output, labels=y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss)
# 定义评估模型的指标
correct_pred = tf.equal(tf.argmax(output, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
# 初始化变量
init = tf.global_variables_initializer()
# 开始训练
with tf.Session() as sess:
sess.run(init)
for step in range(1, training_steps + 1):
# 获取batch数据
batch_x, batch_y = mnist.train.next_batch(batch_size)
batch_x = batch_x.reshape((batch_size, n_steps, n_inputs))
# 运行优化器
sess.run(optimizer, feed_dict={x: batch_x, y: batch_y})
if step % display_step == 0 or step == 1:
# 计算损失和精度
loss_val, acc_val = sess.run([loss, accuracy], feed_dict={x: batch_x, y: batch_y})
print("Step " + str(step) + ", Minibatch Loss= " + \
"{:.4f}".format(loss_val) + ", Training Accuracy= " + \
"{:.3f}".format(acc_val))
print("Optimization Finished!")
# 进行测试
test_data = mnist.test.images.reshape((-1, n_steps, n_inputs))
test_labels = mnist.test.labels
print("Testing Accuracy:", \
sess.run(accuracy, feed_dict={x: test_data, y: test_labels}))
这段代码定义了一个有一层LSTM和一个全连接输出层的神经网络,可以对28*28像素的手写数字图像进行分类,最终的精度可达到约97%。static_rnn和dynamic_rnn是TensorFlow中两种不同的RNN实现方式。
例子中使用的静态的rnn
static_rnn是一种静态的RNN实现方式,需要**事先定义好RNN的长度**
在静态图中将RNN展开成固定的层数,每层都有相同的权重和偏置参数
。这种方式适用于序列长度已知、且固定不变的情况。
dynamic_rnn是一种动态的RNN实现方式,可以处理不同长度的输入序列,
不需要事先定义好序列长度,而是根据输入数据在图中动态构建计算图。
这种方式相对于static_rnn更加灵活,适用于序列长度不固定的情况。
在实际使用中,如果序列长度已知且固定,可以使用static_rnn;如果序列长度不固定,需要动态构建计算图,则应该使用dynamic_rnn。
4.1一开始会需要产生initial_state(c_state,m_state)可以看成一个是主线另一个是支线,共同推进故事
tf.nn.rnn_cell.BasicLSTMCell(state_is_tuple=True)中的这个tuple元组参数由initial_state决定
outputs,states=tf.nn.dynamic_rnn(lstm_cell,X_in,initial_state=__init_state,time_major=False)此处的time_major是指主要依赖输入的这个x_in中起到分步走的要素是否是元组的第一个要素(元组中占位是否第一)
4.2lstm cell is divided into two part(c_state,m_state)
在隐藏层中
results=tf.matmul(state[1],weight['out'])+biases['out']
state[1]指的就是m_state
4.3关于结尾处的state[1]代码中的代替写法
4.3.1基础前提:tf.transpose中perm的对维度的影响
首先我们看下perm会对tensor的shape会进行什么调整,比如我们有一个shape=[2, 3, 4],那么经过各种perm配置后转换shape结果如下
tf.transpose(x, perm=[0, 1, 2]) # shape变为[2, 3, 4]
tf.transpose(x, perm=[0, 2, 1]) # shape变为[2, 4, 3]
tf.transpose(x, perm=[1, 0, 2]) # shape变为[3, 2, 4]
tf.transpose(x, perm=[1, 2, 0]) # shape变为[3, 4, 2]
tf.transpose(x, perm=[2, 1, 0]) # shape变为[4, 3, 2]
tf.transpose(x, perm=[2, 0, 1]) # shape变为[4, 2, 3]
官网上找不到unpack的用法了,不知道是不是看的视频太老
b站莫烦中outputs=tf.unpack(tf.transpose(outputs,[1,0,2]))#让states成为最后的ouputs
所以results=tf.matmul(output[-1],weight['out'])+biases['out']
引用自链接: 知乎中关于transpose的说法
4.4数据处理过程最重要的事情是保持数据的格式
比如:在含有batchsize和图片的行列数据形成了三维数据,必须先转为2维,再进行matmulW+bias
然后在重新分离开(课程中的案例)
"""
Know more, visit my Python tutorial page: https://morvanzhou.github.io/tutorials/
My Youtube Channel: https://www.youtube.com/user/MorvanZhou
Dependencies:
tensorflow: 1.1.0
matplotlib
numpy
"""
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import numpy as np
import matplotlib.pyplot as plt
tf.set_random_seed(1)
np.random.seed(1)
# Hyper Parameters
BATCH_SIZE = 64
TIME_STEP = 28 # rnn time step / image height
INPUT_SIZE = 28 # rnn input size / image width
LR = 0.01 # learning rate
# data
mnist = input_data.read_data_sets('./mnist', one_hot=True) # they has been normalized to range (0,1)
test_x = mnist.test.images[:2000]
test_y = mnist.test.labels[:2000]
# plot one example
print(mnist.train.images.shape) # (55000, 28 * 28)
print(mnist.train.labels.shape) # (55000, 10)
plt.imshow(mnist.train.images[0].reshape((28, 28)), cmap='gray')
plt.title('%i' % np.argmax(mnist.train.labels[0]))
plt.show()
# tensorflow placeholders
tf_x = tf.placeholder(tf.float32, [None, TIME_STEP * INPUT_SIZE]) # shape(batch, 784)
image = tf.reshape(tf_x, [-1, TIME_STEP, INPUT_SIZE]) # (batch, height, width, channel)
tf_y = tf.placeholder(tf.int32, [None, 10]) # input y
# RNN
rnn_cell = tf.nn.rnn_cell.LSTMCell(num_units=64)
outputs, (h_c, h_n) = tf.nn.dynamic_rnn(
rnn_cell, # cell you have chosen
image, # input
initial_state=None, # the initial hidden state
dtype=tf.float32, # must given if set initial_state = None
time_major=False, # False: (batch, time step, input); True: (time step, batch, input)
)
output = tf.layers.dense(outputs[:, -1, :], 10) # output based on the last output step
loss = tf.losses.softmax_cross_entropy(onehot_labels=tf_y, logits=output) # compute cost
train_op = tf.train.AdamOptimizer(LR).minimize(loss)
accuracy = tf.metrics.accuracy( # return (acc, update_op), and create 2 local variables
labels=tf.argmax(tf_y, axis=1), predictions=tf.argmax(output, axis=1),)[1]
sess = tf.Session()
init_op = tf.group(tf.global_variables_initializer(), tf.local_variables_initializer()) # the local var is for accuracy_op
sess.run(init_op) # initialize var in graph
for step in range(1200): # training
b_x, b_y = mnist.train.next_batch(BATCH_SIZE)
_, loss_ = sess.run([train_op, loss], {tf_x: b_x, tf_y: b_y})
if step % 50 == 0: # testing
accuracy_ = sess.run(accuracy, {tf_x: test_x, tf_y: test_y})
print('train loss: %.4f' % loss_, '| test accuracy: %.2f' % accuracy_)
# print 10 predictions from test data
test_output = sess.run(output, {tf_x: test_x[:10]})
pred_y = np.argmax(test_output, 1)
print(pred_y, 'prediction number')
print(np.argmax(test_y[:10], 1), 'real number')
新版本换用法
#RNN layer can take a list of cells, which will then stack them together.
#By default, keras RNN will only return the last timestep output and will not
return states. If you need whole time sequence output as well as the states,
you can set return_sequences
and return_state
to True.
rnn_layer = tf.keras.layers.RNN([tf.keras.layers.LSTMCell(128),
tf.keras.layers.LSTMCell(256)],
return_sequences=True,
return_state=True)
outputs, output_states = rnn_layer(inputs, states)