首页 > 其他分享 >from_rnn_2_transformer-cnblog

from_rnn_2_transformer-cnblog

时间:2023-10-07 18:57:15浏览次数:34  
标签:transformer Attention rnn self 30 key model 512 cnblog

从RNN到Transformer

各式各样的“attention”

不管是在CV领域还是NLP领域, attention实质上就是一种取权重求和的过程。使得网络focus在其应该focus的地方。

根据Attention的计算区域,可以分成以下几种:

1)Soft Attention,这是比较常见的Attention方式,对所有key求权重概率,每个key都有一个对应的权重,是一种全局的计算方式(也可以叫Global Attention)。这种方式比较理性,参考了所有key的内容,再进行加权。但是计算量可能会比较大一些。

2)Hard Attention,这种方式是直接精准定位到某个key,其余key就都不管了,相当于这个key的概率是1,其余key的概率全部是0。因此这种对齐方式要求很高,要求一步到位,如果没有正确对齐,会带来很大的影响。另一方面,因为不可导,一般需要用强化学习的方法进行训练。(或者使用gumbel softmax之类的)

3)Local Attention,这种方式其实是以上两种方式的一个折中,对一个窗口区域进行计算。先用Hard方式定位到某个地方,以这个点为中心可以得到一个窗口区域,在这个小区域内用Soft方式来算Attention。

循环神经网络

RNN

用公式表达就是

\[\begin{aligned}O_t&=g(V\cdot S_t)\\S_t&=f(U\cdot X_t+W\cdot S_{t-1})\end{aligned} \]

当t=0时, 使用torch.zeros作为上一个的输入

LSTM

如果句子过长,rnn有可能会出现梯度消失或者爆炸现象, 导致“记不住句子开头的详细内容”。LSTM通过它的“门控装置”有效的缓解了这个问题,这也就是为什么我们现在都在使用LSTM而非普通RNN。

遗忘门

如下图\(\sigma\) 表示的sigmoid神经网络(不是简单的sigmoid函数)

记忆门

更新细胞状态

输出门

Transformer

整体结构

以中英翻译为例, 介绍transformer整体流程

step.1 获得输入X

step.2 将得到的单词表示向量矩阵 (如上图所示,每一行是一个单词的表示 x) 传入 Encoder 中,经过 6 个 Encoder block 后可以得到句子所有单词的编码信息矩阵 C,如下图。单词向量矩阵用\(X_{n*d}\) 表示, n 是句子中单词个数,d 是表示向量的维度 (论文中 d=512)。每一个 Encoder block 输出的矩阵维度与输入完全一致。

step.3 将 Encoder 输出的编码信息矩阵 C传递到 Decoder 中,Decoder 依次会根据当前翻译过的单词 1~ i 翻译下一个单词 i+1,如下图所示。在使用的过程中,翻译到单词 i+1 的时候需要通过 Mask (掩盖) 操作遮盖住 i+1 之后的单词。

上图 Decoder 接收了 Encoder 的编码矩阵 C,然后首先输入一个翻译开始符 "",预测第一个单词 "I";然后输入翻译开始符 "" 和单词 "I",预测单词 "have",以此类推。以上三步是 Transformer 使用时候的大致流程

Embedding

word embedding

单词的 Embedding 有很多种方式可以获取,例如可以采用 Word2Vec、Glove 等算法预训练得到,也可以在 Transformer 中训练得到

Annotated Transformer实现

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import math, copy, time
from torch.autograd import Variable
class Embeddings(nn.Module):
    def __init__(self, d_model, vocab):
        #d_model=512, vocab=当前语言的词表大小
        super(Embeddings, self).__init__()
        #这里有一个待训练的矩阵E,大小是vocab*d_model
        self.lut = nn.Embedding(vocab, d_model)
        self.d_model = d_model

    def forward(self, x):
        # x ~ (batch.size, sequence.length, one-hot)
        # one-hot大小=vocab,当前语言的词表大小
        return self.lut(x) * math.sqrt(self.d_model)

positional embedding

因为 Transformer 不采用 RNN 的结构,而是使用全局信息,不能利用单词的顺序信息,而这部分信息对于 NLP 来说非常重要。所以 Transformer 中使用位置 Embedding 保存单词在序列中的相对或绝对位置。

Annotated Transformer实现

class PositionalEncoding(nn.Module):
    "Implement the PE function."
    def __init__(self, d_model, dropout, max_len=5000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)
        
        # Compute the positional encodings once in log space.
        #(5000,512)矩阵,保持每个位置的位置编码,一共5000个位置,
    	#每个位置用一个512维度向量来表示其位置编码
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len).unsqueeze(1)
        ## 注意下面一句代码的实现,见下面的解释
        div_term = torch.exp(torch.arange(0, d_model, 2) *
                             -(math.log(10000.0) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0)
        self.register_buffer('pe', pe)
        
    def forward(self, x):
        x = x + Variable(self.pe[:, :x.size(1)], 
                         requires_grad=False)
        return self.dropout(x)

其中 div_term = torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model))由下面公式转换得来

\[\begin{array}{l} \frac{1}{10000^{\frac{2 i}{d_{\text {model }}}}}=e^{\log \left\{\frac{1}{10000^{\frac{2 i}{d_{\text {model }}}}}\right\}}= \\ e^{-\log \left\{10000^{\left.\frac{2 i}{d_{\text {model }}}\right\}}\right.}=e^{-\frac{2 i}{d_{\text {model }}} \log \{10000\}}=\epsilon^{2 i *\left(-\frac{\log \{10000\}}{d_{\text {mod }}}\right)} \\ \end{array}\]

在具体使用的时候, 词编码与位置编码结合参考如下

nn.Sequential(Embeddings(d_model,src_vocab),
  PositionalEncoding(d_model,dropout)) 
# 例如,d_model=512, src_vocab=源语言的词表大小, 
#dropout=0.1即 dropout rate

Self-Attention(自注意力机制)

transformer内部结构图如下图所示,红色圈中的部分为 Multi-Head Attention,是由多个 Self-Attention组成的。Multi-Head Attention 上方还包括一个 Add & Norm 层,Add 表示残差连接 (Residual Connection) 用于防止网络退化,Norm 表示 Layer Normalization,用于对每一层的激活值进行归一化。下面详细介绍一下self-attention

Self-Attention ,在计算的时候需要用到矩阵Q(查询),K(键值),V(值)。在实际中,Self-Attention 接收的是输入(单词的表示向量x组成的矩阵X) 或者上一个 Encoder block 的输出。而Q,K,V正是通过 Self-Attention 的输入进行线性变换得到的。

可以线性变阵矩阵WQ,WK,WV计算得到Q,K,V。得到QKV就可以计算self-attention的输出, 公式和图解如上图所示

代码实现

def attention(query, key, value, mask=None, dropout=None): 
# query, key, value的形状类似于(30, 8, t, 64), (30, 8, t, 64), (30, 8, t, 64)
# 例如30是batch.size,即当前batch中有多少个序列;
# 8=head.num,注意力头的个数;
# 10=目标序列中词的个数,64是每个词对应的向量表示;
# 64是每个词对应的向量表示。
# 类似于,这里假定query来自target language sequence;
# key和value都来自source language sequence.
  """
  Compute 'Scaled Dot Product Attention'
  """
  d_k = query.size(-1) # 64=d_k
  scores = torch.matmul(query, key.transpose(-2, -1)) / 
    math.sqrt(d_k) # 先是(30,8,t,64)和(30, 8, 64, t)相乘,
    #(注意是最后两个维度相乘)得到(30,8,t, t),
    #代表t个目标语言序列中每个词和t个源语言序列的分别的“亲密度”。
    #然后除以sqrt(d_k)=8,防止过大的亲密度。
    #这里的scores的shape是(30, 8, t, t)
  if mask is not None: 
    scores = scores.masked_fill(mask == 0, -1e9) 
    #使用mask,对已经计算好的scores,按照mask矩阵,填-1e9,
    #然后在下一步计算softmax的时候,被设置成-1e9的数对应的值~0,被忽视
  p_attn = F.softmax(scores, dim = -1) 
    #对scores的最后一个维度执行softmax,得到的还是一个tensor, 
    #(30, 8, t, t)
  if dropout is not None: 
    p_attn = dropout(p_attn) #执行一次dropout
  return torch.matmul(p_attn, value), p_attn
#返回的第一项,是(30,8,t, t)乘以(最后两个维度相乘)
#value=(30,8,t,t),得到的tensor是(30,8,t,t),
#和query的最初的形状一样。另外,返回p_attn,形状为(30,8,t,t). 
#注意,这里返回p_attn主要是用来可视化显示多头注意力机制。

Multi-head Attention

实际上就是h个self-attention计算得到h个输出矩阵Z, 再进行拼接

class MultiHeadedAttention(nn.Module): 
  def __init__(self, h, d_model, dropout=0.1): 
    # h=8, d_model=512
    "Take in model size and number of heads." 
    super(MultiHeadedAttention, self).__init__() 
    assert d_model % h == 0 # We assume d_v always equals d_k 512%8=0
    self.d_k = d_model // h # d_k=512//8=64
    self.h = h #8
    self.linears = clones(nn.Linear(d_model, d_model), 4) 
    #定义四个Linear networks, 每个的大小是(512, 512)的,
    #每个Linear network里面有两类可训练参数,Weights,
    #其大小为512*512,以及biases,其大小为512=d_model。

    self.attn = None 
    self.dropout = nn.Dropout(p=dropout)
  def forward(self, query, key, value, mask=None): 
   # 注意,输入query的形状类似于(30, t, 512),
   # key.size() ~ (30, t, 512), 
   #以及value.size() ~ (30, t, 512)
    
    if mask is not None: # Same mask applied to all h heads. 
      mask = mask.unsqueeze(1)
    nbatches = query.size(0) #e.g., nbatches=30
    # 1) Do all the linear projections in batch from 
    #d_model => h x d_k 
    query, key, value = [l(x).view(nbatches, -1, self.h, self.d_k)
      .transpose(1, 2) for l, x in 
      zip(self.linears, (query, key, value))] 
      # 这里是前三个Linear Networks的具体应用,
      #例如query=(30,10, 512) -> Linear network -> (30, 10, 512) 
      #-> view -> (30,10, 8, 64) -> transpose(1,2) -> (30, 8, 10, 64)
      #,其他的key和value也是类似地,
   
    # 2) Apply attention on all the projected vectors in batch. 
    x, self.attn = attention(query, key, value, mask=mask, 
      dropout=self.dropout) 
      #调用上面定义好的attention函数,输出的x形状为(30, 8, 10, 64);
    # 3) "Concat" using a view and apply a final linear. 
    x = x.transpose(1, 2).contiguous().
      view(nbatches, -1, self.h * self.d_k) 
      # x ~ (30, 8, 10, 64) -> transpose(1,2) -> 
      #(30, 10, 8, 64) -> contiguous() and view -> 
      #(30, 10, 8*64) = (30, 10, 512)
return self.linears[-1](x) 
#执行第四个Linear network,把(30, 10, 512)经过一次linear network,
#得到(30, 10, 512).

Encoder结构

结构图可参考self-attention中的结构图, 由 Multi-Head Attention, Add & Norm, Feed Forward, Add & Norm 组成的,

公式如下

Feed Forward 层比较简单,是一个两层的全连接层,第一层的激活函数为 Relu,第二层不使用激活函数,对应的公式如下。

Decoder结构

decoder block有一些特殊的地方

  • 包含两个 Multi-Head Attention 层。
  • 第一个 Multi-Head Attention 层采用了 Masked 操作。
  • 第二个 Multi-Head Attention 层的K, V矩阵使用 Encoder 的编码信息矩阵C进行计算,而Q使用上一个 Decoder block 的输出计算。
  • 最后有一个 Softmax 层计算下一个翻译单词的概率。

第一个mult-head attention

第二个multi-head attention

Decoder block 第二个 Multi-Head Attention 变化不大, 主要的区别在于其中 Self-Attention 的 K, V矩阵不是使用 上一个 Decoder block 的输出计算的,而是使用 Encoder 的编码信息矩阵 C 计算的。

根据 Encoder 的输出 C计算得到 K, V,根据上一个 Decoder block 的输出 Z 计算 Q (如果是第一个 Decoder block 则使用输入矩阵 X 进行计算),后续的计算方法与之前描述的一致。

这样做的好处是在 Decoder 的时候,每一位单词都可以利用到 Encoder 所有单词的信息 (这些信息无需 Mask)。

其它

why LayerNorm?

BN

如下图所示, 一个batch有R条数据, BN就是对同一维度的特征进行normalization, 这是基于同一维度都是表示同一特征(比如第n维为身高)。但是bs比较小的时候bn效果不好

对于nlp而言, 词嵌入之后的句子长度是不一样的, 比如一个句子长度是20,另外9个句子长度不及5, 那这个时候做BN就是对每一个单词做bn, 显然是不太合理的, 除此之外, 不同句子相同位置的单词是没有语义关系的, 不适合做BN

LN

BatchNorm是对一个batch-size样本内的每个特征做归一化,LayerNorm是对每个样本的所有特征做归一化,对于NLP来说, 有两个句子“今天天气很好”, “你中午吃的什么”, LN就是对整个句子进行归一化处理

transformer为什么可以并行计算?

Transformer可以并行运算的原因是因为其使用了自注意力机制(Self-Attention)。自注意力机制可以同时计算所有输入序列中每个位置的表示,因此可以并行化处理整个输入序列。
具体来说,自注意力机制通过计算每个位置与所有其他位置之间的相关性来确定位置的表示。在计算相关性时,可以通过矩阵乘法来实现并行计算。因此,Transformer可以将整个输入序列通过矩阵乘法并行化处理,从而大大提高了模型的计算效率。
除此之外,Transformer还使用了多头注意力机制(Multi-Head Attention),将自注意力机制并行化,进一步提高了模型的计算效率。多头注意力机制将输入序列分成多个子序列,并对每个子序列进行注意力计算,最后将不同子序列的注意力计算结果拼接在一起,得到整个输入序列的表示。这样可以同时计算多个位置之间的相关性,大大提高了模型的计算效率。
因此,Transformer可以通过自注意力机制和多头注意力机制的并行计算,实现对输入序列进行高效的处理和表示学习。

未完待更新...

标签:transformer,Attention,rnn,self,30,key,model,512,cnblog
From: https://www.cnblogs.com/xle97/p/17747183.html

相关文章

  • 【NIPS2021】Twins: Revisiting the Design of Spatial Attention in Vision Transfor
    来自美团技术团队♪(^∀^●)ノシ论文地址:https://arxiv.org/abs/2104.13840代码地址:https://git.io/Twins一、写在前面 本文提出了两种视觉转换器架构,即Twins-PCPVT和Twins-SVT。Twins-PCPVT将金字塔Transformer模型PVT [2] 中的固定位置编码(PositionalEncoding)更改为团队......
  • 听我说,Transformer它就是个支持向量机
    Transformer是一个支持向量机(SVM)一种新型理论在学界引发了人们的讨论。上周末,一篇来自宾夕法尼亚大学、加州大学河滨分校的论文试图研究大模型基础Transformer结构的原理,其在注意力层的优化几何与将最优输入token与非最优token分开的硬边界SVM问题之间建立了形式等价。在......
  • 矩阵成真!Pytorch最新工具mm,3D可视化矩阵乘法、Transformer注意力
    前言 Pytorch团队推出的最新3D可视化最新工具mm,能够将矩阵乘法模拟世界还原。本文转载自新智元仅用于学术分享,若侵权请联系删除欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读、CV招聘信息。CV各大方向专栏与各个部署框架最全教程整理【C......
  • 手动实现Transformer
      Transformer和BERT可谓是LLM的基础模型,彻底搞懂极其必要。Transformer最初设想是作为文本翻译模型使用的,而BERT模型构建使用了Transformer的部分组件,如果理解了Transformer,则能很轻松地理解BERT。一.Transformer模型架构1.编码器(1)Multi-HeadAttention(多头注意力机制)  ......
  • 详细了解Transformer:Attention Is All You Need
    原文链接:AttentionIsAllYouNeed1.背景在机器翻译任务下,RNN、LSTM、GRU等序列模型在NLP中取得了巨大的成功,但是这些模型的训练是通常沿着输入和输出序列的符号位置进行计算的顺序计算,无法并行。文中提出了名为Transformer的模型架构,完全依赖注意力机制(AttentionMechanis......
  • 大模型增量训练--基于transformer制作一个大模型聊天机器人
    ChatGPTBook/UniLMProj 代码地址FoldersandfilesNameLastcommitmessageLastcommitdateparentdirectory..data(Directory)updatecode3monthsagoimages(Directory)updatecode3monthsagokuaku......
  • [NIPS 2021]Do Transformers Really Perform Bad for Graph Representation
    [NIPS2021]DoTransformersReallyPerformBadforGraphRepresentation微软提出的graphtransformer,名叫GraphormerTransformer通常,transformerlayer有一个self-attentionmodule和一个position-wisefeed-forwardnetwork(FFN)组成。首先将特征映射成三组:\[Q=HW_Q,K=......
  • EfficientFormer:高效低延迟的Vision Transformers
    我们都知道Transformers相对于CNN的架构效率并不高,这导致在一些边缘设备进行推理时延迟会很高,所以这次介绍的论文EfficientFormer号称在准确率不降低的同时可以达到MobileNet的推理速度。Transformers能否在获得高性能的同时,跑得和MobileNet一样快?为了回答这个问题,作者首先回顾......
  • Transformer架构解析及其pytorch实现
    备注本文对Transformer架构的分析来源于论文AttentionisAllYouNeed以及部分其引用的论文,可以理解为对该论文的翻译以及相关内容的整理。本文对Transformer的实现基于Pytorch,但是不直接调用Pytorch封装的Transformer,而是手动实现Encoder和Decoder等;与Transformer......
  • 【转载https://www.cnblogs.com/niuben/p/12017242.html】Linux top命令详解
    Linuxtop命令详解转载出处:https://www.cnblogs.com/niuben/p/12017242.htmltop参数详解第一行,任务队列信息,同uptime命令的执行结果系统时间:07:27:05运行时间:up1:57min,当前登录用户:3user负载均衡(uptime)loadaverage:0.00,0.00,0.00average后面的三个数分......