11_从注意力机制到序列处理的革命:Transformer原理详解

1.1 简介

Transformer是一种深度学习模型,主要用于处理序列数据,尤其是自然语言处理任务,如机器翻译、文本摘要等。该模型由Vaswani等人在2017年的论文《Attention is All You Need》中首次提出,它的出现极大地推动了自然语言处理领域的发展,并逐渐扩展应用到图像识别、音乐生成等多个AI领域。Transformer的核心优势在于其并行处理能力、长距离依赖的高效建模以及对序列数据的强大表达能力。

1.2 整体架构

Transformer模型通常采用编码器-解码器(Encoder-Decoder)结构,但与传统的RNN或LSTM模型不同,它完全基于自注意力(Self-Attention)机制和多层神经网络结构,摒弃了循环神经网络中的序列依赖性,从而实现了高度并行化,提高了训练速度。、

注意力机制详解:10_Transformer预热---注意力机制(Attention)-CSDN博客

编码器(Encoder)

编码器负责接收输入序列并将其转换成一个高级语义表示。每个编码器层包含以下主要组件:

  1. 自注意力(Self-Attention)层

    • 查询(Query)、键(Key)、值(Value)计算:首先,输入序列的每个位置的嵌入向量会被线性映射为Q、K、V三组向量。
    • 注意力权重计算:通过计算Q和K的点积,然后应用softmax函数,得到每个位置上的注意力权重,这代表了序列中各位置的重要性。
    • 加权求和:使用上述权重对V向量进行加权求和,得到每个位置的上下文感知表示。
  2. 层归一化(Layer Normalization):对自注意力层的输出进行归一化,帮助稳定训练过程。

  3. 残差连接(Residual Connection):自注意力层的输出与原始输入(或前一层的输出)相加,保留原始信息并促进梯度流通。

  4. 前馈神经网络(Feed Forward Network, FFN)

    • 两次线性变换:通常包含两个全连接层,中间夹着一个非线性激活函数(如ReLU),用于学习复杂的特征表示。
    • 再次归一化:FFN输出后也会进行层归一化。

解码器(Decoder)

解码器负责根据编码器产生的表示和之前已生成的输出序列部分来生成下一个输出。每个解码器层包含以下组件:

  1. 自注意力层:与编码器中的自注意力相似,但仅关注解码器自身的输入,帮助捕捉序列内的依赖。

  2. 编码器-解码器注意力层(Encoder-Decoder Attention)

    • 计算:类似于自注意力,但这里的Q来自解码器的前一时刻输出,而K和V来自编码器的输出,这样解码器的每个位置都能关注到输入序列的所有位置。
    • 加权求和与归一化:同自注意力层的操作。
  3. 残差连接与层归一化:与编码器相同,用于稳定训练和加速收敛。

  4. 前馈神经网络(FFN):与编码器的FFN相同,用于进一步的特征转换。

整体运行路线

  1. 输入编码:输入序列首先转换为词嵌入,并加上位置编码,形成初始输入向量。

  2. 编码器处理:输入向量通过编码器的每一层,每层中自注意力机制帮助模型理解输入序列的上下文关系,FFN则学习更复杂的特征表示。

  3. 解码器处理

    • 解码器逐步生成输出序列。每一步,解码器的自注意力层处理当前已生成的部分序列,编码器-解码器注意力层则利用编码器的输出信息,指导下一步的生成。
    • 随着每一步的进行,解码器不断生成新的输出,并将其反馈给下一个时间步的解码器自注意力层。
  4. 输出:解码器的最后一层输出经过一个线性层和softmax函数,转换为预测的下一个单词的概率分布。
     

1.3 编码器

Transformer的编码器是其架构中的关键组成部分,负责将输入序列转换为一系列隐藏表示,这些表示携带了输入序列的语义信息。编码器由多个相同的层(通常称为编码器层)堆叠而成,每一层又可以细分为几个子层,主要包括自注意力(Self-Attention)机制、层归一化(Layer Normalization)、残差连接(Residual Connections),以及前馈神经网络(Feed Forward Networks, FFNs)。下面是编码器及其各部分的详细介绍:

编码器层结构

  1. 输入Embedding与位置编码(Positional Encoding)

    • 输入Embedding:首先,输入序列的每个词被映射到一个高维向量空间中,这是通过查找预定义的词嵌入表实现的,每个词都有一个固定的向量表示。
    • 位置编码:为了使模型能够区分不同位置的词,会为每个词的位置添加一个唯一的向量,这个向量包含了词在序列中的位置信息。位置编码通常是基于正弦和余弦函数计算的,确保了模型对序列长度的不变性。
  2. 自注意力(Self-Attention)

    • 自注意力机制允许输入序列中的每一个词同时“关注”到其他所有词,从而捕捉序列中的长距离依赖关系。
    • 每个位置的词首先被映射为查询(Q)、键(K)和值(V)向量。
    • 计算查询与键之间的点积注意力分数,经过softmax函数归一化,得到每个位置的注意力权重。
    • 使用这些权重加权求和值向量,得到每个位置的上下文感知表示。
  3. 多头注意力(Multi-Head Attention)

    • 实际操作中,自注意力机制常常以多头的形式实现,即将Q、K、V向量分别分割成几个部分,独立进行注意力计算,最后再合并结果。这样可以使得模型能够并行地关注不同位置的不同表示子空间,增强模型的表达能力。
  4. 层归一化(Layer Normalization)

    • 自注意力层的输出会通过层归一化,目的是对输入数据进行标准化处理,使其具有稳定的分布,有助于训练的稳定性。
  5. 残差连接(Residual Connections)

    • 自注意力层(可能包括多头注意力后的组合)的输出会与该层的输入(即上一层的输出或最初的嵌入+位置编码)相加,保留原始信息并帮助梯度顺利传播。
  6. 前馈神经网络(Feed Forward Network, FFN)

    • 通常包含两个线性层,中间夹着一个激活函数(如ReLU),用于进一步提取特征和转换表示。
    • 第一个线性层增加模型的非线性,第二个线性层则用于降低维度,保持或减少输出的复杂度。
  7. 第二次层归一化

    • FFN之后再次进行层归一化,确保输出稳定并准备进入下一层或作为最终输出。

层间堆叠

多个这样的编码器层按顺序堆叠,每一层都会对前一层的输出进行更深层次的特征提取和表示学习,使得模型能够逐步构建出更加抽象和丰富的输入序列表示。

1.4 解码器

Transformer的解码器是其架构的另一半,负责根据编码器提供的输入序列的上下文信息生成目标序列。解码器同样是由多个相同的解码器层堆叠而成,每层包含自注意力机制、编码器-解码器注意力机制、层归一化和前馈神经网络等关键组件。以下是解码器及其各部分的详细介绍:

解码器层结构

  1. 自注意力(Self-Attention)

    • 解码器中的自注意力机制与编码器相似,但有一个重要区别:为了保证自回归特性(即预测的词只能依赖于它前面的词),解码器在计算自注意力时会使用掩码(Masking),遮住当前位置之后的词,确保每个位置只能看到它之前的词。这通常通过在计算注意力权重时为未来位置设置负无穷大值来实现,使得softmax后这些位置的权重为0。
  2. 编码器-解码器注意力(Encoder-Decoder Attention)

    • 这一层让解码器能够关注到编码器输出的每个位置,从而获取输入序列的全局信息。解码器的每个位置会产生一组查询向量,这些查询向量会与编码器输出的所有键(K)和值(V)向量进行匹配,计算注意力权重,然后加权求和得到解码器每个位置的上下文表示。这一步是解码器与编码器信息交流的关键,帮助生成与输入序列相关的输出。
  3. 层归一化(Layer Normalization)

    • 同编码器一样,自注意力层和编码器-解码器注意力层的输出都会经过层归一化,以稳定训练过程并加速收敛。
  4. 残差连接(Residual Connections)

    • 解码器中每个子层(自注意力、编码器-解码器注意力、FFN)的输出会与该层的输入相加,以保留低层信息并帮助梯度传播。
  5. 前馈神经网络(Feed Forward Network, FFN)

    • 与编码器中的FFN类似,解码器中的FFN也包含两个线性层和一个激活函数,用于进一步处理和转换信息。

特殊之处

  • 自回归生成:在生成模式下,解码器每一步生成一个词,并将此词添加到输入序列中,供下一步生成使用,这一过程是逐步进行的。训练时通常使用教师强制(Teacher Forcing)策略,即直接用真实的下一个词作为输入而非模型的预测,以提高训练效率和稳定性。

  • 位置编码:和编码器一样,解码器的输入也需要加上位置编码,以区分不同时间步的位置信息。

  • 并行与串行处理:虽然Transformer的编码器部分可以并行处理所有输入位置,解码器由于自回归的特性,其内部的自注意力计算是限制并行化的(因为需要按照序列顺序逐个位置生成),但编码器-解码器注意力和FFN层仍然可以并行执行。

1.5 为什么解码器需要掩码注意力

解码器需要掩码注意力机制主要是为了维持自回归(Autoregressive)属性,这是生成任务(如文本生成、机器翻译等)中的一个关键要求。自回归意味着每一个时间步的输出只依赖于它之前的时间步的输出,这样可以确保模型在生成序列时遵循正确的顺序。具体来说,掩码注意力机制在解码器中的作用体现在以下几个方面:

  1. 防止信息泄露(Future Information Leakage):在自注意力层中,如果没有掩码,解码器在生成当前位置的词时,可能会“看到”后面未生成的词,这违反了自回归原则。通过应用掩码,特别是“未来遮挡”(future masking),模型会“屏蔽”当前位置之后的所有位置,确保预测当前位置的输出时不考虑未来的上下文信息。

  2. 确保逐位生成:掩码机制允许模型在生成序列时,每次迭代只基于已经生成的部分,这有助于模型理解和学习到序列中元素间的依赖关系,从而更准确地预测下一个词。

  3. 训练与推理的一致性:在训练时使用掩码注意力可以帮助模型学会合理的生成顺序,这与实际推理过程中模型只能访问到目前为止已生成的信息是一致的。如果训练时不使用掩码,模型可能无法正确学习到这种依赖关系,导致推理时生成不合理的序列。

  4. 简化学习任务:掩码机制简化了学习问题,使得模型能够在每个时间步集中精力学习如何基于历史信息做出最佳预测,而不是试图解析复杂的时间依赖关系。

1.6为什么解码器的交叉注意力模块的KV矩阵来自编码器

解码器的交叉注意力模块的设计中,其Key(K)和Value(V)矩阵来源于编码器,这是基于Seq2Seq(Sequence to Sequence)模型的核心思想和Transformer架构的特点,具体原因包括:

  1. 传递编码信息:编码器负责捕获输入序列(如源语言句子)的语义信息,并将其编码为一系列高维向量。这些向量不仅包含了每个输入位置的语义,还包括了输入序列中的上下文依赖。将编码器的输出作为解码器交叉注意力模块的K和V矩阵,可以让解码器在生成目标序列的每个词时,都能够访问到源序列的完整上下文信息。

  2. 促进并行计算:Transformer架构利用自注意力机制,允许在计算过程中高度并行化。通过将编码器输出固定为解码器交叉注意力中的K和V,可以在解码阶段的每一步都独立地计算出与编码信息相关的注意力权重,这非常适合并行处理,加速训练和推断过程。

  3. 维护时序一致性:在解码器生成目标序列的过程中,每个时间步的Query(Q)来自前一时间步的解码器输出或者初始的解码器状态(在没有先前输出时)。通过查询编码器的K和V矩阵,模型能够在生成每个目标词时,基于已经编码的输入序列选择性地聚焦相关信息,从而维护了从输入到输出的时序一致性,这对于生成高质量的、符合逻辑的输出序列至关重要。

  4. 增强注意力机制的有效性:交叉注意力机制使得解码器能够根据当前生成进度的不同需求,动态地调整对输入序列不同部分的关注。这种灵活性允许模型在生成特定词汇时,侧重考虑输入序列中的相关部分,提高了翻译或生成任务的准确性。

1.7 Transformer的输出和输入是什么

输入

  1. 编码器(Encoder)的输入:

    • 编码器接收一个序列作为输入,比如一个句子。这个序列首先被转换成一系列token(分词),每个token对应一个唯一的ID。然后,这些ID通过一个嵌入层(Embedding Layer)转换成高维向量,代表每个单词的语义信息。除此之外,还会给每个位置添加位置编码(Positional Encoding),以告知模型单词在序列中的位置信息。因此,编码器的输入是一个形状为 [batch_size, sequence_length, embedding_dim] 的张量,其中 sequence_length 是序列的长度,embedding_dim 是嵌入向量的维度。
  2. 解码器(Decoder)的输入:

    • 解码器的输入也是序列形式,但与编码器不同,它不仅要处理目标序列的单词,还要考虑已经生成的部分序列。解码器的自注意力部分(Self-Attention)只关注已生成的词(通过使用掩码遮盖未来的信息),而交叉注意力(Cross-Attention)部分则会使用编码器的输出作为K和V矩阵,来获取源序列的信息。解码器的输入同样经过嵌入层和位置编码处理,形状同样是 [batch_size, target_sequence_length, embedding_dim]

输出

  1. 编码器(Encoder)的输出:

    • 编码器的最后一个自注意力和前馈网络(Feed Forward Network, FFN)层之后,会产生一系列高维向量,每个向量对应输入序列中的一个位置。这些向量综合了序列中的上下文信息,形状为 [batch_size, sequence_length, model_dim],其中 model_dim 是模型的隐藏层维度。这些输出会被解码器的交叉注意力模块使用。
  2. 解码器(Decoder)的输出:

    • 解码器的输出经过一个线性层(Linear Layer)和softmax函数处理,转化为一个概率分布,表示下一个预测词的可能性。形状为 [batch_size, target_vocab_size],其中 target_vocab_size 是目标词汇表的大小。在生成任务中,模型通常会选择概率最高的词作为输出;而在序列到序列的翻译或文本生成任务中,这一过程会重复进行,每次为序列增加一个新词,直到达到终止条件(如遇到结束标记)。

2.pytorch复现

# Author:SiZhen
# Create: 2024/6/12
# Description: pytorch构建一个transformer框架
import copy
import math
import torch
import numpy as np
import torch.nn as nn
import torch.nn as nn
import torch.nn.functional as F
from  collections import namedtuple
from torch.autograd import Variable

#克隆n次
def clones(module,n):
    return nn.ModuleList([copy.deepcopy(module)for _ in range(n)])

class FeatEmbedding(nn.Module):

    def __init__(self, d_feat, d_model, dropout):
        super(FeatEmbedding, self).__init__()
        self.video_embeddings = nn.Sequential(
            LayerNorm(d_feat),
            nn.Dropout(dropout),
            nn.Linear(d_feat, d_model))

    def forward(self, x):
        return self.video_embeddings(x)


class TextEmbedding(nn.Module):

    def __init__(self, vocab_size, d_model):
        super(TextEmbedding, self).__init__()
        self.d_model = d_model
        self.embed = nn.Embedding(vocab_size, d_model)

    def forward(self, x):
        return self.embed(x) * math.sqrt(self.d_model)

#层归一化
class LayerNorm(nn.Module):
    def __init__(self,feature,eps=1e-6):
        super(LayerNorm, self).__init__()
        #feature 是self-attention中x的大小
        self.a_2 = nn.Parameter(torch.ones(feature))
        self.b_2 = nn.Parameter(torch.zeros(feature))
        self.eps = eps #epsilon,一个很小的正数,用来避免除以零或者其他数值稳定性问题。

    def forward(self,x):
        mean = x.mean(-1,keepdim=True)
        std = x.std(-1,keepdim=True)
        return self.a_2*(x-mean)/math.sqrt(std+self.eps)  + self.b_2

#残差和层归一化
class SublayerConnection(nn.Module):
   def __init__(self,size,dropout= 0.1):
       super(SublayerConnection, self).__init__()
       #层归一化
       self.layer_norm = LayerNorm(size)
       #随机失活
       self.dropout = nn.Dropout(p=dropout)

   def forward(self,x,sublayer):
       #x:self-attention的输入,sublayer:self-attention层
       return self.dropout(self.layer_norm(x+sublayer(x)))

#自注意力机制
def self_attention(query,key,value,dropout=None,mask=None):
    d_k = query.size(-1)
    scores = torch.matmul(query,key.transpose(-2,-1))/math.sqrt(d_k)
    if mask is not None:
        mask.cuda()
        scores = scores.masked_fill(mask==0,-1e9)
    self_attn = F.softmax(scores,dim=-1)
    if dropout is not None :
        self_attn = dropout(self_attn)
    return torch.matmul(self_attn,value),self_attn

#多头自注意力机制
class MultiHeadAttention(nn.Module):
    def __init__(self,head,d_model,dropout=0.1):
        super(MultiHeadAttention, self).__init__()
        #d_model是输入的维度
        assert (d_model % head == 0)
        self.d_k = d_model // head #每个头分配到的维度数,空间上并行学习,增加模型的表达能力
        self.head = head
        self.d_model = d_model
        self.linear_query = nn.Linear(d_model,d_model)
        self.linear_key = nn.Linear(d_model,d_model)
        self.linear_value = nn.Linear(d_model,d_model)
        # 自注意力机制的QKV同源,线性变换
        self.linear_out = nn.Linear(d_model,d_model)
        self.dropout = nn.Dropout(p=dropout)
        self.attn = None

    def forward(self,query,key,value,mask=None):
        if mask is not None:
            mask = mask.unsqueeze(1)
        n_batch = query.size(0)#需要对X切分成多头

        query = self.linear_query(query).view(n_batch,-1,self.head,self.d_k).tranpose(1,2) #[b,8,32,64]
        key = self.linear_key(key).view(n_batch,-1,self.head,self.d_k).tranpose(1,2) #[b,8,28,64]
        value = self.linear_value(value).view(n_batch,-1,self.head,self.d_k).tranpose(1,2) #[b,8,28,64]


        x,self.attn = self_attention(query,key,value,dropout=self.dropout,mask=mask)
        x = x.transpose(1,2).contiguous().view(n_batch,-1,self.head*self.d_k) #[b,32*8,512]

        return self.linear_out(x)

#位置编码
class PositionalEncoding(nn.Module):
    def __init__(self,dim,dropout,max_len=5000):
        super(PositionalEncoding, self).__init__()
        if dim % 2 !=0:
            raise ValueError("Cannot use sin/cos positional encoding with"
                             "odd dim (got dim = {:d})".format(dim))
        #位置编码pe : PE(pos,2i/2i+1) = sin/cos (pos/10000^{2i/d_{model}})
        pe = torch.zeros(max_len,dim) #max_len是解码器生成句子的最长的长度,假设是10
        position = torch.arange(0,max_len).unsqueeze(1)
        div_term = torch.exp((torch.arange(0,dim,2,dtype=torch.float)*
                             -(math.log(10000.0)/dim)))

        pe[:,0::2]=torch.sin(position.float()*div_term)
        pe[:,1::2] = torch.cos(position.float()*div_term)
        pe = pe.unsqueeze(1)
        self.register_buffer('pe',pe)
        self.dropout = nn.Dropout(p=dropout)
        self.dim = dim

    def forward(self,emb,step = None):
        #emb:初始的x
        emb = emb*math.sqrt(self.dim)
        if step is None :
            emb = emb+self.pe[:emb.size(0)]
        else:
            emb = emb+self.pe[step]
        emb = self.drop_out(emb)
        return emb

#前馈神经网络feedforward
class PositionWiseFeedForward(nn.Module):
    def __init__(self,d_model,d_ff,dropout=0.1):
        super(PositionWiseFeedForward, self).__init__()
        self.w_1 = nn.Linear(d_model,d_ff)
        self.w_2 = nn.Linear(d_ff,d_model)
        self.layer_norm = nn.LayerNorm(d_model,eps=1e-6)
        self.dropout_1 = nn.Dropout(dropout)
        self.relu = nn.ReLU()
        self.dropout_2 = nn.Dropout(dropout)
    def forward(self,x):
        inter = self.dropout_1(self.relu(self.w_1(self.layer_norm(x))))
        output = self.dropout_2(self.w_2(inter))
        return output

#Linear和softmax
class Generator(nn.Module):
    def __init__(self,d_model,vocab_size):
        super(Generator, self).__init__()
        self.linear = nn.Linear(d_model,vocab_size)

    def forward(self,x):
        return F.log_softmax(self.linear(x),dim=-1)

#掩码自注意力机制
def pad_mask(src,r2l_trg,trg,pad_idx):
    if isinstance(src,tuple):
        if len(src)==4:
            src_image_mask = (src[0][:,:,0]!=pad_idx).unsqueeze(1)
            src_motion_mask=(src[1][:,:,0]!=pad_idx).unsqueeze(1)
            src_object_mask = (src[2][:,:,0]!=pad_idx).unsqueeze(1)
            src_rel_mask = (src[3][:, :, 0] != pad_idx).unsqueeze(1)
            enc_src_mask=(src_image_mask,src_motion_mask,src_object_mask,src_rel_mask)
            dec_src_mask_1=src_image_mask & src_motion_mask
            dec_src_mask_2=src_image_mask & src_motion_mask &src_object_mask & src_rel_mask
            dec_src_mask =(dec_src_mask_1,dec_src_mask_2)
            src_mask = (enc_src_mask,dec_src_mask)
        if len(src)==3:
            src_image_mask = (src[0][:, :, 0] != pad_idx).unsqueeze(1)
            src_motion_mask = (src[1][:, :, 0] != pad_idx).unsqueeze(1)
            src_object_mask = (src[2][:, :, 0] != pad_idx).unsqueeze(1)
            enc_src_mask = (src_image_mask, src_motion_mask, src_object_mask)
            dec_src_mask = src_image_mask &src_motion_mask
            src_mask = (enc_src_mask,dec_src_mask)
        if len(src)==2:
            src_image_mask = (src[0][:, :, 0] != pad_idx).unsqueeze(1)
            src_motion_mask = (src[1][:, :, 0] != pad_idx).unsqueeze(1)
            enc_src_mask = (src_image_mask, src_motion_mask)
            dec_src_mask = src_image_mask &src_motion_mask
            src_mask = (enc_src_mask,dec_src_mask)
    else:
        src_mask = (src[:,:,0]!= pad_idx).unsqueeze(1)
    if trg is not None:
        if isinstance(src_mask,tuple):
            trg_mask = subsequent_mask(trg.size(1)).type_as(src_image_mask.data)
            trg_mask = (trg != pad_idx).unsqueeze(1).type_as(src_image_mask.data)


    else:
        return src_mask


def subsequent_mask(size):
    attn_shape = (1,size,size)
    mask = np.triu(np.ones(attn_shape),k=1).astype('uint8')
    return (torch.from_numpy(mask)==0).cuda()

#编码器模块
class EncoderLayer(nn.Module):
    def __init__(self,size,attn,feed_forward,dropout=0.1):
        super(EncoderLayer, self).__init__()
        self.attn = attn
        self.feed_forward = feed_forward
        self.sublayer_connection = clones(SublayerConnection(size,dropout),2)

    def forward(self,x,mask):
        x = self.sublayer_connection[0](x,lambda x:self.attn(x,x,x,mask))
        return self.sublayer_connection[1](x,self.feed_forward)

#整个编码器
class Encoder(nn.Module):
    def __init__(self,n,encoder_layer):
        super(Encoder, self).__init__()
        self.encoder_layer = clones(encoder_layer,n)

    def forward(self,x,src_mask):
        for layer in self.encoder_layer:
            x = layer(x,src_mask)
        return x

#解码器模块
class DecoderLayer(nn.Module):
    def __init__(self,size,attn,feed_forward,sublayer_num,dropout=0.1):
        super(DecoderLayer, self).__init__()
        self.attn = attn
        self.feedforward = feed_forward
        self.sublayer_connection = clones(SublayerConnection(size,dropout),sublayer_num)

    def forward(self,x,memory,src_mask,trg_mask,r2l_memory=None,r2l_trg_mask=None):
        x = self.sublayer_connection[0](x,lambda x:self.attn(x,x,x,trg_mask))
        x = self.sublayer_connection[1](x,lambda x:self.attn(x,memory,memory,src_mask))

        if r2l_memory is not None:
            x = self.sublayer_connection[-2](x,lambda x:self.attn(x,r2l_memory,r2l_memory,r2l_trg_mask))
        return self.sublayer_connection[-1](x,self.feedforward)

#双向解码器(右到左)
class R2L_Decoder(nn.Module):
    def __init__(self,n,decoder_layer):
        super(R2L_Decoder, self).__init__()
        self.decoder_layer = clones(decoder_layer,n)
    def forward(self,x,memory,src_mask,r2l_trg_mask):
        for layer in self.decoder_layer:
            x = layer(x,memory,src_mask,r2l_trg_mask)
        return  x

#双向解码器(左到右)
class L2R_Decoder(nn.Module):
    def __init__(self,n,decoder_layer):
        super(L2R_Decoder, self).__init__()
        self.decoder_layer = clones(decoder_layer,n)

    def forward(self,x,memory,src_mask,trg_mask,r2l_memory,r2l_trg_mask):
        for layer in self.decoder_layer:
            x = layer(x,memory,src_mask,trg_mask,r2l_memory,r2l_trg_mask)
        return x

#构建Transformer
class ABDTransformer(nn.Module):
    def __init__(self,vocab,d_feat,d_model,d_ff,n_heads,n_layers,dropout,feature_mode,
                 device = 'cuda',n_heads_big=128):
        super(ABDTransformer, self).__init__()
        self.vocab = vocab
        self.device = device
        self.feature_mode = feature_mode #多模态

        c = copy.deepcopy

        attn_no_heads = MultiHeadAttention(0,d_model,dropout)
        attn = MultiHeadAttention(n_heads,d_model,dropout)
        attn_big = MultiHeadAttention(n_heads_big,d_model,dropout)

        feed_forward = PositionWiseFeedForward(d_model,d_ff)
        if feature_mode == 'one':
            self.src_embed = FeatEmbedding(d_feat,d_model,dropout)
        elif feature_mode == "two":
            self.image_src_embed= FeatEmbedding(d_feat[0],d_model,dropout)
            self.motion_src_embed = FeatEmbedding(d_feat[1],d_model,dropout)
        elif feature_mode == 'three':
            self.image_src_embed = FeatEmbedding(d_feat[0],d_model,dropout)
            self.motion_src_embed = FeatEmbedding(d_feat[1],d_model,dropout)
            self.object_src_embed = FeatEmbedding(d_feat[2].d_model,dropout)
        elif feature_mode == 'four':
            self.image_src_embed = FeatEmbedding(d_feat[0],d_model,dropout)
            self.motion_src_embed = FeatEmbedding(d_feat[1],d_model,dropout)
            self.object_src_embed = FeatEmbedding(d_feat[2],d_model,dropout)
            self.rel_src_embed = FeatEmbedding(d_feat[3],d_model,dropout)

        self.trg_embed = TextEmbedding(vocab.n_vocabs,d_model)
        self.pos_embed = PositionalEncoding(d_model,dropout)
        self.encoder = Encoder(n_layers,Encoder(d_model,c(attn),c(feed_forward),dropout))

        self.r2l_decoder = R2L_Decoder(n_layers,DecoderLayer(d_model,c(attn),c(feed_forward),
                                                             sublayer_num=3,dropout=dropout))
        self.l2r_decoder = L2R_Decoder(n_layers,DecoderLayer(d_model,c(attn),c(feed_forward),
                                       sublayer_num=4,dropout=dropout))
        self.generator = Generator(d_model,vocab.n_vocabs)

    def encode(self, src, src_mask, feature_mode_two=False):
        if self.feature_mode == 'two':
            x1 = self.image_src_embed(src[0])
            x1 = self.pos_embed(x1)
            x1 = self.encoder_big(x1, src_mask[0])
            x2 = self.motion_src_embed(src[1])
            x2 = self.pos_embed(x2)
            x2 = self.encoder_big(x2, src_mask[1])
            return x1 + x2
        if feature_mode_two:
            x1 = self.image_src_embed(src[0])
            x1 = self.pos_embed(x1)
            x1 = self.encoder_big(x1, src_mask[0])
            x2 = self.motion_src_embed(src[1])
            x2 = self.pos_embed(x2)
            x2 = self.encoder_big(x2, src_mask[1])
            return x1 + x2
        if self.feature_mode == 'one':
            x = self.src_embed(src)
            x = self.pos_embed(x)
            return self.encoder(x, src_mask)
        elif self.feature_mode == 'two':
            x1 = self.image_src_embed(src[0])
            x1 = self.pos_embed(x1)
            x1 = self.encoder_big(x1, src_mask[0])
            x2 = self.motion_src_embed(src[1])
            x2 = self.pos_embed(x2)
            x2 = self.encoder_big(x2, src_mask[1])
            return x1 + x2
        elif self.feature_mode == 'three':
            x1 = self.image_src_embed(src[0])
            x1 = self.pos_embed(x1)
            x1 = self.encoder(x1, src_mask[0])
            x2 = self.motion_src_embed(src[1])
            x2 = self.pos_embed(x2)
            x2 = self.encoder(x2, src_mask[1])
            x3 = self.object_src_embed(src[2])
            x3 = self.pos_embed(x3)
            x3 = self.encoder(x3, src_mask[2])
            return x1 + x2 + x3
        elif self.feature_mode == 'four':
            x1 = self.image_src_embed(src[0])
            x1 = self.pos_embed(x1)
            x1 = self.encoder(x1, src_mask[0])

            x2 = self.motion_src_embed(src[1])
            x2 = self.pos_embed(x2)
            x2 = self.encoder(x2, src_mask[1])

            x3 = self.object_src_embed(src[2])
            # x3 = self.pos_embed(x3)
            x3 = self.encoder(x3, src_mask[2])
            # x3 = self.encoder_no_attention(x3, src_mask[2])

            x4 = self.rel_src_embed(src[3])
            # x4 = self.pos_embed(x4)
            # x4 = self.encoder_no_
            # heads(x4, src_mask[3])
            x4 = self.encoder_no_attention(x4, src_mask[3])
            # x4 = self.encoder(x4, src_mask[3])
            return x1 + x2 + x3 + x4

    def r2l_decode(self, r2l_trg, memory, src_mask, r2l_trg_mask):
        x = self.trg_embed(r2l_trg)
        x = self.pos_embed(x)
        return self.r2l_decoder(x, memory, src_mask, r2l_trg_mask)

    def l2r_decode(self, trg, memory, src_mask, trg_mask, r2l_memory, r2l_trg_mask):
        x = self.trg_embed(trg)
        x = self.pos_embed(x)
        return self.l2r_decoder(x, memory, src_mask, trg_mask, r2l_memory, r2l_trg_mask)

    def forward(self, src, r2l_trg, trg, mask):
        src_mask, r2l_pad_mask, r2l_trg_mask, trg_mask = mask
        if self.feature_mode == 'one':
            encoding_outputs = self.encode(src, src_mask)
            r2l_outputs = self.r2l_decode(r2l_trg, encoding_outputs, src_mask, r2l_trg_mask)
            l2r_outputs = self.l2r_decode(trg, encoding_outputs, src_mask, trg_mask, r2l_outputs, r2l_pad_mask)

        elif self.feature_mode == 'two' or 'three' or 'four':
            enc_src_mask, dec_src_mask = src_mask
            r2l_encoding_outputs = self.encode(src, enc_src_mask, feature_mode_two=True)
            encoding_outputs = self.encode(src, enc_src_mask)

            r2l_outputs = self.r2l_decode(r2l_trg, r2l_encoding_outputs, dec_src_mask[0], r2l_trg_mask)
            l2r_outputs = self.l2r_decode(trg, encoding_outputs, dec_src_mask[1], trg_mask, r2l_outputs, r2l_pad_mask)

            # r2l_outputs = self.r2l_decode(r2l_trg, encoding_outputs, dec_src_mask, r2l_trg_mask)
            # l2r_outputs = self.l2r_decode(trg, encoding_outputs, dec_src_mask, trg_mask, None, None)
        else:
            raise Exception("没有输出")

        r2l_pred = self.generator(r2l_outputs)
        l2r_pred = self.generator(l2r_outputs)

        return r2l_pred, l2r_pred

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/706121.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机msvcp100.dll丢失怎么办,分享5种亲测有效的解决方法

电脑已经成为我们生活中不可或缺的一部分。然而,在使用电脑的过程中,我们常常会遇到一些问题,其中之一就是电脑提示缺失msvcp100.dll。这个问题可能会让我们感到困惑和烦恼,但是只要我们了解其原因并采取相应的解决方法&#xff0…

React-配置json-server

安装json-server:json-server工具准备后端接口服务环境_jsonserver临时后端-CSDN博客 在package.json文件中的scripts添加: "serve":"json-server json文件路径 --port 端口号" 在终端输入命令npm run serve,就可以启动…

SPI通信外设

SPI外设介绍 时钟频率就是SCK波形的频率,一个SCK时钟交换一个bit,所以时钟频率一般体现的是传输速度,单位是Hz或者bit/s。可以看出来,SPI的时钟其实就是由pclk分频得来的,pclk就是外设时钟,APB2的PCLK就是7…

重复文件怎么查找并清理?6种重复文件清理方法亲测好用!

重复文件怎么查找并清理?重复的文件会占用计算机中不必要的空间,从而降低计算机速度。这些文件是您设备上现有文件的副本。您可能有照片、视频、音频、档案、文档等的文件副本。因此,当电脑被这些文件占用运行速度时,你会迫切地希…

Java项目:111 基于SpringBoot的在线家具商城设计与实现

作者主页:舒克日记 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 本系统有管理员和用户两个角色,包括前台商城平台及后台管理系统。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订…

探索Java 8 Stream API:现代数据处理的新纪元

Stream流 Stream初探:何方神圣? Stream流是一种处理集合数据的高效工具,它可以让你以声明性的方式处理数据集合。Stream不是存储数据的数据结构,而是对数据源(如集合、数组)的运算操作概念,支…

❤vue2项目webpack打包的优化策略

❤ vue2项目webpack打包的优化策略 (优化前) 现在我们的打包时间为: >打包体积大小为: 1、去除开发环境和生产环境提示以及日志 开发环境和生产环境的打印处理 生产环境去除console.log打印的两种方式 通过环境变量控制co…

一张图读懂天然气气源

一张图读懂天然气气源

《pvz植物大战僵尸杂交版》V2.0.88整合包火爆全网,支持安卓、ios、电脑等!

今天来给大家安利一款让人欲罢不能的游戏——《植物大战僵尸杂交版》2.0.88版。这可不是普通的植物大战僵尸,它可是席卷了B站,火爆全网的存在! 先说说这个版本,它可是网络上现存最全的植物大战僵尸杂交版整合包。里面不仅有修改工…

tkinter文本对齐方式

tkinter文本对齐方式 文本对齐方式效果代码 文本对齐方式 左对齐(left):默认对齐方式,文本从左边界开始。右对齐(right):文本从右边界开始。居中对齐(center)&#xff1…

NVIDIA MPS详解

NVIDIA 文章目录 NVIDIANVIDIA MPS介绍一、MPS作用二、MPS实例MPS与Hyper-Q区别Pascal架构和Volta架构不同架构上的MPS实现MPS基准测试MPS的使用MPS组成MPS执行过程开启与关闭MPS Volta MPS资源配置公平性MPS程序示例编写开启MPS脚本编写执行程序编写关闭MPS脚本运行MPS脚本运行…

linux部署运维3——centos7.9离线安装部署配置涛思taos2.6时序数据库TDengine以及java项目链接问题处理(二)

上一篇讲了centos7.9如何安装涛思taos2.6时序数据库的操作步骤和方案,本篇主要讲解taos数据库的初始化,相关配置说明,数据库和表的创建问题以及java项目连接问题。 centos7.9如何离线安装taos2.6,请点击下方链接详细查看&#xf…

新疆在线测宽仪配套软件实现的9大功能!

在线测宽仪可应用于各种热轧、冷轧板带材的宽度尺寸检测,材质不限,木质、钢制、铁质、金属、纸质、塑料、橡胶等都可以进行无损非接触式的检测,在各式各样的产线应用中,有些厂家,需要更加详尽完备的分析信息&#xff0…

离散化——Acwing.802区间和

离散化 定义 离散化可以简单理解为将连续的数值或数据转换为离散的、有限个不同的值或类别。离散化就是将一个可能具有无限多个取值或在一个较大范围内连续取值的变量,通过某种规则或方法,划分成若干个离散的区间或类别,并将原始数据映射到…

【Go】用 Go 原生以及 Gorm 读取 SQLCipher 加密数据库

本文档主要描述通过 https://github.com/mutecomm/go-sqlcipher 生成和读取 SQLCipher 加密数据库以及其中踩的一些坑 用 go 去生成读取 SQLCipher 数据库用 gorm 去读取 SQLCipher 数据库在生成后分别用 DBeaver、db browser 和 sqlcipher 读取 SQLCipher 数据库,…

基于jeecgboot-vue3的Flowable流程-流程处理(二)

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 对应VForm3&#xff0c;原先的后端解析也要做调整 1、获取历史任务的表单信息 // 获取历史任务节点表单数据值List<HistoricVariableInstance> listHistoricVariableInstance his…

Python第二语言(十二、SQL入门和实战)

目录 1. Python中使用MySQL 1.1 pymysql第三方库使用MySQL 1.2 连接MySQL 1.3 操作数据库&#xff0c;创建表 1.4 执行查询数据库语句 2. python中MySQL的插入语句 2.1 commit提交 2.2 自动提交 3. pymysql案例 3.1 数据内容 3.2 DDL定义 3.3 实现步骤 3.4 文件操…

最小生成树kruskal算法详解

kruskal算法的思想简单来说就是&#xff1a;每次选择图中最小边权的边&#xff0c;如果边两端的顶点不在相同的连通块中&#xff0c;就把这条边加入到最小生成树中。 具体实现如下&#xff1a; 首先是边的定义&#xff0c;需要判断边的两个端点是否在不同的连通块中&#xff…

Vue前端ffmpeg压缩视频再上传(全网唯一公开真正实现)

1.Vue项目中安装插件ffmpeg 1.1 插件版本依赖配置 两个插件的版本 "ffmpeg/core": "^0.10.0", "ffmpeg/ffmpeg": "^0.10.1"package.json 和 package-lock.json 都加入如下ffmpeg的版本配置&#xff1a; 1.2 把ffmpeg安装到项目依…

深入探究MySQL游标(Cursor)

前言 MySQL游标&#xff08;Cursor&#xff09;是MySQL中用于处理查询结果的一种机制。游标允许我们在查询结果集中逐行处理数据&#xff0c;而不是一次性获取所有数据。这对于处理大量数据非常有用&#xff0c;因为它可以减少内存消耗并提高性能。在MySQL中&#xff0c;游标主…