Recurrent Neural Network
这个问题可以利用一个前馈神经网络(feedforward neural network)来解,如图5.2 所示,
输入是一个单词,把“上海”变成一个向量,“丢”到这个神经网络里面。输入是一个单词,把“上海”变成一个向量,“丢”到这个神经网络里面。要把一个单词丢到一个神经网络里面去,就必须把它变成一个向量。
以下是把单词用向量来表示的方法。
独热编码
假设词典中有5 个单词:apple,bag,cat,dog,elephant,如式(5.1)。向量的巨细是词典巨细。每一个维度对应词典的一个单词。对应单词的维度为1,其他为0。
如果只是用独热编码来描述一个单词,会有一些问题:由于许多单词可能都没有见过,所以需要在独热编码里面多加维度,用一个维度代表other,如图5.3(a) 所示。如果不是在词表中,有的单词就归类到other 里面去(Pig,Cow 归类到other 里面去)。我们可以用每一个单词的字母来表示它的向量,好比单词是apple,apple 里面有出现app、ple、ppl,在这个向量里面对应到app、ple、ppl 的维度就是1,其他都为0,如图5.3(b) 所示。
假设把单词表示为向量,把这个向量丢到前馈神经网络里面去,在该任务里面,输出是一个概率分布,该概率分布代表着输入单词属于每一个槽的概率,好比“上海”属于目的地的概率和“上海”属于出发地的概率,如图5.4 所示。但是前馈网络会有问题,如图5.5 所示,假设用户1 说:“在6 月1 号抵达上海”。用户2 说:“在6 月1 号离开上海”,这时间“上海”就变成了出发地。但是对于神经网络,输入一样的东西,输出就应该是一样的东西。在例子中,输入“上海”,输出要么让目的地概率最高,要么让出发地概率最高。不能一会让出发地的概率最高,一会让目的地概率最高。在这种环境下,如果神经网络有影象力的,它记得它看过“抵达”,在看到“上海”之前;大概它记得它已经看过“离开”,在看到“上海”之前。通过影象力,它可以根据上下文产生差别的输出。如果让神经网络是有影象力,其就可以解决输入差别的单词,输出差别的问题。
什么是RNN?
在RNN 里面,每一次埋伏层的神经元产生输出的时间,该输出会被存到影象元(memory cell),图5.6(a) 中的蓝色方块表示 影象元。下一次有输入时,这些神经元不仅会考虑输入x1, x2,还会考虑存到影象元里的值。除了x1, x2,存在影象元里的值a1, a2 也会影响神经网络的输出。
影象元可简称为单元(cell),影象元的值也可称为隐状态(hidden state)。
举个例子,假设图5.6(b) 中的神经网络全部的权重都是1,全部的神经元没有任何的偏置(bias)。为了便于盘算,假设全部的激活函数都是线性的,输入是序列[1, 1]T, [1, 1]T, [2, 2]T, · · · ,全部的权重都是1。起首设置影象元的初始值都为0,接着输入第一个[1, 1]T,对于左边的神经元(第一个埋伏层),其除了接到输入的[1, 1]T,还接到了影象元(0 跟0),输出就是2。同理,右边神经元的输出为2,第二层埋伏层输出为4。
接下来循环神经网络会将绿色神经元的输出存在影象元里去,所以影象元里面的值被更新为2。如图5.6© 所示,接下来再输入[1, 1]T,接下来绿色的神经元输入为[1, 1]T、[2, 2]T,输出为[6, 6]T,第二层的神经元输出为[12, 12]T。所以由于循环神经网络有影象元,就算输入相同,输出也可能不一样。如图5.6(d) 所示,[6, 6]T 存到影象元里去,接下来输入是[2, 2]T,输出为[16, 16]T;第二层埋伏层为[32, 32]T。在做循环神经网络时,它会考虑序列的次序,输入序列调换次序之后输出差别。
由于当前时刻的隐状态利用与上一时刻隐状态相同的定义,所以隐状态的盘算是循环的(recurrent),基于循环盘算的隐状态神经网络被称为循环神经网络。
RNN架构
利用循环神经网络处理槽添补这件事,如图5.7 所示。用户说:“我想在6 月1 日抵达上海”,“抵达”就变成了一个向量“丢”到神经网络里面去,神经网络的埋伏层的输出为向量a1,a1产生“抵达”属于每一个槽添补的概率y1。接下来a1 会被存到影象元里面去,“上海”会变为输入,这个埋伏层会同时考虑“上海”这个输入和存在影象元里面的a1,得到a2。根据a2 得到y2,y2 是属于每一个槽添补的概率。
有了影象元以后,输入同一个单词,希望输出差别的问题就有可能被解决。如图5.8 所示,同样是输入“上海”这个单词,但是由于赤色“上海”前接了“离开”,绿色“上海”前接了“抵达”,“离开”和“抵达”的向量不一样,埋伏层的输出会差别,所以存在影象元里面的值会差别。固然x2的值是一样的,由于存在影象元里面的值差别,所以埋伏层的输出会差别,所以最后的输出也就会不一样。
其他RNN
循环神经网络的架构是可以恣意设计的,之前提到的RNN 只有一个埋伏层,但RNN 也可以是深层的。好比把xt 丢进去之后,它可以通过一个埋伏层,再通过第二个埋伏层,以此类推(通过许多的埋伏层) 才得到最后的输出。每一个埋伏层的输出都会被存在影象元里面,在下一个时间点的时间,每一个埋伏层会把前一个时间点存的值再读出来,以此类推最后得到输出,这个过程会一直持续下去。
Elman 网络&Jordan 网络
循环神经网络会有差别的变形,如图5.10 所示,刚才讲的是简朴循环网络(Simple Recurrent Network,SRN),即把埋伏层的值存起来,在下一个时间点在读出来。简朴循环网络也称为Elman 网络。
尚有别的一种叫做Jordan 网络,Jordan 网络存的是整个网络输出的值,它把输出值在下一个时间点在读进来,把输出存到影象元里。Elman 网络没有目的,很难控制说它能学到什么埋伏层信息(学到什么放到影象元里),但是Jordan 网络是有目的,比较很清晰影象元存储的东西。
双向循环神经网络
循环神经网络还可以是双向。刚才RNN 输入一个句子,它就是从句首一直读到句尾。如图5.11 所示,假设句子里的每一个单词用xt 表示,其是先读xt,再读xt+1、xt+2。但其读取方向也可以是反过来的,它可以先读xt+2,再读xt+1、xt。我们可以同时训练一个正向的循环神经网络,又可以训练一个逆向的循环神经网络,然后把这两个循环神经网络的埋伏层拿出来,都接给一个输出层得到最后的yt。所以把正向的网络在输入xt 的时间跟逆向的网络在输入xt 时,都丢到输出层产生yt,产生yt+1, yt+2,以此类推。双向循环神经网络(Bidirectional Recurrent Neural Network,Bi-RNN)的好处是,神经元产生输出的时间,它看的范围是比较广的。如果只有正向的网络,再产生yt、yt+1 的时间,神经元只看过x1 到xt+1 的输入。但双向循环神经网络产生yt+1 的时间,网络不只是看过x1, 到xt+1 全部的输入,它也看了从句尾到xt+1 的输入。网络就等于整个输入的序列。假设考虑的是槽添补,网络就等于看了整个句子后,才决定每一个单词的槽,这样会比看句子的一半还要得到更好的性能。
长短期影象网络LSTM
之前提到的影象元是最单纯的,可以随时把值存到影象元去,也可以把值读出来。但最常用的影象元是长短期影象网络(Long Short-Term Memory network,LSTM),长时间的短期影象。LSTM 是比较复杂的。LSTM 有三个门(gate),当外界某个神经元的输出想要被写到影象元里面的时间,必须通过一个输入门(input gate),输入门要被打开的时间,才能把值写到影象元里面。如果把这个关起来的话,就没有办法把值写进去。至于输入门的开关是神经网络自己学的,其可以自己学什么时间要把输入门打开,什么时间要把输入门关起来。输出的地方也有一个输出门(output gate),输出门会决定外界其他的神经元能否从这个影象元里面把值读出来。把输出门关闭的时间是没有办法把值读出来,输出门打开的时间才可以把值读出来。跟输入门一样,输出门什么时间打开什么时间关闭,网络是自己学到的。第三个门称为忘记门(forget gate),忘记门决定什么时间影象元要把过去记得的东西忘掉。这个忘记门什么时间会把存在影象元的值忘掉,什么时间会把存在影象元里面的值继承保留下来,这也是网络自己学到的。整个LSTM 可以看成有4 个输入、1 个输出。在这4 个输入中,一个是想要被存在影象元的值,但不一定能存进去,尚有操控输入门的信号、操控输出门的信号、操控忘记门的信号,有着四个输入但它只会得到一个输出。
之前的循环神经网络,它的影象元在每一个时间点都会被洗掉,只要有新的输入进来,每一个时间点都会把影象元洗掉,所以的短期是非常短的,但如果是长时间的短期影象元,它记得会比较久一点,只要忘记门不要决定要忘记,它的值就会被存起来。
影象元对应的盘算公式为
c ′ = g ( z ) f ( z i ) + c f ( z f ) c′ = g(z)f (z_i) + cf (z_f ) c′=g(z)f(zi)+cf(zf)
假设单元里面有这四个输入之前,它里面已经存了值c。输出a 会长什么样子,把z 通过激活函数得到g(z),zi 通过别的一个激活函数得到f(zi) (激活函数通常会选择sigmoid 函数),由于其值介在0 到1 之间的,这个0 到1 之间的值代表了这个门被打开的水平。(如果f 的输出是1,表示为被打开的状态,反之代表这个门是关起来的)。
接下来,把g(z) 乘以f(zi) 得到g(z)f(zi),对于忘记门的zf,也通过sigmoid 的函数得到f(zf ) 接下来把存到影象元里面的值c(岂论忘记门01) 乘以 f(zf ) 得到c f(zf ),加起来 c′ = g(z)f(zi)+cf(zf ),那么c′ 就是重新存到影象元里面的值(岂论忘记门01)。所以根据目前的运算,这个 f(zi) 控制这个 g(z)。假设输入 f(zi) = 0,那 g(z)f(zi) 就等于0,就似乎是没有输入一样,如果 f(zi) 等于1 就等于是把 g(z) 当做输入。那这个 f(zf ) 决定是否要把存在影象元的值洗掉,假设 f(zf ) 为1,忘记门开启的时间,这时间c 会直接通过,把之前的值还会记得。如果 f(zf ) 等于0(忘记门关闭的时间) cf(zf ) 等于0。然后把这个两个值加起来( c′ = g(z)f(zi)+cf(zf )) 写到影象元里面得到c′。这个忘记门的开关是跟直觉是相反的,忘记门打开的时间代表的是记得,关闭的时间代表的是忘记。那这个 c′ 通过h(c′),将 h(c′) 乘以 f(zo) 得到 a = h(c′)f(zo)。输出门受f(zo) 所操控, f(zo) 等于1 的话,就分析 h(c′) 能通过, f(zo) 等于0 的话,分析影象元里面存在的值没有办法通过输出门被读取出来。
LSTM原理
在原来的神经网络里面会有许多的神经元,我们会把输入乘以差别的权重当做差别神经元的输入,每一个神经元都是一个函数,输入一个值然后输出一个值。但是如果是LSTM 的话,只要把LSTM 想成是一个神经元。所以要用一个LSTM 的神经元,其实就是原来简朴的神经元换成LSTM。假设用的神经元的数量跟LSTM 是一样的,则LSTM 需要的参数量是一样平常神经网络的四倍。
LSTM 通常不会只有一层,如有五六层的话,如图5.26 所示。一样平常做RNN 的时间,其实指的就用LSTM。
门控循环单元(Gated Recurrent Unit,GRU)是LSTM 稍微简化的版本,它只有两个门。固然少了一个门,但其性能跟LSTM 差不多,少了1/3 的参数,也是比较不容易过拟合。
RNN学习方法
如果要做学习,需要定义一个损失函数(loss function)来评估模子的好坏,选一个参数要让损失最小。以槽添补为例,如图5.27 所示,给定一些句子,要给句子一些标签,告诉呆板说第一个单词它是属于other 槽,“上海”是目的地槽,“on”属于other 槽,“June”和“1st”属于时间槽。“抵达”丢到循环神经网络的时间,循环神经网络会得到一个输出y1。接下来这个y1会看它的参考向量(reference vector)算它的交叉熵。我们会期望如果丢进去的是“抵达”,其参考向量应该对应到other 槽的度,其他为0,这个参考向量的长度就是槽的数量。如果有四十个槽,参考向量的维度就是40。输入的这个单词对应到other 槽的话,对应到other 槽维度为1, 别的为0。把“上海”丢进去之后,由于“上海”属于目的地槽,希望说把x2 丢进去的话,y2 要跟参考向量距离越近越好。那y2 的参考向量是对应到目的地槽是1,别的为0。注意,在丢x2 之前,一定要丢x1(在丢“上海”之前先把“抵达”丢进去),否则就不知道存到影象元里面的值是多少。所以在训练的时间,不可以大概把这些单词序列打散来看,单词序列仍然要当做一个团体来看。把“on”丢进去,参考向量对应的other 的维度是1,别的是0. RNN 的损失函数输出和参考向量的交叉熵的和就是要最小化的对象。
有了这个损失函数以后,对于训练也是用梯度下降来做。也就是如今定义出了损失函数L,要更新这个神经网络里面的某个参数w,就是盘算关于w 的偏导数,偏导数盘算出来以后,就用梯度下降的方法去更新里面的参数。梯度下降用在前馈神经网络里面我们要用一个有效率的算法称为反向流传。循环神经网络里面,为了要盘算方便,提出了反向流传的进阶版,即随时间反向流传(BackPropagation Through Time,BPTT)。BPTT 跟反向流传其实是很类似的,只是循环神经网络它是在时间序列上运作,所以BPTT 它要考虑时间上的信息,如图5.28 所示。
RNN 的训练是比较困难的,如图5.29 所示。一样平常而言,在做训练的时间,期待学习曲线是像蓝色这条线,这边的纵轴是总损失(total loss),横轴是回合的数量,我们会希望随着回合的数量越来越多,随着参数不断的更新,损失会慢慢地下降,最后趋向收敛。但是不幸的是,在训练循环神经网络的时间,有时间会看到绿色这条线。如果第一次训练循环神经网络,绿色学习曲线非常剧烈的抖动,然后抖到某个地方,我们会以为这程序有bug。
The error surface is rough.
The error surface is either very flat or very steep.
如图5.30 所示,RNN 的误差外貌是总损失的变革是非常陡峭的或坎坷的。误差外貌有一些地方非常平坦,一些地方非常陡峭。纵轴是总损失,x 和y 轴代表是两个参数。这样会造成什么样的问题呢?假设我们从橙色的点当做初始点,用梯度下降开始调整参数,更新参数,可能会跳过一个悬崖,这时间损失会突然爆长,损失会非常上下剧烈的震荡(学习率来不及调整,直接跨过了悬崖)。有时间我们可能会遇到更惨的状态,就是以恰好我们一脚踩到这个悬崖上,会发生这样的事情,由于在悬崖上的梯度很大,之前的梯度会很小,所以措手不及,由于之前梯度很小,所以可能把学习率调的比较大。很大的梯度乘上很大的学习率结果参数就更新许多,整个参数就飞出去了。裁剪(clipping)可以解决该问题,当梯度大于某一个阈值的时间,不要让它高出那个阈值,当梯度大于15 时,让梯度等于15 竣事。由于梯度不会太大,所以我们要做裁剪的时间,就算是踩着这个悬崖上,也不飞出来,会飞到一个比较近的地方,这样还可以继承做RNN 的训练。
之前讲过ReLU 激活函数的时间,梯度消失(vanishing gradient)来源于Sigmoid 函数。但RNN 会有很平滑的误差外貌不是来自于梯度消失。把Sigmoid 函数换成ReLU,其实在RNN 性能通常是比较差的,所以激活函数并不是关键点。
有更直观的方法来知道一个梯度的巨细,可以把某一个参数做小小的变革,看它对网络输出的变革有多大,就可以测出这个参数的梯度巨细,如图5.31 所示。举一个很简朴的例子,只有一个神经元,这个神经元是线性的。输入没有偏置,输入的权重是1,输出的权重也是1,转移的权重是w。也就是说从影象元接到神经元的输入的权重是w。
如图5.32 所示,假设给神经网络的输入是[1, 0, 0, 0]T,好比神经网络在最后一个时间点(1000 个输出值是w_999)。假设w 是要学习的参数,我们想要知道它的梯度,所以是改变w 的值时间,对神经元的输出有多大的影响。假设w = 1,y^1000 = 1, 假设w = 1.01,y^1000 ≈ 20000,w 有一点小小的变革,会对它的输出影响是非常大的。所以w 有很大的梯度。有很大的梯度也没关系,把学习率设小一点就好了。但把w 设为0.99,那y^1000 ≈ 0。如果把w 设为0.01,y^1000 ≈ 0。也就是说在1 的这个地方有很大的梯度,但是在0.99 这个地方就突然变得非常小,这个时间需要一个很大的学习率。设置学习率很麻烦,误差外貌很坎坷,梯度是时大时小的,在非常小的地区内,梯度有许多的变革。从这个例子可以看出,RNN 训练的问题其实来自它把同样的东西在转移的时间,在时间按时间转换的时间,反复利用。所以w 只要一有变革,它完全有可能没有造成任何影响,一旦造成影响,影响很大,梯度会很大或很小。所以RNN 欠好训练的原因不是来自激活函数而是来自于它有时间序列同样的权重在差别的时间点被反复的利用。
如何解决 RNN梯度消失大概爆炸
有什么样的技巧可以解决这个问题呢?广泛被利用的技巧是LSTM,LSTM可以让误差外貌不要那么坎坷。它会把那些平坦的地方拿掉,解决梯度消失的问题,不会解决梯度爆炸(gradient exploding)的问题。有些地方还是非常的坎坷的,有些地方仍然是变革非常剧烈的,但是不会有特别平坦的地方。如果做LST时,大部门地方变革的很剧烈,所以做LSTM的时间,可以把学习率设置的小一点,保证在学习率很小的环境下进行训练。
Q: 为什么LSTM 可以解决梯度消失的问题,可以制止梯度特别小呢?为什么把RNN换成LSTM?
A:LSTM 可以处理梯度消失的问题(但注意LSTM不能解决梯度爆炸的问题)。LSTM的error surface有些地方仍然非常坎坷,但是不会有特别平坦的地方。在做LSTM的时间大部门地方变革都很剧烈,所以可以放心的把lr设的小一点,要在lr特别小的环境下训练。在用这边的式子答复看看。RNN 跟LSTM 在面对影象元的时间,它处理的操作其实是不一样的。在RNN 里面,在每一个时间点,神经元的输出都要影象元里面去,影象元里面的值都是会被覆盖掉。但是在LSTM 里面不一样,它是把原来影象元里面的值乘上一个值再把输入的值加起来放到单元里面。所以它的影象和输入是相加的。LSTM 和RNN 差别的是,如果权重可以影响到影象元里面的值,一旦发生影响会永世都存在。而RNN 在每个时间点的值都会被格式化掉,所以只要这个影响被格式化掉它就消失了。但是在LSTM 里面,一旦对影象元造成影响,影响一直会被留着,除非忘记门要把影象元的值洗掉。否则影象元一旦有改变,只会把新的东西加进来,不会把原来的值洗掉,所以它不会有梯度消失的问题。
忘记门可能会把影象元的值洗掉。其实LSTM 的第一个版本其实就是为了解决梯度消失的问题,所以它是没有忘记门,忘记门是后来才加上去的。甚至有个传言是:在训练LSTM的时间,要给忘记门特别大的偏置,确保忘记门在多数的环境下都是开启的,只要少数的环境是关闭的。bias非常大的好处是,加权输入可以维持在一个较大的正值,使得forget gate接近1,尽可能地保留cell里的值
有别的一个版本用门操控影象元,叫做GRU(Gated Recurrent Unit),LSTM 有三个门,而GRU 只有两个门,所以GRU 需要的参数是比较少的。由于它需要的参数量比较少,所以它在训练的时间是比较鲁棒的。所以如果训练LSTM 的时间,过拟合的环境很严重,可以试下GRU。GRU 的精神就是:旧的不去,新的不来。它会把输入门跟忘记门联动起来,也就是说当输入门打开的时间,忘记门会主动的关闭(格式化存在影象元里面的值),当忘记门没有要格式化里面的值,输入门就会被关起来。也就是要把影象元里面的值清掉,才能把新的值放进来。
其实尚有其他技能可以处理梯度消失的问题。好比顺时针循环神经网络(clockwise RNN)[1] 或结构约束的循环网络(Structurally Constrained Recurrent Network,SCRN)[2] 等等。论文“A Simple Way to Initialize Recurrent Networks of Rectified Linear Units”[3] 采用了差别的做法。一样平常的RNN 用单位矩阵(identity matrix)来初始化转移权重和ReLU 激活函数可以得到很好的性能。刚才不是说用ReLU 的性能会比较差,如果用一样平常训练的方法随机初始化权重,ReLU 跟sigmoid 函数来比的话,sigmoid 性能会比较好。但是利用了单位矩阵,这时间用ReLU 性能会比较好。
RNN其他应用
槽添补的例子中假设输入跟输出的数量是一样的,也就是说输入有几个单词,我们就给每一个单词槽标签,即输入输出都是相同长度的序列,但RNN可以做到更复杂的事情。
多对一序列
好比输入是一个序列,输出是一个向量。情绪分析(sentiment analysis)是典型的应用,如图5.33 所示,某家公司想要知道,他们的产物在网上的评价是正面的还是负面的。他们可能会写一个爬虫,把跟他们产物有关的文章都爬下来。那这一篇一篇的看太累了,所以可以用一个呆板学习的方法学习一个分类器(classifier)来判定文档的正、负面。大概在电影上,情绪分析就是给呆板看许多的文章,呆板要主动判定哪些文章是正类,哪些文章是负类。呆板可以学习一个循环神经网络,输入是字符序列,循环神经网络把这个序列读过一遍。在最后一个时间点,把埋伏层拿出来,在通过几个变换,就可以得到最后的情绪分析。
情绪分析是一个分类问题,但是由于输入是序列,所以用RNN来处理。
用RNN来作关键术语抽取(keytermextraction)。关键术语抽取意思就是说给呆板看一个文章,呆板要预测出这篇文章有哪些关键单词。如图5.34所示,如果可以大概收集到一些训练数据(一些文档,这些文档都有标签,哪些单词是对应的,那就可以直接训练一个RNN),那这个RNN把文档当做输入,通过嵌入层(embeddinglayer),用 RNN 把这个文档读过一次,把出如今最后一个时间点的输出拿过来做注意力,可以把这样的信息抽出来再丢到前馈神经网络得到最后的输出。
多对多序列
RNN 也可以处理多对多的问题,好比输入和输出都是序列,但输出序列比输入序列短。如图5.35 所示,在语音辨认这个任务里面输入是声音序列,一句话就是一段声音信号。一样平常处理声音信号的方式就是在这个声音信号里面,每隔一小段时间,就把它用向量来表示。这个一小段时间是很短的(好比0.01秒)。那输出序列是字符序列。
如果是原来的RNN(槽添补的那个RNN),把这一串输入丢进去,它充其量只能做到,告诉我们每一个向量对应到哪一个字符。加入说中文的语音辨认的话,那输出目的理论上就是这个世界上全部可能中文的单词,常用的可能是八千个,RNN分类器的数量可能就是八千个。固然很大,但也是没有办法做的。但是充其量只能做到说:每一个向量属于一个字符。每一个输入对应的时间隔断是很小的(0.01秒),所以通常是好多个向量对应到同一个字符。所以辨认结果为“好好好棒棒棒棒棒”,这不是语音辨认的结果。有一招叫做修剪(trimming),即把重复的东西拿掉,就变成“好棒”。这样会有一个严重的问题,由于它没有辨认“好棒棒 ”。
需要把“好棒”跟“好棒棒”分开来,怎么办,有一招叫做“CTC”,如图5.36所示,在输出时间,不只是输出全部中文的字符,还可以输出一个符号”null”,其代表没有任何东西。所以输入一段声音特征序列,它的输出是“好null null 棒 null null null null”,然后把“null”的部门拿掉,它就变成“好棒”。如果我们输入别的一个序列,它的输出是“好 null null 棒 null 棒 null null”,然后把“null”拿掉,所以它的输出就是“好棒棒”。这样就可以解决叠字的问题了。
CTC 怎么做训练呢?如图5.37所示,CTC在做训练的时间,手上的训练数据就会告诉我们说,这一串声音特征对应到这一串字符序列,但它不会告诉我们说“好”是对应第几个字符到第几个字符。这时间要穷举全部可能的对齐,简朴来说,我们不知道“好”对应到那几个字符,“棒”对应到哪几个字符。假设我们全部的状态都是可能的。可能第一个是“好null 棒null null null”,可能是“好 null null 棒 null null”,也可能是“好 null null null 棒 null”。假设全部都是对的,一起训练。穷举全部的可能,可能性太多了。
在做英文辨认的时间,RNN 输出目的就是字符(英文的字母+空白)。直接输出字母,然后如果字和字之间有边界,就主动有空白。如图5.38所示,第一帧是输出H,第二帧是输出null,第三帧是输出 null,第四帧是输出 I 等等。如果我们看到输出是这样子话,最后把“null”的地方拿掉,这句话的辨认结果就是“HIS FRIEND’S”。我们不需要告诉呆板说:”HIS”是一个单词,“FRIEND’s”是一个单词, 呆板通过训练数据会自己学到这件事情。如果用 CTC来做语音辨认,就算是有某一个单词在训练数据中从来没有出现过(好比英文中的人名或地名),呆板也是有时机把它辨认出来。
序列到序列
另一个 RNN 的应用是序列到序列(Sequence-to-Sequence,Seq2Seq)学习,在序列到序列学习里面,RNN的输入跟输出都是序列(但是两者的长度是不一样的)。刚在在CTC时,输入比较长,输出比较短。在这边我们要考虑的是不确定输入跟输出谁比较长谁比较短。好比呆板翻译(machine translation),输入英文单词序列把它翻译成中文的字符序列。英文和中文序列的长短是未知的。
如果输入呆板学习,然后用RNN读过去,然后在最后一个时间点,这个影象元里面就存了全部输入序列的信息,如图5.39所示。
接下来,我们让呆板吐一个字符(“机”),就让它输出下一个字符,把之前的输出出来的字符当做输入,再把影象元里面的值读进来,它就会输出“器”。那这个“机”怎么接到这个地方呢,有许多支支节节的技巧。在下一个时间输入“器”,输出“学”,然后输出“习”,然后一直输出下去,如图5.40 所示。
要怎么制止让它产生单词呢?要多加一个符号“断”,所以呆板的输出不是只有字符,它尚有一个可能输出“断”。如果“习”背面是符号“===”(断)的话,就停下来了,如图 5.41 所示。这是训练的起来的。序列到序列学习,假设做翻译,原来是输入某种语言的文字,翻译成别的一种语言的文字。有没有可能直接输入某种语言的声音信号,输出别的一种语言的文字呢?我们完全不做语音辨认。好比把英文翻译成中文,收集一大堆英文的句子,看看它对应的中文翻译。我们完全不要做语音辨认,直接把英文的声音信号丢到这个模子里面去,看它能不能输出正确的中文。这一招居然是行得通的。假设要把闽南语转成英文,但是闽南语的语音辨认体系欠好做,由于闽南语根本就没有标准文字体系。如果训练闽南语转英文语音辨认体系的时间,只需要收集闽南语的声音信号跟它的英文翻译就可以了,不需要闽南语语音辨认的结果,也不需要知道闽南语的文字。
序列到序列的技能也被用到句法分析(syntacticparsing)。句法分析,让呆板看一个句子,得到句子结构树。如图5.42 所示,只要把树状图描述成一个序列,好比:“John has a dog.”,序列到序列学习直接学习一个序列到序列模子,其输出直接就是句法分析树,这个是可以训练的起来的。LSTM的输出的序列也是符合文法结构,左、右括号都有。
要将一个文档表示成一个向量,如图5.43所示,每每会用词袋(Bag-of-Words,BoW)的方法,用这个方法的时间,每每会忽略掉单词次序信息。举例来说,有一个单词序列是“white blood cells destroying an infection”,别的一个单词序列是:“an infection destroying white blood cells”,这两句话的意思完全是相反的。但是我们用词袋的方法来描述的话,他们的词袋完全是一样的。它们里面有完全一摸一样的六个单词,由于单词的次序是不一样的,所以他们的意思一个变成正面的,一个变成负面的,他们的意思是很不一样的。
可以用序列到序列自编码器这种做法来考虑单词序列次序的环境下,把一个文档变成一个向量。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |