1. 前言
本文讲解循环神经网络(RNN)的改进方法,并使用改进的RNN实现电影评论情感分析。 本人全部文章请参见:博客文章导航目录 本文归属于:NLP模型原理与应用系列 前文:长短期记忆网络(LSTM)原理与实战
2. 多层RNN(Stacked RNN)
在深度学习领域,可以将许多全连接层堆叠,构成一个多层感知机(Multi-Layer Perception),可以将许多卷积层堆叠,构成一个深度卷积网络。同样的,可以将许多RNN层堆叠,构成一个多层RNN网络。 RNN每读取一个新的输入
x
t
x_t
xt?,就会生成状态向量
h
t
h_t
ht?作为当前时刻的输出和下一时刻的输入状态。 将
T
T
T个输入
x
0
~
x
T
x_0\sim x_T
x0?~xT?依次输入RNN,相应地会产生
T
T
T个输出。第一层RNN输出的
T
T
T个状态向量可以作为第二层RNN的输入,第二层RNN拥有独立的参数,依次读取
T
T
T个来自第一层RNN的状态向量,产生
T
T
T个新的输出。第二层RNN输出的
T
T
T个状态向量可以作为第三层RNN的输入,依此类推,构成一个多层RNN网络。 在图一所示3层RNN网络中,输入为词嵌入
x
0
~
x
t
x_0\sim x_t
x0?~xt?,状态向量
h
1
~
h
t
h_1\sim h_t
h1?~ht?是最终的输出。可以将最后一个状态向量
h
t
h_t
ht?看做是从最底层输入的文本中提取的特征向量。 当训练数据足够多时,多层RNN效果可能会比单层RNN效果好,可以尝试使用多层RNN。
3. 双向RNN(Bidirectional RNN)
RNN对时序数据的处理与人脑一致,人类从左往右依次阅读文本中的每个单词,在大脑里积累信息,RNN从左往右依次读取输入
x
t
,
(
t
=
0
~
T
)
x_t, (t=0\sim T)
xt?,(t=0~T),在状态向量
h
t
,
(
t
=
0
~
T
)
h_t, (t=0\sim T)
ht?,(t=0~T)中积累信息。 人类习惯于从左往右、从前往后阅读,但是一段文本的内在含义取决于不同单词的排列顺序,当排列顺序固定,文本意义一般是固定的。一般来说,从右往左阅读一段文本并不会影响该文本的内在意义及对其整体含义的理解。对于RNN来说,从左往右或从右往左阅读一段文本并没有本质的区别。在实践中,将训练集中每个训练样本的单词均从右往左依次输入RNN,一般可以取得相同的效果。因此可以训练两个完全独立(不共享参数和状态向量)的RNN,一个从左往右,另一个从右往左依次读取训练样本中输入的单词。 如图二所示,两个RNN分别输出各自状态向量,然后将二者输出的状态向量拼接,形成双向RNN的输出状态向量
y
y
y。状态向量
h
t
h_t
ht?和
h
t
′
h_t^\prime
ht′?拼接后形成的向量
[
h
t
,
h
t
′
]
[h_t, h_t^\prime]
[ht?,ht′?]可以看做双向RNN从输入文本中提取的特征向量。 在各类时序数据处理任务中,双向RNN效果总是优于单向RNN。因为不管使用Simple RNN还是LSTM,都会或多或少遗忘掉早先的输入信息。如果RNN从左往右读取输入信息,则最后一个状态
h
t
h_t
ht?可能会遗忘掉左侧的输入信息。如果RNN从右往左读取输入信息,则最后一个状态
h
t
′
h_t^\prime
ht′?可能会遗忘掉右侧的输入信息。二者结合可使得模型不会遗忘最先读取的输入信息。 在实际任务中,如果可以,建议使用双向RNN而不使用单向RNN,双向RNN效果至少不会比单向RNN效果差。
4. 预训练(Pretrain)
预训练在深度学习中非常常用,比如在训练深度卷积神经网络时,如果网络模型很大而训练集不够大,则可以先在ImageNet等大数据集上做预训练,从而使神经网络拥有比较合理的初始化,同时可以在一定程度上减少过拟合(Overfitting) 。 训练RNN时,模型Embedding层参数数量等于【词表大小】乘以【词嵌入维度】,该层参数往往比较多,非常容易导致模型过拟合,因此可以预训练Embedding层。预训练具体过程如下:
- 在更大的数据集上训练一个含有Embedding层的模型
????
~~~~
????- 预训练Embedding层时任务可以与原任务不一致
????
~~~~
????- 预训练Embedding层时模型结构可以与原任务不一致
两个任务越相似,预训练后Embedding层迁移效果就会越好;预训练时神经网络结构是什么都可以,甚至不用是RNN,但是必须包含Embedding层。
- 预训练完成后,只保留预训练阶段模型Embedding层及其参数
- 搭建解决实际问题的RNN模型,并在训练集上训练该RNN模型
????
~~~~
????- 搭建解决实际问题的RNN模型时,Embedding层采用预训练好的Embedding层
????
~~~~
????- 训练该RNN层时,如果训练集较小,建议不训练预训练阶段训练好的Embedding层参数。如果训练集相对较大,建议微调预训练阶段训练好的Embedding层参数
5. 电影评论情感分析(四)
改进电影评论情感分析模型,使用双层双向LSTM取代前文中单层单向LSTM。数据处理及模型训练部分均与电影评论分析(三)一致,只需更改模型搭建部分代码,稍微调整模型结构。 使用PaddlePaddle实现多层双向LSTM非常简单,根据官方API文档,paddle.nn.LSTM 类包含direction 、num_layers 可选参数。direction 表示网络迭代方向,可设置为forward 或bidirect (或bidirectional ),默认为forward 。num_layers 表示网络层数,默认为1。实现双层双向LSTM只需在实例化paddle.nn.LSTM 对象时,传入参数direction='bidirectional' 、num_layers=2 。此外,在重写forward 方法,执行前向计算流程时,需要进行相应的更改,具体代码如下:
import paddle
from emb_softmax import get_data_loader
import warnings
warnings.filterwarnings('ignore')
class AdvancedLSTM(paddle.nn.Layer):
def __init__(self, embedding_dim, hidden_size):
super(AdvancedLSTM, self).__init__()
self.emb = paddle.nn.Embedding(num_embeddings=5149, embedding_dim=embedding_dim)
self.lstm = paddle.nn.LSTM(input_size=embedding_dim, hidden_size=hidden_size,
direction='bidirectional', num_layers=2)
self.flatten = paddle.nn.Flatten()
self.fc = paddle.nn.Linear(in_features=hidden_size*2, out_features=2)
self.softmax = paddle.nn.Softmax()
def forward(self, x):
x = self.emb(x)
_, (x, _) = self.lstm(x)
x = paddle.slice(x, axes=[0], starts=[2], ends=[4])
x = paddle.transpose(x, [1, 0, 2])
x = self.flatten(x)
x = self.fc(x)
x = self.softmax(x)
return x
使用model.summary 可查看模型结构及参数信息:
-----------------------------------------------------------------------------------------------
Layer (type) Input Shape Output Shape Param #
===============================================================================================
Embedding-1 [[1, 200]] [1, 200, 16] 82,384
LSTM-1 [[1, 200, 16]] [[1, 200, 64], [[4, 1, 32], [4, 1, 32]]] 37,888
Flatten-1 [[1, 2, 32]] [1, 64] 0
Linear-1 [[1, 64]] [1, 2] 130
Softmax-1 [[1, 2]] [1, 2] 0
===============================================================================================
Total params: 120,402
Trainable params: 120,402
Non-trainable params: 0
-----------------------------------------------------------------------------------------------
Input size (MB): 0.00
Forward/backward pass size (MB): 0.12
Params size (MB): 0.46
Estimated Total Size (MB): 0.58
-----------------------------------------------------------------------------------------------
开启模型训练,打印训练过程信息:
The loss value printed in the log is the current step, and the metric is the average value of previous steps.
Epoch 1/5
step 195/195 [==============================] - loss: 0.5211 - acc: 0.6451 - 47ms/step
Eval begin...
step 195/195 [==============================] - loss: 0.6951 - acc: 0.7158 - 23ms/step
Eval samples: 24960
Epoch 2/5
step 195/195 [==============================] - loss: 0.2767 - acc: 0.8171 - 50ms/step
Eval begin...
step 195/195 [==============================] - loss: 0.1709 - acc: 0.8181 - 24ms/step
Eval samples: 24960
Epoch 3/5
step 195/195 [==============================] - loss: 0.2327 - acc: 0.8855 - 47ms/step
Eval begin...
step 195/195 [==============================] - loss: 0.2820 - acc: 0.8548 - 24ms/step
Eval samples: 24960
Epoch 4/5
step 195/195 [==============================] - loss: 0.2743 - acc: 0.9044 - 47ms/step
Eval begin...
step 195/195 [==============================] - loss: 0.2730 - acc: 0.8567 - 24ms/step
Eval samples: 24960
Epoch 5/5
step 195/195 [==============================] - loss: 0.2919 - acc: 0.9254 - 47ms/step
Eval begin...
step 195/195 [==============================] - loss: 0.2877 - acc: 0.8539 - 22ms/step
Eval samples: 24960
经过实践发现,在IMDB电影评论情感分析数据集上,单层单向LSTM与双层双向LSTM结果基本一致,没有显著效果提升。回顾电影评论情感分析(一)至(四),我认为问题主要是出在Embedding层,Embedding层参数太多,训练数据集较小,出现过拟合。在此情况下,改进RNN层无法提升模型效果。在此情况下,要想提升模型效果,可以预训练Embedding层。
6. 模型大小与训练数据集大小认识(附议)
过拟合是深度学习实践中非常容易出现的现象,其根本原因是:
- 模型的复杂度大于实际问题的复杂度
- 模型的复杂度大于训练数据的复杂度
在各类深度学习资料中,经常可以看到“模型太大,参数过多,训练数据集较小,模型出现过拟合”,但是几乎没有任何资料给出一个对模型和训练数据集相对大小的认识。任何人都知道训练数据越多,则可以将模型训练得越好,训练数据多多益善。但是,对于给定的存在多少参数的模型,究竟需要多少数据才能够从头训练出一套比较合理的模型参数? 对于上述问题,其实并不存在精确的数学指导理论或证明。下面我将用一个例子,和大家一起感性理解模型大小与训练数据集大小的关系。
假设要用数据拟合二维平面内一条直线(
y
=
k
x
+
b
y=kx+b
y=kx+b,两个参数),当训练数据只有一个,即给定平面上一个点,确定平面内一条直线。显然,在只给定一个点的情况下,拟合出来的直线有极大可能与真实直线存在较大的偏差。如果给定两个点,虽然两点可以确定一条唯一的直线,但是由于给定的两个点,是训练数据集中的随机样本,具有一定的随机性,拟合出来的直线仍有较大可能与真实直线存在一定的偏差。一般来说,当给定十数个甚至数十上百个点,才能拟合出比较精确的直线。 从上述例子可知,比较精确地拟合只有2个参数的模型,需要约20个样本,即在模型复杂度和问题复杂度一致的情况下,训练出一个比较精确合理的模型,训练集数据量预计要十倍于模型参数数量。 在深度学习实践中,模型复杂度很有可能大于实际问题复杂度,而且问题和数据特征空间维度可达成百上千甚至数百万维。模型复杂度比实际问题复杂度越大,则模型越容易拟合训练数据中的噪声、异常值等不能反映实际问题客观规律的随机误差,模型需要更多数据才能学到实际问题中的真实规律。问题和数据特征空间维度越高,则求解空间越大,相当于可选答案更多,干扰项越多,需要更多信息才能找到真实规律。因此,训练深度学习模型,训练集数据量哪怕百倍千倍于模型参数量,都无法肯定的说训练数据集已经足够大。 深度学习模型参数动辄数百万上千万,OpenAI出品的GPT-3模型参数达到了令人恐怖的1750亿,Google出品的Switch Transformer语言模型参数达到了令人发指的1.6万亿,快手出品的精排排序模型参数据说达到了丧心病狂的1.9万亿。 在深度学习领域,训练数据集不论多大,永远都可以说——两个怎么够?我要二十个!
7. 参考资料链接
- 九品芝麻官(普通话版)
- https://www.youtube.com/watch?v=pzWHk_M23a0&list=PLvOO0btloRnuTUGN4XqO85eKPeFSZsEqK&index=5&t=608s
- https://www.icourse163.org/learn/ZJU-1206573810?tid=1206902211#/learn/content?type=detail&id=1234658158&cid=1254264368&replay=true
|