【自然语言处理|RNN-01】:RNN模型

1. 认识RNN模型

1.1 RNN模型

循环神经网络RNN(Recurrent Neural Network,简称RNN)

一般以序列数据为输入, 通过网络内部的结构设计有效捕捉序列之间的关系特征, 一般也是以序列形式进行输出

  • RNN循环神经网络,可以用于捕捉序列特征

RNN模型的网络结构:
在这里插入图片描述
RNN为什么称作循环神经网络:
上一时间步的隐藏层输出作为下一个时间步的隐藏层输入

  • 以时间步对RNN进行展开后的单层网络结构
    在这里插入图片描述
  • RNN网络结构特点
    • 由输入层、隐藏层、输出层组成;上一时间步的隐藏层输出作为下一个时间步的隐藏层输入
    • 每个时间步的输入有2个:数据端输入,隐藏层输入
    • 每个时间步的输出有2个:数据端输出,隐藏层输出(当循环神经网络只有一个隐藏层的时候,时间步的输出output和隐藏层的输出一样,当隐藏的个数不是一的时候,时间步输出与最后一隐藏层的输出一样

1.2 RNN模型作用

  • RNN结构能够连续性的输入序列数据,进行特征提取。比如:人类的语言, 语音特别适合RNN进行处理。
  • RNN模型广泛应用于NLP领域的各项任务, 如文本分类, 情感分析, 意图识别, 机器翻译等。

举例:用户意图识别

  • 第一步: 用户输入“What time is it ?”,先进行分词。RNN是一个时间步一个时间步的输入数据, 每次只接收一个单词处理
  • 第二步: 首先将单词"What"输送给RNN, 它将产生一个输出O1
  • 第三步:继续将单词“time”输送给RNN, RNN不仅利用"time"来产生输出O2, 还使用上一层隐层输出O1作为输入信息,重复这样的步骤, 直到处理完所有的单词,用最后一个输出表示句子的特征。
    在这里插入图片描述
    第五步:最后,将最终的隐层输出O5进行处理来解析用户意图。
    在这里插入图片描述

1.3 RNN模型分类

按照输入和输出的角度进行分类

  • N vs N – RNN 输入N个序列,输出N个序列 写诗 写对联 固定场景表达
  • N vs 1 – RNN 输入N个序列,输出1个的值 情感分类 意图识别
  • 1 vs N – RNN 输入1个序列,输出N个的值 看图说话
  • N vs M – RNN 输入N个序列,输出M个序列 翻译、语音转换;文本生成、摘要

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

按照RNN的内部构造进行分类

  • 传统RNN
  • LSTM
  • Bi-LSTM
  • GRU
  • Bi-GRU

2. 传统RNN模型

2.1 RNN内部结构

在这里插入图片描述

  1. 每个时间步有2个输入:h(t-1)上一时间步的隐层输出, x(t)当前时间步的输入
  2. 进入RNN结构体后, 会"融合"到一起, 通过一个全连接层(线性层), 再使用tanh作为激活函数。经过一个线性层就会有一个权重参数矩阵。
  3. 每个时间步有2个输出:h(t)该时间步的隐藏层输出(它将作为下一个时间步的隐藏层输入),x(t+1) 当前时间步的输出
  4. 编程符号: input, output, ℎ0, ℎn

在这里插入图片描述

  • RNN内部计算公式 – 学术界论文
    在这里插入图片描述

相当于用矩阵参数对数据进行加权求和,再通过激活层添加非线性因素

  • pytorch框架 – 工业界实现
    在这里插入图片描述

相当于用隐藏层数据@内部隐藏层参数矩阵Whh +用时间步数据@内部参数矩阵Wih,加一起,再用tanh激活

在这里插入图片描述

  • 激活函数tanh的作用

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

2.2 RNN API简介

  • 在torch.nn工具包之中, 通过torch.nn.RNN可调用
import torch
import torch.nn as nn


def dm01_rnn_for_base():

    # 第一个参数:input_size,输入数据的尺寸,词向量的维度
    # 第二个参数:hidden_size 经过RNN之后词向量的维度
    # 第三个参数:layer_nums - RNN隐藏层的个数
    # RNN模型的创建只与词向量的维度或者是特征的维度有关
    rnn = nn.RNN(5,6,1)


if __name__ == '__main__':
    dm01_rnn_for_base()

2.3 探究RNN中数据形状的变化

举例:RNN提取文本特征需求

  • 数据:[3,1,5] 3个批次 每个批次1个单词 每个单词5个特征
  • 模型 :经过模型处理能变6个特征
  • 探究:9个参数中 主参数和辅助参数的关系

2.3.1 RNN API中的9个参数

def dm01_rnn_for_base():
    # 1 定义模型
    # 第1个参数input_size: 5 输入的数据特征数
    # 第2个参数hidden_size: 6 输出的特征数(输出的数据尺寸, 也代表了神经元的个数)
    # 第3个参数layer_nums: 1 隐藏层的个数
    rnn = nn.RNN(5, 6, 1)
    print('rnn-->', rnn)

    # 2 准备数据
    # 第1个参数seq_len: 数据长度
    # 第2个参数batch_size: 3代表批次数
    # 第3个参数input_size: 输入数据的尺寸
    # 可以认为是从dataloader中每次取出三个句子,每个句子长度为1,词向量的长度为5
    input = torch.randn(1, 3, 5)

    # 第1个参数layer_nums: 1 隐藏层的个数
    # 第2个参数batch_size: 3 数据的批次数
    # 第3个参数hidden_size: 6 输出的数据特征数
    h0 = torch.randn(1, 3, 6) # 3句话, 每句话有1个单词, 每个单词有6个特征

    # 3 给模型喂数据  h0 和 hn的数据形状一样
    # input[1,3,5],h0[1,3,6] ---> output[1,3,6 ], hn[1,3,6]
    output, hn = rnn(input, h0)

    # 4 打印结果
    print('output-->', output.shape)
    print('hn-->', hn.shape)
    print(output)

在这里插入图片描述

2.3.2 更改输入特征的维度(input_size)

  • 更改输入数据的词向量维度
def dm02_rnn_for_inputdim():
    # 1 定义模型
    # 第1个参数input_size: 55 输入的数据特征数
    # 第2个参数hidden_size: 6 输出的特征数(输出的数据尺寸, 也代表了神经元的个数)
    # 第3个参数lay_nums: 1 隐藏层的个数
    rnn = nn.RNN(55, 6, 1)  # A
    print('rnn-->', rnn)

    # 2 准备数据
    # 第1个参数seq_len: 数据长度
    # 第2个参数batch_size: 3代表批次数
    # 第3个参数input_size: 55输入数据的尺寸
    input = torch.randn(1, 3, 55)  # B

    # 第1个参数layer_nums: 1 隐藏层的个数
    # 第2个参数batch_size: 3 数据的批次数
    # 第3个参数hidden_size: 6 输出的数据特征数
    h0 = torch.randn(1, 3, 6) # C  3句话, 每句话有1个单词, 每个单词有6个特征

    # 3 给模型喂数据  h0 和 hn的数据形状一样
    # input[1,3,55],h0[1,3,6] ---> output[1,3,6 ], hn[1,3,6]
    output, hn = rnn(input, h0)

    # 4 打印结果
    print('output-->', output.shape)
    print('hn-->', hn.shape)
    # print(output)
  • 修改输入数据的特征数(input_size),会影响input中的输入数据特征维度
  • 不影响output和hn

在这里插入图片描述

2.3.3 更改输出数据特征数(hidden_size)

def dm022_rnn_for_outputdim():
	# hidden_size改为66,经过RNN之后,词向量特征会变为66
    rnn = nn.RNN(5, 66, 1) 
    print('rnn-->', rnn)

    input = torch.randn(1, 3, 5)
    h0 = torch.randn(1, 3, 66) # 3句话, 每句话有1个单词, 每个单词有66个特征
    
    # 3 给模型喂数据  h0 和 hn的数据形状一样
    # input[1,3,5],h0[1,3,66] ---> output[1,3,6 ], hn[1,3,66]
    output, hn = rnn(input, h0)

    # 4 打印结果
    print('output-->', output.shape)
    print('hn-->', hn.shape)
    # print(output)

在这里插入图片描述

2.3.4 更改文本序列长度(seq_len)

def dm03_rnn_for_sequencelen():

    rnn = nn.RNN(5, 6, 1)  # A
    print('rnn-->', rnn)
	
	# 每个句子的长度改为11
	# batch_size = 3 
    input = torch.randn(11, 3, 5) # B  3句话, 每句话有1个单词, 每个单词有6个特征

    h0 = torch.randn(1, 3, 6) # C

    # 3 给模型喂数据  h0 和 hn的数据形状一样
    # input[1,3,5],h0[1,3,6] ---> output[11,3,6 ], hn[1,3,6]
    output, hn = rnn(input, h0)

    # 4 打印结果
    print('output-->', output.shape)
    print('hn-->', hn.shape)
    # print(output)

在这里插入图片描述

2.3.5 更改批次数(batch_size)

def dm04_rnn_for_batchsize():
    rnn = nn.RNN(5, 6, 1)  # A
    print('rnn-->', rnn)

	# batch_size = 33
	# 每次送入33个样本
    input = torch.randn(1, 33, 5) # B 3句话, 每句话有1个单词, 每个单词有6个特征

    h0 = torch.randn(1, 33, 6) # C

    # 3 给模型喂数据  h0 和 hn的数据形状一样
    # input[1,33,5],h0[1,33,6] ---> output[1,33,6], hn[1,33,6]
    output, hn = rnn(input, h0)

    # 4 打印结果
    print('output-->', output.shape)
    print('hn-->', hn.shape)
    # print(output)

在这里插入图片描述

2.3.6 更改隐藏层的层数(layer_nums)

def dm05_rnn_for_hiddennum():
    # 1 定义模型
    # 第1个参数: 5 输入的数据特征数
    # 第2个参数: 6 输出的特征数(输出的数据尺寸, 也代表了神经元的个数)
    # 第3个参数: 2 隐藏层的个数
    rnn = nn.RNN(5, 6, 2)  # A
    print('rnn-->', rnn)

    # 2 准备数据
    # 第1个参数: 数据长度
    # 第2个参数: 3代表批次数
    # 第3个参数: 输入数据的尺寸
    input = torch.randn(1, 3, 5) # B 3句话, 每句话有1个单词, 每个单词有6个特征

    # 第1个参数: 1 隐藏层的个数
    # 第2个参数: 3 数据的批次数
    # 第3个参数: 6 输出的数据特征数
    h0 = torch.randn(2, 3, 6) # C

    # 3 给模型喂数据  h0 和 hn的数据形状一样
    # input[1,3,5],h0[2,3,6] ---> output[1,3,6 ], hn[2,3,6]
    output, hn = rnn(input, h0)

    # 4 打印结果
    print('output-->', output.shape, '\n', output)
    print('hn-->', hn.shape, '\n', hn)
    # print(output)

    # 当隐藏层的个数是1时, hn 和 ouput长得一样
    # 当隐藏层的个数是2时, hn 和 ouput?

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

2.3.7 调整参数顺序

  • 默认情况下,RNN模型的input形状为(seq_len,batch_size,input_size)、output的形状为(seq_len,batch_size,hidden_size),这样填写参数的时候需要注意
  • 创建RNN模型的时候,将batch_first设置为True,就可以将input的形状调整为(batch_size,seq_len,input_size),output的形状调整为(batch_size,seq_len,hidden_size)
  • batch_first=True 参数只是对 input 和 output数据影响,不对 h0、hn影响
def dm06_rnn_for_batch_first():
    # 1 定义模型
    # 第1个参数: 5 输入的数据特征数
    # 第2个参数: 6 输出的特征数(输出的数据尺寸, 也代表了神经元的个数)
    # 第3个参数: 1 隐藏层的个数
    rnn = nn.RNN(5, 6, 1, batch_first=True)  # A
    print('rnn-->', rnn)

    # 2 准备数据
    # 第1个参数: 数据长度
    # 第2个参数: 3代表批次数
    # 第3个参数: 输入数据的尺寸
    input = torch.randn(3, 1, 5) # B 3句话, 每句话有1个单词, 每个单词有6个特征

    # 第1个参数: 1 隐藏层的个数
    # 第2个参数: 3 数据的批次数
    # 第3个参数: 6 输出的数据特征数
    h0 = torch.randn(1, 3, 6) # C

    # 3 给模型喂数据  h0 和 hn的数据形状一样
    # input[3,1,5],h0[1,3,6] ---> output[3,1,6 ], hn[1,3,6]
    output, hn = rnn(input, h0)

    # 4 打印结果
    print('output-->', output.shape)
    print('hn-->', hn.shape)
    # print(output)

2.4 输入RNN模型的两种方式

  • 方式1:一个字符一个字符的给模型喂数据 : 程序员需要一个时间步一个时间步的把 hn迭代起来
  • 方式2: 一下子把10个字符送给模型 : rnn模型内部会一次性的把运算结果返回给调用者,模型内部会把hn迭代起来
  • 两种输入方式最终得到的结果是一样的
def dm07_rnn_for_multinum():

    # 1 构建模型 rnn
    rnn = nn.RNN(input_size=5, hidden_size=6, num_layers=1, batch_first=True)
    print('rnn-->', rnn)

    # 2 准备数据 input, hidden
    # batch_size = 1
    # seq_len = 10  - 一个样本有10个词,每个词5个特征
    input = torch.randn(1, 10, 5)
    hidden = torch.zeros(1, 1, 6)

    # 3 方式1 一个字符一个字符的送数据
    for i in range(input.shape[1]):
        tmp = input[0][i] # input[0] - (10,5)  input[0][i] 得到第i个词向量
        print('tmp-->', tmp.shape) # [5] ,需要升维之后送入到rnn中
        output, hidden = rnn(tmp.unsqueeze(0).unsqueeze(0), hidden)
        print(i+1, '-->', output)
        pass

    # 4 方式2:一次性给模型喂数据
    hidden = torch.zeros(1, 1, 6)
    output, hidden = rnn(input, hidden)
    print('output-->', output)

    #  input = torch.randn(1, 10, 5)
    #  hidden = torch.zeros(1, 1, 6)

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

2.5 RNN模型的优缺点

  • RNN网络的优点
    • 内部结构简单,对计算资源要求低
    • 在短序列任务上性能和效果都表现优异
  • RNN网络缺点
    • 长序列文本特征提取效果差
      • 过长的序列导致梯度的计算异常,发生梯度消失或爆炸

3 RNN模型注意事项

  1. N vs M 类型的RNN模型是seq2seq架构的基础
  2. 熟练掌握RNN模型的标志,能够熟练分析数据在RNN中的形状变化
  3. RNN模型API中的9个参数,主要参数有5个,辅助参数有四个
    在这里插入图片描述
  4. 有关隐藏层个数理解,output的输出与最后一个隐藏层的输出一样。每个隐藏层都有权重参数,每个隐藏的不同时间步共享权重参数
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值