Keras系列之LSTM和GRU实践

作者:《python深度学习》学习笔记,用于自己熟悉和理解

 

目录

1. LSTM实践    

2.GRU实践

3.循环神经网络的高级用法

3.1 循环 dropout(recurrent dropout)

3.2 堆叠循环层(stacking recurrent layers)

3.3 双向循环层(bidirectional recurrent layer)

3.4 更多尝试


1. LSTM实践    

Keras中使用LSTM,只需指定LSTM 层的输出维度,其他所有参数(有很多)都使用 Keras 默认值。Keras 具有很好的默认值,无须手动调参,模型通常也能正常运行。
简单使用:

from keras.layers import LSTM
model = Sequential()
model.add(Embedding(max_features, 32))
model.add(LSTM(32))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
               metrics=['acc'])
history = model.fit(input_train, y_train,
                     epochs=10,
                     batch_size=128,
                     validation_split=0.2)

结果:验证精度达到了 89%。还不错,肯定比 SimpleRNN 网络好多了,这主要是因为LSTM 受梯度消失问题的影响要小得多。这个结果也比全连接网络略好,虽然使用的数据量比全连接网络要少。此处在 500 个时间步之后将序列截断,而在全连接网络是读取整个序列。但对于一种计算量如此之大的方法而言,这个结果也说不上是突破性的。
为什么 LSTM 不能表现得更好
一个原因是你没有花力气来调节超参数,比如嵌入维度或 LSTM 输出维度。另一个原因可能是缺少正则化。但说实话,主要原因在于,适用于评论分析全局的长期性结构(这正是 LSTM 所擅长的),对情感分析问题帮助不大。对于这样的基本问题,观察每条评论中出现了哪些词及其出现频率就可以很好地解决。这也正是第一个全连接方法的做法。但还有更加困难的自然语言处理问题,特别是问答和机器翻译,这时 LSTM 的优势就明显了。

2.GRU实践

GRU是LSTM的简化,运算代价更低。
简单使用:

model = Sequential()
model.add(layers.GRU(32, input_shape=(None, float_data.shape[-1])))
model.add(layers.Dense(1))
model.compile(optimizer=RMSprop(), loss='mae')
history = model.fit_generator(train_gen,
                              steps_per_epoch=500,
                              epochs=20,
                              validation_data=val_gen,
                              validation_steps=val_steps)

3.循环神经网络的高级用法

提高循环神经网络的性能和泛化能力的三种高级技巧。

3.1 循环 dropout(recurrent dropout)

dropout用来降低过拟合,通过将某一层的输入单元随机设为 0,来打破该层训练数据中的偶然相关性。但在循环网络中如何正确地使用dropout,这并不是一个简单的问题。
在循环网络中使用 dropout 的正确方法:
对每个时间步应该使用相同的 dropout 掩码(dropout mask,相同模式的舍弃单元),而不是让 dropout 掩码随着时间步的增加而随机变化。对 GRU、LSTM 等循环层得到的表示做正则化,应该将不随时间变化的 dropout 掩码应用于层的内部循环激活(叫作循环 dropout 掩码)。对每个时间步使用相同的 dropout 掩码,可以让网络沿着时间正确地传播其学习误差,而随时间随机变化的 dropout 掩码则会破坏这个误差信号,并且不利于学习过程。
Keras中如何使用dropout:
这项研究成果直接内置到 Keras 循环层中了,Keras的每个循环层都有两个与 dropout 相关的参数:一个是 dropout,它是一个浮点数,指定该层输入单元的 dropout 比率;另一个是 recurrent_dropout,指定循环单元的 dropout 比率。例子如下:

model = Sequential()
model.add(layers.GRU(32,
                     dropout=0.2,
                     recurrent_dropout=0.2,
                     input_shape=(None, float_data.shape[-1])))
model.add(layers.Dense(1))
model.compile(optimizer=RMSprop(), loss='mae')
history = model.fit_generator(train_gen,
                              steps_per_epoch=500,
                              epochs=40,
                              validation_data=val_gen,
                              validation_steps=val_steps)

结果:

validation loss稳定在较低水平,而且最佳分数也不低。

3.2 堆叠循环层(stacking recurrent layers)

模型不再过拟合,但似乎遇到了性能瓶颈,所以我们应该考虑增加网络容量。增加网络容量的通常做法是增加每层单元数或增加层数,使得网络表达能力更强,当然计算代价也会增加。
在 Keras 中逐个堆叠循环层,所有中间层都应该返回完整的输出序列(一个 3D 张量),而不是只返回最后一个时间步的输出。这可以通过指定 return_sequences=True 来实现。

model = Sequential()
model.add(layers.GRU(32,
                     dropout=0.1,
                     recurrent_dropout=0.5,
                     return_sequences=True,
                     input_shape=(None, float_data.shape[-1])))
model.add(layers.GRU(64, activation='relu',
                      dropout=0.1,
                      recurrent_dropout=0.5))
model.add(layers.Dense(1))
model.compile(optimizer=RMSprop(), loss='mae')
history = model.fit_generator(train_gen,
                              steps_per_epoch=500,
                              epochs=40,
                              validation_data=val_gen,
                              validation_steps=val_steps)

结果:

对结果确实有改进,相对于上图,training loss更低了,而且也不过拟合,可以放心增大每层的大小以进一步来降低val loss。
添加一层后模型并没有显著改进,所以你可能发现,提高网络能力的回报在逐渐减小。

3.3 双向循环层(bidirectional recurrent layer)

将相同的信息以不同的方式呈现给循环网络,可以提高精度并缓解遗忘问题。双向 RNN 是一种常见的RNN 变体,常用于自然语言处理任务。
           RNN 特别依赖于顺序或时间,RNN 按顺序处理输入序列的时间步,而打乱时间步或反转时间步会完全改变 RNN 从序列中提取的表示。正是由于这个原因,如果顺序对问题很重要(比如温度预测问题),RNN 的表现会很好。双向 RNN 利用了 RNN 的顺序敏感性:它包含两个普通 RNN,比如你已经学过的 GRU 层和 LSTM 层,每个 RN 分别沿一个方向对输入序列进行处理(时间正序和时间逆序),然后将它们的表示合并在一起。通过沿这两个方向处理序列,双向RNN 能够捕捉到可能被单向 RNN 忽略的模式。
         一般来说,按时间正序的模型会优于时间逆序的模型。但是对应像自然语言处理这些问题来讲,一个单词对理解句子
的重要性通常并不取决于它在句子中的位置。做一个实验,用正序序列和逆序序列分别训练并且评估要给LSTM,性能几乎相同,这证实了一个假设:虽然单词顺序对理解语言很重要,但使用哪种顺序并不重要。
       
双向循环层还有一个好处是,在机器学习中,如果一种数据表示不同但有用,那么总是值得加以利用,这种表示与其他表示的差异越大越好,它们提供了查看数据的全新角度,抓住了数据中被其他方法忽略的内容,因此可以提高模型在某个任务上的性能。这是集成(ensembling)方法背后的直觉。
工作原理:它从两个方向查看数据,从而得到更加丰富的表示,并捕捉到仅使用正序 RNN 时可能忽略的一些模式


使用:要使用 Bidirectional 层,它的第一个参数是一个循环层实例。Bidirectional 对这个循环层创建了第二个单独实例,然后使用一个实例按正序处理输入序列,另一个实例按逆序处理输入序列。

1.训练并评估一个双向 LSTM

model = Sequential() 
model.add(layers.Embedding(max_features, 32)) 
model.add(layers.Bidirectional(layers.LSTM(32))) 
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc']) 
history = model.fit(x_train, y_train,
                    epochs=10, 
                    batch_size=128, 
                    validation_split=0.2)

结果:比上一节的普通 LSTM 略好,验证精度超过 89%。这个模型似乎也很快就开始过拟合,这并不令人惊讶,因为双向层的参数个数是正序 LSTM 的 2 倍。添加一些正则化,双向方法在这个任务上可能会有很好的表现。

2.训练一个双向 GRU

model = Sequential()
model.add(layers.Bidirectional(
    layers.GRU(32), input_shape=(None, float_data.shape[-1])))
model.add(layers.Dense(1))
model.compile(optimizer=RMSprop(), loss='mae')
history = model.fit_generator(train_gen,
                              steps_per_epoch=500,
                              epochs=40,
                              validation_data=val_gen,
                              validation_steps=val_steps)

结果:这个例子是一个天气预报例子,表现与普通 GRU 层差不多一样好。其原因很容易理解:所有的预测能力肯定都来自于正序的那一半网络,因为我们已经知道,逆序的那一半在这个任务上的表现非常糟糕。
 

3.4 更多尝试


1.在堆叠循环层中调节每层的单元个数。当前取值在很大程度上是任意选择的,因此可能不是最优的。
2.调节 RMSprop 优化器的学习率。
3.尝试使用 LSTM 层代替 GRU 层。
4.在循环层上面尝试使用更大的密集连接回归器,即更大的 Dense 层或 Dense 层的堆叠。
5.不要忘记最后在测试集上运行性能最佳的模型(即验证 MAE 最小的模型)。否则,你开发的网络架构将会对验证集过拟合。

     

最后,书中这段话也是很有意思。

 深度学习是一门艺术而不是科学。我们可以提供指导,对于给定问题哪些方法可能有用、哪些方法可能没用,但归根结底,每个问题都是独一无二的,你必须根据经验对不同的策略进行评估。目前没有任何理论能够提前准确地告诉你,应该怎么做才能最优地解决问题。你必须不断迭代。

参考:《python深度学习》

  • 6
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
LSTMGRU是深度学习中常用的循环神经网络模型,它们的主要区别在于内部结构不同,LSTM包含三个门控单元,而GRU只有两个门控单元。在代码实现中,LSTMGRU的差异主要体现在层的定义和参数设置上。 以TensorFlow为例,下面是LSTMGRU的代码实现示例: LSTM实现示例: ```python import tensorflow as tf # 定义LSTMlstm_layer = tf.keras.layers.LSTM(hidden_size, return_sequences=True, return_state=True) # 定义LSTM层的输入 inputs = tf.keras.Input(shape=(timesteps, input_dim)) # 将LSTM层应用到输入上 lstm_outputs, state_h, state_c = lstm_layer(inputs) # 定义LSTM层的输出 lstm_outputs = tf.keras.layers.Dense(output_dim)(lstm_outputs) ``` GRU实现示例: ```python import tensorflow as tf # 定义GRUgru_layer = tf.keras.layers.GRU(hidden_size, return_sequences=True, return_state=True) # 定义GRU层的输入 inputs = tf.keras.Input(shape=(timesteps, input_dim)) # 将GRU层应用到输入上 gru_outputs, state = gru_layer(inputs) # 定义GRU层的输出 gru_outputs = tf.keras.layers.Dense(output_dim)(gru_outputs) ``` 可以看到,LSTMGRU的定义方式非常相似,唯一的区别是使用`tf.keras.layers.LSTM`或`tf.keras.layers.GRU`来定义层。在定义输入和输出时,LSTMGRU也都是类似的,唯一的区别是LSTM的输出需要两个状态变量(state_h和state_c),而GRU只需要一个状态变量(state)。另外,由于LSTMGRU的内部结构不同,它们的参数数量也是不同的,但这个在代码中并不需要额外考虑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值