深度学习从入门到精通—Transformer

news2024/9/23 17:23:29

1.绪论介绍

1.1 传统的RNN网络

传统的RNN(递归神经网络)主要存在以下几个问题:

  1. 梯度消失和梯度爆炸:这是RNN最主要的问题。由于序列的长距离依赖,当错误通过层传播时,梯度可以变得非常小(消失)或非常大(爆炸),这使得网络难以学习。

  2. 计算效率低:RNN由于其递归性质,必须按序列顺序执行计算,这限制了计算的并行性。对于长序列,这会导致训练过程非常慢。

  3. 难以捕捉长距离依赖:尽管理论上RNN能够处理任何长度的序列,但在实践中,它们往往难以学习到输入序列中的长距离依赖关系。

为了解决这些问题,研究者们开发了一些改进的RNN结构,如长短时记忆网络(LSTM)和门控循环单元(GRU),这些结构通过引入门控机制来调节信息的流动,有效地缓解了梯度消失的问题,提高了模型对长距离依赖的捕捉能力。但是依然无法解决并行计算问题;在这里插入图片描述

1.2 Transformer优势

  • Self-Attention机制来进行并行计算,在输入和输出都相同
  • Self-Attention 允许模型在处理数据时同时考虑序列中的所有元素,而不需要顺序处理每个时间步。这意味着模型可以同时处理序列中的多个位置,大幅提高了计算效率。因此,Self-Attention 特别适合于需要处理大规模数据集的任务,能够有效利用现代多核处理器的并行计算能力。现在基本已经取代RNN了
    在这里插入图片描述

1.3传统word2vec

Word2Vec 是一种广泛使用的词嵌入方法,主要由两种模型构成:连续词袋(CBOW)跳字模型(Skip-gram)

  • CBOW 模型通过上下文的词来预测目标词
    在这里插入图片描述
    • 跳字模型则是通过一个目标词来预测它周围的上下文词
      在这里插入图片描述
  • 这两种模型都利用大量文本数据,通过学习词与词之间的关系来生成词向量。
  • CBOW模型通过上下文预测目标词,适合大型数据集且训练速度较快。相比之下,Skip-gram模型通过目标词预测周围上下文,适合小型数据集,能够捕捉稀有词的细节,虽然训练速度较慢但表现更佳。
    这种方法存在几个问题:
  1. 词义多样性: Word2Vec 生成的词向量无法有效表达多义词的不同含义,每个词只有一个向量表示。
  2. 依赖于大量文本数据: 要获得有意义的词向量,Word2Vec 需要大量的文本数据进行训练。
  3. 缺乏语境感知: 生成的词向量不考虑上下文环境,因此在理解依赖上下文的语言特性时可能效果不佳。

尽管存在这些问题,Word2Vec 仍然是自然语言处理领域的基础工具之一,为后续的模型如BERT、GPT等提供了基础架构上的启示。

2. Transformer

思考以下问题:

  • 输入如何编码?
  • 输出结果是什么?
  • Attention的目的?
  • 怎样组合在一起?

2.1 注意力

  • 对于于输入的数据,你的关注点是什么?
  • 注意力机制(Attention Mechanism)是一种用于增强神经网络特定部分的重要性的技术,它最初是在视觉图像处理领域被提出,并在自然语言处理(NLP)中获得了广泛的应用和发展,尤其是在机器翻译任务中表现显著。
2.1.1 原理

注意力机制的核心思想是在处理信息时不是平等地对待所有的数据,而是让模型学会自动地将“注意力”集中在更重要的信息上。在NLP中,这通常意味着模型将更多地关注与当前任务最相关的单词或短语。例如,在机器翻译中,当模型试图翻译一个词时,它会考虑输入句子中哪些词与当前翻译的词最相关,并相应地调整其处理重点。

2.1.2 由来

注意力机制最早是由Bahdanau等人在2014年提出,用于改进序列到序列(Seq2Seq)的模型,特别是在机器翻译任务中。他们的模型通过对输入数据的不同部分赋予不同的权重,动态地选择性地关注输入序列的特定部分,从而改进了模型对长句子的处理能力。这种动态权重分配机制允许模型在翻译时更加灵活和准确,特别是在对齐和选择翻译时最为关键的词汇方面。

2.1.3 发展

随后,注意力机制在许多深度学习应用中被广泛采用,包括但不限于文本摘要、语音识别和计算机视觉等领域。2017年,Google的研究者提出了“Transformer”模型,该模型完全基于注意力机制,不使用任何卷积或递归层。Transformer模型凭借其高效的并行处理能力和出色的性能,迅速成为了许多NLP任务的基础架构,包括后来的BERT、GPT等影响深远的模型。

2.1.4 实现

注意力机制的一个常见形式是所谓的“缩放点积注意力”,它的数学公式可以表示为:

Attention ( Q , K , V ) = softmax ( Q K T d k ) V \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V Attention(Q,K,V)=softmax(dk QKT)V

这里,(Q)、(K)、和 (V) 分别代表查询(query)、键(key)、和值(value)矩阵,(d_k) 是键向量的维度。该公式通过查询和键的点积来计算一个权重,然后这个权重通过 softmax 函数进行归一化,最终这个归一化的权重用来加权值矩阵 (V),从而得到加权后的输出,重点关注与查询最相关的信息。

下面是使用 PyTorch 实现简单的缩放点积注意力机制的代码示例:

import torch
import torch.nn.functional as F

def scaled_dot_product_attention(Q, K, V):
    d_k = K.size(-1)  # 获取键向量的维度
    scores = torch.matmul(Q, K.transpose(-2, -1)) / torch.sqrt(d_k)
    weights = F.softmax(scores, dim=-1)  # 沿最后一个维度应用 Softmax
    output = torch.matmul(weights, V)  # 加权值矩阵
    return output

# 假设 d_model = 512 且 batch_size = 1, seq_length = 10
d_model = 512
batch_size = 1
seq_length = 10

# 随机生成 Q, K, V 矩阵
Q = torch.rand(batch_size, seq_length, d_model)
K = torch.rand(batch_size, seq_length, d_model)
V = torch.rand(batch_size, seq_length, d_model)

# 调用函数并打印输出
output = scaled_dot_product_attention(Q, K, V)
print(output)

这段代码定义了一个函数 scaled_dot_product_attention,它接受查询 (Q)、键 (K)、值 (V) 三个矩阵作为输入,并返回注意力后的输出矩阵。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2 自注意力

自注意力(Self-Attention),也称为内部注意力,是一种注意力机制,它能使模型在处理一个序列的时候,能够考虑序列中的所有元素的相关性。在自注意力机制中,查询(Q)、键(K)和值(V)都来自同一个输入。这使得模型能够在输入序列的每个位置,评估其他位置的重要性,并据此更新自身。

自注意力的优势在于它的并行计算能力和对输入序列内部动态关系的捕捉能力。这种机制广泛应用于自然语言处理(NLP)领域,尤其是在Transformer架构中,它通过自注意力层来捕获单词之间的复杂依赖关系,从而改善了翻译质量、文本生成和其他语言理解任务的性能。

自注意力的计算过程通常包括以下步骤:

  1. 线性变换:输入序列经过线性变换生成查询(Q)、键(K)和值(V)。
  2. 计算注意力分数:通过计算查询和所有键的点积,得到注意力分数。
  3. 应用Softmax:对注意力分数应用Softmax函数,使得分数转换为概率形式,表征每个元素对其他元素的影响程度。
  4. 加权和:利用Softmax输出的权重对值(V)进行加权求和,得到最终的输出。

自注意力机制能够在模型中引入更多的上下文信息,增强模型对信息的整合能力,从而在处理复杂的序列数据时提供更丰富的表征。
在这里插入图片描述

如何计算

输入经过编码后得到向量想得到当前词语上下文的关系,可以当作是是加权构建三个矩阵分别来查询当前词跟其他词的关系,以及特征向量的表达。
在这里插入图片描述
三个需要训练的矩阵
Q:query,要去查询的
K:key,等着被查的
V:value,实际的特征信息

q与k的内积表示有多匹配输入两个向量得到一个分值K:key,等着被查的
V:value,实际的特征A信息

最终的得分值经过softmax就是最终上下文结果ScaledDot-ProductAttention
不能让分值随着向量维度的增大而增加
在这里插入图片描述
在这里插入图片描述

每个词的Q会跟整个序列中每一个K计算得分,然后基于得分再分配特征
在这里插入图片描述

每个词的Q会跟每一个K计算得分
Softmax后就得到整个加权结果
此时每个词看的不只是它前面的序列而是整个输入序列
同一时间计算出所有词的表示结果

在这里插入图片描述

import torch
import torch.nn as nn
import torch.nn.functional as F

class SelfAttention(nn.Module):
    def __init__(self, embed_size, heads):
        super(SelfAttention, self).__init__()
        self.embed_size = embed_size
        self.heads = heads
        self.head_dim = embed_size // heads

        assert self.head_dim * heads == embed_size, "Embed size needs to be divisible by heads"

        self.values = nn.Linear(self.head_dim, self.head_dim, bias=False)
        self.keys = nn.Linear(self.head_dim, self.head_dim, bias=False)
        self.queries = nn.Linear(self.head_dim, self.head_dim, bias=False)
        self.fc_out = nn.Linear(heads * self.head_dim, embed_size)

    def forward(self, values, keys, queries, mask=None):
        N = queries.shape[0]
        value_len, key_len, query_len = values.shape[1], keys.shape[1], queries.shape[1]

        # Split the embedding into self.heads different pieces
        values = values.reshape(N, value_len, self.heads, self.head_dim)
        keys = keys.reshape(N, key_len, self.heads, self.head_dim)
        queries = queries.reshape(N, query_len, self.heads, self.head_dim)

        values = self.values(values)
        keys = self.keys(keys)
        queries = self.queries(queries)

        # Dot product between queries and keys for each batch and head
        energy = torch.einsum("nqhd,nkhd->nhqk", [queries, keys])

        if mask is not None:
            energy = energy.masked_fill(mask == 0, float("-1e20"))

        attention = torch.softmax(energy / (self.embed_size ** (1 / 2)), dim=3)

        out = torch.einsum("nhql,nlhd->nqhd", [attention, values]).reshape(N, query_len, self.heads * self.head_dim)
        
        out = self.fc_out(out)

        return out

# Example usage
embed_size = 256
heads = 8
attention = SelfAttention(embed_size, heads)
input_tensor = torch.rand((1, 10, embed_size))  # (batch_size, sequence_length, embed_size)
output = attention(input_tensor, input_tensor, input_tensor)
print(output.shape)  # Expected shape: (1, 10, 256)

2.3 multi-headed机制

一组q,k,v得到了一组当前词的特征表达类似卷积神经网络中的filter
能不能提取多种特征呢?

多头注意力机制(Multi-head Attention)是一种在自然语言处理和相关领域中广泛使用的技术,它可以帮助模型更好地捕捉信息中的不同方面(如不同位置的语义关系)。这种机制最著名的应用是在Transformer模型中,它通过并行地处理数据以捕捉复杂的依赖关系,提高了模型的表达能力和性能。

2.3.1 基本概念

多头注意力机制的核心思想是将注意力操作分割成多个“头”,每个头独立地学习输入数据的不同表示。这样做的好处是可以让模型在不同的子空间中捕获信息,从而更全面地理解数据。

2.3.2 组件和操作
  1. 线性变换:输入序列(Queries, Keys, Values)首先通过线性层被映射到多个不同的空间(对应于不同的头)。
  2. 点积注意力:每个头计算点积注意力,这是通过对Queries和Keys的点积得到的分数进行softmax运算,然后这些分数用于加权Values。
  3. 拼接与最终线性变换:所有头的输出被拼接起来,然后通过另一个线性变换产生最终的输出。
2.3.3 公式表达

设有h个头,输入的维度为 d m o d e l d_{model} dmodel,每个头处理的维度为 d k = d m o d e l / h d_k = d_{model} / h dk=dmodel/h。对于每个头 i i i

  1. 对于Queries, Keys, Values执行线性变换:
    Q i = Q W i Q , K i = K W i K , V i = V W i V Q_i = QW_i^Q, \quad K_i = KW_i^K, \quad V_i = VW_i^V Qi=QWiQ,Ki=KWiK,Vi=VWiV
    其中 W i Q , W i K , W i V W_i^Q, W_i^K, W_i^V WiQ,WiK,WiV是线性变换的权重矩阵。

  2. 计算每个头的输出:
    head i = Attention ( Q i , K i , V i ) = softmax ( Q i K i T d k ) V i \text{head}_i = \text{Attention}(Q_i, K_i, V_i) = \text{softmax}\left(\frac{Q_iK_i^T}{\sqrt{d_k}}\right)V_i headi=Attention(Qi,Ki,Vi)=softmax(dk QiKiT)Vi

  3. 拼接所有头的输出,并进行最终的线性变换:
    MultiHead ( Q , K , V ) = Concat ( head 1 , . . . , head h ) W O \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, ..., \text{head}_h)W^O MultiHead(Q,K,V)=Concat(head1,...,headh)WO
    其中 W O W^O WO是最终线性层的权重矩阵。

2.3.4 优势
  • 多角度学习:通过多个头观察数据的多个方面,模型可以在不同的表示子空间中捕获更丰富的信息。
  • 灵活性增强:多头注意力允许模型在各种任务中更灵活地调整其注意力焦点。
  • 并行处理:每个头的操作可以并行进行,这在实现上可以显著提高效率。

多头注意力机制提供了一种有效的方式来增强模型的表达力和性能,特别是在处理复杂的序列数据时,如机器翻译、文本摘要等任务中。
卷积中的特征图:
在这里插入图片描述
在这里插入图片描述
不同的注意力结果
得到的特征向量表达也不相同

在这里插入图片描述

2.3.5 堆叠多层

在这里插入图片描述

2.4 位置信息表达

在Transformer模型中,位置编码(Positional Encoding)是用来给模型提供关于单词在序列中位置的信息的一种机制。由于Transformer中的self-attention机制本身不考虑序列的顺序,仅通过内容来计算关联度,所以位置编码成为确保模型能够理解词汇顺序的关键组件。

2.4.1 工作原理

位置编码将每个位置的索引映射到一个高维空间,以捕捉位置间的相对或绝对关系。在原始的Transformer模型中,位置编码通过正弦和余弦函数的组合来实现,公式如下:

P E ( p o s , 2 i ) = sin ⁡ ( p o s 1000 0 2 i / d m o d e l ) PE(pos, 2i) = \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right) PE(pos,2i)=sin(100002i/dmodelpos)
P E ( p o s , 2 i + 1 ) = cos ⁡ ( p o s 1000 0 2 i / d m o d e l ) PE(pos, 2i+1) = \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right) PE(pos,2i+1)=cos(100002i/dmodelpos)

其中( pos )是词汇在序列中的位置,( i )是维度索引,( d_{model} )是模型的维度。通过这种方式,每个位置生成一个唯一的编码,可以加到对应词汇的嵌入向量中。

2.4.2 优点与作用
  1. 捕捉序列信息:通过位置编码,Transformer能够理解单词在文本中的顺序,这对于理解语言结构非常关键,如句法依赖和语序相关性。

  2. 泛化能力:使用正弦和余弦函数允许模型将位置信息泛化到未见过的位置长度,即模型能处理比训练时更长的序列。

  3. 无需额外参数:位置编码是固定的,不需要通过训练学习,这减少了模型的复杂度和过拟合的风险。

2.4.3 应用

位置编码的应用使得Transformer模型在处理需要顺序信息的任务(如机器翻译、文本生成等)时表现出色。同时,位置编码的设计也影响了后续模型的演变,例如各种变种尝试使用可学习的位置编码以适应不同的任务需求。

在self-attention中每个词都会考虑整个序列的加权,所以其出现位置并不会对结果产生什么影响,相当于放哪都无所谓,但是这跟实际就有些不符合了,我们希望模型能对位置有额外的认识。

在这里插入图片描述

在这里插入图片描述

  1. 初始化:PositionalEncoding 类初始化时计算整个编码矩阵,它只需要计算一次。我们创建一个零矩阵,然后填充正弦和余弦值。
  2. 正弦和余弦计算:使用广播机制来填充正弦和余弦值到编码矩阵中。通过指数衰减的方式调整频率,使得模型可以在较大范围内识别位置信息。
  3. 前向传播:在前向传播方法中,我们将位置编码加到输入的嵌入向量上。这里使用 .detach() 来避免在位置编码上进行梯度计算,因为位置编码是固定的,不需要更新。
import torch
import math

class PositionalEncoding(torch.nn.Module):
    def __init__(self, d_model, max_len=5000):
        super(PositionalEncoding, self).__init__()
        self.encoding = 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))

        self.encoding[:, 0::2] = torch.sin(position * div_term)
        self.encoding[:, 1::2] = torch.cos(position * div_term)
        self.encoding = self.encoding.unsqueeze(0)

    def forward(self, x):
        return x + self.encoding[:, :x.size(1)].detach()

# Example usage
d_model = 512  # Dimension of the model
max_len = 100  # Maximum length of the input sequences
pos_encoder = PositionalEncoding(d_model, max_len)

# Assume x is your batch of token embeddings with shape [batch_size, sequence_length, d_model]
x = torch.randn(10, 50, d_model)  # Example tensor
x = pos_encoder(x)

2.5 Add与Normalize

在Transformer模型中,“Add” 和 “Normalize” 是每个编码器和解码器层中的重要部分,具体体现在残差连接(Residual Connection)和层归一化(Layer Normalization)中。

2.5.1 Add (残差连接)

在Transformer的每个子层(如自注意力层和前馈神经网络)后面,都有一个残差连接,其操作是将子层的输入加到其输出上。这种结构有助于缓解深层网络中的梯度消失问题,从而支持模型学习更深层次的表示。具体来说,对于任意一个输入 (x) 和一个子层函数 (F(x)),残差连接的输出是 x + F ( x ) x + F(x) x+F(x),这里的 (+) 就是 Add 操作。

2.5.2Normalize (层归一化)

Add 操作之后,通常会有一个层归一化步骤。层归一化是在特征的最后一维上(即对每个样本单独归一化,而不是整个批次)进行归一化,使得输出均值为0,方差为1。归一化通常是在添加残差之后进行,其目的是帮助模型在训练过程中保持稳定,加速收敛,并减少了不同初始化带来的影响。在数学上,层归一化可以表示为:
LayerNorm ( x + F ( x ) ) \text{LayerNorm}(x + F(x)) LayerNorm(x+F(x))

这种Add和Normalize的组合方式,在每个编码器和解码器层中重复使用,有助于保持不同层的学习在一个稳定的数值范围内,从而改善模型的训练效率和最终的学习效果。
在这里插入图片描述

在这里插入图片描述

2.6 Decoder

Transformer模型中的解码器(Decoder)部分是负责将编码器输出的信息转换成最终输出序列的组件。解码器在结构上与编码器相似,但包括一些额外的机制来处理序列生成任务。具体来说,解码器由多个相同的层组成,每层都包含三个主要的子模块:

  1. 掩蔽自注意力机制(Masked Self-Attention)

    • 解码器的第一个子模块是掩蔽自注意力层,这里的“掩蔽”指的是防止位置关注到未来的位置信息。这是通过在自注意力计算中应用一个三角形掩蔽(mask),确保位置i只能关注到位置i之前(包括i)的输出。
    • 这种掩蔽保证解码器在生成当前词时只依赖于已生成的词,从而有助于顺序生成输出序列。
  2. 编码器-解码器注意力机制(Encoder-Decoder Attention)

    • 第二个子模块是编码器-解码器注意力层,其中解码器利用自己的输出作为查询,而将编码器的输出作为键和值。
    • 这允许每个位置的解码器访问整个输入序列的信息,从而更好地理解和翻译输入的上下文。
  3. 前馈神经网络(Feed-Forward Networks)

    • 每个解码器层还包括一个前馈神经网络,这是一个独立于序列位置的同样操作。该网络包含两个线性变换和一个激活函数。
    • 该层主要作用是对每个位置的表示进行进一步的处理。

每个子模块后面都会跟一个残差连接和层归一化步骤,这与编码器中的操作相同。这些层的堆叠使得解码器不仅能够处理当前位置的信息,还能够利用整个输入序列的信息,有效地生成序列的每个部分。

整体上,解码器的设计使其能够在接收前面生成的输出的同时,考虑整个输入序列的上下文,从而有效地进行序列到序列的学习和生成任务。
Attention计算不同加入了MASK机制

2.6.1 Masked Self-Attention

掩蔽自注意力机制(Masked Self-Attention)是Transformer模型中的一种技术,主要用于解码器部分以防止未来信息的提前泄露。其实现方法如下:

  1. 生成掩蔽矩阵:首先,生成一个上三角形的掩蔽矩阵,其中上三角部分(包括对角线上方的元素)都设置为负无穷大(或非常大的负数),其余部分设置为0。这种掩蔽确保了在计算当前词元时,只能利用该词元以及它之前的词元的信息。

  2. 计算注意力分数:在自注意力机制中,首先计算查询(Query)、键(Key)和值(Value)矩阵。注意力分数是通过查询矩阵与键矩阵的点积得到的,然后将掩蔽矩阵应用于这些分数上。

  3. 应用Softmax:将掩蔽后的注意力分数通过Softmax函数进行归一化处理,这确保了只有未被掩蔽的部分(即当前及之前的词元)对最终输出有贡献。

  4. 计算输出:最后,将Softmax输出的结果与值(Value)矩阵相乘,得到掩蔽自注意力的输出。

这种机制是Transformer解码器正确处理序列生成任务的关键,例如在机器翻译或文本生成中,能够确保模型在预测下一个词时只依赖于前面的词。

import torch
import torch.nn as nn
import torch.nn.functional as F

class MaskedSelfAttention(nn.Module):
    def __init__(self, embed_size):
        super(MaskedSelfAttention, self).__init__()
        self.query = nn.Linear(embed_size, embed_size)
        self.key = nn.Linear(embed_size, embed_size)
        self.value = nn.Linear(embed_size, embed_size)

    def forward(self, x, mask):
        Q = self.query(x)
        K = self.key(x)
        V = self.value(x)

        # 计算Q和K的点积,并除以维度的平方根,以稳定梯度
        attention_scores = torch.matmul(Q, K.transpose(-2, -1)) / torch.sqrt(torch.tensor(x.size(-1), dtype=torch.float32))
        
        # 应用掩蔽,将未来位置的得分设置为负无穷
        attention_scores = attention_scores.masked_fill(mask == 0, float('-inf'))
        
        # 应用Softmax得到注意力权重
        attention_weights = F.softmax(attention_scores, dim=-1)
        
        # 计算输出
        output = torch.matmul(attention_weights, V)
        return output

# 假设embed_size为64,序列长度为10
embed_size = 64
seq_length = 10

# 创建模型实例
model = MaskedSelfAttention(embed_size)

# 创建输入数据,假设batch_size为1
x = torch.rand(1, seq_length, embed_size)

# 创建掩蔽矩阵,确保只能看到当前及之前的位置
mask = torch.tril(torch.ones(seq_length, seq_length)).unsqueeze(0)

# 调用模型
output = model(x, mask)
print(output)

在这里插入图片描述

2.6.2 编码器和解码器注意力机制

seqseq :基于LSTM的编码器和解码器,并通过注意力机制将编码器的输出与解码器的每一步输出关联起来。这允许解码器在生成输出时重点关注输入序列的相关部分。

import torch
import torch.nn as nn
import torch.nn.functional as F

class Encoder(nn.Module):
    def __init__(self, input_dim, hidden_dim):
        super(Encoder, self).__init__()
        self.hidden_dim = hidden_dim
        self.lstm = nn.LSTM(input_dim, hidden_dim, batch_first=True)

    def forward(self, src):
        outputs, (hidden, cell) = self.lstm(src)
        return outputs, hidden, cell

class Decoder(nn.Module):
    def __init__(self, output_dim, hidden_dim):
        super(Decoder, self).__init__()
        self.output_dim = output_dim
        self.hidden_dim = hidden_dim
        self.lstm = nn.LSTM(output_dim, hidden_dim, batch_first=True)
        self.fc_out = nn.Linear(hidden_dim, output_dim)
        self.attention = nn.Linear(self.hidden_dim * 2, 1)

    def forward(self, input, hidden, cell, encoder_outputs):
        # 重复最后的隐藏状态来匹配编码器输出的维度
        hidden_repeated = hidden.repeat(encoder_outputs.size(1), 1, 1).transpose(0, 1)

        # 计算注意力权重
        energy = torch.tanh(self.attention(torch.cat((hidden_repeated, encoder_outputs), dim=2)))
        attention_weights = F.softmax(energy.squeeze(2), dim=1).unsqueeze(1)

        # 应用注意力权重到编码器输出
        weighted = torch.bmm(attention_weights, encoder_outputs)
        lstm_input = torch.cat((input.unsqueeze(1), weighted), dim=2)

        # 通过LSTM传递
        output, (hidden, cell) = self.lstm(lstm_input, (hidden, cell))
        prediction = self.fc_out(output.squeeze(1))

        return prediction, hidden, cell

class Seq2Seq(nn.Module):
    def __init__(self, encoder, decoder):
        super(Seq2Seq, self).__init__()
        self.encoder = encoder
        self.decoder = decoder

    def forward(self, src, trg):
        batch_size = trg.shape[0]
        trg_len = trg.shape[1]
        trg_vocab_size = self.decoder.output_dim

        # 存储输出
        outputs = torch.zeros(batch_size, trg_len, trg_vocab_size).to(trg.device)

        # 编码器的最后隐藏状态用作解码器的初始隐藏状态
        encoder_outputs, hidden, cell = self.encoder(src)

        # 解码器的第一输入初始化为<sos>标记
        input = trg[:,0]

        for t in range(1, trg_len):
            output, hidden, cell = self.decoder(input, hidden, cell, encoder_outputs)
            outputs[:, t] = output
            input = output.argmax(1)  # 贪婪解码

        return outputs

# 参数设置
INPUT_DIM = 10
OUTPUT_DIM = 10
HIDDEN_DIM = 128

# 创建模型实例
encoder = Encoder(INPUT_DIM, HIDDEN_DIM)
decoder = Decoder(OUTPUT_DIM, HIDDEN_DIM)
model = Seq2Seq(encoder, decoder)

# 假设源和目标语言的维度都是10
src = torch.rand(1, 5, INPUT_DIM)  # (batch_size, src_len, input_dim)
trg = torch.rand(1, 5, OUTPUT_DIM)  # (batch_size, trg_len, output_dim)

# 调用模型
output = model(src, trg)
print(output)

Transformer模型是一种基于注意力机制,特别是自注意力(self-attention)和互注意力(cross-attention)机制来处理序列数据的架构。它在很多自然语言处理任务中表现出色,如机器翻译、文本摘要等。Transformer的编码器和解码器都是由多个相同的层堆叠而成,每一层包括多头注意力机制和全连接的前馈网络;

'''
Transformer模型是一种基于注意力机制,特别是自注意力(self-attention)和互注意力(cross-attention)机制来处理序列数据的架构。它在很多自然语言处理任务中表现出色,如机器翻译、文本摘要等。Transformer的编码器和解码器都是由多个相同的层堆叠而成,每一层包括多头注意力机制和全连接的前馈网络。
'''
### 定义模型

```python
import torch
import torch.nn as nn
import torch.nn.functional as F

class MultiHeadAttention(nn.Module):
    def __init__(self, heads, d_model, dropout=0.1):
        super().__init__()
        self.d_model = d_model
        self.d_k = d_model // heads
        self.h = heads

        self.q_linear = nn.Linear(d_model, d_model)
        self.v_linear = nn.Linear(d_model, d_model)
        self.k_linear = nn.Linear(d_model, d_model)
        self.dropout = nn.Dropout(dropout)
        self.out = nn.Linear(d_model, d_model)
    
    def forward(self, q, k, v, mask=None):
        bs = q.size(0)

        # perform linear operation and split into h heads
        k = self.k_linear(k).view(bs, -1, self.h, self.d_k)
        q = self.q_linear(q).view(bs, -1, self.h, self.d_k)
        v = self.v_linear(v).view(bs, -1, self.h, self.d_k)

        # transpose to get dimensions bs * h * sl * d_model
        k = k.transpose(1,2)
        q = q.transpose(1,2)
        v = v.transpose(1,2)

        # calculate attention using function we will define next
        scores = attention(q, k, v, self.d_k, mask, self.dropout)
        
        # concatenate heads and put through final linear layer
        concat = scores.transpose(1,2).contiguous().view(bs, -1, self.d_model)
        output = self.out(concat)
    
        return output

def attention(q, k, v, d_k, mask=None, dropout=None):
    scores = torch.matmul(q, k.transpose(-2, -1)) /  math.sqrt(d_k)
    if mask is not None:
        scores = scores.masked_fill(mask == 0, float('-inf'))
    scores = F.softmax(scores, dim=-1)
    if dropout is not None:
        scores = dropout(scores)
    output = torch.matmul(scores, v)
    return output

class FeedForward(nn.Module):
    def __init__(self, d_model, d_ff=2048, dropout=0.1):
        super().__init__()
        # We set d_ff as a default to 2048
        self.linear_1 = nn.Linear(d_model, d_ff)
        self.dropout = nn.Dropout(dropout)
        self.linear_2 = nn.Linear(d_ff, d_model)
    
    def forward(self, x):
        x = F.relu(self.linear_1(x))
        x = self.dropout(x)
        x = self.linear_2(x)
        return x

class EncoderLayer(nn.Module):
    def __init__(self, d_model, heads, dropout=0.1):
        super().__init__()
        self.norm_1 = nn.LayerNorm(d_model)
        self.norm_2 = nn.LayerNorm(d_model)
        self.attn = MultiHeadAttention(heads, d_model, dropout=dropout)
        self.ff = FeedForward(d_model, dropout=dropout)
    
    def forward(self, x, mask):
        x2 = self.norm_1(x)
        x = x + self.attn(x2, x2, x2, mask)
        x2 = self.norm_2(x)
        x = x + self.ff(x2)
        return x

class DecoderLayer(nn.Module):
    def __init__(self, d_model, heads, dropout=0.1):
        super().__init__()
        self.norm_1 = nn.LayerNorm(d_model)
        self.norm_2 = nn.LayerNorm(d_model)
        self.norm_3 = nn.LayerNorm(d_model)
        self.attn_1 = MultiHeadAttention(heads, d_model, dropout=dropout)
        self.attn_2 = MultiHeadAttention(heads, d_model, dropout=dropout)
        self.ff = FeedForward(d_model, dropout=dropout)

    def forward(self, x, e_outputs, src_mask, trg_mask):
        x2 = self.norm_1(x

得出最终预测结果
损失函数cross-entropy即可

在这里插入图片描述

总结

Self-Attention
Multi-Head
多层堆叠,位置编码并行加速训练

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MATLAB中左边的大括号最后一行为什么会留很大的空白——解决

看了一些帖子说改字体&#xff0c;但是并没有什么用&#xff0c;在此给出亲测有效的方法&#xff1a;改变矩阵的行间距 先说一下问题 上图中留有大块空白 **解决办法&#xff1a;**光标放在矩阵上 格式——矩阵——更改矩阵&#xff0c;在矩阵设置中选中“行高相等”&#xff…

基于Springboot的学生毕业离校系统

基于SpringbootVue的学生毕业离校系统的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 网站首页 离校流程 网站公告 留言反馈 后台登录 学生管理 教师管理 离校流程…

【UI】element-ui的el-dialog的遮罩层在模态框的前面bug

最近在写element ui 的时候使用dialog组件&#xff0c;偶然出现了这种情况 原因&#xff1a; 是因为遮罩层插入进了body标签下&#xff0c;z-index高于当前父元素。 解决&#xff1a;在el-dialog标签里加上:modal-append-to-body"false"就可以了。 饿了么官网文档&a…

Redis - Redisson tryLock 函数参数分析

这里有三个参数&#xff1a; waitTime&#xff1a;等待时间leaseTime&#xff1a;超时施放时间TimeUnit&#xff1a;时间单位 等待时间 如果 ABC… 多个线程去抢夺一把锁&#xff0c;A 成功了&#xff0c;如果设置的是 -1&#xff0c;那么 BCD... 就不等待&#xff0c;直接返…

虚拟化+Docker基本管理

一、虚拟化简介 1、云端 华为云、谷歌云、腾讯云、阿里云、亚马逊、百度云、移动云、天翼云、西部数码云等 1.国内云 华为云、阿里云、腾讯云、天翼云(私有云) 2.国外云 谷歌云、亚马逊 2、云计算的服务模式是分层的 IaaS&#xff1a;Infrastructure&#xff08;基础设…

蓝桥杯第17169题——兽之泪II

问题描述 在蓝桥王国&#xff0c;流传着一个古老的传说&#xff1a;在怪兽谷&#xff0c;有一笔由神圣骑士留下的宝藏。 小蓝是一位年轻而勇敢的冒险家&#xff0c;他决定去寻找宝藏。根据远古卷轴的提示&#xff0c;如果要找到宝藏&#xff0c;那么需要集齐 n 滴兽之泪&#…

【YOLOv8改进[注意力]】YOLOv8添加DAT(Vision Transformer with Deformable Attention)助力涨点

目录 一 DAT 二 YOLOv8添加DAT助力涨点 1 总体修改 2 配置文件 3 训练 其他 一 DAT 官方论文地址&#xff1a;https://openaccess.thecvf.com/content/CVPR2022/papers/Xia_Vision_Transformer_With_Deformable_Attention_CVPR_2022_paper.pdf Transformers最近在各种视…

js连接抖音打印组件实现打印

js连接抖音打印组件实现打印小票 安装抖音打印组件 抖音打印组件文档&#xff1a; https://bytedance.larkoffice.com/docs/doccn2vbOOdd3KWrCd6Z93nIlvg 跟着文档案例一步步配基本上没问题&#xff0c; 打印的时候需要设置下打印机名称 export class DouyinPrint {construct…

完美运营版商城/拼团/团购/秒杀/积分/砍价/实物商品/虚拟商品等全功能商城

源码下载地址&#xff1a;完美运营版商城.zip 后台可以自由拖曳修改前端UI页面 还支持虚拟商品自动发货等功能 挺不错的一套源码 前端UNIAPP 后端PHP 一键部署版本

【计算机组成原理】浮点运算方法和浮点运算器

浮点加法、减法运算 浮点数加减法的步骤结合题目分析步骤 浮点数加减法的步骤 ① 0 操作数检查 ② 比较阶码大小&#xff0c;完成对阶 ③ 尾数进行加减法运算 ④ 结果规格化 ⑤ 舍入处理 ⑥ 判断结果是否溢出 结合题目分析步骤 例&#xff1a;设 x 2010 0.11011011&#x…

展商企业【广东伟创科技开发有限公司】| 2024水科技大会暨技术装备成果展

企业介绍 广东伟创科技开发有限公司成立于2006年&#xff0c;位于广东省江门市。公司是华南理工大学造纸与污染控制国家工程研究中心科技成果转化单位&#xff1b;是华南理工大学产学研合作单位&#xff1b;是广东省高新技术企业&#xff1b;是江门市现代信息服务业重点企业&am…

在线音乐播放网站项目测试(selenium+Junit5)

在做完在线音乐播放网站项目之后&#xff0c;需要对项目的功能、接口进行测试&#xff0c;利用测试的工具&#xff1a;selenium以及Java的单元测试工具Junit进行测试&#xff0c;下面式测试的思维导图&#xff0c;列出该项目需要测试的所有测试用例&#xff1a; 测试结果&#…

嵌入式系统相关知识总结

一、概述 嵌入式系统是以应用为中心、以计算机技术为基础&#xff0c;并将可配置与可裁剪的软、硬件集成与一体的专用计算机系统&#xff0c;需要满足应用对功能、可靠性、成本、提及和功耗等方面的严格要求。 从计算机角度看&#xff0c;嵌入式系统是指嵌入各种设备及应用产品…

使用Jest测试框架测试JS项目

前言 JavaScript的测试框架有很多&#xff0c;这里主要记录一些自己在初次使用jest时遇到的一些问题。详细使用文档可以参照官方说明文档。 简介 Jest 是一款优雅、简洁的 JavaScript 测试框架。 Jest 支持 Babel、TypeScript、Node、React、Angular、Vue 等诸多框架&#…

软件测试之【合理的利用GPT来辅助软件测试一】

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f525; 欢迎来到我的博客 &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️寻至善的主页 文章目录 前言GPT的原理及技巧GPT辅助接口自动化测试 前言 在编程基础栏目中&#xff…

循环神经网络实例——序列预测

我们生活的世界充满了形形色色的序列数据&#xff0c;只要是有顺序的数据统统都可以看作是序列数据&#xff0c;比如文字是字符的序列&#xff0c;音乐是音符组成的序列&#xff0c;股价数据也是序列&#xff0c;连DNA序列也属于序列数据。循环神经网络RNN天生就具有处理序列数…

嵌入式linux学习之arm开发板移植ssh

1.下载源码 &#xff08;1&#xff09;zlib 下载网址&#xff1a;http://www.zlib.net/fossils/ 教程中版本选择的是: zlib-1.2.11.tar.gz &#xff08;2&#xff09;openssl下载网址&#xff1a;https://www.openssl.org/source/mirror.html 教程中版本选择的是: openssl-1.1…

用友U8-Cloud api/hr接口存在SQL注入漏洞

声明&#xff1a; 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 简介 U8 Cloud是由用友推出的新一代云ERP系统&#xff0…

VSCode的C/C++开发 ===> Windows

一、开发环境搭建 安装mingw-w64编译器(GCC for Windows 64 & 32 bits)、Cmake工具(选装) VSCode插件安装 C/C cmake cmake tools 二、代码实践演练 基于g命令 g编译单文件&#xff0c;生成带调试信息的可执行文件、并调试 g -g main.cpp -o my_single_swap g编译多文件…

【C#】rdlc报表答应报错:未能加载文件或程序集“Microsoft.SqlServer.Types

文章目录 一、报错信息二、解决方式 一、报错信息 Microsoft.Reporting.WinForms.LocalProcessingException: An error occurred during local report processing. —> Microsoft.Reporting.DefinitionInvalidException: The definition of the report ‘’ is invalid. —&…