跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
PythonAI算法

注意力机制与 Transformer 模型实战指南

注意力机制解决了 RNN 处理长序列时的依赖捕捉难题,Transformer 凭借自注意力实现高效并行。深入解析 QKV 计算逻辑、多头注意力架构及位置编码原理,并通过 TensorFlow 完整演示英法机器翻译任务。内容涵盖数据预处理、模型搭建、训练策略及优化技巧,适合希望掌握深度学习核心架构的开发者参考。

SqlMaster发布于 2026/3/28更新于 2026/10/1988 浏览
注意力机制与 Transformer 模型实战指南

注意力机制与 Transformer 模型实战指南

核心思想:为什么需要注意力机制

传统的 RNN 和 LSTM 在处理长序列时,往往面临长距离依赖捕捉能力不足以及并行计算效率低下的问题。注意力机制(Attention Mechanism)的引入,有效解决了这两个核心痛点。

其本质是让模型学会'聚焦'——在处理序列数据时,自动分配不同的权重给输入序列中的各个元素,重点关注与当前任务相关的信息,弱化无关信息的干扰。例如在机器翻译中,生成英文单词时,模型会动态关注源句子中对应的中文词汇。

QKV 框架与基础计算

注意力机制的计算通常包含查询(Query)、键(Key)、值(Value)三个核心要素,简称 QKV 框架。流程可概括为三步:

  1. 计算 Query 和所有 Key 的相似度,得到注意力分数。
  2. 对分数进行归一化处理(常用 Softmax),得到注意力权重。
  3. 用归一化后的权重对 Value 进行加权求和,得到最终输出。

基础计算公式如下: $$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$$ 其中 $d_k$ 是 Key 的维度,除以 $\sqrt{d_k}$ 是为了防止内积结果过大导致 Softmax 函数饱和。

import tensorflow as tf
import numpy as np

# 实现基础注意力计算
def scaled_dot_product_attention(q, k, v, mask=None):
    # 计算 Q 和 K 的点积
    matmul_qk = tf.matmul(q, k, transpose_b=True)
    
    # 获取 k 的维度 dk
    dk = tf.cast(tf.shape(k)[-1], tf.float32)
    
    # 缩放点积
    scaled_attention_logits = matmul_qk / tf.math.sqrt(dk)
    
    # 应用掩码(可选)
    if mask is not None:
        scaled_attention_logits += (mask * -1e9)
    
    # 计算注意力权重
    attention_weights = tf.nn.softmax(scaled_attention_logits, axis=-1)
    
    # 计算最终输出
    output = tf.matmul(attention_weights, v)
    return output, attention_weights

# 模拟输入:批次大小=2,序列长度=3,特征维度=4
q = tf.random.normal((2, 3, 4))
k = tf.random.normal((2, 3, 4))
v = tf.random.normal((2, 3, 4))
output, attn_weights = scaled_dot_product_attention(q, k, v)
print("注意力输出形状:", output.shape)
print("注意力权重形状:", attn_weights.shape)

注意:掩码(Mask)分为两种,一种是填充掩码,用于屏蔽无效填充部分;另一种是前瞻掩码,用于自回归任务中防止模型看到未来信息。

自注意力与多头注意力

自注意力机制

自注意力(Self-Attention)是注意力机制的一种特殊形式。在此模式下,Query、Key、Value 三个矩阵都来自同一个输入序列。它能捕捉序列内部元素之间的依赖关系,比如在句子'它很有趣'中,将'它'与前面的名词关联起来。

计算步骤:

  1. 对输入序列每个元素,通过线性变换生成 Q、K、V 矩阵。
  2. 按基础公式计算注意力输出。
  3. 将输出作为当前层特征传递给下一层。

多头注意力机制

多头注意力(Multi-Head Attention)是 Transformer 的核心创新。它通过多个并行的注意力头,从不同角度捕捉序列特征。

流程:

  1. 将特征维度拆分为 h 个独立子空间。
  2. 对每个子空间分别计算自注意力。
  3. 拼接输出并通过线性变换得到最终结果。
class MultiHeadAttention(tf.keras.layers.Layer):
    def __init__(self, d_model, num_heads):
        super(MultiHeadAttention, self).__init__()
        self.num_heads = num_heads
        self.d_model = d_model
        assert d_model % self.num_heads == 0
        self.depth = d_model // self.num_heads
        
        # 定义 Q、K、V 和输出的线性变换层
        self.wq = tf.keras.layers.Dense(d_model)
        self.wk = tf.keras.layers.Dense(d_model)
        self.wv = tf.keras.layers.Dense(d_model)
        self.dense = tf.keras.layers.Dense(d_model)

    def split_heads(self, x, batch_size):
        # 将特征维度拆分为多个头
        x = tf.reshape(x, (batch_size, -1, self.num_heads, self.depth))
        return tf.transpose(x, perm=[0, 2, 1, 3])

    def call(self, v, k, q, mask):
        batch_size = tf.shape(q)[0]
        
        # 生成 Q、K、V 矩阵
        q = self.wq(q)
        k = self.wk(k)
        v = self.wv(v)
        
        # 拆分多头
        q = self.split_heads(q, batch_size)
        k = self.split_heads(k, batch_size)
        v = self.split_heads(v, batch_size)
        
        # 计算缩放点积注意力
        scaled_attention, attention_weights = scaled_dot_product_attention(q, k, v, mask)
        
        # 拼接多头输出
        scaled_attention = tf.transpose(scaled_attention, perm=[0, 2, 1, 3])
        concat_attention = tf.reshape(scaled_attention, (batch_size, -1, self.d_model))
        
        # 线性变换输出
        output = self.dense(concat_attention)
        return output, attention_weights

# 测试多头注意力层
mha = MultiHeadAttention(d_model=128, num_heads=8)
x = tf.random.normal((2, 5, 128))
output, attn_weights = mha(x, x, x, mask=None)
print("多头注意力输出形状:", output.shape)

Transformer 模型架构详解

Transformer 由 Google 团队在 2017 年提出,完全基于注意力机制,摒弃了 RNN 和 CNN 的序列式结构,实现了高度并行化计算。

编码器与解码器

整体架构分为编码器(Encoder)和解码器(Decoder)两大部分。

编码器由 N 个相同的编码层堆叠而成,每层包含两个子层:

  1. 多头自注意力层:捕捉输入序列内部依赖。
  2. 前馈神经网络层:非线性变换。 每个子层均配备残差连接和层归一化。

解码器同样由 N 个相同的解码层堆叠,每层包含三个子层:

  1. 掩码多头自注意力层:防止看到未来信息。
  2. 编码器 - 解码器注意力层:捕捉输入输出序列间依赖。
  3. 前馈神经网络层。

位置编码

由于 Transformer 没有循环结构,无法感知序列顺序,需通过位置编码注入位置信息。常用正弦余弦编码: $$PE_{(pos, 2i)} = \sin(pos / 10000^{2i/d_{model}})$$ $$PE_{(pos, 2i+1)} = \cos(pos / 10000^{2i/d_{model}})$$

def get_angles(pos, i, d_model):
    angles = 1 / np.power(10000, (2 * (i // 2)) / np.float32(d_model))
    return pos * angles

def positional_encoding(position, d_model):
    angle_rads = get_angles(np.arange(position)[:, np.newaxis], 
                            np.arange(d_model)[np.newaxis, :], d_model)
    # 对偶数索引使用 sin
    angle_rads[:, 0::2] = np.sin(angle_rads[:, 0::2])
    # 对奇数索引使用 cos
    angle_rads[:, 1::2] = np.cos(angle_rads[:, 1::2])
    pos_encoding = angle_rads[np.newaxis, ...]
    return tf.cast(pos_encoding, dtype=tf.float32)

# 生成位置编码
pos_enc = positional_encoding(100, 128)
print("位置编码形状:", pos_enc.shape)

实战:基于 Transformer 的机器翻译

本次实战任务是英 - 法机器翻译。我们将搭建 Transformer 模型,实现英文到法文的自动翻译。

数据预处理

加载双语数据集,进行分词、建立词汇表、转换为整数索引序列。统一序列长度,截断过长或填充过短序列。

import tensorflow_datasets as tfds

# 加载英法翻译数据集
dataset, info = tfds.load('ted_hrlr_translate/fr_to_en', with_info=True, as_supervised=True)
train_examples, val_examples = dataset['train'], dataset['validation']

# 构建分词器
tokenizer_en = tfds.deprecated.text.SubwordTextEncoder.build_from_corpus(
    (en.numpy() for en, fr in train_examples), target_vocab_size=2**13)
tokenizer_fr = tfds.deprecated.text.SubwordTextEncoder.build_from_corpus(
    (fr.numpy() for en, fr in train_examples), target_vocab_size=2**13)

def encode(lang1, lang2):
    lang1 = [tokenizer_en.vocab_size] + tokenizer_en.encode(lang1.numpy()) + [tokenizer_en.vocab_size + 1]
    lang2 = [tokenizer_fr.vocab_size] + tokenizer_fr.encode(lang2.numpy()) + [tokenizer_fr.vocab_size + 1]
    return lang1, lang2

def tf_encode(en, fr):
    return tf.py_function(encode, [en, fr], [tf.int64, tf.int64])

MAX_LENGTH = 40
def filter_max_length(x, y, max_length=MAX_LENGTH):
    return tf.logical_and(tf.size(x) <= max_length, tf.size(y) <= max_length)

train_dataset = train_examples.map(tf_encode)
train_dataset = train_dataset.filter(filter_max_length)
train_dataset = train_dataset.cache().shuffle(10000).padded_batch(64, padded_shapes=([-1], [-1])).prefetch(tf.data.AUTOTUNE)

val_dataset = val_examples.map(tf_encode)
val_dataset = val_dataset.filter(filter_max_length).padded_batch(64, padded_shapes=([-1], [-1]))

模型搭建

定义前馈网络、编码层、解码层及完整 Transformer 模型。注意嵌入层需乘以 $\sqrt{d_{model}}$ 以平衡梯度。

def point_wise_feed_forward_network(d_model, dff):
    return tf.keras.Sequential([
        tf.keras.layers.Dense(dff, activation='relu'),
        tf.keras.layers.Dense(d_model)
    ])

class EncoderLayer(tf.keras.layers.Layer):
    def __init__(self, d_model, num_heads, dff, rate=0.1):
        super(EncoderLayer, self).__init__()
        self.mha = MultiHeadAttention(d_model, num_heads)
        self.ffn = point_wise_feed_forward_network(d_model, dff)
        self.layernorm1 = tf.keras.layers.LayerNormalization(epsilon=1e-6)
        self.layernorm2 = tf.keras.layers.LayerNormalization(epsilon=1e-6)
        self.dropout1 = tf.keras.layers.Dropout(rate)
        self.dropout2 = tf.keras.layers.Dropout(rate)

    def call(self, x, training, mask):
        attn_output, _ = self.mha(x, x, x, mask)
        attn_output = self.dropout1(attn_output, training=training)
        out1 = self.layernorm1(x + attn_output)
        ffn_output = self.ffn(out1)
        ffn_output = self.dropout2(ffn_output, training=training)
        out2 = self.layernorm2(out1 + ffn_output)
        return out2

class DecoderLayer(tf.keras.layers.Layer):
    def __init__(self, d_model, num_heads, dff, rate=0.1):
        super(DecoderLayer, self).__init__()
        self.mha1 = MultiHeadAttention(d_model, num_heads)
        self.mha2 = MultiHeadAttention(d_model, num_heads)
        self.ffn = point_wise_feed_forward_network(d_model, dff)
        self.layernorm1 = tf.keras.layers.LayerNormalization(epsilon=1e-6)
        self.layernorm2 = tf.keras.layers.LayerNormalization(epsilon=1e-6)
        self.layernorm3 = tf.keras.layers.LayerNormalization(epsilon=1e-6)
        self.dropout1 = tf.keras.layers.Dropout(rate)
        self.dropout2 = tf.keras.layers.Dropout(rate)
        self.dropout3 = tf.keras.layers.Dropout(rate)

    def call(self, x, enc_output, training, look_ahead_mask, padding_mask):
        attn1, _ = self.mha1(x, x, x, look_ahead_mask)
        attn1 = self.dropout1(attn1, training=training)
        out1 = self.layernorm1(attn1 + x)
        attn2, _ = self.mha2(enc_output, enc_output, out1, padding_mask)
        attn2 = self.dropout2(attn2, training=training)
        out2 = self.layernorm2(attn2 + out1)
        ffn_output = self.ffn(out2)
        ffn_output = self.dropout3(ffn_output, training=training)
        out3 = self.layernorm3(ffn_output + out2)
        return out3

class Encoder(tf.keras.layers.Layer):
    def __init__(self, num_layers, d_model, num_heads, dff, input_vocab_size, maximum_position_encoding, rate=0.1):
        super(Encoder, self).__init__()
        self.d_model = d_model
        self.num_layers = num_layers
        self.embedding = tf.keras.layers.Embedding(input_vocab_size, d_model)
        self.pos_encoding = positional_encoding(maximum_position_encoding, self.d_model)
        self.enc_layers = [EncoderLayer(d_model, num_heads, dff, rate) for _ in range(num_layers)]
        self.dropout = tf.keras.layers.Dropout(rate)

    def call(self, x, training, mask):
        seq_len = tf.shape(x)[1]
        x = self.embedding(x)
        x *= tf.math.sqrt(tf.cast(self.d_model, tf.float32))
        x += self.pos_encoding[:, :seq_len, :]
        x = self.dropout(x, training=training)
        for i in range(self.num_layers):
            x = self.enc_layers[i](x, training, mask)
        return x

class Decoder(tf.keras.layers.Layer):
    def __init__(self, num_layers, d_model, num_heads, dff, target_vocab_size, maximum_position_encoding, rate=0.1):
        super(Decoder, self).__init__()
        self.d_model = d_model
        self.num_layers = num_layers
        self.embedding = tf.keras.layers.Embedding(target_vocab_size, d_model)
        self.pos_encoding = positional_encoding(maximum_position_encoding, d_model)
        self.dec_layers = [DecoderLayer(d_model, num_heads, dff, rate) for _ in range(num_layers)]
        self.dropout = tf.keras.layers.Dropout(rate)

    def call(self, x, enc_output, training, look_ahead_mask, padding_mask):
        seq_len = tf.shape(x)[1]
        x = self.embedding(x)
        x *= tf.math.sqrt(tf.cast(self.d_model, tf.float32))
        x += self.pos_encoding[:, :seq_len, :]
        x = self.dropout(x, training=training)
        for i in range(self.num_layers):
            x = self.dec_layers[i](x, enc_output, training, look_ahead_mask, padding_mask)
        return x

class Transformer(tf.keras.Model):
    def __init__(self, num_layers, d_model, num_heads, dff, input_vocab_size, target_vocab_size, pe_input, pe_target, rate=0.1):
        super(Transformer, self).__init__()
        self.encoder = Encoder(num_layers, d_model, num_heads, dff, input_vocab_size, pe_input, rate)
        self.decoder = Decoder(num_layers, d_model, num_heads, dff, target_vocab_size, pe_target, rate)
        self.final_layer = tf.keras.layers.Dense(target_vocab_size)

    def call(self, inp, tar, training, enc_padding_mask, look_ahead_mask, dec_padding_mask):
        enc_output = self.encoder(inp, training, enc_padding_mask)
        dec_output, _ = self.decoder(tar, enc_output, training, look_ahead_mask, dec_padding_mask)
        final_output = self.final_layer(dec_output)
        return final_output

# 设置参数
num_layers = 4
d_model = 128
dff = 512
num_heads = 8
input_vocab_size = tokenizer_en.vocab_size + 2
target_vocab_size = tokenizer_fr.vocab_size + 2
dropout_rate = 0.1

transformer = Transformer(num_layers, d_model, num_heads, dff, input_vocab_size, target_vocab_size, pe_input=1000, pe_target=1000, rate=dropout_rate)

编译与训练

使用稀疏交叉熵损失函数,优化器选择 Adam,并设置学习率衰减策略。

class CustomSchedule(tf.keras.optimizers.schedules.LearningRateSchedule):
    def __init__(self, d_model, warmup_steps=4000):
        super(CustomSchedule, self).__init__()
        self.d_model = d_model
        self.d_model = tf.cast(self.d_model, tf.float32)
        self.warmup_steps = warmup_steps

    def __call__(self, step):
        arg1 = tf.math.rsqrt(step)
        arg2 = step * (self.warmup_steps ** -1.5)
        return tf.math.rsqrt(self.d_model) * tf.math.minimum(arg1, arg2)

learning_rate = CustomSchedule(d_model)
optimizer = tf.keras.optimizers.Adam(learning_rate, beta_1=0.9, beta_2=0.98, epsilon=1e-9)

loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True, reduction='none')
def loss_function(real, pred):
    mask = tf.math.logical_not(tf.math.equal(real, 0))
    loss_ = loss_object(real, pred)
    mask = tf.cast(mask, dtype=loss_.dtype)
    loss_ *= mask
    return tf.reduce_sum(loss_) / tf.reduce_sum(mask)

train_loss = tf.keras.metrics.Mean(name='train_loss')
train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')

def create_masks(inp, tar):
    enc_padding_mask = create_padding_mask(inp)
    dec_padding_mask = create_padding_mask(inp)
    look_ahead_mask = create_look_ahead_mask(tf.shape(tar)[1])
    dec_target_padding_mask = create_padding_mask(tar)
    combined_mask = tf.maximum(dec_target_padding_mask, look_ahead_mask)
    return enc_padding_mask, combined_mask, dec_padding_mask

def create_padding_mask(seq):
    seq = tf.cast(tf.math.equal(seq, 0), tf.float32)
    return seq[:, tf.newaxis, tf.newaxis, :]

def create_look_ahead_mask(size):
    mask = 1 - tf.linalg.band_part(tf.ones((size, size)), -1, 0)
    return mask

@tf.function
def train_step(inp, tar):
    tar_inp = tar[:, :-1]
    tar_real = tar[:, 1:]
    enc_padding_mask, combined_mask, dec_padding_mask = create_masks(inp, tar_inp)
    
    with tf.GradientTape() as tape:
        predictions, _ = transformer(inp, tar_inp, True, enc_padding_mask, combined_mask, dec_padding_mask)
        loss = loss_function(tar_real, predictions)
    gradients = tape.gradient(loss, transformer.trainable_variables)
    optimizer.apply_gradients(zip(gradients, transformer.trainable_variables))
    train_loss(loss)
    train_accuracy(tar_real, predictions)

EPOCHS = 20
for epoch in range(EPOCHS):
    train_loss.reset_states()
    train_accuracy.reset_states()
    for (batch, (inp, tar)) in enumerate(train_dataset):
        train_step(inp, tar)
        if batch % 50 == 0:
            print(f'Epoch {epoch+1} Batch {batch} Loss {train_loss.result():.4f} Accuracy {train_accuracy.result():.4f}')
    print(f'Epoch {epoch+1} Loss {train_loss.result():.4f} Accuracy {train_accuracy.result():.4f}')

优化技巧

  1. 标签平滑:缓解过拟合,提升泛化能力。
  2. 波束搜索:替代贪心搜索,生成更流畅准确的翻译结果。
  3. 预训练词向量:初始化嵌入层,增强特征表示能力。

总结

注意力机制通过 QKV 框架让模型学会聚焦关键信息,解决了长序列依赖问题。多头注意力从多角度捕捉特征,是 Transformer 的核心组件。该模型完全基于注意力机制,实现了高度并行化计算,在机器翻译等任务中表现优异。

目录

  1. 注意力机制与 Transformer 模型实战指南
  2. 核心思想:为什么需要注意力机制
  3. QKV 框架与基础计算
  4. 实现基础注意力计算
  5. 模拟输入:批次大小=2,序列长度=3,特征维度=4
  6. 自注意力与多头注意力
  7. 自注意力机制
  8. 多头注意力机制
  9. 测试多头注意力层
  10. Transformer 模型架构详解
  11. 编码器与解码器
  12. 位置编码
  13. 生成位置编码
  14. 实战:基于 Transformer 的机器翻译
  15. 数据预处理
  16. 加载英法翻译数据集
  17. 构建分词器
  18. 模型搭建
  19. 设置参数
  20. 编译与训练
  21. 优化技巧
  22. 总结

更多推荐文章

查看全部
  • ZeroClaw:零开销、零妥协的 Rust 原生 AI 助手基础设施
  • 网络安全自学入门:渗透测试方向学习路线与规划指南
  • Whisper-large-v3 语音识别模型缓存加速与 HuggingFace Hub 离线加载
  • Linux 网络编程实战:TCP/IP 协议栈与 UDP 通信
  • C++ 四十年演进:从 C with Classes 到现代标准
  • 大模型推理服务框架 LocalAI 一键部署指南
  • Vivado 与 Modelsim 联合仿真环境配置指南
  • Proxmox VE (PVE) 下载和安装 Kali Linux 教程
  • 二分查找经典例题解析
  • DeepSeek 深度使用指南:提示词技巧与本地知识库搭建
  • C++类型约束实战精要
  • 数据库迁移 TCO 全景账本:MySQL 替代中的隐性成本与工程化工具链实测
  • Python 宠物美容服务预约档案系统设计与可视化
  • 跨平台字体解决方案:构建一致且高性能的 Web 字体体验
  • Docker Compose 重启策略怎么选:always、on-failure、no 与 unless-stopped
  • Python 中不易察觉的有趣事实与底层机制解析
  • DSP 核心组件 SM 算法部署
  • Llama 3-8B-Instruct 在昇腾 NPU 上的 SGLang 性能实测
  • 35 岁程序员职业危机应对策略与成长路径
  • 就业环境变化下的数据分析技能提升与职业路径规划

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online