Transformers 亮相以来彻底改变了深度学习模型。
Transformer 原理详解与 PyTorch 编码实现
Transformer 模型通过注意力机制和编码器 - 解码器架构彻底改变了自然语言处理领域。详细解析了自注意力、位置编码、多头注意力及前馈网络等核心组件的工作原理,并提供了基于 PyTorch 的完整代码实现,涵盖从基础组件到完整模型训练评估的全过程。内容涉及 BERT 与 GPT 等衍生模型,适合希望深入理解 Transformer 底层逻辑与工程实现的开发者。

Transformer 模型通过注意力机制和编码器 - 解码器架构彻底改变了自然语言处理领域。详细解析了自注意力、位置编码、多头注意力及前馈网络等核心组件的工作原理,并提供了基于 PyTorch 的完整代码实现,涵盖从基础组件到完整模型训练评估的全过程。内容涉及 BERT 与 GPT 等衍生模型,适合希望深入理解 Transformer 底层逻辑与工程实现的开发者。

Transformers 亮相以来彻底改变了深度学习模型。
今天,我们来揭示 Transformers 背后的核心概念:注意力机制、编码器 - 解码器架构、多头注意力等等。
通过 Python 代码片段,让你深入了解其原理。
注意力机制是神经网络中一个迷人的概念,特别是在涉及到像 NLP 这样的任务时。它就像给模型一个聚光灯,让它能够集中注意力在输入序列的某些部分,同时忽略其他部分,就像我们人类在理解句子时关注特定的单词或短语一样。
现在,让我们深入了解一种特定类型的注意力机制,称为自注意力,也称为内部注意力。想象一下,当你阅读一句话时,你的大脑会自动突出显示重要的单词或短语来理解意思。这就是神经网络中自注意力的基本原理。它使序列中的每个单词都能'关注'其他单词,包括自己在内,以更好地理解上下文。
以下是自注意力在一个简单示例中的工作原理:
考虑一句话:'The cat sat on the mat.'
嵌入
首先,模型将输入序列中的每个单词嵌入到一个高维向量表示中。这个嵌入过程允许模型捕捉单词之间的语义相似性。
查询、键和值向量
接下来,模型为序列中的每个单词计算三个向量:查询向量、键向量和值向量。在训练过程中,模型学习这些向量,每个向量都有不同的作用。查询向量表示单词的查询,即模型在序列中寻找的内容。键向量表示单词的键,即序列中其他单词应该注意的内容。值向量表示单词的值,即单词对输出所贡献的信息。
注意力分数
一旦模型计算了每个单词的查询、键和值向量,它就会为序列中的每一对单词计算注意力分数。这通常通过取查询向量和键向量的点积来实现,以评估单词之间的相似性。
SoftMax 归一化
然后,使用 softmax 函数对注意力分数进行归一化,以获得注意力权重。这些权重表示每个单词应该关注序列中其他单词的程度。注意力权重较高的单词被认为对正在执行的任务更为关键。
加权求和
最后,使用注意力权重计算值向量的加权和。这产生了每个序列中单词的自注意力机制输出,捕获了来自其他单词的上下文信息。
下面是一个计算注意力分数的简单解释:
# 安装 PyTorch
!pip install torch==2.2.1+cu121
# 导入库
import torch
import torch.nn.functional as F
# 示例输入序列
input_sequence = torch.tensor([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6], [0.7, 0.8, 0.9]])
# 生成 Key、Query 和 Value 矩阵的随机权重
random_weights_key = torch.randn(input_sequence.size(-1), input_sequence.size(-1))
random_weights_query = torch.randn(input_sequence.size(-1), input_sequence.size(-1))
random_weights_value = torch.randn(input_sequence.size(-1), input_sequence.size(-1))
# 计算 Key、Query 和 Value 矩阵
key = torch.matmul(input_sequence, random_weights_key)
query = torch.matmul(input_sequence, random_weights_query)
value = torch.matmul(input_sequence, random_weights_value)
# 计算注意力分数
attention_scores = torch.matmul(query, key.T) / torch.sqrt(torch.tensor(query.size(-1), dtype=torch.float32))
# 使用 softmax 函数获得注意力权重
attention_weights = F.softmax(attention_scores, dim=-1)
# 计算 Value 向量的加权和
output = torch.matmul(attention_weights, value)
print("自注意力机制后的输出:")
print(output)
在我们深入探讨 Transformer 模型的复杂工作原理之前,让我们花点时间欣赏其开创性的架构。正如我们之前讨论的,Transformer 模型通过引入围绕自注意力机制的新颖方法,重塑了自然语言处理(NLP)的格局。在接下来的章节中,我们将揭开 Transformer 模型的核心组件,阐明其编码器 - 解码器架构、位置编码、多头注意力和前馈网络。
编码器 - 解码器架构
在 Transformer 的核心是其编码器 - 解码器架构——两个关键组件之间的共生关系,分别负责处理输入序列和生成输出序列。编码器和解码器中的每一层都包含相同的子层,包括自注意力机制和前馈网络。这种架构不仅有助于全面理解输入序列,而且能够生成上下文丰富的输出序列。
位置编码
尽管 Transformer 模型具有强大的功能,但它缺乏对元素顺序的内在理解——这是位置编码所解决的一个缺点。通过将输入嵌入与位置信息结合起来,位置编码使模型能够区分序列中元素的相对位置。这种细致的理解对于捕捉语言的时间动态和促进准确理解至关重要。
多头注意力
Transformer 模型的一个显著特征是它能够同时关注输入序列的不同部分——这是多头注意力实现的。通过将查询、键和值向量分成多个头,并进行独立的自注意力计算,模型获得了对输入序列的细致透视,丰富了其表示,带有多样化的上下文信息。
前馈网络
与人类大脑能够并行处理信息的能力类似,Transformer 模型中的每一层都包含一个前馈网络——一种能够捕捉序列中元素之间复杂关系的多功能组件。通过使用线性变换和非线性激活函数,前馈网络使模型能够在语言的复杂语义景观中航行,促进文本的稳健理解和生成。
要实现,首先运行位置编码、多头注意力机制和前馈网络的代码,然后是编码器、解码器和 Transformer 架构。
1、位置编码
在 Transformer 模型中,位置编码是一个关键组件,它将关于标记位置的信息注入到输入嵌入中。
与循环神经网络(RNNs)或卷积神经网络(CNNs)不同,由于其置换不变性,Transformers 缺乏对标记位置的内在知识。位置编码通过为模型提供位置信息来解决这一限制,使其能够按照正确的顺序处理序列。
位置编码的概念
通常在将输入嵌入传入 Transformer 模型之前,会将位置编码添加到嵌入中。它由一组具有不同频率和相位的正弦函数组成,允许模型根据它们在序列中的位置区分标记。
位置编码的公式如下
假设您有一个长度为 L 的输入序列,并且需要在该序列中找到第 k 个对象的位置。位置编码由不同频率的正弦和余弦函数给出:
$$PE_{(pos, 2i)} = \sin(pos / 10000^{2i/d_{model}})$$ $$PE_{(pos, 2i+1)} = \cos(pos / 10000^{2i/d_{model}})$$
其中:
不同的位置编码方案
在 Transformer 中使用了各种位置编码方案,每种方案都有其优点和缺点:
位置编码的实现
让我们用 Python 实现位置编码:
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=5000):
super(PositionalEncoding, self).__init__()
pe = 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))
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
pe = pe.unsqueeze(0)
self.register_buffer('pe', pe)
def forward(self, x):
return x + self.pe[:, :x.size(1)]
2、多头注意力机制
在 Transformer 架构中,多头注意力机制是一个关键组件,它使模型能够同时关注输入序列的不同部分。它允许模型捕捉序列内的复杂依赖关系和关联,从而提高了语言翻译、文本生成和情感分析等任务的性能。
多头注意力的重要性
多头注意力机制具有几个优点:
多头注意力的计算:
让我们分解计算多头注意力所涉及的步骤:
用代码实现
让我们将理论转化为代码:
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, num_heads):
super(MultiHeadAttention, self).__init__()
self.num_heads = num_heads
self.d_model = d_model
self.d_k = d_model // num_heads
self.w_q = nn.Linear(d_model, d_model)
self.w_k = nn.Linear(d_model, d_model)
self.w_v = nn.Linear(d_model, d_model)
self.w_o = nn.Linear(d_model, d_model)
def attention(self, q, k, v, mask=None):
scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.d_k)
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9)
scores = F.softmax(scores, dim=-1)
output = torch.matmul(scores, v)
return output
def forward(self, q, k, v, mask=None):
batch_size = q.size(0)
q = self.w_q(q).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
k = self.w_k(k).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
v = self.w_v(v).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
out = self.attention(q, k, v, mask)
out = out.transpose(1, 2).contiguous().view(batch_size, -1, self.d_model)
return self.w_o(out)
3、前馈网络
在 Transformer 的背景下,前馈网络在处理信息和从输入序列中提取特征方面发挥着关键作用。它们是模型的支柱,促进了不同层之间表示的转换。
前馈网络的作用
每个 Transformer 层内的前馈网络负责对输入表示应用非线性变换。它使模型能够捕捉数据中的复杂模式和关系,促进了高级特征的学习。
前馈层的结构和功能
前馈层由两个线性变换组成,两者之间通过一个非线性激活函数(通常是 ReLU)分隔。让我们来解析一下结构和功能:
用代码实现
让我们在 Python 中实现前馈网络:
class FeedForwardNetwork(nn.Module):
def __init__(self, d_model, d_ff, dropout=0.1):
super(FeedForwardNetwork, self).__init__()
self.linear1 = nn.Linear(d_model, d_ff)
self.dropout = nn.Dropout(dropout)
self.linear2 = nn.Linear(d_ff, d_model)
def forward(self, x):
return self.linear2(self.dropout(F.relu(self.linear1(x))))
4、编码器
在 Transformer 模型中起着至关重要的作用,其主要任务是将输入序列转换为有意义的表示,捕捉输入的重要信息。
每个编码器层的结构和功能
编码器由多个层组成,每个层依次包含以下组件:输入嵌入、位置编码、多头自注意力机制和位置逐点前馈网络。
代码实现
让我们来看一下用 Python 实现带有输入嵌入和位置编码的编码器层的代码:
class EncoderLayer(nn.Module):
def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
super(EncoderLayer, self).__init__()
self.attention = MultiHeadAttention(d_model, num_heads)
self.feed_forward = FeedForwardNetwork(d_model, d_ff, dropout)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask=None):
x = self.norm1(x + self.dropout(self.attention(x, x, x, mask)))
x = self.norm2(x + self.dropout(self.feed_forward(x)))
return x
5、解码器
在 Transformer 模型中,解码器在基于输入序列的编码表示生成输出序列方面起着至关重要的作用。它接收来自编码器的编码输入序列,并将其用于生成最终的输出序列。
解码器的功能
解码器的主要功能是生成输出序列,同时注意到输入序列的相关部分和先前生成的标记。它利用输入序列的编码表示来理解上下文,并对生成下一个标记做出明智的决策。
解码器层及其组件
解码器层包括以下组件:
使用代码实现
class DecoderLayer(nn.Module):
def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
super(DecoderLayer, self).__init__()
self.attention = MultiHeadAttention(d_model, num_heads)
self.cross_attention = MultiHeadAttention(d_model, num_heads)
self.feed_forward = FeedForwardNetwork(d_model, d_ff, dropout)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.norm3 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x, enc_output, src_mask, tgt_mask=None):
x = self.norm1(x + self.dropout(self.attention(x, x, x, tgt_mask)))
x = self.norm2(x + self.dropout(self.cross_attention(x, enc_output, enc_output, src_mask)))
x = self.norm3(x + self.dropout(self.feed_forward(x)))
return x
前几节讨论的各种组件的综合体。让我们将编码器、解码器、注意力机制、位置编码和前馈网络的知识汇集起来,以了解完整的 Transformer 模型是如何构建和运作的。
Transformer 模型概述
在其核心,Transformer 模型由编码器和解码器模块堆叠在一起,用于处理输入序列并生成输出序列。以下是架构的高级概述:
编码器
解码器
连接和标准化
完整的 Transformer 模型通过将多个编码器和解码器层堆叠在一起来构建。每个层独立处理输入序列,使模型能够学习分层表示并捕获数据中的复杂模式。编码器将其输出传递给解码器,后者根据输入生成最终的输出序列。
Transformer 模型的实现
让我们在 Python 中实现完整的 Transformer 模型:
import torch.nn as nn
import torch.optim as optim
import math
import torch.nn.functional as F
class Transformer(nn.Module):
def __init__(self, src_vocab_size, tgt_vocab_size, d_model, num_heads, num_layers, d_ff,
max_len, dropout):
super(Transformer, self).__init__()
# 定义编码器和解码器的词嵌入层
self.encoder_embedding = nn.Embedding(src_vocab_size, d_model)
self.decoder_embedding = nn.Embedding(tgt_vocab_size, d_model)
# 定义位置编码层
self.positional_encoding = PositionalEncoding(d_model, max_len)
# 定义编码器和解码器的多层堆叠
self.encoder_layers = nn.ModuleList([EncoderLayer(d_model, num_heads, d_ff, dropout)
for _ in range(num_layers)])
self.decoder_layers = nn.ModuleList([DecoderLayer(d_model, num_heads, d_ff, dropout)
for _ in range(num_layers)])
# 定义线性层
self.linear = nn.Linear(d_model, tgt_vocab_size)
self.dropout = nn.Dropout(dropout)
# 生成掩码
def generate_mask(self, src, tgt):
src_mask = (src != 0).unsqueeze(1).unsqueeze(2)
tgt_mask = (tgt != 0).unsqueeze(1).unsqueeze(3)
seq_length = tgt.size(1)
nopeak_mask = (1 - torch.triu(torch.ones(1, seq_length, seq_length), diagonal=1)).bool()
tgt_mask = tgt_mask & nopeak_mask
return src_mask, tgt_mask
# 前向传播
def forward(self, src, tgt):
src_mask, tgt_mask = self.generate_mask(src, tgt)
# 编码器输入的词嵌入和位置编码
encoder_embedding = self.encoder_embedding(src)
en_positional_encoding = self.positional_encoding(encoder_embedding)
src_embedded = self.dropout(en_positional_encoding)
# 解码器输入的词嵌入和位置编码
decoder_embedding = self.decoder_embedding(tgt)
de_positional_encoding = self.positional_encoding(decoder_embedding)
tgt_embedded = self.dropout(de_positional_encoding)
enc_output = src_embedded
for enc_layer in self.encoder_layers:
enc_output = enc_layer(enc_output, src_mask)
dec_output = tgt_embedded
for dec_layer in self.decoder_layers:
dec_output = dec_layer(dec_output, enc_output, src_mask, tgt_mask)
output = self.linear(dec_output)
return output
# 示例用法
src_vocab_size = 5000
tgt_vocab_size = 5000
d_model = 512
num_heads = 8
num_layers = 6
d_ff = 2048
max_len = 100
dropout = 0.1
transformer = Transformer(src_vocab_size, tgt_vocab_size, d_model, num_heads, num_layers,
d_ff, max_len, dropout)
# 生成随机示例数据
src_data = torch.randint(1, src_vocab_size, (5, max_len)) # (batch_size, seq_length)
tgt_data = torch.randint(1, tgt_vocab_size, (5, max_len)) # (batch_size, seq_length)
transformer(src_data, tgt_data[:, :-1]).shape
训练 Transformer 模型涉及优化其参数以最小化损失函数,通常使用梯度下降和反向传播。一旦训练完成,就会使用各种指标评估模型的性能,以评估其解决目标任务的有效性。
训练过程
梯度下降和反向传播:
学习率调度:
评估指标
困惑度:
BLEU 分数:
让我们使用 PyTorch 对 Transformer 模型进行训练和评估的基本代码实现:
# Transformer 模型的训练和评估
criterion = nn.CrossEntropyLoss(ignore_index=0)
optimizer = optim.Adam(transformer.parameters(), lr=0.0001, betas=(0.9, 0.98), eps=1e-9)
# 训练循环
transformer.train()
for epoch in range(10):
optimizer.zero_grad()
output = transformer(src_data, tgt_data[:, :-1])
loss = criterion(output.contiguous().view(-1, tgt_vocab_size), tgt_data[:, 1:]
.contiguous().view(-1))
loss.backward()
optimizer.step()
print(f"第 {epoch+1} 轮:损失= {loss.item():.4f}")
# 虚拟数据
src_data = torch.randint(1, src_vocab_size, (5, max_len)) # (batch_size, seq_length)
tgt_data = torch.randint(1, tgt_vocab_size, (5, max_len)) # (batch_size, seq_length)
# 评估循环
transformer.eval()
with torch.no_grad():
output = transformer(src_data, tgt_data[:, :-1])
loss = criterion(output.contiguous().view(-1, tgt_vocab_size), tgt_data[:, 1:]
.contiguous().view(-1))
print(f"\n虚拟数据的评估损失= {loss.item():.4f}")
Transformers 在自然语言处理(NLP)领域引发了大量先进概念和应用。让我们深入探讨其中一些主题,包括不同的注意力变体、BERT(来自 Transformers 的双向编码器表示)和 GPT(生成式预训练 Transformer),以及它们的实际应用。
不同的注意力变体
注意力机制是 Transformer 模型的核心,使其能够专注于输入序列的相关部分。各种注意力变体的提议旨在增强 Transformer 的能力。
BERT(来自 Transformers 的双向编码器表示)
BERT 是一个具有里程碑意义的基于 Transformer 的模型,在 NLP 领域产生了深远影响。它通过掩码语言建模和下一句预测等目标,在大规模文本语料库上进行预训练。BERT 学习了单词的深层上下文表示,捕捉双向上下文,使其在广泛的下游 NLP 任务中表现良好。
GPT(生成式预训练 Transformer)
GPT 是一个基于 Transformer 的模型,以其生成能力而闻名。与双向的 BERT 不同,GPT 采用仅解码器的架构和自回归训练来生成连贯且上下文相关的文本。研究人员和开发人员已经成功地将 GPT 应用于各种任务,如文本完成、摘要、对话生成等。
Transformer 通过其捕捉上下文和理解语言的能力,彻底改变了自然语言处理(NLP)领域。
通过注意力机制、编码器 - 解码器架构和多头注意力,它们使得诸如机器翻译和情感分析等任务得以在前所未有的规模上实现。随着我们继续探索诸如 BERT 和 GPT 等模型,很明显,Transformer 处于语言理解和生成的前沿。
它们对 NLP 的影响深远,而与 Transformer 一起的发现之旅将揭示出该领域更多令人瞩目的进展。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online