循环神经网络的高级用法_温度预测时间序列问题

循环神经网络的高级用法_温度预测时间序列问题
参考:
https://www.jianshu.com/p/426a7aed953c
在这里插入图片描述

下面是你应该从本节中学到的要点。
‰ 我们在第 4 章学过,遇到新问题时,最好首先为你选择的指标建立一个基于常识的基准。
如果没有需要打败的基准,那么就无法分辨是否取得了真正的进步。
‰ 在尝试计算代价较高的模型之前,先尝试一些简单的模型,以此证明增加计算代价是有
意义的。有时简单模型就是你的最佳选择。
‰ 如果时间顺序对数据很重要,那么循环网络是一种很适合的方法,与那些先将时间数据
展平的模型相比,其性能要更好。
‰ 想要在循环网络中使用 dropout,你应该使用一个不随时间变化的 dropout 掩码与循环
dropout 掩码。这二者都内置于 Keras 的循环层中,所以你只需要使用循环层的 dropout
和 recurrent_dropout 参数即可。
‰ 与单个 RNN 层相比,堆叠 RNN 的表示能力更加强大。但它的计算代价也更高,因此不
一定总是需要。虽然它在机器翻译等复杂问题上很有效,但在较小、较简单的问题上可
能不一定有用

代码清单 6-28 观察耶拿天气数据集的数据
代码清单 6-29 解析数据
代码清单 6-30 绘制温度时间序列
代码清单 6-31 绘制前 10 天的温度时间序列
代码清单 6-32 数据标准化
代码清单 6-33 生成时间序列样本及其目标的生成器
代码清单 6-34 准备训练生成器、验证生成器和测试生成器
6.3.3 一种基于常识的、非机器学习的基准方法
代码清单 6-35 计算符合常识的基准方法的 MAE
代码清单 6-36 将 MAE 转换成摄氏温度误差
代码清单 6-37 训练并评估一个密集连接模型
代码清单 6-38 绘制结果
6.3.5 第一个循环网络基准
代码清单 6-39 训练并评估一个基于 GRU 的模型
6.3.6 使用循环 dropout 来降低过拟合
代码清单 6-40 训练并评估一个使用 dropout 正则化的基于 GRU 的模型
6.3.7 循环层堆叠
代码清单 6-41 训练并评估一个使用 dropout 正则化的堆叠 GRU 模型
代码清单 6-44 训练一个双向 GRU

# 在这个数据集中,每 10 分钟记录 14 个不同的量(比如气温、气压、湿度、风向等),其中
# 包含多年的记录。原始数据可追溯到 2003 年,但本例仅使用 2009—2016 年的数据。这个数据
# 集非常适合用来学习处理数值型时间序列。我们将会用这个数据集来构建模型,输入最近的一
# 些数据(几天的数据点),可以预测 24 小时之后的气温。
# 下载并解压数据,如下所示。
# cd ~/Downloads
# mkdir jena_climate
# cd jena_climate
# wget https://s3.amazonaws.com/keras-datasets/jena_climate_2009_2016.csv.zip
# unzip jena_climate_2009_2016.csv.zip
# 来观察一下数据

# 代码清单 6-28 观察耶拿天气数据集的数据
import os
# 加载csv文件

# data_dir = '/home/ubuntu/data/'
data_dir = 'C:\\file\data'
fname = os.path.join(data_dir, 'jena_climate_2009_2016.csv')

f = open(fname)
data = f.read()
f.close()

# 以换行符切分
lines = data.split('\n')
# 将第一行赋予首行
header = lines[0].split(',')
lines = lines[1:]

print(header)
print(len(lines))
print(type(lines))

['"Date Time"', '"p (mbar)"', '"T (degC)"', '"Tpot (K)"', '"Tdew (degC)"', '"rh (%)"', '"VPmax (mbar)"', '"VPact (mbar)"', '"VPdef (mbar)"', '"sh (g/kg)"', '"H2OC (mmol/mol)"', '"rho (g/m**3)"', '"wv (m/s)"', '"max. wv (m/s)"', '"wd (deg)"']
420551
<class 'list'>
Let's convert all of these 420,551 lines of data into a Numpy array:

# 接下来,将 420 551 行数据转换成一个 Numpy 数组。
# 代码清单 6-29 解析数据
import numpy as np

float_data = np.zeros((len(lines), len(header) - 1))
for i, line in enumerate(lines):
    values = [float(x) for x in line.split(',')[1:]]
    float_data[i, :] = values
    
#     比如,温度随时间的变化如图 6-18 所示(单位:摄氏度)。在这张图中,你可以清楚地看
# 到温度每年的周期性变化。
# 代码清单 6-30 绘制温度时间序列
# 接下来,将 420 551 行数据转换成一个 Numpy 数组。
# 代码清单 6-29 解析数据
import numpy as np
​
float_data = np.zeros((len(lines), len(header) - 1))
for i, line in enumerate(lines):
    values = [float(x) for x in line.split(',')[1:]]
    float_data[i, :] = values
    
#     比如,温度随时间的变化如图 6-18 所示(单位:摄氏度)。在这张图中,你可以清楚地看
# 到温度每年的周期性变化。
# 代码清单 6-30 绘制温度时间序列

For instance, here is the plot of temperature (in degrees Celsius) over time:

from matplotlib import pyplot as plt
​
temp = float_data[:, 1]  # temperature (in degrees Celsius)
plt.plot(range(len(temp)), temp)
plt.show()

在这里插入图片描述

# 图 6-19 给出了前 10 天温度数据的图像。因为每 10 分钟记录一个数据,所以每天有 144 个
# 数据点。
# 代码清单 6-31 绘制前 10 天的温度时间序列

plt.plot(range(1440), temp[:1440])
plt.show()

# 在这张图中,你可以看到每天的周期性变化,尤其是最后 4 天特别明显。另外请注意,这
# 10 天一定是来自于很冷的冬季月份。
# 如果你想根据过去几个月的数据来预测下个月的平均温度,那么问题很简单,因为数据具
# 有可靠的年度周期性。但从几天的数据来看,温度看起来更混乱一些。以天作为观察尺度,这
# 个时间序列是可以预测的吗?我们来寻找这个问题的答案

在这里插入图片描述

mean = float_data[:200000].mean(axis=0)
float_data -= mean
std = float_data[:200000].std(axis=0)
float_data /= std

def generator(data, lookback, delay, min_index, max_index,
              shuffle=False, batch_size=128, step=6):
    
#     ‰ data:浮点数数据组成的原始数组,在代码清单 6-32 中将其标准化。
# ‰ lookback:输入数据应该包括过去多少个时间步。
# ‰ delay:目标应该在未来多少个时间步之后。
# ‰ min_index 和 max_index: data 数组中的索引,用于界定需要抽取哪些时间步。这有
# 助于保存一部分数据用于验证、另一部分用于测试。
# ‰ shuffle:是打乱样本,还是按顺序抽取样本。
# ‰ batch_size:每个批量的样本数。
# ‰ step:数据采样的周期(单位:时间步)

    if max_index is None:
        max_index = len(data) - delay - 1
    i = min_index + lookback
    while 1:
        if shuffle:
            
            rows = np.random.randint(
                min_index + lookback, max_index, size=batch_size)
            
#             numpy.random.randint(low, high=None, size=None, dtype='l'):
# 生成一个整数或N维整数数组,取数范围:若high不为None时,取[low,high)之间随机整数,否则取值[0,low)之间随机整数。
        else:
            if i + batch_size >= max_index:
                i = min_index + lookback
            rows = np.arange(i, min(i + batch_size, max_index))
            i += len(rows)

        samples = np.zeros((len(rows),
                           lookback // step,
                           data.shape[-1]))
        targets = np.zeros((len(rows),))
        for j, row in enumerate(rows):
            indices = range(rows[j] - lookback, rows[j], step)
            samples[j] = data[indices]
            targets[j] = data[rows[j] + delay][1]
        yield samples, targets


# 下面,我们使用这个抽象的 generator 函数来实例化三个生成器:一个用于训练,一个用6.3 循环神经网络的高级用法  177
# 1 2 3 4 5 6 7 8 9
# 于验证,还有一个用于测试。每个生成器分别读取原始数据的不同时间段:训练生成器读取前
# 200 000 个时间步,验证生成器读取随后的 100 000 个时间步,测试生成器读取剩下的时间步。
# 代码清单 6-34 准备训练生成器、验证生成器和测试生成器

lookback = 1440
step = 6
delay = 144
batch_size = 128

train_gen = generator(float_data,
                      lookback=lookback,
                      delay=delay,
                      min_index=0,
                      max_index=200000,
                      shuffle=True,
                      step=step, 
                      batch_size=batch_size)
val_gen = generator(float_data,
                    lookback=lookback,
                    delay=delay,
                    min_index=200001,
                    max_index=300000,
                    step=step,
                    batch_size=batch_size)
test_gen = generator(float_data,
                     lookback=lookback,
                     delay=delay,
                     min_index=300001,
                     max_index=None,
                     step=step,
                     batch_size=batch_size)

# This is how many steps to draw from `val_gen`
# in order to see the whole validation set:
# 为了查看整个验证集,需要
# 从 val_gen 中抽取多少次
val_steps = (300000 - 200001 - lookback) // batch_size

# This is how many steps to draw from `test_gen`
# in order to see the whole test set:
test_steps = (len(float_data) - 300001 - lookback) // batch_size
# 为了查看整个测试集,需要从
# test_gen 中抽取多少次

# 6.3.3 一种基于常识的、非机器学习的基准方法
# 开始使用黑盒深度学习模型解决温度预测问题之前,我们先尝试一种基于常识的简单方法。
# 它可以作为合理性检查,还可以建立一个基准,更高级的机器学习模型需要打败这个基准才能
# 表现出其有效性。面对一个尚没有已知解决方案的新问题时,这种基于常识的基准方法很有用。
# 一个经典的例子就是不平衡的分类任务,其中某些类别比其他类别更常见。如果数据集中包含
# 90% 的类别 A 实例和 10% 的类别 B 实例,那么分类任务的一种基于常识的方法就是对新样本
# 始终预测类别“A”。这种分类器的总体精度为 90%,因此任何基于学习的方法在精度高于 90%
# 时才能证明其有效性。有时候,这样基本的基准方法可能很难打败。
# 为了查看整个验证集,需要
# 从 val_gen 中抽取多少次
# 为了查看整个测试集,需要从
# test_gen 中抽取多少次178  第 6 章 深度学习用于文本和序列
# 本例中,我们可以放心地假设,温度时间序列是连续的(明天的温度很可能接近今天的温
# 度),并且具有每天的周期性变化。因此,一种基于常识的方法就是始终预测 24 小时后的温度
# 等于现在的温度。我们使用平均绝对误差(MAE)指标来评估这种方法
# 求平均值
np.mean(np.abs(preds - targets))


# 下面是评估的循环代码。
# 代码清单 6-35 计算符合常识的基准方法的 MAEdef evaluate_naive_method():
    batch_maes = []
    for step in range(val_steps):
        samples, targets = next(val_gen)
        preds = samples[:, -1, 1]
        mae = np.mean(np.abs(preds - targets))
        batch_maes.append(mae)
    print(np.mean(batch_maes))
    
evaluate_naive_method()
0.2897359729905486
# 得到的 MAE 为 0.29。因为温度数据被标准化成均值为 0、标准差为 1,所以无法直接对这
# 个值进行解释。它转化成温度的平均绝对误差为 0.29×temperature_std 摄氏度,即 2.57℃。
# 代码清单 6-36 将 MAE 转换成摄氏温度误差
celsius_mae = 0.29 * std[1]
print(celsius_mae)
# 这个平均绝对误差还是相当大的。接下来的任务是利用深度学习知识来改进结果
2.5672247338393395
It yields a MAE of 0.29. Since our temperature data has been normalized to be centered on 0 and have a standard deviation of one, this number is not immediately interpretable. It translates to an average absolute error of 0.29 * temperature_std degrees Celsius, i.e. 2.57˚C. That's a fairly large average absolute error -- now the game is to leverage our knowledge of deep learning to do better.


# 6.3.4 一种基本的机器学习方法
# 在尝试机器学习方法之前,建立一个基于常识的基准方法是很有用的;同样,在开始研究
# 复杂且计算代价很高的模型(比如 RNN)之前,尝试使用简单且计算代价低的机器学习模型也
# 是很有用的,比如小型的密集连接网络。这可以保证进一步增加问题的复杂度是合理的,并且
# 会带来真正的好处。
# 代码清单 6-37 给出了一个密集连接模型,首先将数据展平,然后通过两个 Dense 层并运行。
# 注意,最后一个 Dense 层没有使用激活函数,这对于回归问题是很常见的。我们使用 MAE 作
# 为损失。评估数据和评估指标都与常识方法完全相同,所以可以直接比较两种方法的结果。
# 代码清单 6-37 训练并评估一个密集连接模型

from keras.models import Sequential
from keras import layers
from keras.optimizers import RMSprop
# 代码清单 6-37 给出了一个密集连接模型,首先将数据展平,然后通过两个 Dense 层并运行。
model = Sequential()
model.add(layers.Flatten(input_shape=(lookback // step, float_data.shape[-1])))
model.add(layers.Dense(32, activation='relu'))
model.add(layers.Dense(1))

# 注意,最后一个 Dense 层没有使用激活函数,这对于回归问题是很常见的。我们使用 MAE 作
# 为损失。评估数据和评估指标都与常识方法完全相同,所以可以直接比较两种方法的结果。
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)    


Epoch 1/20
500/500 [==============================] - 20s 40ms/step - loss: 1.5716 - val_loss: 0.8749
Epoch 2/20
500/500 [==============================] - 20s 40ms/step - loss: 0.4991 - val_loss: 0.3975
Epoch 3/20
500/500 [==============================] - 22s 43ms/step - loss: 0.3012 - val_loss: 0.3110
Epoch 4/20
500/500 [==============================] - 21s 41ms/step - loss: 0.2679 - val_loss: 0.3274
Epoch 5/20
500/500 [==============================] - 20s 41ms/step - loss: 0.2560 - val_loss: 0.3293
Epoch 6/20
500/500 [==============================] - 20s 39ms/step - loss: 0.2452 - val_loss: 0.3136
Epoch 7/20
500/500 [==============================] - 22s 43ms/step - loss: 0.2382 - val_loss: 0.3222
Epoch 8/20 

Epoch 13/20
500/500 [==============================] - 22s 44ms/step - loss: 0.2128 - val_loss: 0.3230
Epoch 14/20
500/500 [==============================] - 19s 38ms/step - loss: 0.2109 - val_loss: 0.3192
Epoch 15/20
500/500 [==============================] - 19s 38ms/step - loss: 0.2085 - val_loss: 0.3341
Epoch 16/20
500/500 [==============================] - 20s 39ms/step - loss: 0.2061 - val_loss: 0.3450
Epoch 17/20
500/500 [==============================] - 19s 37ms/step - loss: 0.2042 - val_loss: 0.3459
Epoch 18/20
500/500 [==============================] - 19s 38ms/step - loss: 0.2033 - val_loss: 0.3525
Epoch 19/20
500/500 [==============================] - 19s 38ms/step - loss: 0.2011 - val_loss: 0.3341
Epoch 20/20
500/500 [==============================] - 19s 39ms/step - loss: 0.1992 - val_loss: 0.3365

# 我们来显示验证和训练的损失曲线(见图 6-20)。
# # 代码清单 6-38 绘制结果

import matplotlib.pyplot as plt

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(loss))

plt.figure()

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()
# 图 6-20 简单的密集连接网络在耶拿温度预测任务上的训练损失和验证损失
print(type(history.history))

在这里插入图片描述

<class 'dict'>
print(history.history)
{'val_loss': [0.8748725497482347, 0.3975294645299217, 0.3109697792993953, 0.32736822754454703, 0.32925783149578325, 0.3136130665345372, 0.3221883660155713, 0.3522020755638459, 0.32485968480552746, 0.3193821293605862, 0.3482952474704877, 0.34307795770766675, 0.32300440624104365, 0.3191545883966283, 0.33410712029247197, 0.34500235922256745, 0.3459017112153559, 0.35247658667855825, 0.3340611231497577, 0.3364521464519445], 'loss': [1.571558004796505, 0.4991003686189652, 0.3011927672326565, 0.2678608466684818, 0.25595426523685455, 0.24517172515392305, 0.23824044767022132, 0.23298490041494369, 0.22821045821905137, 0.2227226406633854, 0.2185874055325985, 0.21574989056587218, 0.21279067119956016, 0.210872103959322, 0.20845433309674263, 0.20609600335359574, 0.20415313729643822, 0.20333791476488114, 0.20114947184920312, 0.19921788474917412]}


# 6.3.5 第一个循环网络基准
# 第一个全连接方法的效果并不好,但这并不意味着机器学习不适用于这个问题。前一个方
# 法首先将时间序列展平,这从输入数据中删除了时间的概念。我们来看一下数据本来的样子:
# 它是一个序列,其中因果关系和顺序都很重要。我们将尝试一种循环序列处理模型,它应该特
# 别适合这种序列数据,因为它利用了数据点的时间顺序,这与第一个方法不同。
# 我们将使用 Chung 等人在 2014 年开发的 GRU 层 a,而不是上一节介绍的 LSTM 层。门控循
# 环单元(GRU, gated recurrent unit)层的工作原理与 LSTM 相同。但它做了一些简化,因此运
# 行的计算代价更低(虽然表示能力可能不如 LSTM)。机器学习中到处可以见到这种计算代价与
# 表示能力之间的折中。
# 代码清单 6-39 训练并评估一个基于 GRU 的模型from keras.models import Sequential
from keras import layers
from keras.optimizers import RMSprop
​
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)
Epoch 1/20
500/500 [==============================] - 169s - loss: 0.3216 - val_loss: 0.2738
Epoch 2/20
500/500 [==============================] - 168s - loss: 0.2846 - val_loss: 0.2654
Epoch 3/20
500/500 [==============================] - 168s - loss: 0.2772 - val_loss: 0.2653
Epoch 4/20
500/500 [==============================] - 168s - loss: 0.2707 - val_loss: 0.2663
Epoch 5/20
500/500 [==============================] - 168s - loss: 0.2643 - val_loss: 0.2680
Epoch 6/20
500/500 [==============================] - 169s - loss: 0.2584 - val_loss: 0.2644
Epoch 7/20
500/500 [==============================] - 168s - loss: 0.2553 - val_loss: 0.2658
Epoch 8/20
500/500 [==============================] - 168s - loss: 0.2508 - val_loss: 0.2672
Epoch 9/20
500/500 [==============================] - 165s - loss: 0.2455 - val_loss: 0.2728
Epoch 10/20
500/500 [==============================] - 164s - loss: 0.2423 - val_loss: 0.2801
Epoch 11/20
500/500 [==============================] - 164s - loss: 0.2386 - val_loss: 0.2750
Epoch 12/20
500/500 [==============================] - 164s - loss: 0.2364 - val_loss: 0.2774
Epoch 13/20
500/500 [==============================] - 165s - loss: 0.2313 - val_loss: 0.2810
Epoch 14/20
500/500 [==============================] - 165s - loss: 0.2283 - val_loss: 0.2855
Epoch 15/20
500/500 [==============================] - 165s - loss: 0.2246 - val_loss: 0.2879
Epoch 16/20
500/500 [==============================] - 165s - loss: 0.2194 - val_loss: 0.2894
Epoch 17/20
500/500 [==============================] - 164s - loss: 0.2181 - val_loss: 0.2941
Epoch 18/20
500/500 [==============================] - 164s - loss: 0.2139 - val_loss: 0.2982
Epoch 19/20
500/500 [==============================] - 164s - loss: 0.2100 - val_loss: 0.2973
Epoch 20/20
500/500 [==============================] - 165s - loss: 0.2074 - val_loss: 0.3034

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(loss))

plt.figure()

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()
# 图 6-21 显示了模型结果。效果好多了!远优于基于常识的基准方法。这证明了机器学习的
# 价值,也证明了循环网络与序列展平的密集网络相比在这种任务上的优势。

# 图 6-21 使用 GRU 在耶拿温度预测任务上的训练损失和验证损失
# 新的验证 MAE 约为 0.265(在开始显著过拟合之前),反标准化转换成温度的平均绝对误
# 差为 2.35℃。与最初的误差 2.57℃相比,这个结果确实有所提高,但可能仍有改进的空间。



在这里插入图片描述

# 6.3.6 使用循环 dropout 来降低过拟合
# 从训练和验证曲线中可以明显看出,模型出现过拟合:几轮过后,训练损失和验证损失就
# 开始显著偏离。我们已经学过降低过拟合的一种经典技术——dropout,即将某一层的输入单
# 元随机设为 0,其目的是打破该层训练数据中的偶然相关性
# Keras
# 的每个循环层都有两个与 dropout 相关的参数:一个是 dropout,它是一个浮点数,指定该层
# 输入单元的 dropout 比率;另一个是 recurrent_dropout,指定循环单元的 dropout 比率。我
# 们向 GRU 层中添加 dropout 和循环 dropout,看一下这么做对过拟合的影响。因为使用 dropout
# 正则化的网络总是需要更长的时间才能完全收敛,所以网络训练轮次增加为原来的 2 倍。

# 代码清单 6-40 训练并评估一个使用 dropout 正则化的基于 GRU 的模型

from keras.models import Sequential
from keras import layers
from keras.optimizers import RMSprop

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)
# 结果如图 6-22 所示。成功!前 30 个轮次不再过拟合。不过,虽然评估分数更加稳定,但
# 最佳分数并没有比之前低很多。

Epoch 1/40
500/500 [==============================] - 171s - loss: 0.3526 - val_loss: 0.2740
Epoch 2/40
500/500 [==============================] - 170s - loss: 0.3138 - val_loss: 0.2742
Epoch 3/40
500/500 [==============================] - 170s - loss: 0.3065 - val_loss: 0.2692
Epoch 4/40
500/500 [==============================] - 170s - loss: 0.3033 - val_loss: 0.2694
Epoch 5/40
500/500 [==============================] - 170s - loss: 0.3006 - val_loss: 0.2695
Epoch 6/40
500/500 [==============================] - 170s - loss: 0.2990 - val_loss: 0.2709
Epoch 7/40
500/500 [==============================] - 170s - loss: 0.2955 - val_loss: 0.2667
Epoch 8/40
500/500 [==============================] - 170s - loss: 0.2942 - val_loss: 0.2671
Epoch 9/40
500/500 [==============================] - 171s - loss: 0.2940 - val_loss: 0.2649
Epoch 10/40

Epoch 34/40
500/500 [==============================] - 170s - loss: 0.2753 - val_loss: 0.2711
Epoch 35/40
500/500 [==============================] - 170s - loss: 0.2727 - val_loss: 0.2658
Epoch 36/40
500/500 [==============================] - 170s - loss: 0.2724 - val_loss: 0.2664
Epoch 37/40
500/500 [==============================] - 170s - loss: 0.2725 - val_loss: 0.2661
Epoch 38/40
500/500 [==============================] - 171s - loss: 0.2725 - val_loss: 0.2633
Epoch 39/40
500/500 [==============================] - 170s - loss: 0.2726 - val_loss: 0.2695
Epoch 40/40
500/500 [==============================] - 170s - loss: 0.2727 - val_loss: 0.2719


loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(loss))

plt.figure()

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()

# 图 6-22 使用 dropout 正则化的 GRU 在耶拿温度预测任务上的训练损失和验证损失

在这里插入图片描述

# 6.3.7 循环层堆叠
# 模型不再过拟合,但似乎遇到了性能瓶颈,所以我们应该考虑增加网络容量。回想一下机
# 器学习的通用工作流程:增加网络容量通常是一个好主意,直到过拟合变成主要的障碍(假设
# 你已经采取基本步骤来降低过拟合,比如使用 dropout)。只要过拟合不是太严重,那么很可能
# 是容量不足的问题。
# 增加网络容量的通常做法是增加每层单元数或增加层数。循环层堆叠(recurrent layer
# stacking)是构建更加强大的循环网络的经典方法,例如,目前谷歌翻译算法就是 7 个大型6.3 循环神经网络的高级用法  183
# 1 2 3 4 5 6 7 8 9
# LSTM 层的堆叠——这个架构很大。
# 在 Keras 中逐个堆叠循环层,所有中间层都应该返回完整的输出序列(一个 3D 张量),而
# 不是只返回最后一个时间步的输出。这可以通过指定 return_sequences=True 来实现。
# 代码清单 6-41 训练并评估一个使用 dropout 正则化的堆叠 GRU 模型

from keras.models import Sequential
from keras import layers
from keras.optimizers import RMSprop

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)

# 结果如图 6-23 所示。可以看到,添加一层的确对结果有所改进,但并不显著。我们可以得
# 出两个结论。
# ‰ 因为过拟合仍然不是很严重,所以可以放心地增大每层的大小,以进一步改进验证损失。
# 但这么做的计算成本很高。
# ‰ 添加一层后模型并没有显著改进,所以你可能发现,提高网络能力的回报在逐渐减小。



Epoch 1/40
500/500 [==============================] - 376s 751ms/step - loss: 0.3337 - val_loss: 0.2813
Epoch 2/40
500/500 [==============================] - 319s 638ms/step - loss: 0.3129 - val_loss: 0.2726
Epoch 3/40
500/500 [==============================] - 316s 633ms/step - loss: 0.3060 - val_loss: 0.2781
Epoch 4/40
500/500 [==============================] - 317s 635ms/step - loss: 0.3029 - val_loss: 0.2708
Epoch 5/40
500/500 [==============================] - 318s 635ms/step - loss: 0.2974 - val_loss: 0.2654
Epoch 6/40
500/500 [==============================] - 330s 659ms/step - loss: 0.2938 - val_loss: 0.2712
Epoch 7/40
500/500 [==============================] - 331s 661ms/step - loss: 0.2911 - val_loss: 0.2619
Epoch 8/40
500/500 [==============================] - 353s 706ms/step - loss: 0.2896 - val_loss: 0.2643
Epoch 9/40

500/500 [==============================] - 358s 717ms/step - loss: 0.2742 - val_loss: 0.2668
Epoch 18/40
500/500 [==============================] - 385s 770ms/step - loss: 0.2743 - val_loss: 0.2668
Epoch 19/40
500/500 [==============================] - 379s 758ms/step - loss: 0.2718 - val_loss: 0.2671
Epoch 20/40
500/500 [==============================] - 395s 789ms/step - loss: 0.2719 - val_loss: 0.2710
Epoch 21/40
500/500 [==============================] - 404s 807ms/step - loss: 0.2698 - val_loss: 0.2703
Epoch 22/40
499/500 [============================>.] - ETA: 0s - loss: 0.2697

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(loss))

plt.figure()

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()

# 结果如图 6-23 所示。可以看到,添加一层的确对结果有所改进,但并不显著。我们可以得
# 出两个结论。
# ‰ 因为过拟合仍然不是很严重,所以可以放心地增大每层的大小,以进一步改进验证损失。
# 但这么做的计算成本很高。
# ‰ 添加一层后模型并没有显著改进,所以你可能发现,提高网络能力的回报在逐渐减小。

在这里插入图片描述

# 代码清单 6-44 训练一个双向 GRU
def reverse_order_generator(data, lookback, delay, min_index, max_index,
                            shuffle=False, batch_size=128, step=6):
    if max_index is None:
        max_index = len(data) - delay - 1
    i = min_index + lookback
    while 1:
        if shuffle:
            rows = np.random.randint(
                min_index + lookback, max_index, size=batch_size)
        else:
            if i + batch_size >= max_index:
                i = min_index + lookback
            rows = np.arange(i, min(i + batch_size, max_index))
            i += len(rows)

        samples = np.zeros((len(rows),
                           lookback // step,
                           data.shape[-1]))
        targets = np.zeros((len(rows),))
        for j, row in enumerate(rows):
            indices = range(rows[j] - lookback, rows[j], step)
            samples[j] = data[indices]
            targets[j] = data[rows[j] + delay][1]
        yield samples[:, ::-1, :], targets
        
train_gen_reverse = reverse_order_generator(
    float_data,
    lookback=lookback,
    delay=delay,
    min_index=0,
    max_index=200000,
    shuffle=True,
    step=step, 
    batch_size=batch_size)
val_gen_reverse = reverse_order_generator(
    float_data,
    lookback=lookback,
    delay=delay,
    min_index=200001,
    max_index=300000,
    step=step,
    batch_size=batch_size)

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_reverse,
                              steps_per_epoch=500,
                              epochs=20,
                              validation_data=val_gen_reverse,
                              validation_steps=val_steps)

# 这个模型的表现与普通 GRU 层差不多一样好。其原因很容易理解:所有的预测能力肯定都
# 来自于正序的那一半网络,因为我们已经知道,逆序的那一半在这个任务上的表现非常糟糕(本
# 例同样是因为,最近的数据比久远的数据更加重要)。


Epoch 1/20
500/500 [==============================] - 172s - loss: 0.4781 - val_loss: 0.4797
Epoch 2/20
500/500 [==============================] - 171s - loss: 0.4529 - val_loss: 0.4679
Epoch 3/20
500/500 [==============================] - 170s - loss: 0.4071 - val_loss: 0.4536
Epoch 4/20
500/500 [==============================] - 171s - loss: 0.3670 - val_loss: 0.4398


Epoch 16/20
500/500 [==============================] - 170s - loss: 0.2437 - val_loss: 0.4097
Epoch 17/20
500/500 [==============================] - 170s - loss: 0.2416 - val_loss: 0.4163
Epoch 18/20
500/500 [==============================] - 170s - loss: 0.2373 - val_loss: 0.4174
Epoch 19/20
500/500 [==============================] - 170s - loss: 0.2355 - val_loss: 0.4064
Epoch 20/20
500/500 [==============================] - 170s - loss: 0.2313 - val_loss: 0.4261



loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(loss))

plt.figure()

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()
# 这个模型的表现与普通 GRU 层差不多一样好。其原因很容易理解:所有的预测能力肯定都
# 来自于正序的那一半网络,因为我们已经知道,逆序的那一半在这个任务上的表现非常糟糕(本
# 例同样是因为,最近的数据比久远的数据更加重要)。
6.3.9 更多尝试
为了提高温度预测问题的性能,你还可以尝试下面这些方法。
‰ 在堆叠循环层中调节每层的单元个数。当前取值在很大程度上是任意选择的,因此可能
不是最优的。
‰ 调节 RMSprop 优化器的学习率。
‰ 尝试使用 LSTM 层代替 GRU 层。
‰ 在循环层上面尝试使用更大的密集连接回归器,即更大的 Dense 层或 Dense 层的堆叠。
‰ 不要忘记最后在测试集上运行性能最佳的模型(即验证 MAE 最小的模型)。否则,你开
发的网络架构将会对验证集过拟合。
正如前面所说,深度学习是一门艺术而不是科学。我们可以提供指导,对于给定问题哪些
方法可能有用、哪些方法可能没用,但归根结底,每个问题都是独一无二的,你必须根据经验
对不同的策略进行评估。目前没有任何理论能够提前准确地告诉你,应该怎么做才能最优地解
决问题。你必须不断迭代
  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值