Tensorflow——循环神经网络(二)序列式问题

·  阅读 372
Tensorflow——循环神经网络(二)序列式问题

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情


上一篇文章中,我们介绍了数据padding,Embedding与变长输入的处理的问题,并且使用Embedding解决文本分类的问题

今天我们来解释为什么需要循环神经网络和循环神经网络的结构,并且使用循环神经网络来进行文本分类。


  • 2.1 序列问题
    • 2.1.1 为什么需要循环神经网络——合并+padding的缺点?
      • 信息丢失
        • 多个embedding合并
        • Pad噪音、无主次
      • 无效计算太多、抵消
        • 太多的padding
    • 2.1.2 为什么需要循环神经网络——序列式问题
      • 普通神经网络
      • 1对多:图片生成描述
      • 多对1:文本分类(文本情感分析)
      • 多对多:encoding-decoding机器翻译
      • 实时多对多:视频解说
    • 2.1.3 循环神经网络
    • 维护一个状态作为下一步的额外输出
    • 每一步使用同样的激活函数和参数
    • 最简单的循环神经网络
    • 循环神经网络的列子
  • 2.2 循环神经网络文本分类

    • 2.2.1 定义一个1层单层单向的rnn,2层全连接层的模型
embedding_dim = 16
batch_size = 512

single_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,409
Trainable params: 169,409
Non-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)
复制代码

运行结果:

output_8_0.png

output_8_1.png 在测试集上的效果:

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 = 16
batch_size = 512

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 = 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,393
Trainable params: 203,393
Non-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)
复制代码

运行结果:

PS{~7FN1P2IZ7L@F$3{P126.png E((O5M_15M{XM}0)OWV4JQ4.png

发现在训练集上accuracy非常好,但是在验证集上的accuracy不太好,这说明模型在训练集上过拟合了

这次的模型定义的是一个双层双向的RNN,模型太复杂了

  • 2.2.3 实现一个单层双向的rnn,2层全连接层的模型
embedding_dim = 16
batch_size = 512

bi_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,249
Trainable params: 165,249
Non-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)
复制代码

运行结果:

output_16_0.png

output_16_1.png

虽然在验证集上的效果要好很多,但是依然由过拟合的现象存在。

在测试集上进行评估:

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,正则化项等。

9JQ4ZCQY3M({Q$KEN%9BFQX.png

收藏成功!
已添加到「」, 点击更改