〖TensorFlow2.0笔记25〗循环神经网络RNN原理讲解以及实战!

循环神经网络RNN原理讲解以及实战!

文章目录

    • 一. 引言
    • 二. 序列的表示
      • 2.1. 序列(Sequence)
      • 2.2. Tensorflow2.0中的Embedding层
      • 2.3. 预训练的词向量
    • 三. 循环神经网络
      • 3.1. 全连接层可行吗
      • 3.2. 权值共享解决问题1
      • 3.3. 内存机制解决问题2
      • 3.4. 循环神经网络

一. 引言

  • 卷积神经网络利用数据的局部相关性权值共享的思想大大减少了网络的参数量,非常适合于图片这种具有空间(Spatial)局部相关性的数据,已经被成功地应用到计算机视觉领域的一系列任务上。自然界的信号除了具有空间维度之外,还有一个时间(Temporal)维度。具有时间维度的信号非常常见,比如我们正在阅读的文本、说话时发出的语音信号、随着时间变化的股市参数等。这类数据并不一定具有局部相关性,同时数据在时间维度上的长度也是可变的,卷积神经网络并不擅长处理此类数据。

二. 序列的表示

2.1. 序列(Sequence)

  • 首先我们来介绍对于具有时间先后顺序的数据的表示方法。具有先后顺序的数据一般叫作序列(Sequence)
〖TensorFlow2.0笔记25〗循环神经网络RNN原理讲解以及实战!_第1张图片
  • 对于一个非数值类型的信号如何转换成数值信号,比如常见的文本信号,怎样把这样的String类型转换成数值类型。
〖TensorFlow2.0笔记25〗循环神经网络RNN原理讲解以及实战!_第2张图片

我们把文字编码为数值的过程叫作Word Embedding。One-hot 的编码方式实现Word Embedding 简单直观,编码过程不需要学习和训练。但是One-hot 编码的向量是高维度而且极其稀疏的,大量的位置为0,计算效率较低,同时也不利于神经网络的训练。从语义角度来讲,One-hot 编码还有一个严重的问题,它忽略了单词先天具有的语义相关性。举个例子,对于单词“like”、“dislike”、“Rome”、“Paris”来说,“like”和 dislike”在语义角度就强相关,它们都表示喜欢的程度;“Rome”和“Paris”同样也是强相关,他们都表示欧洲的两个地点。对于一组这样的单词来说,如果采用One-hot 编码,得到的向量之间没有相关性,不能很好地体现原有文字的语义相关度,因此One-hot 编码具有明显的缺陷。

  • 什么是 word embedding:通俗的翻译可以认为是单词嵌入,就是把 X \mathbf X X 所属空间的单词映射为到 Y \mathbf Y Y 空间的多维向量,那么该多维向量相当于嵌入到 Y \mathbf Y Y 所属空间中,一个萝卜一个坑。之乎大神:什么是 word embedding?
〖TensorFlow2.0笔记25〗循环神经网络RNN原理讲解以及实战!_第3张图片
〖TensorFlow2.0笔记25〗循环神经网络RNN原理讲解以及实战!_第4张图片

2.2. Tensorflow2.0中的Embedding层

  • 在神经网络中,单词的表示向量可以直接通过训练的方式得到,我们把单词的表示层叫作Embedding 层。Embedding 层负责把单词编码为某个词向量 v \boldsymbol{v} v,它接受的是采用数字编码的单词编号 i i i,如2 表示“I”,3 表示“me”等,系统总单词数量记为 N v o c a b N_{vocab} Nvocab,输出长度为 n n n 的向量 v \boldsymbol{v} v v = f θ ( i ∣ N vocab  , n ) (1) \boldsymbol{v}=f_{\theta}\left(i | N_{\text {vocab }}, n\right)\tag{1} v=fθ(iNvocab ,n)(1)
  • Embedding 层实现起来非常简单,构建一个shape 为 [ N vocab  , n ] \left[N_{\text {vocab }}, n\right] [Nvocab ,n]的查询表对象table,对于任意的单词编号 i i i,只需要查询到对应位置上的向量并返回即可: v = t a b l e [ i ] (2) \boldsymbol{v}=table[i]\tag{2} v=table[i](2)
  • Embedding 层是可训练的,它可放置在神经网络之前,完成单词到向量的转换,得到的表示向量可以继续通过神经网络完成后续任务,并计算误差,采用梯度下降算法来实现端到端(end-to-end)的训练。
  • 在 TensorFlow 中,可以通过layers.Embedding( N v o c a b N_{vocab} Nvocab, n n n)来定义一个Word Embedding层,其中 N v o c a b N_{vocab} Nvocab 参数指定词汇数量 n n n 为指定单词向量的长度。例如:
x = tf.range(10) # 生成10 个单词的数字编码
x = tf.random.shuffle(x) # 打散
# 创建共10 个单词,每个单词用长度为4 的向量表示的层
net = layers.Embedding(10, 4)
out = net(x) # 获取词向量
  • 上述代码创建了10个单词的Embedding 层,每个单词用长度为4的向量表示,可以传入数字编码为0~9的输入,得到这4 个单词的词向量,这些词向量随机初始化的,尚未经过网络训练,例如:
.Tensor: id=96, shape=(10, 4), dtype=float32, numpy=
array([[-0.00998075, -0.04006485, 0.03493755, 0.03328368],
       [-0.04139598, -0.02630153, -0.01353856, 0.02804044],
  • 我们可以直接查看Embedding 层内部的查询表table:
In [1]: net.embeddings
Out[1]:
.Variable 'embedding_4/embeddings:0' shape=(10, 4) dtype=float32, numpy=
array([[ 0.04112223, 0.01824595, -0.01841902, 0.00482471],
[-0.00428962, -0.03172196, -0.04929272, 0.04603403],
  • 并查看net.embeddings 张量的可优化属性为True,即可以通过梯度下降算法优化。
In [2]: net.embeddings.trainable
Out[2]:True

2.3. 预训练的词向量

  • Embedding 层的查询表是随机初始化的,需要从零开始训练。实际上,我们可以使用预训练的Word Embedding 模型来得到单词的表示方法,基于预训练模型的词向量相当于迁移了整个语义空间的知识,往往能得到更好的性能。
  • 目前应用的比较广泛的预训练模型有 Word2Vec 和 GloVe 等。它们已经在海量语料库训练得到了较好的词向量表示方法,并可以直接导出学习到的词向量表,方便迁移到其它任务。比如 GloVe 模型GloVe.6B.50d,词汇量为40 万,每个单词使用长度为50 的向量表示,用户只需要下载对应的模型文件即可,“glove6b50dtxt.zip”模型文件约69MB。
  • 那么如何使用这些预训练的词向量模型来帮助提升NLP 任务的性能?非常简单,对于Embedding 层,不再采用随机初始化的方式,而是利用我们已经预训练好的模型参数去初始化 Embedding 层的查询表。 例如:
# 从预训练模型中加载词向量表
embed_glove = load_embed('glove.6B.50d.txt')
# 直接利用预训练的词向量表初始化Embedding 层
net.set_weights([embed_glove])
  • 经过预训练的词向量模型初始化的Embedding 层可以设置为不参与训练:net.trainable= False,那么预训练的词向量就直接应用到此特定任务上;如果希望能够学到区别于预训练词向量模型不同的表示方法,那么可以把Embedding 层包含进反向传播算法中去,利用梯度下降来微调单词表示方法。

三. 循环神经网络

  • 现在我们来考虑如何处理序列信号,以文本序列为例,考虑一个句子 :“I hate this boring movie”。
  • 通过Embedding 层,可以将它转换为shape 为 [ b , s , n ] [b,s,n] [b,s,n] 的张量, b b b 为句子数量, s s s 为句子长度, n n n 为词向量长度。上述句子可以表示为shape 为 [ 1 , 5 , 10 ] [1,5,10] [1,5,10]的张量,其中5代表句子单词长度,10 表示词向量长度。
〖TensorFlow2.0笔记25〗循环神经网络RNN原理讲解以及实战!_第5张图片

3.1. 全连接层可行吗

〖TensorFlow2.0笔记25〗循环神经网络RNN原理讲解以及实战!_第6张图片
  • 2大缺陷:
〖TensorFlow2.0笔记25〗循环神经网络RNN原理讲解以及实战!_第7张图片
  • 1、网络参数量是相当可观的,内存占用和计算代价较高,同时由于每个序列的长度 s s s 并不相同,网络结构是动态变化的。
  • 2、每个全连接层子网络 W i \boldsymbol{W_i} Wi b i \boldsymbol{b_i} bi 只能感受当前词向量的输入,并不能感知之前和之后的语境信息,导致句子整体语义的缺失,每个子网络只能根据自己的输入来提取高层特征。

3.2. 权值共享解决问题1

  • 在介绍卷积神经网络时,我们就比较过,卷积神经网络之所以在处理局部相关数据时优于全连接网络,是因为它充分利用了权值共享的思想,大大减少了网络的参数量,使得网络训练起来更加高效。那么,我们在处理序列信号的问题上,能否借鉴权值共享的思想呢?

  • 我们尝试将这 s s s 个网络层参数共享,这样其实相当于使用一个全连接网络来提取所有单词的特征信息,如下图所示:

〖TensorFlow2.0笔记25〗循环神经网络RNN原理讲解以及实战!_第8张图片
  • 通过权值共享后,参数量大大减少,网络训练变得更加稳定高效。但是,这种网络结构并没有考虑序列之间的先后顺序,将词向量打乱次序仍然能获得相同的输出,无法获取有效的全局语义信息。

3.3. 内存机制解决问题2

  • 如何赋予网络提取整体语义特征的能力呢?或者说,如何让网络能够按序提取词向量的语义信息,并累积成整个句子的全局语义信息呢?我们想到了内存(Memory)机制。如果网络能够提供一个单独的内存变量,每次提取词向量的特征并刷新内存变量,直至最后一个输入完成,此时的内存变量即存储了所有序列的语义特征,并且由于输入序列之间的先后顺序,使得内存变量内容与序列顺序紧密相连。如下图未添加偏置:
〖TensorFlow2.0笔记25〗循环神经网络RNN原理讲解以及实战!_第9张图片
  • 我们将上述Memory 机制实现为一个状态张量 ,如图上图所示,除了原来的 W x h \boldsymbol{W}_{xh} Wxh 参数共享外,这里额外增加了一个和 W h h \boldsymbol{W}_{hh} Whh 参数,每个时间戳 t t t上状态张量刷新机制为: h t = σ ( W x h x t + W h h h t − 1 + b ) (3) \boldsymbol{h}_{t}=\sigma\left(\boldsymbol{W}_{x h} \boldsymbol{x}_{t}+\boldsymbol{W}_{h h} \boldsymbol{h}_{t-1}+\boldsymbol{b}\right)\tag{3} ht=σ(Wxhxt+Whhht1+b)(3)
  • 其中状态张量 h 0 \boldsymbol{h}_{0} h0 初始的内存状态,可以初始化为全0,经过个词向量的输入后得到网络最终的状态张量 h s \boldsymbol{h}_{s} hs h s \boldsymbol{h}_{s} hs较好地代表了句子的全局语义信息,基于 h s \boldsymbol{h}_{s} hs 通过某个全连接层分类器即可完成情感分类任务。

3.4. 循环神经网络

  • 通过一步步地探索,我们最终提出了一种“新型”的网络结构,如图下图所示,在每个时间戳 t t t,网络层接受当前时间戳的输入 x t \boldsymbol{x}_t xt 和上一个时间戳的网络状态向量 h t − 1 \boldsymbol{h}_{t-1} ht1 ,经过 h t = f θ ( h t − 1 , x t ) (4) \boldsymbol{h}_{t}=f_{\theta}\left(\boldsymbol{h}_{t-1}, \boldsymbol{x}_{t}\right)\tag{4} ht=fθ(ht1,xt)(4)
  • 变换后得到当前时间戳的新状态向量 t t t,并写入内存状态中,其中 f θ f_{\theta} fθ 代表网络的运算逻辑, θ \theta θ 为网络参数集。在每个时间戳上,网络层均有输出产生 o t \boldsymbol{o}_t ot o t = g ϕ ( h t ) \boldsymbol{o}_{t}=g_{\phi}\left(\boldsymbol{h}_{t}\right) ot=gϕ(ht)即将网络的状态向量变换后输出
〖TensorFlow2.0笔记25〗循环神经网络RNN原理讲解以及实战!_第10张图片
  • 上述网络结构在时间戳上折叠,如图下图所示(折叠的RNN模型),网络循环接受序列的每个特征向量 x t \boldsymbol{x}_t xt,并刷新内部状态向量 h t \boldsymbol{h}_{t} ht ,同时形成输出 o t \boldsymbol{o}_t ot 。对于这种网络结构,我们把它叫做循环网络结构(Recurrent Neural Network,简称RNN)
〖TensorFlow2.0笔记25〗循环神经网络RNN原理讲解以及实战!_第11张图片
  • 更特别地,如果使用张量 W x h \boldsymbol{W}_{x h} Wxh W h h \boldsymbol{W}_{hh} Whh 和偏置 b \boldsymbol{b} b 来参数化 f θ f_{\theta} fθ 网络,并按照方式更新内存状态,我们把这种网络叫做基本的循环神经网络,如无特别说明,一般说的循环神经网络即指这种实现。在循环神经网络中,激活函数更多地采用tanh函数,并且可以选择不使用偏置 b \boldsymbol{b} b 来进一步减少参数量。状态向量 h t \boldsymbol{h}_{t} ht 可以直接用作输出,即 o t = h t \boldsymbol{o}_{t}=\boldsymbol{h}_{t} ot=ht,也可以对 h t \boldsymbol{h}_{t} ht 做一个简单的线性变换 o t = W h o h t \boldsymbol{o}_{t}=\boldsymbol{W}_{ho}\boldsymbol{h}_{t} ot=Whoht 后得到每个时间戳上的网络输出 o t \boldsymbol{o}_{t} ot h t = σ ( W x h x t + W h h h t − 1 + b ) (5) \boldsymbol{h}_{t}=\sigma\left(\boldsymbol{W}_{x h} \boldsymbol{x}_{t}+\boldsymbol{W}_{h h} \boldsymbol{h}_{t-1}+\boldsymbol{b}\right)\tag{5} ht=σ(Wxhxt+Whhht1+b)(5)

你可能感兴趣的:(〖TensorFlow2.0笔记25〗循环神经网络RNN原理讲解以及实战!)