当前位置:   article > 正文

深度学习-神经网络-循环神经网络(一):RNN(Recurrent Neural Network,循环神经网络;1990年)

深度学习-神经网络-循环神经网络(一):RNN(Recurrent Neural Network,循环神经网络;1990年)

Jordan RNN于1986年提出:《SERIAL ORDER: A PARALLEL DISTRmUTED PROCESSING APPROACH》
Elman RNN于1990年提出:《Finding Structure in Time》
《LSTM原始论文:Long Short-Term Memory》
《GRU原始论文:Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation》

一、RNN概述

为什么有了神经网络还需要有循环神经网络?

在普通的神经网络中,信息的传递是单向的,这种限制虽然使得网络变得更容易学习,但在一定程度上也减弱了神经网络模型的能力。特别是在很多现实任务中,网络的输出不仅和当前时刻的输入相关,也和其过去一段时间的输出相关。此外,普通网络难以处理时序数据,比如视频、语音、文本等,时序数据的长度一般是不固定的,而前馈神经网络要求输入和输出的维数都是固定的,不能任意改变。因此,当处理这一类和时序相关的问题时,就需要一种能力更强的模型。

循环神经网络(Recurrent Neural Network,RNN)是一类具有短期记忆能力的神经网络。在循环神经网络中,神经元不但可以接受其它神经元的信息,也可以接受自身的信息,形成具有环路的网络结构。换句话说:神经元的输出可以在下一个时间步直接作用到自身。

RNN(Recurrent Neural Network), 中文称作循环神经网络, 它一般以序列数据为输入, 通过网络内部的结构设计有效捕捉序列之间的关系特征, 一般也是以序列形式进行输出.

DNN算法(全连接神经网络)、CNN算法(卷积神经网络)的输出都是只考虑前一个输入的影响而不考虑其它时刻输入的影响,比如简单的猫、狗、手写数字等单个物体的识别具有较好的效果。

  • 但是,对于一些与时间先后有关的,比如视频的下一时刻的预测,文档前后文内容的预测等,这些算法的表现就不尽如人意了。因此, RNN就应运而生了。RNN 解决了这个问题。RNN 是包含循环的网络,允许信息的持久化。
  • RNN是一种特殊的神经网络结构, 它是根据"人的认知是基于过往的经验和记忆"这一观点提出的. 它与DNN,CNN不同的是: 它不仅考虑前一时刻的输入,而且赋予了网络对前面的内容的一种’记忆’功能.
  • RNN之所以称为循环神经网路,即一个序列 “当前的输出” 与 “前面的输出” 也有关。具体的表现形式为网络会对前面的信息进行记忆并应用于当前输出的计算中,即隐藏层之间的节点不再无连接而是有连接的,并且隐藏层的输入不仅包括输入层的输出还包括上一时刻隐藏层的输出。
  • RNN的应用领域有很多, 可以说只要考虑时间先后顺序的问题都可以使用RNN来解决:
    • 自然语言处理(NLP): 主要有视频处理, 文本生成, 语言模型, 图像处理
    • 机器翻译, 机器写小说
    • 语音识别
    • 图像描述生成
    • 文本相似度计算
    • 音乐推荐、网易考拉商品推荐、Youtube视频推荐等新的应用领域.

因为RNN结构能够很好利用序列之间的关系, 因此针对自然界具有连续性的输入序列, 如人类的语言, 语音等进行很好的处理, 广泛应用于NLP领域的各项任务, 如文本分类, 情感分析, 意图识别, 机器翻译等.

以一个用户意图识别的例子进行简单的分析:
在这里插入图片描述

  • 第一步: 用户输入了"What time is it ?", 我们首先需要对它进行基本的分词, 因为RNN是按照顺序工作的, 每次只接收一个单词进行处理.
    在这里插入图片描述
  • 第二步: 首先将单词"What"输送给RNN, 它将产生一个输出O1.
    在这里插入图片描述
  • 第三步: 继续将单词"time"输送给RNN, 但此时RNN不仅仅利用"time"来产生输出O2, 还会使用来自上一层隐层输出O1作为输入信息.
    在这里插入图片描述
  • 第四步: 重复这样的步骤, 直到处理完所有的单词.
    在这里插入图片描述
  • 第五步: 最后,将最终的隐层输出O5进行处理来解析用户意图.
    在这里插入图片描述

RNN模型的分类 { 按照输入和输出的结构进行分类 { “N vs N” RNN “N vs 1” RNN “1 vs N” RNN “N vs M” RNN 按照 R N N 的内部构造进行分类 { 传统RNN LSTM Bi-LSTM GRU Bi-GRU

RNN模型的分类{{“N\ vs\ N”\ RNN“N\ vs\ 1”\ RNN“1\ vs\ N”\ RNN“N\ vs\ M”\ RNNRNN{传统RNNLSTMBi-LSTMGRUBi-GRU
RNN模型的分类 按照输入和输出的结构进行分类 “N vs N” RNN“N vs 1” RNN“1 vs N” RNN“N vs M” RNN按照RNN的内部构造进行分类 传统RNNLSTMBi-LSTMGRUBi-GRU

在这里插入图片描述

  • 图1:固定长度的输入和输出 (e.g. 图像分类)
  • 图2:序列输出 (e.g.图像转文字)
  • 图3:数列输入 (e.g. 文本分类)
  • 图4:异步的序列输入和输出(e.g.文本翻译).
  • 图5:同步的序列输入和输出 (e.g. 根据视频的每一帧来对视频进行分类)

1、“N vs N” RNN

它是RNN最基础的结构形式, 最大的特点就是: 输入和输出序列是等长的. 由于这个限制的存在, 使其适用范围比较小, 可用于生成等长度的合辙诗句.

在这里插入图片描述

2、“N vs 1” RNN

有时候我们要处理的问题输入是一个序列,而要求输出是一个单独的值而不是序列,应该怎样建模呢?我们只要在最后一个隐层输出h上进行线性变换就可以了,大部分情况下,为了更好的明确结果, 还要使用sigmoid或者softmax进行处理. 这种结构经常被应用在文本分类问题上.
在这里插入图片描述

3、“1 vs N” RNN

如果输入不是序列而输出为序列的情况怎么处理呢?我们最常采用的一种方式就是使该输入作用于每次的输出之上. 这种结构可用于将图片生成文字任务等.
在这里插入图片描述

4、“N vs M” RNN

这是一种不限输入输出长度的RNN结构, 它由编码器和解码器两部分组成, 两者的内部结构都是某类RNN, 它也被称为seq2seq架构. 输入数据首先通过编码器, 最终输出一个隐含变量c, 之后最常用的做法是使用这个隐含变量c作用在解码器进行解码的每一步上, 以保证输入信息被有效利用.
在这里插入图片描述
seq2seq架构最早被提出应用于机器翻译, 因为其输入输出不受限制,如今也是应用最广的RNN模型结构. 在机器翻译, 阅读理解, 文本摘要等众多领域都进行了非常多的应用实践.

二、RNN传统结构

在这里插入图片描述
通过简化图,我们看到RNN比传统的神经网络多了一个循环圈,这个循环表示的就是在下一个时间步(Time Step)上会返回作为输入的一部分,我们把RNN在时间点上展开,得到的图形如下:

在这里插入图片描述

对于一个模型而言,最重要的四个部分:输入、输出、参数、对应运算关系。

在这里插入图片描述

  1. 输入: X X X,以及来自前一时刻隐藏层的结果 h t − 1 h_{t-1} ht1
  2. 输出: Y Y Y,以及传递给下一时刻隐藏层的结果 h t h_t ht
  3. 参数: W i W_{i} Wi W h W_{h} Wh b b b W o W_o Wo
  4. 对应运算关系(信息传播):
    h t = σ [ W i X + W h ⋅ h t − 1 + b ] Y = S o f t m a x [ W o ⋅ h t ]
    ht=σ[WiX+Wh·ht1+b]Y=Softmax[Wo·ht]
    htY=σ[WiX+Whht1+b]=Softmax[Woht]

有了这个RNN基本单元,整个RNN网络也就呼之欲出了。

1、传统RNN结构

在这里插入图片描述
在这里插入图片描述

  • Pointwise Operation:对应位置的运算(加法/乘法)【比如:2个3行4列的矩阵的Pointwise Operation就是2个矩阵对应位置的元素分别做运算】
  • Concatenate:矩阵的合并【一般是左右方向(横向)的合并,因为横向一般表示各个特征,每一列代表一个特征。比如A矩阵是4列(4个特征),B矩阵是10列(10个特征),合并后就是一个14列的矩阵(14个特征)】

内部结构分析: 我们把目光集中在中间的方块部分, 它的输入有两部分, 分别是 h t − 1 h_{t-1} ht1 以及 X t X_{t} Xt, 代表上一时间步的隐层输出, 以及此时间步的输入, 它们进入RNN结构体后, 会"融合"到一起, 这种融合我们根据结构解释可知, 是将二者进行拼接, 形成新的张量 [ h t − 1 , X t ] [h_{t-1}, X_{t}] [ht1,Xt], 之后这个新的张量将通过一个全连接层(线性层), 该层使用tanh作为激活函数, 最终得到该时间步的输出 h t h_t ht, 它将作为下一个时间步的输入和 X t + 1 X_{t+1} Xt+1一起进入结构体. 以此类推.
在这里插入图片描述

  • RNN的cell比较简单,我们用 X t X_t Xt表示 t t t 时刻cell的输入, C t C_t Ct 表示 t t t 时刻cell的状态, h t h_t ht 表示 t t t 时刻的输出(输出和状态在RNN里是一样的)。
  • 那么其前向传播的公式也很简单: h t = C t = [ h t − 1 , X t ] ∗ W + b t h_t=C_t=[h_{t−1},X_t]∗W+b_t ht=Ct=[ht1,Xt]W+bt,其中 [,] 表示concat。 W W W b b b 分别为RNN的 kernel 和 bias。
    在这里插入图片描述

RNN传统结构:

  • 在每个神经单元中只有一个tanh激活函数。激活函数tanh用于帮助调节流经网络的值, tanh函数将值压缩在-1和1之间.
  • 相对于DNN、CNN每个神经元只有一个输入,RNN的每个神经单元有两个输入。如上图所示,第 t t t 个神经元的输入不仅仅有 x t x_t xt,还有上个神经元的状态 h t − 1 h_{t-1} ht1 或者是上一个神经元的输出 y t − 1 y_{t-1} yt1。这里根据 h t h_t ht接收的是 h t − 1 h_{t-1} ht1 还是 y t − 1 y_{t-1} yt1的不同,分为Elman network与Jordan Network,一般来说我们用的都是Elman network。
  • X X X 它表示输入层的值,是一个向量;
  • U U U 是输入层到隐藏层的权重矩阵;跟CNN类似,RNN模型中不同时刻的 U U U是共享的
  • W W W 是隐藏层上一次的值作为这一次的输入的权重;跟CNN类似,RNN模型中不同时刻的 W W W是共享的
  • S S S 表示隐藏层的值Embedding,是一个向量;
  • V V V 是隐藏层到输出层的权重矩阵;
  • O O O 表示输出层的值,是一个向量;
    在这里插入图片描述
Feed Forward Neural NetworkRecurrent Neural NetworkPassing Hidden State to next time step
在这里插入图片描述在这里插入图片描述在这里插入图片描述
  • RNN网络在 t t t 时刻接收到输入 X t X_t Xt 之后,隐藏层的值是 S t S_t St,输出值是 O t O_t Ot。关键一点是, S t S_t St 的值不仅仅取决于 X t X_t Xt ,还取决于 S t − 1 S_{t-1} St1 。我们可以用下面的公式来表示循环神经网络的计算方法:
    S ⃗ t = f ( X ⃗ t ⋅ U ⃗ + S ⃗ t − 1 ⋅ W ⃗ ) O ⃗ t = g ( V ⃗ ⋅ S ⃗ t )
    St=f(Xt·U+St1·W)Ot=g(V·St)
    S tO t=f(X tU +S t1W )=g(V S t)

在这里插入图片描述
传统RNN输入有两部分, 分别是 h t − 1 h_{t-1} ht1以及 x t x_t xt, 代表上一时间步的隐层输出, 以及此时间步的输入, 它们进入RNN结构体后, 会"融合"/concat到一起, 这种融合我们根据结构解释可知, 是将二者进行拼接, 形成新的张量 [ x t , h t − 1 ] [x_t, h_{t-1}] [xt,ht1], 之后这个新的张量将通过一个全连接层(线性层), 该层使用tanh作为激活函数, 最终得到该时间步的输出 h t h_t ht, 它将作为下一个时间步的输入和 x t + 1 x_{t+1} xt+1一起进入结构体. 以此类推.

# 导入工具包
import torch
import torch.nn as nn

# 实例化一个RNN对象
rnn = nn.RNN(input_size=5, hidden_size=6, num_layers=1)  # input_size:输入张量x中特征维度的大小(输入WordEmbedding的维度);hidden_size:隐层张量h中特征维度的大小(隐藏层神经元的个数);num_layers: 隐含层的层数.

# 初始化输入
input = torch.randn(1, 3, 5)  # 初始化一个输入张量【batch_size=1表示当前批次的样本数量;3表示样本序列长度/sequence_lenght;5表示WordEmbedding维度】此处的WordEmbedding维度要与rnn对象的input_size一致
h0 = torch.randn(1, 3, 6)  # 初始化一个初始隐藏层张量【1表示隐藏层层数num_layers,要与与rnn对象的num_layers一致;3表示样本序列长度/sequence_lenght;6表示隐藏层神经元的个数(隐层张量的特征维度的大小)】
print("input.shape = {0}\ninput = \n{1}".format(input.shape, input))

# 利用RNN模型计算输出
output, hn = rnn(input, h0)

print("\noutput.shape = {0}\noutput = \n{1}".format(output.shape, output))
print("\nhn.shape = {0}\nhn = \n{1}".format(hn.shape, hn))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

打印结果:

input.shape = torch.Size([1, 3, 5])
input = 
tensor([[[-0.9448,  0.4040, -1.5022, -1.3403, -0.9938],
         [-0.5331,  0.0470, -0.3628,  0.3317, -0.0419],
         [-0.9932, -0.1746, -1.2205,  0.8281,  0.3448]]])

output.shape = torch.Size([1, 3, 6])
output = 
tensor([[[-0.5093,  0.0121,  0.7261,  0.7477, -0.8238, -0.0444],
         [ 0.0887,  0.2913,  0.2533,  0.4546, -0.0650,  0.1601],
         [-0.5954,  0.4542,  0.1506,  0.5844, -0.3318, -0.6880]]], grad_fn=<StackBackward0>)

hn.shape = torch.Size([1, 3, 6])
hn = 
tensor([[[-0.5093,  0.0121,  0.7261,  0.7477, -0.8238, -0.0444],
         [ 0.0887,  0.2913,  0.2533,  0.4546, -0.0650,  0.1601],
         [-0.5954,  0.4542,  0.1506,  0.5844, -0.3318, -0.6880]]], grad_fn=<StackBackward0>)

Process finished with exit code 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

nn.RNN类初始化主要参数解释:

  • input_size:输入张量x中特征维度的大小(输入WordEmbedding的维度).
  • hidden_size:隐层张量h中特征维度的大小(隐藏层神经元的个数).
  • num_layers:隐含层的层数.
  • nonlinearity:激活函数的选择, 默认是tanh.

nn.RNN类实例化对象主要参数解释:

  • input:输入张量x.
  • h0:初始化的隐层张量h.

2、传统RNN经典结构:Elman Network、Jordan Network、Bidirectional RNN

Jordan Network于1986年提出。《SERIAL ORDER: A PARALLEL DISTRmUTED PROCESSING APPROACH》

Elman Network于1990年提出,公认的叫法为RNN。《Finding Structure in Time》

在这里插入图片描述

3、RNN传统结构:梯度消失、梯度爆炸问题

如果在训练过程中发生了梯度消失,权重无法被更新,最终导致训练失败; 梯度爆炸所带来的梯度过大,大幅度更新网络参数,在极端情况下,结果会溢出(NaN值).
在这里插入图片描述

  • 假设我们的时间序列只有三段, S 0 S_{0} S0 为给定值,神经元没有激活函数,则RNN最简单的前向传播过程如下:
    S 1 = W x X 1 + W s S 0 + b 1 ; O 1 = W o S 1 + b 2 S_{1}=W_{x}X_{1}+W_{s}S_{0}+b_{1};O_{1}=W_{o}S_{1}+b_{2} S1=WxX1+WsS0+b1O1=WoS1+b2
    S 2 = W x X 2 + W s S 1 + b 1 ; O 2 = W o S 2 + b 2 S_{2}=W_{x}X_{2}+W_{s}S_{1}+b_{1};O_{2}=W_{o}S_{2}+b_{2} S2=WxX2+WsS1+b1O2=WoS2+b2
    S 3 = W x X 3 + W s S 2 + b 1 ; O 3 = W o S 3 + b 2 S_{3}=W_{x}X_{3}+W_{s}S_{2}+b_{1};O_{3}=W_{o}S_{3}+b_{2} S3=WxX3+WsS2+b1O3=WoS3+b2
  • 假设在t=3时刻,损失函数为 L 3 = 1 2 ( Y 3 − O 3 ) 2 L_{3}=\frac{1}{2}(Y_{3}-O_{3})^{2} L3=21(Y3O3)2
  • 则对于一次训练任务的损失函数为 L = ∑ t = 0 T L t L=\sum_{t=0}^{T}{L_{t}} L=t=0TLt,即每一时刻损失值的累加。
  • 使用随机梯度下降法训练RNN其实就是对 W x W_{x} Wx W s W_{s} Ws W o W_{o} Wo 以及 b 1 、 b 2 b_{1}、b_{2} b1b2 求偏导,并不断调整它们以使 L L L 尽可能达到最小的过程。
  • 现在假设我们我们的时间序列只有三段, t 1 t_1 t1 t 2 t_2 t2 t 3 t_3 t3
  • 我们只对t3时刻的 W x W_{x} Wx W s W_{s} Ws W 0 W_{0} W0 求偏导(其他时刻类似):
    ∂ L 3 ∂ W 0 = ∂ L 3 ∂ O 3 ∂ O 3 ∂ W o ∂ L 3 ∂ W x = ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ W x + ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ S 2 ∂ S 2 ∂ W x + ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ S 2 ∂ S 2 ∂ S 1 ∂ S 1 ∂ W x ∂ L 3 ∂ W s = ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ W s + ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ S 2 ∂ S 2 ∂ W s + ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ S 2 ∂ S 2 ∂ S 1 ∂ S 1 ∂ W s \frac{\partial{L_{3}}}{\partial{W_{0}}}=\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{W_{o}}}\\[2ex] \frac{\partial{L_{3}}}{\partial{W_{x}}}=\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{W_{x}}}+\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{S_{2}}}\frac{\partial{S_{2}}}{\partial{W_{x}}}+\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{S_{2}}}\frac{\partial{S_{2}}}{\partial{S_{1}}}\frac{\partial{S_{1}}}{\partial{W_{x}}}\\[2ex] \frac{\partial{L_{3}}}{\partial{W_{s}}}=\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{W_{s}}}+\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{S_{2}}}\frac{\partial{S_{2}}}{\partial{W_{s}}}+\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{S_{2}}}\frac{\partial{S_{2}}}{\partial{S_{1}}}\frac{\partial{S_{1}}}{\partial{W_{s}}} W0L3=O3L3WoO3WxL3=O3L3S3O3WxS3+O3L3S3O3S2S3WxS2+O3L3S3O3S2S3S1S2WxS1WsL3=O3L3S3O3WsS3+O3L3S3O3S2S3WsS2+O3L3S3O3S2S3S1S2WsS1
  • 可以看出对于 W 0 W_{0} W0 求偏导并没有长期依赖,但是对于 W x W_{x} Wx W s W_{s} Ws 求偏导,会随着时间序列产生长期依赖。因为 S t S_{t} St 随着时间序列向前传播,而 S t S_{t} St 又是 W x W_{x} Wx W s W_{s} Ws 的函数。
  • 根据上述求偏导的过程,我们可以得出任意时刻对 W x W_{x} Wx W s W_{s} Ws 求偏导的公式:
    ∂ L t ∂ W x = ∑ k = 1 t ∂ L t ∂ O t ∂ O t ∂ S t ( ∏ j = k + 1 t ∂ S j ∂ S j − 1 ) ∂ S k ∂ W x \frac{\partial{L_{t}}}{\partial{W_{x}}}=\sum_{k=1}^{t}{\frac{\partial{L_{t}}}{\partial{O_{t}}}\frac{\partial{O_{t}}}{\partial{S_{t}}}}(\prod_{j=k+1}^{t}{\frac{\partial{S_{j}}}{\partial{S_{j-1}}}})\frac{\partial{S_{k}}}{\partial{W_{x}}} WxLt=k=1tOtLtStOt(j=k+1tSj1Sj)WxSk
  • 任意时刻对 W s W_{s} Ws 求偏导的公式同上。
  • 加上激活函数
    S j = t a n h ( W x X j + W s S j − 1 + b 1 ) S_{j}=tanh(W_{x}X_{j}+W_{s}S_{j-1}+b_{1}) Sj=tanh(WxXj+WsSj1+b1)

    ∏ j = k + 1 t ∂ S j ∂ S j − 1 = ∏ j = k + 1 t t a n h ′ ( W x X j + W s S j − 1 + b 1 ) ⋅ W s \prod^t_{j=k+1}\cfrac{\partial S_j}{\partial S_{j-1}}=\prod^t_{j=k+1}tanh'(W_{x}X_{j}+W_{s}S_{j-1}+b_{1})·W_s j=k+1tSj1Sj=j=k+1ttanh(WxXj+WsSj1+b1)Ws
  • 激活函数 tanh 和它的导数图像如下
    在这里插入图片描述
  • 由上图可以看出 t a n h ′ ∈ [ 0 , 1 ] \color{red}{tanh'\in [0,1]} tanh[0,1],对于训练过程大部分情况下 t a n h tanh tanh 的导数是小于1的,只有当 W x X j + W s S j − 1 + b 1 = 0 W_{x}X_{j}+W_{s}S_{j-1}+b_{1}=0 WxXj+WsSj1+b1=0,此时导数等于 1 1 1
  • 如果 W s W_{s} Ws 也是一个大于0小于1的值,则当 t t t 很大时,使得 t a n h ′ ⋅ W s < 1 tanh' · W_s < 1 tanhWs<1,那么 ∏ j = k + 1 t t a n h ′ ⋅ W s \prod_{j=k+1}^{t}{tanh^{'}}·W_{s} j=k+1ttanhWs 就会趋近于0,和 (0.9*0.8)^50趋近与0是一个道理,这就是RNN中梯度弥散的原因
  • 同理当 W s W_{s} Ws 很大时,具体指(比如 t a n h ′ = 0.1 tanh' = 0.1 tanh=0.1,而 W s = 99 W_s=99 Ws=99,则相乘为9.9),使得 t a n h ′ ⋅ W s > 1 tanh' · W_s > 1 tanhWs>1,那么 ∏ j = k + 1 t t a n h ′ W s \prod_{j=k+1}^{t}{tanh^{'}}W_{s} j=k+1ttanhWs 就会趋近于无穷,这就是RNN中梯度爆炸的原因
  • 至于怎么避免这种现象,让我在看看 ∂ L t ∂ W x = ∑ k = 0 t ∂ L t ∂ O t ∂ O t ∂ S t ( ∏ j = k + 1 t ∂ S j ∂ S j − 1 ) ∂ S k ∂ W x \frac{\partial{L_{t}}}{\partial{W_{x}}}=\sum_{k=0}^{t}{\frac{\partial{L_{t}}}{\partial{O_{t}}}\frac{\partial{O_{t}}}{\partial{S_{t}}}}(\prod_{j=k+1}^{t}{\frac{\partial{S_{j}}}{\partial{S_{j-1}}}})\frac{\partial{S_{k}}}{\partial{W_{x}}} WxLt=k=0tOtLtStOt(j=k+1tSj1Sj)WxSk 梯度消失和爆炸的根本原因就是 ∏ j = k + 1 t ∂ S j ∂ S j − 1 \prod_{j=k+1}^{t}{\frac{\partial{S_{j}}}{\partial{S_{j-1}}}} j=k+1tSj1Sj 这一坨,要消除这种情况就需要把这一坨在求偏导的过程中去掉,至于怎么去掉,一种办法就是使 ∂ S j ∂ S j − 1 ≈ 1 {\frac{\partial{S_{j}}}{\partial{S_{j-1}}}}\approx1 Sj1Sj1 另一种办法就是使 ∂ S j ∂ S j − 1 ≈ 0 {\frac{\partial{S_{j}}}{\partial{S_{j-1}}}}\approx0 Sj1Sj0 。其实这就是LSTM做的事情。

4、RNN传统结构长期依赖(Long-Term Dependencies)减弱的问题

  • RNN 的关键点之一就是他们可以用来连接先前的信息到当前的任务上,例如使用过去的视频段来推测对当前段的理解。如果 RNN 可以做到这个,他们就变得非常有用。但是真的可以么?答案是,还有很多依赖因素。
  • 有时候,我们仅仅需要知道先前的信息来执行当前的任务。例如,我们有一个语言模型用来基于先前的词来预测下一个词。如果我们试着预测 “the clouds are in the sky” 最后的词,我们并不需要任何其他的上下文 —— 因此下一个词很显然就应该是 sky。在这样的场景中,相关的信息和预测的词位置之间的间隔是非常小的,RNN 可以学会使用先前的信息。
    在这里插入图片描述
  • 但是同样会有一些更加复杂的场景。假设我们试着去预测“I grew up in France… I speak fluent French”最后的词。当前的信息建议下一个词可能是一种语言的名字,但是如果我们需要弄清楚是什么语言,我们是需要先前提到的离当前位置很远的 France 的上下文的。这说明相关信息和当前预测位置之间的间隔就肯定变得相当的大。
  • 不幸的是,在这个间隔不断增大时,RNN 会丧失学习到连接如此远的信息的能力
    在这里插入图片描述
  • 在理论上,RNN 绝对可以处理这样的长期依赖问题。人们可以仔细挑选参数来解决这类问题中的最初级形式,但在实践中,RNN 肯定不能够成功学习到这些知识。Bengio, et al. (1994)等人对该问题进行了深入的研究,他们发现一些使训练 RNN 变得非常困难的相当根本的原因。

5、Tensorflow2-SimpleRNNCell案例(构建每一个cell及memorycell)-imdb数据集【电影评论情感二分类】

import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import tensorflow as tf
import numpy as np
from tensorflow import keras
from tensorflow.keras import layers
import time

tf.random.set_seed(22)
np.random.seed(22)

assert tf.__version__.startswith('2.')

batch_size = 500  # 每次训练500个句子

total_words = 10000  # the most frequest words
max_review_len = 80  # 设置句子长度,如果有的句子的长度不到80则补齐,如果有的句子超过80则截断
embedding_len = 100  # 每个单词转为向量后的向量维度

# 一、获取数据集
(X_train, Y_train), (X_val, Y_val) = keras.datasets.imdb.load_data(num_words=total_words)
print('X_train[0] = {0},\nY_train[0] = {1}'.format(X_train[0], Y_train[0]))
print('X_train.shpae = {0},Y_train.shpae = {1}------------type(X_train) = {2},type(Y_train) = {3}'.format(X_train.shape, Y_train.shape, type(X_train), type(Y_train)))

# 二、数据处理
# 2.1 # 设置句子统一长度
X_train = keras.preprocessing.sequence.pad_sequences(X_train, maxlen=max_review_len)  # 设置句子长度    [b, 80]
X_val = keras.preprocessing.sequence.pad_sequences(X_val, maxlen=max_review_len)  # 设置句子长度
print('X_train.shpae = {0},Y_train.shpae = {1},tf.reduce_max(Y_train) = {2},tf.reduce_min(Y_train) = {3}'.format(X_train.shape, Y_train.shape, tf.reduce_max(Y_train), tf.reduce_min(Y_train)))
# 2.1 处理训练集为batch模型
db_train = tf.data.Dataset.from_tensor_slices((X_train, Y_train))
db_train = db_train.shuffle(1000).batch(batch_size, drop_remainder=True)  # 通过 drop_remainder=True 把 最后一个不满足batch_size大小的batch丢弃掉
db_val = tf.data.Dataset.from_tensor_slices((X_val, Y_val))
db_val = db_val.batch(batch_size, drop_remainder=True)  # 通过 drop_remainder=True 把 最后一个不满足batch_size大小的batch丢弃掉
print('db_train = {0},len(db_train) = {1}'.format(db_train, len(db_train)))


class MyRNN(keras.Model):
    def __init__(self, output_dim):
        super(MyRNN, self).__init__()
        # ***********************************************************memoryCell***********************************************************
        # [b, 64]
        # 用于保存上一次的隐藏状态的输出值h_{t-1},作为计算本次的输出值h_t时的输入值之一
        # 使用多个memoryCell串联即实现Deep的作用
        self.memoryCell01 = [tf.zeros([batch_size, output_dim])]  # 初始化memoryCell01,维度为 [b, 64]
        self.memoryCell02 = [tf.zeros([batch_size, output_dim])]  # 初始化memoryCell02,维度为 [b, 64]
        # ***********************************************************Embedding***********************************************************
        # 将每一个句子(维度为[80,1],80表示每个句子包含的word数量,1表示1个word)变换为wordEmbedding(维度为[80,100],80表示每个句子包含的word数量,100表示每个wordEmbedding的维度)
        # [b, 80, 1] => [b, 80, 100]
        # input_dim:表示输入维度,即设定词库总单词数量;b
        # input_length:表示每个句子统一长度(包含的单词数量);80
        # output_dim:表示输出维度,即每个单词转为向量后的向量维度;100
        self.embedding = layers.Embedding(input_dim=total_words, input_length=max_review_len, output_dim=embedding_len)
        # ***********************************************************RNNCell Layer***********************************************************
        # [b, 80, 100]=>[b, 64]
        self.rnn_cell01 = layers.SimpleRNNCell(output_dim, dropout=0.2)  # output_dim: dimensionality of the output space. 隐藏状态的维度;dropout 防止过拟合
        self.rnn_cell02 = layers.SimpleRNNCell(output_dim, dropout=0.2)
        # ***********************************************************全连接层***********************************************************
        # [b, 64] => [b, 1]
        self.outlayer = layers.Dense(1)

    def call(self, inputs, training=None):
        """
        net(x) net(x, training=True) :train mode
        net(x, training=False): test mode
        :param inputs: [b, 80, 1]
        :param training:
        :return:
        """
        # ***********************************************************Embedding***********************************************************
        # embedding: [b, 80, 1] => [b, 80, 100]
        wordEmbeddings = self.embedding(inputs)  # inputs 为1个batch的句子文本
        print('\nwordEmbeddings.shape = {0}, wordEmbeddings = {1}'.format(wordEmbeddings.shape, wordEmbeddings))
        # rnn cell compute
        # ***********************************************************RNNCell Layer***********************************************************
        # [b, 80, 100] => [b, 1, 64],每个句子都从降维:[80, 100]=>[1, 64]
        memoryCell01 = self.memoryCell01
        memoryCell02 = self.memoryCell02
        wordEmbedding_index = 0
        for wordEmbedding in tf.unstack(wordEmbeddings, axis=1):  # wordEmbedding: [b, 100],将每个句子中的80个单词展开,即按读取该句子的时间轴展开
            # 隐含状态:out01/out02: [b, 64]
            # h_t = x_t×w_{xh}+h_{t-1}×w_{hh};其中:x_t=wordEmbedding;h_{t-1}=memoryCell01;输出值h_t = out01
            out01, memoryCell01_current = self.rnn_cell01(wordEmbedding, memoryCell01, training=training)  # training=True 表示模式是训练模式,dropout功能有效,默认是True
            memoryCell01 = memoryCell01_current  # 并将h_t替代memoryCell01中的旧的h_{t-1}用于下个单词
            # 将rnn_cell01的输出值out01传入下一个rnn_cell02提升RNNCell Layer的提取能力
            out02, memoryCell02_current = self.rnn_cell02(out01, memoryCell02, training=training)  # training=True 表示模式是训练模式,dropout功能有效,默认是True
            memoryCell02 = memoryCell02_current  # 并将h_t替代memoryCell02中的旧的h_{t-1}用于下个单词
            if wordEmbedding_index == 0:
                print('wordEmbedding.shape = {0}, wordEmbedding = {1}'.format(wordEmbedding.shape, wordEmbedding))
                print('out01.shape = {0}, out01 = {1}'.format(out01.shape, out01))
                print('out02.shape = {0}, out02 = {1}'.format(out02.shape, out02))
            wordEmbedding_index += 1
        # ***********************************************************全连接层***********************************************************
        # out: [b, 1, 64] => [b, 1, 1]
        out_logit = self.outlayer(out02)  # out02代表了每个句子的语义信息的提取
        print('out_logit.shape = {0}, out_logit = {1}'.format(out_logit.shape, out_logit))
        out_prob = tf.sigmoid(out_logit)  # p(y is pos|x)
        print('out_prob.shape = {0}, out_prob = {1}, {2}'.format(out_prob.shape, out_prob, '\n'))
        return out_prob


def main():
    output_dim = 64     # 设定输出的隐藏状态维度  [b, 100] => [b,64]
    epochs = 4
    t0 = time.time()
    network = MyRNN(output_dim)
    # 不需要设置from_logits=True,因为MyRNN()中已经设定了激活函数层 out_prob = tf.sigmoid(X)
    # metrics=['accuracy']表示打印测试数据
    network.compile(optimizer=keras.optimizers.Adam(0.001),
                    loss=tf.losses.BinaryCrossentropy(),
                    metrics=['accuracy'])
    print('\n***********************************************************训练network:开始***********************************************************')
    network.fit(db_train, epochs=epochs, validation_data=db_val)
    print('***********************************************************训练network:结束***********************************************************')
    print('\n***********************************************************评估network(其实训练时已经评估):开始***********************************************************')
    network.evaluate(db_val)  # 评估模型
    print('***********************************************************评估network(其实训练时已经评估):结束***********************************************************')
    t1 = time.time()
    print('total time cost:', t1 - t0)


if __name__ == '__main__':
    main()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124

打印结果:

X_train[0] = [1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32],
Y_train[0] = 1
X_train.shpae = (25000,),Y_train.shpae = (25000,)------------type(X_train) = <class 'numpy.ndarray'>type(Y_train) = <class 'numpy.ndarray'>
X_train.shpae = (25000, 80),Y_train.shpae = (25000,),tf.reduce_max(Y_train) = 1,tf.reduce_min(Y_train) = 0
db_train = <BatchDataset shapes: ((500, 80), (500,)), types: (tf.int32, tf.int64)>len(db_train) = 50

***********************************************************训练network:开始***********************************************************
Epoch 1/4

wordEmbeddings.shape = (500, 80, 100), wordEmbeddings = Tensor("my_rnn/embedding/embedding_lookup/Identity:0", shape=(500, 80, 100), dtype=float32)
wordEmbedding.shape = (500, 100), wordEmbedding = Tensor("my_rnn/unstack:0", shape=(500, 100), dtype=float32)
out01.shape = (500, 64), out01 = Tensor("my_rnn/simple_rnn_cell/Tanh:0", shape=(500, 64), dtype=float32)
out02.shape = (500, 64), out02 = Tensor("my_rnn/simple_rnn_cell_1/Tanh:0", shape=(500, 64), dtype=float32)
out_logit.shape = (500, 1), out_logit = Tensor("my_rnn/dense/BiasAdd:0", shape=(500, 1), dtype=float32)
out_prob.shape = (500, 1), out_prob = Tensor("my_rnn/Sigmoid:0", shape=(500, 1), dtype=float32), 


wordEmbeddings.shape = (500, 80, 100), wordEmbeddings = Tensor("my_rnn/embedding/embedding_lookup/Identity:0", shape=(500, 80, 100), dtype=float32)
wordEmbedding.shape = (500, 100), wordEmbedding = Tensor("my_rnn/unstack:0", shape=(500, 100), dtype=float32)
out01.shape = (500, 64), out01 = Tensor("my_rnn/simple_rnn_cell/Tanh:0", shape=(500, 64), dtype=float32)
out02.shape = (500, 64), out02 = Tensor("my_rnn/simple_rnn_cell_1/Tanh:0", shape=(500, 64), dtype=float32)
out_logit.shape = (500, 1), out_logit = Tensor("my_rnn/dense/BiasAdd:0", shape=(500, 1), dtype=float32)
out_prob.shape = (500, 1), out_prob = Tensor("my_rnn/Sigmoid:0", shape=(500, 1), dtype=float32), 

50/50 [==============================] - ETA: 0s - loss: 0.6942 - accuracy: 0.5303
wordEmbeddings.shape = (500, 80, 100), wordEmbeddings = Tensor("my_rnn/embedding/embedding_lookup/Identity:0", shape=(500, 80, 100), dtype=float32)
wordEmbedding.shape = (500, 100), wordEmbedding = Tensor("my_rnn/unstack:0", shape=(500, 100), dtype=float32)
out01.shape = (500, 64), out01 = Tensor("my_rnn/simple_rnn_cell/Tanh:0", shape=(500, 64), dtype=float32)
out02.shape = (500, 64), out02 = Tensor("my_rnn/simple_rnn_cell_1/Tanh:0", shape=(500, 64), dtype=float32)
out_logit.shape = (500, 1), out_logit = Tensor("my_rnn/dense/BiasAdd:0", shape=(500, 1), dtype=float32)
out_prob.shape = (500, 1), out_prob = Tensor("my_rnn/Sigmoid:0", shape=(500, 1), dtype=float32), 

50/50 [==============================] - 11s 125ms/step - loss: 0.6938 - accuracy: 0.5309 - val_loss: 0.5607 - val_accuracy: 0.7175
Epoch 2/4
50/50 [==============================] - 5s 98ms/step - loss: 0.4480 - accuracy: 0.7937 - val_loss: 0.4222 - val_accuracy: 0.8073
Epoch 3/4
50/50 [==============================] - 5s 99ms/step - loss: 0.2625 - accuracy: 0.8933 - val_loss: 0.4523 - val_accuracy: 0.8001
Epoch 4/4
50/50 [==============================] - 5s 98ms/step - loss: 0.1500 - accuracy: 0.9448 - val_loss: 0.5610 - val_accuracy: 0.8037
***********************************************************训练network:结束***********************************************************

***********************************************************评估network(其实训练时已经评估):开始***********************************************************
50/50 [==============================] - 1s 23ms/step - loss: 0.5610 - accuracy: 0.8037
***********************************************************评估network(其实训练时已经评估):结束***********************************************************
total time cost: 26.676692247390747

Process finished with exit code 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

6、Tensorflow2-SimpleRNN案例-imdb数据集【电影评论情感二分类】

import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import tensorflow as tf
import numpy as np
from tensorflow import keras
from tensorflow.keras import layers
import time

tf.random.set_seed(22)
np.random.seed(22)

assert tf.__version__.startswith('2.')

batch_size = 500  # 每次训练500个句子

total_words = 10000  # the most frequest words
max_review_len = 80  # 设置句子长度,如果有的句子的长度不到80则补齐,如果有的句子超过80则截断
embedding_len = 100  # 每个单词转为向量后的向量维度

# 一、获取数据集
(X_train, Y_train), (X_val, Y_val) = keras.datasets.imdb.load_data(num_words=total_words)
print('X_train[0] = {0},\nY_train[0] = {1}'.format(X_train[0], Y_train[0]))
print('X_train.shpae = {0},Y_train.shpae = {1}------------type(X_train) = {2},type(Y_train) = {3}'.format(X_train.shape, Y_train.shape, type(X_train), type(Y_train)))

# 二、数据处理
# 2.1 # 设置句子统一长度
X_train = keras.preprocessing.sequence.pad_sequences(X_train, maxlen=max_review_len)  # 设置句子长度    [b, 80]
X_val = keras.preprocessing.sequence.pad_sequences(X_val, maxlen=max_review_len)  # 设置句子长度
print('X_train.shpae = {0},Y_train.shpae = {1},tf.reduce_max(Y_train) = {2},tf.reduce_min(Y_train) = {3}'.format(X_train.shape, Y_train.shape, tf.reduce_max(Y_train), tf.reduce_min(Y_train)))
# 2.1 处理训练集为batch模型
db_train = tf.data.Dataset.from_tensor_slices((X_train, Y_train))
db_train = db_train.shuffle(1000).batch(batch_size, drop_remainder=True)  # 通过 drop_remainder=True 把 最后一个不满足batch_size大小的batch丢弃掉
db_val = tf.data.Dataset.from_tensor_slices((X_val, Y_val))
db_val = db_val.batch(batch_size, drop_remainder=True)  # 通过 drop_remainder=True 把 最后一个不满足batch_size大小的batch丢弃掉
print('db_train = {0},len(db_train) = {1}'.format(db_train, len(db_train)))


class MyRNN(keras.Model):
    def __init__(self, output_dim):
        super(MyRNN, self).__init__()
        # ***********************************************************Embedding***********************************************************
        # transform text to embedding representation
        # 将每一个句子(维度为[80,1],80表示每个句子包含的word数量,1表示1个word)变换为wordEmbedding(维度为[80,100],80表示每个句子包含的word数量,100表示每个wordEmbedding的维度)
        # [b, 80, 1] => [b, 80, 100]
        # input_dim:表示输入维度,即设定词库总单词数量;b
        # input_length:表示每个句子统一长度(包含的单词数量);80
        # output_dim:表示输出维度,即每个单词转为向量后的向量维度;100
        self.embedding = layers.Embedding(input_dim=total_words, input_length=max_review_len, output_dim=embedding_len)
        # ***********************************************************RNN神经网络结构:SimpleRNN 表示SimpleRNN连接层***********************************************************
        # [b, 80, 100]=>[b, 64]
        self.rnn = keras.Sequential([
            # output_dim: dimensionality of the output space. 隐藏状态的维度;dropout 防止过拟合
            # return_sequences: Boolean. Whether to return the last output in the output sequence, or the full sequence.
            # unroll: Boolean (default False). If True, the network will be unrolled, else a symbolic loop will be used.
            # Unrolling can speed-up a RNN, although it tends to be more memory-intensive. Unrolling is only suitable for short sequences.
            layers.SimpleRNN(output_dim, dropout=0.5, return_sequences=True, unroll=True),
            layers.SimpleRNN(output_dim, dropout=0.5, unroll=True)
        ])
        # ***********************************************************全连接层***********************************************************
        # [b, 64] => [b, 1]
        self.outlayer = layers.Dense(1)

    def call(self, inputs, training=None):
        """
        net(x) net(x, training=True) :train mode
        net(x, training=False): test
        :param inputs: [b, 80]
        :param training:
        :return:
        """
        # ***********************************************************Embedding***********************************************************
        # embedding: [b, 80, 1] => [b, 80, 100]
        x_wordEmbeddings = self.embedding(inputs)  # inputs 为1个batch的句子文本
        print('\nx_wordEmbeddings.shape = {0}, x_wordEmbeddings = {1}'.format(x_wordEmbeddings.shape, x_wordEmbeddings))
        # ***********************************************************RNN神经网络结构计算***********************************************************
        out = self.rnn(x_wordEmbeddings)    # x: [b, 80, 100] => [b, 64]
        print('out.shape = {0}, out = {1}'.format(out.shape, out))
        out_logit = self.outlayer(out)  # 隐含状态=>0/1   out: [b, 64] => [b, 1]
        print('out_logit.shape = {0}, out_logit = {1}'.format(out_logit.shape, out_logit))
        out_prob = tf.sigmoid(out_logit)    # p(y is pos|x)
        print('out_prob.shape = {0}, out_prob = {1}, {2}'.format(out_prob.shape, out_prob, '\n'))
        return out_prob


def main():
    output_dim = 64     # 设定输出的隐藏状态维度  [b, 100] => [b,64]
    epochs = 4
    t0 = time.time()
    network = MyRNN(output_dim)
    # 不需要设置from_logits=True,因为MyRNN()中已经设定了激活函数层 out_prob = tf.sigmoid(X)
    # metrics=['accuracy']表示打印测试数据
    network.compile(optimizer=keras.optimizers.Adam(0.001),
                  loss=tf.losses.BinaryCrossentropy(),
                  metrics=['accuracy'])
    print('\n***********************************************************训练network:开始***********************************************************')
    network.fit(db_train, epochs=epochs, validation_data=db_val)
    print('***********************************************************训练network:结束***********************************************************')
    print('\n***********************************************************评估network(其实训练时已经评估):开始***********************************************************')
    network.evaluate(db_val)  # 评估模型
    print('***********************************************************评估network(其实训练时已经评估):结束***********************************************************')
    t1 = time.time()
    print('total time cost:', t1 - t0)


if __name__ == '__main__':
    main()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107

打印结果:

X_train[0] = [1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32],
Y_train[0] = 1
X_train.shpae = (25000,),Y_train.shpae = (25000,)------------type(X_train) = <class 'numpy.ndarray'>type(Y_train) = <class 'numpy.ndarray'>
X_train.shpae = (25000, 80),Y_train.shpae = (25000,),tf.reduce_max(Y_train) = 1,tf.reduce_min(Y_train) = 0
db_train = <BatchDataset shapes: ((500, 80), (500,)), types: (tf.int32, tf.int64)>len(db_train) = 50

***********************************************************训练network:开始***********************************************************
Epoch 1/4

x_wordEmbeddings.shape = (500, 80, 100), x_wordEmbeddings = Tensor("my_rnn/embedding/embedding_lookup/Identity:0", shape=(500, 80, 100), dtype=float32)
out.shape = (500, 64), out = Tensor("my_rnn/sequential/simple_rnn_1/simple_rnn_cell_1/Tanh_79:0", shape=(500, 64), dtype=float32)
out_logit.shape = (500, 1), out_logit = Tensor("my_rnn/dense/BiasAdd:0", shape=(500, 1), dtype=float32)
out_prob.shape = (500, 1), out_prob = Tensor("my_rnn/Sigmoid:0", shape=(500, 1), dtype=float32), 


x_wordEmbeddings.shape = (500, 80, 100), x_wordEmbeddings = Tensor("my_rnn/embedding/embedding_lookup/Identity:0", shape=(500, 80, 100), dtype=float32)
out.shape = (500, 64), out = Tensor("my_rnn/sequential/simple_rnn_1/simple_rnn_cell_1/Tanh_79:0", shape=(500, 64), dtype=float32)
out_logit.shape = (500, 1), out_logit = Tensor("my_rnn/dense/BiasAdd:0", shape=(500, 1), dtype=float32)
out_prob.shape = (500, 1), out_prob = Tensor("my_rnn/Sigmoid:0", shape=(500, 1), dtype=float32), 

50/50 [==============================] - ETA: 0s - loss: 0.7086 - accuracy: 0.5031
x_wordEmbeddings.shape = (500, 80, 100), x_wordEmbeddings = Tensor("my_rnn/embedding/embedding_lookup/Identity:0", shape=(500, 80, 100), dtype=float32)
out.shape = (500, 64), out = Tensor("my_rnn/sequential/simple_rnn_1/simple_rnn_cell_1/Tanh_79:0", shape=(500, 64), dtype=float32)
out_logit.shape = (500, 1), out_logit = Tensor("my_rnn/dense/BiasAdd:0", shape=(500, 1), dtype=float32)
out_prob.shape = (500, 1), out_prob = Tensor("my_rnn/Sigmoid:0", shape=(500, 1), dtype=float32), 

50/50 [==============================] - 11s 129ms/step - loss: 0.7084 - accuracy: 0.5034 - val_loss: 0.6804 - val_accuracy: 0.5906
Epoch 2/4
50/50 [==============================] - 5s 94ms/step - loss: 0.6384 - accuracy: 0.6291 - val_loss: 0.4407 - val_accuracy: 0.7966
Epoch 3/4
50/50 [==============================] - 5s 95ms/step - loss: 0.4024 - accuracy: 0.8191 - val_loss: 0.4072 - val_accuracy: 0.8284
Epoch 4/4
50/50 [==============================] - 5s 94ms/step - loss: 0.2899 - accuracy: 0.8829 - val_loss: 0.4479 - val_accuracy: 0.8289
***********************************************************训练network:结束***********************************************************

***********************************************************评估network(其实训练时已经评估):开始***********************************************************
50/50 [==============================] - 1s 24ms/step - loss: 0.4479 - accuracy: 0.8289
***********************************************************评估network(其实训练时已经评估):结束***********************************************************
total time cost: 26.05630612373352

Process finished with exit code 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/627150
推荐阅读
相关标签
  

闽ICP备14008679号