循环神经网络知识总结

循环神经网络知识总结

一、概念部分

RNN中的Dropout

Dropout可以理解为bagging。

  • 在训练阶段,在每次迭代中,按一定比例随机丢弃神经元之间的连接来改变网络结构,以实现训练不同网络的目的。
  • 在测试时,使用全部的神经元,相当于全部进行投票。这边要注意比例的处理,训练÷p或者测试×。
  • 但与bagging不同点在于不是独立的,相当于是参数共享的。

在RNN中,有两种形式:

  • 对于同1个t,前馈阶段dropput
  • 对于同一个序列,不同step,循环阶段用相同的丢弃方法。
  • 第二种效果好,因为大量参数是在循环连接的阶段,是主要矛盾。

梯度消失问题

RNN中的梯度消失与前馈网络梯度消失类似,但有所区别。

  • 前馈网络梯度消失:是因为误差反向传播,每次都要乘上激活函数的导数。当激活函数是sigmoid或者tanh时,其饱和区的导数接近为0。因此,误差在每一层后逐渐衰减。当网络很深的时候甚至会消失(→0),网络就很难训练。这个现象也称为梯度弥散。
  • 解决办法:最为直接的改变激活函数,比如relu之类的。也可以用残差网络、batch_normalization。详解深度学习中的梯度消失、爆炸原因及其解决方法

  • RNN中的梯度消失:并不是说整体的梯度小时了,而是对于与t时刻间隔比较远的位置的梯度消失了。也就是说,参数的更新主要依赖于与当前时刻相邻的状态,长距离的状态对参数没啥影响。

  • 搞清楚反向传播中那部分导致了。
    = _−1 + +
    循环神经网络知识总结_第1张图片
    循环神经网络知识总结_第2张图片
    循环神经网络知识总结_第3张图片
    可见,一般选用的激活函数是sigmoid,所以里面导数的范围是0-0.25。因此,取决于 U T U^T UT的里面元素的大小。只要距离变长,就造成系统的不稳定。这就是长时间间隔的依赖关系难以建模的原因。
    所以,真正的问题是出在在这里插入图片描述
    其实说白了就是 h k h_k hk h k − 1 h_{k-1} hk1的导数需要一直算下去。比如你计算 δ t , 1 \delta_{t,1} δt,1,理解为t时刻对于1时刻的误差项,那就要从 h 1 , h 2 , . . . . h t h_1,h_2,....h_t h1,h2,....ht算下来,所以连乘的代价就是消失或者爆炸。

  • 解决办法:LSTM、GRU。但注意是缓解,并不是真正解决。LSTM中的缓解主要体现在cell单元的更新上。
    c t c_t ct对于 c t − 1 c_{t-1} ct1的导数写起来很复杂,但第一部分就是遗忘门 f t f_t ft。其可以决定有多少信息要被遗忘(有多少信息保留)。这个能力是网络自己学习的。所以最终连乘的这部分梯度,在LSTM中不会始终大于1或者小于1,一定程度缓解。但也要注意, f t f_t ft的值也和参数初始化有关,一般而言,会把偏置项设为1、2这样。

一般在深度网络参数学习时,参数初始化的值一般都比较小.但是在训练LSTM 网络时,过小的值会使得遗忘门的值比较小.这意味着前一时刻的信息大部分都丢失了,这样网络很难捕捉到长距离的依赖信息.并且相邻时间间隔的梯度会非常小,这会导致梯度弥散问题.因此遗忘的参数初始值一般都设得比较大,其偏置向量 设为1 或2.

  • 《Why LSTMs Stop Your Gradients From Vanishing: A View from the Backwards Pass》翻译:
  • How LSTM sovle this
  • 面试回答:
  • 漫谈LSTM系列的梯度问题

二、模型部分

LSTM

理论部分

理解梯度消失,就很容易记住LSTM的结构了。

用top-down的写法。

  • 隐藏单元: h t = o t t a n h ( c t ) h_t = o_ttanh(c_t) ht=ottanh(ct)

  • 记忆单元 c t = f t c t − 1 + i t c ~ t c_t = f_tc_{t-1}+i_t\tilde c_t ct=ftct1+itc~t

  • 候选状态: c ~ t = t a n h ( W c x t + U c h t − 1 + b c ) \tilde c_t = tanh(W_cx_t+U_ch_{t-1}+b_c) c~t=tanh(Wcxt+Ucht1+bc)

  • 遗忘门: f t = σ ( W f x t + U f h t − 1 + b f ) f_t = \sigma(W_fx_t+U_fh_{t-1}+b_f) ft=σ(Wfxt+Ufht1+bf)决定上一时刻记忆单元要遗忘多少

  • 输入门: i t = σ ( W i x t + U i h t − 1 + b i ) i_t = \sigma(W_ix_t+U_ih_{t-1}+b_i) it=σ(Wixt+Uiht1+bi)决定候选状态要保留多少

  • 输出门: o t = σ ( W o x t + U o h t − 1 + b o ) o_t = \sigma(W_ox_t+U_oh_{t-1}+b_o) ot=σ(Woxt+Uoht1+bo)决定当前记忆单元要输出多少

  • 说明

    • 门控机制的作用是解决记忆容量问题。如果没有信息传入传出的限制,单纯不断累加,就会发生饱和。但其实,隐状态容量是有限的。
    • 记忆单元的形式是为了缓解梯度消失问题。

另一个细节,两类激活函数:

  • sigmoid:值域是[0,1]就是控制信息的传输。门的激活函数如果用relu的话会有个问题,就是relu是没有饱和区域的,那么就没法起到门的作用。
  • tanh:候选记忆用tanh是因为tanh的输出在-1~1,是0中心的,并且在0附近的梯度大,模型收敛快

LSTM中遗忘门输出门的激活函数比较重要。删除任何一个激活函数都会对性能有较大的影响。说明这两个激活函数对于信息的取舍作用很重要。

代码部分

学会区分 RNN 的 output 和 state

来看看tensorflow中的LSTM【当然我用的是keras里面的】

tf.keras.layers.LSTM(units,return_sequences=True,return_state=True)

参数有很多,不一一列举,看文档就很清晰,最关键是把输入、输出、维度搞清楚。
units:Positive integer, dimensionality of the output space.
【其实就是隐藏层呀,输出的维度】比如embeeding的维度是128,units是64,最后就是64。

return_sequences:返回的是h1,…ht还是最后一个。
看下源代码:

    if self.return_sequences:
      output = outputs
    else:
      output = last_output

return_state:Whether to return the last state in addition to the output.
这里的state包含了h与c。
看下源代码:

states = [new_h, new_c]

if self.return_state:
	return [output] + list(states)
elif self.return_runtime:
	return output, runtime
else:
	return output

看个例子:
循环神经网络知识总结_第4张图片
注意下,如果return_sequences=False,output与hidden_state还是不一样的。

双向LSTM

循环神经网络知识总结_第5张图片
注意,前向后向的输入都是Input Layer,然后进行拼接。
双向的输出应该是[forward,backward],所以如果输出维度每个是128,最后应该是256。

看下如何实现:

bi_output = Bidirectional(LSTM(128,return_sequences=True),backward_layer = LSTM(128,return_sequences=True,go_backwards= True) )(x)

注意,backward_layer的那一层要有参数go_backwards 。就是方向一定要相反,不然会报错。【我试了下不同层,比如LSTM GRU】好像不太行,不过可以手工拼接(如果想尝试的话)。

其实backward_layer可以忽略,这个层是会默认算的。

bi_output = Bidirectional(LSTM(128,return_sequences=True))(x)

这样即可。
最后输出shape=(None, 64, 256)。

GRU

  • 与LSTM相比具有更少的参数,更易于计算和实现。
  • 用两个门控单元重置门更新门实现一个控制短期记忆,一个控制长期记忆。

重置门: r t r_t rt = σ ( W r x t + U r h t − 1 ) \sigma(W_rx_t + U_rh_{t-1}) σ(Wrxt+Urht1)
更新门: z t z_t zt = σ ( W z x t + U z h t − 1 ) \sigma(W_zx_t + U_zh_{t-1}) σ(Wzxt+Uzht1)
状态的更新公式:
h ^ t = t a n h ( W h x t + U h ( r t ⋅ h t − 1 ) ) \hat h_t=tanh(W_hx_t+U_h(r_t\cdot h_{t-1})) h^t=tanh(Whxt+Uh(rtht1))
h t = ( 1 − z t ) h t − 1 + z t h ^ t h_t = (1-z_t)h_{t-1}+z_t\hat h_t ht=(1zt)ht1+zth^t

因此用一个更新门 z t z_t zt实现了遗忘更新的功能。

你可能感兴趣的:(机器学习\深度学习理论知识,深度学习,python,神经网络)