这篇文章实际上是 Coursera 上吴恩达老师的深度学习专业课程的第五门课程的课程笔记。git
参考了其余人的笔记继续概括的。github
假如咱们想要创建一个可以自动识别句中人名地名等位置的序列模型,也就是一个命名实体识别问题,这经常使用于搜索引擎。命名实体识别系统能够用来查找不一样类型的文本中的人名、公司名、时间、地点、国家名和货币名等等。算法
咱们输入语句 "Harry Potter and Herminoe Granger invented a new spell." 做为输入数据 \(x\),咱们想要这个序列模型输出 \(y\),使得输入的每一个单词都对应一个输出值,同时这个 \(y\) 可以代表输入的单词是不是人名的一部分。技术上来讲,还有更加复杂的输出形式,它不只可以代表输入词是不是人名的一部分,它还可以告诉你这我的名在这个句子里从哪里开始到哪里结束。api
以简单的输出形式为例。这个输入数据是 9 个单词组成的序列,因此最终咱们会有 9 个特征集合来表示这 9 个单词,并按序列中的位置进行索引,\(x^{<1>},x^{<2>}\) 直到 \(x^{<9>}\) 来索引不一样的位置。网络
输出数据也是同样,用 \(y^{<1>},y^{<2>}\) 到 \(y^{<9>}\) 来表示输出数据。同时使用 \(T_x\) 来表示输入序列的长度,\(T_y\) 表示输出序列的长度。在这里例子里,\(T_x=9\),且 \(T_x=T_y\)。app
想要表示一个句子里的单词,首先须要作一张词表(或者说词典),也就是列一列咱们的表示方法中用到的单词。如下图这个词表为例,它是一个 10,000 个单词大小的词表。这对现代天然语言处理应用来讲过小了,对于通常规模的商业应用来讲 30,000 到 50,000 词大小的词表比较常见,有些大型互联网公司会有百万词等。less
咱们以这个 10,000 词的词表为例。咱们用 one-hot 表示法来表示词典里的每一个单词,也就是说 \(x^{<1>}\) 表示 Harry 这个单词,而 Harry 在词表中的第 4075 行,因此 \(x^{<1>}\) 最终表示为一个长度为 10,000,在 4075 行为 1,其他行为 0 的向量。同理,其余的词也这样进行编码。dom
若是直接把 9 个 one-hot 向量输入到一个标准神经网络中,通过一些隐藏层,最终会输出 9 个值为 0 或者 1 的项来代表每一个输入单词是不是人名的一部分。机器学习
可是结果发现这种方法并很差,主要有两个问题。ide
循环神经网络以下图所示。将第一个词输入一个神经网络层,让神经网络尝试预测输出,判断这是不是人名的一部分。而接下来第二个词,它不只用 \(x^{<2>}\) 来预测 \(y^{<2>}\),它也会输入来自上一层神经网络的激活值,接下来的词也以此类推。因此在每个时间步中,循环神经网络传递一个激活值到下一个时间步中用于计算。若是 \(T_x\) 和 \(T_y\) 不相等,这个结果会须要做出一些改变。
要开始整个流程,在零时刻须要构造一个激活值 \(a^{<0>}\),这一般是零向量。固然也有其余初始化 \(a^{<0>}\) 的方法,不过使用零向量的伪激活值是最多见的选择。
循环神经网络是从左向右扫描数据,同时每一个时间步的参数也是共享的。咱们用 \(W_{ax}\) 来表示管理着从 \(x_{<1>}\) 到隐藏层的链接的一系列参数,而激活值也就是水平联系是由参数 \(W_aa\) 决定的,同理,输出结果由 \(W_ya\) 决定。这些参数在每一个时间步都是相同的。
这个循环神经网络的一个缺点就是它只使用了这个序列中以前的信息来作出预测,如预测 \(\hat{y}^{<3>}\) 时,它没有用到 \(x^{<4>},x^{<5>}\) 等的信息。因此对于这两个句子
Teddy Roosevelt was a great President.
Teddy bears are on sale!
为了判断 Teddy 是不是人名的一部分,仅仅知道句中前两个词是彻底不够的。因此后续咱们须要使用双向循环神经网络 (BRNN) 来解决这个问题。
咱们仍以单向神经网络为例了解其计算过程。
通常开始先输入 \(a^{<0>}\),接着就是前向传播过程。
\[ a^{<1>} = g_{1}(W_{{aa}}a^{< 0 >} + W_{{ax}}x^{< 1 >} + b_{a})\\ \hat y^{< 1 >} = g_{2}(W_{{ya}}a^{< 1 >} + b_{y})\\ \cdots \cdots \]
循环神经网络用的激活函数常常是 tanh,偶尔也会用 ReLU。
前向传播公式的泛化公式以下,在 t 时刻
\[ a^{< t >} = g_{1}(W_{aa}a^{< t - 1 >} + W_{ax}x^{< t >} + b_{a})\\ \hat y^{< t >} = g_{2}(W_{{ya}}a^{< t >} + b_{y}) \]
咱们的符号约定,以 \(W_{ax}\) 为例,第二个下标意味着它要乘以某个 \(x\) 类型的量,而后第一个下标 \(a\) 表示它是用来计算某个 \(a\) 类型的变量。其余几个矩阵符号也是同理。
为了简化这些符号,咱们能够简化一下,第一个计算 \(a^{<t>}\) 的公式能够写做
\[ a^{<t>} =g(W_{a}\left\lbrack a^{< t-1 >},x^{} \right\rbrack +b_{a}) \]
而后咱们定义 \(W_a\) 为矩阵 \(W_{aa}\) 和 \(W_{ax}\) 水平并列放置,即 \([ {{W}_{aa}}\vdots {{W}_{ax}}]=W_{a}\)。而 \(\left\lbrack a^{< t - 1 >},x^{< t >}\right\rbrack\) 表示的是将这两个向量堆在一块儿,即 \(\begin{bmatrix}a^{< t-1 >} \\ x^{< t >} \\\end{bmatrix}\)。这样,咱们就把两个参数矩阵压缩成了一个参数矩阵,当咱们创建更复杂模型时,这能简化咱们要用到的符号。
同理,对于 \(\hat y^{< t >}\) 的计算,也能够写做
\[ \hat y^{< t >} = g(W_{y}a^{< t >} +b_{y}) \]
RNN 前向传播示意图以下。
为了计算反向传播,咱们先定义一个元素损失函数。
\[ L^{}( \hat y^{},y^{}) = - y^{}\log\hat y^{}-( 1- y^{})log(1-\hat y^{}) \]
它对应的是序列中一个具体的词,若是它是某我的的名字,那么 \(y^{<t>}\) 的值为 1,而后神经网络将输出这个词是名字的几率值。它被定义为标准逻辑回归损失函数,也叫交叉熵损失函数 (cross entropy loss)。
整个序列的损失函数为
\[ L(\hat y,y) = \ \sum_{t = 1}^{T_{x}}{L^{< t >}(\hat y^{< t >},y^{< t >})} \]
也就是把每一个单独时间步的损失函数都加起来。
在这个反向传播过程当中,最重要的信息传递或者说最重要的递归运算就是这个从右到左的运算,因此它被叫作穿越时间反向传播 (backpropagation through time)。
RNN 反向传播示意图以下。
并非全部的状况都知足 \(T_x=T_y\)。好比电影情感分类,输出 \(y\) 能够是 1 到 5 的整数,而输入是一个序列。
以前的命名实体识别问题,属于多对多 (many-to-many) 的结构。由于输入序列有不少的输入,而输出序列也有不少的输出。还有一种多对多结构,和命名实体识别问题不一样,它的输入和输出的序列多是不一样长度的。例如,机器翻译,不一样语言对于同一句话可能会有不一样的长度的语句。而情感分类问题,属于多对一 (many-to-one) 的结构。由于它有不少输入,而后输出一个数字。固然也有一对一 (one-to-one) 结构,也就是标准的神经网络。
其实还有一对多 (one-to-many) 的结构。例子是音乐生成,咱们可使用神经网络经过咱们输入的一个整数(用来表示音乐类型或者第一个音符等信息)来生成一段音乐。
假如咱们在作一个语音识别系统,听到一个句子
The apple and pear (pair) salad was delicious.
语音识别系统就要判断,在这个句子中是 pear 仍是 pair。这里,就要使用一个语言模型,它能计算出这两句话各自的可能性。
这个几率指的是,假设咱们随机拿起一张报纸,打开任意邮件,或者任意网页或者听某人说一句话,这个即将从世界上的某个地方获得的句子会是某个特定句子的几率是多少。
使用 RNN 创建出这样的模型,首先须要一个训练集,包含一个很大的英文文本语料库 (corpus) 或者其余的语言(这取决于咱们的目的)。语料库是天然语言处理的一个专有名词,意思就是很长的或者说数量众多的句子组成的文本。
若是训练集中有这么一句话
Cats average 15 hours of sleep a day.
那么首先将这个句子标记化,就是像以前那样,创建一个词典,而后将每一个单词都转换为对应的 one-hot 向量。而后咱们要定义句子的结尾,通常的作法就是增长一个额外的标记,叫作 EOS,用来表示句子的结尾。这样能帮助咱们明白一个句子何时结束。
在标记化的过程当中,咱们能够自行决定要不要把标点符号当作标记。若是要把标点符号看做标记的话,那么咱们创建的词典也应该加入这些标点符号。
若是训练集有一些词不在创建的词典里,以下面这个句子
The Egyptian Mau is a bread of cat.
Mau 这个词可能比较少见,并不在咱们创建的词典里。这种状况下,咱们能够把 Mau 替换成一个叫作 UNK 的表明未知词的标志,咱们只针对 UNK 创建几率模型,而不是针对这个具体的词 Mau。
完成标记化后,意味着输入的句子都映射到了各个标志上。下一步就是构建 RNN。
仍然以 "Cats average 15 hours of sleep a day。“ 做为输入为例。在第 0 个时间步,计算激活项 \(a^{<1>}\),它是以 \(x^{<1>}\) 做为输入的函数,而\(x^{<1>},a^{<1>}\) 都会被设为全为 0 的向量。因而 \(a^{<1>}\) 要作的就是它会经过 softmax 进行一些预测来计算出第一个词可能会是什么,结果为 \(\hat{y}^{<1>}\)。这一步其实就是经过一个 softmax 层来预测词典中任意单词会是第一个词的几率。
在下一时间步中,使用激活项 \(a^{<1>}\),而后输入 \(x^{<2>}\) 告诉模型,第一个词是 Cats,以此来计算第二个词会是什么。同理,输出结果赞成通过 softmax 层进行预测,预测这些词的几率。以此类推。
为了训练这个网络,咱们须要定义代价函数。在某个时间步 \(t\),若是真正的词是 \(y^{<t>}\),而神经网络的 softmax 层预测结果值为 \(\hat{y}^{<t>}\)。那么 softmax 损失函数为
\[ L\left( \hat y^{<t>},y^{<t>}\right) = - \sum_{i}^{}{y_{i}^{<t>}\log\hat y_{i}^{<t>}} \]
而整体损失函数为
\[ L = \sum_{t}^{}{L^{< t >}\left( \hat y^{<t>},y^{<t>} \right)} \]
也就是把全部单个预测的损失函数相加。
若是咱们用很大的训练集来训练这个 RNN,那么咱们能够经过开头一系列单词来预测以后单词的几率。假设一个新句子只有三个单词,那么这个句子的几率计算以下
\[ P(y^{<1>},y^{<2>},y^{<3>})=P(y^{<1>})P(y^{<2>}|y^{<1>})P(y^{<3>}|y^{<1>},y^{<2>}) \]
在训练一个序列模型以后,要想了解这个模型学到了什么,一种非正式的方法就是进行一次新序列采样。
咱们要作的就是对这些几率分布进行采样来生成一个新的单词序列。
第一步要作的就是对咱们想要模型生成的第一个词进行采样。输入 \(x^{<1>},a^{<1>}\) 为 0 向量,而后获得一个 softmax 结果,根据这个 softmax 的分布进行随机采样。也就是对这个结果使用 numpy 命令 (np.random.choice
)。
而后根据模型结构,以此类推。直到获得 EOS 标识或者达到所设定的时间步。若是不想采样到未知标识 UNK,能够拒绝采样到的未知标识,继续在剩下的词中进行重采样。
根据实际应用,也能够构建一个基于字符的 RNN 结构,这样字典仅包含从 a 到 z 的字母,也能够再包含一些标点符号,特殊字符,数字等。这样序列 \(y^{<1>},y^{<2>},\cdots\) 将会是单独的字符而不是单词。
这种结构优势是,咱们没必要担忧会出现未知的标识。而一个主要缺点就是,最后会获得太多太长的序列,计算成本比较高昂。
循环神经网络的梯度消失
对于下面两个句子。
The cat, which already ate ......, was full.
The cats, which ate ......, were full.
前面的名词和动词应该保持一致的单复数形式,可是基本的 RNN 模型不擅长捕获这种长期依赖效应。由于梯度消失问题,后面层的输出偏差很难影响前面层的计算。
尽管梯度爆炸也是会出现,可是梯度爆炸很明显。由于指数级大的梯度会让参数变得极其大,以致于网络参数崩溃,咱们会看到不少 NaN,这意味着网络计算出现了数值溢出。若是发现了梯度爆炸问题,一个解决办法就是用梯度修剪。梯度修剪的意思就是观察梯度向量,若是它大于某个阈值,缩放梯度向量,保证它不会太大。
GRU
标准的 RNN 单元以下图所示。
使用 GRU 可使 RNN 更好地捕捉深层链接,并改善梯度消失问题。
仍然使用上面提到的单复数例子。GRU 会有个新的变量称为 \(c\),表明细胞 (cell),即记忆细胞。记忆细胞的做用是提供了记忆的能力,好比猫是单数仍是复数,当它看到以后的句子的时候,它仍可以判断句子的主语是单数仍是复数。因而在时间 \(t\) 处,有记忆细胞 \(c^{<t>}\),而后 GRU 实际输出了激活值 \(a^{<t>}\),且 \(c^{<t>}=a^{<t>}\)。
在每一个时间步,咱们将用一个候选值重写记忆细胞,即 \({\tilde{c}}^{<t>}\)。而后咱们用 tanh 函数来计算
\[ {\tilde{c}}^{<t>} =tanh(W_{c}\left\lbrack c^{<t-1>},x^{<t>} \right\rbrack +b_{c}) \]
GRU 中真正重要的思想是咱们有一个门,记为 \(\Gamma_{u}\),其中下标 \(u\) 表明更新 (update) 。它是一个 0 到 1 之间的值。它的计算方式以下
\[ \Gamma_{u}= \sigma(W_{u}\left\lbrack c^{<t-1>},x^{<t>} \right\rbrack +b_{u}) \]
对于大多数可能的输入,sigmoid 函数的输出老是很是接近 0 或者 1,因此这个值大多数状况下也是很是接近 0 或 1 的。
因此 GRU 的关键部分就是使用 \(\tilde{c}\) 来更新 \(c\),而后使用门来决定是否真的要更新。即
\[ c^{<t>} = \Gamma_{u}*{\tilde{c}}^{<t>} +\left( 1- \Gamma_{u} \right)*c^{<t-1>} \]
GRU 的一个简化示意图以下。
由于 \(\Gamma_u\) 很接近 0,那么更新式子就会变成 \(c^{<t>}=c^{<t-1>}\)。也就是说,即便通过不少不少的时间步,\(c^{<t>}\) 的值也很好地被维持了,这就是缓解梯度消失问题的关键。
而对于一个完整的 GRU,咱们须要在计算第一个式子中给记忆细胞的新候选值加上一个新的项。咱们要添加一个新的门 \(\Gamma_r\),其中下标 \(r\) 能够表明相关性 (relevance)。这个门的做用是告诉咱们,计算出的下一个 \(c^{<t>}\) 的候选值 \(\tilde{c}^{<t-1>}\) 与 \(c^{<t-1>}\) 有多大的相关性。它的计算方式以下
\[ \Gamma_{r}= \sigma(W_{r}\left\lbrack c^{<t-1>},x^{<t>} \right\rbrack + b_{r}) \]
那么完整的 GRU 计算公式则为
\[ \tilde{c}^{<t>}=tanh(W_c[\Gamma_r\times c^{<t-1>},x^{<t>}]+b_c)\\ \Gamma_{u}= \sigma(W_{u}\left\lbrack c^{<t-1>},x^{<t>} \right\rbrack +b_{u})\\ \Gamma_{r}= \sigma(W_{r}\left\lbrack c^{<t-1>},x^{<t>} \right\rbrack + b_{r})\\ c^{<t>} = \Gamma_{u}*{\tilde{c}}^{<t>} +\left( 1- \Gamma_{u} \right)*c^{<t-1>}\\ a^{<t>}=c^{<t>} \]
LSTM 是一个比 GRU 更增强大和通用的版本。
LSTM 的主要公式以下
\[ \tilde{c}^{<t>}=tanh(W_c[a^{<t-1>},x^{<t>}]+b_c)\\ \Gamma_u=\sigma(W_u[a^{<t-1>},x^{<t>}]+b_u)\\ \Gamma_f=\sigma(W_f[a^{<t-1>},x^{<t>}]+b_f)\\ \Gamma_o=\sigma(W_o[a^{<t-1>},x^{<t>}]+b_o)\\ c^{<t>}=\Gamma_u \times \tilde{c}^{<t>}+\Gamma_f \times c^{<t-1>}\\ a^{<t>}=\Gamma_o \times c^{<t>} \]
在 LSTM 中,咱们再也不有 \(a^{<t>}=c^{<t>}\),咱们专门使用 \(a^{<t>}\) 或者 \(a^{<t-1>}\),而不是用 \(c^{<t-1>}\),也再也不用相关门 \(\Gamma_r\)。LSTM 保留了更新门,但不只仅由更新门来控制,加入了遗忘门 (the forget gate) \(\Gamma_f\) 和输出门 (the output gate) \(\Gamma_o\)。
因此给了记忆细胞选择权去维持旧的值 \(c^{<t-1>}\) 或者加上新的值 \(\tilde{c}^{<t>}\)。
LSTM 示意图以下。
能够发如今上图中的序列中,上面有条线显示了只要正确地设置了遗忘门和更新门,LSTM 是很容易把 \(c^{<0>}\) 的值一直往下传递的。固然,这个图示和通常使用的版本有些许不一样。最经常使用的版本的门值不只取决于 \(a^{<t-1>}\) 和 \(x^{<t>}\),偶尔也能够偷窥一下 \(c^{<t-1>}\) 的值(上图中编号 13),这叫作窥视孔链接 (peephole connection)。
LSTM 前向传播图:
LSTM 反向传播计算:
门求偏导
\[ d \Gamma_o^{\langle t \rangle} = da_{next}*\tanh(c_{next}) * \Gamma_o^{\langle t \rangle}*(1-\Gamma_o^{\langle t \rangle})\\ d\tilde c^{\langle t \rangle} = dc_{next}*\Gamma_i^{\langle t \rangle}+ \Gamma_o^{\langle t \rangle} (1-\tanh(c_{next})^2) * i_t * da_{next} * \tilde c^{\langle t \rangle} * (1-\tanh(\tilde c)^2)\\ d\Gamma_u^{\langle t \rangle} = dc_{next}*\tilde c^{\langle t \rangle} + \Gamma_o^{\langle t \rangle} (1-\tanh(c_{next})^2) * \tilde c^{\langle t \rangle} * da_{next}*\Gamma_u^{\langle t \rangle}*(1-\Gamma_u^{\langle t \rangle})\\ d\Gamma_f^{\langle t \rangle} = dc_{next}*\tilde c_{prev} + \Gamma_o^{\langle t \rangle} (1-\tanh(c_{next})^2) * c_{prev} * da_{next}*\Gamma_f^{\langle t \rangle}*(1-\Gamma_f^{\langle t \rangle}) \]
参数求偏导
\[ dW_f = d\Gamma_f^{\langle t \rangle} * \begin{pmatrix} a_{prev} \\ x_t\end{pmatrix}^T\\ dW_u = d\Gamma_u^{\langle t \rangle} * \begin{pmatrix} a_{prev} \\ x_t\end{pmatrix}^T\\ dW_c = d\tilde c^{\langle t \rangle} * \begin{pmatrix} a_{prev} \\ x_t\end{pmatrix}^T\\ dW_o = d\Gamma_o^{\langle t \rangle} * \begin{pmatrix} a_{prev} \\ x_t\end{pmatrix}^T \]
为了计算 \(db_f, db_u, db_c, db_o\),须要各自对 \(d\Gamma_f^{\langle t \rangle}, d\Gamma_u^{\langle t \rangle}, d\tilde c^{\langle t \rangle}, d\Gamma_o^{\langle t \rangle}\) 求和。
最后,计算隐藏状态、记忆状态和输入的偏导数。
\[ da_{prev} = W_f^T*d\Gamma_f^{\langle t \rangle} + W_u^T * d\Gamma_u^{\langle t \rangle}+ W_c^T * d\tilde c^{\langle t \rangle} + W_o^T * d\Gamma_o^{\langle t \rangle} \\ dc_{prev} = dc_{next}\Gamma_f^{\langle t \rangle} + \Gamma_o^{\langle t \rangle} * (1- \tanh(c_{next})^2)*\Gamma_f^{\langle t \rangle}*da_{next} \\ dx^{\langle t \rangle} = W_f^T*d\Gamma_f^{\langle t \rangle} + W_u^T * d\Gamma_u^{\langle t \rangle}+ W_c^T * d\tilde c_t + W_o^T * d\Gamma_o^{\langle t \rangle} \]
何时用 GRU,何时用 LSTM,其实没有统一的标准。
GRU 的优势是,它是个更加简单的模型,因此容易建立一个更大的网络,并且它只有两个门,在计算性上也运行得更快,而后它能够扩大模型的规模。
可是 LSTM 更增强大和灵活。如今大部分的人仍是会把 LSTM 做为默认的选择来尝试。
咱们以一个只有 4 个单词的句子为例。那么这个网络会有一个前向的循环单元为 \({\overrightarrow{a}}^{<1>},{\overrightarrow{a}}^{<2>},{\overrightarrow{a}}^{<3>},{\overrightarrow{a}}^{<4>}\),这四个循环单元输入,都会获得对应的输出 \(\hat{y}^{<1>},\hat{y}^{<2>},\hat{y}^{<3>},\hat{y}^{<4>}\)。
接下来,咱们增长一个反向循环层,\({\overleftarrow{a}}^{<1>},{\overleftarrow{a}}^{<2>},{\overleftarrow{a}}^{<3>},{\overleftarrow{a}}^{<4>}\),一样这一层也向上链接。这样,这个网络以下所示。先前向计算,而后再反向计算,把全部激活值都计算完了就能够计算预测结果了。
这些单元能够是标准 RNN 单元,也能够是 GRU 或者 LSTM 单元。并且实践中,不少 NLP 问题,有 LSTM 单元的双向 RNN 模型是用得最多的。
BRNN 的缺点就是须要完整的数据序列,才能预测任意位置。
一个标准的神经网络,首先是输入 \(x\),而后堆叠上隐含层。深层 RNN 相似,堆叠隐含层,而后每层按时间展开就是了,以下图所示。
对于标准的神经网络,可能有很深的网络,可是对于 RNN 来讲,有三层就已经很多了。因为时间的维度,RNN 网络会变得至关大。
词嵌入是语言表示的一种方式,可让算法自动的理解一些相似的词。好比男人对女人,国王对王后等等。
以前咱们是用词典的 one-hot 向量来表示词,好比说 man 在词典中第 5391 个位置,那么它的 one-hot 向量标记为 \(O_{5391}\)。这种表示方法的一大缺点就是它把每一个词都孤立起来了,使得算法对相关词的泛化能力不强。
举个例子,咱们的语言模型已经学习到了 "I want a glass of orange juice",可是当它看到 "I want a glass of apple ____" 时,算法可能没法填出 juice 这个单词。算法不知道 apple 和 orange 的关系很接近,由于任何两个 one-hot 向量的内积都是 0。
可是若是咱们用特征化来表示每一个词,假如说这些特征维度 Gender, Royal, Age 等等,这样对于不一样的单词,算法会泛化得更好。
固然,咱们最终学习的特征可能不会像 Gender, Royal 等这些比较好理解,甚至不太好用实际意义去解释。
接下来,咱们能够把词嵌入应用到命名实体识别任务当中,尽管咱们可能只有一个很小的训练集,100,000 个单词,甚至更小。咱们可使用迁移学习,把互联网上免费得到的大量的无标签文本中学习到的知识迁移到一个任务中。
因此,如何用词嵌入作迁移学习的步骤以下:
假如说咱们以这四个维度的特征来表征词。词的特征向量都以符号 \(e\) 表示。
那么
\[ e_{\text{man}} - e_{\text{woman}} = \begin{bmatrix} - 1 \\ 0.01 \\ 0.03 \\ 0.09 \\ \end{bmatrix} - \begin{bmatrix} 1 \\ 0.02 \\ 0.02 \\ 0.01 \\ \end{bmatrix} = \begin{bmatrix} - 2 \\ - 0.01 \\ 0.01 \\ 0.08 \\ \end{bmatrix} \approx \begin{bmatrix} - 2 \\ 0 \\ 0 \\ 0 \\ \end{bmatrix}\\ e_{\text{king}} - e_{\text{queen}} = \begin{bmatrix} - 0.95 \\ 0.93 \\ 0.70 \\ 0.02 \\ \end{bmatrix} - \begin{bmatrix} 0.97 \\ 0.95 \\ 0.69 \\ 0.01 \\ \end{bmatrix} = \begin{bmatrix} - 1.92 \\ - 0.02 \\ 0.01 \\ 0.01 \\ \end{bmatrix} \approx \begin{bmatrix} - 2 \\ 0 \\ 0 \\ 0 \\ \end{bmatrix} \]
能够发现这两组向量相减获得的向量基本一致。也就代表,这两对词都只在 gender 这个特征维度有显著差别。
咱们可使用余弦类似度来表征这些向量的类似度。
\[ \text{sim}\left( u,v \right) = \frac{u^{T}v}{\left| \left| u \right| \right|_{2}\left| \left| v \right| \right|_{2}} \]
这样,咱们就能经过计算类似度来找到相近的词。
当咱们应用算法来学习词嵌入时,实际上是在学习一个嵌入矩阵 (embedding matrix)。
假设咱们的词典有 10,000 个单词,咱们要作的就是学习一个嵌入矩阵 \(E\),它将是一个 \(300\times10,000\) 的矩阵。这个矩阵的各列表明的是词典中 10,000 个单词所表明的特征向量。
创建一个语言模型是学习词嵌入的好方法。
如何创建神经网络来预测序列中的下一个单词呢?首先,如下图中的句子为例。先使用 one-hot 向量表示这些单词,而后生成一个参数矩阵 \(E\),用 \(E\) 乘以 one-hot 向量 \(o\),这样获得嵌入向量 \(e\)。因而咱们有了不少 300 维的嵌入向量,把它们放进神经网络中,而后再经过一个 softmax 层,而后 softmax 分类器会在 10,000 个可能的输出中预测结尾这个单词。
实际上,更常见的是有一个固定的历史窗口。举个例子,咱们老是想预测给定四个单词(也能够是其余的个数)后的下一个单词,这样就能够适应很长或者很短的句子。用一个固定的历史窗口意味着能够处理任意长度的句子,由于输入的维度老是固定的。因此,这个模型的参数就是矩阵 \(E\),对全部的单词用的都是同一个矩阵 \(E\)。
固然除了选前四个单词,还有其余的上下文构建方式。可是创建语言模型,用目标词的前几个单词做为上下文是常见作法。
假设在训练集中给定了一个这样的句子 "I want a glass of orange juice to go along with my cereal.",在 skip-gram 模型中,咱们要作的是抽取上下文和目标词配对,来构造一个监督学习问题。上下文不必定老是目标单词之间离得最近的四个单词或 n 个单词。
咱们要作的是随机选一个词做为上下文词,而后随机在必定词距内选另外一个词做为目标词。因而咱们将构造一个监督学习问题,它给定上下文词,要求预测在这个词必定词距内随机选择的某个目标词。显然,这不是个很是简单的学习问题。可是,构造这个监督学习问题的目标并非想要解决这个监督学习问题自己,而是想要使用这个学习问题来学到一个好的词嵌入模型。
咱们要解决的基本的监督学习问题是学习一种映射关系,从上下文 \(c\) 到某个目标词 \(t\)。从 one-hot 向量 \(O_c\) 开始,而后乘以嵌入矩阵 \(E\) 获得上下文词的嵌入向量,\(e_c=EO_c\)。接着,把向量 \(e_c\) 喂入 softmax 单元,输出 \(\hat{y}\),预测不一样目标词的几率:
\[ Softmax:p(t|c)=\frac{e^{\theta_t^T e_c}}{\sum_{j=1}^{10,000}e^{\theta_j^T e_c}} \]
其中 \(\theta_t\) 是一个与输出 \(t\) 有关的参数,即某个词 \(t\) 和标签相符的几率是多少,这里省略了 softmax 中的误差项,想要加上的话也是能够加上的。
因而 softmax 的损失函数为
\[ L(\hat{y},y)=-\sum_{i=1}^{10,000}y_i\log{\hat{y_i}} \]
矩阵 \(E\) 将会有不少参数,优化这个关于全部这些参数的损失函数,就能获得一个较好的嵌入向量集。这个就叫作 skip-gram 模型。
这个算法首要的问题就是计算速度,尤为是在 softmax 模型中,每次要计算这个几率,就要对词典中全部词作求和计算,这个求和操做是至关慢的。
这里有一些解决方案,如分级 (hierarchical) 的 softmax 分类器和负采样 (Negative Sampling)。
分级 softmax 分类器
这个分类器的意思是,经过一层一层的节点来分类词。这样计算成本与词典大小的对数成正比,而不是词典大小的线性函数。在实践中,不会使用一棵完美平衡的分类树或者说一棵左边和右边分支的词数相同的对称树,而是会被构形成经常使用词在顶部,不经常使用的词在树的更深处。这是一种加速 softmax 分类器的方法。
这个算法要作的是构造一个新的监督学习算法。给定一对单词来预测者是不是一对上下文词-目标词 (context-target)。
好比,orange 和 juice 为一对正样本,orange 和 king 为一对负样本。咱们要作的就是采样获得一个上下文词和一个目标词。正样本的生成方式与 word2vec 相似,先抽取一个上下文词,在必定词距内选一个目标词,标记为 1。而后为了生成一个负样本,咱们将用相同的上下文词,再在字典中随机选一个词,标记为 0。若是咱们挑选负样本的时候,从字典中随机选到的词,正好出如今了词距内,可是咱们标记为负样本也不要紧。
而后,咱们将构造一个监督学习问题。咱们的算法输入词对,预测其标签。
K 值的选取。论文做者推荐小数据集的话,K 从 5 到 20 比较好;若是数据集很大,K 就选的小一点,如 K 等于 2 到 5。这个例子中,咱们使 \(K=4\)。
咱们定义一个逻辑回归模型,给定输入的 \(c,t\) 对(上下文词 \(c\) 和目标词 \(t\))的条件下输出 \(y=1\) 的几率,即
\[ P(y=1|c,t)=\sigma(\theta_t^Te_c) \]
把它画成一个神经网络,若是输入词是 orange,即第 6257 个词,那么输入它的 one-hot 向量,乘以嵌入矩阵 \(E\),得到嵌入向量 \(e_{6257}\)。这样,咱们获得了 10,000 个可能的逻辑回归分类问题。其中一个是用来判断目标词是不是 juice 的分类器。但不是每次迭代都训练所有 10,000 个,\(K=5\) 时,咱们只训练其中的 5 个。训练对应真正目标词那一个分类器,再训练 4 个随机选取的负样本,因此不使用一个巨大的 softmax,而是把它转变为多个二分类问题。二分类问题每一个都很容易计算,并且每次迭代只要训练它们其中的几个。
其中一个重要的细节就是如何选取负样本。一个方法是根据语言中的经验频率对这些词进行采样,可是 like, the, of, and 这种词有很高的频率。另外一个就是用 1 除以词典总词数,即 \(\frac{1}{|v|}\),均匀且随机地抽取负样本,可是这对于英文单词的分布是很是没有表明性的。做为一个折中,论文做者根据经验,采用如下方式进行采样,也就是实际观察到的英文文本的分布:
\[ P(w_i)=\frac{{f(w_i)^{\frac{3}{4}}}}{\sum_{j=1}^{10,000}{f(w_j)}^{\frac{3}{4}}} \]
也就是 \(f(w_i)\) 是观测到的在语料库中的某个单词的词频,经过 \(\frac{3}{4}\) 次方的计算,使其处于彻底独立的分布和训练集的观测分布两个极端之间。
GloVe 算法不如 Word2Vec 或是 Skip-Gram 模型用的多,可是也有研究者热衷于它,多是由于其简便性。
GloVe 表明用词表示的全局变量。仍是挑选语料库中位置相近的两个词,即上下文-目标词。GloVe 算法作的就是使其关系开始明确化。假设 \(X_{ij}\) 是单词 \(i\) 在单词 \(j\) 上下文中出现的次数,那么这里 \(i\) 和 \(j\) 就和 \(t\) 和 \(c\) 的功能同样。
若是对于上下文的定义是目标词必定范围词距的单词,那么 \(X_{ij}=X_{ji}\);而若是对于上下文的定义为目标词的前一个单词,那么 \(X_{ij}\) 和 \(X_{ji}\) 就不会相同。
不过对于 GloVe 算法,咱们能够定义上下文和目标词为任意两个位置相近的单词,假设是左右各 10 词的距离,那么 \(X_{{ij}}\) 就是一个可以获取单词 \(i\) 和单词 \(j\) 出现位置相近时或是彼此接近的频率的计数器。GloVe 模型作的就是进行优化,将它们之间的差距进行最小化处理。
\[ minimize \sum_{i=1}^{10,000} \sum_{j=1}^{10,000} f(X_{ij})(\theta_i^Te_j+b_i+b_j'-\log{X_{ij}})^2 \]
而若是 \(X_{ij}=0\) 的话,\(log0\) 为未定义,为负无穷大。因此公式中加上了一个额外的加权项 \(f(X_{ij})\),这样 \(X_{ij}=0\) 时,咱们有 \(0log0=0\)。这个加权项还有一个做用是,有些词在英语中出现十分频繁如 this, is, of, a 等,它们被叫做中止词,加权项能够给予大量有意义的运算给不经常使用词,一样给中止词更大但不至于过度的权重。所以,有一些对加权函数 \(f\) 的选择有着启发性的原则。
情感分类任务就是看一段文本,而后分辨这我的是否喜欢他们在讨论的这个东西,这是天然语言处理中最重要的模块之一,常常用在许多应用中。情感分类一个最大的挑战就是可能标记的训练集没有那么多,可是有了词嵌入,即便只有中等大小的标记的训练集,也能构建一个不错的情感分类器。
下图是一个简单的情感分类模型。假设输入为 "The dessert is excellent",咱们从词典中取出这些词,而后造成 one-hot 向量,乘以嵌入矩阵 \(E\) 来获取嵌入向量。其中嵌入矩阵能够从很大的训练集上训练得到。接着,对这些嵌入向量进行求和或者平均,就会获得一个特征向量,把它输入 softmax 分类器,输出 \(\hat{y}\) 也就是一星到五星的几率值。
这个算法运用的平均值运算单元适用于任何长短的评论,它实际上会把全部单词的意思给平均起来。
这个算法有一个问题就是没有考虑词序,尤为是这样一个负面的评价。
"Completely lacking in good taste, good service, and good ambiance."
这个句子中出现了不少 good,分类器极可能会认为这是一个好的评价。
这样,咱们有一个更加复杂的模型来处理,使用 RNN 来作情感分类。以下图所示。
一个已经完成学习的词嵌入可能会输出Man:Computer Programmer,同时输出Woman:Homemaker,那个结果看起来是错的,而且它执行了一个十分不良的性别歧视。所以根据训练模型所使用的文本,词嵌入可以反映出性别、种族、年龄、性取向等其余方面的偏见,一件我尤为热衷的事是,这些偏见都和社会经济状态相关,我认为每一个人不论你出身富裕仍是贫穷,亦或是两者之间,我认为每一个人都应当拥有好的机会,同时由于机器学习算法正用来制定十分重要的决策,它也影响着世间万物,从大学录取到人们找工做的途径,到贷款申请,不论你的的贷款申请是否会被批准,再到刑事司法系统,甚至是判决标准,学习算法都在做出很是重要的决策,因此我认为咱们尽可能修改学习算法来尽量减小或是理想化消除这些非预期类型的偏见是十分重要的。
假设说咱们已经完成一个词嵌入的学习,先咱们要作的事就是辨别出咱们想要减小或想要消除的特定偏见的趋势。
以性别偏见为例。主要有如下三个步骤。
对于性别偏见来讲。咱们将一些性别相关的词对进行嵌入向量相减,如 \(e_{he}-e_{she},e_{male}-e_{female}\),而后将这些值取平均。这个趋势,看起来就是性别趋势,可是与咱们想要处理的特定偏见无关,因此这就是个无偏的性别趋势。实际上,它会用一个更加复杂的算法——奇异值分解(SVU),和主成分分析很相似。
中和步骤。对于那些定义不确切的词能够将其处理一下。如 grandmother, grandfather 这些词定义中原本就含有性别意义,而 doctor, babysitter 这些词咱们但愿它是中立的。因此对于中立词,咱们想要减小他们在水平方向上的距离。
均衡背后的关键思想是确保一对特定的单词与49维\(g_\perp\)距离相等 。均衡步骤还能够确保两个均衡步骤如今与\(e_{receptionist}^{debiased}\) 距离相同,或者用其余方法进行均衡。下图演示了均衡算法的工做原理:
主要步骤以下:
$$
\mu = \frac{e_{w1} + e_{w2}}{2}\
\mu_{B} = \frac {\mu * \text{bias_axis}}{||\text{bias_axis}||_2} + ||\text{bias_axis}||_2 *\text{bias_axis}\
\mu_{\perp} = \mu - \mu_{B} \
e_{w1B} = \sqrt{ |{1 - ||\mu_{\perp} ||^2_2} |} * \frac{(e_{{w1}} - \mu_{\perp}) - \mu_B} {|(e_{w1} - \mu_{\perp}) - \mu_B)|}\
e_{w2B} = \sqrt{ |{1 - ||\mu_{\perp} ||^2_2} |} * \frac{(e_{\text{w2}} - \mu_{\perp}) - \mu_B} {|(e_{w2} - \mu_{\perp}) - \mu_B)|} \
$e_1 = e_{w1B} + \mu_{\perp} \
$e_2 = e_{w2B} + \mu_{\perp}
$$
如何构建一个网络来实现机器翻译呢?好比实现输出法语句子 "Jane visite I'Afrique en septembre.",输出英语句子 "Jane is visiting Africa in September."。
首先,创建一个网络,这个网络叫编码网络 (encoder network),以下图编号 1 所示。它是一个 RNN 的结构,RNN 的单元能够是 GRU 也能够是 LSTM。每次只向该网络中输入一个法语单词,将输入序列接收完毕后,这个 RNN 网络会输出一个向量来表明这个输入序列。在这个网络后面,咱们创建一个解码网络 (decoder network),以下图编号2所示。它以编码网络的输出做为输入,被训练为每次输出一个翻译后的单词,一直到它输出序列的结尾或者句子结尾标记。
这个模型简单地用一个编码网络来对输入的法语句子进行编码,而后用一个解码网络来生成对应的英语翻译。
与此相似的结构也被用来作图像描述,给出一张图片,以下图中的猫的图片,它能自动地输出该图片的描述:一只猫坐在椅子上。
咱们以前已经知道如何将图片输入到卷积神经网络中,好比一个预训练的 AlexNet 结构(上图编号 2)。而后让其学习图片的编码或者学习图片的一系列特征,也就是去掉 softmax 单元(上图编号 3)后的部分会输出一个 4096 维的特征向量,也就是一个图像的编码网络。而后把这个向量输入到 RNN 中(上图编号 4),使用 RNN 来生成图像的描述。
咱们能够把机器翻译当作是创建一个条件语言模型,在语言模型中上方是一个咱们以前创建的模型,这个模型能够估计句子的可能性,也就是语言模型所作的事情。而机器翻译分为两部分:编码网络(下图绿色)和解码网络(下图紫色),而咱们发现解码网络其实和语言模型几乎如出一辙。不一样在于语言模型老是以零向量(下图编号 4)开始,而机器翻译的编码网络会计算出一系列向量(下图编号 2)来表示输入的句子,解码网络则以这个句子的特征开始,而不是零向量。因此吴恩达老师称之为条件语言模型 (conditional language model)。
咱们想实现真正地经过模型将法语翻译成英文,经过输入的法语句子获得各类英文翻译所对应的可能性。\(x\) 在这里是法语句子 "Jane visite I'Afrique en septembre"。咱们不想让模型随机地输出,即从获得的分布中进行随机取样,而是找到一个英语句子 \(y\),使得条件几率最大化。
解决这种问题,最通用的算法就是集束搜索 (Beam Search),而不用贪心搜索 (Greedy Search)。
贪心搜索指的是一种来自计算机科学的算法。生成第一个词的分布之后,它将会根据条件语言模型挑选出最有可能的第一个词进入机器翻译模型中,而后继续挑选最有可能的第二个词,接着一直日后挑选最有可能的词。
可是咱们真正须要的是一次性挑选出整个单词序列,从 \(y^{<1>},y^{<2>}\) 到 \(y^{<T_y>}\) 来使得总体的几率最大化。因此贪心算法并无论用。
上图中编号 1 的翻译明显比编号 2 的好,因此咱们但愿机器翻译模型会输出第一个句子的 \(P(y|x)\) 比第二个句子要高。但若是使用贪心算法来挑选出了 "Jane is" 做为前两个词,由于在英语中 going 更加常见,因此模型会选择 "Jane is going" 而不是 "Jane is visiting" 做为翻译,最终获得一个欠佳的句子。
集束搜索算法首先作的就是挑选要输出的英语翻译中的第一个单词,为了简化问题,咱们忽略大小写,列出了 10,000 个词的词汇表。集束搜索的第一步是用这个网络(绿色是编码网络;紫色是解码网络),来评估第一个单词的几率值。给定输入序列 \(x\),即法语句子,输出 \(y\) 的几率值是多少。
贪婪算法只会挑选最可能的一个单词,而后继续,而集束搜索则会考虑多个选择。集束搜索算法会有一个参数 \(B\),称为集束宽 (beam width)。本例中咱们设为 3,意味着集束搜索一次会考虑 3 个词,而后把结果存在计算机内存里以便后面尝试使用这三个词。
假设咱们选出了第一个单词三个最有可能的选择为 in, jane, september,集束搜索的第二步会针对每一个第一个单词考虑第二个单词是什么,以下图编号 1。为了评估第二个词的几率值,咱们用神经网络,绿色是编码部分(下图编号 2)。对于解码部分,当决定单词 in 后面是什么时,解码器的第一个输出 \(y^{<1>}\) 为 in (下图编号 3),而后把它喂回下一个网络单元(下图编号 4)。这里的目的是找出第一个单词是 in 的状况下,第二个单词是什么,即 \(y^{<2>}\) (下图编号 5)。
在第二步中,咱们更关心的是要找到最可能的单词对(下图编号 7),而不只仅是最大几率的第二个单词。按照条件几率的准则,单词对的几率能够表示为第一个单词的几率(下图编号 8)乘以以第一个单词为条件的第二个单词的几率(下图编号 9),然后者能够从编号 10 的网络中获得。
同理,对于第一个单词的第二个备选 "jane" ,第三个备选 "september" 也是一样的步骤。因为咱们一直用的集束宽为 3,而且词汇表里有 10,000 个单词,那么最终会有 \(3\times10,000\) 也就是 30,000 个可能的结果。而后依旧按照单词对的几率选出前三个,减小到集束宽的大小。集束搜索算法会保存这些结果,而后用于下一次集束搜索。
接下来的步骤,继续选择与第二步相似。值得注意的是,若是集束宽等于 1,只考虑一种可能结果,这实际上就变成了贪婪搜索算法。
有一些小技巧能够帮助集束搜索算法运行的更好。
长度归一化 (length normalization) 就是对集束搜索算法稍做调整的一种方式。集束搜索其实就是最大化
\[ \begin{equation} \mathop{\arg\max}_{y} \prod_{t=1}^{T_y}P(y^{<t>}|x,y^{<1>},\dots,y^{<t-1>}) \end{equation} \]
而连乘的乘积其实就是 \(P(y^{<1>},\dots,y^{<{T_y}>}|x)\)。若是计算它,其实相乘的这些几率值都是小于 1 的,一般远小于 1。而不少小于 1 的数相乘,会获得很小很小的数字,会形成数值下溢 (numerical underflow)。指的是数值过小了,致使电脑的浮点表示不能精确地存储。所以在实践中,咱们取 log 值,从而获得一个数值上更稳定的算法。即
\[ \begin{equation} \mathop{\arg\max}_{y} \sum_{t=1}^{T_y}\log P(y^{<t>}|x,y^{<1>},\dots,y^{<t-1>}) \end{equation} \]
对于目标函数,还能够作一些改变,可使得机器翻译表现得更好。若是使用上面的目标函数,那么对于一个很长的句子,这个句子的几率会很低,由于乘了不少项小于 1 的数字。因此这个目标函数有一个缺点是,它可能不天然地倾向于简短的翻译结果。咱们能够再也不最大化这个目标函数,而是对其进行归一化,经过除以翻译结果的单词数 \(T_y\)。这样就是取每一个单词的几率对数值的平均了,这样很明显地减小了对输出长的结果的惩罚。即
\[ \begin{equation} \frac{1}{T_y^{\alpha}} \sum_{t=1}^{T_y}\log P(y^{<t>}|x,y^{<1>},\dots,y^{<t-1>})\end{equation} \]
上式中的参数 \(\alpha\),可使得归一化更加柔和,\(\alpha\) 能够等于 0.7。若是 \(\alpha\) 等于 1,就至关于彻底用句子长度来归一化,若是 \(\alpha\) 等于 0,就至关于彻底没有归一化。它就是算法另外一个超参数,须要调整大小来获得最好的结果。
对于如何选择集束宽参数 \(B\)。\(B\) 越大,考虑的选择越多,找到的句子可能越好;可是算法的计算代价也会越大,算法会运行得慢一些,内存占用也会增大。在实践中,其实使用 \(B=3\) 有点偏小。在生产中,常常能够看到把集束宽设为 10,集束宽为 100 对于生产系统来讲有点过大;但对于科研来讲,人们想得到最好的结果用来发表论文,因此常常能够看到集束宽为 1,000 甚至 3,000。对不少应用来讲,从集束宽为 1,到 3,到 10,可能能够看到一个很大的提高;可是当集束宽从 1,000 增长到 3,000 时,效果可能就没那么明显了。
如下面的例子来讲明。
仍然须要翻译法语句子 "Jane visite I'Afrique en septembre"。假设机器翻译的 dev 集中,也就是开发集 (development set),人工是这样翻译的 "Jane visits Africa in September",记为 \(y^*\)。当已经完成学习 RNN 模型,也就是已完成学习的翻译模型中运行集束搜索算法时,它输出的翻译为 "Jane visited Africa last September",记为 \(\hat{y}\)。
咱们的模型有两个主要部分:RNN 模型和集束搜索算法。如今,咱们想要找出形成输出 \(\hat{y}\) 这个不太好的翻译的缘由。
RNN 其实是个编码器和解码器,它会计算 \(P(y|x)\)。咱们可使用这个模型来计算 \(P(y^*|x)\) 和 \(P(\hat{y}|x)\),而后比较一下这两个值哪一个更大。
第一种状况: \(P(y^*|x)>P(\hat{y}|x)\)
这种状况下,意味着集束搜索选择了 \(\hat{y}\),也就是集束搜索算法此时不可以输出一个使 \(P(y|x)\) 最大化的 \(y\) 值,由于集束搜索算法的目的就是寻找一个 \(y\) 值来使它更大。
所以这种状况下,咱们可以得出是集束搜索算法出错了。
第二种状况: \(P(y^*|x)\le P(\hat{y}|x)\)
这种状况下,意味着相比与 \(\hat{y}\),\(y^*\) 成为输出的可能性更小,可是后者其实上是比前者更好的翻译结果。也就是说,这种状况下,是 RNN 模型出了问题。
因此偏差分析的过程其实就以下图这样。先遍历开发集,而后在其中找出算法产生的错误。经过这个过程,咱们就可以执行偏差分析,得出集束搜索算法和 RNN 模型出错的比例,来指导模型的优化。
Bleu 表明的是 bilingual evaluation understudy (双语评估替补),这是一种常见的衡量机器翻译的准确性的方法。
假如咱们有一个法语句子 "Le chat est sur le tapis",而后其对应的一我的工翻译参考为 "The cat is on the mat"。不过有多种至关不错的翻译。因此其余的人,也许会翻译为 "There is a cat on the mat"。实际上,这两个都是很好的翻译。Bleu 得分作的就是,给定一个机器生成的翻译,它可以自动地计算一个分数来衡量机器翻译的好坏。直觉告诉咱们,只要这个机器生成的翻译与任何一我的工翻译的结果足够接近,那么它就会获得一个高的 Bleu 分数。
咱们以一个极端的例子为例。假设机器翻译 (MT) 的输出是 "the the the the the the the"。这显然是一个十分糟糕的翻译。衡量机器翻译输出质量的方法之一,是观察输出结果的每个词看其是否出如今参考中,这杯称做是机器翻译的精确度。这种状况下,机器翻译输出了七个单词而且这七个词中的每个都出如今了参考 1 或是参考 2。单词 the 在两个参考中都出现了,因此看上去每一个词都是很合理的,即这个精确度就是 \(\frac{7}{7}\),看起来是一个极好的精确度。
因此这种方法并非颇有用,将其进行改良,咱们把每个单词的计分上限定为它在参考句子中出现的最屡次数。在参考 1 中,单词 the 出现了两次;参考 2 中,单词 the 出现了一次。因此单词 the 的得分上限为 2。那么这个改良后的精确度为 \(\frac{2}{7}\)。分母为 7 个词中单词 the 总共出现的次数,分子为单词 the 在参考中的出现的计数。
到目前为止,咱们都只是关注单独的单词。若是咱们想考虑成对的单词,定义一下二元词组 (bigrams) 的 Bleu 得分。固然这仅仅只是最终的 Bleu 得分的一部分,可能会考虑单个单词以及二元或多元词组。在下面的例子中,咱们分别统计 MT 输出的二元词组在 MT 输出和参考中的计数。所以 \(\frac{4}{6}=\frac{2}{3}\) 为二元词组的改良后的精确度。
如今咱们将其泛化为 n 元词组,其精确度定义为
\[ P_n=\frac{\sum_{n-grams\in\hat{y}}Count_{clip}(n-gram)}{\sum_{n-grams\in\hat{y}}Count(n-gram)} \]
最终的 Bleu 得分被定义为(以综合 \(P_1,P_2,P_3,P_4\) 为例)
\[ Combined Bleu score = exp(\frac{1}{4}\sum_{n=1}^4P_n) \]
实际上还会用到额外的一个叫作 \(BP\) 的惩罚因子来调整,其意思为简短惩罚 (brevity penalty)。那么定义则为
\[ Combined Bleu score = BP\cdot exp(\frac{1}{4}\sum_{n=1}^4P_n)\\ BP=\begin{cases} 1 & if\ MT\_output\_length > reference\_output\_length\\ exp(1-\frac{reference\_output\_length}{MT\_output\_length}) & otherwise \end{cases} \]
像下图这样一个很长的法语句子,咱们的神经网络中,绿色部分的编码器要作的就是读整个句子,而后记忆整个句子,再在感知机中传递。而对于紫色部分的解码器,它将生成英文翻译。
可是人工翻译并不会经过读整个法语句子,再记忆里面的东西,而后从零开始翻译成英语句子。人工翻译会一部分一部分地翻译,由于记忆整个句子是很是困难的。对于机器翻译来讲也是如此,对于短句子效果可能很是好,有相对高的 Bleu 分数,可是对于长句子,它的表现就会变差。
注意力模型源于机器翻译,但也推广到了其余应用领域。
仍然以法语句子 "Jane visite I'Afrique en Septerbre" 为例。假定咱们使用一个双向的 RNN,为了计算每一个输入单词的特征集。它可使用 GRU 或者 LSTM 做为基本单元,实践中, LSTM 使用得更为常常一些。而后,使用另外一个 RNN 生成对应的英文翻译,咱们使用记号 \(S\) 表示这个 RNN 的隐藏状态而不用 \(A\)。
当咱们尝试生成英文翻译的第一个词时,咱们应该看对应法语句子的第一个单词及它附近的词。因此注意力模型就会计算注意力权重,咱们使用 \(\alpha^{<1,1>}\) 来表示当生成第一个词时,注意力放在第一块信息处的权重。对应的有 \(\alpha^{<1,2>},\alpha^{<1,3>}\)。把他们综合起来做为翻译第一个词的上下文语境,记为 \(C\),这就是这个 RNN 的一个单元。其余单词以此类推,直到最终生成 <EOS>。
再次说明,注意力权重 \(\alpha^{<t,t>}\) 表示的是,生成第 t 个英文词时,须要花多少注意力在第 t 个法语词上面。
咱们仍然使用 \(t\) 来表示时间步,\(a^{<t>}\) 就是时间步 \(t\) 上的特征向量。使用 \(t'\) 来索引法语句子里面的词。那么 \(t=1\) 时的上下文语境,就是经过计算注意力权重(上图编号 1)和其对应的特征向量(上图编号 2)的乘积和。即
\[ C^{<1>}=\sum_{t'}\alpha^{<1,t'>}a^{<t'>} \]
注意,在一个时间步中,全部的注意力权重均为非负,且它们的和为 1,即
\[ \sum_{t'}\alpha^{<1,t'>}=1 \]
\(\alpha^{<t,t'>}\) 是花费在 \(a^{<t'>}\) 上的注意力权重。它的公式如上图所示。计算它以前,咱们须要先计算 \(e^{<t,t'>}\),关键要用 softmax 以确保这些权重加起来等于 1。
计算 \(e\) 值能够训练一个上图所示的小型的神经网络。咱们不知道具体的函数去计算它,可是可使用梯度降低算法计算一个正确的函数。
这个算法的一个缺点就是它要花费三次方的时间,也就是说这个算法的复杂度是 \(O(n^3)\)。可是在机器翻译的应用上,输入和输出的句子通常不会太长,可能三次方的消耗也是能够接受的。
语音识别问题指的是,输出音频片断 \(x\) 自动地生成文本 \(y\)。
咱们使用注意力模型来构建语音识别系统。就是在横轴上,也就是输入音频的不一样时间帧上,用注意力模型来输出文本描述。
也可使用 CTC 损失函数来作语言识别,其中 CTC 指的是 Connectionist Temporal Classification。
其算法思想以下:
假设语言片断内容为 "the quick brown fox",这时咱们使用一个新的网络,结构如上图所示。输入的 \(x\) 与输出 \(y\) 的长度是同样的,示例的只是一个简单的单向 RNN 结构。在实践中,它能够是双向的 LSTM 或 GRU,而且一般是很深的模型。注意,这里时间步的数量很是大。在语音识别中,一般输入的时间步数量要比输出的时间步数量多出不少。这种状况下,CTC 损失函数容许 RNN 生成相似这样的输出 "ttt",而后一个空白符,咱们如下划线表示,而后 "h_eee___" 等。这样的输出(如上图所示)对应的就是 "the q"。这样,须要输出的内容其实只有 19 个字符,可是神经网络容许有不少这种重复的字符和不少插入在其中的空白符,使得它能强制输出 1000 个字符。
如今有不少智能系统有其对应的触发词模块,以下图所示。
对于触发词检测,最好的算法是什么,目前尚未一个普遍的定论。
咱们以一个算法为例。如今有一个 RNN 结构,咱们须要把一个音频片断计算出它的声谱图特征 (spectrogram features) 获得特征向量 \(x^{<1>},x^{<2>},\dots\)。而后,把它放到另外一个 RNN 中,再定义目标标签 \(y\)。假如音频片断中的某一点为刚刚说完一个触发词,那么以前的目标标签都设为 0,这点以后对应触发词的音频特征设为 1。这样的标签方案对于 RNN 来讲是可行的,而且确实运行得不错。不过该算法一个明显的缺点就是它构建了一个很不平衡的训练集,0 的数量比 1 多太多了。
这里有一个解决方法,虽然听起来有点简单粗暴,但确实能使其变得更容易训练。比起只在一个时间步上去输出 1,其实你能够在输出变回 0 以前,屡次输出 1,或说在固定的一段时间内输出多个 1。这样的话,就稍微提升了 1 与 0 的比例。
References