祝福国家强大 發表於 2026-4-2 13:33:00

深度学习进阶(二)多头自注意力机制(Multi-Head Attention)

<p>在第一篇中,我们已经得到了自注意力的核心公式:</p>
<p></p><div class="math display">\[\mathrm{Attention}(\mathbf{Q},\mathbf{K},\mathbf{V})=\mathrm{softmax}\left(\frac{ \mathbf{Q}\mathbf{K}^T}{\sqrt{d_k}}\right) \mathbf{V}
\]</div><p></p><p>再概述一下自注意力的本质:<strong>通过一次全局加权,将序列中的所有信息重新融合到每一个位置上,最终强化信息表示。</strong></p>
<p>但单头的自注意力还是有些局限:<strong>一组 <span class="math inline">\((\mathbf{W}_Q, \mathbf{W}_K, \mathbf{W}_V)\)</span> 只能用一种方式去理解序列</strong>。<br>
这其实和在卷积层中使用多个卷积核是相似的道理,我们不能只用一个卷积核去提取纹理、色彩、形状等所有特征。<br>
同理,我们不能指望一组参数矩阵就能学习到序列在语义、语法、情感等多个方面的关联。</p>
<p>因此,我们在实际算法设计中,使用的往往是<strong>多头自注意力</strong>。<br>
其原理并不复杂,只是在单头自注意力基础上的简单改进。</p>
<h1 id="1-多头注意力的核心思想">1. 多头注意力的核心思想</h1>
<p>多头注意力的核心思想很直观:</p>
<blockquote>
<p><strong>在“一层自注意力层”中,不只做一次注意力,而是做多次注意力,每次关注不同的信息子空间。</strong></p>
</blockquote>
<p>具体来说就是:不再只用一组 <span class="math inline">\((\mathbf{W}_Q, \mathbf{W}_K, \mathbf{W}_V)\)</span>, 而是<strong>同时学习 <span class="math inline">\(h\)</span> 组不同的参数矩阵</strong>,这样的每一组参数矩阵就是一个“头”,综合所有头的注意力信息,得到最终输出。</p>
<p>比如,第 <span class="math inline">\(t\)</span> 个头为:</p>
<p></p><div class="math display">\[\mathbf{Q}_t = \mathbf{X} \mathbf{W}_Q^{(t)}
\]</div><p></p><p></p><div class="math display">\[\mathbf{K}_t = \mathbf{X} \mathbf{W}_K^{(t)}
\]</div><p></p><p></p><div class="math display">\[\mathbf{V}_t = \mathbf{X} \mathbf{W}_V^{(t)}
\]</div><p></p><p>然后,每一个头都独立进行注意力计算:</p>
<p></p><div class="math display">\[\mathbf{Z}_t = \mathrm{Attention}(\mathbf{Q}_t, \mathbf{K}_t, \mathbf{V}_t)
\]</div><p></p><p>由于每个注意力头拥有<strong>独立的初始化参数矩阵</strong>,所以每一个头都是一个“观察角度”,它们分别回答在不同语义空间下相关性问题。<br>
其计算过程和单头自注意力并无区别,但一个新的问题是:</p>
<blockquote>
<p><strong>如何融合多头输出?</strong></p>
</blockquote>
<h1 id="2-多头的融合方式">2. 多头的融合方式</h1>
<h2 id="21-拼接">2.1 拼接</h2>
<p>通过多头自注意力,现在我们得到了多个输出:</p>
<p></p><div class="math display">\[\mathbf{Z}_1, \mathbf{Z}_2, \dots, \mathbf{Z}_h
\]</div><p></p><p>而要回答这些信息怎么合在一起,<strong>首先要了解 Transformer 对每个头的维度划分设计</strong>:</p>
<p></p><div class="math display">\[d_k = d_v = \frac{d}{h}
\]</div><p></p><p>这里的 <span class="math inline">\(d\)</span> 仍是表示序列中一个位置,或者说一个 <strong>token</strong> 的特征维度。</p>
<p>举个例子来说明:<br>
假定每个 token进入模型的特征维度:</p>
<p></p><div class="math display">\[d = 8
\]</div><p></p><p>并且使用:</p>
<p></p><div class="math display">\[h = 2 \quad (\text{两个注意力头})
\]</div><p></p><p>那么每个头的维度为:</p>
<p></p><div class="math display">\[d_k = d_v = \frac{d}{h} = \frac{8}{2} = 4
\]</div><p></p><p>这代表每个头的 Query / Key 向量维度和 Value 输出维度都为 4,分别计算注意力得到输出:</p>
<p></p><div class="math display">\[\mathbf{Z_1},\mathbf{Z_2} \in \mathbb{R}^{n \times 4}
\]</div><p></p><p>明确了维度变化后,我们就能进行多头输出融合的第一步:<strong>拼接</strong>。</p>
<p></p><div class="math display">\[\mathbf{Z} = \text{Concat}(\mathbf{Z}_1, \mathbf{Z}_2, \dots, \mathbf{Z}_h)
\]</div><p></p><p>思路很明确,就是<strong>把所有头的输出先直接拼在一起</strong>:</p>
<p></p><div class="math display">\[\mathbf{Z} \in \mathbb{R}^{n \times (h \cdot d_v=d)}
\]</div><p></p><p>可以发现,拼接后的维度重新恢复到了原始的模型维度 <span class="math inline">\(d\)</span> 。<br>
这样的切分逻辑可以在固定模型维度 <span class="math inline">\(d\)</span> 的前提下,使多头不会增加总体计算复杂度的数量级,从而避免因头数增加而产生计算量爆炸问题。<br>
同时,这种<strong>让输入输出维度相同</strong>的设计也和 Transformer 的后续逻辑相关。</p>
<h2 id="22-线性变换">2.2 线性变换</h2>
<p>拼接完还没有结束,实际上,在这之后,<span class="math inline">\(\mathbf{Z}\)</span> 还需要经过一个线性层:</p>
<p></p><div class="math display">\[\mathbf{Z}_{\text{final}} = \mathbf{Z} \mathbf{W}_O,\mathbf{W}_O \in \mathbb{R}^{d \times d}   
\]</div><p></p><p>你会发现,这里<strong>没有加入偏置</strong>。<br>
实际上,线性层本身自然是拥有偏置的,但许多 Transformer 实现都会选择关闭偏置,这是因为 Transformer block 的结构中仍存在后续线性变换以及归一化操作,这里的单个线性层中的偏置项对整体表达能力的影响较小,因此在理论公式中经常省略。</p>
<p>现在把整体写成一行如下:</p>
<p></p><div class="math display">\[\mathrm{MultiHead}(\mathbf{Q},\mathbf{K},\mathbf{V}) = \text{Concat}(\mathbf{Z}_1,\dots,\mathbf{Z}_h)\mathbf{W}_O
\]</div><p></p><p>这就是多头注意力的融合公式。</p>
<p>到这里,你可能有这样一个问题:<strong>只拼接不行吗?为什么还要再过一个线性层?</strong><br>
我们举个例子来回答这个问题:<br>
假设对于某个位置 <span class="math inline">\(i\)</span>,经过多头注意力后,我们得到了两个头的输出:</p>
<p></p><div class="math display">\[\mathbf{z}_1^{(i)} = [语法,结构,顺序,主谓]
\]</div><p></p><p></p><div class="math display">\[\mathbf{z}_2^{(i)} = [语义,情感,主题,语境]
\]</div><p></p><p>拼接之后得到:</p>
<p></p><div class="math display">\[\mathbf{z}^{(i)} = [语法,结构,顺序,主谓 , 语义,情感,主题,语境]
\]</div><p></p><p>此时,不同头的信息只是被“并排放在一起”,但它们之间并没有发生任何交互或关联。<br>
也就是说:<strong>有点用,但还不够。</strong></p>
<p>现在再进行融合:</p>
<p></p><div class="math display">\[\mathbf{z}_{final}^{(i)} = \mathbf{z}^{(i)} \mathbf{W}_O
\]</div><p></p><p>这步计算实际上是<strong>对拼接后的所有特征进行一次“重新加权组合”。</strong><br>
假设经过学习后,<span class="math inline">\(\mathbf{W}_O\)</span> 做出的组合类似于:</p>
<ol>
<li>把“主谓” + “语义”组合,得到更准确的句法语义关系。</li>
<li>把“结构” + “语境”组合,得到更高层次的上下文理解。</li>
<li>把“情感”适当放大或抑制。</li>
</ol>
<p>最终形成新的表示:</p>
<p></p><div class="math display">\[\mathbf{z}_{final}^{(i)} = [综合特征_1, 综合特征_2, \dots]
\]</div><p></p><p>由此,所有头的信息被打散并重新组合,<strong>模型可以自由地学习跨多头的特征关系。</strong></p>
<p>这就是多头自注意力机制的详细内容,我们由此实现了从多个角度对输入信息的强化表示,从模型整体角度来说,多头注意力本质上是一个<strong>用于建模序列内部关系的计算模块。</strong></p>
<p>因此,在 Transformer 中,多头注意力并不是单独使用的,而是被嵌入到一个更完整的结构单元中,这个单元就是 <strong>Transformer Block</strong>,我们在下一篇中再对其展开介绍。</p><br><br>
来源:https://www.cnblogs.com/Goblinscholar/p/19811074
頁: [1]
查看完整版本: 深度学习进阶(二)多头自注意力机制(Multi-Head Attention)