吴恩达深度学习笔记——序列模型与循环神经网络(Sequence Models)

前言

本系列文章是吴恩达深度学习攻城狮系列课程的笔记,分为五部分。

这一章主要将了循环神经网络。

我的笔记不同于一般的笔记,我的笔记更加凝练,除了结论以及公式,更多的是对知识的理解,结合课程可以加速理解,省去很多时间,但是请注意,笔记不能替代课程,应该结合使用。

传送门

结构化机器学习项目,我建议放在最后看。

首先学这一节对你后面的学习没有影响,我就是跳过去学的。而且他更多的讲的是策略,尤其是举了很多后面的例子,你听了不仅不太好懂,而且没啥意思,所以我建议放在最后看。

神经网络与深度学习(Neural Networks and Deep Learning)

改善深层神经网络:超参数调整,正则化,最优化(Hyperparameter Tuning)

卷积神经网络(Convolutional Neural Networks)

序列模型与循环神经网络(Sequence Models)

结构化机器学习项目(Structuring Machine Learning Projects)

序列模型(Sequence Models)

Recurrent Neural Networks(循环神经网络)

序列模型

输入和输出都可能是序列,而且输入和输出的序列长度并不一定能保证一致,甚至输入内部的长度也不能保证一致。

符号:以NLP举例

  1. x ( i ) x^{(i)} x(i),一个样本,在NLP中通常为一句话。
  2. x ( i ) < t > x^{(i)<t>} x(i)<t>代表第个i样本序列中第t个元素。比如一个单词
  3. T x ( i ) T_{x^{(i)}} Tx(i)代表第i个样本序列的长度。
  4. Vocabulary:一个向量,每一个元素都是一个单词,比如big,cat等,长度从3w到10w不等大的可能有100w。
  5. 有了这个Vocabulary,那么 x ( i ) < t > x^{(i)<t>} x(i)<t>就可以用这个单词在Vocabulary中的位置来表示,通常是用one-hot编码来把一个单词表示成一个向量。
  6. 如上规则,对于y也是一样的。

到现在,已经构建了x,一个维度可变的矩阵,到y,维度可变的矩阵,如何建立映射就是模型的事情了。

循环神经网络基础

如果用传统神经网络实现模型,会面临两个问题:

  1. 对于一个样本,他实际上是二维的,由 T x T_x Tx个one-hot向量构成,那么神经网络的参数量会特别大。
  2. 每一个样本的长度是不同的,传统神经网络处理需要扩展数据到同一维度,效率很低。
  3. 而且这种独立处理的方式,并不能学习到序列的特性,前后关系。

循环神经网络的思路就是,在输入层到输出层之间弄一个隐藏层,这一个隐藏层可以适应不通长度的输入。

对一个样本的计算过程是:

  1. 计算输入的长度,同时初始化
    a < 0 > = 0 ⃗ a^{<0>}=\vec{0} a<0>=0
  2. 用前一层的a和这一层的x计算这一层的a
    a < i > = σ ( W a a a < i − 1 > + W a x x < i > + b a ) a^{<i>}=\sigma(W_{aa}a^{<i-1>}+W_{ax}x^{<i>}+b_a) a<i>=σ(Waaa<i1>+Waxx<i>+ba)
  3. 用这一层的a计算输出
    y < i > = σ ( W y a a < i > + b y ) y^{<i>}=\sigma(W_{ya}a^{<i>}+b_y) y<i>=σ(Wyaa<i>+by)
  4. 由此递归/循环完整个长度的序列

双下标可以这么理解,第一个下标是目标,第二个下标是要利用的(要乘的)变量类型

之所以叫循环,是因为全体过程走下来,对于一个样本的每一次循环来说,用的参数都是一个参数,只不过在递归的,依次地对样本的每个词元做处理。

从这个过程来看,每一个a和前面所有的样本有关,也就是说有时序特性。缺点在于无法学习反向的联系,后面会优化。

在这里插入图片描述

重写一下公式:

  1. a < t > = σ ( W a [ a < t − 1 > , x < t > ] + b a ) a^{<t>}=\sigma(W_a[a^{<t-1>},x^{<t>}]+b_a) a<t>=σ(Wa[a<t1>,x<t>]+ba)
    这里注意[ ]是类似矩阵写法,实际上是竖着的分块矩阵,同样, w a w_a wa其实也是两个参数矩阵水平分块堆叠起来。用分块矩阵积可以还原回最初始的公式
  2. y < i > = σ ( W y a < i > + b y ) y^{<i>}=\sigma(W_{y}a^{<i>}+b_y) y<i>=σ(Wya<i>+by)
    这个很好理解,因为只需要乘a,所以没必要区分。
  3. 经过重写后的参数,就非常容易区分, W a , W y , b a , b y W_a,W_y,b_a,b_y Wa,Wy,ba,by

bp through time环节:

损失函数:对每一个 y < t > y^{<t>} y<t>进行内部的交叉熵计算,然后把所有T个y的交叉熵结果求和。

这样,如果把损失函数降到很低,那么总和低,每一个词元预测的正确率就都会很高。

至于具体怎么求导,就是框架的事儿了。(其实我很好奇,但是现在先还不管),知道损失函数就足够了。

RNN变体

在这里插入图片描述

RNN变体用于各种序列任务中。

多对多的叫many-to-many,是最经典的RNN,通常用于命名实体识别这种。

多对一的叫many-to-one,这种只在最后一个有输出。

一对一的,就是前面经典的神经网络

one-to-many。这个就是送进去第一个x,然后别x的可以用前一步输出的y替代,或者就不需要再送进来了,感觉就是个马尔科夫。

many-to-many有特殊变体,比如机器翻译中,两个序列的长度就不同,又比如有的输入不是序列,有的输出不是序列。

这个要用encoder和decoder,前面是一个多对一的,不输出,但是会把整个句子变成一个a,相当于编码,然后送到decoder里,相当于一对多,再解码。

还有双向RNN,相当于一个从前到后的+一个从后到前的RNN,然后y通过两个隐变量计算。

语言模型(重点)

基础概念

这一节比较难理解,我重点解释。

语言模型:计算一个给定句子的概率。

至于概率怎么算的,你可以记住这句话,从本质来说,是计算一系列soft-max向量,基于这些向量可以得出给定句子的概率。具体细节后面慢慢说。

语料库:首先要有一个corpus,应该是语料库,语料库里有一大堆句子,每一个句子可以理解为一个样本。

标记化tokenize:然后运用已有的vocabulary,将语料库中的每一个样本,转化为one-hot相量的矩阵,比如 y < 1 > , y < 2 > , y < 3 > y^{<1>},y^{<2>},y^{<3>} y<1>,y<2>,y<3>

  1. 通常,输入用x表示,用y表示一个绝对正确的句子,输出用 y ^ \hat{y} y^表示,注意区分y和 y ^ \hat{y} y^,一个是绝对正确的句子,是one-hot编码,另一个只是预测,通常是soft-max格式的。
  2. 这时你可能会好奇,x应该是什么,请保持疑问,往下面看。
  3. 且有时候句号(period)以及其他标点会被考虑
  4. 不认识的词可以被标记为UNK
  5. 而且在结尾可能会加上EOS标记。

最后就是训练参数。

训练过程详解

在这里插入图片描述

这里给的例子看起来比较迷惑,和前面的many-to-many很不一样,因为最开始的任务是词性分类任务,T个输入就有T个输出,对一个输入就要预测其词性。而这个任务是单步预测,预测第一个不需要任何条件,预测第二个需要第一个作为输入以此类推,令y落后一步与x,因为要落后,所以 x < 1 > = 0 , x < t > = y < t − 1 > x^{<1>}=0,x^{<t>}=y^{<t-1>} x<1>=0,x<t>=y<t1>。这也解释了前面的疑问:x用什么?

我们再逐个解释一下 y ^ \hat{y} y^的含义。

y ^ < 1 > = [ P ( a ) P ( c a t ) ⋮ P ( Z u l u ) ] \hat{y}^{<1>}=\begin{bmatrix} P(a)\\ P(cat) \\ \vdots \\ P(Zulu) \end{bmatrix} y^<1>= P(a)P(cat)P(Zulu)
y ^ < 2 > = [ P ( a ∣ c a t ) P ( c a t ∣ c a t ) ⋮ P ( Z u l u ∣ c a t ) ] \hat{y}^{<2>}=\begin{bmatrix} P(a|cat)\\ P(cat|cat) \\ \vdots \\ P(Zulu|cat) \end{bmatrix} y^<2>= P(acat)P(catcat)P(Zulucat)

y ^ < 3 > = [ P ( a ∣ c a t , a v e r a g e ) P ( c a t ∣ c a t , a v e r a g e ) ⋮ P ( Z u l u ∣ c a t , a v e r a g e ) ] \hat{y}^{<3>}=\begin{bmatrix} P(a|cat,average)\\ P(cat|cat,average) \\ \vdots \\ P(Zulu|cat,average) \end{bmatrix} y^<3>= P(acat,average)P(catcat,average)P(Zulucat,average)

首先明白,输出的是一个soft-max向量,代表着10000个词的概率。因为预测第一个词没有给出任何条件,所以就直接预测,至于预测如何,第一个是随机的。但是从第二个开始就不一样了,就变成条件概率了,至于条件,就是 y < t − 1 > y^{<t-1>} y<t1>,而不是 y ^ < t − 1 > \hat{y}^{<t-1>} y^<t1>,请注意区分。这也很合理,预测第一个啥也不用,预测第二个就需要前一个,那预测第三个就需要前两个,以此类推。

最后就是损失函数定义,还是如前面所说的交叉熵。

以上只是训练的过程,正因为是巡练,所以 x < t > = y < t − 1 > x^{<t>}=y^{<t-1>} x<t>=y<t1>,但是到了预测的时候,就只会给出前半截语句,让你预测后半段,因为后半段没给,所以后半段的 x < t > = y ^ < t − 1 > x^{<t>}=\hat{y}^{<t-1>} x<t>=y^<t1>,即,给定模型与前半截语句,那后半截的输出预测就已经确定下来了。

请注意,后半截的预测并不是结果,只是给出soft-max向量,很多时候,并不一定采用概率最大的那个词元作为预测结果。

在你明白预测相量的本质就是soft-max向量以后,我们回归最开始的定义:语言模型可以给出一个语句的概率,那这个概率是怎么算的?

计算过程:给定一系列y,丢进去语言模型,可以得到一系列 y ^ \hat{y} y^,然后 P ( 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>}) P(y<1>,y<2>,y<3>)=P(y<1>)P(y<2>y<1>)P(y<3>y<1>y<2>),至于这三个条件概率,就从对应的 y ^ < t > \hat{y}^{<t>} y^<t>中去寻找对应词的概率,可以想象到,一个词的概率本来就不高,你这T个词的概率一乘,最后得出的概率为 1 0 − 10 10^{-10} 1010级别也很合理了,什么时候能做到0.1级别的概率就很恐怖了。

到这里你也会发现,其实语言模型不只是能计算概率,也可以进行预测,但究其根本,都是概率的计算。

新序列采样(sample novel sequences):

已经训练好了,怎么用呢?所谓的用,就是生成一个序列,可以理解为根据模型本身就代表一种分布,我们给输入,就可以对分布进行采样,得到输出。

采样原则就是根据输出的y-hat里面的概率,随机选择一个作为采样结果词元,而并不一定是直接用概率最大的那个。

理论上,如果你不给输入,也就是直接从头预测,可能每次的结果都基本相同,因为这是个马尔科夫过程。

如果要改变结果,你就需要给一些输入,即使只是第一个输入,也会极大地改变结果,输入越多,预测越准。即使是这样,普通的NLP生成的结果也有一种胡言乱语的感觉,尤其是用char作为词元的模型。

有一处不合理的就是,就是无论你输入什么,都不会改变第一个输出的概率分布。

RNN改进

梯度消失

梯度下降的原理至今没搞清楚,一方面大致是越靠前的层,就越不容易被bp调节,另一方面就是前面的信息向后传递的过程中损失了很多,所以中间隔得越长,就越学不到长期依赖。

梯度爆炸虽然也会出现,但是很明显,可以迅速处理。比如使用gradient clip,当发现有溢出,就进行缩放。

目前有GRU和LSTM两种方式改进,改进后,隐藏层对外的接口表现不变,只不过内部增加了新的变量和转化。

GRU(gated recurrent unit)

Cho, Kyunghyun, et al. “On the properties of neural machine translation: Encoder-decoder approaches.” arXiv preprint arXiv:1409.1259 (2014).

Chung, Junyoung, et al. “Empirical evaluation of gated recurrent neural networks on sequence modeling.” arXiv preprint arXiv:1412.3555 (2014).

思想:

既然单靠隐变量无法记忆较远的信息,那么干脆就创建一个记忆变量,储存较远的信息,又因为较远的信息不多,所以创建一个c变量(可以是向量)也还合理。

c变量通过门变量(也可以是向量)的控制,来确定是要采用新的c值还是保持原状。假设c保持原状,那么就代表一种记忆,也意味着隐变量只受到当前步的微小影响。如果用新的c值,那么就代表更新记忆,舍弃陈旧的记忆。

那么问题来了,看起来c和a是独立的,如何通过c影响输出呢?

那就是用这个c来替代a。

公式:

  1. c ~ < t > = σ ( W c [ c < t − 1 > , x < t > ] + b c ) \tilde{c}^{<t>}=\sigma(W_c[c^{<t-1>},x^{<t>}]+b_c) c~<t>=σ(Wc[c<t1>,x<t>]+bc)
    简单版:tilde-c使用类似于隐变量的计算方式来进行传递,但是这里只是一个临时的c,并不一定会将c覆盖。
  2. c ~ < t > = σ ( W c [ Γ r c < t − 1 > , x < t > ] + b c ) \tilde{c}^{<t>}=\sigma(W_c[\Gamma_r c^{<t-1>},x^{<t>}]+b_c) c~<t>=σ(Wc[Γrc<t1>,x<t>]+bc)
    完全版:这里加入r门表示相关性relative。这个r有时候也被叫做reset门,如果r是1,那么相当于基本循环神经网络,如果r是0,那么候选c就完全不受过往状态影响。
  3. Γ r = s i g m o i d ( W r [ c < t − 1 > , x < t > ] + b r ) \Gamma_r=sigmoid(W_r[c^{<t-1>},x^{<t>}]+b_r) Γr=sigmoid(Wr[c<t1>,x<t>]+br)
  4. Γ u = s i g m o i d ( W u [ c < t − 1 > , x < t > ] + b u ) \Gamma_u=sigmoid(W_u[c^{<t-1>},x^{<t>}]+b_u) Γu=sigmoid(Wu[c<t1>,x<t>]+bu)
    门控符号也是类似于隐变量的生成方式,但是是基于上一个c生成的,最后基本就是接近于0或者1
  5. c < t > = Γ u × c ~ < t > + ( 1 − Γ u ) × c < t − 1 > c^{<t>}=\Gamma_u\times\tilde{c}^{<t>}+(1-\Gamma_u)\times c^{<t-1>} c<t>=Γu×c~<t>+(1Γu)×c<t1>
    类似于交叉熵的写法,通过门控符号决定当前层的c采用新计算出来的c还是维持上一个c不变
  6. a < t > = c < t > a^{<t>}=c^{<t>} a<t>=c<t>
LSTM(long short term memory)

Hochreiter, Sepp, and Jürgen Schmidhuber. “Long short-term memory.” Neural computation 9.8 (1997): 1735-1780.

这篇论文比较难,深入的讲解了gradient vanishing的原理。

公式:

  1. c ~ < t > = t a n h ( W c [ a < t − 1 > , x < t > ] + b c ) \tilde{c}^{<t>}=tanh(W_c[a^{<t-1>},x^{<t>}]+b_c) c~<t>=tanh(Wc[a<t1>,x<t>]+bc)
    相比于GRU,tilde-c由a,x,b决定,直观上不受上一个c影响,更加合理。有一些变体会显式地增添上一个c的影响(peephole connection)
  2. Γ u = σ ( W u [ a < t − 1 > , x < t > ] + b u ) \Gamma_u=\sigma(W_u[a^{<t-1>},x^{<t>}]+b_u) Γu=σ(Wu[a<t1>,x<t>]+bu)
    控制update,相当于记忆
  3. Γ f = σ ( W f [ a < t − 1 > , x < t > ] + b f ) \Gamma_f=\sigma(W_f[a^{<t-1>},x^{<t>}]+b_f) Γf=σ(Wf[a<t1>,x<t>]+bf)
    控制forget,相当于遗忘(跳过当前步)
  4. Γ o = σ ( W o [ a < t − 1 > , x < t > ] + b o ) \Gamma_o=\sigma(W_o[a^{<t-1>},x^{<t>}]+b_o) Γo=σ(Wo[a<t1>,x<t>]+bo)
    控制output,控制c到a的转化
  5. c < t > = Γ u × c ~ < t > + Γ f × c < t − 1 > c^{<t>}=\Gamma_u\times\tilde{c}^{<t>}+\Gamma_f\times c^{<t-1>} c<t>=Γu×c~<t>+Γf×c<t1>
    长短期记忆的线性组合
  6. a < t > = Γ o × c < t > a^{<t>}=\Gamma_o\times c^{<t>} a<t>=Γo×c<t>
    这一步实现了c到a的转化

我个人觉得LSTM更加合理,首先是a和输入共同转化为门,tilde-c,进一步变成c,最后变成a,一个循环走下来,实际上所有的量本质上还是由a,x决定的,这和经典RNN的本质是相同的。

同时,形式上看c和a几乎是平行的,c用来储存记忆,而是否储存记忆,是由a和x决定的,而a代表着曾经的信息,这个因果决定关系很合理,即通过从开始到现在的所有信息决定更新和遗忘的比例,进而影响a。

LSTM和GRU对比:

说实话,相比于GRU,我更喜欢LSTM,并且LSTM更早提出,更加直观,更加灵活,但是灵活的代价就是成本高,因为有三个门。

GRU只有两个门,所以成本更低,结构简单,很多人逐渐开始测试这种方式。

NLP and Word Embeddings(词嵌入与自然语言处理)

基本概念

word representation(词汇表征)

Van der Maaten L, Hinton G. Visualizing data using t-SNE[J]. Journal of machine learning research, 2008, 9(11).

平常我们写句子的时候,同义词总是可以互相替换,但是在基于频率的one-hot编码下,即使是同义词,也可能相距甚远,不能相互替换。

这个时候,就需要一种更好的编码方式,试想能否采用多种特征去描述一个词,构成一个特征向量,近义词的特征向量比较相似,这样就可以互相替换。

这就是词嵌入的理念,向量之间的相似性类似于数值的相似性,其实在最开始的线性网络中,数值的相似性就已经是隐含的了,只要输入的数值接近,结果就会接近。

之所以叫词嵌入,是因为可以将n维的特征向量放到一个n维空间里,每一个向量对应于一个点,相当于把一个点嵌入空间中。

理论上,相似的向量总是会聚在一起,就像聚类一样。词嵌入好处很多,比如信息储存量很大,比如存10000类,只需要300维的向量,相比起来,one-hot就得10000维,这样的话,词嵌入就可以有巨大的词汇量。而且,这种词嵌入是有扩展性的,新进来的类别不会影响维度,所以可以不断扩充。

为了直观查看词嵌入的效果,需要可视化,但是n维空间无法可视化,所以需要降维,这种降维算法就叫做t-SNE,作者是大名鼎鼎的Hinton。

二维化以后,就可以直观地看出词嵌入的分布了。

词嵌入是NLP领域中很重要的概念,但是思想却很朴实,所以开创性的成就不一定复杂。

using word embeddings(使用词嵌入)

词嵌入模型只是一个编码模型,和训练模型是解耦的,所以获取词嵌入模型有两种方式:

  1. 自己训练,这需要大量的语料库。至于怎么训练,那是后话了。训练的过程,我现在感觉就像是无监督的聚类。
  2. 直接下载别人的

用已经预训练好的词嵌入模型,迁移到另一个小任务中去训练。这一个训练过程,相当于在已经构建好的空间中,再次嵌入一些新的向量。

为什么是小任务呢?因为新样本的训练无疑会影响原来的向量空间,样本太多就会破坏掉原来的模型,这就和重新训练一个没太大区别了。

最后就是选择性的微调一下。

词嵌入的特性:相似性计算

词嵌入可以用于找同义词,比如男人对女人,我就可以通过国王对应到王后。

设三个embedding后的向量 e m a n , e w o m a n , e k i n g , e q u e e n e_{man},e_{woman},e_{king},e_{queen} eman,ewoman,eking,equeen

从向量角度理解, e m a n − e w o m a n ≈ e k i n g − e q u e e n e_{man}-e_{woman}\approx e_{king}-e_{queen} emanewomanekingequeen

假设我们要找出这个 e q u e e n e_{queen} equeen,我们就需要找到满足这个条件的变量: arg max ⁡ s i m ( e w , e k i n g − e m a n + e w o m a n ) \argmax sim(e_{w},e_{king}-e_{man}+e_{woman}) argmaxsim(ew,ekingeman+ewoman)

s i m ( u , v ) = u T v ∣ ∣ u ∣ ∣ 2 ∣ ∣ v ∣ ∣ 2 sim(u,v)=\dfrac{u^Tv}{||u||_2||v||_2} sim(u,v)=∣∣u2∣∣v2uTv

这个sim函数就叫cosine similarity,用于衡量向量相似性,当向量同向,sim=1,垂直就是sim=0,相反就是-1。

另一种计算方式是计算不相似性,这个就是经典的欧氏距离。

通过这种方式,就可以实现基于embedding的类比推理。

训练方法详解

词嵌入矩阵

词嵌入矩阵 E = [ e 1 , e 2 , ⋯   , e n ] E=[e_1,e_2,\cdots,e_n] E=[e1,e2,,en]
里面的每一列都是embedding后的向量。

如何从词嵌入矩阵中提取一个向量呢,有 E ⋅ o j = e j E\cdot o_j=e_j Eoj=ej,因为one-hot的特殊性,故有上面的公式,但是实际要取一个,直接用one-hot中值为1的索引对E切片就好了。

词嵌入学习

Bengio, Yoshua, Réjean Ducharme, and Pascal Vincent. “A neural probabilistic language model.” Advances in neural information processing systems 13 (2000).

建立一个语言模型可以用于学习词嵌入。

思路就是在第一层就用embedding层,之后加入语言模型,感觉有点卷积层的意思了,卷积层就是先提取特征,而我认为embedding就是词元的特征。

由于将E矩阵也在bp的范围内,所以E矩阵会朝着最大化提取信息的方向训练,最终目的是让准确率提高。

另一种理解方式,假设我们给出orange juice,apple juice,purple juice,三个水果都预测出一个juice,为了得到同一个结果,bp训练强迫E矩阵对三种水果形成相近的embeddings。

这一过程听起来比较玄学,效果和原理有待考证。

因为这里没有用到RNN,所以我们可以用多种方式选择参考的输入数据

  1. 利用n时间窗口,以预测位置的前n个词元作为输入
  2. 前后各取m,n个单词,有点完形填空的味道
  3. 取紧挨着的一个单词

Word2Vec算法

Mikolov, Tomas, et al. “Efficient estimation of word representations in vector space.” arXiv preprint arXiv:1301.3781 (2013).

Word2Vec是一种学习词嵌入的算法。

(context,target)配对,作为监督学习。

skip-grams

通过中心词排推断上下文一定窗口内的单词。这个听起来就很难,但是我们也不是要去解决这个问题,而是要顺带学了词嵌入

  1. 随机选择中心词,标记为context。实际上,并不是随机,有更多的启发式算法。
  2. 以中心词周边skip-window范围内,随机选择Target词配对。
  3. 训练链条: o c → E → e c → s o f t m a x → y ^ o_c\rightarrow E\rightarrow e_c\rightarrow softmax \rightarrow \hat{y} ocEecsoftmaxy^
  4. 其中,o和y都是one-hot向量,o是context,y是target
  5. 其中soft-max层,首先每一个神经元要用一个参数 θ j \theta_j θj e c e_c ec点乘,形成一个标量,如此用n个soft-max神经元产生n个结果,之后指数化+归一化,得到概率值。假如n=10000,则公式可以写作: p ( t ∣ c ) = e θ t T e c ∑ j = 1 10000 e θ j T e c p(t|c)=\dfrac{e^{\theta_t^Te_c}}{\sum_{j=1}^{10000}e^{\theta^T_je_c}} p(tc)=j=110000eθjTeceθtTec,公式中每一个t都代表一类,总共10000类
  6. 因为soft-max层要计算的太多了(nlp领域中one-hot编码太长了导致n特别大),所以为了加速,有时候会采用二分分级(hierarchical)分类的方式,而且二叉树有时候会按照赫夫曼法则去构建,让高频词更容易出现。另一种方法是后面的负采样。
负采样

Mikolov, Tomas, et al. “Distributed representations of words and phrases and their compositionality.” Advances in neural information processing systems 26 (2013).

首先构造样本,样本由1+k个样本组成:

  1. 给定context为中心词,保持不变
  2. 正采样:从窗口中取一个词作为word,标记target为1,寓意是word与context有关
  3. 负采样:从vocabulary中取k个次作为word,生成k个样本,所有target标记都是0,代表word与context无关,即使是word碰巧出现在了context的窗口内

规定问题:训练一个分类器,辨别context和word是正采样获得的还是负采样获得的

简化后的问题公式就变成了: P ( y = 1 ∣ c , t ) = σ ( θ t T e c ) P(y=1|c,t)=\sigma(\theta_t^Te_c) P(y=1∣c,t)=σ(θtTec),对一个context,我们理论上需要训练n个target二元分类器。

实际上,因为我们只采样了1+k个样本,所以只需要针对这1+k个分类器训练就可以。

直观理解训练原理:

  1. 一个绕不开的点就是,为什么需要负采样,单一个正采样不可以吗?我们可以理解为抑制。如果只有正采样,那么正采样以外的分类器都只是随机的,可以低也可以高,加了负采样,负采样部分就会被抑制,向着0训练。
  2. 对于一个分类器说,训练好以后,就可以明辨context-word对是正采样还是负采样来的,那如果是正采样,就意味着单词之间至少都是属于一句话的。
  3. 进一步讲,如果同一个context对两个不同的word都能得出同样的结果,那么可见这两个word的embedding也是类似的,这可以作为一个直观解释
CBOW(continuous bag of words)

通过上下文推断中心词。和skip-gram基本是相反的。

GloVe词向量算法

Pennington J, Socher R, Manning C D. Glove: Global vectors for word representation[C]//Proceedings of the 2014 conference on empirical methods in natural language processing (EMNLP). 2014: 1532-1543.

GloVe通过算法计算出单词之间的关联度,然后根据关联度进行embedding。

x i j = x c t = t a r g e t 出现在 c o n t e n t 附近的次数 x_{ij}=x_{ct}=target出现在content附近的次数 xij=xct=target出现在content附近的次数

如果窗口是对称的,比如左右5个单词的范围,那么很明显 x i j = x j i x_{ij}=x_{ji} xij=xji

训练算法: m i n ∑ i = 1 10000 ∑ j = 1 10000 ( θ i T e j − l o g X i j ) 2 min\sum_{i=1}^{10000}\sum_{j=1}^{10000}(\theta_i^Te_j-logX_{ij})^2 mini=110000j=110000(θiTejlogXij)2

训练的最终目的本质上还是soft-max,如果ij的关联大,则logX就大,要将算法训练到最小,那么 θ i T e j \theta_i^Te_j θiTej就要尽量大(匹配logX),思考一下soft-max里面的公式,这个就决定了该分类概率的大小。

换言之,这个模型和soft-max的本质一样,X越大,对应于soft-max中概率就越大。

现在有一个问题如果X=0,那么log是无限小的,所以需要调节一下,不要让结果太小,同时,有很多像the,of之类出现频率太高的词也不应该让结果太大,所以就要加一个系数

m i n ∑ i = 1 10000 ∑ j = 1 10000 f ( X i j ) ( θ i T e j − l o g X i j ) 2 min\sum_{i=1}^{10000}\sum_{j=1}^{10000}f(X_{ij})(\theta_i^Te_j-logX_{ij})^2 mini=110000j=110000f(Xij)(θiTejlogXij)2

当X=0,f(X)=0,其他情况根据算法具体而定,总之是一项启发性的东西。

最后增加的这两项,目前意义不明

m i n ∑ i = 1 10000 ∑ j = 1 10000 f ( X i j ) ( θ i T e j + b i + b j ′ − l o g X i j ) 2 min\sum_{i=1}^{10000}\sum_{j=1}^{10000}f(X_{ij})(\theta_i^Te_j+b_i+b'_j-logX_{ij})^2 mini=110000j=110000f(Xij)(θiTej+bi+bjlogXij)2

最后的最后, e w f i n a l = e w + θ w 2 e_w^{final}=\dfrac{e_w+\theta_w}{2} ewfinal=2ew+θw,之所以采取平均,是因为这个和soft-max还是有不同,这里的 θ , e \theta,e θe起到的作用类似,有对称性,所以求平均。

补充

情感分类

这属于nlp的一个综合应用。

o n e − h o t → e m b e d d i n g s   l a y e r → e → R N N   l a y e r → s o f t − m a x   l a y e r → r e s u l t one-hot\rightarrow embeddings \ layer \rightarrow e \rightarrow RNN \ layer \rightarrow soft-max \ layer \rightarrow result onehotembeddings layereRNN layersoftmax layerresult

embeddings的E矩阵从另一个地方来,总之是训练好的嵌入矩阵。

然后送进RNN模型,到达模型底部后,利用最后一个隐变量,进行soft-max分类,得到结果。

词嵌入除偏

其实机器学习比较诚实,喂给什么,就只会什么,就一定会什么。

如果喂不好的东西,比如涉及到各种歧视的语句,那么输出就不太好,所以要除偏。

多对多模型(Seq to Seq Models)

多对多模型是真正的应用阶段

基础模型

Sutskever I, Vinyals O, Le Q V. Sequence to sequence learning with neural networks[J]. Advances in neural information processing systems, 2014, 27.

Cho K, Van Merriënboer B, Gulcehre C, et al. Learning phrase representations using RNN encoder-decoder for statistical machine translation[J]. arXiv preprint arXiv:1406.1078, 2014.

其实在前面RNN变体中已经说过了,就是encoder-decoder模型,在这里重申一下。

比如一个机器翻译任务,首先把原文输入到encoder,经过循环,最后一步输出一个隐变量a,用这个a作为decoder的第一个a,decoder的第一个x可以选0,此后每一层循环用的x,都直接用上一层循环的输出y,直到输出整句。

Mao J, Xu W, Yang Y, et al. Deep captioning with multimodal recurrent neural networks (m-rnn)[J]. arXiv preprint arXiv:1412.6632, 2014.

Vinyals O, Toshev A, Bengio S, et al. Show and tell: A neural image caption generator[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2015: 3156-3164.

Karpathy A, Fei-Fei L. Deep visual-semantic alignments for generating image descriptions[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2015: 3128-3137.

又比如图片解释任务,encoder不一定要从文本中提取,还可以从图片中提取,比如用AlexNet(去掉soft-max层)作为encoder,这样就可以得到一个长度为n的特征向量,把这个向量丢进decoder就可以得到输出。

实际上,Machine translation的encoder-decoder结构中的decoder,就是一个语言模型,且是一个一对多的序列生成语言模型。

抽象一点,就是条件语言模型。

但是问题在于,输出的是一系列soft-max向量,如何选择最佳的句子成为问题。

一种简单的思路就是每个词只取最大概率的,另一种简单的思路是直接按照概率进行采样。

但是这两种都不是很靠谱,第一种可能有误,第二种基于概率就更不靠谱了。

更大的问题是,你只有确定t-1位置的输出概率分布,才能确定t位置的输出概率分布,所以你想要直接得出概率最大的句子,不太容易。进一步说,搜索空间有 1000 0 T 10000^T 10000T这么大,根本搜不完,所以这里反而回归了最优化理论。

arg max ⁡ y < 1 > , ⋯   , y < T > P ( y < 1 > , ⋯   , y < T > ∣ X ) \mathclap{\argmax_{y^{<1>},\cdots,y^{<T>}}P(y^{<1>},\cdots,y^{<T>}|X)} y<1>,,y<T>argmaxP(y<1>,,y<T>X)

比如,假设第一层有AB两个选项,第二层有CD两个选项,第一个词P(A)=0.6,P(B)=0.4,很多人就会选A,但是紧接着第一层选了A以后,P(C|A)=0.5,P(D|A)=0.5,P(C|B)=1,P(D|B)=0.

如果你想当然的选择了A,那么最终结果就是0.6*0.5=0.3,但是如果你先选了B,那么最终结果就可以是0.4*1=0.4,看起来吃亏结果反而概率还更大。

以上的选择思路叫贪心,每次选择最大概率的,这样的问题在于容易陷入局部最优,贪心AC不如不贪心的BC就说明了这种方法的缺陷。

优化算法

Beam Search基本思路

首先确定搜索宽度Beam Width,这里以3举例。

对于 y < 1 > y^{<1>} y<1>,我们从中取三个概率最大的作为束的状态。

下面的符号,注意区分 y < t > 和 y ^ < t > y^{<t>}和\hat{y}^{<t>} y<t>y^<t>,虽然前者代表通过输出选择出来的one-hot结果,后者代表一个soft-max向量,是输出本身(此处有待考证,仅仅是我的个人猜想,不排除直接把soft-max向量喂到下一个输入的可能)

此后,分别以这三个状态作为 y < 1 > y^{<1>} y<1>,分别去计算三个 y ^ < 2 > \hat{y}^{<2>} y^<2>,由此,这个就代表 P ( y < 2 > ∣ x , y < 1 > ) P(y^{<2>}|x,y^{<1>}) P(y<2>x,y<1>),进一步可以计算出 P ( y < 1 > , y < 2 > ∣ x ) = P ( y < 1 > ∣ x ) P ( y < 2 > ∣ x , y < 1 > ) P(y^{<1>},y^{<2>}|x)=P(y^{<1>}|x)P(y^{<2>}|x,y^{<1>}) P(y<1>,y<2>x)=P(y<1>x)P(y<2>x,y<1>),之后,选择 P ( y < 1 > , y < 2 > ∣ x ) P(y^{<1>},y^{<2>}|x) P(y<1>,y<2>x)最大的三个 y < 1 > , y < 2 > y^{<1>},y^{<2>} y<1>,y<2>组合作为新的束状态,继续进行递归的计算。

由此可见,这里的局部束搜索仍然和最优化理论中的局部束搜索一样,只不过选择最优束不是通过当前步概率,而是通过联合条件分布 P ( y < 1 > , y < 2 > ∣ x ) P(y^{<1>},y^{<2>}|x) P(y<1>,y<2>x)来选择。

局部束的开销并不大,束宽为n,则只需要同时维持n个RNN即可。

如果n=1,则局部束搜索退化成贪心搜索。

改进的Beam Search

首先是概率累乘造成的数值下溢问题。

P ( y < 1 > , ⋯   , y < T > ∣ x ) = arg max ⁡ y ∏ t = 1 T y P ( y < t > ∣ x , y < 1 > , ⋯   , y < t − 1 > ) P(y^{<1>}, \cdots,y^{<T>}|x)=\\ \argmax_y\prod_{t=1}^{T_y}P(y^{<t>}|x,y^{<1>},\cdots,y^{<t-1>}) P(y<1>,,y<T>x)=yargmaxt=1TyP(y<t>x,y<1>,,y<t1>)

既然是比大小,那可以施加一个单调函数,这样也不会影响到最后结果,所以进行log处理,类似于对数最大似然的处理方式,可以将趋于0的值转化为负数之和,有效解决下溢

P ( y < 1 > , ⋯   , y < T > ∣ x ) = arg max ⁡ y ∑ t = 1 T y log ⁡ P ( y < t > ∣ x , y < 1 > , ⋯   , y < t − 1 > ) P(y^{<1>}, \cdots,y^{<T>}|x)=\\ \argmax_y\sum_{t=1}^{T_y}\log P(y^{<t>}|x,y^{<1>},\cdots,y^{<t-1>}) P(y<1>,,y<T>x)=yargmaxt=1TylogP(y<t>x,y<1>,,y<t1>)

前面加了单调函数相当于做了个等效变换,所以还是可以按照累乘分析。

这就引出另一个潜在问题,因为输出的长度不是固定的(直到有EOS才会停止,这也是递归的特征),而每一项概率都是小于0的,所以算法会倾向于选择更短的序列,因为少乘1项,结果肯定概率高。如果从对数角度理解,结果也是一样。

这个时候,只需要对长度进行一个修正即可,自然引出归一化。最简单的归一化就是平均,但是还应该考虑到,有时候简单的翻译确实更好,所以最好取个折中,可以继续叠参数,就是加一个指数 α \alpha α,如果指数为1,就是完全平均归一化,如果指数为0,就是完全不归一化,这个指数可以作为超参数调节。

P ( y < 1 > , ⋯   , y < T > ∣ x ) = arg max ⁡ y 1 T y α ∑ t = 1 T y log ⁡ P ( y < t > ∣ x , y < 1 > , ⋯   , y < t − 1 > ) P(y^{<1>}, \cdots,y^{<T>}|x)=\\ \argmax_y\dfrac{1}{T_y^\alpha}\sum_{t=1}^{T_y}\log P(y^{<t>}|x,y^{<1>},\cdots,y^{<t-1>}) P(y<1>,,y<T>x)=yargmaxTyα1t=1TylogP(y<t>x,y<1>,,y<t1>)

局部束搜索的误差分析

假设结果不太好,那么有两种可能:

  1. RNN出了问题
  2. Beam Search出了问题

如何以最小的成本定位这两个问题是一个新的问题,毕竟有的大模型跑一次就得几千块。

先针对一个错误样本做判断。

假设 y ∗ y^* y是人工结果(绝对正确), y ^ \hat{y} y^是输出的结果。首先计算 P ( y ∗ ∣ x ) , P ( y ^ ∣ x ) P(y^*|x),P(\hat{y}|x) P(yx),P(y^x),然后比较大小,生成两种情况。

  1. P ( y ∗ ∣ x ) > P ( y ^ ∣ x ) P(y^*|x)>P(\hat{y}|x) P(yx)>P(y^x)
    理论上Beam Search应该找出最大概率的组合,但是却存在比结果更大更好的组合,说明Beam Search没找好,有优化的空间,比如增大宽度
  2. P ( y ∗ ∣ x ) < P ( y ^ ∣ x ) P(y^*|x)<P(\hat{y}|x) P(yx)<P(y^x)
    局部束搜索的结果是基于现有RNN架构上,最好的结果,但是很明显结果并不好,所以是RNN这个基础出了问题,指标就没给好,即使是遍历所有可能也无法找出实际上的最优解

很显然,考虑到局部束搜索并不是遍历,存在运气因素,所以一个样本并不能决定整个模型的错误,我们需要找出所有错误样本,判断每个样本错在哪,最后给整个模型的错误定性,比如Beam Search出问题的比例是90%,那大概率就是Beam Search出bug了。

Bleu得分(选修)

如果同时存在很好的翻译,那么该如何在这些翻译中选出最好的呢?

Bleu就是要解决这个问题,在我心目中,最好的翻译应当是信达雅,但是我不是搞这个领域的,就略过了Bleu了。

注意力模型

直观理解

Bahdanau D, Cho K, Bengio Y. Neural machine translation by jointly learning to align and translate[J]. arXiv preprint arXiv:1409.0473, 2014.

前面说到的Bleu评分用于给翻译打分,但是Bleu的对太短与太长的句子表现都不好,尤其是超过20长度以后准确率会快速下降,这是因为一次性翻译长句子本身就很难。

现实中,人翻译长句子都是一截一截翻译的,有了这种机制,就可以保证翻译长句子时的准确率。

具体到架构,前面的encoder没有太多改变,主要是decoder时,增加了一个注意力矩阵。对每一个decoder输出,都要同时参考所有encoder的隐变量,参考的权重就是注意力权重,写作 α < t , t ′ > \alpha^{<t,t^\prime>} α<t,t>,其中, t t t是decoder输出的位置, t ′ t^\prime t是参考的encoder位置,后面这两种记法会很常用。

更多的细节将会在后面解释。

注意力模型详解

Xu K, Ba J, Kiros R, et al. Show, attend and tell: Neural image caption generation with visual attention[C]//International conference on machine learning. PMLR, 2015: 2048-2057.

在这里插入图片描述

首先是encoder,实际使用中,encoder通常都是双向RNN,可能使用GRU和LSTM模型。

这样,每一个encoder位置有一正一反两个隐变量。两个隐变量构成这一步的特征向量,写作 a < t ′ > a^{<t^\prime>} a<t>

在decoder阶段,用 S < t > S^{<t>} S<t>表示隐变量,这里与前面的架构最为不同。前面的是一对多,用前一个输出作为后一个输入,但是这里还要增加注意力机制,使用 c < t > c^{<t>} c<t>作为上下文的输入,decoder实际上已经变成了n对n的类似于词性鉴别的架构了

我对架构有两个不确定的疑问

  1. S < 0 > S^{<0>} S<0>是用零向量还是用encoder的隐变量,如果用隐变量,现在的双向RNN已经没有最后的输出了,所以我更倾向于是零向量
  2. 每一步的输入除了 c < t > c^{<t>} c<t>貌似还有 y < t − 1 > y^{<t-1>} y<t1>,我并不确定。

现在的架构已经改变,encoder部分是双向RNN,只负责提供隐变量,这些隐变量由注意力矩阵进行采集,构成上下文变量c,供给给decoder部分作为输入,逐层输出。

接下来自上而下说明如何计算c

  1. c < t > = ∑ t ′ α < t , t ′ > a < t ′ > c^{<t>}=\sum\limits_{t^\prime}\alpha^{<t,t^\prime>}a^{<t^\prime>} c<t>=tα<t,t>a<t>
    c是a的加权平均,权重就是注意力矩阵中的一行
  2. ∑ t ′ α < t , t ′ > = 1 \sum\limits_{t^\prime}\alpha^{<t,t^\prime>}=1 tα<t,t>=1
    很明显,作为权重,必然是要归一化的。
  3. α < t , t ′ > = s o f t m a x t ′ ( e < t , t ′ > ) \alpha^{<t,t^\prime>}=\mathop{softmax}\limits_{t^\prime}(e^{<t,t^\prime>}) α<t,t>=tsoftmax(e<t,t>)
    本层的 α \alpha α通过本层的e归一化得来
  4. e < t , t ′ > = f ( s < t − 1 > , a < t ′ > ) e^{<t,t^\prime>}=f(s^{<t-1>},a^{<t^\prime>}) e<t,t>=f(s<t1>,a<t>)
    这个f是一层神经网络,需要加入架构中用梯度下降训练。有趣的是,a的权重居然要通过a来计算,但是仔细一想又很合理,注意力自然要从词本身出发,另一个参数S可以理解为,翻译一句话除了从原来句中参考,也可以从已经翻译出来的部分参考。

这就是全部算法,至于具体的维度细节,就没有细究了,这个算法的缺陷在于时间复杂度是 O ( T x , T y ) O(T_x,T_y) O(Tx,Ty),因为实际上是要构建一个注意力矩阵。好在句子一般不长,30个词顶天了,所以复杂度还可以容忍。

这个网络训练下来,会收获一个优秀的注意力权重,以及一个好的计算e的参数。

对输入信息选择性的加权,这种注意力思想可以放到各种领域中,比如图片相关,这也和人的视觉机制相似,人的眼睛视野很大,但是你只会注意直视部分,而不是余光。

至于可解释性,貌似已经有注意力机制可视化做出来了,感觉也不会很难,毕竟权重就摆在那里。

speech recognition

基本知识

语音识别是seq2seq应用的另一个领域,视频识别也是类似。

同图片的卷积,文本的embeddings,声音也需要预处理:将波形图转化为声谱图(spectrogram),横轴是时间,纵轴是频率,某一个点的颜色代表某时间某频率的能量(响度)大小,实际上这种方式也是一种仿生处理。

我感觉,频率相当于embeddings的特征维度,那这个声谱图可以理解为类似embeddings的东西,变成声谱图以后,类似的发音就会有类似的特征向量。

现在的语音识别系统都是end-to-end的,不需要手工预处理数据,以前都是先手工把音频转化为phonemes(音位),比如quick转化为kwik,类似于提取音标。

在语音识别领域,输入的维度要远远大于输出维度,因为采样率如果是100,那一秒钟就有100个帧,但是你一秒钟可能就只说了一个字。

注意力模型

这个就很经典,和翻译一模一样,但是输出是26个英文字母(我感觉用词也可以)

这里可见注意力模型的厉害了,如果没有注意力模型,那么长的输入,效果估计不会太好。

CTC模型

Graves A, Fernández S, Gomez F, et al. Connectionist temporal classification: labelling unsegmented sequence data with recurrent neural networks[C]//Proceedings of the 23rd international conference on Machine learning. 2006: 369-376.

这个模型是n对n的等长模型。

比如一个输出可能是“ttt_h_eee___<空格>__qqq__u__ii___ccc_kkk

对应的识别就是一个“the quick”,

原理在于,下划线是一种分隔符,用于分割连续重复的字母,这样就可以融合逐帧识别产生的冗余数据了。

Trigger word detection(触发字检测)

小爱同学之类的设备,你要唤醒就得使用特定的词,这个词就叫触发词。

这是一个新型领域,所以并没有成熟算法。这里只有一个比较好的例子。

大致思路就是把输入扔进语音识别系统中,输出不是识别出来的语句,而是0和1,如果出现触发词(从头到尾说完一个词),就在结尾输出1,否则就是0.

细节没讲。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亦梦亦醒乐逍遥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值