之前我们讨论了怎样实现 Transformer 的核心多头注意力机制,那么这期我们来完整地实现整个 Transformer 的编码器和解码器。
Transformer 架构最初由 Vaswani 等人在 2017 年的论文《Attention Is All You Need》中提出,专为序列到序列(seq2seq)的机器翻译使命计划。其核心创新在于完全摒弃了传统的循环神经网络(RNN)和卷积神经网络(CNN)结构,仅依赖自注意力机制来捕捉输入和输出序列之间的长间隔依赖关系。原始 Transformer 采用了编码器 - 解码器(Encoder - Decoder)架构,这种架构在后续的天然语言处理研究中被广泛应用和扩展。
编码器的作用是将源语言中的句子作为输入,并生成基于注意力的表示。这种表示可以或许有用地捕捉输入序列中各个元素之间的关系。颠末后续的发展,研究者们发现可以根据具体使命的需求,对 Transformer 架构举行简化和调整,从而出现了仅编码器(Encoder - only)和仅解码器(Decoder - only)的 Transformer 模子。例如,BERT(Bidirectional Encoder Representations from Transformers)是仅编码器架构的典型代表,重要用于语言理解使命;而 GPT(Generative Pre - trained Transformer)则是仅解码器架构的代表,重要用于文本生成使命。
我们在这里先手动实现一个编码器块(encoder block)。
在神经网络中,"块"(Block)和 "层"(Layer)都是常用的概念。"层"(Layer)是神经网络的基本盘算单元,如全毗连层、卷积层、注意力层等,它们执行特定的数学运算,对输入数据举行变换。而 "块"(Block)通常指模块化计划的子网络结构,由多个层(Layer)或操纵组合而成,用于实现特定的功能。块的核心头脑是复用性和可扩展性 —— 通过重复堆叠相同的块来构建复杂的模子,同时简化代码和优化过程。这种计划模式使得网络结构更加清楚,便于理解和维护,同时也提高了模子的灵活性和可扩展性。通过将多个编码器块和 / 或解码器块按照特定的方式组合起来,就可以构建出完整的 Transformer 模子,以适应差别的天然语言处理使命。- class EncoderBlock(nn.Module):
- def __init__(self, input_dim, num_heads, dim_feedforward, dropout=0.0):
- """EncoderBlock.
- Args:
- input_dim: Dimensionality of the input
- num_heads: Number of heads to use in the attention block
- dim_feedforward: Dimensionality of the hidden layer in the MLP
- dropout: Dropout probability to use in the dropout layers
- """
- super().__init__()
- # Attention layer
- self.self_attn = MultiheadAttention(input_dim, input_dim, num_heads)
- # Two-layer MLP
- self.linear_net = nn.Sequential(
- nn.Linear(input_dim, dim_feedforward),
- nn.Dropout(dropout),
- nn.ReLU(inplace=True),
- nn.Linear(dim_feedforward, input_dim),
- )
- # Layers to apply in between the main layers
- self.norm1 = nn.LayerNorm(input_dim)
- self.norm2 = nn.LayerNorm(input_dim)
- self.dropout = nn.Dropout(dropout)
- def forward(self, x, mask=None):
- # Attention part
- attn_out = self.self_attn(x, mask=mask)
- x = x + self.dropout(attn_out)
- x = self.norm1(x)
- # MLP part
- linear_out = self.linear_net(x)
- x = x + self.dropout(linear_out)
- x = self.norm2(x)
- return x
复制代码 首先依旧是实现一个编码器块的类并初始化,在上期中我们编写了一个多头注意力的类MultiheadAttention,用来实现多头注意力机制。这里我们使用- self.self_attn = MultiheadAttention(input_dim, input_dim, num_heads)
复制代码 来创建该类的一个实例,并将其赋值给self.self_attn以成为当前类的一个属性。
然后我们界说一个前馈神经网络(指数据单向活动),使用两层的MLP,分别包罗两个线性层、drop out层以及激活函数ReLU。(这些是神经网络常用结构,简单来说drop out层防止过拟合,激活函数增加非线性)。
再界说一下每层的归一化(nn.LayerNorm(input_dim)),以稳定练习过程
最后把上面界说好的内容串起来,实现编码块的前向流传部分:
1、首先使用多头注意力对输入x盘算
2、实现残差毗连和drop out(可以解决梯度消散、爆炸的题目,稳定练习),然后举行第一次层归一化
3、颠末我们上面界说的线性层,然后再次残差毗连
4、第二次层归一化,并得到终极输出
然后我们基于上面的编码器块来实现完整的transformer编码器(可以理解为上面添了一块砖,下面是我们用砖堆叠形成必要的房子)- class TransformerEncoder(nn.Module):
- def __init__(self, num_layers, **block_args):
- super().__init__()
- self.layers = nn.ModuleList([EncoderBlock(**block_args) for _ in range(num_layers)])
- def forward(self, x, mask=None):
- for layer in self.layers:
- x = layer(x, mask=mask)
- return x
复制代码 上述代码中的关键代码- self.layers = nn.ModuleList([EncoderBlock(**block_args) for _ in range(num_layers)])
复制代码 顾名思义就是是通过for循环重复创建神经网络的层,层数为指定的num_layers的值,这里的block_args是在实例化TransformerEncoder时网络额外参数打包成字典,然后在创建EncoderBlock时解包并作为参数传入。
然后再来实现一个前面讲解过的位置编码- class PositionalEncoding(nn.Module):
- def __init__(self, d_model, max_len=5000):
- """Positional Encoding.
- Args:
- d_model: Hidden dimensionality of the input.
- max_len: Maximum length of a sequence to expect.
- """
- super().__init__()
- # Create matrix of [SeqLen, HiddenDim] representing the positional encoding for max_len inputs
- pe = torch.zeros(max_len, d_model)
- position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
- div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
- pe[:, 0::2] = torch.sin(position * div_term)
- pe[:, 1::2] = torch.cos(position * div_term)
- pe = pe.unsqueeze(0)
- # register_buffer => Tensor which is not a parameter, but should be part of the modules state.
- # Used for tensors that need to be on the same device as the module.
- # persistent=False tells PyTorch to not add the buffer to the state dict (e.g. when we save the model)
- self.register_buffer("pe", pe, persistent=False)
- def forward(self, x):
- x = x + self.pe[:, : x.size(1)]
- return x
复制代码 接下来,我们再来实现一个优化器(optimizer)和学习率调度器(lr scheduler),这些可以说是神经网络必备的组件,而且和其他超参数一样大多是工程实践的结果,信赖大家多多少少叶听过,在此就不再赘述了,感兴趣的同砚可以自行查阅我之前的博客,内里提及了差别optimizer和lr scheduler之间的优劣。 - class CosineWarmupScheduler(optim.lr_scheduler._LRScheduler):
- def __init__(self, optimizer, warmup, max_iters):
- self.warmup = warmup
- self.max_num_iters = max_iters
- super().__init__(optimizer)
- def get_lr(self):
- lr_factor = self.get_lr_factor(epoch=self.last_epoch)
- return [base_lr * lr_factor for base_lr in self.base_lrs]
- def get_lr_factor(self, epoch):
- lr_factor = 0.5 * (1 + np.cos(np.pi * epoch / self.max_num_iters))
- if epoch <= self.warmup:
- lr_factor *= epoch * 1.0 / self.warmup
- return lr_factor
复制代码 到此,我们造车所需的全部零件就都计划好了,接下来我们使用pytorch_lighting框架把这些零件全部组装起来,让车真正开始上路。
这里我提一嘴,PyTorch Lightning(PL)是基于 PyTorch 的开源深度学习框架,旨在简化复杂模子的练习流程,同时保留 PyTorch 的灵活性与原生功能。其核心价值是PL 通过模块化计划和主动化机制,将深度学习开发流程尺度化为以下核心组件:
- 模子界说:仅需关注神经网络结构(如 Transformer 的编码器 - 解码器)和丧失函数,无需重复编写练习循环代码。
- 练习逻辑:主动管理练习、验证、测试阶段的流程,支持分布式练习、混合精度盘算等高级功能。
- 日记与回调:内置对 TensorBoard、W&B 等日记工具的支持,可主动记录指标、生存查抄点。
与 PyTorch 的关系
- 非侵入性:PL 不改变 PyTorch 的底层逻辑,模子仍以原生 PyTorch 张量(Tensor)和模块(Module)为基础。
- 流程抽象:将数据加载、优化器设置、反向流传等重复性代码封装为框架逻辑,开发者仅需实现核心业务逻辑(如多头注意力机制、位置编码)。
典型应用场景
在构建 Transformer 等复杂模子时,PL 可大幅淘汰样板代码。例如:
- 界说模子时,只需编写编码器和解码器的前向流传逻辑;
- 练习时,PL 主动处理批次迭代、梯度更新、验证集评估等流程;
- 支持快速扩展功能(如模子量化、onnx 导出),无需修改核心代码。
PL 怎样简化 Transformer 实现呢? 首先练习流程抽象,就是PL 的 Trainer 主动管理 epoch 循环、梯度更新、设备同步等底层逻辑。然后对超参数管理,通过 self.hparams 统一访问参数,支持下令行或配置文件动态调整(如 Hydra)。当然代码复用性也是必不可少的,自界说模块(如 PositionalEncoding)与 PL 组件解耦,便于单独测试或更换。以下是对代码结构与逻辑的详细解析,以技术视比赛层拆解实现细节:
1. 整体架构:基于 PyTorch Lightning 的模块化计划
TransformerPredictor 类继承自 pl.LightningModule,核心目标是构建一个基于 Transformer 的序列猜测模子。其核心优势在于:
练习流程主动化:通过 PL 的 Trainer 类主动管理多 epoch 练习,无需手动编写循环。
超参数集成:通过 self.save_hyperparameters() 主动管理超参数,支持与 Hydra 等工具集成,实现配置文件驱动的参数管理。
2. 初始化与超参数
- def __init__(
- self,
- input_dim,
- model_dim,
- num_classes,
- num_heads,
- num_layers,
- lr,
- warmup,
- max_iters,
- dropout=0.0,
- input_dropout=0.0,
- ):
- super().__init__()
- self.save_hyperparameters() # 自动保存超参数到self.hparams
- self._create_model()
复制代码 关键参数:
input_dim:输入特征维度(如文本嵌入维度)。
model_dim:Transformer 内部特征维度(需与多头注意力头数匹配,即 model_dim % num_heads == 0)。
num_layers:Transformer 编码器层数,每层包罗多头注意力和前馈网络。
warmup/max_iters:学习率调度器参数,控制练习初期的学习率升温与余弦衰减。
3. 模子构建:从输入到输出的流水线
3.1 输入预处理模块
- self.input_net = nn.Sequential(
- nn.Dropout(self.hparams.input_dropout),
- nn.Linear(self.hparams.input_dim, self.hparams.model_dim)
- )
复制代码 功能:将输入特征(如词嵌入)从 input_dim 投影到 model_dim,并添加 Dropout 抑制过拟合。
3.2 位置编码模块
- self.positional_encoding = PositionalEncoding(d_model=self.hparams.model_dim)
复制代码 作用:为序列添加位置信息(因 Transformer 无循环结构,需显式编码顺序)。
实现:通过正弦 / 余弦函数生成固定位置编码,与输入特征相加后输入编码器。
3.3 Transformer 编码器模块
- self.transformer = TransformerEncoder(
- num_layers=self.hparams.num_layers,
- input_dim=self.hparams.model_dim,
- dim_feedforward=2 * self.hparams.model_dim,
- num_heads=self.hparams.num_heads,
- dropout=self.hparams.dropout,
- )
复制代码 核心组件:多层 EncoderBlock:每层包罗 多头注意力层 和 前馈神经网络,通过残差毗连与层归一化(LayerNorm)稳定练习。
多头注意力:将输入拆分为多个头(num_heads),每个头独立盘算注意力,捕捉差别语义特征(如语法、上下文),终极拼接输出。
前馈网络:对注意力输出举行非线性变换,加强特征表达本领。
3.4 输出分类模块
- self.output_net = nn.Sequential(
- nn.Linear(model_dim, model_dim),
- nn.LayerNorm(model_dim),
- nn.ReLU(),
- nn.Dropout(dropout),
- nn.Linear(model_dim, num_classes),
- )
复制代码 功能:将 Transformer 输出的特征(model_dim)投影到使命目标维度(num_classes),用于序列标注或分类。
结构:通过两层线性变换 + 非线性激活,引入层归一化和 Dropout 提升泛化本领。
4. 前向流传:数据活动的核心逻辑
- def forward(self, x, mask=None, add_positional_encoding=True):
- x = self.input_net(x) # 输入投影
- if add_positional_encoding:
- x = self.positional_encoding(x) # 添加位置编码
- x = self.transformer(x, mask=mask) # Transformer编码
- x = self.output_net(x) # 输出分类
- return x
复制代码 输入形状:x 为 [Batch, SeqLen, input_dim],经投影后变为 [Batch, SeqLen, model_dim]。
位置编码:通过 add_positional_encoding 参数控制是否启用(实用于无需位置信息的特殊使命)。
掩码机制:mask 用于屏蔽无效位置(如添补符),制止注意力盘算时引入噪声。
5. 练习相干组件:优化器与调度器
5.1 优化器配置
- def configure_optimizers(self):
- optimizer = optim.Adam(self.parameters(), lr=self.hparams.lr)
- self.lr_scheduler = CosineWarmupScheduler(
- optimizer, warmup=self.hparams.warmup, max_iters=self.hparams.max_iters
- )
- return optimizer
复制代码 优化器:使用 Adam 优化器,参数包罗模子所有可学习权重(如线性层、位置编码中的可学习参数)。
学习率调度器:Warmup 阶段:练习初期渐渐提升学习率,制止模子在随机初始化阶段过拟合。Cosine 衰减阶段:学习率随迭代次数呈余弦曲线降落,促进模子收敛到更优解。
5.2 分步执行逻辑
- def optimizer_step(self, *args, **kwargs):
- super().optimizer_step(*args, **kwargs)
- self.lr_scheduler.step() # 每步迭代更新学习率
复制代码 关键点:PL 默认学习率调度器按 epoch 更新,此处逼迫按迭代更新(step() 在 optimizer_step 中调用),适配复杂练习场景。
我们简单梳理一下上述代码的结构,内里调用了三个我们之前创建的类,分别是:
PositionalEncoding :位置编码类
TransformerEncoder :Transformer编码器类
CosineWarmupScheduler :学习率调度器类
而Transformer编码器类是靠循环创建我们界说好的编码器块类(EncoderBlock)实现的。
EncoderBlock类则通过调用我们先前写好的多头注意力机制盘算以及计划网络毗连方式来实现的。
而对多头注意力机制的核心盘算方式的实现就又回到了我们一开始中的函数把QKV按照差别的方式做点积。
也就是说我们之前计划的所有结构就都汇总到了最后这段代码里,并通过PyTorch Lightning实现了练习流程的主动化。
上面的代码就是我们造好的transformer车辆的完整形态,下一期我们将实验驾驶这辆车开在我们的数据公路上。
Transformer模子测试
在本节内容中,我们将对之前实现的 Transformer 模子举行测试,以验证其性能。我们创建了一个简单的数据集,其中输入是介于 0 和 M 之间的数字序列 N,使命是将输入序列反转,即输出为 x [::-1]。选择这个使命是因为它必要模子捕捉长期依赖关系,而这正是 Transformer 架构的优势所在。相比之下,传统的循环神经网络(RNN)在处理此类使命时可能会遇到困难,因为它们在处理长序列时碰面临梯度消散或爆炸的题目。通过这个简单的使命,我们可以直观地比力 Transformer 与传统模子的性能差别。接下来,我们将使用 PyTorch Lightning 搭建的练习流程在这个数据集上举行实行,观察 Transformer 模子的体现。- class ReverseDataset(data.Dataset):
- def __init__(self, num_categories, seq_len, size):
- super().__init__()
- self.num_categories = num_categories
- self.seq_len = seq_len
- self.size = size
- self.data = torch.randint(self.num_categories, size=(self.size, self.seq_len))
- def __len__(self):
- return self.size
- def __getitem__(self, idx):
- inp_data = self.data[idx]
- labels = torch.flip(inp_data, dims=(0,))
- return inp_data, labels
复制代码 接下来,我们创建任意数量的介于 0 到 num_categories-1,代码中的示例是10, 之间的随机数字序列。标签只是将张量沿序列维度翻转。我们可以在下面创建相应的数据加载器。- dataset = partial(ReverseDataset, 10, 16)
- train_loader = data.DataLoader(dataset(50000), batch_size=128, shuffle=True, drop_last=True, pin_memory=True)
- val_loader = data.DataLoader(dataset(1000), batch_size=128)
- test_loader = data.DataLoader(dataset(10000), batch_size=128)
复制代码 我们必要使用 Python 的 functools.partial 对自界说数据集类 ReverseDataset 举行参数预绑定,核心目标是固定部分参数并保留灵活性。首先明白 ReverseDataset 的功能:生成由数字组成的序列样本,每个样本包罗 16 个数字(范围 0-9),输出为该序列的逆序。例如输入 [1,2,3,4,5] 对应输出 [5,4,3,2,1],每个样本的数字范围和样本内数字数量(16 个)是固定的,而数据集的样本总数(如练习集 50000 条)通过 size 参数动态指定。
使用 functools.partial 时,先将 ReverseDataset 的参数 num_samples(样本内数字数量)固定为 16,digit_range(数字范围)固定为 (0,10)(即 0-9),这两个参数对所有数据集实例是统一的。剩下的 size 参数(数据集样本总数)不预先绑定,而是留给差别数据集(如练习集、验证集)自行指定。例如创建练习集时,通过partial(ReverseDataset, num_samples=16, digit_range=(0,10))(size=50000)生成 50000 条样本,每个样本是 16 个 0-9 的数字,输出为逆序序列。这种方式通过参数预绑定淘汰重复设置,同时保留 size 的灵活性,使数据集构建更简洁且符合使命需求。- inp_data, labels = train_loader.dataset[0]
- print("Input data:", inp_data)
- print("Labels: ", labels)
复制代码
导入所有必要的库 - import math
- import os
- import urllib.request
- from functools import partial
- from urllib.error import HTTPError
- # Plotting
- import matplotlib
- import matplotlib.pyplot as plt
- import matplotlib_inline.backend_inline
- import numpy as np
- # PyTorch Lightning
- import pytorch_lightning as pl
- import seaborn as sns
- # PyTorch
- import torch
- import torch.nn as nn
- import torch.nn.functional as F
- import torch.optim as optim
- import torch.utils.data as data
- # Torchvision
- import torchvision
- from pytorch_lightning.callbacks import ModelCheckpoint
- from torchvision import transforms
- from tqdm.notebook import tqdm
- # Setting the seed
- pl.seed_everything(42)
复制代码 新建生存模子的文件夹- DATASET_PATH = os.environ.get("PATH_DATASETS", "data/")
- # Path to the folder where the pretrained models are saved
- CHECKPOINT_PATH = os.environ.get("PATH_CHECKPOINT", "saved_models/Transformers/")
- os.makedirs(CHECKPOINT_PATH, exist_ok=True)
复制代码 这里提供两种选项,我们如果想自己从头练习一个模子,则无需下面的代码(纯CPU练习1分钟左右)。如果想直接加载已经练习好的预练习模子,则添加以下代码下载。- # Github URL where saved models are stored for this tutorial
- base_url = "fill in yours"
- # Files to download
- pretrained_files = ["ReverseTask.ckpt", "SetAnomalyTask.ckpt"]
- # Create checkpoint path if it doesn't exist yet
- os.makedirs(CHECKPOINT_PATH, exist_ok=True)
- # For each file, check whether it already exists. If not, try downloading it.
- for file_name in pretrained_files:
- file_path = os.path.join(CHECKPOINT_PATH, file_name)
- if "/" in file_name:
- os.makedirs(file_path.rsplit("/", 1)[0], exist_ok=True)
- if not os.path.isfile(file_path):
- file_url = base_url + file_name
- print(f"Downloading {file_url}...")
- try:
- urllib.request.urlretrieve(file_url, file_path)
- except HTTPError as e:
- print(
- "Something went wrong. Please try to download the file manually,"
- " or contact the author with the full output including the following error:\n",
- e,
- )
复制代码 继承并重写一下transformer,因为我们的目标是基于 Transformer 架构实现序列反转使命,核心在于通过模子学习输入数字序列与反转后的目标序列之间的映射关系。由于序列中的每个数字代表独立的类别(如数字 1 和 3 是完全差别的类别,而非数值巨细或数量的线性关系),因此采用 one-hot 编码对输入序罗列行离散化处理至关重要。这是因为若直接使用数值编码(如整数嵌入),模子可能会错误捕捉数字间的线性关联(例如认为 3 比 1 “大” 或存在倍数关系),而现实上我们必要模子将每个数字视为独立的离散符号,仅关注其位置和类别信息。
在模子计划上,继承 PyTorch Lightning 的 LightningModule 类并重写 Transformer 结构,使其适配序列反转使命。核心修改在于通过_calculate_loss方法统一处理三种模式(练习、验证、测试)的丧失盘算与指标记录。具体而言,该方法接收模子输出、真实标签和模式标识,使用交织熵丧失函数盘算丧失(因 one-hot 编码的标签本质是多分类题目),并通过 PyTorch Lightning 内置的self.log方法主动记录各模式下的丧失值及其他指标(如准确率)。这种计划确保了练习流程的尺度化,同时利用框架特性实现指标的实时监控 与日记管理。模子的输入层将 one-hot 编码的序列映射到高维嵌入空间,结合位置编码捕捉顺序信息(因 Transformer 自己不具备序列顺序感知本领)。编码器和解码器均采用多头注意力机制与前馈神经网络的组合,通过层归一化和残差毗连提升练习稳定性。终极输出层通过线性变换将解码器输出映射回 one-hot 空间,生成反转后的序列猜测。通过统一的丧失盘算模块,模子在差别练习阶段(练习 / 验证 / 测试)均可保持同等的指标盘算逻辑,确保评估结果的可靠性与可比性,同时利用框架的日记系统简化实行跟踪流程。- class ReversePredictor(TransformerPredictor):
- def _calculate_loss(self, batch, mode="train"):
- # Fetch data and transform categories to one-hot vectors
- inp_data, labels = batch
- inp_data = F.one_hot(inp_data, num_classes=self.hparams.num_classes).float()
- # Perform prediction and calculate loss and accuracy
- preds = self.forward(inp_data, add_positional_encoding=True)
- loss = F.cross_entropy(preds.view(-1, preds.size(-1)), labels.view(-1))
- acc = (preds.argmax(dim=-1) == labels).float().mean()
- # Logging
- self.log(f"{mode}_loss", loss)
- self.log(f"{mode}_acc", acc)
- return loss, acc
- def training_step(self, batch, batch_idx):
- loss, _ = self._calculate_loss(batch, mode="train")
- return loss
- def validation_step(self, batch, batch_idx):
- _ = self._calculate_loss(batch, mode="val")
- def test_step(self, batch, batch_idx):
- _ = self._calculate_loss(batch, mode="test")
复制代码 再组织一下练习环境,设备检测、模子检测等。若未检测到模子则主动重新练习,若已有模子存在则主动加载- def train_reverse(**kwargs):
- # Create a PyTorch Lightning trainer with the generation callback
- root_dir = os.path.join(CHECKPOINT_PATH, "ReverseTask")
- os.makedirs(root_dir, exist_ok=True)
- trainer = pl.Trainer(
- default_root_dir=root_dir,
- callbacks=[ModelCheckpoint(save_weights_only=True, mode="max", monitor="val_acc")],
- accelerator="auto",
- devices=1,
- max_epochs=10,
- gradient_clip_val=5,
- )
- trainer.logger._default_hp_metric = None # Optional logging argument that we don't need
- # Check whether pretrained model exists. If yes, load it and skip training
- pretrained_filename = os.path.join(CHECKPOINT_PATH, "ReverseTask.ckpt")
- if os.path.isfile(pretrained_filename):
- print("Found pretrained model, loading...")
- model = ReversePredictor.load_from_checkpoint(pretrained_filename)
- else:
- model = ReversePredictor(max_iters=trainer.max_epochs * len(train_loader), **kwargs)
- trainer.fit(model, train_loader, val_loader)
- # Test best model on validation and test set
- val_result = trainer.test(model, dataloaders=val_loader, verbose=False)
- test_result = trainer.test(model, dataloaders=test_loader, verbose=False)
- result = {"test_acc": test_result[0]["test_acc"], "val_acc": val_result[0]["test_acc"]}
- model = model.to(device)
- return model, result
复制代码 开始练习模子(给定超参数)- reverse_model, reverse_result = train_reverse(
- input_dim=train_loader.dataset.num_categories,
- model_dim=32,
- num_heads=1,
- num_classes=train_loader.dataset.num_categories,
- num_layers=1,
- dropout=0.0,
- lr=5e-4,
- warmup=50,
- )
复制代码 开始练习-练习完成,我们再来看看其准确度- print("Val accuracy: %4.2f%%" % (100.0 * reverse_result["val_acc"]))
- print("Test accuracy: %4.2f%%" % (100.0 * reverse_result["test_acc"]))
复制代码
可以看出输出的结果非常完善
在本系列对 Transformer 的探索中,我们完整拆解了其核心构成。多头注意力机制作为 Transformer 的基石,通过查询向量、键向量与值向量的缩放点积运算,实现了对输入序列元素间相干性的精准捕捉。具体而言,该机制将输入特征分别投影为多个子查询、子键和子值,通过并行盘算差别头的注意力分布,使模子可以或许从多个独立视角提取序列的语义、结构等特征,终极将各头结果拼接融合,明显加强了特征表达的丰富性。
Transformer 架构以多头注意力层为核心,通过叠加类似 ResNet 的残差毗连与层归一化模块,构建起深度网络以建模复杂模式。这一架构的强大之处不仅在于其对天然语言处理使命的革新,更表如今跨范畴的泛化本领 —— 从文本翻译、图像生成到音频处理等序列或非序列使命,均能通过适配输入情势发挥作用。值得关注的是,Transformer 自己对输入序列的排列顺序不敏感(置换等变性),这一特性使其能灵活处理无序数据,但也需通过位置编码显式引入序列顺序信息,以补充对时序关系建模的缺失。
在工程实现中,除了理解架构原理,还需关注练习细节。例如学习率预热策略,可通过在初始迭代阶段渐渐提升学习率,制止因参数随机初始化导致的梯度波动,确保模子稳定收敛。通过本系列的实践,我们不仅复现了 Transformer 的核心头脑,更深入理解了其适应多样化数据与使命的内涵逻辑。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|