RNN基本思想及简单代码实现

RNN简单介绍

计算机视觉中,识别图像时每张图片是孤立的,前一张图片识别的结果并不会对后一张图片识别的结果有影响。但现实生活中,许多数据带有明显的顺序,如NLP领域中,顺序是语言的基本特征,如“我吃苹果”与“苹果吃我”就是两个完全不同的意义,也可以从语言结构中得到信息,比如主语“我”后面接一个动词“吃”,“吃”后面往往接一个名词,这种隐藏在语言当中的序列关系如何提取与表示呢,人们找到了RNN(Recurrent Neural Network),一个高度重视序列信息的网络。

RNN基本思想

RNN的基础结构仍然是神经网络,只不过多了个“小盒子”,用来记录数据输入时的状态,如输入的数据为多个单词时,在每一个单词输入到输出的过程中会考虑前一个单词输入的状态,当前状态加上前一个的状态再训练输出。随着数据的一次次输入,“小盒子”中保存的信息会不断更新,盒子中的信息称为隐状态。
大致流程如下图:
RNN基本思想

左边部分表示单个词输入到输出的过程,其中黑色方框就是“小盒子”,表示上一次输入数据的状态,在数据x输入时会加入。RNN输入到隐藏的连接由权重矩阵 U 参数化,隐藏到隐藏的循环连接由权重矩阵 W 参数化以及隐藏到输出的连接由权重矩阵 V 参数化。
我们可以将左图中一次输入输出的过程用一下数学形式表示:
a ( t ) = b + W h ( t − 1 ) + U x ( t ) (1) a^{(t)}=b+Wh^{(t-1)}+Ux^{(t)}\tag{1} a(t)=b+Wh(t1)+Ux(t)(1)
h ( t ) = t a n h ( a ( t ) ) (2) h^{(t)}=tanh(a^{(t)})\tag{2} h(t)=tanh(a(t))(2)
o ( t ) = c + V h ( t ) (3) o^{(t)}=c+Vh^{(t)}\tag{3} o(t)=c+Vh(t)(3)
y ^ ( t ) = s o f t m a x ( o ( t ) ) (4) \hat{y}^{(t)}=softmax(o^{(t)})\tag{4} y^(t)=softmax(o(t))(4)
其中(1)表示原始数据进入隐藏层时加入隐状态,偏置项为b,权重从W、V给出;(2)表示将结合隐状态的数据激活,activation为tanh,并将激活后的数据作为新的隐状态与下一次输入的数据结合;(3)表示用权重矩阵V对激活后 a ( t ) a^{(t)} a(t)做线性变换,加入偏置项c;(4)为最后的输出,对 o ( t ) o^{(t)} o(t)做softmax,在单词序列中,最后softmax的结果输出的是下一个某某单词出现的概率,这样我们可以根据训练数据确定loss,训练参数。

注意:RNN的参数一旦训练出来就不会变,每次数据的输入输出用的参数是一样的,如W、U、b、c。

举例

输入一词x1的向量维度为3,表示为 ( x 11   x 12   x 13 ) ′ (x_{11}  x_{12} x_{13})^{'} (x11 x12 x13),隐藏层有4个神经元,设为 ( h 11   h 12   h 13   h 14 ) ′ (h_{11}  h_{12} h_{13} h_{14})^{'} (h11 h12 h13 h14),输出类别数为2,设为 ( y 11   y 12 ) ′ (y_{11} y_{12})^{'} (y11 y12)。基础架构表示如下:
在这里插入图片描述
由于x1为第一个输入的数据,没有之前数据的隐状态,这里定义一个初始隐转态为 h 0 h_0 h0,为4维的列向量。权重矩阵U为4x3的矩阵,权重矩阵W为4x4的矩阵(因为隐状态是通过隐藏层后得到的,形状由隐含层的维数决定),偏置项b为4维的列向量。
t=1(第一次输入)时:
由以上定义,得到隐含层的输出 h 1 h_1 h1
h 1 = t a n h ( b + W h 0 + U x 1 ) h_1=tanh(b+Wh_0+Ux_1) h1=tanh(b+Wh0+Ux1)
由于输出的类别数为2,权重矩阵的形状为2x4,偏置项c为2维的列向量,得到第一次输入数据后的输出结果为:
y 1 = s o f t m a x ( c + V h 1 ) y_1=softmax(c+Vh_1) y1=softmax(c+Vh1)
softmax后输出的是概率值。
t=2(第二次输入)时,参数是一模一样的:
由以上定义,得到隐含层的输出 h 1 h_1 h1
h 2 = t a n h ( b + W h 1 + U x 2 ) h_2=tanh(b+Wh_1+Ux_2) h2=tanh(b+Wh1+Ux2)
第一次输入数据后的输出结果为:
y 2 = s o f t m a x ( c + V h 2 ) y_2=softmax(c+Vh_2) y2=softmax(c+Vh2)
类似的,可以算出t=3、4、5…
不断数据输入的情况可由下图表示:
含隐藏状态的循环神经⽹络
隐状态在不断更新,但网络参数始终不会改变。
损失函数可以有以下定义,B为总的词的长度:
L τ = 1 ∣ B ∣ l ( y t + τ , f ( x t + τ ) ) L_\tau=\frac{1}{|B|}l(y_{t+\tau},f(x_{t+\tau})) Lτ=B1l(yt+τ,f(xt+τ))
其中的 l l l可以为交叉熵或者其他的损失函数,随后可用梯度下降求解参数。

简单RNN的Numpy代码实现

import numpy as np

timesteps = 100  # 输入序列的时间步数,自然语言处理中可以看做词的个数
input_features = 32 # 输入特征空间的维度
out_features = 64 # 输出特征空间的维度(注意对应的是隐藏层的输出)

inputs = np.random.random((timesteps, input_features)) # 随机生成输入数据

state_t = np.zeros((output_features,)) # 初始状态,为全0向量

#创建随机的权重矩阵 
U = np.random.random((output_features, input_features)) # 为输入样本的权重矩阵
W = np.random.random((output_features, output_features)) # 为隐状态的权重矩阵
b = np.random.random((output_features, )) # 偏置项

successive_outputs = [] # 保存输出的list
for input_t in inputs:   # input_t的形状为(input_features, )的一维张量
	output_t = np.tanh(np.dot(U, input_t)+np.dot(W, state_t)+b) # 由输入和当前状态(前一个输出)计算得到当前输出
	successive_outputs.append(output_t) # 保存输出
	state_t = output_t # 更新隐状态,用于下一次输入

final_output_sequence = np.stack(successive_outputs, axis=0) # 最终输出(隐藏层的输出)的结果是一个形状为(timesteps, output_features)的二维张量

RNN缺陷与改进

RNN虽然是重视序列信息的网络,但只有一定的记忆能力,只能保留短期记忆,如果一段话中的一个词与后面很长的一个词有关联的话,此时RNN通常捕捉不到这个信息。所以在实际任务中RNN表现并不好。此时人们改造了RNN,引入LSTM(长短时记忆网络)以及和面的变种GRU(门控循环单元),后面会写一下。

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值