<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Seq2Seq on 边个濑椰的博客</title><link>https://blog.kawausococo.top/tags/seq2seq/</link><description>Recent content in Seq2Seq on 边个濑椰的博客</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><lastBuildDate>Wed, 08 Apr 2026 09:46:07 +0800</lastBuildDate><atom:link href="https://blog.kawausococo.top/tags/seq2seq/index.xml" rel="self" type="application/rss+xml"/><item><title>CS224N Assignment3 NMT</title><link>https://blog.kawausococo.top/p/cs224n-assignment3-nmt/</link><pubDate>Wed, 08 Apr 2026 09:46:07 +0800</pubDate><guid>https://blog.kawausococo.top/p/cs224n-assignment3-nmt/</guid><description>&lt;h1 id="cs-224n-spring-2024-assignment-3"&gt;CS 224n Spring 2024: Assignment #3
&lt;/h1&gt;&lt;p&gt;&lt;a class="link" href="https://web.stanford.edu/class/archive/cs/cs224n/cs224n.1246/assignments/a3_spr24_student_handout.pdf" target="_blank" rel="noopener"
 &gt;a3_spr24_student_handout.pdf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;本post从cs224n独立出来，旨在尽可能掌握Assignment3中基于RNN的NMT背后各步的数学原理，以及将代码部分和数学部分对应起来。&lt;/p&gt;
&lt;p&gt;参考论文：&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://arxiv.org/pdf/1409.0473" target="_blank" rel="noopener"
 &gt;NEURAL MACHINE TRANSLATION BY JOINTLY LEARNING TO ALIGN AND TRANSLATE&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="neural-machine-translation-with-rnns"&gt;Neural Machine Translation with RNNs
&lt;/h2&gt;&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n-a3/figure1.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;h3 id="model-description-training-procedure"&gt;Model description (training procedure)
&lt;/h3&gt;&lt;p&gt;Given a sentence in the source language, we look up the character or word embeddings from an &lt;strong&gt;embeddings matrix&lt;/strong&gt;, yielding $\mathbf{x}_1, \ldots, \mathbf{x}_m$ ($\mathbf{x}_i \in \mathbb{R}^{e \times 1}$), where $m$ is the length of the source sentence and $e$ is the embedding size.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;我们手里的一条源语言句子，由于计算机无法直接理解文字，我们首先要进行“查表”操作，也就是文本中提到的&lt;strong&gt;获取词嵌入&lt;/strong&gt;（look up &amp;hellip; embeddings matrix）。假设句子有 $m$ 个词，经过这一步，每个词都被映射成了一个长度为 $e$ 的列向量 $\mathbf{x}_i$。这样一来，整句话就被转换成了一个由实数向量构成的序列&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;We then feed the embeddings to a &lt;strong&gt;convolutional layer&lt;/strong&gt;$^1$ while maintaining their shapes.&lt;/p&gt;
&lt;p&gt;$^1$ : Checkout &lt;a class="link" href="https://cs231n.github.io/convolutional-networks" target="_blank" rel="noopener"
 &gt;Convolutional Neural Networks&lt;/a&gt; for an in-depth description for convolutional layers if you are not familiar.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;带着这些初步的向量表示，模型并没有直接把它们送入主要的编码器，而是先让它们穿过一个&lt;strong&gt;卷积层&lt;/strong&gt;（convolutional layer）。文本中特别强调了这一步“保持了它们的形状”，这意味着经过卷积处理后，我们依然拥有 $m$ 个向量，且每个向量的维度依然是 $e$。这一步的作用通常是对局部的特征进行一次平滑和提取，为后续更深层的语义理解打好基础。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;We feed the convolutional layer outputs to the &lt;strong&gt;bidirectional encoder&lt;/strong&gt;, yielding hidden states and cell states for both the forwards ($\rightarrow$) and backwards ($\leftarrow$) LSTMs. The forwards and backwards versions are concatenated to give hidden states $\mathbf{h}_i^{\text{enc}}$ and cell states $\mathbf{c}_i^{\text{enc}}$:&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;接下来，这些被初步加工过的特征向量正式进入了核心组件——&lt;strong&gt;双向编码器&lt;/strong&gt;（bidirectional encoder）。这里其实包含了两条流水线：一条是前向 LSTM（在数学符号中用向右的箭头 $\rightarrow$ 表示），它顺着我们阅读的习惯，从第一个词读到最后一个词，负责收集每个词左侧的“上文”信息；另一条是后向 LSTM（用向左的箭头 $\leftarrow$ 表示），它逆着顺序，从最后一个词倒着读回来，负责收集每个词右侧的“下文”信息。当这两条流水线各自运转完毕后，对于句子中的任意第 $i$ 个位置，我们就得到了两个不同视角的隐藏状态 $\mathbf{h}_i^{\text{enc}}$ 和细胞状态 $\mathbf{c}_i^{\text{enc}}$，前向( $\overrightarrow{\mathbf{h}_i^{\text{enc}}}$ , $\overrightarrow{\mathbf{c}_i^{\text{enc}}}$ )或后向( $\overleftarrow{\mathbf{h}_i^{\text{enc}}}$ , $\overleftarrow{\mathbf{c}_i^{\text{enc}}}$ )分别的状态量，维度都是 $h \times 1$ 。&lt;/p&gt;

 &lt;/blockquote&gt;
$$
\mathbf{h}_i^{\text{enc}} = [\overleftarrow{\mathbf{h}_i^{\text{enc}}} ; \overrightarrow{\mathbf{h}_i^{\text{enc}}}] \quad \text{where} \quad \mathbf{h}_i^{\text{enc}} \in \mathbb{R}^{2h \times 1}, \overleftarrow{\mathbf{h}_i^{\text{enc}}}, \overrightarrow{\mathbf{h}_i^{\text{enc}}} \in \mathbb{R}^{h \times 1} \quad 1 \leq i \leq m \quad (1)
$$$$
\mathbf{c}_i^{\text{enc}} = [\overleftarrow{\mathbf{c}_i^{\text{enc}}} ; \overrightarrow{\mathbf{c}_i^{\text{enc}}}] \quad \text{where} \quad \mathbf{c}_i^{\text{enc}} \in \mathbb{R}^{2h \times 1}, \overleftarrow{\mathbf{c}_i^{\text{enc}}}, \overrightarrow{\mathbf{c}_i^{\text{enc}}} \in \mathbb{R}^{h \times 1} \quad 1 \leq i \leq m \quad (2)
$$
 &lt;blockquote&gt;
 &lt;p&gt;$[ ; ]$ 在线性代数中通常表示向量或矩阵的拼接。&lt;strong&gt;为了让第 $i$ 个位置最终的表示既包含左侧上下文，又包含右侧上下文&lt;/strong&gt;，模型将同一时刻 $i$ 的前向状态和后向状态直接“拼接”在一起。因为前向和后向状态都是 $h \times 1$ 的列向量，将它们沿行方向拼接后，最终的&lt;strong&gt;联合隐藏状态&lt;/strong&gt; $\mathbf{h}_i^{\text{enc}}$ 和&lt;strong&gt;联合细胞状态&lt;/strong&gt; $\mathbf{c}_i^{\text{enc}}$ 的维度就翻倍了，变成了 $(h+h) \times 1 = \mathbf{2h \times 1}$。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;We then initialize the &lt;strong&gt;decoder&lt;/strong&gt;&amp;rsquo;s first hidden state $\mathbf{h}_0^{\text{dec}}$ and cell state $\mathbf{c}_0^{\text{dec}}$ with a linear projection of the encoder&amp;rsquo;s final hidden state and final cell state.$^2$&lt;/p&gt;
&lt;p&gt;$^2$ : If it’s not obvious, think about why we regard $[\overleftarrow{\mathbf{h}_1^{\text{enc}}} , \overrightarrow{\mathbf{h}_m^{\text{enc}}}]$ as the ‘final hidden state’ of the Encoder.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;双向编码器（Encoder）已经工作完毕，看完了整句源语言文本，并在每一个位置都留下了浓缩的上下文信息。接下来的任务就是把这些信息传递给&lt;strong&gt;解码器&lt;/strong&gt;（Decoder），让它开始生成翻译。传递的过程就是通过下面公式中，&lt;strong&gt;初始化解码器&lt;/strong&gt;在第0步的隐藏状态 $\mathbf{h}_0^{\text{dec}}$ 和细胞状态 $\mathbf{c}_0^{\text{dec}}$ 来完成的。解码器的初始状态是从编码器那里继承来的“最终状态”。&lt;/p&gt;

 &lt;/blockquote&gt;
$$
\mathbf{h}_0^{\text{dec}} = \mathbf{W}_h [\overleftarrow{\mathbf{h}_1^{\text{enc}}} ; \overrightarrow{\mathbf{h}_m^{\text{enc}}}] \quad \text{where} \quad \mathbf{h}_0^{\text{dec}} \in \mathbb{R}^{h \times 1}, \mathbf{W}_h \in \mathbb{R}^{h \times 2h} \quad (3)
$$$$
\mathbf{c}_0^{\text{dec}} = \mathbf{W}_c [\overleftarrow{\mathbf{c}_1^{\text{enc}}} ; \overrightarrow{\mathbf{c}_m^{\text{enc}}}] \quad \text{where} \quad \mathbf{c}_0^{\text{dec}} \in \mathbb{R}^{h \times 1}, \mathbf{W}_c \in \mathbb{R}^{h \times 2h} \quad (4)
$$
 &lt;blockquote&gt;
 &lt;p&gt;编码器在“读完”整句话后的最终状态，可见公式：前向 LSTM 是顺着语序从左到右读的，所以当它读完最后一个词时，它的最终状态自然就落在了句尾，也就是第 $m$ 个位置，记作 $\overrightarrow{\mathbf{h}_m^{\text{enc}}}$。相反，后向 LSTM 是倒着从右向左读的，它“通读全文”后的最后一站其实是句子的开头，也就是第 $1$ 个位置，记作 $\overleftarrow{\mathbf{h}_1^{\text{enc}}}$。&lt;/p&gt;
&lt;p&gt;为了把前向和后向这两股贯穿全文的“记忆”汇聚起来，我们按照公式将它们进行了拼接操作 $[\overleftarrow{\mathbf{h}_1^{\text{enc}}} ; \overrightarrow{\mathbf{h}_m^{\text{enc}}}]$。（需注意，在Decoder这一步和公式 (1) (2) 的上下文状态量不一样）&lt;/p&gt;
&lt;p&gt;通过这个拼接，我们得到了一个维度为 $2h \times 1$ 的长向量。同样地，对于细胞状态 $\mathbf{c}$，我们用完全相同的逻辑提取出 $[\overleftarrow{\mathbf{c}_1^{\text{enc}}} ; \overrightarrow{\mathbf{c}_m^{\text{enc}}}]$。那么对于 $2h \times 1$ 的向量，我们要将其转换成 $h\times 1$ 才能满足解码器的隐藏层容量。&lt;/p&gt;
&lt;p&gt;所以引入权重（投影）矩阵，用来做线性投影(linear projection)，维度是 $h \times 2h$ ，再通过矩阵相乘，映射出 $h\times 1$ 的向量。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;With the decoder initialized, we must now feed it a target sentence.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;成功初始化了解码器的第 0 步状态（$\mathbf{h}_0^{\text{dec}}$ 和 $\mathbf{c}_0^{\text{dec}}$）之后。现在，解码器已经准备好生成（或在训练时接收）目标语言的句子了。下面内容讲的就是在任意的第 $t$ 步，解码器是如何“吃进”数据并更新自己状态的。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;On the $t^{th}$ step, we look up the embedding for the $t^{th}$ subword, $\mathbf{y}_t \in \mathbb{R}^{e \times 1}$.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;要让解码器在第 $t$ 步进行工作，我们首先得给它提供当前的输入词，同样需要查表，把目标语言的第 $t$ 个子词（subword）变成一个维度为 $e$ 的词嵌入向量，记作 $\mathbf{y}_t$。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;We then concatenate $\mathbf{y}_t$ with the &lt;em&gt;combined-output vector&lt;/em&gt; $\mathbf{o}_{t-1} \in \mathbb{R}^{h \times 1}$ from the previous timestep (we will explain what this is later down this page!) to produce $\overline{\mathbf{y}_t} \in \mathbb{R}^{(e+h) \times 1}$.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;但是，如果只把 $\mathbf{y}_t$ 喂给解码器，它会缺乏连贯性。为了让解码器知道“我刚才干了什么”，文本中引入了一个非常关键的设计：把当前的词向量 $\mathbf{y}_t$ 与上一步（第 $t-1$ 步）的维度为 $h$ 的&lt;strong&gt;联合输出向量 (combined-output vector)&lt;/strong&gt; $\mathbf{o}_{t-1}$ 拼接在一起，得到新的输入向量 $\overline{\mathbf{y}_t}$，它的维度变成了 $e+h$。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;Note that for the first target subword (i.e. the start token) $\mathbf{o}_0$ is a zero-vector. We then feed $\overline{\mathbf{y}_t}$ as input to the decoder.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;注意到当 $t=1$ ，也就是解码器刚开始吐出第一个词（或者接收 start token）时，因为前面还没有任何输出，所以初始的 $\mathbf{o}_0$ 就简单地设定为一个全零向量。&lt;/p&gt;

 &lt;/blockquote&gt;
$$
\mathbf{h}_t^{\text{dec}}, \mathbf{c}_t^{\text{dec}} = \text{Decoder}(\overline{\mathbf{y}_t}, \mathbf{h}_{t-1}^{\text{dec}}, \mathbf{c}_{t-1}^{\text{dec}}) \quad \text{where} \quad \mathbf{h}_t^{\text{dec}} \in \mathbb{R}^{h \times 1}, \mathbf{c}_t^{\text{dec}} \in \mathbb{R}^{h \times 1} \quad (5)
$$
 &lt;blockquote&gt;
 &lt;p&gt;关于向Decoder中输入了三个变量后，在LSTM的Decoder里进行了什么计算，需结合&lt;a class="link" href="https://blog.kawausococo.top/p/cs224n/" target="_blank" rel="noopener"
 &gt;CS224N&lt;/a&gt;中LSTM的部分进行理解：(generated by Gemini)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;遗忘门 (Forget Gate)
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;参与者：&lt;/strong&gt; 新输入$\overline{\mathbf{y}_t}$和旧的短期记忆$\mathbf{h}_{t-1}^{\text{dec}}$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动作：&lt;/strong&gt; 把这两个变量拼接在一起，乘上一个权重矩阵，加上偏置，然后送入一个 &lt;strong&gt;Sigmoid 激活函数&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;产出：&lt;/strong&gt; 得到一个介于 0 到 1 之间的向量 $f_t$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;意义：&lt;/strong&gt; 这个 $f_t$ 会盯着传送带上的旧长期记忆 $\mathbf{c}_{t-1}^{\text{dec}}$ 看。0 代表“彻底忘掉”，1 代表“完全保留”。比如遇到新的主语，它可能就会决定忘掉旧主语的单复数信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;输入门与候选记忆 (Input Gate &amp;amp; Candidate Memory)
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;参与者：&lt;/strong&gt; 依然是 $\overline{\mathbf{y}_t}$ 和 $\mathbf{h}_{t-1}^{\text{dec}}$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动作：&lt;/strong&gt; 这里分两头行动：
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;输入门 $i_t$：&lt;/strong&gt; 再次经过一个 Sigmoid 函数，产生 0 到 1 的值，决定我们&lt;strong&gt;有多希望&lt;/strong&gt;把新信息存进去。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;候选记忆 $\tilde{\mathbf{c}}_t$：&lt;/strong&gt; 经过一个 &lt;strong&gt;tanh 激活函数&lt;/strong&gt;，产生 -1 到 1 的值，这代表从当前输入中提取出的&lt;strong&gt;全部潜在新知识&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;意义：&lt;/strong&gt; 这两步结合，就是要把提炼出的新知识（$\tilde{\mathbf{c}}_t$）按照我们的渴望程度（$i_t$）进行打包，准备放到传送带上。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;状态更新 (Cell State Update)
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;参与者：&lt;/strong&gt; 旧记忆 $\mathbf{c}_{t-1}^{\text{dec}}$，遗忘门 $f_t$，输入门 $i_t$，候选记忆 $\tilde{\mathbf{c}}_t$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动作：&lt;/strong&gt; 纯粹的数学运算。首先用旧记忆乘以遗忘门：$f_t * \mathbf{c}_{t-1}^{\text{dec}}$ （执行“丢弃”动作）；然后加上新的包裹：$i_t * \tilde{\mathbf{c}}_t$ （执行“装载”动作）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;产出：&lt;/strong&gt; 我们得到了&lt;strong&gt;当前时刻的全新细胞状态 $\mathbf{c}_t^{\text{dec}}$&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;意义：&lt;/strong&gt; 长期记忆的传送带在这里完成了向前推进，旧的糟粕被剔除，新的信息被注入。这就是 LSTM 能够跨越长距离保持梯度的核心。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;输出门与隐藏状态 (Output Gate &amp;amp; Hidden State)
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;参与者：&lt;/strong&gt; $\overline{\mathbf{y}_t}$，$\mathbf{h}_{t-1}^{\text{dec}}$，以及刚刚出炉的新细胞状态 $\mathbf{c}_t^{\text{dec}}$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动作：&lt;/strong&gt;
&lt;ol&gt;
&lt;li&gt;用 $\overline{\mathbf{y}_t}$ 和 $\mathbf{h}_{t-1}^{\text{dec}}$ 经过 Sigmoid 算出一个输出门 $o_t$（决定展示的比例）。&lt;/li&gt;
&lt;li&gt;把刚刚做好的新细胞状态 $\mathbf{c}_t^{\text{dec}}$ 用 tanh 函数“压”到 -1 到 1 之间。&lt;/li&gt;
&lt;li&gt;两者相乘：$\mathbf{h}_t^{\text{dec}} = o_t * \tanh(\mathbf{c}_t^{\text{dec}})$。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;产出：&lt;/strong&gt; 我们得到了&lt;strong&gt;当前时刻的全新隐藏状态 $\mathbf{h}_t^{\text{dec}}$&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;意义：&lt;/strong&gt; 细胞状态 $\mathbf{c}_t$ 包含的信息太庞杂了（有些可能是给未来留的伏笔），我们不能全部暴露。输出门 $o_t$ 就像一个滤网，只把当前这一步预测下一个词&lt;strong&gt;最需要&lt;/strong&gt;的那部分特征提取出来，作为对外的公开展示（$\mathbf{h}_t$）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

 &lt;/blockquote&gt;
&lt;p&gt;We then use $\mathbf{h}_t^{\text{dec}}$ to compute multiplicative attention over $\mathbf{h}_1^{\text{enc}}, \dots, \mathbf{h}_m^{\text{enc}}$:
&lt;/p&gt;
$$
\mathbf{e}_{t,i} = (\mathbf{h}_t^{\text{dec}})^T \mathbf{W}_{\text{attProj}} \mathbf{h}_i^{\text{enc}} \quad \text{where} \quad \mathbf{e}_t \in \mathbb{R}^{m \times 1}, \mathbf{W}_{\text{attProj}} \in \mathbb{R}^{h \times 2h} \quad 1 \leq i \leq m \quad (6)
$$&lt;p&gt;
$\mathbf{e}_{t,i}$ is a scalar, the $i$ th element of $\mathbf{e}_t \in \mathbb{R}^{m \times 1}$, computed using the hidden state of the decoder at the $t$ th step, $\mathbf{h}_t^{\text{dec}} \in \mathbb{R}^{h \times 1}$, the attention projection $\mathbf{W}_{\text{attProj}} \in \mathbb{R}^{h \times 2h}$, and the hidden state of the encoder at the $i$ th step, $\mathbf{h}_i^{\text{enc}} \in \mathbb{R}^{2h \times 1}$.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;公式(6)是注意力机制的Scoring，解码器现在的状态是 $\mathbf{h}_t^{\text{dec}}$，它需要和字典里的每一个词 $\mathbf{h}_i^{\text{enc}}$进行比对，看看有多匹配。&lt;/p&gt;
&lt;p&gt;$(\mathbf{h}_t^{\text{dec}})^T$ , $\mathbf{W}_{\text{attProj}}$ , $\mathbf{h}_i^{\text{enc}}$ 这三者相乘后维度为 $1\times 1$ ，即Scoring出的标量 $\mathbf{e}_{t,i}$ ，它代表了在生成第 $t$ 个翻译词时，源语言句子中第 $i$ 个词的重要程度。 我们把对所有 $m$ 个词的打分收集起来，就得到了一个长度为 $m$ 的得分向量 $\mathbf{e}_t$ 。&lt;/p&gt;

 &lt;/blockquote&gt;
$$
\alpha_t = \text{softmax}(\mathbf{e}_t) \quad \text{where} \quad \alpha_t \in \mathbb{R}^{m \times 1} \quad (7)
$$
 &lt;blockquote&gt;
 &lt;p&gt;为了把公式 (6) 的得分变成标准的“注意力分配比例”，公式 (7) 引入了 &lt;code&gt;softmax&lt;/code&gt; 函数。它把向量 $\mathbf{e}_t$ 里的所有数字全部转换为 $0$ 到 $1$ 之间的正数，并且保证它们的&lt;strong&gt;总和严格等于 $1$&lt;/strong&gt;。转换后的结果就是 $\alpha_t$（被称为注意力分布 Attention Distribution）。&lt;/p&gt;

 &lt;/blockquote&gt;
$$
\mathbf{a}_t = \sum_{i=1}^m \alpha_{t,i} \mathbf{h}_i^{\text{enc}} \quad \text{where} \quad \mathbf{a}_t \in \mathbb{R}^{2h \times 1} \quad (8)
$$
 &lt;blockquote&gt;
 &lt;p&gt;公式 (8) 用刚算出来的注意力百分比 $\alpha_{t,i}$，去对字典里的词 $\mathbf{h}_i^{\text{enc}}$ 进行&lt;strong&gt;加权求和&lt;/strong&gt;。把这 $m$ 个按比例缩放的向量全部加起来，我们就得到了最终的&lt;strong&gt;注意力输出向量&lt;/strong&gt;（Attention Output / Context Vector），记作 $\mathbf{a}_t$。
由于被加和的 $\mathbf{h}_i^{\text{enc}}$ 都是 $2h \times 1$ 维的，所以最终得到的 $\mathbf{a}_t$ 也是 &lt;strong&gt;$2h \times 1$&lt;/strong&gt; 维。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;We now concatenate the attention output $\mathbf{a}_t$ with the decoder hidden state $\mathbf{h}_t^{\text{dec}}$ and pass this through a linear layer, tanh, and dropout to attain the &lt;em&gt;combined-output&lt;/em&gt; vector $\mathbf{o}_t$.
&lt;/p&gt;
$$
\mathbf{u}_t = [\mathbf{a}_t ; \mathbf{h}_t^{\text{dec}}] \quad \text{where} \quad \mathbf{u}_t \in \mathbb{R}^{3h \times 1} \quad (9)
$$
 &lt;blockquote&gt;
 &lt;p&gt;将注意力输出向量和解码器隐藏状态结合，得到维度为 $3h\times 1$ 的 $u_t$&lt;/p&gt;

 &lt;/blockquote&gt;
$$
\mathbf{v}_t = \mathbf{W}_u \mathbf{u}_t \quad \text{where} \quad \mathbf{v}_t \in \mathbb{R}^{h \times 1}, \mathbf{W}_u \in \mathbb{R}^{h \times 3h} \quad (10)
$$
 &lt;blockquote&gt;
 &lt;p&gt;加入了一个线性投影矩阵 $\mathbf{W}_u$（维度是 $h \times 3h$ ，当它和 $\mathbf{u}_t$ 相乘时，把维度压缩回了标准尺寸 $h \times 1$&lt;/p&gt;

 &lt;/blockquote&gt;
$$
\mathbf{o}_t = \text{dropout}(\tanh(\mathbf{v}_t)) \quad \text{where} \quad \mathbf{o}_t \in \mathbb{R}^{h \times 1} \quad (11)
$$
 &lt;blockquote&gt;
 &lt;p&gt;首先给它套上一个 &lt;code&gt;tanh&lt;/code&gt; 激活函数，将其内部的数值平滑地压缩到 -1 到 1 之间，这赋予了模型非线性的表达能力。紧接着，再让它穿过一层 &lt;code&gt;dropout&lt;/code&gt;。&lt;code&gt;dropout&lt;/code&gt; 是一种防止模型过拟合的正则化技术，它在训练时会随机屏蔽掉一部分神经元，逼迫模型学到更鲁棒、更泛化的特征。&lt;/p&gt;
&lt;p&gt;最后得到&lt;strong&gt;联合输出向量&lt;/strong&gt;(combined-output vector) $\mathbf{o}_t$&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;Then, we produce a probability distribution $\mathbf{P}_t$ over target subwords at the $t^{th}$ timestep:
&lt;/p&gt;
$$
\mathbf{P}_t = \text{softmax}(\mathbf{W}_{\text{vocab}}\mathbf{o}_t) \quad \text{where} \quad \mathbf{P}_t \in \mathbb{R}^{V_t \times 1}, \mathbf{W}_{\text{vocab}} \in \mathbb{R}^{V_t \times h} \quad (12)
$$
 &lt;blockquote&gt;
 &lt;p&gt;我们要把一个 $h$ 维的向量，变成目标语言词典里的一个具体词汇。我们的目标语言词典（Vocabulary）里一共有 $V_t$ 个单词。公式 (12) 引入了一个最终的变换矩阵 $\mathbf{W}_{\text{vocab}}$，它的维度是 $V_t \times h$。相乘后输出一个 $[V_t \times 1]$ 的列向量。这个向量里的每一个数字，就代表了模型对词典里对应单词的“打分”（Logits）。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;Here, $V_t$ is the size of the target vocabulary. Finally, to train the network we then compute the cross entropy loss between $\mathbf{P}_t$ and $\mathbf{g}_t$, where $\mathbf{g}_t$ is the one-hot vector of the target subword at timestep $t$:
&lt;/p&gt;
$$
J_t(\theta) = \text{CrossEntropy}(\mathbf{P}_t, \mathbf{g}_t) \quad (13)
$$
 &lt;blockquote&gt;
 &lt;p&gt;训练过程使用了&lt;strong&gt;交叉熵损失 (Cross Entropy Loss)&lt;/strong&gt; 函数。简单来说，交叉熵会去对比 $\mathbf{P}_t$ 和 $\mathbf{g}_t$ (one-hot vector)之间的差距。计算出损失 $J_t(\theta)$ 之后，在接下来的代码实现中，模型就会利用反向传播机制，顺着网络一路往回找，去微调那些导致错误的参数 $\theta$。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="implementation-and-written-questions"&gt;Implementation and written questions
&lt;/h3&gt;&lt;h4 id="__init__"&gt;&lt;code&gt;__init__()&lt;/code&gt;
&lt;/h4&gt;&lt;p&gt;在问题 (c) 的&lt;code&gt;nmt_model.py&lt;/code&gt;的&lt;code&gt;__inti__()&lt;/code&gt;中，关于神经网络各层的定义，其中&lt;code&gt;self.att_projection&lt;/code&gt;的定义需注意，虽然它对应的是 $W_{attProj}$ ，但是在编写代码时，要拆解结合律，决定计算顺序。&lt;/p&gt;
&lt;p&gt;应该先算$\mathbf{W}_{\text{attProj}} $ 和 $\mathbf{h}_i^{\text{enc}}$ 相乘，再将其乘积与 $(\mathbf{h}_t^{dec})^T$ 相乘求最终结果，这是因为所有的编码器状态 $\mathbf{h}_i^{\text{enc}}$ 在 Decoder 开始工作前就已经全部计算好了。你可以&lt;strong&gt;一次性&lt;/strong&gt;把整句话的 $\mathbf{h}^{\text{enc}}$ 丢进 &lt;code&gt;Linear&lt;/code&gt; 层进行投影（这叫 Pre-computation 预计算）。但是在某一时刻只有一个 $\mathbf{h}_t^{dec}$ 而没有未来的量，导致不能一次性投影完成，而只能把线性层放到了循环中，引发严重的性能问题（虽然数学原理上没错）。下面是一个例子(generated by Gemini) :&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;假设我们的模型参数如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;隐藏层大小 $h = 512$&lt;/li&gt;
&lt;li&gt;句子长度 $m = 50$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;把 &lt;code&gt;Linear&lt;/code&gt; 层塞进循环里&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在这个路线里，&lt;code&gt;Linear&lt;/code&gt; 层每次要干的活是：把解码器 $1 \times 512$ 的向量，乘以一个 $512 \times 1024$ 的巨大投影矩阵 $\mathbf{W}$。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;计算量&lt;/strong&gt;：这需要进行 $512 \times 1024 = \mathbf{524,288}$ 次乘加运算！&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;额外开销&lt;/strong&gt;：每次循环，PyTorch 都要去内存里把这个包含 50 多万个参数的 $\mathbf{W}$ 矩阵重新搬出来读一遍，并且要经过一层完整的 &lt;code&gt;nn.Linear&lt;/code&gt; 封装代码（包含各种维度检查、梯度跟踪的准备）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然后，算出投影结果后，还得再去和字典做点积：$1 \times 1024$ 的向量乘以 $1024 \times 50$ 的字典，又是 $\mathbf{51,200}$ 次运算。&lt;/p&gt;
&lt;p&gt;所以在这个路线下，解码器每走一步，都要背着将近 &lt;strong&gt;60万次运算&lt;/strong&gt; 和一个巨大的矩阵跑。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;预计算 + 循环内纯点积&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我们把庞大的 &lt;code&gt;Linear&lt;/code&gt; 投影放在循环外面，利用 GPU 的超级并行能力，**“一瞬间”**把整本字典从 $50 \times 1024$ 压成了 $50 \times 512$。我们把这本新字典称为 &lt;strong&gt;“投影后字典”&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;现在我们进入了 &lt;code&gt;for&lt;/code&gt; 循环。在第 $t$ 步，我们需要做的乘法是什么呢？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;计算量&lt;/strong&gt;：我们直接拿解码器原生的 $1 \times 512$ 向量，去乘以准备好的“投影后字典”（$512 \times 50$）。只需要进行 $512 \times 50 = \mathbf{25,600}$ 次乘加运算！&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;没有额外的层开销&lt;/strong&gt;：这里不需要调用 &lt;code&gt;nn.Linear&lt;/code&gt;，我们在代码里只要写一个极其轻量的纯矩阵乘法指令（比如 &lt;code&gt;torch.matmul&lt;/code&gt; 或 &lt;code&gt;@&lt;/code&gt; 符号）就搞定了。不需要加载任何权重矩阵，因为权重矩阵的任务在循环外已经完成了！&lt;/li&gt;
&lt;/ul&gt;

 &lt;/blockquote&gt;
&lt;h4 id="encode"&gt;&lt;code&gt;encode&lt;/code&gt;
&lt;/h4&gt;&lt;p&gt;问题 (d) 是encoder部分，以及对decoder部分的初始化：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model_embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ModelEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embed_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vocab&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;self.model_embeddings&lt;/code&gt;是对&lt;code&gt;ModelEmbeddings&lt;/code&gt;的一个实例化，而回到问题 (a) 发现&lt;code&gt;.source&lt;/code&gt;是一个&lt;code&gt;nn.Embedding&lt;/code&gt;对象：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_embeddings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vocab&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="n"&gt;embedding_dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embed_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;padding_idx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;src_pad_token_idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;但是在问题 (a) 中只是对它的实例化，在内存中申请了一个形状为 &lt;code&gt;(num_embeddings, embedding_dim)&lt;/code&gt; 的&lt;strong&gt;大矩阵&lt;/strong&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model_embeddings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_padded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;又因为 &lt;code&gt;nn.Embedding&lt;/code&gt; 继承自 &lt;code&gt;nn.Module&lt;/code&gt;，而 &lt;code&gt;nn.Module&lt;/code&gt; 重写了 &lt;code&gt;__call__&lt;/code&gt; 方法，所以你可以像函数一样使用它（调用），参数设为&lt;code&gt;source_padded&lt;/code&gt;这样一个Tensor，它把 Tensor 里的每一个数字当成“行号”，去刚才创建的那个大矩阵里把对应的行找出来，然后输出包含词向量的 Tensor。&lt;/p&gt;
&lt;p&gt;下面在将Tensor送进Encoder之前，要用&lt;code&gt;torch.permute&lt;/code&gt;变换形状，从&lt;code&gt;(src_len, b, e)&lt;/code&gt;变到&lt;code&gt;(b, e, src_len)&lt;/code&gt;。那么&lt;code&gt;torch.permute&lt;/code&gt;的参数，只是参数位置（索引号）。如下，将原来索引为1的&lt;code&gt;b&lt;/code&gt;放到第0个位置，原来索引为2的放到第1个位置，原来索引为0的放到第2个位置。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;之后将Tensor &lt;code&gt;X&lt;/code&gt;送进LSTM的Encoder后，得到输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,(&lt;/span&gt;&lt;span class="n"&gt;last_hidden&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;last_cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;output&lt;/code&gt;对应的就是双向LSTM的全局上下文向量： $\mathbf{h}_1^{\text{enc}}, \ldots, \mathbf{h}_m^{\text{enc}}$&lt;/p&gt;
&lt;p&gt;&lt;code&gt;last_hidden&lt;/code&gt;对应的是：$\overrightarrow{\mathbf{h}_m^{\text{enc}}}$ 和 $\overleftarrow{\mathbf{h}_1^{\text{enc}}}$ ，它的形状是 &lt;code&gt;(num_layers * num_directions, batch_size, hidden_size)&lt;/code&gt; ，由于它是双向的，所以索引0和索引1分别对应，我们需要将其分离再拼接，形成我们需要的$\overleftarrow{\mathbf{h}_1^{\text{enc}}} ; \overrightarrow{\mathbf{h}_m^{\text{enc}}}$ 。注意&lt;code&gt;torch.cat&lt;/code&gt;的参数&lt;code&gt;dim=1&lt;/code&gt;表示为横向拼接，即拼接好的向量的列数变成了&lt;code&gt;2 * hidden_size&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;与投影矩阵相乘的部分，由实例化好的线性层&lt;code&gt;self.h_projection&lt;/code&gt;完成。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;init_decoder_hidden&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;h_projection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;last_hidden&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="n"&gt;last_hidden&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;&lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;last_cell&lt;/code&gt;同理，不再赘述。&lt;/p&gt;
&lt;h4 id="decode"&gt;&lt;code&gt;decode&lt;/code&gt;
&lt;/h4&gt;&lt;p&gt;问题 (e) 是decode部分：
&lt;code&gt;enc_hiddens_proj = self.att_projection(enc_hiddens)&lt;/code&gt; 对应公式 (6) 中 $\mathbf{W}_{\text{attProj}} \mathbf{h}_i^{\text{enc}}$ 的部分。正如在 &lt;code&gt;__init__()&lt;/code&gt; 部分讨论的，编码器的全部隐藏状态在进入 &lt;code&gt;decode&lt;/code&gt; 时已经固定，所以可以&lt;strong&gt;在循环外一次性&lt;/strong&gt;完成投影预计算，将 &lt;code&gt;enc_hiddens&lt;/code&gt; 从 &lt;code&gt;(b, src_len, 2h)&lt;/code&gt; 投影为 &lt;code&gt;(b, src_len, h)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Y = self.model_embeddings.target(target_padded)&lt;/code&gt; 和 &lt;code&gt;encode&lt;/code&gt; 中对源语言做查表的操作对称，这里调用&lt;strong&gt;目标语言&lt;/strong&gt;的嵌入矩阵，将 &lt;code&gt;target_padded&lt;/code&gt; 中的词索引映射成词向量，得到形状为 &lt;code&gt;(tgt_len, b, e)&lt;/code&gt; 的张量 &lt;code&gt;Y&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;torch.split(Y, 1)&lt;/code&gt; 沿第 0 维（时间维度）将 &lt;code&gt;Y&lt;/code&gt; 切成一系列 &lt;code&gt;(1, b, e)&lt;/code&gt; 的张量。&lt;code&gt;squeeze(Y_t, 0)&lt;/code&gt; 去掉多余的维度变为 &lt;code&gt;(b, e)&lt;/code&gt;。注意&lt;strong&gt;必须指定 &lt;code&gt;dim=0&lt;/code&gt;&lt;/strong&gt;，否则当 &lt;code&gt;batch_size = 1&lt;/code&gt; 时会误删 batch 维度。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;torch.cat((Y_t, o_prev), dim=1)&lt;/code&gt; 对应前文的拼接操作：将 $\mathbf{y}_t$（维度 $e$）与 $\mathbf{o}_{t-1}$（维度 $h$）拼接为 $\overline{\mathbf{y}_t} \in \mathbb{R}^{(e+h)}$。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;self.step(...)&lt;/code&gt; 内部一次性完成公式 (5)-(11) 的计算：Decoder LSTM 前向传播 → 注意力评分与分布 → 加权求和得到上下文向量 → 线性投影、tanh、dropout，最终产出联合输出向量 $\mathbf{o}_t$。每步将 $\mathbf{o}_t$ 存入列表并更新 &lt;code&gt;o_prev&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;torch.stack(combined_outputs, dim=0)&lt;/code&gt; 将列表中所有 &lt;code&gt;(b, h)&lt;/code&gt; 的张量堆叠为 &lt;code&gt;(tgt_len, b, h)&lt;/code&gt;，随后送入公式 (12) 的词汇投影层生成概率分布。&lt;/p&gt;
&lt;h4 id="step"&gt;&lt;code&gt;step&lt;/code&gt;
&lt;/h4&gt;&lt;p&gt;问题 (f) 是step部分，即解码器单步计算，内部完成公式 (5)-(11)。核心代码分两段：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一段（~3行）：Decoder LSTM 前向 + 注意力评分&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dec_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ybar_t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dec_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dec_hidden&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dec_cell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dec_state&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;e_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bmm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enc_hiddens_proj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unsqueeze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dec_hidden&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;squeeze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;前两行直接对应公式 (5)：将 $\overline{\mathbf{y}_t}$ 和上一步状态送入 Decoder LSTM，得到新的 &lt;code&gt;dec_state&lt;/code&gt;，再拆分为 &lt;code&gt;dec_hidden&lt;/code&gt;（$\mathbf{h}_t^{\text{dec}}$）和 &lt;code&gt;dec_cell&lt;/code&gt;（$\mathbf{c}_t^{\text{dec}}$），形状均为 &lt;code&gt;(b, h)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;第三行对应公式 (6) 中的注意力评分。这里的关键在于 &lt;code&gt;torch.bmm&lt;/code&gt; 的形状要求——它执行&lt;strong&gt;批量矩阵乘法&lt;/strong&gt;，要求输入严格为三维张量 &lt;code&gt;(b, n, m)&lt;/code&gt; × &lt;code&gt;(b, m, p)&lt;/code&gt; → &lt;code&gt;(b, n, p)&lt;/code&gt;。而我们手里的张量：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;enc_hiddens_proj&lt;/code&gt;：形状 &lt;code&gt;(b, src_len, h)&lt;/code&gt; — 已经是三维，无需处理&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dec_hidden&lt;/code&gt;：形状 &lt;code&gt;(b, h)&lt;/code&gt; — 只有二维，不满足 &lt;code&gt;bmm&lt;/code&gt; 的要求&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以需要 &lt;code&gt;torch.unsqueeze(dec_hidden, 2)&lt;/code&gt; 在第 2 维（最后）插入一个维度，将 &lt;code&gt;(b, h)&lt;/code&gt; 变为 &lt;code&gt;(b, h, 1)&lt;/code&gt;。这样 &lt;code&gt;bmm&lt;/code&gt; 的乘法就变成了：&lt;/p&gt;
$$\underbrace{(b, \text{src\_len}, h)}_{\text{enc\_hiddens\_proj}} \times \underbrace{(b, h, 1)}_{\text{dec\_hidden}} = \underbrace{(b, \text{src\_len}, 1)}_{e_t}$$&lt;p&gt;这实质上就是对 batch 内的每一条样本，让 &lt;code&gt;enc_hiddens_proj&lt;/code&gt; 的每一行（某个源词的投影）与 &lt;code&gt;dec_hidden&lt;/code&gt; 做点积，得到该源词的注意力分数——正是公式 (6) 的 $(\mathbf{h}_t^{\text{dec}})^T \mathbf{W}_{\text{attProj}} \mathbf{h}_i^{\text{enc}}$。&lt;/p&gt;
&lt;p&gt;最后 &lt;code&gt;.squeeze(2)&lt;/code&gt; 去掉末尾多余的维度 &lt;code&gt;1&lt;/code&gt;，从 &lt;code&gt;(b, src_len, 1)&lt;/code&gt; 变回 &lt;code&gt;(b, src_len)&lt;/code&gt;，得到注意力得分向量 $\mathbf{e}_t$。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二段（~6行）：注意力加权 → 联合输出&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;alpha_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;softmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e_t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;a_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bmm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unsqueeze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alpha_t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;enc_hiddens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;squeeze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;U_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;a_t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dec_hidden&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;V_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;combined_output_projection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;U_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;O_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dropout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tanh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;V_t&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;F.softmax(e_t, dim=1)&lt;/code&gt; 对应公式 (7)，沿 &lt;code&gt;dim=1&lt;/code&gt;（即 &lt;code&gt;src_len&lt;/code&gt; 维度）做 softmax，将分数归一化为注意力分布 $\alpha_t$，形状仍为 &lt;code&gt;(b, src_len)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;计算上下文向量 $\mathbf{a}_t$（公式 (8)）时又遇到了 &lt;code&gt;bmm&lt;/code&gt; 的三维要求。&lt;code&gt;alpha_t&lt;/code&gt; 是 &lt;code&gt;(b, src_len)&lt;/code&gt;，需要 &lt;code&gt;unsqueeze(alpha_t, 1)&lt;/code&gt; 在第 1 维插入，变为 &lt;code&gt;(b, 1, src_len)&lt;/code&gt;：&lt;/p&gt;
$$\underbrace{(b, 1, \text{src_len})}_{\alpha_t} \times \underbrace{(b, \text{src_len}, 2h)}_{\text{enc_hiddens}} = \underbrace{(b, 1, 2h)}_{a_t}$$&lt;p&gt;这就是用注意力权重对编码器隐藏状态做加权求和。&lt;code&gt;.squeeze(1)&lt;/code&gt; 去掉中间的 &lt;code&gt;1&lt;/code&gt;，得到 &lt;code&gt;(b, 2h)&lt;/code&gt; 的上下文向量 $\mathbf{a}_t$。&lt;/p&gt;
&lt;p&gt;后三行依次对应公式 (9)(10)(11)：&lt;code&gt;torch.cat&lt;/code&gt; 拼接 $\mathbf{a}_t$ 与 $\mathbf{h}_t^{\text{dec}}$ 得到 &lt;code&gt;(b, 3h)&lt;/code&gt; 的 $\mathbf{u}_t$；线性层投影回 &lt;code&gt;(b, h)&lt;/code&gt; 的 $\mathbf{v}_t$；最后 tanh + dropout 得到联合输出 $\mathbf{o}_t$。&lt;/p&gt;
&lt;h4 id="attention-masking"&gt;Attention Masking
&lt;/h4&gt;&lt;p&gt;问题 (g) 是关于 &lt;code&gt;step()&lt;/code&gt; 中注意力掩码的作用。在 &lt;code&gt;step()&lt;/code&gt; 的两段代码之间，有这样一段：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;enc_masks&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;e_t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;masked_fill_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enc_masks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;inf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;enc_masks&lt;/code&gt; 由 &lt;code&gt;generate_sent_masks()&lt;/code&gt; 生成：它创建一个 &lt;code&gt;(b, src_len)&lt;/code&gt; 的零矩阵，然后对 batch 中每条句子，在其&lt;strong&gt;实际长度之后&lt;/strong&gt;的位置全部填 &lt;code&gt;1&lt;/code&gt;。换言之，&lt;code&gt;1&lt;/code&gt; 标记的是 &lt;code&gt;&amp;lt;pad&amp;gt;&lt;/code&gt; token 的位置，&lt;code&gt;0&lt;/code&gt; 标记的是真实词的位置。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;掩码对注意力计算的影响：&lt;/strong&gt; &lt;code&gt;masked_fill_&lt;/code&gt; 将 $\mathbf{e}_t$ 中所有对应 &lt;code&gt;&amp;lt;pad&amp;gt;&lt;/code&gt; 位置的注意力分数替换为 $-\infty$。当这些 $-\infty$ 的值随后经过 &lt;code&gt;softmax&lt;/code&gt; 时，$e^{-\infty} = 0$，因此 &lt;code&gt;&amp;lt;pad&amp;gt;&lt;/code&gt; 位置的注意力权重 $\alpha_{t,i}$ 会变为 $0$，而所有真实词的权重之和仍归一化为 $1$。这意味着在公式 (8) 的加权求和中，&lt;code&gt;&amp;lt;pad&amp;gt;&lt;/code&gt; 位置的编码器隐藏状态对上下文向量 $\mathbf{a}_t$ 完全没有贡献。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么必须这样做：&lt;/strong&gt; 由于 batch 内各句子长度不一，短句会被 &lt;code&gt;&amp;lt;pad&amp;gt;&lt;/code&gt; 填充到统一长度。如果不加掩码，&lt;code&gt;&amp;lt;pad&amp;gt;&lt;/code&gt; 对应位置的编码器隐藏状态（本质上是无意义的噪声）会分走一部分注意力权重，从而污染上下文向量，导致翻译质量下降。&lt;/p&gt;
&lt;h4 id="training"&gt;Training
&lt;/h4&gt;&lt;p&gt;问题 (h) 是在代码工作完成后，进入训练阶段。由于我没有海外支付方式，用不了cs224n指定的Google Cloud，所以使用本机显卡进行训练（速度快于文档的预估值）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bash run.sh train
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;观察&lt;code&gt;tensorboard&lt;/code&gt;可见loss曲线并无异常，待训练完成后进行测试评估：
&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n-a3/figure2.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bash run.sh &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;结果：the model’s corpus BLEU Score is larger than 18, tests passed.&lt;/p&gt;
&lt;h4 id="attention-comparison"&gt;Attention Comparison
&lt;/h4&gt;&lt;p&gt;最后问题 (i) 是点积注意力、加性注意力分别与乘性注意力进行比较：&lt;/p&gt;
&lt;h5 id="dot-product-vs-multiplicative"&gt;Dot Product vs. Multiplicative
&lt;/h5&gt;&lt;ul&gt;
&lt;li&gt;Dot Product Attention: $\mathbf{e}_{t,i} = \mathbf{s}_t^T \mathbf{h}_i$&lt;/li&gt;
&lt;li&gt;Multiplicative Attention: $\mathbf{e}_{t,i} = \mathbf{s}_t^T \mathbf{W} \mathbf{h}_i$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;点积注意力的优势：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;计算速度更快，显存占用更小。&lt;/strong&gt; 点积注意力没有任何可学习的权重矩阵 $\mathbf{W}$，它仅仅是两个向量的内积。在 GPU 上，这种纯粹的向量点积运算被优化到了极致，没有任何额外的内存开销和参数更新负担。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;点积注意力的劣势：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;强制要求维度严格一致，且表达能力较弱。&lt;/strong&gt; 要做点积，解码器状态 $\mathbf{s}_t$ 和编码器状态 $\mathbf{h}_i$ 的维度必须&lt;strong&gt;完全相同&lt;/strong&gt;。更致命的是，它假设这两个空间天然就是对齐的。而乘性注意力多了一个矩阵 $\mathbf{W}$，不仅允许两者的维度不同（$\mathbf{W}$ 可以做维度转换），还能通过学习 $\mathbf{W}$ 将它们投影到一个更好的共享特征空间中再进行比较。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id="additive-vs-multiplicative"&gt;Additive vs. Multiplicative
&lt;/h5&gt;&lt;ul&gt;
&lt;li&gt;Additive Attention: $\mathbf{e}_{t,i} = \mathbf{v}^T \tanh(\mathbf{W}_1 \mathbf{h}_i + \mathbf{W}_2 \mathbf{s}_t)$&lt;/li&gt;
&lt;li&gt;Multiplicative Attention: $\mathbf{e}_{t,i} = \mathbf{s}_t^T \mathbf{W} \mathbf{h}_i$&lt;/li&gt;
&lt;/ul&gt;

 &lt;blockquote&gt;
 &lt;p&gt;(注：Additive Attention 也就是大名鼎鼎的 Bahdanau Attention，它是 Attention 机制的开山鼻祖；而 Multiplicative 则是 Luong Attention 的核心。)&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;加性注意力的优势：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;在特征维度很大时，表现通常更好（模型容量大）。&lt;/strong&gt; 乘性注意力在维度很大时，点积的结果方差会变得极其巨大，容易把 Softmax 推向梯度消失的边缘（也就是后来 Transformer 引入缩放因子的原因）。而加性注意力通过 $\tanh$ 激活函数将内部数值稳稳地压制在 $[-1, 1]$ 之间，天然具有极好的数值稳定性，对超大维度的宽容度更高。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;加性注意力的劣势：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;计算效率较低，难以发挥底层矩阵乘法的极致加速。&lt;/strong&gt; 乘性注意力可以极其优雅地打包成一个巨大的矩阵乘法（在 Transformer 里就是 $\mathbf{Q}\mathbf{K}^T$），这正是现代 GPU 最擅长的事情。而加性注意力不仅要做两次独立的线性变换，还要过一遍非线性激活函数 $\tanh$，最后再乘一个向量 $\mathbf{v}$。这种复杂的计算图打破了矩阵乘法的纯粹性，导致它在实际工程中的运行速度明显慢于乘性注意力。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="analyzing-nmt-systems"&gt;Analyzing NMT Systems
&lt;/h2&gt;&lt;p&gt;问题 (a) 是为什么在embedding层后加1D卷积后，再输入双向encoder效果会好一些？&lt;/p&gt;
&lt;p&gt;添加一维卷积层可以作为 n-gram 特征提取器，用于捕捉局部组合性。由于中文词语通常由多个词素组成（例如，“电”+“脑”=“电脑”），一维卷积神经网络的滑动窗口会在序列建模之前，将相邻字符/子词的嵌入向量显式地组合成更高层次的语义表示（一个词或短语）。这提供了一种层级结构，其中卷积神经网络处理局部词汇语义（充当软分词器），从而使双向编码器能够专注于学习全局的、长程的句法依存关系。&lt;/p&gt;
&lt;p&gt;问题 (b) 是分析四句中文为什么翻译错了，那么作为普通话母语者不难解答：&lt;/p&gt;
&lt;p&gt;i. (2 points) &lt;strong&gt;Source Sentence:&lt;/strong&gt; 贼人其后被警方拘捕及被判处盗窃罪名成立。
&lt;strong&gt;Reference Translation:&lt;/strong&gt; &lt;u&gt;&lt;em&gt;the culprits were&lt;/em&gt;&lt;/u&gt; &lt;em&gt;subsequently arrested and convicted.&lt;/em&gt;
&lt;strong&gt;NMT Translation:&lt;/strong&gt; &lt;u&gt;&lt;em&gt;the culprit was&lt;/em&gt;&lt;/u&gt; &lt;em&gt;subsequently arrested and sentenced to theft.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;ii. (2 points) &lt;strong&gt;Source Sentence:&lt;/strong&gt; 几乎已经没有地方容纳这些人, 资源已经用尽。
&lt;strong&gt;Reference Translation:&lt;/strong&gt; &lt;em&gt;there is almost no space to accommodate these people, and resources have run out.&lt;/em&gt;
&lt;strong&gt;NMT Translation:&lt;/strong&gt; &lt;em&gt;the resources have been exhausted and&lt;/em&gt; &lt;u&gt;&lt;em&gt;resources have been exhausted&lt;/em&gt;&lt;/u&gt;.&lt;/p&gt;
&lt;p&gt;iii. (2 points) &lt;strong&gt;Source Sentence:&lt;/strong&gt; 当局已经宣布今天是国殇日。
&lt;strong&gt;Reference Translation:&lt;/strong&gt; &lt;em&gt;authorities have announced&lt;/em&gt; &lt;u&gt;&lt;em&gt;a national mourning today.&lt;/em&gt;&lt;/u&gt; &lt;strong&gt;NMT Translation:&lt;/strong&gt; the administration has announced today&amp;rsquo;s day.
&lt;strong&gt;NMT Translation:&lt;/strong&gt; &lt;em&gt;the administration has announced&lt;/em&gt; &lt;u&gt;&lt;em&gt;today&amp;rsquo;s day.&lt;/em&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;iv. (2 points) &lt;strong&gt;Source Sentence&lt;/strong&gt;: 俗语有云:“唔做唔错”。
&lt;strong&gt;Reference Translation:&lt;/strong&gt; &lt;u&gt;&lt;em&gt;“ act not, err not ”&lt;/em&gt;&lt;/u&gt;, &lt;em&gt;so a saying goes.&lt;/em&gt;
&lt;strong&gt;NMT Translation:&lt;/strong&gt; &lt;em&gt;as the saying goes,&lt;/em&gt; &lt;u&gt;&lt;em&gt;“ it&amp;rsquo;s not wrong. ”&lt;/em&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(i) 是单/复数的问题，缺少上下文以及整体理解，难以判断“贼人”是复数还是单数。&lt;/li&gt;
&lt;li&gt;(ii) “资源已经用尽”被重复翻译了两遍，简单来说是注意力先偏移到了后半句，然后NMT不知道前半句没有翻译，加上前面错译出的&amp;quot;&amp;hellip;and&amp;quot;，导致重复翻译后半句。&lt;/li&gt;
&lt;li&gt;(iii) &lt;code&gt;src.vocab&lt;/code&gt;没有记录“国殇”，所以NMT越过了这个词。&lt;/li&gt;
&lt;li&gt;(iv) 本质是句俗语，或者说是粤语，而多数模型都是基于普通话训练的。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="bleu"&gt;BLEU
&lt;/h3&gt;&lt;p&gt;BLEU score is the most commonly used automatic evaluation metric for NMT systems.
It is usually calculated across the entire test set, but here we will consider BLEU defined for a single
example. Suppose we have a source sentence s, a set of k reference translations $\mathbf{r}_1,\dots,\mathbf{r}_k$, and a
candidate translation c. To compute the BLEU score of c, we first compute the modified n-gram
precision pn of c, for each of n = 1,2,3,4, where n is the n in n-gram:&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;BLEU 分数是 NMT (神经机器翻译) 系统中最常用的自动评估指标。它通常在整个测试集上计算，但在这里我们将考虑为单个示例定义的 BLEU。假设我们有一个源句子 $\mathbf{s}$，一组 $k$ 个参考翻译 $\mathbf{r}_1,\dots,\mathbf{r}_k$，以及一个候选翻译 $\mathbf{c}$。为了计算 $\mathbf{c}$ 的 BLEU 分数，我们首先计算 $\mathbf{c}$ 的&lt;em&gt;修正 n-gram 精确度 (modified n-gram precision)&lt;/em&gt; $p_n$，其中 $n = 1, 2, 3, 4$，$n$ 是 n-gram 中的 $n$：&lt;/p&gt;

 &lt;/blockquote&gt;
$$
p_n = \frac{\sum_{\text{ngram} \in \mathbf{c}} \min \left( \max_{i=1,\dots,k} \text{Count}_{\mathbf{r}_i}(\text{ngram}), \text{Count}_{\mathbf{c}}(\text{ngram}) \right)}{\sum_{\text{ngram} \in \mathbf{c}} \text{Count}_{\mathbf{c}}(\text{ngram})}
$$&lt;p&gt;Here, for each of the n-grams that appear in the candidate translation c, we count the maxi
mum number of times it appears in any one reference translation, capped by the number of times
it appears in c (this is the numerator). We divide this by the number of n-grams in c (denominator).&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;这里，对于出现在候选翻译 $\mathbf{c}$ 中的每一个 $n$-gram，我们计算它在任何一个参考翻译中出现的最大次数，上限为它在 $\mathbf{c}$ 中出现的次数（这是分子）。我们将其除以 $\mathbf{c}$ 中 $n$-gram 的数量（分母）。&lt;/p&gt;
&lt;p&gt;$p_n$ 公式的分母部分比较清晰，分子部分我们继续拆解：&lt;/p&gt;
&lt;p&gt;$\text{Count}_{\mathbf{c}}(\text{ngram})$ : 该词组在模型输出中出现几次。&lt;/p&gt;
&lt;p&gt;$\max_{i=1,\dots,k} \text{Count}_{\mathbf{r}_i}(\text{ngram})$ : 该词组在&lt;strong&gt;所有参考答案&lt;/strong&gt;中出现次数最多的那一次。比如三个参考答案里，&lt;code&gt;the&lt;/code&gt; 分别出现了 1、2、1 次，那么这个值就是 2。&lt;/p&gt;
&lt;p&gt;$\min(\dots)$ : 这是**截断（Clipping）**机制。它规定：即便你在输出里写了 10 个 &lt;code&gt;the&lt;/code&gt;，如果参考答案里最多只出现了 2 个，那我也只算你命中了 2 个。&lt;/p&gt;
&lt;p&gt;综上，公式可以被翻译成：
&lt;/p&gt;
$$
&gt; p_n = \frac{\text{被认可的 n-gram 匹配总数}}{\text{候选翻译中总共生成的 n-gram 数量}}
&gt; $$
 &lt;/blockquote&gt;
&lt;p&gt;Next, we compute the brevity penalty BP. Let $len(\mathbf{c})$ be the length of c and let $len(\mathbf{r})$ be the
length of the reference translation that is closest to $len(\mathbf{c})$ (in the case of two equally-close reference
translation lengths, choose $len(\mathbf{r})$ as the shorter one).&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;接下来，我们计算&lt;em&gt;长度惩罚 (brevity penalty)&lt;/em&gt; $BP$。令 $len(\mathbf{c})$ 为 $\mathbf{c}$ 的长度，令 $len(\mathbf{r})$ 为最接近 $len(\mathbf{c})$ 的参考翻译的长度（在有两个同样接近的参考翻译长度的情况下，选择较短的那个作为 $len(\mathbf{r})$）。&lt;/p&gt;

 &lt;/blockquote&gt;
$$
BP = \begin{cases} 1 &amp; \text{if } len(\mathbf{c}) \geq len(\mathbf{r}) \\ \exp\left(1 - \frac{len(\mathbf{r})}{len(\mathbf{c})}\right) &amp; \text{otherwise} \end{cases}
$$
 &lt;blockquote&gt;
 &lt;p&gt;那么引入 $BP$ 有什么用？首先 $len(\mathbf{c}) \geq len(\mathbf{r})$ 时 $BP=1$ ，这是在NMT输出较长时，$BP$ 不需要惩罚它，而会在 $p_n$ 的分母体现（分母变大，概率降低）。&lt;/p&gt;
&lt;p&gt;$len(\mathbf{c}) &lt; len(\mathbf{r})$ 时引入指数惩罚，如果NMT只翻译最有把握的一个词（比如输出一个单词 &lt;code&gt;apple&lt;/code&gt;），$p_n$ 可能是 100%，但这没有意义。这个指数函数 $\exp(1 - r/c)$ 在 $c$ 越小时，值会迅速接近 0，从而给短句一个沉重的打击。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;Lastly, the BLEU score for candidate c with respect to $\mathbf{r}_1,\dots,\mathbf{r}_k$ is:&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;最后，候选 $\mathbf{c}$ 相对于 $\mathbf{r}_1,\dots,\mathbf{r}_k$ 的 BLEU 分数为：&lt;/p&gt;

 &lt;/blockquote&gt;
$$
BLEU = BP \times \exp \left( \sum_{n=1}^4 \lambda_n \log p_n \right)
$$
 &lt;blockquote&gt;
 &lt;p&gt;本质上等同于：
&lt;/p&gt;
$$
&gt; BLEU = BP \times (p_1^{\lambda_1} \cdot p_2^{\lambda_2} \cdot p_3^{\lambda_3} \cdot p_4^{\lambda_4})
&gt; $$
 &lt;/blockquote&gt;
&lt;p&gt;where $\lambda_1, \lambda_2, \lambda_3, \lambda_4$ are weights that sum to 1. The log here is natural log.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;其中 $\lambda_1, \lambda_2, \lambda_3, \lambda_4$ 是总和为 1 的权重。此处的 $\log$ 是自然对数。&lt;/p&gt;
&lt;p&gt;几何平均的一个特性是：&lt;strong&gt;如果其中任何一项 $p_n$ 为 0，最终结果就会是 0&lt;/strong&gt;。所以模型的词汇和语序的正确性都会被计算。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;问题 (c) 结合实例运算：&lt;/p&gt;
&lt;p&gt;i. Source Sentence &lt;strong&gt;s&lt;/strong&gt;: 需要有充足和可预测的资源。
Reference Translation $r_1$ : &lt;em&gt;resources have to be sufficient and they have to be predictable&lt;/em&gt;
Reference Translation $r_2$ : &lt;em&gt;adequate and predictable resources are required&lt;/em&gt;
NMT Translation $c_1$ : there is a need for adequate and predictable resources
NMT Translation $c_2$ : resources be sufficient and predictable to
Please compute the BLEU scores for and $c_1$ $c_2$. Let $\lambda_i = 0.5$ for $i \in \{1, 2\}$ and $\lambda_i = 0$ for $i \in \{3, 4\}$ (&lt;strong&gt;this means we ignore 3-grams and 4-grams&lt;/strong&gt;, i.e., don&amp;rsquo;t compute $p_3$ or $p_4$).
When computing BLEU scores, show your work (i.e., show your computed values for $p_1$, $p_2$, $len(c)$, $len(r)$ and $BP$). Note that the BLEU scores can be expressed between 0 and 1 or between 0 and 100. The code is using the 0 to 100 scale while in this question we are using the &lt;strong&gt;0 to 1&lt;/strong&gt; scale. Please round your responses to 3 decimal places.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;先计算 $c_1$ : $len(c_1)=9$ 更接近于 $len(r_1)=11$ , $BP=exp(1-\frac{11}{9})$&lt;/p&gt;
&lt;p&gt;$p_1$ 匹配的Unigrams 为 {adequate, and, predictable, resources}，共 4 个。 $p_1=4/9$&lt;/p&gt;
&lt;p&gt;$p_2$ 匹配的Bigrams 为 {adequate and, and predictable, predictable resources}，共 3 个。 $p_2=3/8$&lt;/p&gt;
&lt;p&gt;$BLEU_{c1} = BP \times \sqrt{p_1 \times p_2}$&lt;/p&gt;
&lt;p&gt;$c_2$ 同理。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;ii. Our hard drive was corrupted and we lost Reference Translation $r_1$. Please recom
pute BLEU scores for $c_1$ and $c_2$, this time with respect to $r_2$ only. Which of the two NMT
translations now receives the higher BLEU score? Do you agree that it is the better translation?&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;计算方式不变，但是在去掉了 $r_1$ 后， ${BLEU}_{c_1}$ 不变， ${BLEU}_{c_2}$ 大幅缩小（由于 $p_1$ 和 $p_2$ 下降）&lt;/p&gt;
&lt;p&gt;这说明了 &lt;strong&gt;$c_1$ 的鲁棒性更强&lt;/strong&gt;，它使用了更通用的词汇（如 &lt;code&gt;adequate&lt;/code&gt;），即使在参考答案有限的情况下，依然能保持较合理的得分，这更符合人类对“好翻译”的评价标准。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;iv. List two advantages and two disadvantages of BLEU, compared to human evaluation,
as an evaluation metric for Machine Translation.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;优点不列了，缺点：BLEU 仅进行&lt;strong&gt;字面上的 N-gram 匹配&lt;/strong&gt;，缺乏语义理解。由于 BLEU 主要关注局部词汇的重合度，它往往无法识别严重的语法错误或逻辑扭曲。一个逻辑完全相反、但在词汇上高度重合的句子可能获得极高的 BLEU 分，却无法被人类理解，所以它无法准确衡量语言质量。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;问题 (d) 是Beam Search相关的，关于训练次数与其“假设”质量的问题，比如i. 要求进行三个对比：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// iteration 200
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;hypothesis&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁it&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁is&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁not&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁that&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁the&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁united&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁nations&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁the&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁united&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁nations&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁the&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁united&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁nations&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;score&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;-31.211565017700195&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// iteration 3000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;hypothesis&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁i&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁would&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁also&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁like&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁to&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁clarify&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁the&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁number&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁cases&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁in&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁the&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁conference&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;score&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;-15.013087272644043&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// iteration 17200 (last)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;hypothesis&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁i&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁have&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁also&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁clarified&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁number&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁matters&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁raised&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁by&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁the&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;▁conference&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;score&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;-7.69414758682251&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>CS224N</title><link>https://blog.kawausococo.top/p/cs224n/</link><pubDate>Wed, 28 Jan 2026 14:31:08 +0800</pubDate><guid>https://blog.kawausococo.top/p/cs224n/</guid><description>&lt;h2 id="intro"&gt;Intro
&lt;/h2&gt;&lt;p&gt;本篇是对&lt;a class="link" href="https://web.stanford.edu/class/archive/cs/cs224n/cs224n.1246/" target="_blank" rel="noopener"
 &gt;Stanford CS 224N | Natural Language Processing with Deep Learning (Spring 2024)&lt;/a&gt; 这门课程的学习笔记。关于这门课的知识点，总结如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;词向量、RNN、LSTM、Seq2Seq 模型、机器翻译、注意力机制、Transformer 等等&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="what-is-this-course-about"&gt;What is this course about?
&lt;/h3&gt;&lt;p&gt;Natural language processing (NLP) or computational linguistics is one of the most important technologies of the information age. Applications of NLP are everywhere because people communicate almost everything in language: web search, advertising, emails, customer service, language translation, virtual agents, medical reports, politics, etc. In the 2010s, deep learning (or neural network) approaches obtained very high performance across many different NLP tasks, using single end-to-end neural models that did not require traditional, task-specific feature engineering. In the 2020s amazing further progress was made through the scaling of Large Language Models, such as ChatGPT. In this course, students will gain a thorough introduction to both the basics of Deep Learning for NLP and the latest cutting-edge research on Large Language Models (LLMs). Through lectures, assignments and a final project, students will learn the necessary skills to design, implement, and understand their own neural network models, using the Pytorch framework.&lt;/p&gt;
&lt;h2 id="word-vectors"&gt;Word Vectors
&lt;/h2&gt;&lt;p&gt;$ vector(”King”)- vector(”Man”) + vector(”Woman”) $&lt;/p&gt;
&lt;p&gt;&lt;em&gt;results in a vector that is closest to the vector representation of the word Queen&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="how-do-we-have-usable-meaning-in-a-computer"&gt;How do we have usable meaning in a computer?
&lt;/h3&gt;&lt;p&gt;Slides里介绍了几种方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WordNet&lt;/li&gt;
&lt;li&gt;one-hot vectors&lt;/li&gt;
&lt;li&gt;word vectors&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么前两种都有其现实意义，但也有明显的弊端。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;WordNet，完全靠&lt;code&gt;synonum sets&lt;/code&gt;和&lt;code&gt;hypernyms sets&lt;/code&gt;来确定词汇间的关系、构造复杂、无法及时吸收新词汇&amp;hellip;&amp;hellip;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;one-hot vectors给每个词都设置了&lt;code&gt;symbol&lt;/code&gt;，尽管是用数字表达，但是&lt;strong&gt;相近的词之间在数学上没有联系&lt;/strong&gt;（向量的点积为0）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么下面就引出了一句名言：&lt;em&gt;&amp;ldquo;You shall know a word by the company it keeps&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;以及word vectors的思想，将&lt;strong&gt;词汇归一化到一个向量&lt;/strong&gt;中，虽然每个词汇有多个词义，但其分布却近似是其多个词义的平均，并用点积来确定词向量间的相关性。&lt;/p&gt;
&lt;h3 id="word2vec"&gt;Word2vec
&lt;/h3&gt;&lt;p&gt;&lt;a class="link" href="https://arxiv.org/pdf/1301.3781" target="_blank" rel="noopener"
 &gt;original word2vec paper&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Word2vec&lt;/code&gt;很好地反映了word vectors的思想，计算中心词和相邻词的相似度，确定其概率分布：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We have a large corpus (“body”) of text: a long list of words&lt;/li&gt;
&lt;li&gt;Every word in a fixed vocabulary is represented by a &lt;strong&gt;vector&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Go through each position t in the text, which has a center word c and context (“outside”) words o&lt;/li&gt;
&lt;li&gt;Use the &lt;strong&gt;similarity of the word vectors for c and o to calculate the probability&lt;/strong&gt; of o given c (or vice versa)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep adjusting the word vectors&lt;/strong&gt; to maximize this probability&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/Word2Vec-1.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;h4 id="objective-function"&gt;Objective Function
&lt;/h4&gt;&lt;p&gt;那么如何计算其似然程度(Likelihood)呢，For each position = $t=1,......,T$, predict context words within a window of fixed size $m$, given center word $w_t$. Data likelihood :
&lt;/p&gt;
$$
L(\theta) = \prod_{t=1}^{T} \prod_{\substack{-m \le j \le m \\ j \neq 0}} P(w_{t+j} \mid w_t; \theta)
$$&lt;p&gt;
为减小数据复杂程度，及方便计算机处理，优化上公式，对其进行平均负对数似然操作，The objective function $J(\theta)$ is the (average) negative log likelihood:
&lt;/p&gt;
$$
J(\theta) = -\frac{1}{T} \log L(\theta) = -\frac{1}{T} \sum_{t=1}^{T} \sum_{substack{-m \le j \le m \\ j \neq 0}} \log P(w_{t+j} \mid w_t; \theta)
$$&lt;p&gt;
&lt;strong&gt;最小化 $J(\theta)$&lt;/strong&gt; 等价于 &lt;strong&gt;最大化预测准确率&lt;/strong&gt;。&lt;/p&gt;
&lt;h4 id="prediction-function"&gt;Prediction Function
&lt;/h4&gt;$$
P(o \mid c) = \frac{\exp(u_o^T v_c)}{\sum_{w \in V} \exp(u_w^T v_c)}
$$&lt;ol&gt;
&lt;li&gt;$u_o^T v_c$ : 点积越大，代表这两个词在向量空间中的位置越接近，即语义相关性越高。
Dot product compares similarity of o and c. $u^T v = u \cdot v = \sum_{i=1}^{n} u_i v_i$ Larger dot product = larger probability.&lt;/li&gt;
&lt;li&gt;$\exp()$ : 将任何实数映射为&lt;strong&gt;正数&lt;/strong&gt;。由于指数函数的增长特性，它会放大较大点积的影响，使相关性高的词获得更高的权重。&lt;/li&gt;
&lt;li&gt;${\sum_{w \in V} \exp(u_w^T v_c)}$ : 分母是对词汇表$V$中所有可能的词进行求和。这一步确保了所有可能输出的概率之和等于 &lt;strong&gt;1&lt;/strong&gt;，从而构成一个标准的概率分布。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这也是对Softmax Function的应用
&lt;/p&gt;
$$
\text{softmax}(x_i) = \frac{\exp(x_i)}{\sum_{j=1}^{n} \exp(x_j)} = p_i
$$&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Softmax 函数将任意值 $x_i$ 映射为一个概率分布 $p_i$。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;“max”：因为它会&lt;strong&gt;放大&lt;/strong&gt;最大值 $x_i$ 对应的概率，使大的更大。&lt;/li&gt;
&lt;li&gt;“soft”：因为它仍然会给较小的 $x_i$ &lt;strong&gt;分配一些概率&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="to-train-the-model"&gt;To Train the Model
&lt;/h4&gt;&lt;p&gt;To train a model, we gradually adjust parameters to minimize a loss.
&lt;/p&gt;
$$
\theta = \begin{bmatrix} 
v_{aardvark} \\ v_a \\ \vdots \\ v_{zebra} \\ u_{aardvark} \\ u_a \\ \vdots \\ u_{zebra}
\end{bmatrix} \in \mathbb{R}^{2dV}
$$&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如果词向量维度为 $d$，词汇量为 $V$，则&lt;strong&gt;总参数量&lt;/strong&gt;为 $2dV$。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每个单词有两个向量，$\theta$ 包含了词汇表中所有单词的两种向量表示（中心词向量 $v$ 和背景词向量 $u$）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;计算所有参数的梯度，以优化模型&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;梯度公式就是对Softmax Function的求偏导，数学过程如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;初始损失函数 (单词对的负对数似然)&lt;/strong&gt;
这是针对一个中心词 $c$ 和一个背景词 $o$ 的基本损失定义：
&lt;/p&gt;
$$
 \text{Loss} = -\log P(o \mid c)
 $$&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;代入Softmax定义展开&lt;/strong&gt;
将 $P(o|c)$ 的公式代入，利用对数性质将除法化为减法：
&lt;/p&gt;
$$
 \text{Loss} = -\log \left( \frac{\exp(u_o^T v_c)}{\sum_{w \in V} \exp(u_w^T v_c)} \right) = -u_o^T v_c + \log \sum_{w \in V} \exp(u_w^T v_c)
 $$&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;第一部分求导&lt;/strong&gt;
对点积项关于中心词向量 $v_c$ 求偏导：
&lt;/p&gt;
$$
 \frac{\partial}{\partial v_c} (u_o^T v_c) = u_o
 $$&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;第二部分求导&lt;/strong&gt;
利用链式法则对 $\log \sum \exp(\dots)$ 形式进行求导：
&lt;/p&gt;
$$
 \frac{\partial}{\partial v_c} \log \sum_{w \in V} \exp(u_w^T v_c) = \frac{1}{\sum_{w \in V} \exp(u_w^T v_c)} \cdot \sum_{x \in V} \left[ \exp(u_x^T v_c) \cdot u_x \right]
 $$&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;转化为概率期望形式&lt;/strong&gt;
将上一步的结果重新组合，提取出原始的概率项 $P(x|c)$：
&lt;/p&gt;
$$
 \sum_{x \in V} \left[ \frac{\exp(u_x^T v_c)}{\sum_{w \in V} \exp(u_w^T v_c)} \right] u_x = \sum_{x \in V} P(x \mid c) u_x
 $$&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;最终梯度公式&lt;/strong&gt;
将两部分合并，得到最终用于更新 $v_c$ 的梯度：
&lt;/p&gt;
$$
 \frac{\partial \text{Loss}}{\partial v_c} = -u_o + \sum_{x \in V} P(x \mid c) u_x
 $$&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="gradient-descent"&gt;Gradient Descent
&lt;/h4&gt;&lt;p&gt;梯度下降更新方程 (矩阵形式)
&lt;/p&gt;
$$
\theta^{new} = \theta^{old} - \alpha \nabla_{\theta} J(\theta)
$$&lt;p&gt;
梯度下降更新方程 (单个参数形式)
&lt;/p&gt;
$$
\theta_j^{new} = \theta_j^{old} - \alpha \frac{\partial}{\partial \theta_j^{old}} J(\theta)
$$&lt;ul&gt;
&lt;li&gt;$\alpha$ (alpha): 步长或学习率（step size / learning rate）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但是实际应用并非上面的方法，而是用&lt;strong&gt;随机梯度下降&lt;/strong&gt;，(Stochastic Gradient Descent, SGD)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目标函数&lt;/strong&gt; $J(\theta)$ 是语料库中&lt;strong&gt;所有&lt;/strong&gt;窗口的函数。如果每次更新都要计算整个语料库的梯度 $\nabla_{\theta} J(\theta)$，计算成本极其昂贵，在进行单次更新前需要等待极长时间。&lt;/li&gt;
&lt;li&gt;SGD不再计算整个语料库，而是通过&lt;strong&gt;重复采样窗口&lt;/strong&gt;，&lt;strong&gt;每处理一个（或一小批）窗口就更新一次参数。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="skip-gram-model-with-negative-sampling"&gt;Skip-gram Model with Negative Sampling
&lt;/h4&gt;&lt;p&gt;&lt;a class="link" href="https://proceedings.neurips.cc/paper_files/paper/2013/file/9aa42b31882ec039965f3c4923ce901b-Paper.pdf" target="_blank" rel="noopener"
 &gt;negative sampling paper&lt;/a&gt;
&lt;/p&gt;
$$
P(o\mid c)=\frac{\exp(u_x^T v_c)}{\sum_{w \in V} \exp(u_w^T v_c)}
$$&lt;p&gt;
采用最传统的方法计算概率时，即softmax，它的&lt;strong&gt;分母&lt;/strong&gt;是全部词的叠加，计算量太大&lt;/p&gt;
&lt;p&gt;而Skip-gram Model with Negative Sampling不需计算所有可能的词，而是训练一些逻辑回归，更偏好实际的上下文而非随机的上下文，实际上会选K个负样本，计算量就变为了$O(K)$
&lt;/p&gt;
$$
J_{neg-sample}(u_o, v_c, U) = -\log \sigma(u_o^T v_c) - \sum_{k \in \{K \text{ sampled indices}\}} \log \sigma(-u_k^T v_c)
$$&lt;p&gt;
其中，$\sigma(x)=\frac{1}{1+e^{-x}}$即sigmoid函数，使符合的结果概率接近于1，而不符合的则接近于0&lt;/p&gt;
&lt;p&gt;但这又会导致低频词，如&amp;quot;zebra&amp;quot;的概率过低，而像&amp;quot;the&amp;quot;这样的词则概率较高，所以给公式加上$3/4$次方：
&lt;/p&gt;
$$
P(W)=U(W)^{3/4}/Z
$$&lt;p&gt;
来提高低频词的概率。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="glove"&gt;GloVe
&lt;/h3&gt;&lt;p&gt;&lt;a class="link" href="https://nlp.stanford.edu/pubs/glove.pdf" target="_blank" rel="noopener"
 &gt;original GloVe papar&lt;/a&gt; (Global Vectors for Word Representation)&lt;/p&gt;
&lt;h4 id="co-occurrence-martix"&gt;Co-occurrence Martix
&lt;/h4&gt;&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/co-occurrence.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;构建共现矩阵的方法非常简单，首先设定窗口长度，然后对在窗口长度内出现的共现词频率进行计数，一个简单的例子如上图（窗口为1，仅算相邻词）。但是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;词向量&lt;strong&gt;维度&lt;/strong&gt;会随着语料库中词汇的增多而大幅&lt;strong&gt;增加&lt;/strong&gt;，这会导致所需存储空间增大，且矩阵会变得相当&lt;strong&gt;稀疏&lt;/strong&gt;，基于此构建的模型&lt;strong&gt;鲁棒性较差&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;功能词出现频次极高，但没有提供相应的信息；&lt;/li&gt;
&lt;li&gt;没有反映出词距与词相关性之间的联系。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么如何进行降维来优化？经典的方法是进行SVD矩阵分解（虽然问了AI也没明白原理，而且Assignment1中用一个&lt;code&gt;sklearn&lt;/code&gt;的函数就解决了）&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/SVD.png" alt="" loading="lazy" /&gt;
&lt;/p&gt;
$$
X = U \Sigma V^T
$$&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;$X$ (共现矩阵)&lt;/strong&gt;：大小为 $|V| \times |V|$（词表大小）。每个元素 $X_{ij}$ 代表词 $i$ 和词 $j$ 在语料库中共同出现的次数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$U$ 和 $V$ (正交矩阵)&lt;/strong&gt;：它们的列向量是相互正交的单位向量（Orthonormal）。在 NLP 中，$U$ 的每一行通常被视为该词的原始“嵌入”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$\Sigma$ (对角矩阵)&lt;/strong&gt;：对角线上的值 $\sigma_1, \sigma_2, \dots$ 称为&lt;strong&gt;奇异值&lt;/strong&gt;。它们按从大到小排列，代表了数据在对应维度上的&lt;strong&gt;重要程度（方差/信息量）&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么降维便是将从共现矩阵得到的的，长度为$V$的词向量降维成长度为$K$的向量&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;真正编码语义的不是共现概率本身，而是共现概率的比值&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;共现概率的比率可以编码语义成分，我们希望将它们作为线性语义成分捕捉在词向量空间中！&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style="text-align: left"&gt;&lt;/th&gt;
					&lt;th style="text-align: left"&gt;$x = \text{solid}$&lt;/th&gt;
					&lt;th style="text-align: left"&gt;$x = \text{gas}$&lt;/th&gt;
					&lt;th style="text-align: left"&gt;$x = \text{water}$&lt;/th&gt;
					&lt;th style="text-align: left"&gt;$x = \text{fashion}$&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style="text-align: left"&gt;$P(x\mid\text{ice})$&lt;/td&gt;
					&lt;td style="text-align: left"&gt;$1.9 \times 10^{-4}$&lt;/td&gt;
					&lt;td style="text-align: left"&gt;$6.6 \times 10^{-5}$&lt;/td&gt;
					&lt;td style="text-align: left"&gt;$3.0 \times 10^{-3}$&lt;/td&gt;
					&lt;td style="text-align: left"&gt;$1.7 \times 10^{-5}$&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style="text-align: left"&gt;$P(x\mid\text{steam})$&lt;/td&gt;
					&lt;td style="text-align: left"&gt;$2.2 \times 10^{-5}$&lt;/td&gt;
					&lt;td style="text-align: left"&gt;$7.8 \times 10^{-4}$&lt;/td&gt;
					&lt;td style="text-align: left"&gt;$2.2 \times 10^{-3}$&lt;/td&gt;
					&lt;td style="text-align: left"&gt;$1.8 \times 10^{-5}$&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style="text-align: left"&gt;$\dfrac{P(x\mid\text{ice})}{P(x\mid\text{steam})}$&lt;/td&gt;
					&lt;td style="text-align: left"&gt;$8.9$&lt;/td&gt;
					&lt;td style="text-align: left"&gt;$8.5 \times 10^{-2}$&lt;/td&gt;
					&lt;td style="text-align: left"&gt;$1.36$&lt;/td&gt;
					&lt;td style="text-align: left"&gt;$0.96$&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id="analogies"&gt;Analogies
&lt;/h4&gt;&lt;p&gt;词向量虽然数学原理上很强大，但是其实际的类比(Analogy)场景却是有不少问题：&lt;/p&gt;
&lt;p&gt;下例可见，是一个$woman+grandfather-man=?$的问题，那么显而易见且最有可能的结果就是&lt;code&gt;grandmother&lt;/code&gt;，那么为何程序给出的另外几个，如&lt;code&gt;granddaughter&lt;/code&gt;, &lt;code&gt;mother&lt;/code&gt;之类，score也几乎一样很高呢？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Run this cell to answer the analogy -- man : grandfather :: woman : x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pprint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wv_from_bin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;most_similar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;positive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;woman&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;grandfather&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;negative&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;man&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;grandmother&amp;#39;&lt;/span&gt;, 0.7608445286750793&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;granddaughter&amp;#39;&lt;/span&gt;, 0.7200808525085449&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;daughter&amp;#39;&lt;/span&gt;, 0.7168302536010742&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mother&amp;#39;&lt;/span&gt;, 0.7151536345481873&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;niece&amp;#39;&lt;/span&gt;, 0.7005682587623596&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;father&amp;#39;&lt;/span&gt;, 0.6659888029098511&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;aunt&amp;#39;&lt;/span&gt;, 0.6623409390449524&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;grandson&amp;#39;&lt;/span&gt;, 0.6618767976760864&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;grandparents&amp;#39;&lt;/span&gt;, 0.644661009311676&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;wife&amp;#39;&lt;/span&gt;, 0.6445354223251343&lt;span class="o"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;虽说Assignment里没有标准答案，但是通过AI可以得知：这涉及到“语义聚类”&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;语义的“近邻效应”&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;在向量空间中，逻辑相似的词通常会聚在一起形成一个“簇”。当你计算出 $\vec{w} + \vec{g} - \vec{m}$ 时，你实际上是在空间中定位了一个&lt;strong&gt;坐标点&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;granddaughter&lt;/code&gt; 等同样具有“女性”和“亲属”属性，且在语料库中经常与 &lt;code&gt;grandfather&lt;/code&gt; 或 &lt;code&gt;grandmother&lt;/code&gt; 出现在相似的上下文。在向量维度上，它们在“亲属关系”这个维度上非常接近。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;维度的“重叠性”&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;词向量通常有几百个维度。虽然我们减去了 &lt;code&gt;man&lt;/code&gt; 并加上了 &lt;code&gt;grandfather&lt;/code&gt;，但这并不能完全抹除其他维度的相似性。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;daughter&lt;/code&gt;、&lt;code&gt;mother&lt;/code&gt;、&lt;code&gt;grandmother&lt;/code&gt; 共享了绝大部分维度：[+女性]、[+人类]、[+亲属]。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面的例子，属于&amp;quot;Incorrect Analogy&amp;quot;，我们来看易得答案为&lt;code&gt;socks&lt;/code&gt;，但为何模型忽略了&lt;code&gt;glove&lt;/code&gt;和&lt;code&gt;hand&lt;/code&gt;，而输出了各种&lt;code&gt;square&lt;/code&gt;的名词？显然和&lt;code&gt;foot&lt;/code&gt;“脚”的释义无关，而是“英尺”的释义。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pprint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wv_from_bin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;most_similar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;positive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;foot&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;glove&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;negative&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hand&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;45,000-square&amp;#39;&lt;/span&gt;, 0.4922032654285431&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;15,000-square&amp;#39;&lt;/span&gt;, 0.4649604558944702&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;10,000-square&amp;#39;&lt;/span&gt;, 0.4544755816459656&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;6,000-square&amp;#39;&lt;/span&gt;, 0.44975775480270386&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;3,500-square&amp;#39;&lt;/span&gt;, 0.444133460521698&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;700-square&amp;#39;&lt;/span&gt;, 0.44257497787475586&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;50,000-square&amp;#39;&lt;/span&gt;, 0.4356396794319153&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;3,000-square&amp;#39;&lt;/span&gt;, 0.43486514687538147&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;30,000-square&amp;#39;&lt;/span&gt;, 0.4330596923828125&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;footed&amp;#39;&lt;/span&gt;, 0.43236875534057617&lt;span class="o"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;语义多义性&lt;/strong&gt;的干扰
&lt;ul&gt;
&lt;li&gt;上面也提到，&lt;code&gt;foot&lt;/code&gt;也是计量单位”英尺“的英文，和&lt;code&gt;square&lt;/code&gt;组合是合理的&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;训练语料的偏见&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;既然&lt;code&gt;foot&lt;/code&gt;有多个释义，但输出却全是其“英尺”的意思，那么这可能体现了用来训练的语料中多是&lt;code&gt;...square&lt;/code&gt;和&lt;code&gt;foot&lt;/code&gt;相关联&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;词的选择
&lt;ul&gt;
&lt;li&gt;即使输出全是&lt;code&gt;...square&lt;/code&gt;，其score值也不过0.5，这不仅说明模型找不到强相关的词，而且说明了模型很可能没能理解&lt;code&gt;glove&lt;/code&gt;与&lt;code&gt;hand, foot&lt;/code&gt;的联系&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="neural-network"&gt;Neural Network
&lt;/h2&gt;&lt;p&gt;&lt;em&gt;A neural network = running several logistic regressions at the same time.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://cs231n.github.io/neural-networks-1/" target="_blank" rel="noopener"
 &gt;CS231n Deep Learning on Network Architectures&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://cs231n.github.io/optimization-2/" target="_blank" rel="noopener"
 &gt;CS231n Deep Learning for Computer Vision on Backprop&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="structure"&gt;Structure
&lt;/h3&gt;&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/neural-network-1.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/neural-network-2.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="non-linearities"&gt;Non-linearities
&lt;/h3&gt;&lt;p&gt;为什么神经网络需要非线性(Non-linearities)？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;核心观点：神经网络执行函数逼近，例如回归或分类。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;没有非线性&lt;/strong&gt;：深度神经网络只能执行&lt;strong&gt;线性变换&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多层失效&lt;/strong&gt;：额外的层会被压缩成单个线性变换：$W_1 W_2 x = Wx$（即多层线性层等同于一层）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;有了非线性&lt;/strong&gt;：通过包含非线性的多层结构，网络可以逼近更复杂的函数！&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/neural-network-3.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;左下侧图对&lt;/strong&gt;：左图显示线性分类（只能画直线），无法区分复杂的红绿点分布；右图显示非线性分类（可以画曲线），完美分割了数据。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;右侧三张波形图&lt;/strong&gt;：展示了随着函数复杂度的增加，只有非线性模型才能拟合这些起伏的蓝点（观测数据）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;关于常用的非线性函数，在《智能计算系统》课上均有学习，不多赘述&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/neural-network-4.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="gradients"&gt;Gradients
&lt;/h3&gt;&lt;p&gt;&lt;a class="link" href="https://cs231n.stanford.edu/handouts/derivatives.pdf" target="_blank" rel="noopener"
 &gt;derivatives.pdf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;梯度，简单地讲就是对变量的微积分，比如我们有：
&lt;/p&gt;
$$
f(x)=x^3
$$&lt;p&gt;
那么它的梯度就是：
&lt;/p&gt;
$$
\frac{df}{dx}=3x^2
$$&lt;p&gt;
当然，这只是个非常简单的例子，实际上是大规模的&lt;strong&gt;链式法则&lt;/strong&gt;计算，对矩阵(Jacabian Matrix)进行梯度计算。&lt;/p&gt;
&lt;h4 id="chain-rule"&gt;Chain Rule
&lt;/h4&gt;&lt;p&gt;在单变量微积分中，如果 $y = f(u)$ 且 $u = g(x)$，那么：
&lt;/p&gt;
$$
\frac{dy}{dx} = \frac{dy}{du} \cdot \frac{du}{dx}
$$&lt;p&gt;
但在神经网络中，每一层都是一个向量 $\mathbf{h,z} \in \mathbb{R}^n$。当我们将这个逻辑扩展到向量时，乘法就变成了&lt;strong&gt;矩阵乘法&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;对于多个变量，应与&lt;strong&gt;Jacobians矩阵相乘&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;假设有 $\mathbf h= f(z)$ 和 $\mathbf z=Wx+b$ ，下面的这些偏导数就是雅可比矩阵
&lt;/p&gt;
$$
\frac{\partial \mathbf{h}}{\partial \mathbf{x}} = \frac{\partial \mathbf{h}}{\partial \mathbf{z}} \frac{\partial \mathbf{z}}{\partial \mathbf{x}}
$$&lt;h4 id="matrix-calculus"&gt;Matrix Calculus
&lt;/h4&gt;&lt;p&gt;由下式得，矩阵只有对角元素，其余部分均为0：
&lt;/p&gt;
$$
\begin{aligned} \left( \frac{\partial \mathbf{h}}{\partial \mathbf{z}} \right)_{ij} &amp;= \frac{\partial h_i}{\partial z_j} = \frac{\partial}{\partial z_j} f(z_i) \quad &amp;&amp; \text{definition of Jacobian} \\ &amp;= \begin{cases} f'(z_i) &amp; \text{if } i = j \\ 0 &amp; \text{if otherwise} \end{cases} \quad &amp;&amp; \text{regular 1-variable derivative} \end{aligned}
$$$$
\frac{\partial \mathbf h}{\partial \mathbf z} = 
\begin{pmatrix}
f'(z_1) &amp; 0 &amp; \cdots &amp; 0 \\
0 &amp; f'(z_2) &amp; \cdots &amp; 0 \\
\vdots &amp; \vdots &amp; \ddots &amp; \vdots \\
0 &amp; 0 &amp; \cdots &amp; f'(z_n)
\end{pmatrix}
= \operatorname{diag}(f'(\mathbf z))
$$&lt;p&gt;此外，还有一常有的Jacobian：
&lt;/p&gt;
$$
\frac{\partial}{\partial \mathbf{u}}(\mathbf{u}^T \mathbf{h})=\mathbf h^T
$$&lt;p&gt;
假设 $\mathbf{u}$ 和 $\mathbf{h}$ 都是 $n$ 维列向量：
&lt;/p&gt;
$$
\mathbf{u} = \begin{bmatrix} u_1 \\ u_2 \\ \vdots \\ u_n \end{bmatrix}, \quad \mathbf{h} = \begin{bmatrix} h_1 \\ h_2 \\ \vdots \\ h_n \end{bmatrix}
$$&lt;p&gt;
那么它们的内积（也就是括号里的部分）是一个&lt;strong&gt;标量&lt;/strong&gt;：
&lt;/p&gt;
$$
f = \mathbf{u}^T \mathbf{h} = u_1 h_1 + u_2 h_2 + \dots + u_n h_n = \sum_{i=1}^n u_i h_i
$$&lt;p&gt;
我们要对向量 $\mathbf{u}$ 求导。根据 Jacobian Matrix 的定义，我们需要对 $\mathbf{u}$ 中的每一个元素 $u_k$ 分别求导：
&lt;/p&gt;
$$
\frac{\partial f}{\partial u_k} = \frac{\partial}{\partial u_k} (u_1 h_1 + \dots + u_k h_k + \dots + u_n h_n)
$$&lt;p&gt;
由于除了 $u_k h_k$ 这一项外，其他项都不包含 $u_k$，所以它们对 $u_k$ 的导数都是 $0$：
&lt;/p&gt;
$$
\frac{\partial f}{\partial u_k} = h_k
$$&lt;p&gt;
按照 &lt;strong&gt;Jacobian 矩阵的惯例&lt;/strong&gt;，一个标量对一个&lt;strong&gt;列向量&lt;/strong&gt;求导，结果是一个&lt;strong&gt;行向量&lt;/strong&gt;：
&lt;/p&gt;
$$
\frac{\partial f}{\partial \mathbf{u}} = \begin{bmatrix} \frac{\partial f}{\partial u_1} &amp; \frac{\partial f}{\partial u_2} &amp; \dots &amp; \frac{\partial f}{\partial u_n} \end{bmatrix} = \begin{bmatrix} h_1 &amp; h_2 &amp; \dots &amp; h_n \end{bmatrix} = \mathbf{h}^T
$$&lt;h5 id="write-out-the-jacobians"&gt;Write out the Jacobians
&lt;/h5&gt;$$
\begin{aligned} \frac{\partial s}{\partial \mathbf{b}} &amp;= \frac{\partial s}{\partial \mathbf{h}} \frac{\partial \mathbf{h}}{\partial \mathbf{z}} \frac{\partial \mathbf{z}}{\partial \mathbf{b}} \\ &amp;= \mathbf{u}^T \text{diag}(f'(\mathbf{z})) \mathbf{I} \\ &amp;= \mathbf{u}^T \odot f'(\mathbf{z}) \end{aligned}
$$&lt;p&gt;$\odot$ = Hadamard product = element-wise multiplication of 2 vectors to give vector&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;strong&gt;变量&lt;/strong&gt;&lt;/th&gt;
					&lt;th&gt;&lt;strong&gt;神经网络中的含义&lt;/strong&gt;&lt;/th&gt;
					&lt;th&gt;&lt;strong&gt;说明&lt;/strong&gt;&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;$s$&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;损失函数值 (Loss/Score)&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;最终的标量输出（例如交叉熵损失）。我们要看它随参数如何变化。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;$\mathbf{b}$&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;偏置向量 (Bias)&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;当前层的偏置项，神经网络需要学习的参数之一。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;$\mathbf{z}$&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;净输入 (Logits/Pre-activation)&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;线性组合后的结果，即 $\mathbf{z} = \mathbf{W}\mathbf{x} + \mathbf{b}$。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;$\mathbf{h}$&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;激活值 (Activation/Hidden state)&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;$\mathbf{z}$ 经过非线性激活函数后的输出，即 $\mathbf{h} = f(\mathbf{z})$。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;$\mathbf{u}^T$&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;上游传回的梯度 ($\frac{\partial s}{\partial \mathbf{h}}$)&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;损失函数对当前层输出的导数，它是从更高层“反向传播”回来的信号。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;$f'(\mathbf{z})$&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;激活函数的导数&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;比如 ReLU 或 Sigmoid 的导数。它决定了哪些神经元处于活跃状态。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;$\mathbf{I}$&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;单位矩阵 (Identity Matrix)&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;因为 $\mathbf{z} = \dots + \mathbf{b}$，$\mathbf{z}$ 对 $\mathbf{b}$ 求导的结果是 1（矩阵形式即为单位阵）。&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h5 id="re-using-computation"&gt;Re-using Computation
&lt;/h5&gt;&lt;p&gt;误差信号（Upstream Gradient）$\boldsymbol{\delta}$：
&lt;/p&gt;
$$
\boldsymbol{\delta} = \frac{\partial s}{\partial \mathbf{h}} \frac{\partial \mathbf{h}}{\partial \mathbf{z}} = \mathbf{u}^T \circ f'(\mathbf{z})
$$&lt;p&gt;
我们先算出这个值$\boldsymbol{\delta}$ ，可以简化计算：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;对权重矩阵 $W$ 的梯度展开：
&lt;/p&gt;
$$
 \frac{\partial s}{\partial \mathbf{W}} = \boldsymbol{\delta} \frac{\partial \mathbf{z}}{\partial \mathbf{W}}
 $$&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对偏置向量 $\mathbf{b}$ 的梯度简化：
&lt;/p&gt;
$$
 \frac{\partial s}{\partial \mathbf{b}} = \boldsymbol{\delta} \frac{\partial \mathbf{z}}{\partial \mathbf{b}} = \boldsymbol{\delta}
 $$&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id="shape-convention"&gt;Shape Convention
&lt;/h4&gt;&lt;p&gt;假设权重 $\mathbf{W} \in \mathbb{R}^{n \times m}$，输出是一个标量 $s$（损失）。按纯数学定义，$\frac{\partial s}{\partial \mathbf{W}}$ 应该是一个 $1 \times nm$ 的行向量（Jacobian）。但如果使用这种形式，梯度更新公式 $\theta^{new} = \theta^{old} - \alpha \nabla_{\theta} J(\theta)$ 就会因维度不匹配而无法直接相减。&lt;/p&gt;
&lt;p&gt;为了计算方便，我们约定&lt;strong&gt;梯度的形状必须等于参数的形状&lt;/strong&gt;。因此 $\frac{\partial s}{\partial \mathbf{W}}$ 也是一个 $n \times m$ 的矩阵：
&lt;/p&gt;
$$
\frac{\partial s}{\partial \mathbf{W}} = \begin{bmatrix} \frac{\partial s}{\partial W_{11}} &amp; \dots &amp; \frac{\partial s}{\partial W_{1m}} \\ \vdots &amp; \ddots &amp; \vdots \\ \frac{\partial s}{\partial W_{n1}} &amp; \dots &amp; \frac{\partial s}{\partial W_{nm}} \end{bmatrix}
$$$$
\frac{\partial s}{\partial \mathbf{W}} = \boldsymbol{\delta}^T \mathbf{x}^T
$$&lt;p&gt;那么我们究竟应该以什么样的“形状”来呈现导数结果？&lt;/p&gt;
&lt;p&gt;采取始终遵循**形状约定 (Shape Convention)**的方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;做法&lt;/strong&gt;：不拘泥于严格的 Jacobian 定义，而是时刻盯着变量的维度（Dimensions）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;核心技巧&lt;/strong&gt;：通过观察维度来决定何时需要对某个项进行转置，或者调整矩阵相乘的顺序，以确保每一层算出来的梯度形状和该层的参数形状完全一致。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关于 $\boldsymbol{\delta}$ 的重要结论&lt;/strong&gt;：传导至隐层（Hidden layer）的误差信号 $\boldsymbol{\delta}$，其维度应该与该隐层的神经元数量（即激活值向量的维度）完全相同。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="backpropagation"&gt;Backpropagation
&lt;/h3&gt;&lt;p&gt;按流程逐步计算各函数，从输入得到输出，即是&lt;strong&gt;正向传播(Forward Propagation)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/backpropagation-1.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;对于反向传播中的单个节点，有$downstream\ gradient=upstream\ gradient\times local\ gradient$&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/backpropagation-2.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;对于单节点的多输入，upstream gradient不变，各输入的local gradient不同，但计算公式是不变的&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/backpropagation-3.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;下面举个多输入的实际例子&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/backpropagation-4.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;我们可以在这个基础上，假设输入y的值变为了2.1，那么$a=x+y=3.1$，$b=max(y+z)=y=2.1$，$a\times b=6.51$&lt;/p&gt;
&lt;p&gt;所以，y值变化的0.1导致了结果0.51的变化，那么梯度就是$\frac{\Delta f}{\Delta y}=5.1$&lt;/p&gt;
&lt;h4 id="implementations"&gt;Implementations
&lt;/h4&gt;&lt;p&gt;那么理论上，在已知正向传播的符号和计算的情况下，计算机可以自动得出反向传播的结果。但是在现代框架中，用户需手动设计局部导数的结算，这也比全自动方式提升了系统的运行效率和稳定性。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MultiplyGate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="c1"&gt;# must keep these around!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dz&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dz&lt;/span&gt; &lt;span class="c1"&gt;# [dz/dx * dL/dz]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dz&lt;/span&gt; &lt;span class="c1"&gt;# [dz/dy * dL/dz]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h5 id="numeric-gradient-checking"&gt;Numeric Gradient Checking
&lt;/h5&gt;&lt;p&gt;在手动推导和实现反向传播（Backprop）时，这是确保你数学公式没推错、代码没写错的标准验证方法：
&lt;/p&gt;
$$
f'(x) \approx \frac{f(x + h) - f(x - h)}{2h}
$$&lt;ul&gt;
&lt;li&gt;只需要前向传播函数 $f(x)$ 即可计算，不需要任何复杂的数学推导，不容易写错。&lt;/li&gt;
&lt;li&gt;必须对模型的&lt;strong&gt;每一个参数&lt;/strong&gt;分别进行两次前向传播（加 $h$ 和减 $h$），效率很低。&lt;/li&gt;
&lt;li&gt;适合局部测试，不要对整个大型网络做验证，只针对某个特定层或小规模参数（如一个 $3 \times 3$ 的矩阵）进行。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="dependency-parsing"&gt;Dependency Parsing
&lt;/h2&gt;&lt;h3 id="syntactic-structure"&gt;Syntactic Structure
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Phrase structure&lt;/strong&gt; organizes words into nested constituents.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们可以自己定义phrase构成的语法，比如名词短语可以是“&lt;em&gt;限定词 + 形容词 + 名词&lt;/em&gt;”，“&lt;em&gt;限定词 + 名词 + 介词短语&lt;/em&gt;”……介词短语可以是“&lt;em&gt;介词 + 名词&lt;/em&gt;……”&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dependency structure&lt;/strong&gt; shows which words depend on (modify, attach to, or are arguments of) which other words&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;语言中很容易出现歧义(ambiguity)，在英语里有了介词短语，歧义就更多了，举个例子：&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Scientists count whales from space&amp;quot;可以理解为&amp;quot;Scientists [count] [whales from space]&amp;rdquo; 或者 &amp;ldquo;Scientists [count whales] [from space]&amp;rdquo; ……&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/dependency-1.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;h3 id="dependency-grammar-and-treebanks"&gt;Dependency Grammar and Treebanks
&lt;/h3&gt;&lt;p&gt;Dependency syntax postulates that syntactic structure consists of relations between
lexical items, normally binary asymmetric relations (“arrows”) called dependencies&lt;/p&gt;
&lt;p&gt;下图是个比较古老的&amp;quot;dependency structure&amp;quot;&lt;/p&gt;
&lt;p&gt;An arrow connects a head (governor, superior, regent) with a dependent (modifier, inferior, subordinate)&lt;/p&gt;
&lt;p&gt;Usually, dependencies form a tree (a connected, acyclic, single-root graph)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/dependency-2.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;h4 id="annotated-data"&gt;Annotated Data
&lt;/h4&gt;&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/dependency-3.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;起初，构建语言库（Treebank）似乎比手动编写语法规则要慢得多，且看起来没那么有用。手动标注数据确实是件麻烦的工作，但现在看来，却有很大的优势：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;复用性(Reusability)：一套标注好的数据可以用来训练多种解析器（Parsers）、词性标注器（POS Taggers）&lt;/li&gt;
&lt;li&gt;覆盖面广(Board coverage)：手动编写规则往往只能覆盖几个直觉上的例子，而标注真实语料可以涵盖语言在现实中的各种复杂用法。&lt;/li&gt;
&lt;li&gt;频率与分布信息(Frequencies and distributional information)：它能告诉机器哪些结构更常见，帮助概率模型做出更准确的判断。&lt;/li&gt;
&lt;li&gt;评估NLP(A way to evaluate NLP systems)：没有这套“标准”，我们就无法衡量一个 AI 模型的准确率（如 LAS/UAS 得分）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么关于示例图中的各依存关系的意思，如下：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;strong&gt;缩写&lt;/strong&gt;&lt;/th&gt;
					&lt;th&gt;&lt;strong&gt;意思&lt;/strong&gt;&lt;/th&gt;
					&lt;th&gt;&lt;strong&gt;简单理解&lt;/strong&gt;&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;nsubj&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;名词主语&lt;/td&gt;
					&lt;td&gt;动作的发出者（如 &lt;strong&gt;I&lt;/strong&gt; think）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;nsubjpass&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;被动主语&lt;/td&gt;
					&lt;td&gt;被动语态里的主语（如 &lt;strong&gt;city&lt;/strong&gt; called）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;ccomp&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;从句&lt;/td&gt;
					&lt;td&gt;动词后面的整个小句子（如 think &lt;strong&gt;&amp;hellip;&lt;/strong&gt;）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;advmod&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;状语修饰&lt;/td&gt;
					&lt;td&gt;修饰动词的程度、疑问等（如 &lt;strong&gt;Why&lt;/strong&gt;）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;amod&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;形容词修饰&lt;/td&gt;
					&lt;td&gt;修饰名词的形容词（如 &lt;strong&gt;famous&lt;/strong&gt; goat）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;compound&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;复合修饰&lt;/td&gt;
					&lt;td&gt;名词修饰名词（如 &lt;strong&gt;goat&lt;/strong&gt; trainer）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;det&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;限定关系&lt;/td&gt;
					&lt;td&gt;指向 a, the, any 等词&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;case&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;介词关系&lt;/td&gt;
					&lt;td&gt;指向 in, at 等介词&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;conj&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;并列关系&lt;/td&gt;
					&lt;td&gt;用 or, and 连接的词（如 trainer or &lt;strong&gt;something&lt;/strong&gt;）&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id="dependency-conditioning-preferences"&gt;Dependency Conditioning Preferences
&lt;/h4&gt;&lt;p&gt;在进行句法分析时，计算机会根据“依存条件偏好（Dependency Conditioning Preferences）”来判断两个词之间是否存在依存关系：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;双词亲和力 (Bilexical affinities)：依存关系（例如 [discussion → issues]）是合理的。&lt;/li&gt;
&lt;li&gt;依存距离 (Dependency distance)：大多数（但并非所有）依存关系发生在邻近的单词之间。&lt;/li&gt;
&lt;li&gt;介入材料 (Intervening material)：依存关系很少跨越中间的动词或标点符号。&lt;/li&gt;
&lt;li&gt;中心词的价态 (Valency of heads)：对于一个中心词（Head）来说，通常在它的哪一侧会有多少个依存词？&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="projectivity"&gt;Projectivity
&lt;/h4&gt;&lt;p&gt;如果一个句子的单词按线性顺序排列，且所有的依存弧（dependency arcs）都画在单词上方时，&lt;strong&gt;没有任何两条弧线发生交叉&lt;/strong&gt;，那么这个解析就是“投影的”。那么互出现了交叉，就是非投影的(non-projectivity)，说明发生了“长距离位移”或结构重叠。&lt;/p&gt;
&lt;p&gt;但是现实中非投影的例子很常见，比如&amp;quot;&lt;em&gt;Who did Bill buy the coffee from yesterday&lt;/em&gt;&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/dependency-4.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;h3 id="transition-based-dependency-parser"&gt;Transition-Based Dependency Parser
&lt;/h3&gt;&lt;p&gt;首先，这Transition-Based Dependency Parser分有一个Stack和一个Buffer，和三种操作：&lt;/p&gt;
&lt;p&gt;Start: $\sigma = [ROOT], \beta = w_1, ..., w_n, A = \emptyset$&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Shift: $\sigma, w_i | \beta, A \Rightarrow \sigma | w_i, \beta, A $&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Left-$Arc_r$: $\sigma | w_i | w_j, \beta, A \Rightarrow \sigma | w_j, \beta, A \cup \{r(w_j, w_i)\} $&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Right-$Arc_r$: $\sigma | w_i | w_j, \beta, A \Rightarrow \sigma | w_j, \beta, A \cup \{r(w_i, w_j)\}$&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Finish: $\sigma = [w], \beta = \emptyset$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;σ&lt;/strong&gt;（sigma）表示 &lt;strong&gt;堆栈（stack）&lt;/strong&gt;，存储当前正在处理或等待建立关系的词。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;β&lt;/strong&gt;（beta）表示 &lt;strong&gt;缓冲区（buffer）&lt;/strong&gt;，存储尚未处理的输入词序列。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A&lt;/strong&gt; 表示 &lt;strong&gt;依存弧集合（set of dependency arcs）&lt;/strong&gt;，存储已建立的依存关系。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Left-$Arc_r$ 和 Right-$Arc_r$ 是两种规约模型，用来表明一个词是另一个的依存词，以左或右方向。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么下面先来举个例子：Analysis of &amp;ldquo;&lt;em&gt;I ate fish&lt;/em&gt;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/dependency-5.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Left Arc&lt;/strong&gt;操作 生成了一个由栈顶指向第二个元素的弧，建立了&amp;quot;&lt;code&gt;ate&lt;/code&gt;&amp;ldquo;是中心词，&amp;ldquo;I&amp;quot;依存于&amp;rdquo;&lt;code&gt;ate&lt;/code&gt;&amp;ldquo;的关系。然后将&amp;rdquo;&lt;code&gt;I&lt;/code&gt;&amp;ldquo;移出栈。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shift&lt;/strong&gt;操作 将&amp;rdquo;&lt;code&gt;fish&lt;/code&gt;&amp;ldquo;从缓冲区移入栈中&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Right Arc&lt;/strong&gt;操作 生成了一个由第二个元素指向栈顶元素的弧，建立了&amp;rdquo;&lt;code&gt;ate&lt;/code&gt;&amp;ldquo;是中心词,&amp;quot;&lt;code&gt;fish&lt;/code&gt;&amp;ldquo;依存于&amp;rdquo;&lt;code&gt;ate&lt;/code&gt;&amp;ldquo;的关系。然后将&amp;rdquo;&lt;code&gt;fish&lt;/code&gt;&amp;ldquo;移出栈。&lt;/li&gt;
&lt;li&gt;最后的&lt;strong&gt;Right Arc&lt;/strong&gt;操作 使&amp;rdquo;&lt;code&gt;[root]&lt;/code&gt;&amp;ldquo;指向&amp;rdquo;&lt;code&gt;ate&lt;/code&gt;&amp;quot;，并在&amp;rdquo;&lt;code&gt;ate&lt;/code&gt;&amp;ldquo;出栈后，只剩下根节点，操作完成。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="evaluation-of-dependency-parsing"&gt;Evaluation of Dependency Parsing
&lt;/h4&gt;&lt;p&gt;评价依存分析的指标分为&lt;strong&gt;UAS&lt;/strong&gt;（无标签附件分数）和 &lt;strong&gt;LAS&lt;/strong&gt;（有标签附件分数），下面是一个具体的例子: &amp;ldquo;&lt;em&gt;[ROOT] She saw the video lecture.&lt;/em&gt;&amp;quot;，表中&amp;quot;Gold&amp;quot;是标准答案，&amp;ldquo;Parsed&amp;quot;是分析出的答案：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/dependency-6.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可见，UAS计算的是&lt;code&gt;Head&lt;/code&gt;寻找得是否正确，本例中第三个&amp;quot;the&amp;quot;的依存词和标准不符。&lt;/li&gt;
&lt;li&gt;LAS计算的它们之间的关系类型是否标注正确，即&lt;code&gt;Head&lt;/code&gt;和关系标签(&lt;code&gt;label&lt;/code&gt;)都要一致，本例中只有&amp;quot;She&amp;quot;和&amp;quot;saw&amp;quot;是与标准相符的。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="neural-dependency-parsing"&gt;Neural dependency parsing
&lt;/h3&gt;&lt;p&gt;More than 95% of parsing time is consumed by feature computation&lt;/p&gt;
&lt;p&gt;所以，我们可以用神经网络来加速特征提取，当然其方法还是基于上面的基于转移（Transition-based）的神经依存句法分析器。具体使用了了向量化、非线性等深度学习知识搭建出了第一个基于神经网络的依存分析器(2014年)。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/dependency-7.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;h2 id="recurrent-neural-networks"&gt;Recurrent Neural Networks
&lt;/h2&gt;&lt;h3 id="language-modeling"&gt;Language Modeling
&lt;/h3&gt;&lt;p&gt;语言模型(Language Modeling) 简单地讲就是输入文本（词），输出概率。
&lt;/p&gt;
$$
\begin{aligned}
P(\boldsymbol{x}^{(1)}, \dots, \boldsymbol{x}^{(T)}) &amp;= P(\boldsymbol{x}^{(1)}) \times P(\boldsymbol{x}^{(2)} | \boldsymbol{x}^{(1)}) \times \dots \times P(\boldsymbol{x}^{(T)} | \boldsymbol{x}^{(T-1)}, \dots, \boldsymbol{x}^{(1)}) \\
&amp;= \prod_{t=1}^{T} \underbrace{P(\boldsymbol{x}^{(t)} | \boldsymbol{x}^{(t-1)}, \dots, \boldsymbol{x}^{(1)})}_{\text{This is what our LM provides}}
\end{aligned}
$$&lt;p&gt;
$P(\boldsymbol{x}^{(1)}, \dots, \boldsymbol{x}^{(T)})$ 表示一整个序列（如一句话）出现的概率，通过将联合概率&lt;strong&gt;分解为一系列条件概率的乘积&lt;/strong&gt;（链式法则），我们可以计算序列的概率。语言模型的核心任务就是根据之前的上下文 $\boldsymbol{x}^{(t-1)}, \dots, \boldsymbol{x}^{(1)}$ 来&lt;strong&gt;预测&lt;/strong&gt;下一个 token $\boldsymbol{x}^{(t)}$ 出现的概率。&lt;/p&gt;
&lt;h3 id="n-gram-language-models"&gt;n-gram Language Models
&lt;/h3&gt;&lt;p&gt;An n-gram is a chunk of n consecutive words. n表示由几个词构成一个单位，即n-gram。为实现n-gram Language Models，步骤如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;首先，做一个&lt;strong&gt;马尔可夫假设&lt;/strong&gt;：第 $t+1$ 个词 $x^{(t+1)}$ 仅取决于其前面的 $n-1$ 个词。
&lt;/p&gt;
$$
 P(x^{(t+1)} | x^{(t)}, \dots, x^{(1)}) = P(x^{(t+1)} | \underbrace{x^{(t)}, \dots, x^{(t-n+2)}}_{n-1 \text{ words}}) \quad \text{(assumption)}
 $$&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;利用条件概率的定义，上述公式可以写为 $n$-gram 与 $(n-1)$-gram 概率的比值：
&lt;/p&gt;
$$
 = \frac{P(x^{(t+1)}, x^{(t)}, \dots, x^{(t-n+2)}) \leftarrow \text{prob of a n-gram}}{P(x^{(t)}, \dots, x^{(t-n+2)}) \leftarrow \text{prob of a (n-1)-gram}} \quad \text{(definition of conditional prob)}
 $$&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通过在大型文本语料库中&lt;strong&gt;计数 (Counting)&lt;/strong&gt; 它们来统计词组出现的频率，来近似概率：
&lt;/p&gt;
$$
 \approx \frac{\text{count}(x^{(t+1)}, x^{(t)}, \dots, x^{(t-n+2)})}{\text{count}(x^{(t)}, \dots, x^{(t-n+2)})} \quad \text{(statistical approximation)}
 $$&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;举个例子，假设我们有一个4-gram Language Model，要预测最后一个空可能出现的单词：&lt;/p&gt;
&lt;p&gt;&amp;ldquo;&lt;em&gt;as the proctor started the clock, the students opened their &amp;hellip;&amp;hellip;&lt;/em&gt;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;我们只取最后的三个词组成的短语&amp;rdquo;&lt;em&gt;students opened their&lt;/em&gt;&amp;rdquo;
&lt;/p&gt;
$$
P(w\mid students\ opened\ their)=\frac{count(students\ opened\ their\ w)}{count(students\ opened\ their)}
$$&lt;p&gt;
根据语料库，&amp;quot;&lt;em&gt;students opened their books&lt;/em&gt;&amp;ldquo;可能是出现频率最高的，而更符合语境的&amp;rdquo;&lt;em&gt;&amp;hellip;&amp;hellip;exams&lt;/em&gt;&amp;ldquo;出现频率更低&lt;/p&gt;
&lt;h4 id="problems-with-n-gram-language-models"&gt;Problems with n-gram Language Models
&lt;/h4&gt;&lt;p&gt;当使用计数法计算概率，会面临&lt;strong&gt;稀疏性问题&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;如果词组 &amp;ldquo;students opened their $w$&amp;rdquo; 在训练数据中从未出现过，那么对于任何该词 $w$，其概率都将变为 &lt;strong&gt;0&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;通过为词表中的每个词 $w \in V$ 的计数增加一个极小的数值 $\delta$ (平滑化 Smoothing)解决&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果前缀 &amp;ldquo;students opened their&amp;rdquo; 在训练数据中从未出现过，我们将无法计算任何 $w$ 的概率，因为分母为 0。&lt;/p&gt;
&lt;p&gt;不再考虑完整的前缀，而是退而求其次，仅根据更短的上下文（例如 &amp;ldquo;opened their&amp;rdquo;）进行条件概率计算。(回退 Backoff)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;同时也会面临&lt;strong&gt;存储&lt;/strong&gt;的问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;需要存放语料库中所有n-gram的计数&lt;/li&gt;
&lt;li&gt;若是n需要增加，语料库的size也要大幅增加&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="a-fixed-window-neural-language-model"&gt;A fixed-window neural Language Model
&lt;/h3&gt;&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/RNN-1.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;输入层 (Words / One-hot vectors)&lt;/strong&gt;: 输入为单词的 one-hot 向量 $\boldsymbol{x}^{(1)}, \boldsymbol{x}^{(2)}, \boldsymbol{x}^{(3)}, \boldsymbol{x}^{(4)}$。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;嵌入层 (Concatenated word embeddings)&lt;/strong&gt;: 将单词转换为稠密的向量表示（embeddings），并进行拼接：
&lt;/p&gt;
$$
 \boldsymbol{e} = [\boldsymbol{e}^{(1)}; \boldsymbol{e}^{(2)}; \boldsymbol{e}^{(3)}; \boldsymbol{e}^{(4)}]
 $$&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;隐藏层 (Hidden layer)&lt;/strong&gt;: 通过权重矩阵 $W$ 和偏置 $b_1$ 进行线性变换，并经过激活函数 $f$（通常为 tanh 或 ReLU）：
&lt;/p&gt;
$$
 \boldsymbol{h} = f(W\boldsymbol{e} + \boldsymbol{b}_1)
 $$&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;输出层 (Output distribution)&lt;/strong&gt;: 经过权重矩阵 $U$ 和偏置 $b_2$，最后通过 &lt;strong&gt;softmax&lt;/strong&gt; 函数生成词典 $V$ 上的概率分布 $\hat{\boldsymbol{y}}$：
&lt;/p&gt;
$$
 \hat{\boldsymbol{y}} = \text{softmax}(U\boldsymbol{h} + \boldsymbol{b}_2) \in \mathbb{R}^{|V|}
 $$&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;那么相对于n-gram方法，改进了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;解决稀疏性问题&lt;/strong&gt;：不再依赖精确的计数，通过向量空间的相似性来泛化未见过的词组。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;存储优化&lt;/strong&gt;：不需要存储所有观察到的 $n$-gram 频率，只需存储模型参数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但是仍有问题没能解决：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;固定窗口限制&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;固定的上下文窗口通常太小。&lt;/li&gt;
&lt;li&gt;扩大窗口会线性导致权重矩阵 $W$ 的参数量激增。&lt;/li&gt;
&lt;li&gt;无论窗口多大，它永远无法捕捉超出该范围的长程依赖。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺乏对称性&lt;/strong&gt;：输入 $\boldsymbol{x}^{(1)}$ 和 $\boldsymbol{x}^{(2)}$ 与矩阵 $W$ 中完全不同的权重相乘，模型处理每个位置输入的方式没有一致性。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="rnn-language-model"&gt;RNN Language Model
&lt;/h3&gt;&lt;p&gt;&lt;a class="link" href="https://karpathy.github.io/2015/05/21/rnn-effectiveness/" target="_blank" rel="noopener"
 &gt;The Unreasonable Effectiveness of Recurrent Neural Networks&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/RNN-2.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RNN 的优点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可以处理&lt;strong&gt;任意长度&lt;/strong&gt;的输入。&lt;/li&gt;
&lt;li&gt;理论上，第 $t$ 步的计算可以使用&lt;strong&gt;很多步之前&lt;/strong&gt;的信息。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模型大小固定&lt;/strong&gt;：增加输入长度不会增加模型参数量。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对称性&lt;/strong&gt;：每一步应用相同的权重，处理输入的方式具有一致性。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;RNN 的缺点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;计算速度慢&lt;/strong&gt;：由于是递归计算，无法并行处理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实践难题&lt;/strong&gt;：在实际应用中，很难获取到&lt;strong&gt;很多步之前&lt;/strong&gt;的信息（梯度消失/爆炸问题）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="train-an-rnn-language-modle"&gt;Train an RNN Language Modle
&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;获取一个大型文本语料库，它是由单词序列组成的：$\boldsymbol{x}^{(1)}, \dots, \boldsymbol{x}^{(T)}$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将序列输入 RNN-LM，并为&lt;strong&gt;每一个时间步 $t$&lt;/strong&gt; 计算输出分布 $\hat{\boldsymbol{y}}^{(t)}$。这意味着模型在给定目前为止已见单词的情况下，预测&lt;strong&gt;每一个位置&lt;/strong&gt;上可能出现的单词概率分布。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;模型在每一个步长 $t$ 都会产生一个损失，第 $t$ 步的损失函数是预测概率分布 $\hat{\boldsymbol{y}}^{(t)}$ 与&lt;strong&gt;真实的下一个单词&lt;/strong&gt; $\boldsymbol{y}^{(t)}$（即 $\boldsymbol{x}^{(t+1)}$ 的 one-hot 向量）之间的&lt;strong&gt;交叉熵&lt;/strong&gt;
&lt;/p&gt;
$$
 J^{(t)}(\theta) = CE(\boldsymbol{y}^{(t)}, \hat{\boldsymbol{y}}^{(t)}) = - \sum_{w \in V} \boldsymbol{y}^{(t)}_w \log \hat{\boldsymbol{y}}^{(t)}_w = - \log \hat{\boldsymbol{y}}^{(t)}_{\boldsymbol{x}_{t+1}}
 $$&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;为了获得整个训练集的损失，需要将所有步骤的损失取平均值：
&lt;/p&gt;
$$
 J(\theta) = \frac{1}{T} \sum_{t=1}^{T} J^{(t)}(\theta) = \frac{1}{T} \sum_{t=1}^{T} - \log \hat{\boldsymbol{y}}^{(t)}_{\boldsymbol{x}_{t+1}}
 $$&lt;p&gt;
计算损失应用了&lt;strong&gt;Teacher Forcing&lt;/strong&gt;的概念，即并不使用模型在上一步&lt;strong&gt;实际预测&lt;/strong&gt;出的单词作为下一步的输入，而是直接将语料库中的&lt;strong&gt;真实正确答案&lt;/strong&gt;喂给模型。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一次性计算整个语料库 $\boldsymbol{x}^{(1)}, \dots, \boldsymbol{x}^{(T)}$ 的损失（Loss）和梯度（Gradients）在内存方面是&lt;strong&gt;极其昂贵的&lt;/strong&gt;，所以在实际操作中，我们将序列 $\boldsymbol{x}^{(1)}, \dots, \boldsymbol{x}^{(T)}$ 看作是一个个&lt;strong&gt;句子&lt;/strong&gt;或&lt;strong&gt;文档&lt;/strong&gt;，使用随机梯度下降来对一**小块数据 **计算损失和梯度，并立即进行参数更新&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="backpropagation-for-rnn"&gt;Backpropagation for RNN
&lt;/h4&gt;&lt;p&gt;RNN参数的训练，采用&lt;strong&gt;随时间&lt;/strong&gt;的反向传播，沿着时间步 $i = t, \dots, 0$ 反向传播，并在过程中累加梯度。&lt;/p&gt;
&lt;p&gt;由于 $\boldsymbol{W}_h$ 在每一个时间步都是共享的（相同的权重），因此总梯度是每一个时间步产生的梯度之和。
&lt;/p&gt;
$$
\frac{\partial J^{(t)}}{\partial \boldsymbol{W}_h} = \sum_{i=1}^{t} \left. \frac{\partial J^{(t)}}{\partial \boldsymbol{W}_h} \right|_{(i)} \frac{\partial \boldsymbol{W}_h|_{(i)}}{\partial \boldsymbol{W}_h} = \sum_{i=1}^{t} \left. \frac{\partial J^{(t)}}{\partial \boldsymbol{W}_h} \right|_{(i)}
$$&lt;p&gt;
随着序列增长，完整的反向传播计算量极大，且容易出现梯度消失或爆炸问题。在实际应用中，为了提高训练效率，通常会在约 &lt;strong&gt;20 个时间步&lt;/strong&gt;后进行“截断”。&lt;/p&gt;
&lt;h4 id="exploding-gradient"&gt;Exploding Gradient
&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;梯度爆炸&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果 $W_h$ 的特征值（大致可以理解为权重的大小）大于 1。&lt;/li&gt;
&lt;li&gt;随着时间步 $T$ 的增加，梯度会呈&lt;strong&gt;指数级增长&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;模型权重会更新得过大，导致网络变得极不稳定，参数值可能会溢出（变成 NaN），训练崩溃。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果在更新模型参数前，梯度的&lt;strong&gt;范数 (norm)&lt;/strong&gt; 超过了预设的某个&lt;strong&gt;阈值&lt;/strong&gt;，则按比例将其缩小，如果 $\|\hat{\boldsymbol{g}}\| \ge threshold$ ，则进行&lt;strong&gt;梯度裁剪&lt;/strong&gt;，梯度裁剪&lt;strong&gt;让模型更新时保持在&lt;/strong&gt;相同的方向&lt;strong&gt;上，但只迈出&lt;/strong&gt;更小的一步：
&lt;/p&gt;
$$
\hat{\boldsymbol{g}} \leftarrow \frac{threshold}{\|\hat{\boldsymbol{g}}\|} \hat{\boldsymbol{g}}
$$&lt;h4 id="vanishing-gradient"&gt;Vanishing Gradient
&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;梯度消失&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果 $W_h$ 的特征值小于 1，或者激活函数（如图中提到的 $f$ 或 tanh）的导数小于 1。&lt;/li&gt;
&lt;li&gt;梯度会随着反向传播的步数增加而&lt;strong&gt;指数级减小&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;这对应了提到的 RNN 缺点：&lt;strong&gt;“在实践中，很难获取到很多步之前的信息”&lt;/strong&gt;。当梯度变得极其微小时，远距离的权重更新几乎停滞，模型“忘记”了长期的上下文。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于 vanilla RNN（基础 RNN）来说，学习如何跨越多个时间步长来&lt;strong&gt;保留信息&lt;/strong&gt;实在是太困难了，因为隐藏状态 $\boldsymbol{h}^{(t)}$ 会被不断地&lt;strong&gt;重写&lt;/strong&gt;：
&lt;/p&gt;
$$
\boldsymbol{h}^{(t)} = \sigma(\boldsymbol{W}_h \boldsymbol{h}^{(t-1)} + \boldsymbol{W}_x \boldsymbol{x}^{(t)} + \boldsymbol{b})
$$&lt;p&gt;
所以，我们引入独立记忆 ，如LSTM。或者建立直接连接，如Attention机制。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="long-short-term-memory"&gt;&lt;strong&gt;Long&lt;/strong&gt; &lt;strong&gt;Short-Term&lt;/strong&gt; Memory
&lt;/h3&gt;&lt;p&gt;&lt;a class="link" href="https://colah.github.io/posts/2015-08-Understanding-LSTMs/" target="_blank" rel="noopener"
 &gt;Understanding LSTM Networks &amp;ndash; colah&amp;rsquo;s blog&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;遗忘门 (Forget gate)&lt;/strong&gt;：控制从上一个细胞状态中保留什么以及忘记什么。
&lt;/p&gt;
$$
\boldsymbol{f}^{(t)} = \sigma (\boldsymbol{W}_f \boldsymbol{h}^{(t-1)} + \boldsymbol{U}_f \boldsymbol{x}^{(t)} + \boldsymbol{b}_f)
$$&lt;p&gt;
&lt;strong&gt;输入门 (Input gate)&lt;/strong&gt;：控制新细胞内容的哪些部分被写入到细胞中。
&lt;/p&gt;
$$
\boldsymbol{i}^{(t)} = \sigma (\boldsymbol{W}_i \boldsymbol{h}^{(t-1)} + \boldsymbol{U}_i \boldsymbol{x}^{(t)} + \boldsymbol{b}_i)
$$&lt;p&gt;
&lt;strong&gt;输出门 (Output gate)&lt;/strong&gt;：控制细胞的哪些部分被输出到隐藏状态中。
&lt;/p&gt;
$$
\boldsymbol{o}^{(t)} = \sigma (\boldsymbol{W}_o \boldsymbol{h}^{(t-1)} + \boldsymbol{U}_o \boldsymbol{x}^{(t)} + \boldsymbol{b}_o)
$$&lt;p&gt;
&lt;strong&gt;New cell content&lt;/strong&gt;：这是要被写入细胞的新内容（即我们在之前讨论中提到的“候选内容”）。
&lt;/p&gt;
$$
\tilde{\boldsymbol{c}}^{(t)} = \tanh (\boldsymbol{W}_c \boldsymbol{h}^{(t-1)} + \boldsymbol{U}_c \boldsymbol{x}^{(t)} + \boldsymbol{b}_c)
$$&lt;p&gt;
&lt;strong&gt;Cell state&lt;/strong&gt;：擦除（“忘记”）上一个细胞状态中的某些内容，并写入（“输入”）一些新的细胞内容。
&lt;/p&gt;
$$
\boldsymbol{c}^{(t)} = \boldsymbol{f}^{(t)} \odot \boldsymbol{c}^{(t-1)} + \boldsymbol{i}^{(t)} \odot \tilde{\boldsymbol{c}}^{(t)}
$$&lt;p&gt;
&lt;strong&gt;隐藏状态 (Hidden state)&lt;/strong&gt;：从细胞中读取（“输出”）某些内容。
&lt;/p&gt;
$$
\boldsymbol{h}^{(t)} = \boldsymbol{o}^{(t)} \odot \tanh \boldsymbol{c}^{(t)}
$$&lt;h4 id="step-by-step-lstm-walk-through"&gt;Step-by-Step LSTM Walk Through
&lt;/h4&gt;&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/LSTM3-chain.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在上图中，每条线都承载着一个完整的向量，从一个节点的输出指向其他节点的输入。粉色圆圈代表逐点运算，例如向量加法，而黄色方框代表已学习的神经网络层。合并的线条表示连接，而分叉的线条表示其内容被复制，并将副本发送到不同的位置。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/LSTM3-C-line.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;LSTM 的关键在于单元状态，也就是图中贯穿顶部的水平线。&lt;/p&gt;
&lt;p&gt;单元状态就像一条传送带，它沿着整个链条直线传递，只有一些微小的线性交互。信息很容易原封不动地沿着传送带流动。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;LSTM 能够对单元状态进行信息添加或移除，这种操作由称为“门”的结构进行精细调控。&lt;/p&gt;
&lt;p&gt;门是一种选择性地允许信息通过的方式。它们由一个 sigmoid 神经网络层和一个逐点乘法运算组成。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/LSTM3-focus-f.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LSTM 的第一步是决定我们要从细胞状态中丢弃什么信息 。这个决定由一个称为“遗忘门层”的 sigmoid 层来做出 。它接收 $h_{t-1}$ 和 $x_t$，并为细胞状态 $C_{t-1}$ 中的每个数字输出一个介于 0 和 1 之间的数值；1 代表“完全保留”，而 0 代表“彻底丢弃” 。&lt;/li&gt;
&lt;li&gt;让我们回到之前那个尝试根据前面所有单词预测下一个单词的语言模型例子 。在这样一个问题中，细胞状态可能包含了当前主语的性别信息，以便模型能使用正确的代词 。当我们看到一个新的主语时，我们会希望忘记旧主语的性别 。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/LSTM3-focus-i.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;下一步是决定我们要将什么新信息存储在细胞状态中 。这包含两个部分。首先，一个称为“输入门层”的 sigmoid 层决定了我们将更新哪些值；接着，一个 $\tanh$ 层创建了一个包含新候选值的向量 $\tilde{C}_t$，这些值可以被添加到状态中。在下一步里，我们将结合这两个部分来对状态进行更新 。&lt;/li&gt;
&lt;li&gt;在我们的语言模型例子中，我们会希望将新主语的性别添加到细胞状态中，以替换我们正在遗忘的旧性别信息 。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/LSTM3-focus-C.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;现在是时候将旧的细胞状态 $C_{t-1}$ 更新为新的细胞状态 $C_t$ 了。前面的步骤已经决定了要做什么，我们只需要去实际执行它 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;我们将旧状态乘以 $f_t$，以此来忘掉我们之前决定要忘记的信息 。然后我们加上 $i_t * \tilde{C}_t$。这就是新的候选值，根据我们决定更新每个状态值的程度进行了缩放 。&lt;/p&gt;
&lt;p&gt;在语言模型的例子中，正是在这里，我们实际丢弃了关于旧主语性别的信息，并添加了新信息，就像我们在前面步骤中决定的那样 。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.kawausococo.top/uploads/posts/cs224n/LSTM3-focus-o.png" alt="" loading="lazy" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;最后，我们需要决定我们要输出什么 。这个输出将基于我们的细胞状态，但会是一个经过过滤的版本 。&lt;/p&gt;
&lt;p&gt;首先，我们运行一个 sigmoid 层，它决定了我们要输出细胞状态的哪些部分 。然后，我们将细胞状态通过 $\tanh$（将值推到 -1 和 1 之间）并将其乘以 sigmoid 门的输出，这样我们就只输出了我们决定输出的部分 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;以语言模型为例，由于它刚刚处理了一个主语，接下来可能会倾向于输出与谓语动词相关的信息，以预判后续内容。例如，模型可能会输出该主语是单数还是复数，这样如果接下来出现动词，我们就能知道该以何种形式进行词形变化。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="how-does-lstm-solve-vanishing-gradients"&gt;How does LSTM solve vanishing gradients
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;LSTM 架构使得 RNN 更容易在多个时间步长内保留信息。例如，如果某个单元维度的遗忘门设置为 1，输入门设置为 0，那么该单元的信息将被无限期地保留。&lt;/p&gt;
&lt;p&gt;相比之下，普通的 RNN 更难学习一个循环权重矩阵 $W_h$，以保留隐藏状态中的信息。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;尽管梯度消失/爆炸现象无法避免，但对于长距离依赖关系，模型还可以创建更直接、更线性的直通连接。比如ResNet, DenseNet&amp;hellip;&amp;hellip;都在模块之间、层之间不同程度地创建了直接连接。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="bidirectional-rnns"&gt;Bidirectional RNNs
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;传统的单向 RNN 或 LSTM 存在一个明显的局限：在处理序列时，它只能“向左看”（即只利用过去的历史上下文）。然而，在许多 NLP 任务（如情感分类、命名实体识别或句子整体理解）中，当前词的准确含义往往也依赖于“右侧”（未来）的上下文。&lt;/li&gt;
&lt;li&gt;为了解决这个问题，研究者引入了双向架构（通常使用 LSTM 实现，即 BiLSTM）：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;前向 (Forward) RNN&lt;/strong&gt;：按照从左到右的顺序处理输入序列，计算出一系列隐藏状态 $\overrightarrow{h}_t$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;后向 (Backward) RNN&lt;/strong&gt;：按照从右到左的逆序处理同一个输入序列，计算出一系列隐藏状态 $\overleftarrow{h}_t$。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;拼接状态 (Concatenated State)&lt;/strong&gt;：在每一个时间步 $t$，将前向和后向的隐藏状态拼接在一起，形成该位置的最终表示 $h_t = [\overrightarrow{h}_t; \overleftarrow{h}_t]$。这样，每个词的特征表示就同时包含了整个句子的左侧和右侧完整信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;双向 LSTM 的特征提取能力非常强大，但它&lt;strong&gt;仅适用于能够一次性获取完整输入序列的任务&lt;/strong&gt;（例如对整段文本进行分类，或翻译时的源句子编码）。它&lt;strong&gt;不能&lt;/strong&gt;用于传统的语言模型（Language Modeling），因为语言模型的本质任务是“预测下一个词”，如果允许模型看到右侧的“未来”信息，就违背了自回归预测的初衷。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="neural-machine-translation"&gt;Neural Machine Translation
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;神经机器翻译是 NLP 深度学习领域的第一个巨大成功。NMT 主要是基于 &lt;strong&gt;Sequence-to-Sequence (Seq2Seq)&lt;/strong&gt; 架构，该架构的核心正是由两个 RNN（通常是 LSTM）组成的：&lt;strong&gt;编码器 (Encoder)&lt;/strong&gt; 和 &lt;strong&gt;解码器 (Decoder)&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;Encoder负责读取源语言句子，但在读取过程中它不产生实际的翻译输出，而是不断更新其隐藏状态。当 Encoder 处理完源句子的最后一个词后，其&lt;strong&gt;最终的隐藏状态&lt;/strong&gt;（Final Hidden State）被视作整个句子的语义浓缩。它充当了一个“信息瓶颈”，因为整个源句子的所有复杂含义都必须被压缩进这一个固定维度的向量中。&lt;/li&gt;
&lt;li&gt;Decodere端的 LSTM 本质上是一个&lt;strong&gt;条件语言模型 (Conditional Language Model)&lt;/strong&gt;，初始隐藏状态不再是随机的或全零的，而是被严格赋值为 Encoder 输出的那个“瓶颈”向量。这意味着 Decoder 的所有生成动作都是&lt;strong&gt;以源句子的语义向量为条件&lt;/strong&gt;展开的。
在每一个时间步，它根据当前的隐藏状态输出概率最高的词，并将该词（Feeding in last word）作为下一步的输入继续循环，直到生成句子结束标记 &lt;code&gt;&amp;lt;EOS&amp;gt;&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>