TensorFlow需手动配置Attention层或自定义实现:用tf.keras.layers.Attention需确保shape对齐和attention_mask传入;手写Bahdanau需用tf.where掩码;MultiHeadAttention要注意num_heads整除key_dim及LayerNormalization位置。

TensorFlow 本身不提供开箱即用的 Attention 层来直接替代 Seq2Seq 中的旧式注意力计算;你得明确用 tf.keras.layers.Attention(缩放点积注意力)还是手写 tf.linalg.matmul + tf.nn.softmax 控制细节。选错类型或忽略 mask 参数,模型会学出错误的对齐。
用 tf.keras.layers.Attention 做标准缩放点积注意力
这是最常用也最稳妥的方式,适用于 encoder-decoder 架构中 decoder 对 encoder 输出的注意力。它自动处理缩放、softmax 和加权求和,但要求输入 shape 对齐:
-
query形状为[batch, q_seq, features],比如 decoder 的隐藏状态 -
value和可选的key形状为[batch, v_seq, features],比如 encoder 的输出 - 若未传
key,则key == value;若传了key,必须保证key.shape[-1] == query.shape[-1] - 务必传
attention_mask(shape[batch, q_seq, v_seq]),否则 padding 位置也会参与 softmax —— 这是训练崩掉的常见原因
示例:
attention_layer = tf.keras.layers.Attention()
# encoder_out: [batch, enc_len, d_model]
# decoder_state: [batch, 1, d_model](单步解码)
context = attention_layer(
inputs=[decoder_state, encoder_out],
attention_mask=tf.cast(mask, tf.float32) # mask shape: [batch, 1, enc_len]
)
手动实现带 masking 的 Bahdanau 注意力
当需要自定义打分函数(如 score = tanh(Wq + Uk + b))、或复现论文细节时,必须手写。核心陷阱是:softmax 前必须用 tf.where 把 padding 位置设为极小值(如 -1e9),不能只靠 mask * score —— 否则 0 会干扰 softmax 概率分布。
- 先拼接
query和key,过一层tf.keras.layers.Dense得到 logits - 用
tf.expand_dims(query, axis=2)和tf.expand_dims(key, axis=1)对齐广播维度 - mask 必须是
bool类型,再转float32后做tf.where(mask, score, -1e9) - 输出权重 shape 是
[batch, q_seq, v_seq],需tf.matmul(weights, value)得到 context
tf.keras.layers.MultiHeadAttention 的坑在哪
它不是简单堆多个 Attention 层,而是内部做了 split_head + concat,且默认启用 use_bias=True。容易出问题的点:
-
num_heads必须整除key_dim(即query/key/value的最后一维),否则报ValueError: num_heads must divide key_dim - 如果输入没有归一化(比如没接
LayerNormalization),梯度容易爆炸;官方 Transformer 模板里每个MultiHeadAttention后都跟一个LayerNormalization - 它的
attention_mask接收的是[batch, q_seq, v_seq],但如果是 causal attention(如 GPT),要自己构造上三角 mask:tf.linalg.band_part(tf.ones((seq, seq)), -1, 0)
注意力输出后要不要 residual connection 和 normalization
要,而且顺序不能错。标准做法是:output = layer_norm(x + dropout(attention_output))。漏掉 residual 会让深层网络梯度消失;在 LayerNormalization 前不加 dropout 会导致 attention 权重过于稳定,泛化变差。
尤其注意:如果你用 tf.keras.layers.Attention 但没手动加 residual,那它只是个纯计算模块,不包含任何残差或 norm —— 这和 PyTorch 的 nn.MultiheadAttention 默认行为不同,容易误以为“封装好了”。
真正难的不是写注意力公式,而是 mask 怎么传、维度怎么对、norm 放哪一层——这些地方错一点,loss 就不降,还很难 debug。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











