|
28 | 28 | <meta property="og:description" content="Task 1: 均方根层归一化 (RMS Norm)均方根层归一化(RMS Norm)是深度学习中应用最广泛的归一化模块,尤其在自然语言处理(NLP)和大语言模型(LLM)领域。该模块以形状为 [batch_size, seqlen, hidden_size] 的张量为输入(记为 X,形状为 [b, s, h]),并沿着隐藏层 h 维度,执行带可学习缩放变换的均方根归一化操作,得到输出 Y,形状为"> |
29 | 29 | <meta property="og:locale" content="zh_CN"> |
30 | 30 | <meta property="article:published_time" content="2025-06-17T06:17:05.000Z"> |
31 | | -<meta property="article:modified_time" content="2025-06-18T01:06:31.432Z"> |
| 31 | +<meta property="article:modified_time" content="2025-06-18T08:30:06.897Z"> |
32 | 32 | <meta property="article:author" content="DeepEngine"> |
33 | 33 | <meta property="article:tag" content="RMSNorm"> |
34 | 34 | <meta property="article:tag" content="Vocab Embedding"> |
@@ -187,7 +187,7 @@ <h1 class="post-title" itemprop="name headline"> |
187 | 187 | <i class="far fa-calendar-check"></i> |
188 | 188 | </span> |
189 | 189 | <span class="post-meta-item-text">更新于</span> |
190 | | - <time title="修改时间:2025-06-18 09:06:31" itemprop="dateModified" datetime="2025-06-18T09:06:31+08:00">2025-06-18</time> |
| 190 | + <time title="修改时间:2025-06-18 16:30:06" itemprop="dateModified" datetime="2025-06-18T16:30:06+08:00">2025-06-18</time> |
191 | 191 | </span> |
192 | 192 | <span class="post-meta-item"> |
193 | 193 | <span class="post-meta-item-icon"> |
@@ -251,25 +251,36 @@ <h5 id="TODO-2"><a href="#TODO-2" class="headerlink" title="TODO"></a>TODO</h5>< |
251 | 251 | <ol><li>输入 <code>I</code> 存储每个 token 的 ID,其 <code>dtype</code> 为 <code>torch.long</code>。</li><li>你的实现不应该更改 <code>I</code>,包括 <code>I</code> 的数值与属性(包括 <code>I</code> 的 <code>shape</code>, <code>dtype</code> 和 <code>device</code> 等),因为 <code>I</code> 可能还有其他用途。</li><li>参数中的 <code>dtype</code> 和 <code>device</code> 仅针对可学习嵌入表 <code>Tr</code> ,<code>Tr</code> 的 <code>dtype</code> 和 <code>device</code> 可能与 <code>I</code> 的不同,可以使用这两个参数和 <code>torch.nn.Parameter</code> 完成对 <code>Tr</code> 的申请与初始化。</li><li>返回的嵌入张量 <code>Er</code> 的 <code>device</code> 应与 <code>I</code> 相同,<code>dtype</code> 与 <code>Tr</code> 相同。</li><li>在所有测试用例中,<code>v</code> 均能被 <code>w</code> 整除,但仍然建议在 <code>__init__</code> 方法中使用 <code>assert</code> 进行检查,并附上错误提示,这是编程的良好习惯。</li></ol> |
252 | 252 | </div> |
253 | 253 |
|
254 | | -<h4 id="Optional-Task4:旋转位置编码"><a href="#Optional-Task4:旋转位置编码" class="headerlink" title="[Optional] Task4:旋转位置编码"></a>[Optional] Task4:旋转位置编码</h4><p>Transformer 模型将输入的词元(token)视为一个“词袋”并并行处理,因而本身不具备对序列顺序的感知能力。为保留输入中的序列信息,最初版本的 Transformer 引入了一种新颖的正弦位置编码(Sinusoidal Positional Encoding,简称 SinPE),其定义如下面公式所示:<br>$$<br>\text{SinPE}(n) :=<br>\begin{bmatrix}<br>\sin{\left(n\theta^0\right)} \cr<br>\cos{\left(n\theta^0\right)} \cr<br>\sin{\left(n\theta^1\right)} \cr<br>\cos{\left(n\theta^1\right)} \cr<br>\vdots \cr<br>\sin\left(n\theta^{\frac{d}{2}-1}\right) \cr<br>\cos\left(n\theta^{\frac{d}{2}-1}\right)<br>\end{bmatrix}<br>\quad \text{where }<br>\theta := \beta^{-1},<br>\beta := \text{base}^{\frac{2}{d}},<br>n \in {0, 1, \ldots, L - 1}<br>\tag{5}<br>$$<br>其中,<code>L</code> 表示序列长度,<code>d</code> 表示隐藏层维度,<code>base</code> 是一个人为设定的大整数,通常取值为10000(请参考原始论文),$\beta$ 是三角函数基的波长或周期的幂次基数,随着维度 <code>i</code> 的增大而按几何级数增长,其形式为 $\beta ^ i$,其中 $i=0,1,\ldots,d/2$。</p> |
| 254 | +<h4 id="Optional-Task4:旋转位置编码"><a href="#Optional-Task4:旋转位置编码" class="headerlink" title="[Optional] Task4:旋转位置编码"></a>[Optional] Task4:旋转位置编码</h4><p>Transformer 模型将输入的词元(token)视为一个“词袋”并并行处理,因而本身不具备对序列顺序的感知能力。为保留输入中的序列信息,最初版本的 Transformer 引入了一种新颖的正弦位置编码(Sinusoidal Positional Encoding,简称 SinPE),其定义如下面公式所示:<br>$$<br>\text{SinPE}(n) :=<br>\begin{bmatrix}<br>\sin{\left(n\theta^0\right)} \cr<br>\cos{\left(n\theta^0\right)} \cr<br>\sin{\left(n\theta^1\right)} \cr<br>\cos{\left(n\theta^1\right)} \cr<br>\vdots \cr<br>\sin\left(n\theta^{\frac{d}{2}-1}\right) \cr<br>\cos\left(n\theta^{\frac{d}{2}-1}\right)<br>\end{bmatrix}<br>\quad \text{where }<br>\theta := \beta^{-1},<br>\beta := \text{base}^{\frac{2}{d}},<br>n \in {0, 1, \ldots, L - 1}<br>\tag{5}<br>$$<br>其中,<code>L</code> 表示序列长度,<code>d</code> 表示隐藏层维度,<code>base</code> 是一个人为设定的大整数,通常取值为10000(请参考原始论文),$\beta$ 是三角函数基的波长或周期的幂次基数,随着维度 <code>i</code> 的增大而按几何级数增长,其形式为 $\beta ^ i$,其中 $i=0,1,\ldots,\frac{d}{2}-1$。</p> |
255 | 255 | <p>相比之下,旋转位置编码(Rotary Position Embedding,简称 RoPE)在处理长序列时提供了更稳定的方案。它在具备绝对位置信息感知能力的同时,能够捕捉相对位置模式,因此被广泛应用于当前的主流开源大模型(如 LLaMA,ChatCLM)中。随着研究的推进,RoPE 逐渐取代了原始的 SinPE、可学习位置编码(Learnable PE)以及相对位置编码(Relative PE),成为当前 Transformer 结构中位置编码的主流选择。</p> |
256 | 256 | <p>更具体的说,RoPE 在复数域中对隐藏状态进行旋转操作,而不像 SinPE 那样将位置编码加到隐藏状态中。该方法与 SinPE 共享相同的基函数,如下式所示:<br>$$<br>\text{RoPE}(n) :=<br>\begin{bmatrix}<br>R_n^{(0)} \cr<br>\phantom{R_n^{(0)}}& R_n^{(1)} \cr<br>\phantom{R_n^{(0)}}& \phantom{R_n^{(0)}}& \ddots \cr<br>\phantom{R_n^{(0)}}&\phantom{R_n^{(0)}}&\phantom{R_n^{(0)}}& R_n^{\left(\frac{d}{2} - 1\right)}<br>\end{bmatrix},<br>\quad \text{where }<br>R_n^{(i)} :=<br>\begin{bmatrix}<br>\cos(n\theta^i) & -\sin(n\theta^i) \cr<br>\sin(n\theta^i) & \cos(n\theta^i)<br>\end{bmatrix}<br>\tag{6}<br>$$<br>尽管 RoPE(旋转位置编码)具备相对距离衰减和训练稳定性等优势,但在序列长度的外推能力方面仍然存在不足,尤其是在“短序列训练、长序列推理”(Train Short and Test Long)场景下表现不佳(详见参考文献中的 Length Extrapolation 相关论文)。因此,已有多项研究致力于扩展 RoPE 的泛化能力,使其在推理时能有效处理远超训练长度的序列。</p> |
257 | 257 | <p>在这些方法中,<strong>NTK-aware RoPE</strong> 通过结合高频外推和低频内插来提升外推性能。它通过缩放系数 $c_𝜅$ 对参数 $\beta$ 进行调整,从而实现在最低频率项上以比例 $𝜅$ 进行等效插值,同时保持高频项的尺度不变,如下式所示。这种非线性缩放方式可以直接应用于使用 RoPE 预训练的大语言模型(如 Llama),无需微调即可扩展其上下文长度的边界,这一方法已被 <em>CodeLlama</em> 所采纳(详见参考文献中的 Llama RoPE 源代码)。</p> |
258 | 258 | <p>$$<br>\tilde{\beta} := c_\kappa \cdot \beta, \quad<br>s.t. \quad \frac{n}{\tilde{\beta}^{d/2 - 1}} = \frac{n/\kappa}{\beta^{d/2 - 1}}<br>\Rightarrow c_\kappa = \kappa^{2/(d - 2)} \tag{7}<br>$$<br>在 <strong>Task4</strong> 中,你需要像 <code>Llama</code> 一样实现 <code>NTKAwareRoPE</code> 模块,但是,有一些差异如下:</p> |
259 | 259 | <ul> |
260 | | -<li>标准的 RoPE 模块在前向传播时仅返回余弦/正弦基张量,形状为 <code>[seqlen, head_dim]</code>,该参数对记作 <code>(C, S)</code>,形状记作 <code>[s, hd]</code>,实际的旋转编码操作是在另一个独立的函数 <code>apply_rotary_pos_emb</code> 完成。</li> |
261 | | -<li>我们遵循这种设计模式:你需要在 <code>src/functional.py</code> 中实现 <code>apply_rotary_pos_emb</code> 函数,该函数会在 <code>src/modeling/pos_emb.py</code> 中导入,并在 <code>NTKAwareRoPE</code> 的 <code>forward</code> 方法中被调用。与标准做法不同的是,<code>NTKAwareRoPE</code> 的 <code>forward</code> 方法不仅返回 <code>(C, S)</code> 的基张量,还应对输入张量 <code>X</code> 应用旋转编码并返回嵌入后的输出张量 <code>E</code>,其中:<ul> |
| 260 | +<li><p>标准的 RoPE 模块在前向传播时仅返回余弦/正弦基张量,形状为 <code>[seqlen, head_dim]</code>,该参数对记作 <code>(C, S)</code>,分别存储 $\sin{n\theta^i}$ 和 $\cos{n\theta^i}$(请参考 $(5)(6)$ 式中对 $n,\theta$ 的定义)。实际的旋转编码操作是在另一个独立的函数 <code>apply_rotary_pos_emb</code> 完成。</p> |
| 261 | +</li> |
| 262 | +<li><p>我们遵循这种设计模式:你需要在 <code>src/functional.py</code> 中实现 <code>apply_rotary_pos_emb</code> 函数,该函数会在 <code>src/modeling/pos_emb.py</code> 中导入,并在 <code>NTKAwareRoPE</code> 的 <code>forward</code> 方法中被调用。与标准做法不同的是,<code>NTKAwareRoPE</code> 的 <code>forward</code> 方法不仅返回 <code>(C, S)</code> 的基张量,还应对输入张量 <code>X</code> 应用旋转编码并返回嵌入后的输出张量 <code>E</code>,其中:</p> |
| 263 | +<ul> |
262 | 264 | <li>输入张量 <code>X</code> 的形状为 <code>[batch_size, seqlen, num_heads, head_dim]</code>,记作 <code>[b, s, nh, hd]</code>;</li> |
263 | 265 | <li>输出张量 <code>E</code> 的形状与 <code>X</code> 的形状相同,表示应用旋转编码后的结果。</li> |
264 | 266 | </ul> |
265 | 267 | </li> |
266 | | -<li>另一个问题是,初始化 <code>NTKAwareRoPE</code> 时会提供一个训练阶段使用的最大序列长度(记作 <code>ms</code>)和一个缩放比例(记作 <code>k</code>),此时我们可以预先计算好 <code>(C, S)</code>,其形状为 <code>[es, hd]</code>,其中 <code>es = ms x k</code> 表示最大支持的拓展序列长度。因此,当有一个输入张量 <code>X_</code> 的实际序列长度 <code>s_</code> 超过了 <code>es</code>,即 <code>s_ > es</code>,我们必须动态重新计算一对新的 <code>(C_, S_)</code>,以确保旋转编码操作可以适用于这类超长输入。</li> |
267 | | -<li>但这里有两个问题:<ol> |
| 268 | +<li><p>由于 <strong>RoPE</strong> 矩阵的稀疏性,直接用矩阵乘法来实现会很浪费算力,推荐使用以下方式来实现 <strong>RoPE</strong>:<br>$$<br>\begin{pmatrix}<br>x_0 \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>x_1 \vphantom{\cos{\left(n\theta^0\right)}} \cr<br>x_2 \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>x_3 \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>\vdots \cr<br>x_{d-2} \vphantom{\cos\left(n\theta^{\frac{d}{2}-1}\right)}\cr<br>x_{d-1} \vphantom{\cos\left(n\theta^{\frac{d}{2}-1}\right)}<br>\end{pmatrix}<br>\quad<br>\bigotimes<br>\quad<br>\begin{pmatrix}<br>\cos{\left(n\theta^0\right)} \cr<br>\cos{\left(n\theta^0\right)} \cr<br>\cos{\left(n\theta^1\right)} \cr<br>\cos{\left(n\theta^1\right)} \cr<br>\vdots \cr<br>\cos\left(n\theta^{\frac{d}{2}-1}\right) \cr<br>\cos\left(n\theta^{\frac{d}{2}-1}\right)<br>\end{pmatrix}<br>\quad</p> |
| 269 | +<ul> |
| 270 | +<li></li> |
| 271 | +</ul> |
| 272 | +<p>\quad<br>\begin{pmatrix}<br>-x_1 \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>x_0 \vphantom{\cos{\left(n\theta^0\right)}} \cr<br>-x_3 \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>x_2 \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>\vdots \cr<br>-x_{d-1} \vphantom{\cos\left(n\theta^{\frac{d}{2}-1}\right)}\cr<br>x_{d-2} \vphantom{\cos\left(n\theta^{\frac{d}{2}-1}\right)}<br>\end{pmatrix}<br>\quad<br>\bigotimes<br>\quad<br>\begin{pmatrix}<br>\sin{\left(n\theta^0\right)} \cr<br>\sin{\left(n\theta^0\right)} \cr<br>\sin{\left(n\theta^1\right)} \cr<br>\sin{\left(n\theta^1\right)} \cr<br>\vdots \cr<br>\sin\left(n\theta^{\frac{d}{2}-1}\right) \cr<br>\sin\left(n\theta^{\frac{d}{2}-1}\right)<br>\end{pmatrix}<br>\tag{8}<br>$$<br>其中,$\otimes$ 是逐位相乘,并且可以注意到上述式子中,按 $(x_0,x_1),(x_2,x_3),\cdots$ 的顺序进行分组。而 <code>Llama</code> 和 <code>ChatGLM</code> 的 <strong>RoPE</strong> 模块对 $x$ 的顺序进行了重组,也即按 $(x_0,x_{\frac{d}{2}}),(x_1,x_{\frac{d}{2}+1}),\cdots$ 的顺序进行分组,请使用如下方法进行计算。<br>$$<br>\begin{pmatrix}<br>x_0 \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>x_1 \vphantom{\cos{\left(n\theta^0\right)}} \cr<br>x_2 \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>x_3 \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>\vdots \cr<br>x_{\frac{d}{2}-1} \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>x_{\frac{d}{2}} \vphantom{\cos{\left(n\theta^0\right)}} \cr<br>x_{\frac{d}{2}+1} \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>x_{\frac{d}{2}+2} \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>\vdots \cr<br>x_{d-2} \vphantom{\cos\left(n\theta^{\frac{d}{2}-1}\right)}\cr<br>x_{d-1} \vphantom{\cos\left(n\theta^{\frac{d}{2}-1}\right)}<br>\end{pmatrix}<br>\quad<br>\bigotimes<br>\quad<br>\begin{pmatrix}<br>\cos{\left(n\theta^0\right)} \cr<br>\cos{\left(n\theta^1\right)} \cr<br>\cos{\left(n\theta^2\right)} \cr<br>\cos{\left(n\theta^3\right)} \cr<br>\vdots \cr<br>\cos\left(n\theta^{\frac{d}{2}-1}\right) \cr<br>\cos{\left(n\theta^0\right)} \cr<br>\cos{\left(n\theta^1\right)} \cr<br>\cos{\left(n\theta^2\right)} \cr<br>\vdots \cr<br>\cos\left(n\theta^{\frac{d}{2}-2}\right) \cr<br>\cos\left(n\theta^{\frac{d}{2}-1}\right)<br>\end{pmatrix}<br>\quad<br>+<br>\quad<br>\begin{pmatrix}<br>-x_{\frac{d}{2}} \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>-x_{\frac{d}{2}+1} \vphantom{\cos{\left(n\theta^0\right)}} \cr<br>-x_{\frac{d}{2}+2} \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>-x_{\frac{d}{2}+3} \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>\vdots \cr<br>-x_{d-1} \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>x_0 \vphantom{\cos{\left(n\theta^0\right)}} \cr<br>x_1 \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>x_2 \vphantom{\cos{\left(n\theta^0\right)}}\cr<br>\vdots \cr<br>x_{\frac{d}{2}-2} \vphantom{\cos\left(n\theta^{\frac{d}{2}-1}\right)}\cr<br>x_{\frac{d}{2}-1} \vphantom{\cos\left(n\theta^{\frac{d}{2}-1}\right)}<br>\end{pmatrix}<br>\quad<br>\bigotimes<br>\quad<br>\begin{pmatrix}<br>\sin{\left(n\theta^0\right)} \cr<br>\sin{\left(n\theta^1\right)} \cr<br>\sin{\left(n\theta^2\right)} \cr<br>\sin{\left(n\theta^3\right)} \cr<br>\vdots \cr<br>\sin\left(n\theta^{\frac{d}{2}-1}\right) \cr<br>\sin{\left(n\theta^0\right)} \cr<br>\sin{\left(n\theta^1\right)} \cr<br>\sin{\left(n\theta^2\right)} \cr<br>\vdots \cr<br>\sin\left(n\theta^{\frac{d}{2}-2}\right) \cr<br>\sin\left(n\theta^{\frac{d}{2}-1}\right)<br>\end{pmatrix}<br>\tag{9}<br>$$<br>注意:余弦\正弦参数对 <code>(C, S)</code> 也要做相应调整。</p> |
| 273 | +</li> |
| 274 | +<li><p>另一个问题是,初始化 <code>NTKAwareRoPE</code> 时会提供一个训练阶段使用的最大序列长度(记作 <code>ms</code>)和一个缩放比例(记作 <code>k</code>,也即 $\kappa$),此时我们可以预先计算好 <code>(C, S)</code>,其形状为 <code>[es, hd]</code>,其中 <code>es = ms x k</code> 表示最大支持的拓展序列长度。因此,当有一个输入张量 <code>X_</code> 的实际序列长度 <code>s_</code> 超过了 <code>es</code>,即 <code>s_ > es</code>,我们必须动态重新计算一对新的 <code>(C_, S_)</code>,以确保旋转编码操作可以适用于这类超长输入。</p> |
| 275 | +</li> |
| 276 | +<li><p>但这里有两个问题:</p> |
| 277 | +<ol> |
268 | 278 | <li>当需要重新计算新的余弦/正弦基 <code>(C', S')</code> 时,我们应如何为输入张量 <code>X'</code> 确定新的缩放比例 <code>k'</code> ?</li> |
269 | 279 | <li>当遇到这类超长序列时,我们是否应该每次仅计算并使用该输入所需的 <code>(C', S')</code>,同时保留原始的缩放比例 <code>k</code> 及其对应的 <code>(C, S)</code> 用于常规输入?或者,我们应该每次都更新当前的 <code>k</code> 及其对应的 <code>(C, S)</code> 为新的 <code>k'</code> 和 <code>(C', S')</code> ?</li> |
270 | 280 | </ol> |
271 | 281 | </li> |
272 | | -<li>上述问题尚无标准答案。在此任务中,我们采用如下策略:<ol> |
| 282 | +<li><p>上述问题尚无标准答案。在此任务中,我们采用如下策略:</p> |
| 283 | +<ol> |
273 | 284 | <li>当出现新的输入长度 <code>s' > es</code> 时,我们选择满足 <code>es' = ms x k' >= s'</code> 的最小 <code>k'</code>,其中 <code>k'</code> 是一个偶数;</li> |
274 | 285 | <li>我们在初始化 <code>NTKAwareRoPE</code> 模块时新增了一个参数 <code>dynamic</code>。当 <code>dynamic = True</code> 时,每次遇到超出长度的输入时,都会更新当前的 $k \leftarrow k’$ 以及 $(C,S) \leftarrow (C’, S’)$;反之,若 <code>dynamic = False</code> 时,则仅为当前超长输入临时计算并使用 $(C’,S’)$,而全局的 $k$ 和 $(C,S)$ 保持不变。</li> |
275 | 286 | </ol> |
|
0 commit comments