什么是 Transformer?

Transformer 是由 Google 在 2017 年发表的论文《Attention is All You Need》中提出的深度学习模型架构。它彻底改变了自然语言处理(NLP)领域,成为了现代大语言模型(如 GPT、BERT、T5 等)的基础架构。

核心特点

  • 完全基于注意力机制:不需要循环(RNN)或卷积(CNN)结构
  • 并行计算能力强:可以同时处理所有位置的输入
  • 长距离依赖建模优秀:能够直接捕捉序列中任意两个位置之间的关系
  • 训练效率高:比 RNN 训练速度快得多

为什么需要 Transformer?

传统模型的局限

在 Transformer 出现之前,主流的序列模型主要使用 RNN(循环神经网络)和 LSTM:

RNN 的问题:

  • ❌ 无法并行计算,训练速度慢
  • ❌ 难以处理长距离依赖关系
  • ❌ 梯度消失/爆炸问题

LSTM 的改进:

  • ✅ 一定程度上解决了长距离依赖问题
  • ❌ 但仍然是串行处理,无法并行化
  • ❌ 计算复杂度仍然较高

Transformer 的优势

Transformer 通过自注意力机制(Self-Attention)解决了这些问题:

  • ✅ 并行化:所有位置可以同时计算
  • ✅ 长距离依赖:直接计算任意两个位置的关系
  • ✅ 效率高:训练和推理速度大幅提升

Transformer 整体架构

Transformer 由两部分组成:编码器(Encoder) 和 解码器(Decoder)。

架构概览

输出处理
解码器 Decoder × N
编码器 Encoder × N
输入处理
是
否
是
否
线性层 Linear
Softmax
输出概率分布
掩码多头自注意力 Masked Multi-Head Self-Attention
目标序列嵌入
残差连接 & 层归一化
交叉注意力 Cross-Attention
残差连接 & 层归一化
前馈神经网络 Feed Forward
残差连接 & 层归一化
更多层?
解码器输出
多头自注意力 Multi-Head Self-Attention
残差连接 & 层归一化
前馈神经网络 Feed Forward
残差连接 & 层归一化
更多层?
编码器输出
Tokenization 分词
输入文本
输入嵌入 Input Embedding
位置编码 Positional Encoding

架构特点

  1. 编码器:理解输入序列的含义
  2. 解码器:基于编码器的信息生成输出序列
  3. 堆叠结构:编码器和解码器都可以堆叠多层(通常 6-12 层)

核心组件详解

1. 输入嵌入(Input Embedding)

作用:将输入的词汇(token)转换为数值向量。

处理过程:

# 伪代码示例
vocab_size = 50000  # 词汇表大小
embedding_dim = 512  # 嵌入维度

# 每个词汇被映射为一个 512 维的向量
embedding_matrix = [
    [0.1, 0.3, -0.2, ..., 0.5],  # "我" 的向量
    [0.2, -0.1, 0.4, ..., 0.3],  # "爱" 的向量
    ...
]

示例:

输入文本:"我爱学习AI"
分词结果:["我", "爱", "学习", "AI"]

每个词 → 512维向量
"我"   → [0.1, 0.3, -0.2, ..., 0.5]
"爱"   → [0.2, -0.1, 0.4, ..., 0.3]
"学习" → [-0.1, 0.5, 0.2, ..., -0.3]
"AI"   → [0.4, -0.2, 0.3, ..., 0.1]

2. 位置编码(Positional Encoding)

为什么需要位置编码?

Transformer 没有循环结构,无法自然地理解词汇的顺序。位置编码告诉模型每个词在序列中的位置。

编码方式:

Transformer 使用正弦和余弦函数来编码位置:

# 位置编码公式
PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))

# 其中:
# pos: 位置(0, 1, 2, ...)
# i: 维度索引
# d_model: 模型维度(通常是 512)

可视化理解:

输入序列
我 爱 学习 AI
位置 0
位置 1
位置 2
位置 3
位置编码
PE(0): [sin, cos, sin, cos, ...]
PE(1): [sin, cos, sin, cos, ...]
PE(2): [sin, cos, sin, cos, ...]
PE(3): [sin, cos, sin, cos, ...]
输入嵌入 + 位置编码
最终输入向量

关键点:

  • 位置编码与输入嵌入相加(不是拼接)
  • 每个位置都有唯一的编码
  • 模型可以学习位置之间的相对距离

3. 多头自注意力机制(Multi-Head Self-Attention)

这是 Transformer 的核心创新!

3.1 什么是注意力?

注意力机制让模型能够"关注"输入序列中不同位置的信息。

形象理解:
想象你在阅读一句话"我喜欢吃苹果",当看到"吃"这个词时,你的注意力会集中在"苹果"上,而不是"我"或"喜欢"。

3.2 注意力机制的计算过程

步骤 1:生成 Q、K、V 矩阵

# 伪代码
输入序列 X: [batch_size, seq_len, d_model]  # 例如:[1, 4, 512]

# 通过线性变换生成 Q、K、V
Q = X @ W_q  # Query(查询)
K = X @ W_k  # Key(键)
V = X @ W_v  # Value(值)

# Q, K, V 的形状都是 [batch_size, seq_len, d_model]

步骤 2:计算注意力分数

# 计算注意力分数
scores = Q @ K.T / sqrt(d_k)  # 缩放点积注意力

# 其中 d_k 是 K 的维度,sqrt(d_k) 用于缩放,防止分数过大

步骤 3:应用 Softmax

# 对每一行应用 Softmax,使分数变成概率分布
attention_weights = softmax(scores, dim=-1)
# 每行的和 = 1

步骤 4:加权求和

# 用注意力权重对 V 进行加权求和
output = attention_weights @ V
3.3 完整的注意力计算流程

在这里插入图片描述

3.4 多头注意力(Multi-Head Attention)

为什么需要多头?

单一注意力只能关注一种关系,多头注意力可以让模型同时关注多种不同的关系。

计算过程:

# 假设有 8 个头(heads)
num_heads = 8
d_model = 512
d_k = d_model // num_heads  # 64

# 对每个头分别计算
for head in range(num_heads):
    Q_head = Q[:, :, head*d_k:(head+1)*d_k]
    K_head = K[:, :, head*d_k:(head+1)*d_k]
    V_head = V[:, :, head*d_k:(head+1)*d_k]
    
    # 计算该头的注意力
    attention_head = Attention(Q_head, K_head, V_head)

# 拼接所有头的结果
multi_head_output = Concat([attention_head_1, ..., attention_head_8])

# 通过线性层输出
output = multi_head_output @ W_o

可视化理解:

输入 X
生成 Q, K, V
头 1: 关注语法关系
头 2: 关注语义关系
头 3: 关注局部依赖
头 4: 关注全局依赖
...
头 8: 其他关系
拼接所有头
线性变换 W_o
多头注意力输出

4. 前馈神经网络(Feed Forward Network)

作用:对每个位置的特征进行非线性变换。

结构:

FFN(x) = max(0, x @ W1 + b1) @ W2 + b2

# 通常的结构:
# 第一层:线性变换 + ReLU 激活
# 第二层:线性变换(无激活函数)

参数设置:

  • 第一层维度通常是 4 * d_model(例如 512 → 2048)
  • 第二层维度是 d_model(例如 2048 → 512)

可视化:

在这里插入图片描述

5. 残差连接和层归一化

残差连接(Residual Connection):

output = LayerNorm(x + Sublayer(x))

作用:

  • 帮助梯度流动,防止梯度消失
  • 允许模型学习残差(差值),更容易训练

层归一化(Layer Normalization):

  • 对每个样本的特征进行归一化
  • 稳定训练过程

可视化:

输入 x
多头注意力层
输出
残差连接 +
层归一化
输出

6. 编码器完整结构

是
否
输入嵌入 + 位置编码
多头自注意力
残差连接 + 层归一化
前馈神经网络
残差连接 + 层归一化
还有更多层?
多头自注意力
残差连接 + 层归一化
前馈神经网络
残差连接 + 层归一化
编码器输出

7. 解码器结构

解码器比编码器多了一个**交叉注意力(Cross-Attention)**层。

三个子层:

  1. 掩码多头自注意力:关注已生成的部分
  2. 交叉注意力:关注编码器的输出
  3. 前馈神经网络:特征变换

掩码(Mask)的作用:

  • 防止模型在生成时"看到"未来的词汇
  • 保证自回归生成的正确性

解码器结构:

是
否
目标序列嵌入 + 位置编码
掩码多头自注意力
残差连接 + 层归一化
交叉注意力
编码器输出
残差连接 + 层归一化
前馈神经网络
残差连接 + 层归一化
还有更多层?
掩码多头自注意力
残差连接 + 层归一化
交叉注意力
残差连接 + 层归一化
前馈神经网络
残差连接 + 层归一化
解码器输出

完整执行流程

现在让我们详细追踪一个提示词从输入到输出的完整过程。

场景示例

假设我们有一个翻译任务:

  • 输入(英文):“I love learning AI”
  • 输出(中文):“我喜欢学习AI”

阶段 1:输入处理

输入文本
I love learning AI
分词 Tokenization
[I, love, learning, AI]
词汇ID映射
[101, 234, 567, 890]
输入嵌入
每个词 → 512维向量
位置编码
PE(0), PE(1), PE(2), PE(3)
最终输入
嵌入 + 位置编码
[4, 512]

详细步骤:

  1. 文本输入:"I love learning AI"

  2. 分词处理:将文本分割成 tokens

    ["I", "love", "learning", "AI"]
    
  3. 词汇表映射:每个词映射到唯一ID

    [101, 234, 567, 890]
    
  4. 嵌入查找:从嵌入矩阵中查找对应的向量

    I:      [0.1, 0.3, -0.2, ..., 0.5]  (512维)
    love:   [0.2, -0.1, 0.4, ..., 0.3]  (512维)
    learning: [-0.1, 0.5, 0.2, ..., -0.3]  (512维)
    AI:     [0.4, -0.2, 0.3, ..., 0.1]  (512维)
    
  5. 位置编码:为每个位置添加位置信息

    位置 0: [sin(0/10000^0), cos(0/10000^0), ...]
    位置 1: [sin(1/10000^0), cos(1/10000^0), ...]
    位置 2: [sin(2/10000^0), cos(2/10000^0), ...]
    位置 3: [sin(3/10000^0), cos(3/10000^0), ...]
    
  6. 相加:嵌入向量 + 位置编码 = 最终输入

    最终输入形状: [4, 512]  # 4个词,每个512维
    

阶段 2:编码器处理

在这里插入图片描述

编码器第 1 层的详细过程:

步骤 1:多头自注意力
# 输入: [batch=1, seq_len=4, d_model=512]

# 1. 生成 Q, K, V
Q = input @ W_q  # [1, 4, 512]
K = input @ W_k  # [1, 4, 512]
V = input @ W_v  # [1, 4, 512]

# 2. 分割成 8 个头
Q = reshape(Q, [1, 4, 8, 64])  # 8个头,每个头64维
K = reshape(K, [1, 4, 8, 64])
V = reshape(V, [1, 4, 8, 64])

# 3. 对每个头计算注意力
for head in range(8):
    scores = Q[:, :, head] @ K[:, :, head].T / sqrt(64)
    # scores: [1, 4, 4] - 每个词对其他词的注意力分数
    
    attention_weights = softmax(scores, dim=-1)
    # 例如,对于 "I" 这个词:
    # attention_weights[0] = [0.7, 0.2, 0.08, 0.02]
    # 表示 "I" 对 "I" 自己关注度最高,其次是 "love"
    
    head_output = attention_weights @ V[:, :, head]

# 4. 拼接所有头
multi_head_output = concat([head_0, ..., head_7])  # [1, 4, 512]

# 5. 输出投影
attention_output = multi_head_output @ W_o  # [1, 4, 512]

注意力权重示例(实际值会更复杂):

         I      love   learning  AI
I      [0.70   0.20   0.08     0.02]
love   [0.15   0.65   0.15     0.05]
learning [0.10  0.15   0.60     0.15]
AI     [0.05   0.10   0.15     0.70]
步骤 2:残差连接和层归一化
# 残差连接
residual_output = input + attention_output  # [1, 4, 512]

# 层归一化
normalized_output = LayerNorm(residual_output)  # [1, 4, 512]
步骤 3:前馈神经网络
# 第一层:扩展到 2048 维
ffn_intermediate = normalized_output @ W1 + b1  # [1, 4, 2048]
ffn_intermediate = ReLU(ffn_intermediate)  # 激活函数

# 第二层:压缩回 512 维
ffn_output = ffn_intermediate @ W2 + b2  # [1, 4, 512]
步骤 4:再次残差连接和层归一化
# 残差连接
residual_output = normalized_output + ffn_output  # [1, 4, 512]

# 层归一化
layer_output = LayerNorm(residual_output)  # [1, 4, 512]

重复 6 次:上述过程会在编码器中重复 6 次(假设有 6 层编码器)。

编码器输出:

  • 形状:[1, 4, 512]
  • 每个位置的向量都包含了整个输入序列的信息
  • 这些向量会被传递给解码器

阶段 3:解码器处理(生成阶段)

解码器是自回归的,即逐个生成输出词。

3.1 初始状态
开始标记
<START>
嵌入 + 位置编码
[1, 512]
解码器输入
3.2 第一个词的生成

步骤 1:掩码多头自注意力

# 输入: [<START>]  # 只有开始标记

# 生成 Q, K, V
Q = decoder_input @ W_q
K = decoder_input @ W_k
V = decoder_input @ W_v

# 掩码:防止看到未来的词(虽然现在只有一个词,但这是为了训练时的正确性)
mask = [[True]]  # 只允许看到当前位置及之前的位置

# 计算注意力(应用掩码)
scores = Q @ K.T / sqrt(d_k)
scores = masked_fill(scores, mask, -inf)  # 掩码位置设为负无穷
attention_weights = softmax(scores)
masked_attention_output = attention_weights @ V

步骤 2:交叉注意力

# Query 来自解码器,Key 和 Value 来自编码器输出
Q = masked_attention_output @ W_q  # 来自解码器
K = encoder_output @ W_k  # 来自编码器 [1, 4, 512]
V = encoder_output @ W_v  # 来自编码器 [1, 4, 512]

# 计算交叉注意力
scores = Q @ K.T / sqrt(d_k)
attention_weights = softmax(scores)
# 此时解码器会"关注"编码器中的哪些词?
# 例如:"我" 可能对 "I" 和 "love" 的注意力较高

cross_attention_output = attention_weights @ V

步骤 3:前馈神经网络

# 与前文相同的前馈网络处理
ffn_output = FeedForward(cross_attention_output)

步骤 4:输出层

# 线性层将 512 维映射到词汇表大小(例如 50000)
logits = decoder_output @ W_output  # [1, 1, 50000]

# Softmax 得到概率分布
probabilities = softmax(logits)  # [1, 1, 50000]

# 例如输出:
# "我": 0.35
# "喜欢": 0.15
# "AI": 0.05
# ... 其他词的概率

步骤 5:选择第一个词

# 方法1:贪心搜索(选择概率最高的词)
first_token = argmax(probabilities)  # "我"

# 方法2:采样(根据概率分布随机采样)
first_token = sample(probabilities)  # 可能是 "我"
3.3 第二个词的生成
已有序列
<START> 我
掩码自注意力
只关注 <START> 和 我
交叉注意力
关注编码器输出
前馈网络
输出层
概率分布
选择'喜欢'

关键点:

  • 掩码确保在生成"喜欢"时,只能看到"“和"我”
  • 交叉注意力让"喜欢"能够关注到编码器中的相关信息(如 “love”)
3.4 继续生成

重复上述过程,直到生成结束标记 <END>:

<START> → 我 → 喜欢 → 学习 → AI → <END>

完整流程可视化

输入文本嵌入层编码器解码器输出层"I love learning AI"Tokenization + 嵌入 + 位置编码[4, 512] 输入向量多头自注意力前馈网络loop[6层编码器-]编码器输出 [4, 512]开始标记 <START>掩码自注意力交叉注意力(关注编码器)前馈网络解码器输出线性层 + Softmax生成词(如"我")将新词加入序列loop[每个输出词]完整输出 "我喜欢学习AI"输入文本嵌入层编码器解码器输出层

关键技术细节

1. 缩放点积注意力公式

完整的注意力计算公式:

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

为什么除以 sqrt(d_k)?

  • 防止点积值过大导致 Softmax 梯度消失
  • 保持梯度的稳定性

2. 位置编码的数学原理

位置编码使用不同频率的正弦和余弦函数:

PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))

优势:

  • 可以表示相对位置关系
  • 对于训练时未见过的位置,也能外推

3. 掩码机制详解

填充掩码(Padding Mask)
# 当序列长度不一致时,使用填充
# 例如:[I, love, learning, AI, <PAD>, <PAD>]
# 掩码:[True, True, True, True, False, False]

mask = (input_ids != PAD_TOKEN_ID)
attention_mask = mask.unsqueeze(1).unsqueeze(2)
因果掩码(Causal Mask)
# 下三角矩阵,防止看到未来信息
# [True,  False, False, False]
# [True,  True,  False, False]
# [True,  True,  True,  False]
# [True,  True,  True,  True]

causal_mask = torch.tril(torch.ones(seq_len, seq_len))

4. 训练 vs 推理的区别

特性训练阶段推理阶段
输入方式并行:一次性输入完整目标序列串行:逐个生成词
掩码使用因果掩码使用因果掩码
效率高效(并行计算)相对较慢(串行生成)

训练时的并行化:

# 训练时,解码器一次性看到完整目标序列
target = ["<START>", "我", "喜欢", "学习", "AI"]
# 通过掩码确保每个位置只能看到之前的位置

推理时的自回归:

# 推理时,逐个生成
step1: "<START>" → "我"
step2: "<START> 我" → "喜欢"
step3: "<START> 我 喜欢" → "学习"
...

实际应用示例

示例 1:文本翻译

输入:"Hello, how are you?"
输出:"你好,你好吗?"

执行步骤:

  1. 编码器理解英文句子的含义
  2. 解码器基于编码器的信息,逐个生成中文词
  3. 交叉注意力确保生成的中文词与对应的英文词对齐

示例 2:文本摘要

输入:一篇长文章
输出:摘要

执行步骤:

  1. 编码器处理整篇文章,提取关键信息
  2. 解码器生成简洁的摘要
  3. 注意力权重可以可视化,显示摘要中每个词关注原文的哪些部分

示例 3:问答系统

输入:问题 + 上下文
输出:答案

执行步骤:

  1. 编码器同时处理问题和上下文
  2. 解码器生成答案
  3. 注意力机制帮助模型找到相关的上下文信息

代码实现示例(简化版)

以下是一个简化的 Transformer 实现,帮助理解核心概念:

import torch
import torch.nn as nn
import math

class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, num_heads):
        super().__init__()
        self.d_model = d_model
        self.num_heads = num_heads
        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 forward(self, Q, K, V, mask=None):
        batch_size = Q.size(0)
        
        # 1. 线性变换
        Q = self.W_q(Q)
        K = self.W_k(K)
        V = self.W_v(V)
        
        # 2. 分割成多头
        Q = Q.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        K = K.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        V = V.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        
        # 3. 计算注意力
        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)
        
        attention_weights = torch.softmax(scores, dim=-1)
        attention_output = torch.matmul(attention_weights, V)
        
        # 4. 拼接多头
        attention_output = attention_output.transpose(1, 2).contiguous()
        attention_output = attention_output.view(batch_size, -1, self.d_model)
        
        # 5. 输出投影
        output = self.W_o(attention_output)
        
        return output, attention_weights

class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=5000):
        super().__init__()
        
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len).unsqueeze(1).float()
        
        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)
        self.register_buffer('pe', pe)
        
    def forward(self, x):
        return x + self.pe[:, :x.size(1)]

class TransformerEncoderLayer(nn.Module):
    def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
        super().__init__()
        self.self_attention = MultiHeadAttention(d_model, num_heads)
        self.feed_forward = nn.Sequential(
            nn.Linear(d_model, d_ff),
            nn.ReLU(),
            nn.Linear(d_ff, d_model)
        )
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.dropout = nn.Dropout(dropout)
        
    def forward(self, x, mask=None):
        # 多头自注意力 + 残差连接 + 层归一化
        attn_output, _ = self.self_attention(x, x, x, mask)
        x = self.norm1(x + self.dropout(attn_output))
        
        # 前馈网络 + 残差连接 + 层归一化
        ff_output = self.feed_forward(x)
        x = self.norm2(x + self.dropout(ff_output))
        
        return x

class TransformerDecoderLayer(nn.Module):
    def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
        super().__init__()
        self.self_attention = MultiHeadAttention(d_model, num_heads)
        self.cross_attention = MultiHeadAttention(d_model, num_heads)
        self.feed_forward = nn.Sequential(
            nn.Linear(d_model, d_ff),
            nn.ReLU(),
            nn.Linear(d_ff, d_model)
        )
        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, encoder_output, src_mask=None, tgt_mask=None):
        # 掩码自注意力
        attn_output, _ = self.self_attention(x, x, x, tgt_mask)
        x = self.norm1(x + self.dropout(attn_output))
        
        # 交叉注意力
        cross_attn_output, _ = self.cross_attention(x, encoder_output, encoder_output, src_mask)
        x = self.norm2(x + self.dropout(cross_attn_output))
        
        # 前馈网络
        ff_output = self.feed_forward(x)
        x = self.norm3(x + self.dropout(ff_output))
        
        return x

class Transformer(nn.Module):
    def __init__(self, src_vocab_size, tgt_vocab_size, d_model=512, num_heads=8,
                 num_encoder_layers=6, num_decoder_layers=6, d_ff=2048, max_len=5000, dropout=0.1):
        super().__init__()
        
        # 嵌入层
        self.src_embedding = nn.Embedding(src_vocab_size, d_model)
        self.tgt_embedding = nn.Embedding(tgt_vocab_size, d_model)
        self.pos_encoding = PositionalEncoding(d_model, max_len)
        
        # 编码器
        self.encoder_layers = nn.ModuleList([
            TransformerEncoderLayer(d_model, num_heads, d_ff, dropout)
            for _ in range(num_encoder_layers)
        ])
        
        # 解码器
        self.decoder_layers = nn.ModuleList([
            TransformerDecoderLayer(d_model, num_heads, d_ff, dropout)
            for _ in range(num_decoder_layers)
        ])
        
        # 输出层
        self.output_projection = nn.Linear(d_model, tgt_vocab_size)
        self.dropout = nn.Dropout(dropout)
        
    def forward(self, src, tgt, src_mask=None, tgt_mask=None):
        # 编码器
        src_emb = self.pos_encoding(self.src_embedding(src) * math.sqrt(self.d_model))
        src_emb = self.dropout(src_emb)
        
        encoder_output = src_emb
        for layer in self.encoder_layers:
            encoder_output = layer(encoder_output, src_mask)
        
        # 解码器
        tgt_emb = self.pos_encoding(self.tgt_embedding(tgt) * math.sqrt(self.d_model))
        tgt_emb = self.dropout(tgt_emb)
        
        decoder_output = tgt_emb
        for layer in self.decoder_layers:
            decoder_output = layer(decoder_output, encoder_output, src_mask, tgt_mask)
        
        # 输出投影
        output = self.output_projection(decoder_output)
        
        return output

总结与思考

Transformer 的核心优势

  1. 并行化能力:训练时可以并行处理所有位置
  2. 长距离依赖:直接建模任意两个位置的关系
  3. 可解释性:注意力权重可以可视化,理解模型的关注点
  4. 通用性强:适用于多种 NLP 任务

关键创新点

  1. 自注意力机制:让模型自己决定关注哪些信息
  2. 位置编码:用数学方法编码位置信息,无需循环结构
  3. 多头注意力:同时关注多种不同的关系
  4. 残差连接:帮助深层网络训练

更多推荐