大语言模型微调及其应用的探索 跟踪前沿的技术
Google 2017年分布 Attention Is All You Need

主要致力于在序列建摸中提升并行性与长距离原来建模能力, 拜脱对循环与的依赖,
我来为你详细绘制Transformer的编码器和解码器流程图。
输入序列 → 编码器 → 解码器 → 输出序列
┌─────────────────────────────────────────────────────────┐
│ 编码器 (Encoder) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 输入序列 (Input Sequence) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 嵌入层 + 位置编码 │
│ (Embedding + Positional Encoding) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ N × 编码器层 │
│ (N × Encoder Layer) │
├─────────────────────────────────────────────────────────┤
│ 层 1: │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ 多头自注意力 │ → │ 前馈神经网络 │ │
│ │ (Multi-Head │ │ (Feed Forward) │ │
│ │ Self-Attention) │ └────────────────────┘ │
│ └────────────────────┘ ↑ │
│ ↑ │ 残差连接 │
│ │ 残差连接 │ + 层归一化 │
│ │ + 层归一化 │ │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 编码器输出 │
│ (Encoder Output) │
│ 包含输入序列的上下文表示 │
└─────────────────────────────────────────────────────────┘
编码器详细步骤:
输入 → 线性变换 → 分成多个头 → 计算注意力 → 拼接 → 线性变换
┌─────────────────────────────────────────────────────────┐
│ 解码器 (Decoder) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 目标序列 (Target Sequence) │
│ (右移一位,用于训练) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 嵌入层 + 位置编码 │
│ (Embedding + Positional Encoding) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ N × 解码器层 │
│ (N × Decoder Layer) │
├─────────────────────────────────────────────────────────┤
│ 层 1: │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ 掩码多头自注意力 │ → │ 编码器-解码器 │ → │
│ │ (Masked Multi-Head │ │ 注意力 │ │
│ │ Self-Attention) │ │ (Encoder-Decoder │ │
│ └────────────────────┘ │ Attention) │ │
│ ↑ └────────────────────┘ │
│ │ 残差连接 ↑ │
│ │ + 层归一化 │ 残差连接 │
│ │ │ + 层归一化 │
├─────────────────────────────────────────────────────────┤
│ ↓ │
│ ┌────────────────────┐ │
│ │ 前馈神经网络 │ │
│ │ (Feed Forward) │ │
│ └────────────────────┘ │
│ ↑ │
│ │ 残差连接 │
│ │ + 层归一化 │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 线性层 + Softmax │
│ (Linear + Softmax) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 输出概率分布 │
│ (Output Probability) │
└─────────────────────────────────────────────────────────┘
解码器详细步骤:
┌─────────────┐ 编码器输出 ┌─────────────┐
│ 编码器 │─────────────────→│ 解码器 │
│ │ 作为K和V │ │
└─────────────┘ └─────────────┘
↓
┌─────────────┐
│ 输出序列 │
└─────────────┘
多头注意力机制流程:
输入 → 线性投影 → 分割成h个头 → 每个头:
Q, K, V = 线性变换
注意力分数 = softmax(Q·Kᵀ/√dₖ) · V
→ 拼接所有头 → 线性投影 → 输出
训练 vs 推理流程对比:
训练(Teacher Forcing):
输入序列 → 编码器 → 解码器(并行处理整个目标序列)
↓
计算损失 ← 与真实标签比较
推理(自回归):
输入序列 → 编码器 → 解码器(逐个生成token)
↓
生成token₁ → 加入输入 → 生成token₂ → ...
这个架构使得Transformer能够处理序列到序列的任务,如机器翻译、文本摘要等,同时具有高度并行化的优势。
:编码器多头注意力、**交叉注意力**、解码器多头自注意力(含因果掩码)
:形成标准层块,稳定深层训练
位置编码格式
${PE_{(pos, 2i)} = sin(\frac{pos}{10000^{\frac{2i}{d}}}) }$
${PE_{(pos, 2i+1)}}$ = ${cos(\frac{pos}{10000^{\frac{2i}{d}}})}$
也就是是,对于输入的每个token(列如第5个词),我们遍历维度, 对每一个维度使用公式计算出其数值, 为其生成一个长度为d_model的向量, 交替使用sin和cos来填充奇偶维度
为什么要用正函数?
通过
公式${10000^{\frac{2i}{d}}}$
</font> 这样的频率控制, sin/cos的周期从大到小, 可以编码不同粒度的顺序信息。 低纬捕捉长距离位置变化, 高维度捕捉局部变化
随着位置pos变化, 编码向量是连续的, 符合自然语言中位置是连续变量的特点
因为是数字函数, 位置可以扩展到比训练时更长的序列, 而不会像可学习位置编码那样固定在训练长度上
不需要额外学习参数, 占用显存小, 适合参数紧凑的场景

我们可以绘制前 100 个位置编码在不同维度上的曲线图:
import numpy as np
import matplotlib.pyplot as plt
def get_positional_encoding(seq_len, d_model):
pos = np.arange(seq_len)[:, np.newaxis]
i = np.arange(d_model)[np.newaxis, :]
angle_rates = 1 / np.power(10000, (2 * (i // 2)) / np.float32(d_model))
angle_rads = pos * angle_rates
# apply sin to even indices, cos to odd indices
pos_encoding = np.zeros_like(angle_rads)
pos_encoding[:, 0::2] = np.sin(angle_rads[:, 0::2])
pos_encoding[:, 1::2] = np.cos(angle_rads[:, 1::2])
return pos_encoding
# visualize
pe = get_positional_encoding(100, 16)
plt.figure(figsize=(12, 6))
plt.plot(pe[:, :8])
plt.legend([f"dim {i}" for i in range(8)])
plt.title("Positional Encoding (first 8 dimensions)")
plt.xlabel("Position")
plt.ylabel("Value")
plt.grid(True)
plt.show()