MultiHeadAttention实现详解
Transformer自2017年推出之后,已经横扫NLP领域,成为当之无愧的state-of-the-art。原始paper “Attention is All you Need”中对attention提出了通用的query/key/value抽象,开始时觉得很难理解,后来随着读的文献更多,慢慢体会到了其中的意思。关于Transformer和attention的各种解释类文章有很多,不再赘述,本文仅就其中的核心,MultiHeadAttention的实现进行源码剖析。
Transformer的实现有很多,但我看到实现得最clean的还是 # The Annotated Transformer。它的实现是一个最基本的版本,但五脏俱全,理解原理再合适不过。

如图所示,所谓Multi-Head Attention其实是把QKV的计算并行化,原始attention计算d_model维的向量,而Multi-Head Attention则是将d_model维向量先经过一个Linear Layer,再分解为h个Head计算attention,最终将这些attention向量连在一起后再经过一层Linear Layer输出。所以在整个过程中需要4个输入和输出维度都是d_model的Linear Layer,而整个Model的输入是(batch_size, seq_length, d_model),输出也是(batch_size, seq_length, d_model)。
先上原始代码:
1 | class MultiHeadedAttention(nn.Module): |
这段代码中最费解的地方:
1 | # 1) Do all the linear projections in batch from d_model => h x d_k |
前面提到MultiHeadAttention需要4个Linear Layer,而上面这段代码用到了其中前三个,最后一个用在最后一行self.linears[-1]。重写下这段代码:
1 | query, key, value = [l(x) for l, x in zip(self.linears, (query, key, value))] |
第一行把QKV分别经过一层Linear变换,tensor size不变,第二行将QKV的d_model维向量分解为h * d_k。
跑一个self-attention的实例,作为输入,query/key/value的shape为(batch_size, seq_lengh, d_model):
1 | h = 8 |
将代码中的tensor变换维度加上注释,类重命名为MultiHeadAttention,可运行的完整代码如下:
1 | import torch |
运行结果:
1 | Input size: torch.Size([1, 10, 512]) |