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

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.mfbz.cn/a/575507.html

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

相关文章

mybatisplus3.5.4基础生成代码完整步骤(超详细)

在网上看了很多自动生成的例子本地不是很好使&#xff0c;最后找到了一套好用的&#xff0c;适合版本&#xff1a; idea:2024.1 springboot2.6.12 java17 mybatisplus3.5.4 废话不多说&#xff0c;直接上步骤&#xff1a; 新建项目&#xff1a; 结构如下&#xff1a; 添加依…

Calibre Ebook Management 一款功能强大操作简单的电子书管理神器(V7.9.0)

Calibre Ebook Management Calibre 软件被广泛认可为一款功能全面并且用户友好的电子书管理工具。其卓越性能和必备性得到了用户的高度评价。该软件提供了一套综合性的功能&#xff0c;允许用户执行各种电子书相关的操作。此外&#xff0c;Calibre 软件遵循免费和开源的原则&a…

spring基本使用

文章目录 1. ioc(Inversion of Control) | DI(Dependency Injection)(1) maven坐标导包(2) 编写配置文件bean.xml(3) 配置bean(4) 配置文件注入属性 2. DI(dependency injection) 依赖注入(setter)其他属性(1) 对象属性注入(2) 数组属性输入(3) 集合属性注入(4) map集合注入(5)…

【一刷《剑指Offer》】面试题 9:斐波那契数列(扩展:青蛙跳台阶、矩阵覆盖)

力扣对应链接&#xff1a;LCR 126. 斐波那契数 - 力扣&#xff08;LeetCode&#xff09; 牛客对应链接&#xff1a;斐波那契数列_牛客题霸_牛客网 (nowcoder.com) 核心考点&#xff1a;空间复杂度&#xff0c;fib 理解&#xff0c;剪枝重复计算。 一、《剑指Offer》内容 二、分…

ThingsBoard处理设备上报的属性并转换为可读属性

一、前言 二、案例 1、AI生成JSON数据体 2、将json数据体直接通过遥测topic发送查看效果 3、可查看目前整个数据都在一起 ​编辑 4、配置附规则链路 5、对msg的消息值&#xff0c;进行数据的转换&#xff0c;并从新进行赋值。 6、规则链路关联关系 7、再次通过MQTT发送遥…

618大促有哪些值得买的家居好物?618五款必Buy好物

来了&#xff01;来了&#xff01;万众瞩目的618购物狂欢节即将拉开帷幕&#xff0c;我们的目标清晰而坚定&#xff0c;那就是用最实惠的价格尽情享受购物的乐趣。然而&#xff0c;面对各种纷繁复杂的促销活动和琳琅满目的商品&#xff0c;选择困难症似乎也在悄然滋生。因此&am…

【自定义渲染通道】

自定义渲染通道 2023-09-07 14:58 How to Create Masks With the Custom Depth Buffer Tips - Tricks Unreal Engine.mp4 后期材质ppm_customDepth 要加入通道的物体设置 render customdepth pass postprocessvolue 设置post process materials 为上面的ppm_customDepth 不同…

【信安评估】2024年全国职业院校技能大赛高职组“信息安全管理与评估”安徽省选拔赛赛项规程

培训、环境、资料、考证 公众号&#xff1a;Geek极安云科 网络安全群&#xff1a;624032112 网络系统管理群&#xff1a;223627079 网络建设与运维群&#xff1a;870959784 移动应用开发群&#xff1a;548238632 极安云科专注于技能提升&#xff0c;赋能 2024年广东省高校的技…

PLL深度解析第一篇——PLL的知识图谱

在硬件电路中&#xff0c;时钟就像心脏一样&#xff0c;在时钟的节拍下&#xff0c;不同的芯片、不同的电路、不同的接口都可以有序的进行工作或者通信&#xff08;类似流水线一样&#xff0c;必须有节奏的运行&#xff09;。 但是在芯片中&#xff0c;不同的模块和接口工作的频…

基于SSM的物业管理系统(含源码+sql+视频导入教程+文档+PPT)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的物业管理系统2拥有三种角色 管理员&#xff1a;用户管理、物业管理、房产信息管理、小区概况管理、开发商管理、收费标准管理、物业公司管理等 物业&#xff1a;住户管理、收费…

C语言求 MD5 值

MD5值常被用于验证数据的完整性&#xff0c;嵌入式开发时经常用到。md5sum命令可以求MD5码&#xff0c;下面介绍如何用C语言实现MD5功能。 一、求字符串MD5值 1、md5sum命令 $ echo -n "12345678" | md5sum //获取"12345678"字符串的md5值 结果&…

1小时学会SpringBoot3+Vue3前后端分离开发

首发于Enaium的个人博客 引言 大家可能刚学会Java和Vue之后都会想下一步是什么&#xff1f;那么就先把SpringBoot和Vue结合起来&#xff0c;做一个前后端分离的项目吧。 准备工作 首先你需要懂得Java和Vue的基础知识&#xff0c;环境这里就不多说了&#xff0c;直接开始。 …

Neo-reGeorg明文流量

Neo-reGeorg 1 同IP对&#xff0c;同一个URI&#xff0c;第一个TCP流是“GET”请求&#xff0c;随后的TCP流请求为“POST”。&#xff08;jsp\jspx\php&#xff09; 2 第一个TCP流中&#xff0c;GET只有一个会话。&#xff08;jsp\jspx\php&#xff09;&#xff0c;响应body79…

stm32HAL库-GPIO

一 什么是 GPIO: GPIO(general porpose intput output), 通用输入输出端口 . 二 我们先认识芯片控制 GPIO 输出控制。 2.1LED 硬件原理如图&#xff1a; 当电流从这根电线流通&#xff0c; LED 亮。当电流不通过这根电线&#xff0c; LED 灭。 上面 PF** &#xff0c;芯片电…

平芯微PW7014中文规格书

产品概述 PW7014 具有前端过电压和过温保护功能。 支持 3V 到 36V 的宽输入电压工作范围。 过压保护阈 值可以外部设置 4V~22V 或采用内部默认 6.1V 设置。 超快的过压保护响应速度能够确保后级电路 的安全。 集成了超低导通阻抗的 nFET 开关&#xff0c; 确保电路系统应用更好…

如何替代传统的方式,提高能源企业敏感文件传输的安全性?

能源行业是一个关键的基础设施领域&#xff0c;它涉及能源的勘探、开采、生产、转换、分配和消费。随着全球经济的发展和人口的增长&#xff0c;能源需求持续上升&#xff0c;这对能源行业的可持续发展提出了挑战。能源行业的传输场景多种多样&#xff0c;需要重点关注能源企业…

性能工具之 JMeter 自定义 Java Sampler 支持国密 SM2 算法

文章目录 一、前言二、加密接口1、什么是SM22、被测接口加密逻辑 三、准备工作四、JMeter 扩展实现步骤1&#xff1a;准备开发环境步骤2&#xff1a;了解实现方法步骤3&#xff1a;runTest 方法步骤4&#xff1a;getDefaultParameters 方法步骤5&#xff1a;setupTest 方法 五、…

3.Docker常用镜像命令和容器命令详解

文章目录 1、Docker镜像命令1.1 获取镜像1.2 查看镜像1.2.1、images命令列出镜像1.2.2、tag命令添加镜像标签1.2.3、inspect命令查看详细信息1.2.4、history命令查看镜像历史 1.3 搜索镜像1.4 删除和清理镜像1.4.1、使用标签删除镜像1.4.2、清理镜像 1.5 创建镜像1.5.1、基于已…

LANGUAGE-DRIVEN SEMANTIC SEGMENTATION

环境不易满足&#xff0c;不建议复现

Google Ads广告为Demand Gen推出生成式AI工具,可自动生成广告图片

谷歌今天宣布在Google Ads广告中为Demand Gen活动推出新的生成人工智能功能。 这些工具由谷歌人工智能提供支持&#xff0c;广告商只需几个步骤即可使用文本提示创建高质量的图片。 这些由人工智能驱动的创意功能旨在增强视觉叙事能力&#xff0c;帮助品牌在YouTube、YouTube…
最新文章