咱们好,我是半虹,这篇文章来讲循环神经网络 (Recurrent Neural Network, RNN)

文章行文思路如下:

  1. 首要经过前馈神经网络引出为啥需求循环神经网络
  2. 然后介绍循环神经网络的中心思维与运作方式
  3. 最终拓宽两个循环神经网络常见且常用的变体

在讲循环神经网络前,先来回顾下前馈神经网络,这是深度学习中最基础的网络结构之一

前馈神经网络也称为多层感知机,由多个躲藏层叠加得到,每个躲藏层可看作是一个线性模型加激活函数

为简略起见,这儿先以单层感知机为例进行介绍

NLP学习笔记(一) RNN基本介绍

上图是单层感知机的结构示意图,其间,XX 是输入,HH 是输出

对应的公式表达如下所示:

H=(XWxh+bh)H = \alpha(X W_{xh} + b_{h})

其间,WxhW_{xh}bhb_{h} 都是躲藏层的参数,表明可学习的权重矩阵,\alpha 是激活函数


研究表明,前馈神经网络具有很强的学习能力,只需有足够多练习数据,理论上能拟合任意函数

既然如此,那为什么还需求有循环神经网络呢

这是由于前馈神经网络只能单独处理每个输入,关于网络而言,前一个输入和后一个输入是没有任何关系的

这在处理时序数据时前馈神经网络就会显得力不从心,一个典型的场景便是文本处理


举个比如,假定现在咱们需求完成一个词性标示任务,给定的语句是:我在看画

如果用前馈神经网络完成上述任务时,就会呈现下面的状况:

NLP学习笔记(一) RNN基本介绍

这是由于前馈神经网络关于语句中的每个词都是独立处理的

关于画这个词,在不给定上下文的状况下,它既能够是动词,也能够是名词,这时候网络无法判别它的词性

而人是怎样判别词性的呢?当咱们发现看是动词时,自然就能揣度画是名词,由于动词后接的是名词

这就引出一个重要的结论,在处理文本时,网络需求依据之前的词语来了解当时的词语

NLP学习笔记(一) RNN基本介绍

这便是循环神经网络的中心,在处理序列当时的数据时,同时考虑序列之前的数据

循环神经网络是怎样做到这一点的呢?答案便是:使用一个隐状况保存之前的信息,具体网络结构请看下图:

要注意这儿的隐状况跟躲藏层不是一回事噢

NLP学习笔记(一) RNN基本介绍

上图左半部分是循环神经网络的结构示意图,右半部分是其按时刻打开的示意图

能够看到,循环神经网络当时躲藏层的输出不只取决于当时的输入,并且取决于从前躲藏层的输出

对应的公式表达如下所示:

Ht=(XtWxh+Ht−1Whh+bh)H_{t} = \alpha(X_{t} W_{xh} + H_{t-1} W_{hh} + b_{h})

其间,HtH_{t} 是当时躲藏层,XtX_{t} 是当时输入,Ht−1H_{t-1} 是从前躲藏层,WxhW_{xh}WhhW_{hh}bhb_h 都是可学习的参数


比照循环神经网络和前馈神经网络的结构表明和公式表达,能够很清楚地发现两者的差异

在结构表明上,循环神经网络添加了一个隐状况,能保存上一个躲藏层的信息

在公式表达上,循环神经网络计算当时躲藏层时,不只取决于当时输入,还会考虑从前的躲藏层

能了解这两点,就能了解循环神经网络的中心思维


为了帮助咱们进一步了解循环神经网络的工作方式,下面咱们举一个比如来说,并给出关键代码

假定咱们用循环神经网络对下面这个语句进行编码:我在画画

import torch
import torch.nn as nn
# 定义输入数据
# 关于输入语句我在画画,首要用独热编码得到其向量表明
x1 = torch.tensor([1, 0, 0]).float() # 我
x2 = torch.tensor([0, 1, 0]).float() # 在
x3 = torch.tensor([0, 0, 1]).float() # 画
x4 = torch.tensor([0, 0, 1]).float() # 画
h0 = torch.zeros(5) # 初始化隐状况
# 定义模型参数
# 模型的输入是三维向量,这儿定义模型的输出是五维向量
W_xh = nn.Parameter(torch.randn(3, 5), requires_grad = True)
W_hh = nn.Parameter(torch.randn(5, 5), requires_grad = True)
b_h  = nn.Parameter(torch.randn(5)   , requires_grad = True)
# 前向传达
h1 = torch.tanh(torch.matmul(x1, W_xh) + torch.matmul(h0, W_hh) + b_h)
h2 = torch.tanh(torch.matmul(x2, W_xh) + torch.matmul(h1, W_hh) + b_h)
h3 = torch.tanh(torch.matmul(x3, W_xh) + torch.matmul(h2, W_hh) + b_h)
h4 = torch.tanh(torch.matmul(x4, W_xh) + torch.matmul(h3, W_hh) + b_h)
# 结果验证
# 能够发现,即使是关于同一个词语,得到的表明也是不相同的
# 这是由于在计算当时词语时,会考虑从前的词语
print(h3) # tensor([0.9787, 0.9974, 0.9995, 0.9999, 0.9970])
print(h4) # tensor([0.6867, 0.6352, 0.9994, 0.2269, 0.9801])

至此,咱们现已介绍了循环神经网络的基本原理

下面再补充循环神经网络的两个变体,深度循环神经网络以及双向循环神经网络


不知道咱们是否还记得,文章开头咱们是经过单层感知机来引出循环神经网络的

比照单层感知机拓宽成多层感知机时,一般的循环神经网络同理能够拓宽成深度循环神经网络

其按时刻打开的示意图如下所示:

NLP学习笔记(一) RNN基本介绍

能够看到,上图有 LL 个躲藏层、TT 个时刻步,其间 XX 是输入,HH 是躲藏层的输出

从横向看,上一步躲藏层输出作为下一步躲藏层输入,这和一般的循环神经网络是相同的

从纵向看,上一层躲藏层输出作为下一层躲藏层输入,这是一般的循环神经网络上进行的堆叠

不难发现,当 L=1L = 1 时,便是一般的循环神经网络

在实践使用中,通常会取最终一层躲藏层的输出 Ht(L)H^{(L)}_{t} 作为每个时刻步输入 XtX_{t} 的表明


而另一个循环神经网络的变种是双向循环神经网络

上面咱们所讲的循环神经网络都是从左往右递推的,这让咱们能够依据前文的语义了解当时的词语

但有的时候,后文的语义关于当时词语的了解也同样重要

因此咱们无妨在从左往右递推的基础上,补一个从右往左的递推,这便是双向循环神经网络

其按时刻打开的示意图如下所示:

NLP学习笔记(一) RNN基本介绍

如图所示,其间 XX 是输入,H→\overrightarrow{H} 是从左往右的躲藏层输出,H←\overleftarrow{H} 是从右往左的躲藏层输出

在实践使用中,通常会取两个躲藏层输出的拼接 [H→t;H←t][\overrightarrow{H}_{t}\ ;\ \overleftarrow{H}_{t}] 作为每个时刻步输入 XtX_{t} 的表明


至此本文完毕,要点总结如下:

  1. 循环神经网络能够有效地处理时序数据,弥补前馈神经网络的缺点

  2. 循环神经网络的中心在于当时输出不只由当时输入决议,还由从前的输出决议

    这是经过添加隐状况在上一时刻步保存、鄙人一时刻步读取而完成的

  3. 深度循环神经网络和双向循环神经网络是循环神经网络的两个变体