1 序列数据表示
1.1 简述
语音、文字等有先后顺序,属于序列数据,将时序列据表达为能处理的形式就叫Sequence Representation。如对文字而言,PyTorch中没有对string的支持,所以要将其表示成数值形式,相应的方法就是Word Embedding。
通常将一个序列表示为[元素数、每个元素的向量长]
的Tensor形式:
注意,这个表示方法对不同的具体序列数据的类型有不同的具体解释,上面的seq_len
既是序列的长度,也是序列的特征的数量,因为序列中每个元素就是序列的一个特征,所以也可以理解成feature_num
。
1.1.1 文本数据
例如,每句话有5个单词,每个单词表示为长度100的词向量,那么一句话表示为Tensor后的shape就是[5,100]
了。
1.1.2 数值数据
例如,房价时序数据,有100个月的房价,每个月的房价是一个数值,那么一个房价时序数据的Tensor的shape就应该是[100,1]
。房价本身就是一个可以处理的数值数据,也就不需要做embedding了。
1.1.3 数字图像数据
数字图像数据可以用前面的CNN处理,但是也可以从扫描数字图像的角度来处理。看图片可以是逐行扫描的,那么图片从上到下就成了一个序列数据,每行是序列中的一个特征。如28*28的图像数据,每次扫描一行作为序列中的一个元素,其作为序列数据的Tensor的shape就是[28,28]
(28个特征,每个特征是28个像素值组成的向量)。
不过强行这样处理也往往没有CNN那样做得更好。
1.2 加入batch size后的序列数据
为了训练效率,每次输入的不一定是一个序列样本,可以是多个序列样本,也就要在Tensor里加入一个batch size的维度b,这样输入的Tensor的shape可以有下面两种表示法:
其中b就是一次送入多少个序列。这两种方式中前面一种是比较常用的,因为最外层循环的就应该是一个个特征(特征数即序列长度)。如给出三条房价曲线,都是100个月的房价,那么就是月份编号从0到99,样本从0到2,房价维度是1。
具体使用哪种方式存储数据,在PyTorch中只要使用batch_front
标志位即可。
2 循环神经网络
循环(Recurrent)神经网络将序列数据从时间上展开处理。以下简称循环网络,为了避免和递归神经网络混淆,不使用RNN这个简称。循环网络可以用于序列数据的分析、生成和转换。
2.1 共享参数的引入
对于序列数据最直观的处理方法是对序列中的每个特征单独处理,如下图,是对一句话的每个词向量用独立的线性层处理,最后再将结果聚合:
但这样的处理方式会带来两个问题:
- 参数量太大,因为序列可能很长(如长句子序列会有很多单词)
- 没有使用前面时刻的语境信息(如句子"喜欢"和"不 喜欢"意思是不一样的)
可以采用权值共享的方式,所有的特征共享权值矩阵和偏置:
这样可以解决上面的第一个问题,但是这样做对语境信息处理得还不好。
2.2 循环网络的结构
循环网络和传统的前馈式网络的区别就是,循环网络的输入是分散在各个时刻上的,而不是一次性输入的。
图中所有的A都是同一个处理单元,里面的运算是一样的,参数也是共享的。保存了网络在时刻之前学习到的语境信息,具体地,每层综合之前信息和当前输入计算得到:
其中部分即是对之前的信息的处理,而则是对新输入的特征的处理。这就需要一个单元用来保存语境信息,最开始可以将其初始化为全0的Tensor。
而这一层的输出是可选的,有的循环网络中有输出,有的可以没有输出。最简单的方式就是用感知器的方式,对进行一个线性层处理再**一下:
这样的循环网络叫Elman网络,目前用得较少了。
3 循环网络下的语言模型
3.1 n-gram模型
如对句子,估算句子出现概率,可以将其用条件概率分解:
可见其中存在这样的项,这样的项条件部分太多,在现实中没法做到准确的估计,可以在一定程度上进行独立性假设,n元文法(n-gram)模型就是解决类似的问题,例如:
即是认为一个词的出现仅依赖于前面的两个词,这样就是三元文法(Tri-gram)模型。
3.2 困惑度(Preplexity)
困惑度是评价训练好的语言模型的一种理论方法,对测试集中的数据,困惑度的计算方法是:
困惑度本质上是测试数据上的经验分布和模型上的概率分布的交叉熵:
3.3 循环网络的语言模型结构
使用循环网络构建语言模型,输入和输出都是自然语言的词,上一时刻的输出直接作为下一时刻的输入:
图中特殊词<bos>
表示begin of sentence,相应的还有<eos>
在句子结尾。
在语言模型中,因为每个时刻要输出的需要是一个概率分布,以来表达输出的是每个词的概率,所以不能再像前面的Elman网络那样使用sigmoid**,而是采用softmax来将输出归一化成多类的概率向量的形式:
在类别只有两类的时候,softmax和sigmoid是等价的,但是显然语言中的词汇有很多很多,不止两类。
4 循环网络的参数训练——BPTT算法
BPTT算法,即梯度在循环网络中按时间线回传,以更新网络中的参数。
4.1 每个时刻单独优化(旧的方法)
要进行参数训练,可以用SGD,这就要为循环网络定义一个损失函数。以前面的语言模型为例,最开始时刻的输入是,输出是(会作为下一时刻的输入),那么想要最大化的就是在参数情形下,输入时输出为的概率,即最大化,所以可以将损失函数定义为对数似然度的相反数:
在训练时就是去优化模型的参数使得损失函数越小越好:
李沐老师在这里特别提到,对softmax本身求梯度比较复杂,但和对数似然函数的损失函数复合之后(softmax log-loss)的梯度的梯度却比较简单:
它就等于期望输出这个词的概率,减去当前是否是输出这个词。也就是说在梯度下降中的调整正好是当前值和目标值的差。
在最初的循环网络中,计算损失和计算梯度都是按时刻依次计算的 ,先算第一个时刻再算第二个时刻。而从第二个时刻开始,就会开始涉及上一时刻的计算图,如下图:
这时还要将梯度从传到的部分,来更新网络参数,所以这个算法叫BPTT(Back Propagation Through Time),即是经过时间线来反向传播。
同理,在第三个时刻,有:
4.2 整体优化(新的方法)
有了计算图工具,不用一个时刻一个时刻的分别计算梯度,可以把整个计算图做完,把所有的损失加在一起,求出一个总的损失再整体优化循环网络:
以图中序列长为3为例,总的损失为:
优化模型参数以最小化损失:
最终的优化结果和4.1
中的单独优化的结果是等价的。但这样可以简化程序(把计算图搭建完一步计算梯度)、提高效率(4.1
中的方法较前面的层要更新很多次,但用现在的方法只要更新一次)。
5 梯度消失/梯度爆炸问题
循环网络训练过程中,把梯度从最后的时刻传到最前面的时刻,要经过的传导路径很长,也就是链式法则展开后是非常多的偏导数相乘,如前面的序列长为3的例子中:
所以就可能导致梯度消失和梯度爆炸问题,后面会学习的LSTM就较好的解决了这个问题。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:【DL学习笔记】3:循环神经网络(Recurrent Neural Network) - Python技术站