Deep Learning Note: 5-3 语言模型

1. 语言建模

1.1. 语言模型

  考虑通过语音识别以下两个句子:

[code lang=”java”]The apple and pair salad.
The apple and pear salad.
[/code]

  这两个句子的读音完全一样,如果一个人听到这样的句子,可以很自然地认为听到了第二句,而对于算法来说,需要通过语言模型来判断当前输入的语音到底对应了那一句话。语言模型是语音识别和机器翻译系统的核心组件,其作用是,给定一个句子,给出该句子是特定一句话的概率,即给出:

\begin{equation}
P(y^{<1>}, y^{<2>},…,y^{<T_y>},) = 3.2 \times 10^{-13}
\end{equation}

  这里语言模型给出输入语音是特定一句话的概率,假设模型给出:

\begin{equation}
P(The \; apple \; and \; pair \; salad) = 3.2 \times 10^{-13} \\
P(The \; apple \; and \; pear \; salad) = 5.7 \times 10^{-10}
\end{equation}

  算法通过比较上面两个概率,判断输入的语音是 The apple and pear salad.

1.2. 符号化

  要使用 RNN 构建语言模型,首先需要有大量的文本语料(Corpus)作为训练集。假设训练集中有这样一句话:

[code lang=”java”]Cats average 15 hours of sleep a day.
[/code]

  首先要对这句话进行符号化(Tokenize),即使用词汇表将这句话映射为一个向量。如果算法需要知道句子的结束位置,则在句子末尾追加一个额外的符号 <EOS>

[code lang=”java”]Cats average 15 hours of sleep a day.<EOS>
[/code]

  这样上面的句子就被映射为 $[y^{<1>}, y^{<2>},…, y^{<9>}]$ 这样一个向量,其中 $y^{<9>}$ 对应 <EOS>

  如果句子的某个单词在词汇表中找不到,则将其替换为一个特殊符号 <UNK>。例如对于下面的句子:

[code lang=”java”]The Egyptian Mau is a breed of cat.<EOS>
[/code]

  其中的 Mau 没有包含在词汇表中,则将该词替换为 <UNK>,句子变为:

[code lang=”java”]The Egyptian <UNK> is a breed of cat.<EOS>
[/code]

1.3. RNN 模型

  训练语言模型使用的 RNN 结构如图 1 所示。

图 1

图 1

  假设使用的训练样本为:

[code lang=”java”]Cats average 15 hours of sleep a day.<EOS>
[/code]

  此时有 $T_x = T_y = 9$。

  在 $t = 1$ 时刻,网络的输入 $a^{<0>}$ 和 $x^{<1>}$ 都是一个零向量,此时网络通过 Softmax 输出句子第一个单词的概率。对于上面的句子,我们希望 $P(cats)$ 具有最高概率,使得网络能够预测第一个单词为 cats

  在 $t = 2$ 时刻,网络的输入为上一步的激活值 $a^{<1>}$ 和句子中的第 1 个单词 $y^{<1>}$。注意这里输入的 $y^{<1>}$ 是实际句子中的第一个单词 cats,而不是上一步中网络的预测 $\hat{y}^{<1>}$。此时网络计算的是,给定句子中第一个词为 cats 时,句子第二个单词的概率,即 p(w|cats),其中 $w$ 为词汇表中的各个单词。对于前述的句子,我们希望$P(average|cats)$ 具有最高概率,使得网络能够预测第二个单词为 average

  以此类推,在 $t = 9$ 时刻,网络的输入为上一步的激活值 $a^{<1>}$ 和句子中的第 8 个单词 $y^{<8>}$(day),网络计算的是给定句子中前 8 个词,句子的第 9 个词的概率。对于前述的句子,我们希望 $P(<EOS>|Cats…a\;day)$ 具有最高概率,使得网络预测句子结束。

  训练网络时,定义单个样本在 $t$ 时刻的损失函数如下:

\begin{equation}
L(\hat{y}^{<t>}, y^{<t>}) = -\sum_i y_i^{<t>} \log \hat{y}_i^{<t>} \tag{1}
\end{equation}

  单个样本整体的损失函数为:

\begin{equation}
L = \sum_t L^{<t>}(\hat{y}^{<t>}, y^{<t>}) \tag{2}
\end{equation}

  对于由此训练得到的网络,给出句子开头的几个词,网络可以预测句子的下一个词是什么。对于一个句子 $[y^{<1>}, y^{<2>}, y^{<3>}]$,网络通过分别计算 $P(y^{<1>})$、$P(y^{<2>}|y^{<1>})$、$P(y^{<3>}|y^{<1>}, y^{<2>})$,得到整句话的概率 $P(y^{<1>}, y^{<2>}, y^{<3>}) = P(y^{<1>})P(y^{<2>}|y^{<1>})P(y^{<3>}|y^{<1>}, y^{<2>})$。

2. 采样新的序列

  通过训练得到一个序列模型后,可以通过对其输出进行采样来大概地检查模型到底学到了什么。

  采样的步骤如图 2 所示。

图 2

图 2

  在 $t = 1$ 时刻,网络的输入 $a^{<0>}$ 和 $x^{<1>}$ 都是一个零向量,和训练时类似,网络通过 Softmax 得到句子第一个单词的概率,包括所有可能出现在句子第一个位置上的词及其概率,在这些词中进行采样,选择一个词作为 $\hat{y}^{<1>}$,假设选择了 the 这个词。

  在 $t = 2$ 时刻,网络的输入 $a^{<1>}$ 和 $\hat{y}^{<1>}$,注意这里输入的 $\hat{y}^{<1>}$ 是网络在上一个时刻的输出 the。此时通过网络可以得到当句子第一个词为 the 时,所有可能出现在句子第二个位置上的词及其概率,在这些词中采样,得到一个词作为 $\hat{y}^{<2>}$。

  以此类推,如果词汇表中包含表示句子结束的符号 <EOS>,则可以在网络输出 <EOS> 时结束,否则可以在采样的句子长度达到预先设定的上限(如 20 个词)时结束。如果词汇表中包含表示未知单词的符号 <UNK>,则网络也可能会输出这个符号,若不想在生成的句子中出现 <UNK>,则可以在采样得到 <UNK> 时再进行一次采样,直到得到非 <UNK> 的单词。

  通过以上步骤,就可以使用训练得到的 RNN 生成随机的句子。

  前面介绍的模型都是单词级的语言模型,使用的词汇表中包含很多单词,以单词为单位进行预测。根据需要,也可以构建字符级的语言模型,此时词汇表中包含所有字母,比如 26 个(大小写)英文字母、0 到 9 的数字以及空格、逗号、句号等标点符号。此时 $y^{\lt t \gt}$ 表示一个字符而不是一个单词。

  使用字符级模型的一个优势是不需要考虑有未知单词的情况,但它的一个主要缺点是序列的长度会变得很长,计算量很大。在捕获长距离的依赖,即学习句子前端的词对句子后端的词产生的影响上,字符级模型的性能不如单词级模型。

  目前自然语言处理的趋势是,大部分时候都会使用单词级的模型,随着计算能力的提升,也会使用字符级的模型来处理一些特定的问题,例如有很多未知单词的情况。