深度学习系列笔记09循环卷积神经网络RNN

编程入门 行业动态 更新时间:2024-10-11 11:24:52

深度学习系列笔记09循环<a href=https://www.elefans.com/category/jswz/34/1765938.html style=卷积神经网络RNN"/>

深度学习系列笔记09循环卷积神经网络RNN

文章目录

  • 1. 序列数据与模型
    • 1.1 序列数据
    • 1.2 序列模型——自回归模型和隐变量自回归模型
    • 1.3 序列模型——隐马尔可夫模型
    • 1.4 因果关系
    • 1.5 小结
  • 2. 文本预处理
    • 2.1 读取数据集
    • 2.2 词元化
    • 2.3 建立词表
    • 2.4 小结
  • 3. 循环神经网络
    • 3.1 RNN简介
    • 3.2 RNN模型的分类
    • 3.3 传统的RNN模型
      • 3.3.1 内部结构分析:
      • 3.3.2 数据流动过程:
      • 3.3.3 公式:
      • 3.3.4 实验代码:
      • 3.3.5 优缺点:
    • 3.4 LSTM 长短期记忆网络
      • 3.4.1 内部结构分析:
      • 3.4.2 数据流动过程:
      • 3.4.3 实验代码:
      • 3.4.4 优缺点:
    • 3.5 GRU 门控循环单元
      • 3.5.1 内部结构分析:
      • 3.5.3 实验代码:
      • 3.5.4 优缺点:

1. 序列数据与模型

1.1 序列数据

  • 现实生活中很多数据都是有时序结构的,比如电影的评分(既不是固定的也不是随机的,会随着时间的变化而变化)
  • 在统计学中,对超出已知观测范围进行预测称为外推法(extrapolation),在现有的观测值之间进行估计称为内插法(interpolation)

处理序列数据应选用新的深度神经网络架构RNN。
这里我们所使用的数据发生了变化,包含了一个时间维度。
并且不同于之前的图像分类与识别,这里我们的数据之间是具有相关性的。

1.2 序列模型——自回归模型和隐变量自回归模型

1、自回归模型(autoregressive models)

  • 假设在现实情况下相当长的序列 x(t-1) , … , x1 可能是不必要的,因此只需要考虑满足某个长度为 τ 的时间跨度即可,即使用观测序列 x(t-1) , … , x(t-τ)
  • 这样做的好处是保证了在 t > τ 时,参数的数量是固定的
  • 之所以叫做自回归模型是因为该模型是对自己执行回归

2、隐变量自回归模型(latent autoregressive models)

  • 隐变量自回归模型中保留了对过去观测的总结 ht ,并且同时更新预测 xt_hat 和总结 ht
  • xt_hat = P( xt_hat | ht ) ; ht = g( h(t - 1) , x(t - 1) )
  • 因为 ht 从未被观测到,所以这类模型也被称为隐变量自回归模型

1.3 序列模型——隐马尔可夫模型

自回归模型的近似法中,使用 x(t-1) , … , x(t-τ),而不是 x(t-1) , … , x1 来估计 xt ,如果这种假设是成立的,也就是近似是精确的,可以称为满足马尔科夫条件( Markov condition )

如果 t=1,得到一个 一阶马尔可夫模型(first-order Markov model)


  • 假设当前数据只跟 τ 个过去数据点相关(每一次预测一个新的数据,只需要看过去 τ 个数据就可以了,τ 可以自由选择,τ 越小模型越简单,τ 越大模型越复杂),这样假设的好处是 τ 的值是固定的,不会随着时间的增大而增大(这样做也比较符合现实的逻辑,过去事件距离预测事件的时间越长,他们之间的关联程度就越小)

1.4 因果关系

1.5 小结

2. 文本预处理

文本预处理的核心思想是如何将文本中的词转化成能够训练的样本

本节中,我们将解析文本的常见预处理步骤。 这些步骤通常包括:

  • 1.将文本作为字符串加载到内存中
  • 2.将字符串拆分为词元(如单词和字符)
  • 3.建立一个词表,将拆分的词元映射到数字索引。
  • 4.将文本转换为数字索引序列,方便模型操作

2.1 读取数据集

import collections
import re
from d2l import torch as d2l
d2l.DATA_HUB['time_machine'] = (d2l.DATA_URL + 'timemachine.txt', '090b5e7e70c295757f55df93cb0a180b9691891a')def read_time_machine(): """将数据集加载到文本行的列表中"""with open(d2l.download('time_machine'), 'r') as f:lines = f.readlines()return [re.sub('[^A-Za-z]+', ' ', line).strip().lower() for line in lines]"""去掉回车 将所有字母全部变成小写"""lines = read_time_machine()

2.2 词元化

每个文本序列又被拆分成一个词元列表,词元(token)是文本的基本单位。 最后,返回一个由词元列表组成的列表,其中的每个词元都是一个字符串(string)。

tokenize 是 NLP 中一个比较常见的操作:将一个句子或者是一段文字转化成 token(字符串、字符或者是词)

def tokenize(lines, token='word'):  #将文本行列表(lines)作为输入"""将文本行拆分为单词或字符词元"""if token == 'word':return [line.split() for line in lines]elif token == 'char':return [list(line) for line in lines]else:print('错误:未知词元类型:' + token)tokens = tokenize(lines)

2.3 建立词表

词元的类型是字符串,而模型需要的输入是数字,因此这种类型不方便模型使用。 现在,让我们构建一个字典,通常也叫做词表(vocabulary), 用来将字符串类型的词元映射到从开始的数字索引中。
将拆分的词元映射到数字索引:将文本转换为数字索引序列,方便模型操作。

class Vocab:  """文本词表"""def __init__(self, tokens=None, min_freq=0, reserved_tokens=None):if tokens is None:tokens = []if reserved_tokens is None:reserved_tokens = []# 按出现频率排序counter = count_corpus(tokens)self._token_freqs = sorted(counter.items(), key=lambda x: x[1],reverse=True)# 未知词元的索引为0self.idx_to_token = ['<unk>'] + reserved_tokensself.token_to_idx = {token: idxfor idx, token in enumerate(self.idx_to_token)}for token, freq in self._token_freqs:if freq < min_freq:breakif token not in self.token_to_idx:self.idx_to_token.append(token)self.token_to_idx[token] = len(self.idx_to_token) - 1def __len__(self):return len(self.idx_to_token)def __getitem__(self, tokens):if not isinstance(tokens, (list, tuple)):return self.token_to_idx.get(tokens, self.unk)return [self.__getitem__(token) for token in tokens]def to_tokens(self, indices):if not isinstance(indices, (list, tuple)):return self.idx_to_token[indices]return [self.idx_to_token[index] for index in indices]@propertydef unk(self):  # 未知词元的索引为0return 0@propertydef token_freqs(self):return self._token_freqsdef count_corpus(tokens):  #@save"""统计词元的频率"""# 这里的tokens是1D列表或2D列表if len(tokens) == 0 or isinstance(tokens[0], list):# 将词元列表展平成一个列表tokens = [token for line in tokens for token in line]return collections.Counter(tokens)

2.4 小结

  • 文本是序列数据的一种最常见的形式之一。
  • 为了对文本进行预处理,我们通常将文本拆分为词元,构建词表,将词元字符串映射为数字索引,并将文本数据转换为词元索引以供模型操作。

3. 循环神经网络

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

3.1 RNN简介

  • RNN的循环机制使模型隐层上一时间步产生的结果,能够作为当下时间步输入的一部分(当下时间步的输入除了正常的输入外还包括上一步的隐层输出)对当下时间步的输出产生影响。
  • RNN模型的作用:
    因为RNN结构能够很好利用序列之间的关系,因此针对自然界具有连续性的输入序列,如人类的语言,语音等进行很好的处理广泛应用于NLP领域的各项任务,如文本分类情感分析,意图识别,机器翻译等。

例如:给机器输入 What time is it?
经过4次循环,分析最终的输出O5来判断用户输入的信息。

3.2 RNN模型的分类

① 按照输入和输出结构进行分类

  • N vs N - RNN
  • N vs 1 - RNN
  • 1 vs N - RNN
  • N vs M - RNN

N vs N - RNN
它是RNN最基础的结构形式,最大的特点就是:输入和输出序列是等长的。由于这个限制的存在,使其适用范围比较小,可用于生成等长度的合辙诗句。
N vs 1 - RNN
有时候我们要处理的问题输入是一个序列,而要求输出是一个单独的值而不是序列,我们只要在最后一个隐层输出h,上进行线性变换就可以了,大部分情况下,为了更好的明确结果,还要使用sigmold或者softmax进行处理。这种结构经常被应用在文本分类问题上。

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

N vs M - RNN
这是一种不限输入输出长度的RNN结构,它由编码器和解码器两部分组成。两者的内部结构都是某类RNN,它也被称为seq2seq架构。输入数据首先通过编码器,最终输出一个隐含变量c,之后最常用的做法是使用这个隐含变量c作用在解码器进行解码的每一步上,以保证输入信息被有效利用。

② 按照RNN内部结构进行分类

  • 传统RNN
  • LSTM
  • Bi - LSTM (双向 长短期记忆网络)
  • GRU
  • Bi - GRU

下面根据此分类方法一一介绍。

3.3 传统的RNN模型

3.3.1 内部结构分析:

3.3.2 数据流动过程:

3.3.3 公式:


激活函数tanh的作用:用于帮助调节流经网络的值,tanh函数将值压缩在-1和1之间.

3.3.4 实验代码:

#导入若干工具包
import torch
import torch.nn as nn
#实例化rnn对象
#第一个参数: input_size(输入张量x的维度)
#第二个参数: hidden_size(隐藏层的维度,隐藏层神经元数量)
#第三个参数: num_.layers(隐藏层的层数)
rnn = nn.RNN(5, 6, 1)
#设定输入的张量x
#第一个参数: sequence_.length(输入序列的长度)
#第二个参数: batch_size (批次的样本数)
#第三个参数: input_size(输入张量x的维度)
input1 = torch.randn(1, 3, 5)
#设定初始化的h0
#第一个参数: num_layers * num_ directions (层数*网络方向数)
#第二个参数: batch_size (批次的样本数)
#第三个参数: hidden_size(隐藏层的维度)
h0 = torch.randn(1, 3, 6)
#输入张量放入RNN 得到输出结果
output, hn = rnn(input1, h0)
print(output)
print(output.shape)
print(hn)
print(hn.shape)
-----------------------------------------
tensor([[[ 0.2020,  0.3738,  0.8060, -0.6857, -0.6111,  0.6123],[-0.9363,  0.3544, -0.2019,  0.8183, -0.1817, -0.6506],[-0.6587,  0.6482, -0.8166, -0.5486, -0.0163,  0.7191]]],grad_fn=<StackBackward0>)
torch.Size([1, 3, 6])
tensor([[[ 0.2020,  0.3738,  0.8060, -0.6857, -0.6111,  0.6123],[-0.9363,  0.3544, -0.2019,  0.8183, -0.1817, -0.6506],[-0.6587,  0.6482, -0.8166, -0.5486, -0.0163,  0.7191]]],grad_fn=<StackBackward0>)
torch.Size([1, 3, 6])

3.3.5 优缺点:

  • 传统RNN的优势:
    由于内部结构简单,对计算资源要求低,相比之后我们要学习的RNN变体:LSTM和GRU模型参数总量少了很多,在短序列任务上性能和效果都表现优异。
  • 传统RNN的缺点:
    传统RNN在解决长序列之间的关联时,通过实践,证明经典RNN表现很差,原因是在进行反向传播的时候,过长的序列导致梯度的计算异常,发生梯度消失或爆炸

3.4 LSTM 长短期记忆网络

3.4.1 内部结构分析:

LSTM (Long Short-Term Memory)也称长短期记忆结构,它是传统RNN的变体,与经典RNN相比能够有效捕捉长序列之间的语义关联缓解梯度消失或爆炸现象。同时LSTM的结构更复杂,它的核心结构可以分为四个部分去解析:遗忘门、输入门、细胞状态、输出门

  • 遗忘门:代表遗忘过去多少的信息

  • 输入门

  • 细胞状态更新图

  • 输出门

3.4.2 数据流动过程:

  • 遗忘门
  • 输入门

  • 细胞状态更新图
  • 输出门

3.4.3 实验代码:

#导入若干工具包
import torch
import torch.nn as nn
#实例化LSTM对象
#第一个参数: input_size(输入张量x的维度)
#第二个参数: hidden_size(隐藏层的维度, 隐藏层的神经元数量)
#第三个参数: num_layers (隐藏层的层数)
lstm = nn.LSTM(5, 6, 2)
#初始化输入张量x
#第一个参数: sequence_length(输入序列的长度)
#第二个参数: batch_size(批次的样本数量)
#第三个参数: input_size(输入张量x的维度)
input1 = torch. randn(1, 3, 5)
#初始化隐藏层张量h0,和细胞状态c0
#第一个参数: num_layers * num_directions (隐藏层的层数*方向数.
#第二个参数: batch_size (批次的样本数量)
#第三个参数: hidden_size(隐藏层的维度)
h0 = torch. randn(2, 3, 6)
c0 = torch. randn(2, 3, 6)
#将inputI, h0, c0输入lstm中, 得到输出张量结果
output, (hn, cn) = lstm(input1, (h0, c0))
print (output)
print (output.shape)
print (hn)
print (hn.shape)
print(cn)
print (cn.shape)
---------------------------------------
tensor([[[-0.0356,  0.1013, -0.4488, -0.2720, -0.0605, -0.2809],[-0.0743,  0.3319,  0.1953,  0.3076, -0.4295,  0.0784],[-0.2240,  0.1658,  0.1031,  0.3426, -0.2790,  0.3442]]],grad_fn=<StackBackward0>)
torch.Size([1, 3, 6])
tensor([[[ 0.1035,  0.0796, -0.0350,  0.3091, -0.0084, -0.0795],[ 0.1013,  0.4979, -0.3049,  0.3802,  0.2845, -0.1771],[ 0.0804, -0.2093, -0.0581, -0.3859,  0.3678, -0.2731]],[[-0.0356,  0.1013, -0.4488, -0.2720, -0.0605, -0.2809],[-0.0743,  0.3319,  0.1953,  0.3076, -0.4295,  0.0784],[-0.2240,  0.1658,  0.1031,  0.3426, -0.2790,  0.3442]]],grad_fn=<StackBackward0>)
torch.Size([2, 3, 6])
tensor([[[ 0.1972,  0.1682, -0.0902,  0.9651, -0.0115, -0.1569],[ 0.1968,  1.4286, -0.5794,  0.9468,  0.7288, -0.3405],[ 0.2432, -1.5347, -0.1129, -1.4662,  0.5249, -0.6214]],[[-0.0889,  0.4005, -1.2702, -0.5516, -0.0938, -0.6681],[-0.1985,  0.6989,  0.4673,  1.0849, -0.7235,  0.2078],[-0.4790,  0.4915,  0.3270,  0.6981, -0.6362,  0.6638]]],grad_fn=<StackBackward0>)
torch.Size([2, 3, 6])

3.4.4 优缺点:

  • LSTM优势
    LSTM的结构能够有效减缓长序列问题中可能出现的梯度消失或爆炸,虽然并不能杜绝这种现象,但在更长的序列问题上表现优于传统RNN。
  • LSTM缺点:
    由于内部结构相对较复杂,因此训练效率在同等算力下较传统RNN低很多。

3.5 GRU 门控循环单元

GRU (Gated Recurrent Unit) 也称控循环单元结构。它也是传统RNN的变体,同LSTM一样能够有效捕捉长序列之间的语义关联,缓解梯度消失或爆炸现象。同时它的结构和计算要比LSTM更简单,它的核心结构可以分为两个部分去解析:更新门、重置门。

3.5.1 内部结构分析:


3.5.3 实验代码:

#实例化GRU对象
#第一个参数: input_size(输入张量x的维度)
#第二个参数: hidden_size(隐藏层的维度, 隐藏层神经元的数量)
#第三个参数: num_layers (隐藏层的层数)
gru = nn. GRU(5, 6, 2)
#初始化输入张量input1
#第一个参数: sequence_.length(序列的长度)
#第二个参数: batch_size(批次的样本个数)
#第三个参数: input_size(输入张量x的维度)
input1 = torch.randn(1, 3, 5)
#初始化隐藏层的张量h0
#第一个参数: num_layers * num_ _di rections (隐藏层的层数*方向数)
#第二个参数: batch_size(批次的样本个数)
#第三个参数: hidden_size(隐藏层的维度)
h0 = torch. randn(2, 3, 6)
#将input1, h0输入GRU中, 得到输出张量结果
output, hn = gru(input1, h0)
print (output)
print (output. shape)
print (hn)
print (hn. shape )

3.5.4 优缺点:

  • GRU的优势:
    GRU和LSTM作用相同,在捕捉长序列语义关联时,能有效抑制梯度消失或爆炸,效果都优于传统RNN且计算复杂度相比LSTM要小。
  • GRU的缺点:
    GRU仍然不能完全解决梯度消失问题,同时其作用RNN的变体,有着RNN结构本身的一大弊
    端,即不可并行计算,这在数据量和模型体量逐步增大的未来,是RNN发展的关键瓶颈。

更多推荐

深度学习系列笔记09循环卷积神经网络RNN

本文发布于:2024-03-12 18:46:53,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1732169.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:卷积   神经网络   深度   笔记   系列

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!