2月第九讲“探秘Transformer系列”

0.1 流程

使用Transformer来进行文本生成其实就是用模型来预测下一个词,完整流程包括多个阶段,如分词、向量化、计算注意力和采样,具体运作流程如下:

  • 分词(tokenize)。把用户的输入文本(此处假设是“Data visualization empowers users to”)拆解为若干独立的词汇单元,即token。
  • 编码。借助词表把token映射为数字,每个token由一个唯一的数字表示。
  • embedding(嵌入)化。embedding模块将token代表的数字转换为embedding向量,即将词映射到一个向量空间,这样LLM才能处理。此时还会加上位置编码信息,因为理解语言不仅关乎单词,还关乎单词的顺序。位置编码可以确保单词的顺序不会丢失。所有嵌入向量组合在一起形成嵌入矩阵。
  • 注意力计算。这是语境化操作,若干堆叠的Transformer Block通过注意力机制将这些Embedding向量转换成若干特征向量,构建词和词之间的关系。在注意力计算过程中,每个token可以了解自己与其它token的相关性。最终每个token流经Transformer最后一层之后得到的是一个代表语义的特征向量。
  • 计算概率。将最后一个token(”to“)对应的特征向量映射为下一个待预测词的概率分布(logits)。具体操作是通过一个线性层把特征向量升维到词表维度(即把解码器的输出转化为与词典大小相同的向量),并且通过softmax进行归一化,最终输出一个概率分布。该分布表示对词表中每个词匹配这个特征向量的概率。
  • 采样。依据这些概率,按照一定的采样规则来采样下一个token,比如选取概率最高的”visualize“作为最有可能出现的下一个单词。
  • 再次使用分词表将”visualize“对应的整数转换回原始的词汇,形成推理结果句子。
  • 不断重复上述过程。直到LLM输出结束流(EOS)标记表示解码结束或者已经生成所需数量的token。

下图将上述流程的核心部分作了可视化,也是本篇讲解的基础,后续将对模型结构和执行流程进行逐步细化。

0.2 说明

本系列主要以下面几项为基础:

  • Transformer论文:Attention Is All You Need https://arxiv.org/abs/1706.03762v7。
  • 其它相关经典论文和精彩博客,参考将在各个篇幅的具体部分中给出。
  • “The Annotated Transformer” 博客以及其源码(后续简称为哈佛源码)。“The Annotated Transformer” 是Transformer论文的读书笔记,而且博客作者用代码实现了论文的模型,并且结合实现的模型对原始论文做了详细解读。与互联网上可以获取的其他Transformer的模型实现相比较,“The Annotated Transformer” 更适合学习和解读。其地址为:
    • 2025年-2月4日拉取其代码。

另外,本篇以文本翻译功能为例来进行说明。

0x01 总体架构

1.1 设计动机

Transformer的新颖之处在于它是一个完全基于注意力机制实现的序列转换架构,我们对Transformer的主要设计动机分析如下:

  • 解决长距离依赖关系。论文希望解决RNN在序列长距离上的限制,而注意力机制可以将序列中的任意两个位置之间的距离是缩小为一个常量,从而在长文本分析时可以捕获更多的语义关联关系。
  • 提升训练并行度。论文希望克服RNN不能并行的缺点,而注意力机制可以无视序列的先后顺序来捕捉序列间的关系,因此具有更好的并行性,符合现有的GPU框架,能够进行分布式训练,提升模型训练效率。

因此,Jakob Uszkoreit(Transformer作者之一)提出了用自注意力机制来替换RNN对序列的编解码过程。而Noam Shazeer(Transformer作者之一)在此基础上提出了scaled dot-product attention、多头注意力和位置表示。

1.2 模型结构

首先我们来看看原始论文里面的架构图,接下来就以它为源头进行分析。

主体模块

从网络结构来分析,Transformer 包括了四个主体模块。

https://m.56.com/view/id-MTk0NTI4NzIz.html
https://m.56.com/view/id-MTk0NTI4NzEx.html
https://www.56.com/u70/v_MTk0NTI4NzIz.html
https://www.56.com/u58/v_MTk0NTI4NzEx.html
https://i.56.com/u/shunm_56125228199/videos/
https://i.56.com/u/shunm_56125228199/

  • 输入模块,对应下图的绿色圈。
  • 编码器(Encoder),对应下图的蓝色圈。
  • 解码器(Decoder),对应下图的红色圈。编码器和解码器都有自己的输入和输出,编码器的输出会作为解码器输入的一部分(位于解码器的中间的橙色圈)。
  • 输出模块,对应下图的紫色圈。

确切的说,蓝色圈是编码器层(Encoder layer),红色圈是解码器层(Decoder layer)。图中的 N×�× 代表把若干具有相同结构的层堆叠起来,这种将同一结构重复多次的分层机制就是栈。为了避免混淆,我们后续把单个层称为编码器层或解码器层,把堆叠的结果称为编码器或解码器。在Transformer论文中,Transformer使用了6层堆叠来进行学习。

多层

在Transformer中,第一层的输入是嵌入矩阵。第一层的输出随后被用作第二层的输入,依此类推。每一层都生成了一组嵌入,但这些嵌入不再直接与单个词元相关,而是与某种更复杂的词元关系的理解相关联。比如下图给出了一个模型中的第6层和第7层之间的关系,该模型每层有12个注意头。

https://m.56.com/view/id-MTk0NDI5NTcz.html 
https://m.56.com/view/id-MTk0NDQyNzU0.html 
https://m.56.com/view/id-MTk0NDQ0MDE5.html 
https://www.56.com/u22/v_MTk0NDQ0MDE5.html
https://www.56.com/u77/v_MTk0NDQyNzU0.html
https://www.56.com/u96/v_MTk0NDI5NTcz.html
https://i.56.com/u/shunm_56125228199/videos/
https://i.56.com/u/shunm_56125228199/ 

对于多层的作用,目前也有不同的解释。比较常见的解释是分层的本质是由下往上从不同上下文中逐步构建不同层次的特征。比如底层学习单词特征,中间层学习句法特征,高层学习语义特征等,每一层做各自的事情,不会相互影响。

输入文本对应的embedding在Transformer内部各层流通时会不断演变,这个过程类似于逐层“精炼”和“抽象”输入的信息。每一层都会对输入进行不同级别的变换和抽象,都会在其输入的基础之上吸收更多上下文信息来丰富自己的表示,逐层提取出更高层次的特征,从而在综合多层之后就会获得更加强大的表达能力。随着深度学习模型进行训练,这些网络层会逐渐学习到各种范畴之间的关系和相似性,从而在推理和回答问题时能够利用这些知识。当embedding到达最后一层时,其不仅仅代表对应token独立的含义,而是具备深刻的语境信息,反应了该token与序列中其它token的综合关系。我们可以把多层加工理解为工厂的流水线,假定要生产一件瓷器,我们要先通过印坯和修坯来确定器物形状,然后通过刻花来在已经干了的坯体上刻画出各种精美的花纹或者图案。接下来进行施釉,在成型的陶瓷坯体表面施以釉浆;最后将瓷坯装入匣钵,高温入窑烧造。最终才能得到一件精美的瓷器。

针对分层中的每一层可能都会起到不同的作用这点,研究人员做了深入的研究。

论文“What Does BERT Learn about the Structure of Language?”剖析了 BERT 所理解的英语结构的复杂性。他们的研究发现,BERT 的短语表示主要在神经网络的较低层捕捉短语级别的信息,并在中间层中编码了语言要素的复杂层次结构。这个层次结构以表层特征作为基础,中间层提取语法特征,最上层呈现语义特征。

论文"Analyzing Memorization in Large Language Models through the Lens of Model Attribution"指出:

https://www.lofter.com/shareblog/mmmf5dfg/ 


https://m.56.com/view/id-MTkyODM5ODY2.html 
https://www.56.com/u21/v_MTkyODM5ODY2.html 
https://m.56.com/view/id-MTkzMDM5NzE4.html  
https://www.56.com/u25/v_MTkzMDM5NzE4.html
https://i.56.com/u/shunm_56124919816/
https://i.56.com/u/shunm_56124948477/

  • 较深层的注意力模块(最后25%的层)主要负责记忆。
  • 较浅层的注意力模块对模型的泛化和推理能力至关重要。
  • 在深层注意力模块应用短路(short-circuit)干预可以显著降低记忆所需内存,同时保持模型性能。

论文”Interpreting Key Mechanisms of Factual Recall in Transformer-Based Language Models"则发现。语言模型存在一种普遍机制:防止过度自信(anti-overconfidence):在模型的最后若干层,语言模型总是在抑制正确答案的输出。这种抑制具体又分为两种:

  • 通过注意力头将输入起始位置的信息复制到了最末位置,我们发现起始位置的信息似乎包含了很多高频的token,模型可以通过这种方法来让高频token稀释残差流中的正确回答,降低回答的自信度。
  • 末层的MLP似乎在将残差流引导向一个“平均”token的方向(平均token是基于训练数据的词频,对token embedding加权平均得到的结果)。

另外,模型的效果往往和模型的参数量成正比,Transformer就是通过增加模型的层数来加大模型可学习的参数量,让更多的参数来承载文本中深层次的信息。

1.3 注意力模块

注意力机制是Transformer模型的心脏,它赋予模型洞察句子中每个单词与其它单词间错综复杂关系的超能力。

分类

在Transformer中有三种注意力结构:全局自注意力,掩码自注意力和交叉注意力,具体如下图所示。交叉注意力主要用于处理两个不同序列之间的关系;全局自注意力主要用于处理单个序列内元素之间的关系;掩码自注意力(也被称做因果自注意力)通过掩码来控制模型在计算注意力分数时的关注范围,从而确保在解码时不会受到未来信息的影响。

位置

三种注意力模块在Transformer网络对应位置如下图所示。

作用

Transformer实际上是通过三重注意力机制建立起了序列内部以及序列之间的全局联系。论文中对这三种注意力作用的解释如下图所示。

我们具体分析下这三种注意力。

全局自注意力层

全局自注意力层(Global self attention layer)位于编码器中,它负责处理整个输入序列。在全局自注意力机制中,序列中的每个元素都可以直接访问序列中的其它元素,从而与序列中的其他元素建立动态的关联,这样可以使模型更好地捕捉序列中的重要信息。自注意力的意思就是关注于序列内部关系的注意力机制,那么是如何实现让模型关注序列内部之间的关系呢?自注意力将query、key、value设置成相同的东西,都是输入的序列,就是让注意力机制在序列的本身中寻找关系,注意到不同部分之间的相关性。

对于全局自注意力来说,Q、K、V有如下可能:

  • Q、K、V都是输入序列。
  • Q、K、V都来自编码器中前一层的输出。编码器中的每个位置都可以关注编码器前一层输出的所有位置。

再细化来说,Q是序列中当前位置的词向量,K和V是序列中的所有位置的词向量。

掩码自注意力

掩码自注意力层或者说因果自注意力层(Causal attention layer)可以在解码阶段捕获当前词与已经解码的词之间的关联。它是对解码器的输入序列执行类似全局自注意力层的工作,但是又有不同之处。

Transformer是自回归模型,它逐个生成文本,然后将当前输出文本附加到之前输入上变成新的输入,后续的输出依赖于前面的输出词,具备因果关系。这种串行操作会极大影响训练模型的时间。为了并行提速,人们引入了掩码,这样在计算注意力时,通过掩码可以确保后面的词不会参与前面词的计算。

对于掩码自注意力来说,Q、K、V有如下可能:

  • Q、K、V都是解码器的输入序列。
  • Q、K、V都来自解码器中前一层的输出。解码器中的每个位置都可以关注解码器前一层的所有位置。

再细化来说,Q是序列中当前位置的词向量,K和V是序列中的所有位置的词向量。

交叉注意力层

交叉注意力层(Cross attention layer)其实就是传统的注意力机制。交叉注意力层位于解码器中,但是其连接了编码器和解码器,这样可以刻画输入序列和输出序列之间的全局依赖关系,完成输入和输出序列之间的对齐。因此它需要将目标序列作为Q,将上下文序列作为K和V。

对于交叉注意力来说,Q、K、V来自如下:

  • Q来自前一个解码器层,是因果注意力层的输出向量。
  • K和V来自编码器输出的注意力向量。

这使得解码器中的每个位置都能关注输入序列中的所有位置。另外,编码器并非只传递最后一步的隐状态,而是把所有时刻(对应每个位置)产生的所有隐状态都传给解码器,这就解决了中间语义编码上下文的长度是固定的问题。

或者从另一个角度来理解,交叉注意力是序列到序列模式;双向自注意力是自编码模式;单向自注意力是自回归模式。

1.4 执行流程

我们再来结合模型结构图来简述推理阶段的计算流程,具体如下图所示。

假设我们进行机器翻译工作,把中文”我吃了一个苹果“翻译成英文”I ate an apple“,在假设模型只有一层,执行步骤如下:

  1. 处理输入。用户输入自然语言句子”我吃了一个苹果“;tokenizer先把序列转换成token序列;然后Input Embedding层对每个token进行embedding编码,再加入Positional Encoding(位置编码),最终形成带有位置信息的embedding编码矩阵。编码矩阵用 Xn∗d��∗� 表示, n 是句子中单词个数,d 是表示向量的维度(论文中 d=512)。注:原论文图上的输入是token,本篇为了更好的说明,把输入设置为自然语言句子。
  2. 编码器进行编码。编码矩阵首先进入MHA(Multi-Head Attention,多头注意力)模块,在这里每个token会依据一定权重把自己的信息和其它token的信息进行交换融合;融合结果会进入FFN(Feed Forward Network)模块做进一步处理,最终得到整个句子的数学表示,句子中每个字都会带上其它字的信息。整个句子的数学表示就是Encoder的输出。
  3. 通过输入翻译开始符来启动解码器。
  4. 解码器进行解码。解码器首先进入Masked Multi-Head Attention模块,在这里解码器的输入序列会进行内部信息交换;然后在Multi-Head Attention模块中,解码器把自己的输入序列和编码器的输出进行融合转换,最终输出一个概率分布,表示词表中每个单词作为下一个输出单词的概率;最终依据某种策略输出一个最可能的单词。这里会预测出第一个单词”I“。
  5. 把预测出的第一个单词”I“和一起作为解码器的输入,进行再次解码。
  6. 解码器预测出第二个单词”ate“。

针对本例,解码器的每一步输入和输出具体如下表所示。

1.6 小结

Transformer总体架构是一个有机整体,难以分割。组合的意义不在于构成它的基本单元,而在于这些单元之间形成的复杂关系和涌现的行为。比如集体智能来自个体的组合,却产生了所有个体都不具备的高阶能力。

有些工作就将焦点转移到 transformer 模块的高级架构上,并认为其完整结构,而不仅仅是标记混合注意力操作,对Transformer实现具有竞争力的性能至关重要。

论文"Attention is not all you need"指出如果没有skip connection(residual connection-残差链接)和MLP,自注意力网络的输出会朝着一个rank-1的矩阵收缩。即,skip connection和MLP可以很好地阻止自注意力网络的这种”秩坍塌(秩坍塌)退化“。这揭示了skip connection,MLP对self-attention的不可或缺的作用;

论文”MetaFormer is Actually What You Need for Vision“则描述了一种通用架构,在该结构中,输入首先经过embedding,得到 𝑋。然后embedding送入重复的blocks中,第一个block主要包含了token mixer,使得不同的token能够相互信息通信(Y = TokenMixer(Norm(X)) + X,);第二个block包含两层MLP。该架构通过指定token mixer的具体设计,可以获得不同的模型。如果将token mixer指定为注意力或spatial MLP,则MetaFormer将分别成为一个transformer或类似MLP的模型。

0x02 构建

我们接下来结合哈佛源码进行分析和学习。哈佛代码中的make_model()函数是Transformer模型的构建函数。

2.1 参数

make_model()函数的参数有如下7个:

  • src_vocab:源语言词表中单词数目,即源词典的大小。
  • tgt_vocab:目标语言词表中单词数目,即目标词典的大小。
  • N=6:编码器和解码器堆叠数,即编码器层数和解码器层数。
  • d_model=512:模型所处理数据的维度,即词向量(word embedding)的大小。
  • d_ff=2048:FFN(前馈全连接层)中变换矩阵的维度,即隐层神经元的数量。
  • head=8:多头注意力层中的注意力头数。
  • dropout=0.1:防止过拟合。

2.2 构建逻辑

make_model()函数的主要思路就是用从小到大搭建积木的方式来构建Transformer。我们先脱离代码来构思下,看看架构图上的哪些模块可以作为积木。

  • 输入模块:Input Embedding和Positional Encoding分别可以作为单独的积木块,它们结合在一起又可以作为一个新的积木块。
  • 编码器层和解码器层可以作为两个单独的大积木块。其内部的Masked Multi-Head Attention、Multi-Head Attention、Feed Forward和Add & Norm也都可以作为单独的小积木块。
  • 输出模块:Linear和Softmax分别可以作为单独的积木块,它们结合在一起又可以作为一个新的积木块。

有了这些积木块,我们就可以很容易的构建起Tranformer了。当然,在make_model()函数中做了一定的抽象,有些小积木块被用某些类进行了封装(比如Linear和Softmax被封装在Generator类中,细节没有在make_model()函数中展示出来)。我们把这些模块和代码中一一对应起来看,下图中的数字代表代码中某模块出现的顺序,这些数字在下面代码的注释中也有标明。

具体的代码逻辑如下。

  • 把copy.deepcopy()这个深度拷贝函数重新命名为c,这样后续代码会比较简洁。deepcopy()函数会开辟一个新内存并将源实例完全复制过来,复制过来的对象和源对象没有任何关联。后续各种类的构造函数中会调用copy.deepcopy()来重新生成一个对应实例,比如上面图中的标号1,2,3在解码器端就分别被做了深度拷贝。这样,两个标号1对应的实例彼此之间相互独立,不受干扰。
  • 构建 MultiHeadedAttention,PositionwiseFeedForward 和 PositionalEncoding 对象。
  • 构造 EncoderDecoder 对象,这是Transformer主体类,其参数是Encoder、Decoder、src-embed、tgt-embed和 Generator。我们先分析后面三个参数。
    • src-embed是nn.Sequential(Embeddings(d_model, src_vocab), c(position))的返回结果,其意义是输入编码,对应上图的编号7(由编码3和6组成)。nn.Sequential()函数构建了顺序容器,容器内模块的顺序就是模型处理数据的顺序;
    • tgt-embed是nn.Sequential(Embeddings(d_model, tgt_vocab), c(position))的返回结果,其意义是输入编码,对应上图的编号9(由编码3和8组成)。
    • Generator对应上图的编号10,其包括了Linear和Softmax。Generator会把Decoder的输出变成输出词的概率。
  • Encoder和Decoder两个类很像,我们以Encoder为例来说明。Encoder由N个EncoderLayer构成,EncoderLayer的参数是d_model, c(attn), c(ff), dropout,即word embedding维度、多头注意力、FFN层和Dropout。可以看到,Encoder和Decoder类中的注意力都是MultiHeadedAttention的实例,只是因为传递参数的不同,才决定某个注意力是交叉注意力还是掩码多头注意力。
  • 初始化模型参数。Xavier初始化可以参考论文"Understanding the difficulty of training deep feedforward neural networks"。

具体代码如下。

def make_model(src_vocab, tgt_vocab, N=6, d_model=512, d_ff=2048, h=8, dropout=0.1):
    "Helper: Construct a model from hyperparameters."
    # copy.deepcopy是深度拷贝函数,即重新生成一个新实例。重新命名可以让后续代码比较简洁
    c = copy.deepcopy
    # 构建多头注意力层的实例,对应上图的数字标号1
    attn = MultiHeadedAttention(h, d_model)
    # 构建前馈神经网络层的实例,对应上图的数字标号2
    ff = PositionwiseFeedForward(d_model, d_ff, dropout)
    # 构建位置编码模块的实例,对应上图的数字标号3
    position = PositionalEncoding(d_model, dropout)
    # 总的Transformer模型
    model = EncoderDecoder(
        # EncoderLayer只包含一个Attention层,对应上图的数字标号4。Encoder则包括外面的N
        Encoder(EncoderLayer(d_model, c(attn), c(ff), dropout), N),
        # DecoderLayer包含两个Attention层,对应上图的数字标号5,Decoder则包括外面的N
        Decoder(DecoderLayer(d_model, c(attn), c(attn), c(ff), dropout), N),
        # 输入的Embedding和位置编码,Embeddings对应上图的数字标号6,Sequential就是两个编码合并的结果,对应上图的数字标号7
        nn.Sequential(Embeddings(d_model, src_vocab), c(position)),
        # 输出的Embedding和位置编码,Embeddings对应上图的数字标号8,Sequential就是两个编码合并的结果,对应上图的数字标号9
        nn.Sequential(Embeddings(d_model, tgt_vocab), c(position)),
        # Generator类包括Linear层和Softmax层,对应上图的数字标号10,负责依据Decoder的输出来预测下一个token
        Generator(d_model, tgt_vocab),
    )

    # This was important from their code.
    # Initialize parameters with Glorot / fan_avg.
    # 初始化模型参数,这里采用xavier初始化,即如果参数的维度大于1,则将其初始化成一个服从均匀分布的矩阵
    for p in model.parameters():
        if p.dim() > 1:
            nn.init.xavier_uniform_(p)
    return model

2.3 主体类

EncoderDecoder类就是基于Transformer架构的编码器-解码器实现,其成员变量如下:

  • encoder:Encoder类实例,这是编码器的实现。
  • decoder:Decoder类实例,这是解码器的实现。
  • src_embed:源语言的word embedding生成模块,是一个nn.Sequential对象,包括Embebddings和PositionalEncoding。src_embed将对输入进行Embedding和位置编码
  • tgt_embed:目标语言的word embedding生成模块,是一个nn.Sequential对象,包括Embebddings和PositionalEncoding。tgt_embed将对再传入的输出进行Embedding和位置编码
  • generator:Generator类的对象,包括Linear层和Softmax层,负责对Decoder的输出做预测,即依据Decoder的隐状态输出来预测当前时刻的词。隐状态会输入到全连接层(全连接层的输出大小是词典的大小),全连接层会接上一个softmax得到预测词的概率。

EncoderDecoder类的forward()函数完成了编码和解码的工作,它接受四个函数:

  • src:源序列,其内容是token在词表对应的编号。src的形状是[batch_size, seq_len],举例是[[ 0, 2, 4, 8, 1, 2, 2 ]] ,即批量大小为1,句子长度是7,其中0为bos,1为eos,2为pad。
  • tgt:目标序列,具体含义类似src。
  • src_mask:源序列掩码,具体作用是对填充符号进行掩码。以[[ 0, 2, 4, 8, 1, 2, 2 ]]为例,其掩码是[[True,True,True,True,True,False,False]],即对两个填充的pad进行掩码。
  • tgt_mask:目标序列掩码,其作用有两种:不让注意力计算看到未来的单词;对填充符号进行掩码。其形状是[batch_size, seq_len, seq_len],上面对应的掩码如下:
[True,False,False,False,False,False,False],
[True,True,False,False,False,False,False],
[True,True,True,False,False,False,False],
[True,True,True,True,False,False,False],
[True,True,True,True,True,False,False], # 例句是5个正式token,后面两个pad被置为False
[True,True,True,True,True,False,False],
[True,True,True,True,True,False,False],

EncoderDecoder代码具体如下:

# 继承nn.Module
class EncoderDecoder(nn.Module):
    """
    A standard Encoder-Decoder architecture. Base for this and many other models.
    标准的编码器-解码器架构,这是很多模型的基础。
    """

    def __init__(self, encoder, decoder, src_embed, tgt_embed, generator):
        """
        初始化函数有5个参数,从外部传入参数的目的是更加灵活,可以更换组件
        """
        super(EncoderDecoder, self).__init__()
        self.encoder = encoder # 编码器对象
        self.decoder = decoder # 解码器对象
        # 源语言input embedding和position embedding的组合
        self.src_embed = src_embed 
        # 目标语言output embedding和position embedding的组合
        self.tgt_embed = tgt_embed 
        self.generator = generator # 类别生成器对象

    def forward(self, src, tgt, src_mask, tgt_mask):
        # 前向传播函数有四个参数:源序列,目标序列,源序列掩码,目标序列掩码
        "Take in and process masked src and target sequences."
        # 1. 将source, source_mask传入编码函数encode(),让编码器对源序列进行编码,得到编码结果memory
        # 2. 将memory,source_mask,target,target_mask一同传给解码函数decode()进行解码
        return self.decode(self.encode(src, src_mask), src_mask, tgt, tgt_mask)

    # 编码函数,接受参数是源序列和掩码
    def encode(self, src, src_mask):
        # 1. 对src编码,得到input embedding
        # 2. 计算位置编码,将input embedding和位置编码相加,得到word embedding
        # 3. 使用编码器encoder进行编码,编码结果记作memory
        return self.encoder(self.src_embed(src), src_mask)

    # 解码函数,参数为:编码器输出(memory)、源序列掩码、目标序列和目标序列掩码
    def decode(self, memory, src_mask, tgt, tgt_mask):
        # 1. 对tgt编码,得到得到input embedding
        # 2. 计算位置编码,将input embedding和位置编码相加,得到word embedding
        # 3. 使用编码器decoder进行解码,解码器输出可以使用self.generator进行最后的预测
        return self.decoder(self.tgt_embed(tgt), memory, src_mask, tgt_mask)

2.4 如何调用

模型调用的方式如下面代码所示,forward()函数负责把输入编码成隐状态,然后把隐状态解码称输出logits(对数几率)。其参数都从batch类的实例中获取,具体如下:

  • src:源句子列表。形状是[batch size, max sequence length]。每个句子是从数据集提取出来,经过词典处理过的,举例为:[ 0, 5, 12,..., 1, 2, 2]。其中0为bos,1为eos,2为pad。
  • tgt:目标句子列表。具体意义同上。
  • src_mask:注意力层要用的掩码(后面章节会详细分析)。
  • tgt_mask:解码器的掩码自注意力层要用的掩码(后面章节会详细分析)。
out = model.forward(batch.src, batch.tgt, batch.src_mask, batch.tgt_mask)

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

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

相关文章

crewai框架(0.83.0)添加知识源

官方的文档如下 https://docs.crewai.com/concepts/knowledge但是不知道为什么,可能是版本的问题(我用的是0.86.0),参考官方文档的配置我会报错,并且也导入不了数据库,也可能用的不是官方API。本文以常用的…

deepseek + embeding模型搭建本地知识库

上一篇文章讲了ollamadeepseek模型的本地化部署,具体能部署哪一款取决于你的土豪程度: 今天的目标是本地安装部署embeding模型,实现LLMembeding模型的rag知识库的本地化部署,包括: embeding模型的本地化部署anyhingL…

2、树莓派5第一次开机三种方式:使用外设 / 使用网线 / 使用wifi

本文整理了树莓派第一次开机方式,供大家参考 方式一:连接鼠标、键盘、显示器外设开机 树莓派自带USB接口及HDMI接口,因此可以通过USB连接鼠标键盘,HDMI接入显示器,再进行电源供电,就可以完成第一次开机 …

案例-02.部门管理-查询

一.查询部门-需求 二.查询部门-思路 API接口文档 三.代码实现 1.controller层:负责与前端进行交互,接收前端所发来的请求 注:Slf4j用于记录日志使用,可以省略private static Logger log LoggerFactory.getLogger(DeptControlle…

小程序包体积优化指南:静态资源条件编译与分包编译技巧

在开发小程序时,可能大家都遇到过包体积超限的情况,这对多平台支持、用户体验和加载速度带来不少困扰。UniApp 提供了一些强大的功能,比如静态资源的条件编译和分包编译,这些功能可以帮助我们减少小程序的包体积,提高加…

12. QT控件:多元素控件

0. 概述 Qt中提供的多元素控件 QListWidget QListView QTableWidget QTableView QTreeWidget QTreeView xxWidget 和 xxView的区别 以QTableWidget 和 QTableView 为例: QTableView 是基于MVC设计的控件,QTableView自身不持有数据。使用QTableView需…

CAS单点登录(第7版)20.用户界面

如有疑问,请看视频:CAS单点登录(第7版) 用户界面 概述 概述 对 CAS 用户界面 (UI) 进行品牌化涉及编辑 CSS 样式表以及一小部分相对简单的 HTML 包含文件,也称为视图。(可选&…

android 的抓包工具

charles 抓包工具 官网地址 nullCharles Web Debugging Proxy - Official Sitehttps://www.charlesproxy.com/使用手册一定记得看官网 SSL Certificates • Charles Web Debugging Proxy http请求: 1.启动代理: 2.设置设备端口 3.手机连接当前代理 …

关于视频去水印的一点尝试

一. 视频去水印的几种方法 1. 使用ffmpeg delogo滤镜 delogo 滤镜的原理是通过插值算法,用水印周围的像素填充水印的位置。 示例: ffmpeg -i input.mp4 -filter_complex "[0:v]delogox420:y920:w1070:h60" output.mp4 该命令表示通过滤镜…

预测技术在美团弹性伸缩场景的探索与应用

管理企业大规模服务的弹性伸缩场景中,往往会面临着两个挑战:第一个挑战是精准的负载预测,由于应用实例的启动需要一定预热时间,被动响应式伸缩会在一段时间内影响服务质量;第二个挑战是高效的资源分配,即在…

【含开题报告+文档+PPT+源码】基于Spring+Vue的拾光印记婚纱影楼管理系统

开题报告 本论文旨在探讨基于Spring和Vue框架的拾光印记婚纱影楼管理系统的设计与实现。该系统集成了用户注册登录、个人资料修改、婚庆资讯浏览、婚庆套餐查看、婚纱拍摄预约、婚纱浏览与租赁、客片查看以及在线客服等多项功能,为用户提供了一站式的婚纱影楼服务体…

ASP.NET Core 使用 FileStream 将 FileResult 文件发送到浏览器后删除该文件

FileStream 在向浏览器发送文件时节省了服务器内存和资源,但如果需要删除文件怎么办?本文介绍如何在发送文件后删除文件;用 C# 编写。 另请参阅:位图创建和下载 使用FileStream向浏览器发送数据效率更高,因为文件是从…

字节跳动后端二面

📍1. 数据库的事务性质,InnoDB是如何实现的? 数据库事务具有ACID特性,即原子性、一致性、隔离性和持久性。InnoDB通过以下机制实现这些特性: 🚀 实现细节: 原子性:通过undo log实…

【个人开发】cuda12.6安装vllm安装实践【内含踩坑经验】

1. 背景 vLLM是一个快速且易于使用的LLM推理和服务库。企业级应用比较普遍,尝试安装相关环境,尝试使用。 2. 环境 模块版本python3.10CUDA12.6torch2.5.1xformers0.0.28.post3flash_attn2.7.4vllm0.6.4.post1 2.1 安装flash_attn 具体选择什么版本&…

19.4.9 数据库方式操作Excel

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 本节所说的操作Excel操作是讲如何把Excel作为数据库来操作。 通过COM来操作Excel操作,请参看第21.2节 在第19.3.4节【…

2024-2025年主流的开源向量数据库推荐

以下是2024-2025年主流的开源向量数据库推荐,涵盖其核心功能和应用场景: 1. Milvus 特点:专为大规模向量搜索设计,支持万亿级向量数据集的毫秒级搜索,适用于图像搜索、聊天机器人、化学结构搜索等场景。采用无状态架…

vue项目使用vite和vue-router实现history路由模式空白页以及404问题

开发项目的时候,我们一般都会使用路由,但是使用hash路由还是history路由成为了两种选择,因为hash路由在url中带有#号,history没有带#号,看起来更加自然美观。但是hash速度更快而且更通用,history需要配置很…

AI编程01-生成前/后端接口对表-豆包(或Deepseek+WPS的AI

前言: 做过全栈的工程师知道,如果一个APP的项目分别是前端/后端两个团队开发的话,那么原型设计之后,通过接口文档进行开发对接是非常必要的。 传统的方法是,大家一起定义一个接口文档,然后,前端和后端的工程师进行为何,现在AI的时代,是不是通过AI能协助呢,显然可以…

切换git仓库远程地址

1、首先可以先查看一下当前git库的远程地址 【cd .git】 切换到git目录【cat config】查看【cd ../】 返回项目目录 2、 切换到目标远程git地址 【git remote rm origin】 删除现有远程仓库 【git remote add origin url】添加新远程仓库 【cat .git/config】验证是否切换成功…