|
|
@@ -10,20 +10,20 @@
|
|
|
|
|
|
**(1)统计语言模型与** **N-gram** **的思想**
|
|
|
|
|
|
-在深度学习兴起之前,统计方法是语言模型的主流。其核心思想是,一个句子出现的概率,等于该句子中每个词出现的条件概率的连乘。对于一个由词 $$w_1,w_2,dots,w_m$$ 构成的句子 S,其概率 P(S) 可以表示为:
|
|
|
+在深度学习兴起之前,统计方法是语言模型的主流。其核心思想是,一个句子出现的概率,等于该句子中每个词出现的条件概率的连乘。对于一个由词 $w_1,w_2,dots,w_m$ 构成的句子 S,其概率 P(S) 可以表示为:
|
|
|
|
|
|
$$P(S)=P(w_1,w_2,…,w_m)=P(w_1)⋅P(w_2∣w_1)⋅P(w_3∣w_1,w_2)⋯P(w_m∣w_1,…,w_{m−1})$$
|
|
|
|
|
|
-这个公式被称为概率的链式法则。然而,直接计算这个公式几乎是不可能的,因为像 $$P(w_m∣w_1,dots,w_{m−1})$$ 这样的条件概率太难从语料库中估计了,词序列 $$w_1,dots,w_{m−1}$$ 可能从未在训练数据中出现过。
|
|
|
+这个公式被称为概率的链式法则。然而,直接计算这个公式几乎是不可能的,因为像 $P(w_m∣w_1,dots,w_{m−1})$ 这样的条件概率太难从语料库中估计了,词序列 $w_1,dots,w_{m−1}$ 可能从未在训练数据中出现过。
|
|
|
|
|
|
<div align="center">
|
|
|
<img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/3-figures/1757249275674-0.png" alt="图片描述" width="90%"/>
|
|
|
<p>图 3.1 马尔可夫假设示意图</p>
|
|
|
</div>
|
|
|
|
|
|
-为了解决这个问题,研究者引入了**马尔可夫假设 (Markov Assumption)**。其核心思想是:我们不必回溯一个词的全部历史,可以近似地认为,一个词的出现概率只与它前面有限的 $$n−1$$ 个词有关,如图3.1所示。基于这个假设建立的语言模型,我们称之为 **N-gram** **模型**。这里的 "N" 代表我们考虑的上下文窗口大小。让我们来看几个最常见的例子来理解这个概念:
|
|
|
+为了解决这个问题,研究者引入了**马尔可夫假设 (Markov Assumption)**。其核心思想是:我们不必回溯一个词的全部历史,可以近似地认为,一个词的出现概率只与它前面有限的 $n−1$ 个词有关,如图3.1所示。基于这个假设建立的语言模型,我们称之为 **N-gram** **模型**。这里的 "N" 代表我们考虑的上下文窗口大小。让我们来看几个最常见的例子来理解这个概念:
|
|
|
|
|
|
-- **Bigram (当 N=2 时)**:这是最简单的情况,我们假设一个词的出现只与它前面的一个词有关。因此,链式法则中复杂的条件概率 $$P(w_i∣w_1,dots,w_{i−1})$$ 就可以被近似为更容易计算的形式:
|
|
|
+- **Bigram (当 N=2 时)**:这是最简单的情况,我们假设一个词的出现只与它前面的一个词有关。因此,链式法则中复杂的条件概率 $P(w_i∣w_1,dots,w_{i−1})$ 就可以被近似为更容易计算的形式:
|
|
|
|
|
|
$$P(w_{i}∣w_{1},…,w_{i−1})≈P(w_{i}∣w_{i−1})$$
|
|
|
|
|
|
@@ -31,28 +31,28 @@ $$P(w_{i}∣w_{1},…,w_{i−1})≈P(w_{i}∣w_{i−1})$$
|
|
|
|
|
|
$$P(w_i∣w_1,…,w_{i−1})≈P(w_i∣w_{i−2},w_{i−1})$$
|
|
|
|
|
|
-这些概率可以通过在大型语料库中进行**最大似然估计** **(****Maximum Likelihood Estimation****,** **MLE****)** 来计算。这个术语听起来很复杂,但其思想非常直观:最可能出现的,就是我们在数据中看到次数最多的。例如,对于 Bigram 模型,我们想计算在词 $$w_{i−1}$$ 出现后,下一个词是 $$w_i$$ 的概率 $$P(w_i∣w_{i−1})$$。根据最大似然估计,这个概率可以通过简单的计数来估算:
|
|
|
+这些概率可以通过在大型语料库中进行**最大似然估计** **(****Maximum Likelihood Estimation****,** **MLE****)** 来计算。这个术语听起来很复杂,但其思想非常直观:最可能出现的,就是我们在数据中看到次数最多的。例如,对于 Bigram 模型,我们想计算在词 $w_{i−1}$ 出现后,下一个词是 $w_i$ 的概率 $P(w_i∣w_{i−1})$。根据最大似然估计,这个概率可以通过简单的计数来估算:
|
|
|
|
|
|
$$P(w_i∣w_{i−1})=Count(w_{i−1})Count(w_{i−1},w_i)$$
|
|
|
|
|
|
这里的 `Count()` 函数就代表“计数”:
|
|
|
|
|
|
-- $$Count(w_i−1,w_i)$$:表示词对 $$(w_{i−1},w_i)$$ 在语料库中连续出现的总次数。
|
|
|
-- $$Count(w_{i−1})$$:表示单个词 $$w_{i−1}$$ 在语料库中出现的总次数。
|
|
|
+- $Count(w_i−1,w_i)$:表示词对 $(w_{i−1},w_i)$ 在语料库中连续出现的总次数。
|
|
|
+- $Count(w_{i−1})$:表示单个词 $w_{i−1}$ 在语料库中出现的总次数。
|
|
|
|
|
|
-公式的含义就是:我们用“词对 $$Count(w_i−1,w_i)$$ 出现的次数”除以“词 $$Count(w_{i−1})$$ 出现的总次数”,来作为 $$P(w_i∣w_{i−1})$$ 的一个近似估计。
|
|
|
+公式的含义就是:我们用“词对 $Count(w_i−1,w_i)$ 出现的次数”除以“词 $Count(w_{i−1})$ 出现的总次数”,来作为 $P(w_i∣w_{i−1})$ 的一个近似估计。
|
|
|
|
|
|
为了让这个过程更具体,我们来手动进行一次计算。假设我们拥有一个仅包含以下两句话的迷你语料库:`datawhale agent learns`, `datawhale agent works`。我们的目标是:使用 Bigram (N=2) 模型,估算句子 `datawhale agent learns` 出现的概率。根据 Bigram 的假设,我们每次会考察连续的两个词(即一个词对)。
|
|
|
|
|
|
-**第一步:计算第一个词的概率** $$P(datawhale)$$ 这是 `datawhale` 出现的次数除以总词数。`datawhale` 出现了 2 次,总词数是 6。
|
|
|
+**第一步:计算第一个词的概率** $P(datawhale)$ 这是 `datawhale` 出现的次数除以总词数。`datawhale` 出现了 2 次,总词数是 6。
|
|
|
|
|
|
$$P(\text{datawhale}) = \frac{\text{总语料中"datawhale"的数量}}{\text{总语料的词数}} = \frac{2}{6} \approx 0.333$$
|
|
|
|
|
|
-**第二步:计算条件概率** $$P(agent∣datawhale)$$ 这是词对 `datawhale agent` 出现的次数除以 `datawhale` 出现的总次数。`datawhale agent` 出现了 2 次,`datawhale` 出现了 2 次。
|
|
|
+**第二步:计算条件概率** $P(agent∣datawhale)$ 这是词对 `datawhale agent` 出现的次数除以 `datawhale` 出现的总次数。`datawhale agent` 出现了 2 次,`datawhale` 出现了 2 次。
|
|
|
|
|
|
$$P(\text{agent}|\text{datawhale}) = \frac{\text{Count}(\text{datawhale agent})}{\text{Count}(\text{datawhale})} = \frac{2}{2} = 1$$
|
|
|
|
|
|
-**第三步:计算条件概率** $$P(learns∣agent)$$ 这是词对 `agent learns` 出现的次数除以 `agent` 出现的总次数。`agent learns` 出现了 1 次,`agent` 出现了 2 次。
|
|
|
+**第三步:计算条件概率** $P(learns∣agent)$ 这是词对 `agent learns` 出现的次数除以 `agent` 出现的总次数。`agent learns` 出现了 1 次,`agent` 出现了 2 次。
|
|
|
|
|
|
$$P(\text{learns}|\text{agent}) = \frac{\text{Count(agent learns)}}{\text{Count(agent)}} = \frac{1}{2} = 0.5$$
|
|
|
|
|
|
@@ -111,7 +111,7 @@ N-gram 模型的根本缺陷在于它将词视为孤立、离散的符号。为
|
|
|
其核心思想可以分为两步:
|
|
|
|
|
|
1. **构建一个语义空间**:创建一个高维的连续向量空间,然后将词汇表中的每个词都映射为该空间中的一个点。这个点(即向量)就被称为**词嵌入 (Word Embedding)** 或词向量。在这个空间里,语义上相近的词,它们对应的向量在空间中的位置也相近。例如,`agent` 和 `robot` 的向量会靠得很近,而 `agent` 和 `apple` 的向量会离得很远。
|
|
|
-2. **学习从上下文到下一个词的映射**:利用神经网络的强大拟合能力,来学习一个函数。这个函数的输入是前 $$n−1$$ 个词的词向量,输出是词汇表中每个词在当前上下文后出现的概率分布。
|
|
|
+2. **学习从上下文到下一个词的映射**:利用神经网络的强大拟合能力,来学习一个函数。这个函数的输入是前 $n−1$ 个词的词向量,输出是词汇表中每个词在当前上下文后出现的概率分布。
|
|
|
|
|
|
<div align="center">
|
|
|
<img src="https://raw.githubusercontent.com/datawhalechina/Hello-Agents/main/docs/images/3-figures/1757249275674-1.png" alt="图片描述" width="90%"/>
|
|
|
@@ -295,12 +295,12 @@ class DecoderLayer(nn.Module):
|
|
|
- **键 (Key, K)**: 代表句子中可被查询的词元“标签”或“索引”。
|
|
|
- **值 (Value, V)**: 代表词元本身所携带的“内容”或“信息”。
|
|
|
|
|
|
-这三个向量都是由原始的词嵌入向量乘以三个不同的、可学习的权重矩阵 ($$W^Q,W^K,W^V$$) 得到的。整个计算过程可以分为以下几步,我们可以把它想象成一次高效的开卷考试:
|
|
|
+这三个向量都是由原始的词嵌入向量乘以三个不同的、可学习的权重矩阵 ($W^Q,W^K,W^V$) 得到的。整个计算过程可以分为以下几步,我们可以把它想象成一次高效的开卷考试:
|
|
|
|
|
|
-- 准备“考题”和“资料”:对于句子中的每个词,都通过权重矩阵生成其$$Q,K,V$$向量。
|
|
|
-- 计算相关性得分:要计算词$$A$$的新表示,就用词$$A$$的$$Q$$向量,去和句子中所有词(包括$$A$$自己)的$$K$$向量进行点积运算。这个得分反映了其他词对于理解词$$A$$的重要性。
|
|
|
-- 稳定化与归一化:将得到的所有分数除以一个缩放因子$$\sqrt{d_{k}}$$($$d_{k}$$是$$K$$向量的维度),以防止梯度过小,然后用Softmax函数将分数转换成总和为1的权重,也就是归一化的过程。
|
|
|
-- 加权求和:将上一步得到的权重分别乘以每个词对应的$$V$$向量,然后将所有结果相加。最终得到的向量,就是词$$A$$融合了全局上下文信息后的新表示。
|
|
|
+- 准备“考题”和“资料”:对于句子中的每个词,都通过权重矩阵生成其$Q,K,V$向量。
|
|
|
+- 计算相关性得分:要计算词$A$的新表示,就用词$A$的$Q$向量,去和句子中所有词(包括$A$自己)的$K$向量进行点积运算。这个得分反映了其他词对于理解词$A$的重要性。
|
|
|
+- 稳定化与归一化:将得到的所有分数除以一个缩放因子$\sqrt{d_{k}}$($d_{k}$是$K$向量的维度),以防止梯度过小,然后用Softmax函数将分数转换成总和为1的权重,也就是归一化的过程。
|
|
|
+- 加权求和:将上一步得到的权重分别乘以每个词对应的$V$向量,然后将所有结果相加。最终得到的向量,就是词$A$融合了全局上下文信息后的新表示。
|
|
|
|
|
|
这个过程可以用一个简洁的公式来概括:
|
|
|
|
|
|
@@ -386,7 +386,7 @@ class MultiHeadAttention(nn.Module):
|
|
|
|
|
|
$$\operatorname{FFN}(x)=\max\left(0, xW_{1}+b_{1}\right) W_{2}+b_{2}$$
|
|
|
|
|
|
-其中,$$x$$是注意力子层的输出。 $$W_1,b_1,W_2,b_2$$是可学习的参数。通常,第一个线性层的输出维度 `d_ff` 会远大于输入的维度 `d_model`(例如 `d_ff = 4 * d_model`),经过 ReLU 激活后再通过第二个线性层映射回 `d_model` 维度。这种“先扩大再缩小”的模式,也被称为瓶颈结构,被认为有助于模型学习更丰富的特征表示。
|
|
|
+其中,$x$是注意力子层的输出。 $W_1,b_1,W_2,b_2$是可学习的参数。通常,第一个线性层的输出维度 `d_ff` 会远大于输入的维度 `d_model`(例如 `d_ff = 4 * d_model`),经过 ReLU 激活后再通过第二个线性层映射回 `d_model` 维度。这种“先扩大再缩小”的模式,也被称为瓶颈结构,被认为有助于模型学习更丰富的特征表示。
|
|
|
|
|
|
在我们的 PyTorch 骨架中,我们可以用以下代码来实现这个模块:
|
|
|
|
|
|
@@ -418,7 +418,7 @@ class PositionWiseFeedForward(nn.Module):
|
|
|
|
|
|
这个操作由两个部分组成:
|
|
|
|
|
|
-- **残差连接 (Add)**: 该操作将子模块的输入 `x` 直接加到该子模块的输出 `Sublayer(x)` 上。这一结构解决了深度神经网络中的**梯度消失 (Vanishing Gradients)** 问题。在反向传播时,梯度可以绕过子模块直接向前传播,从而保证了即使网络层数很深,模型也能得到有效的训练。其公式可以表示为:$$\text{Output} = x + \text{Sublayer}(x)$$。
|
|
|
+- **残差连接 (Add)**: 该操作将子模块的输入 `x` 直接加到该子模块的输出 `Sublayer(x)` 上。这一结构解决了深度神经网络中的**梯度消失 (Vanishing Gradients)** 问题。在反向传播时,梯度可以绕过子模块直接向前传播,从而保证了即使网络层数很深,模型也能得到有效的训练。其公式可以表示为:$\text{Output} = x + \text{Sublayer}(x)$。
|
|
|
- **层归一化 (Norm)**: 该操作对单个样本的所有特征进行归一化,使其均值为0,方差为1。这解决了模型训练过程中的**内部协变量偏移 (Internal Covariate Shift)** 问题,使每一层的输入分布保持稳定,从而加速模型收敛并提高训练的稳定性。
|
|
|
|
|
|
**3.1.2.5 位置编码**
|
|
|
@@ -432,8 +432,8 @@ $$ PE_{(pos,2i)}=\sin\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) $$,$
|
|
|
其中:
|
|
|
|
|
|
- pos是词元在序列中的位置(例如,0,1,2,...)
|
|
|
-- i是位置向量中的维度索引(从0到$$d_{\text{model}}/2$$)
|
|
|
-- $$d_{\text{model}}$$是词嵌入向量的维度(与我们模型中定义的一致)
|
|
|
+- i是位置向量中的维度索引(从0到$d_{\text{model}}/2$)
|
|
|
+- $d_{\text{model}}$是词嵌入向量的维度(与我们模型中定义的一致)
|
|
|
|
|
|
现在,我们来实现 `PositionalEncoding` 模块,并完成我们 Transformer 骨架代码的最后一部分。
|
|
|
|