deeplearning.ai第五章

哪些例子使用了序列模型

  • 语音识别: 在给定输入的音频片段 X , 并要求输出对应的文字记录 Y.
  • 音乐生成: 输入可以是一个数字, 几个音符, 或者没有输入.
  • 情感分类: 输入可以是一串字符串, 输出是评分.
  • DNA 序列分析: 输入是特定的 DNA 序列, 标记出某部分匹配某种蛋白质.
  • 机器翻译: 一种语言到另外一种语言
  • 视频识别: 输入是一系列的视频帧, 识别其中的行为
  • 命名实体识别(Name entity recognition): 给定一个句子, 识别出句中的人名

概念

假设你想构建一个模型能够识别句中人名位置的序列模型, 输入的语句是这样的:

1
x: Harry Potter and Hermione Granger invented a new spell

那么这就是一个命名实体识别问题。 这常用于搜索引擎。
比如搜索过去 24 小时内, 所有新闻报道提及的人名, 使用这种方式就能够恰当的进行索引。
命名识别系统可以用来查找不同类型文本中的: 人名, 公司名,时间,地点,国家名, 货币名称,等。
你的输出分别对应一个标签, 就像这样
1
y: 1 1 0 1 1 0 0 0 0 

每个值对应一个单词是否是人名的一部分.
从技术上说, 这样的输出方式并不是最好的. 还有更加复杂的输出方式. 不仅仅能够表明输入词是否是人名的一部分, 还能告诉你人名在这个句子中是从哪开始和结束的.

因为输入的词是 9 个, 那么我们最终得到的标签也是 9 个.来进行匹配.
我们使用以下方式对 输入/输出 的序列进行标记:

$X^{(i)}$ 表示的是第 i 个样本序列的第 t 个元素,

$T_x^{(i)}$ 表示的是第 i 样本的输入序列长度.

$y^{(i)}$ 表示的是第 i 个标签序列的第 t 个元素,

$T_y^{(i)}$ 表示的是第 i 样本的输出序列长度.

词的表示

简单的方式是使用字典的形式, 对训练集里的词进行编号, 然后使用 one-hot 编码进行向量化:

RNN 模型

为什么不使用标准的网络

什么是 RNN 模型

在预测第二个词时, 不仅仅是使用第二个词的样本值, 还将使用第一个样本值产生的中间值.

向前传播

反向传播

不同类型的循环神经网络

使用 RNN 构建语言模型

假设我们输入的句子是:

1
Cats average 15 hours of sleep a day

我们需要的第一步是 Token 化这个句子, 并在最后加入 <EOS> 符号.

RNN 模型

从训练好的循环神经网络模型中采样出一个序列数据

RNN 经过训练可以生成序列数据,这句话表示利用训练好的 RNN 模型来生成新的序列数据,这一过程称为采样(Sampling)

RNN 的梯度消失问题

当你在使用 RNN 进行推理一段文字时, 比如是:

1
2
The cat which already ate ..., was full.
The cats which already ate ..., were full.

这时是需要输出的文字前后的语境需要一致, cat 对应 was, cats 对应 were. 前面的单词会对后续的单词产生影响. 但是基础的 RNN 对于这种长期依赖(long-termdependencies) 效应并不擅长捕获

为什么呢? 就如我们之前提到, 当一个神经网络的深度到达一定时, 预测值 $ \hat{y} $ 计算所得的梯度, 很难传播回去. 很难影响前层的权重.
RNN 也有着同样的问题.

而这导致 RNN 某个位置的输出受到局部的影响(local influences)较大. 较前的词对后续的词影响逐渐消无.

这算是 RNN 网络的一个缺点.

很深的网络出现的另外一个问题是梯度爆炸, 这也是 RNN 训练时常会出现的一个问题, 不过也很容易暴露, 会出现较多的 Nan.

这个时候可以使用梯度修剪, 如果大于某个阈值, 缩放梯度向量, 保证不会超过某个值. 或者设定最大值.

但是梯度消失问题更难处理, 后续将会继续介绍.

GRU 单元(Gated Recurrent Unit)

我们先来介绍简单的 GRU 版本
我们还是举例之前的句子:

1
The cat, which already ate ..., was full.

GRU 使用了一个新变量 c (memory cell). 作用是提供类似记忆的能力. 比如上方例句中一只猫是单数还是复数, 以便在后续还能继续判断是否是单数复数.

某个时间的值就是 GRU 中使用这个值, 代替了原来 RNN 中 的位置, 不过为了后续 LSTM 解释,这边还是使用两个不同的符号进行表示.

网络中会进行计算 的某个时间临时值:

然后通过计算一个 门(Gate) 进行决定这边使用 $ \Gamma_u $ 表示

并使用以下式子进行更新 :

因为 的输出在 0 ~ 1 之间, 如果我们这极端的认为 的值为 0 或者 1, 那么这相当于决定了是否使用当前计算的 进行替换 的值.

因为在计算的过程中, 的值大多数接近于 0 , 这将有助于 维持之前的值. 所以梯度消失的问题就得到了缓解.

在我们上面所列出的内容中, 可以不仅仅是一个数值, 它可能是一个 100 维度的向量.

所以进行计算的同时, 可以选择保存一些数值不变, 或者改变一些数值. 比如一些bit 用来记忆我们上面提到的单数和复数, 而有些可以用来帮助理解食物之类的信息.

之前的是简单版本的 GRU , 现在将展示完整版本的 GRU.
增加了 表示 有多大的相关性.

那么除了 的计算还需要进行 的计算

剩下的就是 的更新

LSTM

LSTM 是比 GRU 更为强大和通用的版本。除了增加了 计算方式也发生了变化.

其中门的计算过程有时也会加入 , 称为 peephole connection.

什么时候用 LSTM, 什么时候用 GRU , 并没有确定的准则, 即使这边先讲述了 GRU, 但 LSTM 出现的时间是比 GRU 早的.
不过 GRU 的优点是更加简单, 所以更容易构建更大的网络, 因为门相对较少, 运行速度上也有一定的优势, 适合构建更大的网络.

但 LSTM 更加强大和灵活, 如果默认的话大多数的人可能还是会优先选择 LSTM.

双向神经网络

为了了解双向 RNN 的动机, 我们先来回顾下这个在命名实体识别中的一个神经网络

这个网络的问题在于, 判断第三个词是不是一个人名的一部分时, 仅知道句子前半部分的信息是无法判断的, 所以说这是一个单向的 RNN , 无论其中这些构建的结构是 RNN / GRU / LSTM. 他们总是只有向前一个方向.

那么双向 RNN 是怎么处理这个问题的呢.

我们使用四个输入进行举例

为了解释简单, 我们使用四个输入, 向前传播与之前的并没有什么不同.

接着我们加上绿色部分, 这样网络就变成了一个无环图(acyclic graph)

当完成两个方向的计算后就能开始计算预测值

如果你要观察 的预测结果, 那么句中的所有词将会通过正向 或者反向 来影响 的输出. 预测不仅输入了过去的信息还将未来的信息也考虑在内.

上图中的单元, 可以不仅仅是普通的 RNN 单元, 也可以替换成 GRU 单元或者 LSTM 单元. 实际中很多 NLP 问题 或者说是有大量自然语言处理的文本会使用 LSTM 作为单元的双向 RNN , 这是一个不错的选择.

这种结构的优点是即使是预测句子中间的输出也能用上整个句子的信息, 缺点是你需要完整的数据序列,才能进行预测任意位置.

比如你要构建一个语音输入, 那么你就只能等待此人说完之后才能进行处理语音, 所以对于实际的语音识别有着更加复杂的模块.

对于自然语言处理应用, 你总是能获取整个句子, 所以 BRNN 实际上非常高效.

深层循环神经网络

目前学到不同版本的 RNN , 每个都能独当一面, 但如果要学习非常复杂的函数, 我们通常会把 RNN 的多个层堆叠在一起, 构建更深层次的模型.

这节课我们将会学习如何构建这些更深的 RNN
首先对于一个标准的神经网络, 有一个输入 x , 然后堆叠上隐含层, 然后中间传输激活值, 最后输出预测值

深层次的 RNN 的形式与这个有点像, 只要把标准神经网络按照时间展开就可以了
目前这是一个普通 RNN 的形式:

不过每层网络的激活值使用 格式进行表示, l 表示层数, t 表示时间
所以网络看起来像这样:

我们选取一个具体的值看看是怎么计算的: 假设我们要计算 的值.
这个值有两个输入

其中 在整个层中共享.

对于标准的神经网络, 你可能见过非常深的网络, 比如可能深于 100 层. 但是对于 RNN 来说 三层已经不少了, 由于时间的维度会让 RNN 网络变得相当大, 通常不会见到堆叠到 100 层.

但是有一种可能会见到, 那就是在每个时间点的堆叠输出后接入一个较深的标准神经网络, 但是并不会水平连接, 只是一个深层网络.

这些堆叠的网络单元可以是简单的 RNN 单元, 也可以是 GRU 单元或者 LSTM 单元.

或者也可以构建深层的双向 RNN 进行构建堆叠网络.

单词的表现形式

在之前的介绍中, 我们一直使用词汇表的方式进行表示词。 使用一个 one-hot 向量,表示一个词。
比如, man 这个词在词典里是第 5391 的位置, 那么我们使用一个向量, 并且在 5391 处表示为 1. 并使用 表示这个向量.

再比如 woman 这个词在词典里是第 8953 的位置, 那么我们使用一个向量, 并且在 8953 处表示为 1. 并使用 表示这个向量

其他诸如此类

这样表示词的方式其实是有一缺点的, 这种表示方式将词 "孤立" 了起来. 这样使算法对 相近词 的泛化能力不强

比如, 假设你的算法已经学习了

1
I want a glass of orange ___

那么下一个词汇将会是什么呢? 很大概率是 “果汁”. 而当算法遇到这样的句子

1
I want a glass of apple ___

因为算法不知道 苹果 和 橙子 的关系相比其他值更加接近.

不能从已学习的句子: "I want a glass of orange juice" 明白 "I want a glass of apple juice" 也同样是合理的.

这是因为任意两个 one-hot 向量的内积都是0. 这些词之间的距离并没有什么不同. 所以也就无法区分, 苹果和橙子 相比 苹果和国王 之间更加相近.

那么是否可以用另外一种方式进行表达呢? 如果我们不用 one-hot 而是使用 特征化(featurized representation)的方式表达呢? 将这些词汇的不同特征进行量化并构建表格:

上表是我们认为进行, 量化了一些指标, 并且手动为这些词汇进行特征的赋值. 我们可以想象种类繁多的不同特征, 假设你设想了 大概 300 个维度的特征, 那么这时候 man 这个单词相当于成为了一个具有 300 维度的向量.

为了方便后续解释使用, 我们使用 表示 man 这个词的向量. 其他的以此类推.

现在我们使用这种方式进行标识词汇之后, 那么 苹果和橙子 的词特征表达上总体会更为相似.

那么对于已经知道: "I want a glass of orange juice" 的算法, 大概率也会明白: "I want a glass of apple juice" 这个东西了.

但是手工的方式肯定是不行的, 后续将会介绍学习词嵌入的方式, 这种方式肯定是相对于使用 one-hot 表示不同的词汇要来得好的多.

最终习得的词汇, 可能并不会像这边那样好理解, 比如这边列出的性别 / 高贵 / 年龄 诸如此类. 但是确实能让算法理解 苹果和橙子 相比 国王和橙子 更为接近.

如果我们能够学习到一个 300 维度的特征向量, 或者说是 300 维度的词嵌入. 我们通常能做的一件事是将这些映射到一个二维空间里进行可视化. 常用的可视化算法是 t-SNE.

如果你映射了这些词汇之后, 你会发现相近的词汇在图上更加接近.

使用词嵌入

让我们从一个例子开始, 继续使用命名实体识别的例子.

Sally Johnson 是人名, 所以预测之后输出了 1. 而能够确认是人名是因为结尾的 “orange farmer”. 因为你知道种橙子的农民一定是个人.

当你使用词嵌入映射后的词作为输入, 那么在推断 Robert Lin is an apple farmer 时, 因为 apple farmerorange farmer 更为接近, 那么你的算法很容易就知道 Robert Lin 很大概率也是一个人名.

假设预测的句子变成了: Robert Lin is a durian cultivator 这时怎么办呢? 如果你只有一个很小的标记训练集, 你的训练集种甚至可能没有榴莲或者培育家这两个词汇.

但是如果你有一个已经训练好的词嵌入, 它会告诉你 榴莲 是水果, 培育工和农民性质相似, 那么你就有可能从你的训练集, 种橙子的农民归纳出榴莲培育家也是一个人.

词嵌入能达到这种效果其中的一个原因是, 词嵌入的算法会考察非常大的文本集, 也许是从网上找到的. 可能是 1 亿或者 100亿的单词. 从这些大量未进行标签的文本集中, 你可以发现橙子和榴莲相近, 农民和培育家相近. 因此, 嵌入词向量将他们聚集在一起.

通过这种未进行标签的文本集训练后的词嵌入向量, 即使你的训练集比较小 比如 100,000 个单词, 这使得你可以使用迁移学习, 将词嵌入已经习得的关系(比如这边的橙子 榴莲, 农民 培育家), 并将这些知识迁移到你的任务中. 比如你的只有少量标记的训练集数据的命名实体识别任务中.

当然 这边为了简化只画了单向的 RNN, 如果你实际应用的话, 你应该使用一个双向 RNN.

总结一下用词嵌入做迁移学习的步骤:

  1. 先从大量的文本集中学习词嵌入, 一个非常大的文本集, 或者下载一个预训练好的词嵌入模型. 网络上你可以找到不少
  2. 用这些词嵌入模型迁入到你新的, 只有少量标注的训练集任务中.
  3. 当你在你新的任务上训练模型时, 可以选择是否用新的数据继续微调词嵌入, 一般来说只有2步骤你有非常大的训练集才鼓励你这样做

最后, 词嵌入其实和人脸识别之间有着奇妙的关系, 人脸识别网络会学习不同人脸并且输出一个 128 维度的数据, 然后通过比较编码判断两张图片是否是同一个人脸. 词嵌入的想法和这个有点相似.

于人脸识别的不同点是, 人脸识别是为人脸图片甚至是没见过的人脸输出编码结果, 而词嵌入是拥有一个固定的词汇表, 比如是 10000 个单词的每个单词学习到一个固定的嵌入.

词嵌入的特性

词嵌入的另外一个特性是, 还能帮助实现类比推理. 尽管类比推理并不是自然语言处理应用用最重要的存在. 不过它能帮助人们理解词嵌入做了什么, 以及词嵌入能做什么.

假设下面这些是你需要的词的词嵌入的特征表示.

现在我提出一个问题, Man 如果对于 Woman, 那么 King 应该对应什么? 许多人会说 King 应该对应 Queen, 但是是否有一种算法能够自动推导这种关系? 接下来是实现的方法.

假设我们将 Man 的词嵌入表示为 , 其他的也类似.
我们使用 将大概会得到

而使用 将大概会得到

这样的结果表明了, 之间的差距是性别, 同样的 之间的差异根据向量的表示也是性别上的差异. 这就是为什么 结果是相同的.

所以得出这种类比推理结论的方法, 就是当算法被问及 man 对 woman 相当于 king 对于什么时. 算法所做的就是计算 的值并尝试找到一个向量使 的结果接近.

这种思想首先是被 Tomas Mikolov 和 Wen-tau Yih 还有 Geoffrey Zweig 提出的. 这是词嵌入领域影响力最为惊人和显著的成果之一. 这种思想帮助了研究者们对词嵌入领域建立了更加深刻的理解.

我们怎么把这种思想写成算法, 在下图中, 词嵌入向量在一个可能有 300 维度的空间里, 于是单词 man 代表的就是空间中的一个点, 另外一个单词 woman 代表的是空间的另外一个点, 其他 king/queen 同样也是. 图中画的是向量, 并且表示了他们之间在 性别这一维度的 差.

为了得到这样的类比推理, 你需要做的就是找到单词 w ,使得它于 的相似度最高. 如果结果理想的话将会得到单词 queen.

再继续之前, 我们再来看看左侧的图. 我们之前讨论过使用 t-SNE 算法来将单词可视化, 将这些 300 维度的数据通过非对称映射到 2D 平面上, 可以得知 t-SNE 算法中映射非常复杂且并非线性, 所以在 t-SNE 图中并不一定会有左边这样成平行四边形, 在原始的 300 维度上是可以.

再介绍一个常用的用来作为度量向量相似的函数: cosine similarity(余弦相似度)

之所叫余弦相似度, 是因为实际上是计算 u\v 向量之间的夹角的余弦值, 最终的值域在[-1, 1]之间。
如果你愿意的话,也可以使用平方距离或者是欧式距离进行表示

词嵌入的一个显著成果就是可学习类比关系的一般性. 比如,

  • Man:Woman as Boy:Girl
  • Ottawa:Canada as Nairobi:Kenya
  • Big:Bigger as Tall:Taller
  • Yen:Japan as Ruble:Russia

这些东西都能通过学习获得, 只要你在大型的文本语料库上实现一个词嵌入学习算法, 只要从足够大的语料库中进行学习, 算法就能自行发现这些模式.

嵌入矩阵

接下来我们要将学习词嵌入这一问题具体化, 当你应用算法来学习词嵌入时, 实际上是学习一个嵌入矩阵. 我们接下来将详细讲解.

和之前一样 假设我们的字典 含有 10000 个单词, 我们要做的就是学习一个嵌入矩阵 E.它将是一个 300 x 10000 的矩阵.
假设 orange 是第 6257 个词汇, 如果我们使用 来表示一个 one-hot 向量 那么将在 6257 的位置为1 其他位置为 0 .

我们使用 E 矩阵乘以这个 one-hot 向量, 我们就可以得到 E 中 orange 的所有维度. 我们使用 进行表示, 我们使用 表示 单词 j 的嵌入向量, 我们算法的学习目标就是对这个 E 进行学习.

不过实际上实现时, 并不会使用 one-hot 向量乘以 E 去获得某个单词的词向量, 而是使用特定的函数去查找矩阵 E 的某一列

学习词嵌入

接下来介绍实际学习词嵌入时, 会用到的算法。在学习算法的演化过程中, 人们一开始使用的算法比较复杂, 随着时间的推移,研究者不断发现他们能用更加简单的算法来达到一样好的效果, 尤其是在数据集很大的情况下.

但有一件事是, 现在流行的算法都特别简单. 如果我一开始就介绍这些简单算法, 你可能会觉得很神奇, 这些简单的算法是怎么起作用的. 所以我们先介绍些复杂的. 然后进行优化.

假设你正在构建一个语言模型, 你想要你的神经网络在输入以下这些词后能预测最后一个词:

上方的每个词下方都标注了对应词典中的索引.
我们使用对应词汇的 one-hot 向量从词嵌入向量获取对应值, 并将它们全部放进神经网络中, 然后再经过 softmax 层, 输出一个 one-hot 然后通过这个 one-hot 和目标 one-hot 进行比较, 进行对词嵌入的学习. 比如这边的例子中就是要输出 juice 的one-hot 向量.

这边输入的词汇是 6 个, 而每个词是300维度, 那么输入这个神经网络的维度就是 1800 (6*300).

但实际上更常见的方式是, 使用一个固定大小的窗口, 比如4个词汇, 然后预测下一个.

这时输入的维度就会固定到 1200, 使用了固定的窗口大小就意味着你可以处理任意长度的句子了, 因为输入的维度总是固定的.

以上所描述的就是早期最成功的学习词嵌入的算法之一. 那么其他的简单算法是怎么来的呢?
我们使用一个更加复杂点的句子来进行说明

1
I want a glass of orange juice to go alone whit my cereal

假设我们还是需要预测出 juice 这个词汇, 我们之前的算法是选取的上下文是目标词汇的前4个单词, 但是如果我们的主要目的并不是构建一个语言模型, 而是学习词嵌入向量. 那么就可以不仅仅选取目标词汇前的词作为上下文, 还可以将词汇后的词也纳入训练上下文.

比如一种做法就是选取目标词汇的前后4个词作为上下文.

如果你想用一个更加简单的上下文, 也许目标词汇前的一个单词就行.

还有一个效果非常好的做法, 就是取目标词汇附近的一个单词, 比如对于 juice 就可以问 在 glass 附近可能会有什么词. 这用了 skip-gram 模型的思想

Word2Vec

假设你的训练集中存在这样的一个句子

1
I want a glass of orange juice to go alone whit my cereal

在 skip-gram 模型中, 我们要做的是, 抽取上下文和目标词汇配对, 以构建一个监督学习问题. 上下文不一定总是在目标词汇之前, 或是离的最近的四个单词.

我们要做的是, 随机选取一个词作为上下文, 比如选择了 orange , 那么就在随机距离内选另外一个词, 也许你正好选到 juice, 也许你选到了 glass , 甚至可能选到了 my 这个单词, 于是这样我们就构建了一个监督学习问题. 不过这个监督学习的目的并不是为了训练得到一个能够预测 orange 附近可能出现的词汇是什么, 而是获得一个词嵌入向量.

接下来是模型的细节:
假设我们使用了一个拥有 10000 词的词汇表. 不过有时候训练的使用的词汇表会超过 1百万. 不过我们要解决的有监督学习问题是, 学习一种映射关系, 从上下文 c 到某个目标词汇 t, 我们先使用 c 的one-hot 向量 从 E 中得到 然后经过 softmax 得到 , 其中 softmax 是这样:

上方的 相当于是该词在 词嵌入矩阵中的具体向量是什么

Softmax 的 loss 函数和之前一样

实际上使用这个算法会遇到一些问题, 最主要的问题是计算速度, 在softmax 中, 你每次要计算 的时候, 你需要将词汇表中的所有词做求和计算, 也许 10000 也不算多, 但是如果你使用了 100,000 或者 1,000,000 的词汇表, 那么这个分母的求和操作是相当慢的, 不过实际上10,000 已经相当慢了, 所以扩大词汇表就更加慢了.

这边有一些解决方案, 一种是使用一个分级的 softmax 分类器, 并不是一下就确认是属于 10000 类中的某一类, 比如使用一个分类器进行区分这个词汇是属于前 5k 个词汇还是属于后 5k 个词汇, 以此嵌套. 直到找到词汇所在的分类器. 这样计算成本就与词汇表的对数成正比. 这称为分级 softmax 分类器 (hierarchical softmax classifier).

不过实际的使用中, 并不会使用一棵完美平衡的分类树. 常常会被构建成 常用词汇在顶部, 不常用的词汇在底部的树.

不过我们还有一个问题, 那么就是怎么对 上下文c 进行采样. 一种是直接对语料库进行随机均匀采样, 但是这样你就会发现类似: the, of, a, and, to, ... 这样的词汇出现的很频繁, 这样就导致你在更新词嵌入的时候频繁更新这些词的词嵌入.但是其实你想要更新的词是类似: orange, apple, ... 这样的词汇. 所以一般并不会使用随机均匀采样.

负采样

我们还是沿用之前的例子:

1
I want a glass of orange juice to go alone whit my cereal

在这个学习词嵌入的模型中, 我们需要定义一个新的监督学习问题: 给定一个单词对, 比如 orange 和 juice 然后进行预测, 这是否是一对上下文词-目标词

比如在这个例子中 orange-juice 就是一个正样本对, 正样本是选定一个上下文词, 然后在给定词距内, 比如 10 个词距内, 组成一个正样本, 然后再在词典中随机选取 k 个词作为负样本. 需要注意的是在取负样本时, 即使 of 在句中也是在 orange 的前面, 但是还是取了 0.

对于 k 的取值有什么推荐的呢? 小数据集的话 5 ~ 20. 如果你的数据集很大, 那么 2 ~ 5 就是一个不错的选择. 数据集越小 k 就越大.


那负采样是怎么减少计算的

显示输入 one-hot 向量, 再传递个 E 然后通过两者相乘得到 这时候你就有 10000 个逻辑回归分类的可能. 其中一个是用来判断目标词是否是 juice 的分类器. 其他的就是其他词. 不过每次训练不是都训练全部, 而是选取其中的5个进行训练, 一个是真正对应的, 其他随机的4个负样本. 这就是 k = 4 的情况.

所以这就不用计算巨大的 10000 维度的 softmax, 而是转换成 10000 个二分类问题, 每个都很容易计算. 每次做的就是选中其中的五个. 这就是为什么这个算法的计算成本较低的原因

这个算法有个重要的细节, 如何选取负样本.

一种极端是你根据词频直接选择的话, 那么很大概率会选到 the, of, is, and. 另一种极端是 均匀且随机的抽取.这对于英语是没有什么代表性.

所以在这两种极端中选择了所有词频的 3/4 次方:

许多人这样使用, 效果似乎也不错.

GloVe 词向量

还是使用之前的例子进行举例:

1
I want a glass of orange juice to go alone whit my cereal

之前的例子中我们选取语料库中相近的两个词组成上下文和目标词进行计算, 而 GloVe 是使用 表示的是 i 在 j 上下文中出现的次数. 这里的 i j 就和之前例子中的 t c 一样. 如果你定义的上下文并不是前后多少词距的话, 那么 , 反之则是相等的. 在 GloVe 中是单词前后词距所以是相等的.

GloVe 的目的是最小化

其中 与之前一样, 词向量的内积, 越大表示关联度越高

表示 两个词汇出现的频次对数值, 出现次数越多越大

是额外的加权项(weighting term)对特殊情况进行处理. 比如上下文中并没有出现这个词对时, 我们假定 , 也均衡着比如一些不常见的词汇, 会适当提高点权重.

比较常见的词汇, 例如 this, is, of, ... 适当降低权重.

关于这个算法有趣的一点是, 是对称的, 所以在更新时 (对应自己作为主词以及和作为参照和别人计算)

不过有一点需要注意, 我们之前以这个特质的表格作为例子开始学习词向量, 我们说: “第一行的词向量是用来代表 Gender, 第二行是Royal, …”. 但是你再使用我们之前介绍的词向量学习算法之后, 会发生的一件事是, 你不能保证词嵌入向量的每一维度是可以理解的. 为什么这么说的呢, 比如你在一个平面上画了两个轴, 分别表示 Gender 和 Royal, 那么第一维度很可能是介于着两个轴之间的. 而且不同维度之间也不一定是正交的.

情绪分类

情绪分类模型就是给定一段文本, 然后分辨这个人是否喜欢, 他们正在讨论的这个东西.这个是 NLP 最重要的模块之一, 经常应用在许多应用中. 情感分类最大的一个挑战是标记的训练集可能没有那么多, 但是有了词嵌入, 即使只有中等大小标记的训练集, 你也能构建一个不错的情感分类器.

这是一个情感分类的例子:

输入的 x 是一段文本, 而输出 y 是你想要预测的相应情感. 如果你有这样一个分类器, 那么你就可以从社交媒体或者其他地方获得一些评价, 然后通过观察情感分析结果, 得出你的餐厅的经营情况. 不过即使你的训练集很小, 嵌入词向量也能带来不错的效果. 下面我们将会介绍几个不同的算法.

这是一个简单的例子, 假设这边有个句子

1
The dessert is excellent


你在字典中找到这些词, 我们通常使用10000个词的词汇表, 我们要构建一个分类器能够把它映射输出成四颗星. 然后将它们取平均或者求和, 将会得到一个300维度的向量, 然后再送入 softmax 分类器, 这样就可以用了. 不过这个算法有个问题就是没有考虑词序.比如下方的例子, 出现了很多 good 但是却是差评.

我们可以使用一个 RNN 进行情感分类, 这样考虑了词的顺序, 也就能更好的预测, 即使可能遇到词汇不存在于词表中, 也能很好的预测.

词嵌入消除偏见

现在的机器学习和人工智能算法正渐渐被信任以辅助或者制定及其重要的决策, 因此我们尽可能的确保它们不受非预期形式偏见影响. 比如说性别歧视和种族歧视, 等等.

接下来将介绍一些减少或者是消除这些形式偏见的方法.
当我们在之前讨论词嵌入时, 主要内容是 Man:Woman 就像 king:Queen 这样的关系.
但是如果你问 Man:Computer_Programmer 就像 Woman:? 时候, 可能会给出答案 Homemaker 这输出了一个十分不良的性别歧视.输出如果是是 Woman:Computer_Programmer 这样子会更加合理. 类似的还有 Father:Doctor Mother:Nurse.

因此根据训练模型所使用的文本词嵌入能够反映出 性别 种族 年龄 性取向等其他方面的偏见. 因为机器学习算法正用来制定十分重要的决策, 它影响着世间万物, 所以我们尽量修改学习算法来尽可能减少或者是理想化消除这些非预期的偏见是十分重要的.

至于词嵌入它们能够轻易学会用来训练模型的文本中偏见内容, 所以算法取到的偏见内容就可以反应出人们写作中的偏见, 不过消除 AI 的偏见还要继续努力.

接下来介绍一种方法, 假设我们已经训练好了一个词嵌入. 我们需要按照以下的步骤:

  1. 识别出我们想要消除的特定偏见方向(找到对应的轴)
  2. 中和: 对于那些定义不确切的词, 进行偏见的消除(移动至 no-gender 轴上, 消除 bias 影响)
  3. 均衡: 将类似 grandmother grandfather 这样的词汇相对于 no-gender baise 轴对称

sequence to sequence 基础模型

比如你想通过输入一个法语句子:

1
Jane visite l'Afrique en septembre

来将它翻译成一个英文句子. 和之前一样, 我们使用 对这个例句中的单词进行标注. 然后使用 来表述输出的句子单词. 如何训练一个新的网络来输入序列 x 然后输出序列 y 呢?

这里有一些方法. 我们首先要构建一个编码网络, 它是一个 RNN 的结构. RNN 的单元可以是 GRU 也可以是 LSTM. 每次往网络中输入一个法语单词, 将输入序列接收完毕以后, RNN 网络将会输出一个向量,来代表这个输入序列.

之后你可以构建一个解码网络, 每次输出一个翻译后的单词, 直到输出序列的结尾或者句子结尾标记. 这个解码网络的工作就结束了.

再给出足够的法语和和英语文本的情况下, 如果你训练这个模型. 通过输入一个法语句子来输出对应的英文翻译, 这个模型将会非常有效, 这个模型简单的用一个编码网络, 来对输入的法语句子进行编码. 然后用一个解码网络来生成对应的英语翻译.

还有另外一个于此类似的结构, 被用来做图像描述. 给出一张图片, 比如这张猫的图片它能自动地输出该图片的描述: a cat sitting on a chair.

那么你如何通过输入图像来训练出这样能输出描述的网络呢. 方法如下:
在之前的图像描述课程中你已经知道了如何将图片输入到卷积神经网络中, 比如一个预训练的 AlexNet 结构, 然后让其学习图片的编码, 或者学习图片的一系列特征, 下图展示的就是 AlexNet 结构.

如果我们去掉最后的 Softmax 单元, 这个预训练的 AlexNet 结构, 会给你一个 4096 维度的特征向量. 这个向量表示的就是这张猫的图片, 所以这个预训练网络, 可以是图像的编码网络.

现在你的得到了一个 4096 维的向量, 来表示这张图片, 接着你可以把这个向量输入到 RNN 中. RNN 要做的就是生成图像的描述, 每次生成一个单词. 这和我们之前翻译的例子结构很像, 实际证明这个网络也很有效.

选取最可能的句子

机器翻译其实是 “有条件的” 语言模型. 为什么这么说呢?
下图是我们第一周建立的模型, 这个模型能够预测下一个输出的词是什么. 这个是语言模型所做的事情. 你也可以用这个生产一个新的句子.

而机器翻译的模型是这样的

这边使用两种不同颜色进行区分不同部分. 绿色表示的是 encoder 网络. 紫色表示 decoder 网络, 你会看到 decoder 网路与上部分的 语言模型部分几乎一摸一样.

它们之间的区别是 语言模型总是以 0 向量开始, 而机器翻译模型会使用 encoder 网络计算出一个矩阵来表示输入的句子. 然后decoder 在这个矩阵的基础上进行生成, 而不是从零向量开始.

所以我把它叫做 "条件语言模型" , 语言模型可能输出任意句子,翻译模型则会根据输入的法语句子输出句子的英文翻译.

换句话说, 输出一个英文的句子的概率取决于输入的法语句子: 所以是一个条件语言模型.

如果你真的想要通过模型将法语翻译成英文, 通过输入的法语句子. 模型会告诉你各种英文翻译对应的概率. 显然你不想让它随机地进行输出.

如果你从这个分布中进行取样, 可能取样一次 就能得到很好地翻译. 当然你也可能得到一个截然不同地翻译, 或者其他质量地翻译. 所以当你使用这个模型进行翻译时, 你并不是从得到地分布中随机采样.

而是你要找到一个英文句子y, 使条件概率最大化. 所以在开发机器翻译系统时, 你需要做的一件事是需要想出一个算法, 找出合适的 y 值, 使该项最大化. 而解决这种问题最通用的算法是 束搜索 (Beam Search).

在了解 束搜索 之前, 可能有一个疑惑. 为什么不用 贪心搜索(Greedy Search) 呢? 先选出最可能的第一个词, 然后是第二个词, 以此类推. 但是你真正需要的是一次性挑选出整个单词序列, 来使得整体的概率最大化. 而贪心算法只是局部最大化.

这两种翻译明显第一行翻译比第二个好, 我们希望模型输出的第一个句子的概率更高. 第二个句子稍微有些啰嗦. 当算法输出了 Jane is 时, 因为后续的 going 相比 visiting 更加常见, 概率更高. 如果你仅仅依据前两个词来估计第三个词的概率, 那那么你最终会选择 going, 最终会得到一个欠佳的句子.

这样进行解释可能会比较粗糙, 但是这确实是一个广泛的现象. 而且在英语中各种词汇的组合数量还有很多, 如果你的字典中有 10000 个单词, 并且你的翻译长度达到 10 个词那么长, 那么可能的组合就有 10000 的 10 次方这么多. 这仅仅是 10 个单词的句子.

所以可能的句子数量非常大, 不可能去计算每一种组合的可能性. 所以这时最常用的办法就是用一个近似的搜索算法, 这个近似搜索算法做的就是尽量选出句子 y 使得条件概率最大化.

集束搜索

还是沿用之前的例子进行解释束搜索.

束搜索算法首先做的是, 挑选出要输出的英语翻译的第一个单词. 上图是列出了 10000 个词的词汇表. 为了简化问题, 我们忽略大小写, 所有的单词都以小写的方式列出来.

在束搜索的第一步中, 我用这个网络的部分. 来评估网络输出的第一个单词的概率值. 贪婪算法会选择最可能的那一个单词, 然后继续. 而束算法会考虑多个选择.

集束算法会有一个参数 B, 称为 集束宽 (beam width), 在这个例子中我将这个值设定为 3, 这意味着集束搜索将不止考虑一个可能的结果, 而是一次将会考虑 3 个. 就比如说第一个词可能是 in / jane / september 中的任意一个, 那么就会将这些词存储, 以便后续进行一并考虑.

为了明白集束算法的第一步, 你需要将法语句子输入到编码网络. 然后将结果输入到解码网络. 每个解码网络会输出 10000 个概率值. 然后你根据得到的 10000 概率值取前 3 进行存储.

第二步, 集束算法会根据之前选中的那 3 个单词每个进行考虑第二个单词会是什么, 比如 in 后面可能是 a / aaron / september … 等, 为了评估第二个词的概率值. 我们会假设第一个解码网络部分的输出是 in , 然后将 in 这个结果作为输入传入解码网络的第二部分. 这样就输出了第二个单词的概率了.

注意, 在第二步里我们更关系的是要找到最可能的, 第一和第二个单词的单词对, 所以不仅仅是第二个单词有最大概率, 我们能够根据条件准则进行计算:

这样就获得了概率. 因为我们的 集束宽 为 3 , 也就是我们将会会的 30000 个可能的结果. 我们要做的就是从这 30000 个可能中选取其中的三个进行下一步.
因为我们的 集束宽 为 3, 所以每次我们都将把网络复制 3 个 而不是 30000 个, 然后进行评估最后结果, 并取特定的个数.
第三步也是类似:

最终这个过程的输出一次增加一个单词, 集束搜索最终会输出

1
Jane visits Africa in september.<EOS>

假设将 集束宽 设置为 1 其实就变成了贪心算法了.

改进集束搜索

我们之前讲到集束搜索就是最大化下列公式:

因为概率总是小于 1, 所以直接乘积的结果会得到一个很小的数字, 可能会导致数值下溢.
不能被精确在电脑存储.

所以实际上我们不会直接最大化这个乘积, 而是会最大化 log 乘积的值

所以通过取 log 我们就会得到一个数值上更为稳定的算法. 不容易出现四舍五入的误差, 或者说是数值下溢. 因为 log 是严格的单调递增函数.

我们还可以做一些改变使得机器翻译的表现更好.

原先的目标函数在目标句子是很长的句子上会倾向于选择较短句子, 因为每项的概率都是小于 1 , 那么因为句子越长累计乘积将会得到一个更小的概率值. 这样将会导致更倾向于较短的翻译输出.

改进的 log 式子同样也存在这个问题, 因为 log (0, 1) 区间是小于 0 的, 那么句子越长, 累计的结果负值越大.

所以我们不再最大化这个数值, 而是将其归一化, 除以翻译结果的单词数量. 这样就是取每个单词概率对数的平均值了

这样就减少了对输出长结果的惩罚. 实际中还会使用一个额外的超参 alpha 控制.

如果 alpha = 1 , 就将完全用长度进行归一化. 如果 alpha = 0, 那么就相当于没有进行归一化, alpha 的值可以在 0 ~ 1. 之间进行选择. 不过 alpha 的加入是试探性质的, 是没有理论验证的. 大家发现在实际效果中挺好的, 所以很多人会这样做.

不过还有一个问题, 怎么决定 集束宽 B 的大小? B 的值越大, 意味着你考虑的选择越多, 你找到的句子可能越好. 但表示你需要进行的计算就越多, 因为你需要将很多可能的选择保存起来. 反之越小计算速度越快, 以下是怎么选择 B 值的一些想法:

在之前的例子中, 我们的取值为 3 . 这对于实际来说有点小了. 在生产中一般会设置为 10. 我认为 100 对于生产系统有点过于大了, 不过对于有些系统可能是需要大一些. 但对于科研而言人们想要压榨出全部性能, 以便能用最好的结果发论文. 所以也能看到 1000 或者 3000. 从 1 增长 3 或者 10 ,可能效果会挺好. 但是 1000 到 3000 可能带来的提升就不会很大了.

集束算法不同于 广度优先深度优先算法 这种精确查找算法. 集束算法的运行速度更快, 但是不能保证一定能找到最大的值.

集束算法的误差分析

集束搜索是一种近似搜索算法, 它不总是输出可能性最大的句子. 它仅记录着 B 为前 3 或者 前 100 种可能. 那么如果 集束算法出现错误会怎么样呢? 接下来将会介绍误差分析和集束算法是怎么相互起作用的. 以及你怎样才能发现, 是集束算法出现了问题还是你的 RNN 模型出现了问题.

我们还是使用之前的例子进行说明:

1
Jane visite l'Afrique en septembre

假设在你的机器翻译的 dev 集中. 人工是这样翻译的(标记为 $y^{*}$ ):

1
Jane visits Africa in September.

假设在你的算法输出是这样的 (标记为 $\hat{y}$ ):
1
Jane visits Africa last September.

这并不是一个好的翻译, 实际上改变了原句的意思. 现在你的模型有两个部分:

它实际上是编码器和解码器, 一部分是 RNN ,另一部分是以某个集束宽度 B 运行的集束搜索算法. 如果你能找出错误是由哪个部分导致的不就很好解决了吗. 因为增加数据集和增大 集束宽 B 可能也得不到你想要的结果. 不过你怎么知道值得花时间去改进搜索算法呢?

接下来我们将分步解释这个问题. 弄清楚什么情况下使用什么解决方法.
你可以使用你人类标记的句子, 按照单词计算出
然后根据翻译出的句子, 也按照单词计算出
然后进行比较两个值哪个更大, 通过比较就能将原因归咎于是 RNN 还是 集束算法.

我们来探究下其中的逻辑.
我们需要使用句子计算得到的 进行比较.

  • 情况1:
    意味着集束搜索算法选择了 . 但是实际上 的值更高. 这表明集束算法的作用失效了.
  • 情况2:
    以为 是比 更好的翻译, 但是 RNN 模型却给出了 . 这表明 RNN 模型出了问题.

这里忽略了进行长度归一化的讨论, 如果你使用了长度归一化, 那么这边的概率也需要进行归一化.

所以误差分析过程就是看起来像下表这样:

遍历开发集, 然后在其中找出算法产生的错误. 可以观察出错的比例. 然后依据出错比例进行改正. 如果是集束算法造成了大部分的错误, 那么可能需要增加 集束宽度. 如果是 RNN 模型出现了更多的错误, 那么就进行更深层次的分析, 来决定是需要增加正则化还是获取更多的训练数据, 或是尝试使用一个不同的网络结构, 又或者是其他方案.

Bleu 得分

机器学习翻译的一大难题是, 一个法语句子可以有多种英文翻译, 而且都同样好. 所以当同时有多个同样好的答案时, 怎样评估一个机器翻译系统呢? 与图像识别不同, 图像识别只有一个精确的答案, 只要测量准确性就可以了. 那么怎么对多个答案进行选择呢, 常见的解决办法是通过一个叫 BLEU (bilingual evaluation understudy, 双语评估替补) 得分的东西来解决.

假设给你一个法语句子

1
Le chat est sur le tapis.

句子的人工翻译是:
1
2
R1: The cat is on the mat
R2: There is a cat on the mat

这两个翻译都很好, 那么 BLEU 得分做的就是, 给定一个机器生成的翻译, 它能计算一个分数来衡量机器翻译的好坏.

直觉告诉我们, 只要这个机器生成的翻译与任何一个人工翻译足够接近, 那应该就会得到一个较高的 BLEU 值. BLEU 背后的理念是观察机器生成的翻译, 然后看生成的词是否出现在至少一个人工翻译的参考之中, 因此这些人工翻译的参考会包含在开发集, 或者是测试集中.

现在让我们看一个极端的例子. 假设我们的机器翻译输出 MT 是:

1
the the the the the the the

这显然是一个非常糟糕的翻译, 所以衡量机器翻译输出质量的方法之一是, 观察输出结果的每一个词. 看其是否存在于参考之中(就是前面提到的人工翻译). 这被称为机器翻译的精确度.

上述的情况下, 机器翻译输出了 7 个单词.并且这七个单词中的每一个都出现在了 R1 和 R2. 所以看上去都是很合理的. 因此这个输出的精确度就是 , 看起来是一个极好的精确度. 所以这就是不能把出现在参考句中的词在 MT 输出的所有词所占比例作为评判依据的原因. 因为这个例子就很好的说明 MT 翻译并没有很高的翻译度.

所以取而代之的是我们要用的这个, 改良后的评估方法: 每个单词的计分上限定为出现在 参考句 中出现的最多次数. 比如 the 在 R1 中出现了两次. R2 只出现了一次. 所以 2 比 1 大, 所以the 的得分上限为2. 改良后的得分就为: , 因为我们最多只能给两分. 到目前我们只是关注单独的单词, 但是在 BLEU 中你不想仅仅考虑单个单词.

也许你想考虑成对的单词. 我们定义一下二元词组(bigrams, 相邻的单词)的 BLEU 得分, 这是最后 BLEU 得分的部分.
还是使用之前的 R1 / R2. 不过这次机器学习输出了稍微好点的翻译:

1
The cat the cat on the mat.

仍然不是好的翻译, 不过比上个好. 这里可能的二元词组有:
1
2
3
4
5
the cat
cat the
cat on
on the
the mat

所以现在我们统计每个词组在 MT 中出现了多少次(count). 并统计 R1 / R2 中各词组出现的次数(count clip)作为上限. 然后使用 上限和作为分子, 出现的次数和作为分母. 所以就得到分数

现在我们将其进行公式化, 基于我们在一元词组中学到的内容. 我们将改良后的一元词组精确度定义为 . 所以公式就是这样:

此处的 仅是代表, 一元词组. 你可以使用 n-gram 进行替代:

这个方法能够让你衡量机器翻译中与参考相似重复的程度, 另外你能够确定, 如果机器翻译输出与 R1 / R2 完全相同的话.那么所有这些 P_1 P_2 等, 都将等于 1.0 , 要达到 1.0 只要你的输出与任一参考完全相同即可.

最终我们来得到最后的 BLEU 得分.
目前我们直到 值得 是 n 元词组的精确度. 假设我们有 这几个.
我们假设用这四个值进行计算, 那么公式是:

其中 BP 表示的是 简短惩罚 "brevity penalty". 如果你输出了一个非常段的翻译, 那么其实是容易得到一个高精确度的结果的. 因为所有词出现在参考中是非常可能的. 但是我们不想要太短的翻译结果, 所以简短惩罚就是一个调整因子, 惩罚输出太短翻译的结果. 所以 BP 像这样:

BLEU 相当于给你一个评估, 让你能够比较不同网络之间的优劣.

注意力模型的直观理解

像这样给定一个很长的法语句子, 在你的网络中, 这个绿色编码器所要做的就是读整个句子, 然后记忆住整个句子, 然后再将激活值传入到紫色的网络中. 然后对于紫色网络, 将对这个激活值进行解码, 然后生成英文翻译.

然而人工翻译并不会通读整个发育句子, 然后再记忆里面所有的东西, 然后从零开始机械式的翻译成一个英文句子, 而是可能首先翻译出句子的部分, 然后再看下一部分, 在翻译一部分, 如此反复. 因为记忆像这样整个句子是非常困难的.

你在后面的解码网络中, 会看到它对于短句子, 效果非常好. 于是会有一个相对较高的 BLEU 得分. 但是对于长句子(30 词以上), 效果会变差. 太短的句子不好, 太长同样效果也不好, 因为神经网络中记忆长句子是非常困难的.

注意力模型在处理这个问题更像人类, 一次翻译句子的一部分. 而有了注意力机制, 模型最终的表现会像图中绿线那样.

我们使用一个简短的例子进行演示:

1
Jane visite l'Afrique en Septembre.

在这个情况中, 我们将使用一个双向 RNN , 计算每个单词的特征集. 然后我们将使用另外一个 RNN 进行生成英文翻译, 我们使用 进行表示中间的感知机. 第一个生成的单词将会是 Jane. 那么现在问题是当你尝试生成第一个词, 那么我们应该查看输入的哪个部分. 你应该先看第一个单词, 或者临近的词, 但是别把目光放的太远, 比如句子的末尾.

所以注意力模型就会计算注意力权重, 我们将使用 进行表示权重. 表示的是第一个词你该多么在意(paying attention), 然后是第二个词 , 以此类推, 并得到第一个 RNN 单元的输入向量 C.

然后在生成下一个词时, 会有一个新的注意力权重集, 比如 等, 来获得生成第二个词的输入向量信息. 并且也会加入第一个RNN 单元的输出. 然后往复如此, 一次输出一个词.

注意力模型的细节

上个段落中我们进行直观的介绍了 注意力模型如何让神经网络像人类翻译一样只关注句子的一部分进行翻译.

我们再使用上个段落的例子. 使用一个双向 RNN 对句子进行处理, 计算每个词的特征.
在编码网络中, 每个 RNN 单元都有着两个方向的激活值 为了简化公式表示, 我们统一使用 表示这两个值.

注意力值的权重和会等于 1 :
时候的上下文

所以 就是 需要分配多少”注意”的数值.

是由 计算得到的, 但是我们不知道具体的公式是什么样的, 所以训练一个小神经网络去学习这个函数究竟是什么, 然后相信反向传播, 相信梯度下降算法, 学习到一个正确的函数. 这个函数将会告诉你 需要分配多少”注意”. 然后每次生成一个词.

这个算法的一个缺点是, 需要花费三次方的时间. 也就是算法的复杂度是 O(n^3), 如果你有 T_x 个输入单词, T_y 个输出单词, 于是注意力参数的总数就会是 T_x * T_y , 所以算法有三次方的消耗.

但是在机器翻译的应用上, 输入和输出的句子, 一般不会太长, 可能消耗三次方是可以接受的. 但也有很多研究去减少这样的消耗. 虽然到目前为止是讲述翻译. 但是这个想法也被应用到不同的地方. 比如对图片进行起标题总结: 看一张图片, 并写下这张图片的标题. (Xu et. al., 2015. Show, attend and tell: Neural image caption generation with visual a)

注意力模型实践的一些例子

比如将非标准化的时间标准化:

还有一件有趣的事情是, 可以可视化 注意力权重, 比如机器翻译的例子:

可以看出相关的词, 注意力值相对较高. 也可以来判断什么时候学习完成.

语音识别

什么是语音识别问题呢? 现在你有一个音频片段 x.你的任务是自动的生成文本 y.

现在有一个音频片段画出来是这样, 该图的横轴是时间. 一个麦克风的作用是测量出微小的气压变化, 并进行记录. 并以这样的方式进行可视化.

这个音频图片所展示的音频是 “the quick brown fox” 我们希望一个语音识别算法能够通过输入这段音频然后输出音频的文本内容. 因为人并不会处理声音的原始波形. 而是通过一种特殊的物理结构来测量这些不同频率和强度的声波, 音频数据的常见预处理步骤, 就是运行这个原始音频片段, 然后生成一个声谱图. 就像这样, 同样的横轴是时间, 纵轴是声音的频率, 而图中不同的颜色显示了声波能量的大小. 通过这样的声谱图 可以知道在不同的时间和频率上, 这些声音有多大. 或者你可能还听过人们谈到过伪空白输出(false blank outputs), 也经常应用于预处理步骤. 也就是在音频被输入到算法之前.

而人耳所做的计算, 和这个预处理过程非常相似. 语音识别方面最令人振奋的趋势之一就是, 曾经有一段时间语音识别系统是用音位来构建的, 也就是人工设计的基本单元. 如果使用音位来表示 “the quick brown fox “, 比如简化表示 the 含有 “th” 和 “e” 的音. 而 quick"k w i k" 的音, 语言学家过去把这些音作为声音的基本单元写下来, 把这些语音分解成这些基本声音单元. 那这些语音分解成这些基本单元. 而 “brown” 不是一个很正式的音位, 因为它的音写起来比较复杂, 不过语音学家们认为用这些基本的音位单元来表示音频. 是做语音识别最好的办法.

不过在端对端(end-to-end)模型中, 我们发现这种音位表示法, 已经不再必要了. 而是可以构建一个系统, 通过向系统中输入音频片段, 然后输出音频的文本, 而不需要使用这种人工设计的表示方法, 使这种方法成为可能的一件事. 就是使用一个很大的数据集, 所以语音识别的研究数据集可能长达 300 个小时.在学术界, 甚至 3000 小时的文本音频数据集都被认为是合理的大小.

大量的研究, 大量的论文所使用的数据集中, 有几千种不同的声音. 而且最好的商业系统现在已经训练了超过 1 万个小时的数据. 甚至 10 万个小时. 而且它还会继续变得更大, 在文本音频数据集中同时包含 x 和 y, 通过深度学习算法, 大大推进了. 语音识别的进程. 那么如何建立一个语音识别系统呢?

在上一节中我们介绍了注意力模型, 所以你可以做的事就是. 按照声音的时间帧输入到模型中, 你可以用一个注意力模型来输出文本描述. 如 “The quick brown fox” 或者其他语音内容.

还有一种效果也不错的方法, 就是用 CTC (Connectionist Temporal Classification)损失函数来做语音识别, 算法思想如下. 假设语音片段内容是某人说 “the quick brown fox”, 这是我们使用一个新的网络结构像这个样子:

这里输入 x 和输出 y 的数量都是一样的. 因为我在这里画的只是一个简单的单向 RNN 结构, 然而在实际中, 它有可能是双向的 LSTM 结构, 并且通常是很深的模型. 但注意一下这里时间步的数量. 它非常大. 在语音识别中, 通常输入的时间步数量要比输出的时间步的数量. 多出很多. 比如你有一段 10 秒的音频, 并且特征是 100 赫兹的即是每秒有 100 个样本, 于是这段 10 秒的音频片段, 就会有 1000 个输入, 就是简单的 10秒乘以 100 赫兹. 所以有 1000 个输入. 但可能你的输出就没有 1000 个字母了. 或者说没有 1000 个字符. 所以这时候要怎么办呢? CTC 损失函数允许 RNN 生成 ttt 这样的输出. 这有一个特别的输出, 称为空白符 (blank character) 这边使用下划线表示, 所以模型可能会这样输出 `”tttheee __qqq“` 也被看作是正确的输出. 这段可以被看作是 “the q” 的输出 CTC 损失函数的一个基本规则是将空白符之间的重复字符折叠起来, 所以最后的输出结果是: “the q” 即使神经网络输出重复字符但是你可以得到这段 19 个字符的最终输出.

触发字检测

随着语音识别的发展越来越多的设备可以通过你的声音来唤醒, 这有时被叫做触发字检测系统. 许多产品都有这样的触发字检测. 比如: Alexa, Siri, Okey Google. 如果你能构建一个触发字检测, 也许就能让你的电脑通过你的声音来执行某些事.

关于触发字检测的文献还处于发展阶段, 对于触发字检测最好的算法是什么, 目前还没有一个广泛的定论. 这边就简单介绍一个能够使用的算法. 现在有一个这样的 RNN 结构:

我们要做的就是计算出这段音频片段的声谱特征图. 得到特征向量 x1, x2, x3, 或者说是音频特征 x1, x2, x3… 然后将它放到 RNN 中. 最后要做的就是定义我们的目标标签 y. 假设在图中蓝色划线处, 是某人刚刚说完一个触发字, 比如是: Alexa , 那么在这个点之前所有的输出你可以将其目标值标签设置为 0.然后这个点的输出标签为1. 后面如果再次出现触发字, 那么同样的, 将这个标签设置为 1. 这样的标签行为对于 RNN 来说是可行的. 并且确实运行的不错. 不过该算法的一个明显的缺点是构建了一个很不平衡的训练集, 0 的数量比 1 多太多了. 这里还有一个解决办法, 虽然听起来有点简单粗暴.但确实能使其变得更容易训练, 比起只在一个时间步上去输出1,其实你可以在输出变回 0 之前多次固定输出 1 或说在固定的一段时间内输出多个 1. 这样的话, 就稍微提高了 1 与 0 的比例. 这确实有点简单粗暴.

Author: Sean
Link: https://blog.whileaway.io/posts/4d909dc0/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.