循环神经网络(RNN)简述

embedded/2024/12/27 7:22:42/

RNN及其变体

1、概述

(一)、概念

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

RNN的循环机制使模型隐层**上一时间步产生的结果, 能够作为当下时间步输入的一部分(**当下时间步的输入除了正常的输入外还包括上一步的隐层输出)对当下时间步的输出产生影响。

(二)、作用

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

(三)、分类

这里我们将从两个角度对RNN模型进行分类. 第一个角度是输入和输出的结构, 第二个角度是RNN的内部构造.

  • 按照输入和输出的结构进行分类:
    • N vs N - RNN
    • N vs 1 - RNN
    • 1 vs N - RNN
    • N vs M - RNN
  • 按照RNN的内部构造进行分类:
    • 传统RNN
    • LSTM
    • Bi-LSTM
    • GRU
    • Bi-GRU
(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模型结构. 在机器翻译, 阅读理解, 文本摘要等众多领域都进行了非常多的应用实践

在这里插入图片描述

2、传统RNN模型

(一)、概述

在这里插入图片描述

(1)、内部结构分析:

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

(2)、根据结构分析得出内部计算公式:

h t = t a n h ( W t X t + U i h t − 1 + b i ) h_t=tanh(W_tX_t+U_ih_{t-1}+b_i) ht=tanh(WtXt+Uiht1+bi)

(3)、激活函数tanh的作用
  • 为系统增加非线性元素
  • 加速算法收敛速度

(二)、代码实现

import torch
import torch.nn as nndef  dm_rnn_for_base():'''第一个参数:input_size(输入张量x的维度)第二个参数:hidden_size(隐藏层的维度, 隐藏层的神经元个数)第三个参数:num_layer(隐藏层的数量)'''rnn = nn.RNN(5, 6, 1) #A'''第一个参数:sequence_length(输入序列的长度)第二个参数:batch_size(批次的样本数量)第三个参数:input_size(输入张量的维度)'''input = torch.randn(1, 3, 5) #B'''第一个参数:num_layer * num_directions(层数*网络方向)第二个参数:batch_size(批次的样本数)第三个参数:hidden_size(隐藏层的维度, 隐藏层神经元的个数)'''h0 = torch.randn(1, 3, 6) #C# [1,3,5],[1,3,6] ---> [1,3,6],[1,3,6]output, hn = rnn(input, h0)print('output--->',output.shape, output)print('hn--->',hn.shape, hn)print('rnn模型--->', rnn)

(三)、优缺点

  • 优点

内部结构简单, 对计算资源要求低, 相比RNN变体:LSTM和GRU模型参数总量少了很多, 在短序列任务上性能和效果都表现优异

  • 缺点

传统RNN在解决长序列之间的关联时, 通过实践,证明经典RNN表现很差, 原因是在进行反向传播的时候, 过长的序列导致梯度的计算异常, 发生梯度消失或爆炸

为何会出现梯度消失或爆炸:

根据反向传播算法和链式法则, 梯度的计算可以简化为以下公式:
D n = σ ′ ( z 1 ) w 1 ⋅ σ ′ ( z 2 ) w 2 ⋅ ⋯ ⋅ σ ′ ( z n ) w n D_n=σ′(z_1)w_1⋅σ′(z_2)w_2⋅⋯⋅σ′(z_n)w_n Dn=σ(z1)w1σ(z2)w2σ(zn)wn
其中sigmoid的导数值域是固定的, 在[0, 0.25]之间

一旦公式中的w也小于1, 那么通过这样的公式连乘后, 最终的梯度就会变得非常非常小, 这种现象称作梯度消失.

反之, 如果我们人为的增大w的值, 使其大于1, 那么连乘够就可能造成梯度过大, 称作梯度爆炸

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

3、LSTM模型

(一)、概述

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

  • 遗忘门
  • 输入门
  • 细胞状态
  • 输出门

(二)、结构分析

在这里插入图片描述

(1)、遗忘门

在这里插入图片描述

计算公式:
f t = σ ( W f x t + U f h t − 1 + b f ) f_t=σ(W_fx_t+U_fh_{t-1}+b_f) ft=σ(Wfxt+Ufht1+bf)
与传统的RNN内部结构非常相似,输入上一层的隐藏层和本次的数据,对数据进行拼接后输入到sigmoid函数内,对其激活得到ft。遗忘门的门值将作用于上一层的细胞状态上,代表遗忘过去多少信息。又因为遗忘门门值是由x(t), h(t-1)计算得来的, 因此整个公式意味着根据当前时间步输入和上一个时间步隐含状态h(t-1)来决定遗忘多少上一层的细胞状态所携带的过往信息。过滤上一个隐藏状态的冗余信息。

sigmoid函数的作用:

增加非线性结构,加快收敛。

(2)、输入门

在这里插入图片描述

计算公式:
i t = σ ( W i x t + U i h t − 1 + b i ) C ^ i = t a n h ( W c x t + U c h t − 1 + b c ) i_t = \sigma(W_ix_t+U_ih_{t-1}+b_i)\\ \hat{C}_i=tanh(W_cx_t+U_ch_{t-1}+b_c) it=σ(Wixt+Uiht1+bi)C^i=tanh(Wcxt+Ucht1+bc)
第一个就是产生输入门门值的公式, 它和遗忘门公式几乎相同, 区别只是在于它们之后要作用的目标上. 这个公式意味着输入信息有多少需要进行过滤。

第二个公式是与传统RNN的内部结构计算相同. 对于LSTM来讲, 它得到的是当前的细胞状态, 而不是像经典RNN一样得到的是隐含状态。

过滤当前时间步生成的细胞状态。

(3)、细胞状态

在这里插入图片描述

计算公式:
C t = f t ∗ C t − 1 + i t ∗ C ^ t 哈达玛积 C_t=f_t*C_{t-1}+i_t*\hat{C}_t\\哈达玛积 Ct=ftCt1+itC^t哈达玛积
将刚刚得到的遗忘门门值与上一个时间步得到的C(t-1)相乘, 再加上输入门门值与当前时间步得到的未更新C(t)相乘的结果. 最终得到更新后的C(t)作为下一个时间步输入的一部分. 整个细胞状态更新过程就是对遗忘门和输入门的应用。

(4)、输出门

在这里插入图片描述

O t = σ ( W o x t + U 0 h t − 1 + b o ) h t = O t ∗ t a n h ( C t ) O_t=\sigma(W_ox_t+U_0h_{t-1}+b_o)\\ h_t =O_t*tanh(C_t) Ot=σ(Woxt+U0ht1+bo)ht=Ottanh(Ct)

(三)、Bi_LSTM介绍

Bi-LSTM即双向LSTM, 它没有改变LSTM本身任何的内部结构, 只是将LSTM应用两次且方向不同, 再将两次得到的LSTM结果进行拼接作为最终输出。也就是将下面一层的output作为上一层的xt输入。

在这里插入图片描述

我们看到图中对"我爱中国"这句话或者叫这个输入序列, 进行了从左到右和从右到左两次LSTM处理, 将得到的结果张量进行了拼接作为最终输出. 这种结构能够捕捉语言语法中一些特定的前置或后置特征, 增强语义关联,但是模型参数和计算复杂度也随之增加了一倍, 一般需要对语料和计算资源进行评估后决定是否使用该结构。

(四)、实现

API:

nn.LSTM(input_size, hidden_size, num_layers, batch_first, bidirectional)
'''
input_size: 输入张量x中特征维度的大小.
hidden_size: 隐层张量h中特征维度的大小.
num_layers: 隐含层的数量.
batch_first:设置为True后,输入input第一位为batch_size,第二位为每句长度
bidirectional: 是否选择使用双向LSTM, 如果为True, 则使用; 默认不使用
'''my_LSTM = nn.LSTM(input_size, hidden_size, num_layers, batch_first, bidirectional)
output, (hn, cn) = my_LSTM(input, (h0, c0))
'''
input: 输入张量x.
h0: 初始化的隐层张量h.
c0: 初始化的细胞状态张量c
'''

例子:

# 定义LSTM的参数含义: (input_size, hidden_size, num_layers)
# 定义输入张量的参数含义: (sequence_length, batch_size, input_size)
# 定义隐藏层初始张量和细胞初始状态张量的参数含义:
# (num_layers * num_directions, batch_size, hidden_size)import torch.nn as nn
import torch
rnn = nn.LSTM(5, 6, 2)
input = torch.randn(1, 3, 5)
h0 = torch.randn(2, 3, 6)
c0 = torch.randn(2, 3, 6)
output, (hn, cn) = rnn(input, (h0, c0))

结果:

tensor([[[ 0.0447, -0.0335,  0.1454,  0.0438,  0.0865,  0.0416],[ 0.0105,  0.1923,  0.5507, -0.1742,  0.1569, -0.0548],[-0.1186,  0.1835, -0.0022, -0.1388, -0.0877, -0.4007]]],grad_fn=<StackBackward>)
>>> hn
tensor([[[ 0.4647, -0.2364,  0.0645, -0.3996, -0.0500, -0.0152],[ 0.3852,  0.0704,  0.2103, -0.2524,  0.0243,  0.0477],[ 0.2571,  0.0608,  0.2322,  0.1815, -0.0513, -0.0291]],[[ 0.0447, -0.0335,  0.1454,  0.0438,  0.0865,  0.0416],[ 0.0105,  0.1923,  0.5507, -0.1742,  0.1569, -0.0548],[-0.1186,  0.1835, -0.0022, -0.1388, -0.0877, -0.4007]]],grad_fn=<StackBackward>)
>>> cn
tensor([[[ 0.8083, -0.5500,  0.1009, -0.5806, -0.0668, -0.1161],[ 0.7438,  0.0957,  0.5509, -0.7725,  0.0824,  0.0626],[ 0.3131,  0.0920,  0.8359,  0.9187, -0.4826, -0.0717]],[[ 0.1240, -0.0526,  0.3035,  0.1099,  0.5915,  0.0828],[ 0.0203,  0.8367,  0.9832, -0.4454,  0.3917, -0.1983],[-0.2976,  0.7764, -0.0074, -0.1965, -0.1343, -0.6683]]],grad_fn=<StackBackward>)

(五)、优缺点

优点:

LSTM的门结构能够有效减缓长序列问题中可能出现的梯度消失或爆炸, 虽然并不能杜绝这种现象, 但在更长的序列问题上表现优于传统RNN

缺点:

内部结构相对较复杂, 因此训练效率在同等算力下较传统RNN低很多

4、GRU模型

(一)、概述

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

  • 更新门
  • 重置门

(二)、结构分析

在这里插入图片描述

在这里插入图片描述

(1)、更新门

计算公式:
z t = σ ( W z ∗ h t − 1 + U z ∗ x t ) z_t=\sigma(W_z*h_{t-1}+U_z*x_t) zt=σ(Wzht1+Uzxt)

(2)、重置门

计算公式:
r t = σ ( W r ∗ h t − 1 + U r ∗ x t ) r_t=\sigma(W_r*h_{t-1}+U_r*x_t) rt=σ(Wrht1+Urxt)

(3)、总体流程

将上一层的隐藏状态h(t-1)和现在的输入x(t)先组合然后通过sigmoid函数激活,获得重置门rt,同样的方式获得更新门zt。

rt和ht-1进行哈达玛积后与xt一起经过权重矩阵后,再用tanh函数激活,获得未更新的ht
h ^ t = t a n h ( W h ( r t ∗ h t − 1 ) + U h ∗ x t ) \hat{h}_t=tanh(W_h(r_t*h_{t-1})+U_h*x_t) h^t=tanh(Wh(rtht1)+Uhxt)
此时zt需要先和未更新的ht求哈达玛积,ht-1再和(1-zt)求哈达玛积,再将上述两个结果相加获得到最后的结果ht
h t = ( 1 − z t ) ∗ h t − 1 + z t ∗ h ^ t h_t=(1-z_t)*h_{t-1}+z_t*\hat{h}_t ht=(1zt)ht1+zth^t
这个过程意味着更新门有能力保留之前的结果, 当门值趋于1时, 输出就是新的h(t), 而当门值趋于0时, 输出就是上一时间步的h(t-1)。

(三)、Bi-GRU介绍

Bi-GRU与Bi-LSTM的逻辑相同, 都是不改变其内部结构, 而是将模型应用两次且方向不同, 再将两次得到的LSTM结果进行拼接作为最终输出

(四)、实现

API:

nn.GRU(inputsize,hidden_size,num_layers,batch_first,bidirectional)
'''
input_size: 输入张量x中特征维度的大小.
hidden_size: 隐层张量h中特征维度的大小.
num_layers: 隐含层的数量.
batch_first:设置为True后,输入input第一位为batch_size,第二位为每句长度
bidirectional: 是否选择使用双向GRU, 如果为True, 则使用; 默认不使用
'''my_GRU = nn.GRU(...)
output, hn = my_GRU(input, h0)
'''
input: 输入张量x.
h0: 初始化的隐层张量h
'''

例如:

import torch
import torch.nn as nndef dm_gru():gru = nn.GRU(4, 5, 2, batch_first=True, bidirectional=True)input = torch.randn(2, 4, 4)h0 = torch.randn(4, 2, 5)out, hn = gru(input, h0)print(out)print(out.shape)print(hn)print(hn.shape)if __name__ == '__main__':dm_gru()

结果:

tensor([[[ 6.2202e-01, -5.2002e-01, -2.1665e-01,  1.1606e+00, -6.9614e-01,1.7723e-01,  3.4490e-01,  2.2908e-01, -4.4243e-02,  2.8472e-01],[ 5.9470e-01, -1.1089e-03,  6.1477e-02,  7.7945e-01, -5.7582e-01,2.3751e-01,  2.8009e-01,  2.6080e-01,  1.4921e-01,  3.8098e-01],[ 4.7263e-01,  3.8091e-01,  2.6586e-01,  3.6587e-01, -5.1025e-01,2.6715e-01,  3.1573e-01,  3.6568e-01,  2.7377e-01,  5.2090e-01],[ 3.8016e-01,  5.9008e-01,  3.6576e-01,  2.1258e-02, -5.5816e-01,3.9258e-01,  4.6132e-01,  4.9604e-01,  4.7448e-01,  9.5863e-01]],[[-4.3905e-01,  5.6604e-01,  4.0822e-01,  8.5531e-01,  2.9254e-01,2.4266e-02,  2.8750e-01,  2.1772e-01,  1.5369e-01,  3.0358e-01],[-2.7539e-01,  8.3340e-01,  3.3841e-01,  4.1600e-01,  6.9993e-02,-1.5839e-02,  2.4091e-01,  3.9040e-01,  1.2497e-01,  4.3371e-01],[-1.2397e-01,  8.4867e-01,  2.9182e-01,  1.3590e-01, -4.1246e-02,-9.1719e-03,  2.3020e-01,  4.7799e-01, -1.9405e-02,  5.4496e-01],[ 1.9304e-02,  8.0219e-01,  1.5324e-01, -1.3357e-01, -8.3342e-02,6.3888e-04,  2.0426e-01,  6.7422e-01, -2.9473e-01,  7.3993e-01]]],grad_fn=<TransposeBackward1>)
torch.Size([2, 4, 10])
tensor([[[ 2.5272e-01,  3.5627e-01, -2.0827e-01, -4.5440e-01, -5.4678e-02],[-7.1883e-02,  4.4186e-01, -4.2401e-01, -2.9838e-01, -2.4969e-02]],[[ 3.2858e-01,  4.4854e-01, -3.2011e-01, -1.3063e-01, -2.5779e-01],[ 5.4962e-01, -4.5760e-04, -2.9187e-01, -2.9130e-01,  1.7019e-01]],[[ 3.8016e-01,  5.9008e-01,  3.6576e-01,  2.1258e-02, -5.5816e-01],[ 1.9304e-02,  8.0219e-01,  1.5324e-01, -1.3357e-01, -8.3342e-02]],[[ 1.7723e-01,  3.4490e-01,  2.2908e-01, -4.4243e-02,  2.8472e-01],[ 2.4266e-02,  2.8750e-01,  2.1772e-01,  1.5369e-01,  3.0358e-01]]],grad_fn=<StackBackward0>)
torch.Size([4, 2, 5])

(五)、优缺点

优点:

GRU和LSTM作用相同, 在捕捉长序列语义关联时, 能有效抑制梯度消失或爆炸, 效果都优于传统RNN且计算复杂度相比LSTM要小.

缺点:

GRU仍然不能完全解决梯度消失问题, 同时其作用RNN的变体, 有着RNN结构本身的一大弊端, 即不可并行计算, 这在数据量和模型体量逐步增大的未来, 是RNN发展的关键瓶颈

5、注意力机制

(一)、注意力机制的由来和定义

注意力机制是由机器翻译任务发展而来,seq2seq(Sequence to Sequence)架构翻译任务,seq2seq模型架构包括三部分,分别是encoder(编码器)、decoder(解码器)、中间语义张量c。

比如中文翻译到英文,欢迎 来 北京 → welcome to BeiJing。编码器首先处理中文输入"欢迎 来 北京",通过GRU模型获得每个时间步的输出张量,最后将它们拼接成一个中间语义张量c;接着解码器将使用这个中间语义张量c以及每一个时间步的隐层张量, 逐个生成对应的翻译语言。

早期在解决机器翻译这一类seq2seq问题时,通常采用的做法是利用一个编码器(encoder)和一个解码器(decoder)构建端到端的神经网络模型,但是基于编码解码的神经网络存在两个问题:

  • 如果翻译的句子很长很复杂,比如直接一篇文章输进去,模型的计算量很大,并且模型的准确率下降严重
  • 在翻译时,可能在不同的语境下,同一个词具有不同的含义,但是网络对这些词向量并没有区分度,没有考虑词与词之间的相关性,导致翻译效果比较差

在这种情况下注意力机制被提出。

“注意力机制”实际上就是想将人的感知方式、注意力的行为应用在机器上,让机器学会去感知数据中的重要和不重要的部分

在机器翻译中,我们要让机器注意到每个词向量之间的相关性,有侧重地进行翻译,模拟人类理解的过程。

(二)、注意力机制的分类

通俗的来说,就是对于模型的每一个 输入项,都是图片中的不同部分,或者是语句中的某个单词分配的一个权重,这个权重大小就代表了我们希望模型对该部分一个关注程度。这样就可以通过权重大小来模拟人在处理信息的注意力的侧重,有效的提高了模型的性能,并且一定程度上 降低了计算量。

深度学习中的注意力机制通常可以分为三类:软注意(全局注意),硬注意(局部注意)和自注意(内注意)。

  • 软注意机制(Soft/Global Attention: 对每个输入项的分配的权重为0-1之间,也就是某些部分关注的多一点,某些部分关注的少一点,因为对大部分信息都有考虑,但考虑程度不一样,所以相对来说计算量比较大。这是最常见的一种注意力机制。

  • 硬注意机制(Hard/Local Attention): 对每个输入项分配的权重非0即1,和软注意不同,硬注意机制只考虑那部分需要关注,哪部分不关注,也就是直接舍弃掉一些不相关项。优势在于可以减少一定的时间和计算成本,但有可能丢失掉一些本应该注意的信息。

  • 自注意力机制( Self/Intra Attention): 对每个输入项分配的权重取决于输入项之间的相互作用,即通过输入项内部的"表决"来决定应该关注哪些输入项。和前两种相比,在处理很长的输入时,具有并行计算的优势。

注意:注意力机制是一种通用的思想和技术,不依赖于任何模型,换句话说,注意力机制可以用于任何模型。

(三)、软注意力机制(soft attention)

(1)、普通情况下的Encoder-Decdoer框架

在这里插入图片描述

上图图例可以把它看作由一个句子(或篇章)生成另外一个句子(或篇章)的通用处理模型。对于句子对,我们的目标是给定输入句子Source,期待通过Encoder-Decoder框架来生成目标句子Target。Source和Target可以是同一种语言,也可以是两种不同的语言。而Source和Target分别由各自的单词序列构成:
S o u r c e = < X 1 , X 2 . . . X m > T a r g e t = < y 1 , y 2 . . . y n > Source=<X_1,X_2...X_m>\\ Target=<y_1,y_2...y_n> Source=<X1,X2...Xm>Target=<y1,y2...yn>
encoder顾名思义就是对输入句子Source进行编码,将输入句子通过非线性变换转化为中间语义表示C:
C = F ( X 1 , X 2 . . . X m ) C=F(X_1,X_2...X_m) C=F(X1,X2...Xm)
解码器Decoder来说,其任务是根据句子Source的中间语义表示C和之前已经生成的历史信息,y_1, y_2…y_i-1来生成i时刻要生成的单词y_i
y i = G ( C , y 1 , y 2 . . . y n ) y_i=G(C,y_1,y_2...y_n) yi=G(C,y1,y2...yn)
上述图中展示的Encoder-Decoder框架是没有体现出“注意力模型”的,所以可以把它看作是注意力不集中的分心模型。为什么说它注意力不集中呢?请观察下目标句子Target中每个单词的生成过程如下:
y 1 = f ( C ) y 2 = f ( C , y 1 ) y 3 = f ( C , y 1 , y 2 ) y_1=f(C)\\ y_2=f(C,y_1)\\ y_3=f(C,y_1,y_2) y1=f(C)y2=f(C,y1)y3=f(C,y1,y2)
其中f是Decoder的非线性变换函数。从这里可以看出,在生成目标句子的单词时,不论生成哪个单词,它们使用的输入句子Source的语义编码C都是一样的,没有任何区别。而语义编码C又是通过对source经过Encoder编码产生的,因此对于target中的任何一个单词,source中任意单词对某个目标单词y_i来说影响力都是相同的,这就是为什么说图中的模型没有体现注意力的原因。

(2)、加Attention的Encoder-Decdoer框架

在这里插入图片描述

引入Attention模型,注意力分配模型分配给不同英文单词的注意力大小。由于注意力模型的加入,原来在生成target单词时候的中间语义C就不再是固定的,而是会根据注意力概率变化的C,加入了注意力模型的Encoder-Decoder框架就变成了上图所示。

也就是生成句子的过程变成了:
y 1 = f 1 ( C 1 ) y 2 = f 1 ( C 2 , y 1 ) y 3 = f 1 ( C 3 , y 1 , y 2 ) y_1=f_1(C_1)\\ y_2=f_1(C_2,y_1)\\ y_3=f_1(C_3,y_1,y_2) y1=f1(C1)y2=f1(C2,y1)y3=f1(C3,y1,y2)
每个Ci可能对应着不同的源语句子单词的注意力分配概率分布:
C T o m = g ( 0.6 ∗ f 2 ( T o m ) , 0.2 ∗ f 2 ( C h a s e ) , 0.2 ∗ f 2 ( J e r r y ) ) C C h a s e = g ( 0.2 ∗ f 2 ( T o m ) , 0.7 ∗ f 2 ( C h a s e ) , 0.1 ∗ f 2 ( J e r r y ) ) C J e r r y = g ( 0.3 ∗ f 2 ( T o m ) , 0.2 ∗ f 2 ( C h a s e ) , 0.5 ∗ f 2 ( J e r r y ) ) C_{Tom}=g(0.6*f_2(Tom),0.2*f_2(Chase),0.2*f_2(Jerry))\\ C_{Chase}=g(0.2*f_2(Tom),0.7*f_2(Chase),0.1*f_2(Jerry))\\ C_{Jerry}=g(0.3*f_2(Tom),0.2*f_2(Chase),0.5*f_2(Jerry)) CTom=g(0.6f2(Tom),0.2f2(Chase),0.2f2(Jerry))CChase=g(0.2f2(Tom),0.7f2(Chase),0.1f2(Jerry))CJerry=g(0.3f2(Tom),0.2f2(Chase),0.5f2(Jerry))
f2函数代表Encoder对输入英文单词的某种变换函数,比如如果Encoder是用的RNN模型的话,这个f2函数的结果往往是某个时刻输入后隐层节点的状态值;g代表Encoder根据单词的中间表示合成整个句子中间语义表示的变换函数,一般的做法中,g函数就是对构成元素加权求和,即下列公式
C i = ∑ j = 1 L x a i j h j L x 代表输入句子 s o u r c e 的长度 a i j 代表在 T a r g e t 输出第 i 个单词时 s o u r c e 输入句子中的第 j 个单词的注意力分配系数 h j 则是 s o u r c e 输入句子中第 j 个单词的语义编码 , C_i=\sum^{L_x}_{j=1}a_{ij}h_j\\ Lx代表输入句子source的长度\\ a_{ij}代表在Target输出第i个单词时source输入句子中的第j个单词的注意力分配系数\\ h_j则是source输入句子中第j个单词的语义编码, Ci=j=1LxaijhjLx代表输入句子source的长度aij代表在Target输出第i个单词时source输入句子中的第j个单词的注意力分配系数hj则是source输入句子中第j个单词的语义编码,

(3)、计算注意力概率分布

在这里插入图片描述

(4)、Attention的本质思想

Attention机制可以看作,Target中每个单词是对Source每个单词的加权求和,而权重是Source中每个单词对Target中每个单词的重要程度。因此,Attention的本质思想会表示成下图:
在这里插入图片描述

将Source中的构成元素看作是一系列的数据对,给定Target中的某个元素Query,通过计算Query和各个Key的相似性或者相关性,即权重系数;然后对Value进行加权求和,并得到最终的Attention数值。
A t t e n t i o n ( Q u e r y , S o u r c e ) = ∑ i = 1 L x S i m i l a r i t y ( Q u e r y , K e y i ) ∗ V a l u e i Attention(Query,Source)=\sum^{L_x}_{i=1}Similarity(Query,Key_i)*Value_i Attention(Query,Source)=i=1LxSimilarity(Query,Keyi)Valuei
深度学习中的注意力机制中提到:Source 中的 Key 和 Value 合二为一,指向的是同一个东西,也即输入句子中每个单词对应的语义编码。

输入由三部分构成:Query、Key和Value。其中,(Key, Value)是具有相互关联的KV对,Query是输入的“问题”,Attention可以将Query转化为与Query最相关的向量表示。

在这里插入图片描述

*Attention 3步计算过程:

  • 第一步:Query和Key进行相似度计算(Q*KT,Q与K的转置求哈达玛积),得到Attention Score;
  • 第二步:对Attention Score进行Softmax归一化,得到权值矩阵;
  • 第三步:权重矩阵与Value进行加权求和计算,得到当前对应的C,此时的C再与Q进行处理,解码出当前的信息。

那么Q、K、V到底是什么呢?

Q可以理解为我们的意图信息,类似于我们在搜索引擎输入的问题。K可以理解为原始信息中的关键信息,类似于搜索引擎中文章的标题或者摘要,而V就是原始信息的全部,类似于搜索引擎中文章的正文部分。

(四)、硬注意力机制(hard attention)

与软注意力机制的区别在于权重矩阵,Q*KT得到attention score后,通过softmax归一化得到权重矩阵,此时软注意力机制会直接使用这个权重矩阵,而硬注意力机制有两种方案。

  • 选择将权重最大的那一项直接将权重置为1,其他权重项全部置为0,再使用更改后的权重矩阵对后续做处理。

  • 根据注意力分布进行随机采样,采样结果作为Attention机制的输出。

硬性注意力通过以上两种方式选择Attention的输出,这会使得最终的损失函数与注意力分布之间的函数关系不可导,导致无法使用反向传播算法训练模型,硬性注意力通常需要使用强化学习来进行训练。因此,一般深度学习算法会使用软性注意力的方式进行计算。

(五)、自注意力机制(self attention)

(1)、概述

Self Attention,指的是Source内部元素之间或者Target内部元素之间发生的Attention机制,也可以理解为Target=Source这种特殊情况下的注意力机制。当然,具体的计算过程仍然是一样的,只是计算对象发生了变化而已。

针对全连接神经网络对于多个相关的输入无法建立起相关性的这个问题,通过自注意力机制来解决,自注意力机制实际上是想让机器注意到整个输入中不同部分之间的相关性

(2)、计算方式

在这里插入图片描述

第一种方式计算,先求自生的Q,再求包括自身在内所有元素的K,点乘后得到相关性,再将所有的相关性整合为向量,通过softmax(用其他的也行),得到概率值。

(3)、注意力机制和自注意力机制的区别:
  • 注意力机制的Q和K是不同来源的,例如,在Encoder-Decoder模型中,K是Encoder中的元素,而Q是Decoder中的元素。在中译英模型中,中文句子通过编码器被转化为一组特征表示K,这些特征表示包含了输入中文句子的语义信息。解码器在生成英文句子时,会使用这些特征表示K以及当前生成的英文单词特征Q来决定下一个英文单词是什么。

  • 自注意力机制的Q和K则都是来自于同一组的元素,例如,在Encoder-Decoder模型中,Q和K都是Encoder中的元素,即Q和K都是中文特征,相互之间做注意力汇聚。也可以理解为同一句话中的词元或者同一张图像中不同的patch,这都是一组元素内部相互做注意力机制,因此,自注意力机制(self-attention)也被称为内部注意力机制(intra-attention)。

(六)、注意力计算规则

(1)、拼接做线性变换

A t t e n t i o n ( Q , K , V ) = S o f t m a x ( L i n e a r ( ∣ Q , k ∣ ) ) ⋅ V Attention(Q,K,V)=Softmax(Linear(|Q,k|))·V Attention(Q,K,V)=Softmax(Linear(Q,k))V

在这里插入图片描述

首先编码器encoder先对所有元素进行编码,将所有的output拼接为一个V,在上图中v此时为一个3×3的向量。将encoder的编码器hn作为k输入给解码器decoder,此时开始预测步骤:

除了decoder第一次的GRU输入的是k和GO以外,后续的GRU输入的是上层的隐藏状态以及一个拼接处理矩阵。

将上一层的output作为Q,ht-1作为K,此时将Q与K做拼接,形成一个1×6的向量,再经过一个6×3的线性层,将矩阵转化为1×3,此时该向量就是attn-score,再输入到softmax中输出1×3的权重向量,此时和encoder传输的3×3的V进行相乘,得到一个1×3的向量,此向量再与上一层输出的vec做拼接输出1×6的向量,再经过一个6×3的线性层,将矩阵转化为1×3,此时将该向量作为下一层GRU的输入。

以此类推直到EOS。

(2)、做乘法

A t t e n t i o n ( Q , K , V ) = S o f t m a x ( Q ⋅ K T d k ) ⋅ V Attention(Q,K,V)=Softmax(\frac{Q·K^T}{\sqrt{d_k}})·V Attention(Q,K,V)=Softmax(dk QKT)V
在这里插入图片描述

编码器encoder每次输出一个V和一个K,在计算是我们将所有的K拼接为一个整体,在上图中就是拼接为一个3×3的矩阵,在解码器解码时会输出一个Q,此时用Q·KT,得到一个1×3的矩阵,这个就是attn-score,这个向量经过softmax得到权重向量,再利用该权重向量与拼接后的3×3的V相乘,得到1×3的矩阵,再将这个矩阵和上一层的输出做拼接,得到一个1×6的矩阵,再经过一个6×3的线性层,将矩阵转化为1×3,此时将该向量作为下一层GRU的输入。

(七)、注意力机制QA

(1)、什么是深度神经网络注意力机制

注意力机制是注意力计算规则能够应用的深度学习网络的载体, 同时包括一些必要的全连接层以及相关张量处理, 使其与应用网络融为一体. 使用自注意力计算规则的注意力机制称为自注意力机制。

NLP领域中, 当前的注意力机制大多数应用于seq2seq架构, 即编码器和解码器模型,为什么要在深度神经网络中引入注意力机制?

  • rnn等循环神经网络,随着时间步的增长,前面单词的特征会遗忘,造成对句子特征提取不充分
  • rnn等循环神经网络是一个时间步一个时间步的提取序列特征,效率低下
  • 研究者开始思考,能不能对32个单词(序列)同时提取事物特征,而且还是并行的,所以引入注意力机制
(2)、注意力机制的作用
  • 在解码器端的注意力机制: 能够根据模型目标有效的聚焦编码器的输出结果, 当其作为解码器的输入时提升效果. 改善以往编码器输出是单一定长张量, 无法存储过多信息的情况.
  • 在编码器端的注意力机制: 主要解决表征问题, 相当于特征提取过程, 得到输入的注意力表示. 一般使用自注意力(self-attention).

(八)、例子

# 任务描述:
# 有QKV:v是内容比如32个单词,每个单词64个特征,k是32个单词的索引,q是查询张量
# 我们的任务:输入查询张量q,通过注意力机制来计算如下信息:
# 1、查询张量q的注意力权重分布:查询张量q和其他32个单词相关性(相识度)
# 2、查询张量q的结果表示:有一个普通的q升级成一个更强大q;用q和v做bmm运算
# 3 注意:查询张量q查询的目标是谁,就是谁的查询张量。
# eg:比如查询张量q是来查询单词"我",则q就是我的查询张量import torch
import torch.nn as nn
import torch.nn.functional as F# MyAtt类实现思路分析
# 1 init函数 (self, query_size, key_size, value_size1, value_size2, output_size)
# 准备2个线性层 注意力权重分布self.attn 注意力结果表示按照指定维度进行输出层 self.attn_combine
# 2 forward(self, Q, K, V):
# 求查询张量q的注意力权重分布, attn_weights[1,32]
# 求查询张量q的注意力结果表示 bmm运算, attn_applied[1,1,64]
# q 与 attn_applied 融合,再按照指定维度输出 output[1,1,32]
# 返回注意力结果表示output:[1,1,32], 注意力权重分布attn_weights:[1,32]class MyAtt(nn.Module):#                   32          32          32              64      32def __init__(self, query_size, key_size, value_size1, value_size2, output_size):super(MyAtt, self).__init__()self.query_size = query_sizeself.key_size = key_sizeself.value_size1 = value_size1self.value_size2 = value_size2self.output_size = output_size# 线性层1 注意力权重分布self.attn = nn.Linear(self.query_size + self.key_size, self.value_size1)# 线性层2 注意力结果表示按照指定维度输出层 self.attn_combineself.attn_combine = nn.Linear(self.query_size+self.value_size2, output_size)def forward(self, Q, K, V):# 1 求查询张量q的注意力权重分布, attn_weights[1,32]# [1,1,32],[1,1,32]--> [1,32],[1,32]->[1,64]# [1,64] --> [1,32]# tmp1 = torch.cat( (Q[0], K[0]), dim=1)# tmp2 = self.attn(tmp1)# tmp3 = F.softmax(tmp2, dim=1)attn_weights = F.softmax( self.attn(torch.cat( (Q[0], K[0]), dim=-1)), dim=-1)# 2 求查询张量q的结果表示 bmm运算, attn_applied[1,1,64]# [1,1,32] * [1,32,64] ---> [1,1,64]attn_applied =  torch.bmm(attn_weights.unsqueeze(0), V)# 3 q 与 attn_applied 融合,再按照指定维度输出 output[1,1,64]# 3-1 q与结果表示拼接 [1,32],[1,64] ---> [1,96]output = torch.cat((Q[0], attn_applied[0]), dim=-1)# 3-2 shape [1,96] ---> [1,32]output = self.attn_combine(output).unsqueeze(0)# 4 返回注意力结果表示output:[1,1,32], 注意力权重分布attn_weights:[1,32]return output, attn_weights

http://www.ppmy.cn/embedded/141861.html

相关文章

快速理解微服务中Gateway的概念

一.基本概念 定义&#xff1a; 在微服务架构中&#xff0c;Spring Cloud Gateway 是一个用于API网关的框架&#xff0c;它是一个基于 Spring Framework 的高效、可扩展的路由器和反向代理&#xff0c;它能够将外部请求转发到适当的微服务&#xff0c;并提供一些与请求处理相关…

快速理解倒排索引在ElasticSearch中的作用

一.基础概念 定义&#xff1a; 倒排索引是一种数据结构&#xff0c;用来加速文本数据的搜索和检索&#xff0c;和传统的索引方式不同&#xff0c;倒排索引会被每个词汇项与包含该词汇项的文档关联起来&#xff0c;从而去实现快速的全文检索。 举例&#xff1a; 在传统的全文…

SQL Server第五章-聚合函数在查询中的使用(头歌)答案代码

第1关&#xff1a;AVG() 函数的使用 select prod_name,prod_pricefrom Productswhere prod_price>(select AVG(prod_price) from Products) 16第2关&#xff1a;COUNT() 函数的使用 select count(*)from Productswhere prod_price>18 17.第3关&#xff1a;MAX() 函数…

vue3小案例-信息系统

第一步&#xff0c;拉取或者创建一个含有 axios 插件的 vue3 项目 创建项目教程&#xff1a;vue3项目搭建-1-创建项目-CSDN博客 安装 axios 插件命令&#xff1a; npm install axios 第二步&#xff0c;mock 模拟后端接口 在根目录创建 mock 文件夹&#xff0c;在文件夹中…

深入浅出 Python 网络爬虫:从零开始构建你的数据采集工具

在大数据时代&#xff0c;网络爬虫作为一种数据采集技术&#xff0c;已经成为开发者和数据分析师不可或缺的工具。Python 凭借其强大的生态和简单易用的语言特点&#xff0c;在爬虫领域大放异彩。本文将带你从零开始&#xff0c;逐步构建一个 Python 网络爬虫&#xff0c;解决实…

Sqoop的安装和配置,Sqoop的数据导入导出,MySQL对hdfs数据的操作

sqoop的安装基础是hive和mysql&#xff0c;没有安装好的同学建议去看一看博主的这一篇文章 Hive的部署&#xff0c;远程模式搭建&#xff0c;centos换源&#xff0c;linux上下载mysql。_hive-4.0.1-CSDN博客 好的那么接下来我们开始表演&#xff0c;由于hive是当时在hadoop03上…

git clone超大仓库时报错:fatal: early EOF

环境版本&#xff1a; 系统&#xff1a;Ubuntu git版本&#xff1a;version 2.43.0 在执行git clone命令时报错&#xff0c;信息如下&#xff1a; 系统&#xff1a;Win10 git版本&#xff1a;version 2.47.0 解决办法1&#xff1a; 1、关闭压缩&#xff1a; git conf…

SNMPv3 项目实例

为了支持 更多的 SNMP 功能,例如 安全性、复杂的 MIB 结构 和 增强的功能,我们需要对现有的代理和客户端进行扩展和改进。下面我会详细介绍如何在 SNMP 项目中实现以下功能: 支持 SNMPv3 安全性:SNMPv3 提供了认证和加密功能,保证数据传输的安全性。支持复杂的 MIB 结构:…