在上一篇文章中,我们介绍了数据padding,Embedding与变长输入的处理的问题,并且使用Embedding解决文本分类的问题
今天我们来解释为什么需要循环神经网络和循环神经网络的结构,并且使用循环神经网络来进行文本分类。
2.1 序列问题
- 2.1.1 为什么需要循环神经网络——合并+padding的缺点?
- 信息丢失
- 多个embedding合并
- Pad噪音、无主次
- 无效计算太多、抵消
- 太多的padding
- 信息丢失
- 2.1.2 为什么需要循环神经网络——序列式问题
- 普通神经网络
- 1对多:图片生成描述
- 多对1:文本分类(文本情感分析)
- 多对多:encoding-decoding机器翻译
- 实时多对多:视频解说
- 普通神经网络
2.1.3 循环神经网络
- 维护一个状态作为下一步的额外输出
- 每一步使用同样的激活函数和参数
- 最简单的循环神经网络
- 循环神经网络的列子
- 2.1.1 为什么需要循环神经网络——合并+padding的缺点?
2.2 循环神经网络文本分类
- 2.2.1 定义一个1层单层单向的rnn,2层全连接层的模型
embedding_dim = 16batch_size = 512single_rnn_model = keras.models.Sequential([ # 1. define matrix: [vocab_size, embedding_dim] # 2. [1,2,3,4..], max_length * embedding_dim # 3. batch_size * max_length * embedding_dim keras.layers.Embedding(vocab_size, embedding_dim, input_length = max_length), # return_sequences: 决定你返回的输出(False)是最后一步的输出,还是所有的输出 keras.layers.SimpleRNN(units = 64, return_sequences = False), keras.layers.Dense(64, activation = 'relu'), keras.layers.Dense(1, activation='sigmoid'),])single_rnn_model.summary()single_rnn_model.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = ['accuracy'])
Model: "sequential"_________________________________________________________________Layer (type) Output Shape Param # =================================================================embedding (Embedding) (None, 500, 16) 160000 _________________________________________________________________simple_rnn (SimpleRNN) (None, 64) 5184 _________________________________________________________________dense (Dense) (None, 64) 4160 _________________________________________________________________dense_1 (Dense) (None, 1) 65 =================================================================Total params: 169,409Trainable params: 169,409Non-trainable params: 0
训练模型:
history_single_rnn = single_rnn_model.fit( train_data, train_labels, epochs = 30, batch_size = batch_size, validation_split = 0.2)
在训练30次之后,发现accuracy是0.4990,这对于一个二分类问题来说是没有进展的,因为做随机猜测的话,accuracy也有50%。
这说明普通的单层rnn并没有效果。
打印学习曲线
def plot_learning_curves(history, label, epochs, min_value, max_value): data = {} data[label] = history.history[label] data['val_'+label] = history.history['val_'+label] pd.DataFrame(data).plot(figsize=(8, 5)) plt.grid(True) plt.axis([0, epochs, min_value, max_value]) plt.show() plot_learning_curves(history_single_rnn, 'accuracy', 30, 0, 1)plot_learning_curves(history_single_rnn, 'loss', 30, 0, 1)
运行结果:
在测试集上的效果:
single_rnn_model.evaluate( test_data, test_labels, batch_size = batch_size, verbose = 0)
运行结果:
[0.7533659934997559, 0.5001599788665771]
- 2.2.2 实现一个双层双向的rnn,2层全连接层的模型
- 只需要使用Bidirectional对RNN进行一个封装,封装完之后就是一个双向的RNN
embedding_dim = 16batch_size = 512model = keras.models.Sequential([ # 1. define matrix: [vocab_size, embedding_dim] # 2. [1,2,3,4..], max_length * embedding_dim # 3. batch_size * max_length * embedding_dim keras.layers.Embedding(vocab_size, embedding_dim, input_length = max_length), keras.layers.Bidirectional( keras.layers.SimpleRNN( units = 64, return_sequences = True)), # return_sequences改为True的原因:下一个RNN的输入是序列,而不是单个的输入 keras.layers.Bidirectional( keras.layers.SimpleRNN( units = 64, return_sequences = False)), keras.layers.Dense(64, activation = 'relu'), keras.layers.Dense(1, activation='sigmoid'),])model.summary()model.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = ['accuracy'])
运行结果:
Model: "sequential"_________________________________________________________________Layer (type) Output Shape Param # =================================================================embedding (Embedding) (None, 500, 16) 160000 _________________________________________________________________bidirectional (Bidirectional (None, 500, 128) 10368 _________________________________________________________________bidirectional_1 (Bidirection (None, 128) 24704 _________________________________________________________________dense (Dense) (None, 64) 8256 _________________________________________________________________dense_1 (Dense) (None, 1) 65 =================================================================Total params: 203,393Trainable params: 203,393Non-trainable params: 0_________________________________________________________________
训练模型:
history = model.fit( train_data, train_labels, epochs = 30, batch_size = batch_size, validation_split = 0.2)
可以发现,在训练集上的accuracy是1.0,但是在验证集上的accuracy只有0.5572,也只比单层RNN好了一点点
打印学习曲线:
plot_learning_curves(history, 'accuracy', 30, 0, 1)plot_learning_curves(history, 'loss', 30, 0, 3.8)
运行结果:
发现在训练集上accuracy非常好,但是在验证集上的accuracy不太好,这说明模型在训练集上过拟合了
这次的模型定义的是一个双层双向的RNN,模型太复杂了
- 2.2.3 实现一个单层双向的rnn,2层全连接层的模型
embedding_dim = 16batch_size = 512bi_rnn_model = keras.models.Sequential([ # 1. define matrix: [vocab_size, embedding_dim] # 2. [1,2,3,4..], max_length * embedding_dim # 3. batch_size * max_length * embedding_dim keras.layers.Embedding(vocab_size, embedding_dim, input_length = max_length), keras.layers.Bidirectional( keras.layers.SimpleRNN( units = 32, return_sequences = False)), keras.layers.Dense(32, activation = 'relu'), keras.layers.Dense(1, activation='sigmoid'),])bi_rnn_model.summary()bi_rnn_model.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = ['accuracy'])
运行结果:
Model: "sequential"_________________________________________________________________Layer (type) Output Shape Param # =================================================================embedding (Embedding) (None, 500, 16) 160000 _________________________________________________________________bidirectional (Bidirectional (None, 64) 3136 _________________________________________________________________dense (Dense) (None, 32) 2080 _________________________________________________________________dense_1 (Dense) (None, 1) 33 =================================================================Total params: 165,249Trainable params: 165,249Non-trainable params: 0_________________________________________________________________
训练模型:
history = bi_rnn_model.fit( train_data, train_labels, epochs = 30, batch_size = batch_size, validation_split = 0.2)
可以发现:在训练集上的accuracy达到了1.0,在测试集上的accur达到了0.7888,比双层双向的模型要好很多
打印学习曲线:
plot_learning_curves(history, 'accuracy', 30, 0, 1.1)plot_learning_curves(history, 'loss', 30, 0, 2.5)
运行结果:
虽然在验证集上的效果要好很多,但是依然由过拟合的现象存在。
在测试集上进行评估:
bi_rnn_model.evaluate(test_data, test_labels, batch_size = batch_size, verbose = 0)
运行结果:
[1.404372215270996, 0.7716000080108643]
- 可以发现:训练集上的accuracy是0.7716,相较于之前的简单模型的accur是0.85,那么这是说明循环神经网络比普通的神经网络还要弱吗?
- 可以仔细看一下loss的曲线图,发现循环神经网络在训练第5次的时候,val_loss急剧上升,而在普通神经网络中在训练第10次的时候,val_loss急剧上升,这说明循环神经网络的过拟合更加明显,它的过拟合更加明显反而说明了这个模型更加强大。
- 在搭建模型的过程中,使用了一种方法来缓解过拟合,就是降低模型尺寸,把两层循环神经网络变成单层的;在搭建模型的时候,还有其他手段来缓解过拟合,比如dropout,正则化项等。