循环神经网络基础
- RNN的结构。循环神经网络的提出背景、优缺点。着重学习RNN的反向传播、RNN出现的问题(梯度问题、长期依赖问题)、BPTT算法。
- 双向RNN
- LSTM、GRU的结构、提出背景、优缺点。
- 针对梯度消失(LSTM等其他门控RNN)、梯度爆炸(梯度截断)的解决方案。
- Text-RNN的原理。
- 利用Text-RNN模型来进行文本分类。
一、RNN的结构
将神经网络模型训练好之后,在输入层给定一个x,通过网络之后就能够在输出层得到特定的y,那么既然有了这么强大的模型,为什么还需要RNN(循环神经网络)呢?
为什么需要RNN(循环神经网络):
他们都只能单独的取处理一个个的输入,前一个输入和后一个输入是完全没有关系的。但是,某些任务需要能够更好的处理序列的信息,即前面的输入和后面的输入是有关系的。
比如,当我们在理解一句话意思时,孤立的理解这句话的每个词是不够的,我们需要处理这些词连接起来的整个序列; 当我们处理视频的时候,我们也不能只单独的去分析每一帧,而要分析这些帧连接起来的整个序列。
详细解释:https://zhuanlan.zhihu.com/p/27485750
二、循环神经网络的提出背景、优缺点
- 背景:
RNNs的目的使用来处理序列数据。在传统的神经网络模型中,是从输入层到隐含层再到输出层,层与层之间是全连接的,每层之间的节点是无连接的。但是这种普通的神经网络对于很多问题却无能无力。 - 优缺点:
RNN的优点之一是他们能够将先前的信息连接到当前的任务,例如使用先前的视频帧可以推导对当前帧的理解。
RNN利用内部的记忆来处理任意时序的输入序列,并且在其处理单元之间既有内部的反馈连接又有前馈连接,这使得RNN可以更加容易处理不分段的文本等。但是由于RNN只能对部分序列进行记忆,所以在长序列上表现远不如短序列,造成了一旦序列过长便使得准确率下降的结果。
三、RNN的反向传播
详细推导过程:https://blog.csdn.net/wjc1182511338/article/details/79191099
代码:
def bptt(self, x, y):
T = len(y)
# Perform forward propagation
o, s = self.forward_propagation(x)
# We accumulate the gradients in these variables
dLdU = np.zeros(self.U.shape)
dLdV = np.zeros(self.V.shape)
dLdW = np.zeros(self.W.shape)
delta_o = o
delta_o[np.arange(len(y)), y] -= 1.
# For each output backwards...
for t in np.arange(T)[::-1]: # t:(T-1)->0
dLdV += np.outer(delta_o[t], s[t].T)
# Initial delta calculation: dL/dz
delta_t = self.V.T.dot(delta_o[t]) * (1 - (s[t] ** 2))
# Backpropagation through time (for at most self.bptt_truncate steps)
for bptt_step in np.arange(max(0, t-self.bptt_truncate), t+1)[::-1]: # bptt_step:t->...
# print "Backpropagation step t=%d bptt step=%d " % (t, bptt_step)
# Add to gradients at each previous step
dLdW += np.outer(delta_t, s[bptt_step-1])
dLdU[:,x[bptt_step]] += delta_t
# Update delta for next step dL/dz at t-1
delta_t = self.W.T.dot(delta_t) * (1 - s[bptt_step-1] ** 2)
return [dLdU, dLdV, dLdW]
四、RNN出现的问题(梯度问题、长期依赖问题)
-
梯度消失问题:https://blog.csdn.net/weixin_39773661/article/details/80973956
解决办法:
ReLU函数在定义域大于0部分的导数恒等于1,这样可以解决梯度消失的问题,(虽然恒等于1很容易发生梯度爆炸的情况,但可通过设置适当的阈值可解决)。另外计算方便,计算速度快,可以加速网络训练。但是,定义域负数部分恒等于零,这样会造成神经元无法激活(可通过合理设置学习率,降低发生的概率)。
ReLU有优点也有缺点,其中的缺点可以通过其他操作取避免或者减低发生的概率,是目前使用最多的激活函数。
还可以通过更改内部结构来解决梯度消失和梯度爆炸问题,那就是LSTM。
-
长期依赖问题:https://blog.csdn.net/YZXnuaa/article/details/80009769
解决办法:
渗透单元及其它多时间尺度的策略;
长短期记忆和其它门控RNN;
优化长期依赖(截断梯度、引导信息流的正则化);
五、BPTT算法
循环神网络的训练算法是Backpropagation Through Time,BPTT算法,其基本原理和反向传播算法是一样的,只不过反向传播算法是按照层进行反向传播,BPTT是按照时间t进行反向传播。
具体推导过程:https://zhuanlan.zhihu.com/p/26892413
六、双向RNN
在经典的循环神经网络中,状态的传输是从前往后单向的。然而,在有些问题中,当前时刻的输出不仅和之前的状态有关系,也和之后的状态相关。这时就需要双向RNN(BiRNN)来解决这类问题。例如预测一个语句中缺失的单词不仅需要根据前文来判断,也需要根据后面的内容,这时双向RNN就可以发挥它的作用。
双向RNN是由两个RNN上下叠加在一起组成的。输出由这两个RNN的状态共同决定。
从上图可以看出,双向RNN的主题结构就是两个单向RNN的结合。在每一个时刻t,输入会同时提供给这两个方向相反的RNN,而输出则是由这两个单向RNN共同决定(可以拼接或者求和等)。
七、LSTM、GRU的结构、提出背景、优缺点
参考:https://zhuanlan.zhihu.com/p/46836364
八、Text-RNN的原理
模型结构:embedding—>bi-drectional lstm —> concat output –>average—–> softmax layer
通过利用双向LSTM建模,然后输出最后一个词的结果直接接全连接层softmax输出了。
九、利用Text-RNN模型来进行文本分类
import inspect
import tensorflow as tf
class RNN_Model(object):
def __init__(self, config, num_classes, is_training=True):
keep_prob = config.keep_prob
batch_size = config.batch_size
num_step = config.num_step
embed_dim = config.embed_dim
self.embedded_x = tf.placeholder(tf.float32, [None, num_step, embed_dim], name="embedded_chars")
self.target = tf.placeholder(tf.int64, [None, num_classes], name='target')
self.mask_x = tf.placeholder(tf.float32, [num_step, None], name="mask_x")
hidden_neural_size=config.hidden_neural_size
hidden_layer_num=config.hidden_layer_num
# build LSTM network
def lstm_cell():
if 'reuse' in inspect.signature(tf.contrib.rnn.BasicLSTMCell.__init__).parameters:
return tf.contrib.rnn.BasicLSTMCell(hidden_neural_size, forget_bias=0.0,
state_is_tuple=True,
reuse=tf.get_variable_scope().reuse)
else:
return tf.contrib.rnn.BasicLSTMCell(
hidden_neural_size, forget_bias=0.0, state_is_tuple=True)
attn_cell = lstm_cell
if is_training and keep_prob < 1:
def attn_cell():
return tf.contrib.rnn.DropoutWrapper(
lstm_cell(), output_keep_prob=config.keep_prob)
cell = tf.contrib.rnn.MultiRNNCell(
[attn_cell() for _ in range(hidden_layer_num)], state_is_tuple=True)
self._initial_state = cell.zero_state(batch_size, dtype=tf.float32)
inputs = self.embedded_x
if keep_prob < 1:
inputs = tf.nn.dropout(inputs, keep_prob)
out_put = []
state = self._initial_state
with tf.variable_scope("LSTM_layer"):
for time_step in range(num_step):
if time_step > 0: tf.get_variable_scope().reuse_variables()
(cell_output, state) = cell(inputs[:, time_step,:],state)
out_put.append(cell_output)
out_put=out_put*self.mask_x[:,:,None]
with tf.name_scope("mean_pooling_layer"):
out_put = tf.reduce_sum(out_put,0)/(tf.reduce_sum(self.mask_x,0)[:,None])
with tf.name_scope("Softmax_layer_and_output"):
softmax_w = tf.get_variable("softmax_w",[hidden_neural_size,num_classes],dtype=tf.float32)
softmax_b = tf.get_variable("softmax_b",[num_classes],dtype=tf.float32)
# self.logits = tf.matmul(out_put,softmax_w)
# self.scores = tf.add(self.logits, softmax_b, name='scores')
self.scores = tf.nn.xw_plus_b(out_put, softmax_w, softmax_b, name="scores")
with tf.name_scope("loss"):
self.loss = tf.nn.softmax_cross_entropy_with_logits(labels=self.target, logits=self.scores + 1e-10)
self.cost = tf.reduce_mean(self.loss)
with tf.name_scope("accuracy"):
self.prediction = tf.argmax(self.scores, 1, name="prediction")
correct_prediction = tf.equal(self.prediction, tf.argmax(self.target, 1))
self.correct_num = tf.reduce_sum(tf.cast(correct_prediction, tf.float32))
self.accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32), name="accuracy")
self.probability = tf.nn.softmax(self.scores, name="probability")
# add summary
loss_summary = tf.summary.scalar("loss", self.cost)
# add summary
accuracy_summary = tf.summary.scalar("accuracy_summary", self.accuracy)
if not is_training:
return
self.global_step = tf.Variable(0, name="global_step", trainable=False)
self.lr = tf.Variable(0.0, trainable=False)
tvars = tf.trainable_variables()
grads, _ = tf.clip_by_global_norm(tf.gradients(self.cost, tvars), config.max_grad_norm)
# Keep track of gradient values and sparsity (optional)
grad_summaries = []
for g, v in zip(grads, tvars):
if g is not None:
grad_hist_summary = tf.summary.histogram("{}/grad/hist".format(v.name), g)
sparsity_summary = tf.summary.scalar("{}/grad/sparsity".format(v.name), tf.nn.zero_fraction(g))
grad_summaries.append(grad_hist_summary)
grad_summaries.append(sparsity_summary)
self.grad_summaries_merged = tf.summary.merge(grad_summaries)
self.summary = tf.summary.merge([loss_summary,accuracy_summary,self.grad_summaries_merged])
optimizer = tf.train.GradientDescentOptimizer(self.lr)
optimizer.apply_gradients(zip(grads, tvars))
self.train_op=optimizer.apply_gradients(zip(grads, tvars))
self.new_lr = tf.placeholder(tf.float32,shape=[],name="new_learning_rate")
self._lr_update = tf.assign(self.lr,self.new_lr)
def assign_new_lr(self,session,lr_value):
session.run(self._lr_update,feed_dict={self.new_lr:lr_value})
参考资料
- 一文搞懂RNN(循环神经网络)基础篇 https://zhuanlan.zhihu.com/p/30844905
- 长短期记忆网络(LSTMs)介绍1:传统RNN的缺点,LSTM结构解析,LSTM变体简介 https://zhuanlan.zhihu.com/p/33113729
- LSTM与RNN的优势 https://blog.csdn.net/qq_32113189/article/details/79462696
- 循环神经网络RNN详解 反向传播公式推导+代码(十分详细) https://blog.csdn.net/wjc1182511338/article/details/79191099
- 数学 · RNN(二)· BPTT 算法 https://zhuanlan.zhihu.com/p/26892413
- 循环神经网络RNN 梯度推导(BPTT)
https://ilewseu.github.io/2017/12/30/RNN简单推导/ - 深度学习笔记——RNN(LSTM、GRU、双向RNN)学习总结 https://blog.csdn.net/mpk_no1/article/details/72875185
- LSTM与GRU的一些比较–论文笔记 https://blog.csdn.net/meanme/article/details/48845793
- 2018.06.06论文:12个NLP分类模型 https://www.xuqingtang.top/2018/06/06/2018.06.06论文:12个NLP分类模型/
10.基于RNN的文本分类模型(Tensorflow) https://blog.csdn.net/tcx1992/article/details/78194384