PyTorch构建Transformer模型实战指南

2026-06-15阅读 0热度 0
人工智能

0. 前言

相较于传统的循环神经网络和卷积神经网络,Transformer的一个核心优势在于,它能更好地理解输入和输出序列中元素之间的关联,尤其在处理长距离依赖关系时表现突出——比如文本中相隔很远的两个词之间的语义联系。和RNN不同,Transformer可以并行训练,这大大缩短了训练时间,也让处理大规模数据集成为可能。可以说,这种创新的架构是ChatGPT、BERT、DeepSeek等大语言模型火爆背后的关键推手,标志着人工智能领域的一个重要里程碑。

在Transformer出现之前,自然语言处理及相关任务主要依赖RNN,包括LSTM这类变体。但RNN的致命弱点在于它必须按顺序处理信息,不仅无法并行训练,速度受限,而且在保留序列早期信息方面也捉襟见肘,很难捕捉到真正的长期依赖。

Transformer架构的创新之处,就在于它的注意力机制。这套机制通过给序列中的词分配权重,来评估它们之间的关系——权重的大小完全取决于训练数据中学到的语义相关性。正是靠这个机制,像ChatGPT这样的模型才能“理解”词与词之间的关联,从而更高效地处理语言数据。因为输入不再需要按顺序处理,并行训练成为可能,训练时间大大缩短,大规模数据集的支持也催生了LLM的崛起。

为了从零开始搭建一个Transformer,我们得先搞清楚自注意力机制的工作原理,包括查询、键和值向量的角色,以及缩放点积注意力(SDPA)的计算过程。然后,把层归一化和残差连接集成到多头注意力层中,再和前馈层组合,就能构建出编码器层。接着堆叠六个这样的层组成编码器,同时也会实现解码器部分。

1. 注意力机制 和 Transformer

要理解Transformer,首先得弄明白注意力机制。正是这个机制,让Transformer能识别序列元素之间的长程依赖,这也是它们和RNN这类早期序列模型的关键区别。通过注意力机制,Transformer能同时“看”到序列中的每个元素,从而理解每个单词的上下文。

举个例子,“bank”这个词,在不同句子里意思完全不同。在“I went fishing by the river yesterday, remaining near the bank the whole afternoon”中,“bank”显然是指河岸,和“fishing”有关。Transformer会把它理解为河流地形的一部分。而在“Kate went to the bank after work yesterday and deposited a check there”中,“bank”和“check”联系在一起,Transformer就能认出它指的是金融机构。这就是注意力机制的神奇之处。

1.1 注意力机制

注意力机制本质上是一种方法,用来确定序列中元素之间的相互关系。它会计算一个得分,表示一个元素和序列中其他元素的关系——得分越高,关系越强。在自然语言处理中,这个机制能有效连接句子中的单词。

这里我们要构建一个由编码器和解码器组成的Transformer机器翻译模型。编码器会把英语句子(比如“How are you?”)转换成能捕捉其含义的向量表示,解码器则利用这些向量生成法语翻译。

具体来说,模型先把短语“How are you?”拆成词元序列[how, are, you, ?]。每个词元由一个256维的向量表示,这叫词嵌入,用来捕捉每个词元的含义。编码器还会用位置编码来确定词元在序列中的位置。把位置编码添加到词嵌入中,就形成了输入嵌入,用来计算自注意力。对“How are you?”来说,输入嵌入是一个形状为(4, 256)的张量,4表示词元数量,256表示每个嵌入的维度。

计算注意力有好几种方式,这里我们重点介绍最常见的一种——缩放点积注意力(SDPA),也叫自注意力。它计算的是一个单词如何关注序列中的所有单词,包括它自己。

计算注意力时,查询、键和值的概念其实来自检索系统。想象一下我们去图书馆找书:在搜索引擎里输入“金融中的机器学习”,这个短语就是查询;图书馆里的书名和描述就是键。根据查询和这些键的相似度,检索系统会推荐一份书单(值),其中和“机器学习”“金融”相关的书排名更高,不相关的匹配分数低,自然就不容易被推荐。

计算SDPA时,输入嵌入X会通过三个不同的神经网络层处理。这三层对应的权重分别是W_Q、W_K和W_V,每个权重维度都是256×256,它们是在训练过程中从数据里学出来的。那么,查询Q、键K和值V的计算公式就是:

Q = X × W_Q
K = X × W_K
V = X × W_V

其中Q、K、V的维度和输入嵌入X一样,都是4×256。

类比检索系统,在注意力机制中,我们用SDPA来评估查询向量和键向量之间的相似度。SDPA计算的是查询Q和键K向量的点积,点积越高表示相似度越强。缩放后的注意力分数这样算:

Attention Score (Q,K) = (Q · K^T) / sqrt(d_k)

这里的d_k是键向量K的维度,在本文中是256。除以d_k的平方根是为了缩放点积,稳定训练过程。因为当向量维度很高时,点积的值可能会变得非常大。

接着,对这些注意力分数应用softmax函数,把它们转化为注意力权重,确保一个单词对句子中所有单词的总注意力之和为100%。

上图展示了注意力的计算过程。对句子“How are you?”来说,注意力权重形成一个4×4的矩阵,表示每个词元([“How”, “are”, “you”, “?”])和其他所有词元的关系。比如,第一行中,“How”把10%的注意力给了自己,40%和40%分别给了“are”和“you”,剩下10%给了“?”。

最终的注意力输出,就是通过注意力权重与值向量V的点积算出来的:

Attention (Q, K, V) = softmax((Q · K^T) / sqrt(d_k)) · V

输出维度依然是4×256,和输入保持一致。

总结一下,整个过程从输入嵌入X(维度4×256)开始。这个嵌入包含了四个词元的独立含义,但缺乏上下文理解。注意力的最终输出Attention(Q, K, V)维度不变,但可以看作是对四个原始词元的上下文组合。原始词元的权重根据每个词元在上下文中的相关性变化,更重要的词会获得更高权重。这样一来,注意力机制就把表示孤立词元的向量,变成了蕴含上下文意义的向量。

另外,Transformer模型并不只用一组查询、键和值向量,而是采用多头注意力机制。比如,256维的查询、键和值向量可以分割成8个注意力头,每个头有一组32维的向量(256/8=32)。每个头关注输入的不同方面,让模型能捕捉更广泛的信息。当一个词在句子中有多种含义时(比如双关语),多头注意力尤其有用。在接下来的英法翻译任务中,我们会把Q、K、V分割成多个头,分别计算注意力,再拼接回来。

1.2 Transformer 架构

注意力机制的概念早在2014年就被提出了,但直到《Attention Is All You Need》这篇论文,才被广泛应用于机器翻译领域,并由此诞生了Transformer模型——一种依靠注意力机制的编码器-解码器结构。

还是用英法翻译来举例。Transformer的编码器把英语句子“I don’t speak French”转换成表示其含义的向量,然后解码器处理这些向量,生成法语翻译“Je ne parle pas français”。编码器的核心使命是捕捉原始英语句子的核心含义——如果它足够有效,那么“I don’t speak French”和“I do not speak French”应该被转换成相似的向量表示,解码器也能据此生成相似的翻译。

编码器首先会对英法句子进行分词,这里用的是子词分词技术,也就是把单词拆成更小的组成部分。比如,“I do not speak French”被拆成(i, do, not, speak, fr, ench)六个词元;对应的法语“Je ne parle pas français”也被拆成(je, ne, parle, pas, franc, ais)。这种方法增强了Transformer处理语言变化和复杂性的能力。

深度学习模型没法直接处理文本,所以分词后,词元会被编成整数索引。通常先做独热编码,再通过词嵌入层压缩成更小的连续值向量(比如长256)。这样,“I do not speak French”就被表示成一个6×256的矩阵。

和RNN按顺序处理数据不同,Transformer是并行处理输入的。并行性提高了效率,但也带来了问题:模型没法自动识别输入的顺序。为此,Transformer在输入嵌入中添加了位置编码,每个位置对应一个独特的向量,维度和输入嵌入对齐。这些向量值由特定位置函数决定,涉及不同频率的正弦和余弦函数:

PositionalEncoding(pos, 2i) = sin(pos / n^(2i/d))
PositionalEncoding(pos, 2i+1) = cos(pos / n^(2i/d))

偶数索引用正弦函数,奇数索引用余弦函数。参数pos和i分别表示词元在序列中的位置和向量中的索引。对“I do not speak French”来说,位置编码是一个6×256的矩阵,和词嵌入一样大。pos范围从0到5,索引2i和2i+1共同覆盖了0到255这256个值。这种方法的一个好处是,所有值都被限制在-1到1之间。

需要注意的是,每个词元的位置都由一个256维的向量唯一标识,这些向量在训练过程中保持不变。在输入到注意力层之前,这些位置编码被加到序列的词嵌入中。编码器生成词嵌入和位置编码后,将它们合并成一个6×256维的表示,再应用注意力机制,把嵌入向量精细化为更复杂的表示,捕捉句子的整体含义,最后传递给解码器。

Transformer的编码器由六个相同的层组成(N=6)。每一层包含两个子层:第一个是多头自注意力层,第二个是逐位置的全连接前馈网络。每个子层都包含层归一化和残差连接。层归一化把观测值标准化,使其均值为0、标准差为1,有助于稳定训练过程。然后残差连接把每个子层的输入和输出相加,增强信息在网络中的流动。

解码器也由六个相同的层组成。每个解码器层包含三个子层:一个多头自注意力子层,一个在第一子层输出和编码器输出之间执行多头交叉注意力的子层,还有一个前馈子层。每个子层的输入都是前一个子层的输出。特别地,解码器层中的第二个子层还把编码器的输出作为输入,这正是解码器整合编码器信息、生成翻译的关键。

解码器的自注意力子层有个关键特点——掩码机制。这个掩码防止模型访问序列中的未来位置,确保某个位置的预测只能依赖之前已知的元素。这种顺序依赖性对于语言翻译或文本生成等任务至关重要。

解码过程从接收一个法语输入短语开始。解码器把法语词元转化为词嵌入和位置编码,合并成一个嵌入。这步确保了模型不仅理解短语的语义,还保持顺序上下文。

解码器以自回归方式运行,逐个生成输出序列的词元。第一个时间步,它从表示句子开始的“BOS”(句子开始词元)开始。用这个开始词元作为初始输入,解码器查看英语短语的向量表示,尝试预测“BOS”之后的第一个词元。假设第一个预测是“Je”,下一个时间步就用“BOS Je”作为新输入来预测下一个词元。这个过程持续进行,直到解码器预测出“EOS”(句子结束词元),标志着句子结束。训练时,我们会在每个短语末尾添加EOS,模型学到的就是:EOS表示句子结束。

1.3 不同类型的 Transformer

Transformer主要有三种类型:仅编码器Transformer、仅解码器Transformer,以及编码器-解码器Transformer。

仅编码器Transformer由N个相同的编码器层组成,能把序列转换成抽象的连续向量表示。比如BERT就是一个仅编码器Transformer,包含12个编码器层,常用于文本分类——如果两个句子的向量表示相似,就归为同一类,反之则归为不同类。

仅解码器Transformer由N个相同的解码器层组成。比如ChatGPT,包含多个解码器层,能根据提示生成文本。它会提取提示中单词的语义,预测最可能的下一个词元,然后把该词元加到提示末尾,重复这个过程,直到文本达到一定长度。

编码器-解码器Transformer用于处理更复杂的任务,比如文本到图像生成或语音识别。它结合了编码器和解码器的优势:编码器擅长处理和理解输入数据,解码器擅长生成输出。二者结合,让模型能有效理解复杂的输入(如文本或语音),并生成复杂的输出(如图像或转录文本)。

2. 构建编码器

这部分我们来深入探讨如何构建Transformer中的编码器,具体包括每个编码器层内的各个子层,以及多头自注意力机制的实现。

2.1 注意力机制

注意力机制有不少变种,这里我们用的是缩放点积注意力(SDPA)。它通过查询、键和值来计算序列中元素之间的关系,并为每个元素分配一个分数,表示该元素和序列中所有元素的关联程度。

Transformer并非只用一组查询、键和值,而是采用多头注意力。把256维的向量分成8个头,每个头有一组32维的向量。每个头关注输入的不同方面,让模型能捕捉更广泛的信息。比如,在双关句“Why is the river so rich? Because it has two banks.”中,多头注意力就能帮助模型理解“bank”的多重含义。

(1) 在模块 util.py 中定义 attention() 函数:

import torch
DEVICE = "cuda" if torch.cuda.is_a vailable() else "cpu"
import numpy as np
from torch import nn
from copy import deepcopy
import math

def attention(query, key, value, mask=None, dropout=None):
    d_k = query.size(-1)
    # 缩放后的注意力得分是查询和键的点积,经过 d_k 的平方根缩放
    scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k)
    if mask is not None:
        # 如果存在掩码,隐藏序列中的未来元素
        scores = scores.masked_fill(mask == 0, -1e9)
    # 计算注意力权重
    p_attn = nn.functional.softmax(scores, dim=-1)
    if dropout is not None:
        p_attn = dropout(p_attn)
    # 返回注意力和注意力权重
    return torch.matmul(p_attn, value), p_attn

下图用一个实例展示了多头注意力的工作原理。给句子“How are you?”加上位置编码后,嵌入是一个大小为(1, 6, 256)的张量——1表示批次中只有一个句子,6个词元是因为我们在开头和结尾分别加了BOS和EOS。这些嵌入通过三个线性层,得到Q、K、V,每个大小为(1, 6, 256)。然后分成八个头,得到八组不同的Q、K、V,每个大小是(1, 6, 32)。对每组应用attention()函数,生成八个注意力输出,每个大小也是(1, 6, 32)。再把这八个输出拼接成一个注意力输出,大小是(1, 6, 256)。最后,拼接后的注意力通过一个256×256的线性层,得到MultiHeadAttention()类的最终输出,尺寸保持(1, 6, 256),和原始输入一样。

(2) 用PyTorch在 util.py 中实现多头注意力:

class MultiHeadedAttention(nn.Module):
    def __init__(self, h, d_model, dropout=0.1):
        super().__init__()
        assert d_model % h == 0
        self.d_k = d_model // h
        self.h = h
        self.linears = nn.ModuleList([deepcopy(nn.Linear(d_model, d_model)) for i in range(4)])
        self.attn = None
        self.dropout = nn.Dropout(p=dropout)

    def forward(self, query, key, value, mask=None):
        if mask is not None:
            mask = mask.unsqueeze(1)
        nbatches = query.size(0)
        # 输入通过三个线性层,得到 Q、K、V,并将它们拆分成多个注意力头
        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))]
        # 计算每个头的注意力和注意力权重
        x, self.attn = attention(query, key, value, mask=mask, dropout=self.dropout)
        # 将多头注意力向量连接成一个单一的注意力向量
        x = x.transpose(1, 2).contiguous().view(nbatches, -1, self.h * self.d_k)
        # 输出通过一个线性层
        output = self.linears[-1](x)
        return output

(3) 每个编码器层和解码器层还包含一个前馈子层,这是一个两层的全连接神经网络,用来增强模型捕捉复杂特征的能力。这个网络独立处理每个嵌入,而不是把嵌入序列看作一个整体,所以通常叫它逐位置前馈网络。在 util.py 中定义 PositionwiseFeedForward() 类:

class PositionwiseFeedForward(nn.Module):
    def __init__(self, d_model, d_ff, dropout=0.1):
        super().__init__()
        self.w_1 = nn.Linear(d_model, d_ff)
        self.w_2 = nn.Linear(d_ff, d_model)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        h1 = self.w_1(x)
        h2 = self.dropout(h1)
        return self.w_2(h2)

PositionwiseFeedForward() 定义了两个关键参数:d_ff(前馈层的维度)和 d_model(模型维度)。通常 d_ff 选择为 d_model 的四倍。本文 d_model 是256,所以 d_ff 设为1024。这种把隐藏层扩大的做法是Transformer架构的标准方法,能增强网络捕捉复杂特征的能力。

2.2 创建编码器

(1) 先定义 EncoderLayer() 类和 SublayerConnection() 类:

class SublayerConnection(nn.Module):
    def __init__(self, size, dropout):
        super().__init__()
        self.norm = LayerNorm(size)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x, sublayer):
        # 每个子层都经过残差连接和层归一化
        output = x + self.dropout(sublayer(self.norm(x)))
        return output

class EncoderLayer(nn.Module):
    def __init__(self, size, self_attn, feed_forward, dropout):
        super().__init__()
        self.self_attn = self_attn
        self.feed_forward = feed_forward
        self.sublayer = nn.ModuleList([deepcopy(SublayerConnection(size, dropout)) for i in range(2)])
        self.size = size

    def forward(self, x, mask):
        # 每个编码器层中的第一个子层是一个多头自注意力网络
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, mask))
        # 每个编码器层中的第二个子层是一个前馈网络
        output = self.sublayer[1](x, self.feed_forward)
        return output

每个编码器层由两个子层组成:多头自注意力层(使用 MultiHeadAttention())和逐位置前馈网络(使用 PositionwiseFeedForward())。两个子层都包含层归一化和残差连接,残差连接用于解决梯度消失问题。Transformer中残差连接的另一个好处是,为位置编码传递到后续层提供了一条通道。

(2) 层归一化类似于批归一化,把层中的观测标准化,使其均值为0、标准差为1。在 util.py 中定义 LayerNorm() 类:

class LayerNorm(nn.Module):
    def __init__(self, features, eps=1e-6):
        super().__init__()
        self.a_2 = nn.Parameter(torch.ones(features))
        self.b_2 = nn.Parameter(torch.zeros(features))
        self.eps = eps

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

(3) 通过堆叠六个编码器层创建编码器,在 util.py 中定义 Encoder() 类:

class Encoder(nn.Module):
    def __init__(self, layer, N):
        super().__init__()
        self.layers = nn.ModuleList([deepcopy(layer) for i in range(N)])
        self.norm = LayerNorm(layer.size)

    def forward(self, x, mask):
        for layer in self.layers:
            x = layer(x, mask)
        output = self.norm(x)
        return output

Encoder() 类定义了两个参数:layer(即 EncoderLayer() 指定的编码器层)和 N(编码器中的层数)。它接受输入 x(比如一批英文短语)和 mask(用于掩码序列填充),生成捕捉英文短语含义的向量表示。

3. 构建编码器-解码器 Transformer

接下来实现解码器。先创建解码器层,然后堆叠 N=6 个相同的层来形成解码器。

3.1 创建解码器层

每个解码器层由三个子层组成:

  • 多头自注意力层
  • 第一个子层输出与编码器输出之间的交叉注意力层
  • 前馈网络

每个子层都包含层归一化和残差连接。此外,解码器的多头自注意力子层被掩码化,防止当前位置关注后续位置,强制模型用序列中的之前元素来预测后续元素。

(1) 在 util.py 中定义 DecoderLayer() 类:

class DecoderLayer(nn.Module):
    def __init__(self, size, self_attn, src_attn, feed_forward, dropout):
        super().__init__()
        self.size = size
        self.self_attn = self_attn
        self.src_attn = src_attn
        self.feed_forward = feed_forward
        self.sublayer = nn.ModuleList([deepcopy(SublayerConnection(size, dropout)) for i in range(3)])

    def forward(self, x, memory, src_mask, tgt_mask):
        # 第一个子层是一个掩码多头自注意力层
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, tgt_mask))
        # 第二个子层是目标语言和源语言之间的跨注意力层
        x = self.sublayer[1](x, lambda x: self.src_attn(x, memory, memory, src_mask))
        # 第三个子层是一个前馈网络
        output = self.sublayer[2](x, self.feed_forward)
        return output

继续之前的例子。解码器接受词元[‘BOS’, ‘comment’, ‘et’, ‘es-vous’, ‘?’],以及来自编码器的输出(memory),来预测序列[‘comment’, ‘et’, ‘es-vous’, ‘?’, ‘EOS’]。输入嵌入是一个大小为(1, 5, 256)的张量:1是批次中的序列数量,5是词元数量,256是向量维度。把这个嵌入通过第一个子层(掩码多头自注意力层),计算过程类似编码器,但使用了掩码(代码中的 tgt_mask),它是一个5×5的张量:

可以看到,掩码的下半部分被设为True,上半部分被设为False。应用这个掩码后,第一个词元在第一时间步只关注自身;第二个时间步,注意力只在前两个词元之间计算。依此类推,第三个时间步,解码器用词元[‘BOS’, ‘comment’, ‘et’]来预测‘es-vous’,注意力只在这三个词元之间计算,有效隐藏了后面的词元。

第一个子层的输出大小是(1, 5, 256),和输入一致。这个输出(x)随后进入第二个子层,计算x和编码器输出(称为memory)之间的交叉注意力。memory的维度是(1, 6, 256),因为英文短语“How are you?”被转换成了六个词元。

交叉注意力权重的计算如下:先把x通过一个神经网络得到查询,维度是(1, 5, 256);再把memory通过两个神经网络得到键和值,维度都是(1, 6, 256)。计算缩放注意力分数,维度是(1, 5, 6)。对分数应用softmax后,得到注意力权重,这是一个5×6的矩阵,告诉法语输入的五个词元如何关注英语短语的六个词元。这就是解码器在翻译过程中捕捉英文含义的方式。

最终的交叉注意力通过注意力权重和值向量的点积得到。权重维度是(1, 5, 6),值向量是(1, 6, 256),所以最终结果大小是(1, 5, 256)。第二个子层的输入和输出维度相同。处理后,输出进入第三个子层——前馈网络。

3.2 创建编码器-解码器 Transformer

(1) 解码器由N=6个相同的解码器层组成。在 util.py 中定义 Decoder() 类:

class Decoder(nn.Module):
    def __init__(self, layer, N):
        super().__init__()
        self.layers = nn.ModuleList([deepcopy(layer) for i in range(N)])
        self.norm = LayerNorm(layer.size)

    def forward(self, x, memory, src_mask, tgt_mask):
        for layer in self.layers:
            x = layer(x, memory, src_mask, tgt_mask)
        output = self.norm(x)
        return output

(2) 创建编码器-解码器Transformer,先定义 Transformer() 类:

class Transformer(nn.Module):
    def __init__(self, encoder, decoder, src_embed, tgt_embed, generator):
        super().__init__()
        # 定义编码器
        self.encoder = encoder
        # 定义解码器
        self.decoder = decoder
        self.src_embed = src_embed
        self.tgt_embed = tgt_embed
        self.generator = generator

    def encode(self, src, src_mask):
        return self.encoder(self.src_embed(src), src_mask)

    def decode(self, memory, src_mask, tgt, tgt_mask):
        return self.decoder(self.tgt_embed(tgt), memory, src_mask, tgt_mask)

    def forward(self, src, tgt, src_mask, tgt_mask):
        # 源语言编码为向量表示
        memory = self.encode(src, src_mask)
        # 解码器使用这些向量表示生成目标语言的翻译
        output = self.decode(memory, src_mask, tgt, tgt_mask)
        return output

Transformer() 类由五个关键组件构成:编码器、解码器、源语言嵌入、目标语言嵌入和生成器。编码器和解码器分别由 Encoder()Decoder() 类表示。在英法翻译示例中,源语言嵌入用词嵌入和位置编码处理英语短语的数值表示,组合成 src_embed;同理,目标语言嵌入处理法语短语的数值表示,组合成 tgt_embed。生成器 Generator() 则为目标语言中每个词元对应的索引生成预测概率。

4. 基于 Transformer 构建机器翻译模型

本节整合所有组件,创建一个可以在任意两种语言之间进行翻译的Transformer模型。

4.1 定义生成器

首先,在 util.py 中定义 Generator() 类,用于生成下一个词元的概率分布。基本思路是给解码器附加一个输出头,用于下游任务——这里是预测法语翻译中的下一个词元。

(1) 定义 Generator() 类,它为每个索引生成预测概率,对应目标语言中的词元:

class Generator(nn.Module):
    def __init__(self, d_model, vocab):
        super().__init__()
        self.proj = nn.Linear(d_model, vocab)

    def forward(self, x):
        out = self.proj(x)
        probs = nn.functional.log_softmax(out, dim=-1)
        return probs

4.2 创建翻译模型

(1) 创建Transformer模型,用于任意两种语言之间的翻译。在 util.py 中定义 create_model() 函数:

def create_model(src_vocab, tgt_vocab, N, d_model, d_ff, h, dropout=0.1):
    attn = MultiHeadedAttention(h, d_model).to(DEVICE)
    ff = PositionwiseFeedForward(d_model, d_ff, dropout).to(DEVICE)
    pos = PositionalEncoding(d_model, dropout).to(DEVICE)
    model = Transformer(
        # 通过实例化 Encoder() 类创建编码器
        Encoder(EncoderLayer(d_model, deepcopy(attn), deepcopy(ff), dropout).to(DEVICE), N).to(DEVICE),
        # 通过实例化 Decoder() 类创建解码器
        Decoder(DecoderLayer(d_model, deepcopy(attn), deepcopy(attn), deepcopy(ff), dropout).to(DEVICE), N).to(DEVICE),
        # 将源语言通过词嵌入和位置编码创建 src_embed
        nn.Sequential(Embeddings(d_model, src_vocab).to(DEVICE), deepcopy(pos)),
        # 将目标语言通过词嵌入和位置编码创建 tgt_embed
        nn.Sequential(Embeddings(d_model, tgt_vocab).to(DEVICE), deepcopy(pos)),
        # 通过实例化 Generator() 类创建生成器
        Generator(d_model, tgt_vocab)
    ).to(DEVICE)
    for p in model.parameters():
        if p.dim() > 1:
            nn.init.xa vier_uniform_(p)
    return model.to(DEVICE)

create_model() 函数中,用 Encoder()Decoder()Generator() 类顺序构建了Transformer的五个关键组件:编码器、解码器、源语言嵌入、目标语言嵌入和生成器。

小结

  • Transformer是一种先进的深度学习模型,擅长处理序列到序列的预测任务。它的优势在于能有效理解输入和输出序列中元素之间的长距离关系。
  • Transformer架构的创新之处在于注意力机制。它通过分配权重来评估单词之间的关系,基于训练数据确定单词之间的关联程度。这让模型能理解单词之间的关系,更有效地处理语言。
  • 计算缩放点积注意力时,输入嵌入X通过三个不同神经网络层处理,得到查询Q、键K和值V。SDPA的计算如下:
    Attention(Q,K,V) = softmax((Q·K^T)/sqrt(d_k))·V
    其中d_k是键向量K的维度。对注意力分数应用softmax,确保一个单词对句子中所有单词的总注意力之和为100%。最终注意力是注意力权重与值向量V的点积。
  • Transformer采用多头注意力机制,查询、键和值向量被拆分成多个头,每个头关注输入的不同方面,让模型能捕捉更广泛的信息,形成更详细和更具上下文的理解。当一个单词在句子中有多重含义时,多头注意力尤其有用。
免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策