LSTM和GRU网络的高级运用实例

接着我们看看LSTM网络更复杂的运用,那就是用来预测气温。在这个例子中,我们可以使用很多高级数据处理功能,例如我们可以看到如何使用"recurrent dropout"来预防过度拟合,第二我们会把多个LTSM网络层堆积起来,增强怎个网络的解析能力,第三我们还会使用到双向反复性网络,它会把同一种信息以不同的形式在网络内传递,从而增加网络对信息的理解。

我们首先要做的重要一步,那就是获取数据,打开迅雷,输入下面URL以便下载原始数据:
https://s3.amazonaws.com/keras-datasets/jena_climate_2009_2016.csv.zip
下载后解压,我们就可以用代码将其加载到内存中:

import os
data_dir = '/Users/chenyi/Documents/人工智能/'
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))

上面代码运行后结果如下:
['"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
数据总共有420551个条目,每个条目的内容如上面显示所示。接下来我们把所有条目转换成可以被处理的数组格式:

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

from matplotlib import pyplot as plt
temp = float_data[:, 1] 
plt.plot(range(len(temp)), temp)

上面代码转换数据后,将数据描绘出来,结果如下:

屏幕快照 2018-09-11 下午5.18.57.png

从上图可以看出,温度呈现一种周期性的变化。由于数据记录了2009年到2016年间每天的温度变化,并且温度记录是每十分钟完成一次,于是一天就有144个记录点,我们截取10天的温度信息,也就是总共1440个数据点,我们把这些数据图绘制出来看看:

plt.plot(range(1440), temp[:1440])
屏幕快照 2018-09-11 下午5.22.18.png

我们将构造一个网络,分析当前的天气数据,然后预测未来天气的可能状况。当前数据格式比较理想,问题在于不同数据对应的单位不一样,例如气温和气压采用不同的计量单位,因此我们需要对数据做归一化处理。我们打算使用前200000个数据条目做训练数据,因此我们仅仅对这部分数据进行归一化:

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

接着我们用代码把数据分成三组,一组用来训练网络,一组作为校验数据,最后一组作为测试数据:

'''
假设现在是1点,我们要预测2点时的气温,由于当前数据记录的是每隔10分钟时的气象数据,1点到2点
间隔1小时,对应6个10分钟,这个6对应的就是delay

要训练网络预测温度,就需要将气象数据与温度建立起对应关系,我们可以从1点开始倒推10天,从过去
10天的气象数据中做抽样后,形成训练数据。由于气象数据是每10分钟记录一次,因此倒推10天就是从
当前时刻开始回溯1440条数据,这个1440对应的就是lookback

我们无需把全部1440条数据作为训练数据,而是从这些数据中抽样,每隔6条取一条,
因此有1440/6=240条数据会作为训练数据,这就是代码中的lookback//step

于是我就把1点前10天内的抽样数据作为训练数据,2点是的气温作为数据对应的正确答案,由此
可以对网络进行训练
'''
def 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, targets

有了上面的数据处理函数,我们就可以调用它将原始数据分成三组,一组用于训练,一组用于校验,最后一组用于测试,代码如下:

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=400000,
                   step=step, batch_size=batch_size)

val_steps = (300000 - 200001 - lookback) // batch_size
test_steps = (len(float_data) - 300001 - lookback) //batch_size

神经网络要有效,它就必须做的比人预测的准确度高。对于温度预测而言,人本能直觉会认为温度具有连续性,也就是下一刻的温度应该会比当前时刻的温度差别不了多少,根据人的经验,不可能现在温度是20度,然后下一刻温度里面变成60度,或零下20度,只有世界末日才会出现这样的情形,于是如果要让人来预测,他通常会认为接下24小时内的温度与当前温度是一样的,基于这种逻辑,我们计算一下这种预测方法的准确度:

def  evaluate_naive_method():
    batch_maes = []
    for step in range(val_steps):
        samples, targets = next(val_gen)
        #preds是当前时刻温度,targets是下一小时温度
        preds = samples[:, -1, 1]
        mae = np.mean(np.abs(preds - targets))
        batch_maes.append(mae)
    print(np.mean(batch_maes))
evaluate_naive_method()

上面代码运行后得到结果为0.29,注意到前面我们对数据的每一列都做了归一化处理,因此0.29的解释应该是0.29*std[1]=2.57,也就是说如果我们用前一个小时的温度来预测下一个小时的温度,那么误差是2.57度,这个误差不算小。如果我们的网络要真有效,那么它预测的温度误差应该比2.57要小,小得越多就越有效。

为了比较不同网络模型的效果,我们将分别构造几个不同类别的网络然后分别看看他们的预测效果,首先我们先建立前面几章讲过的全连接网络看看效果如何:

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

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))

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)

我们把上面代码运行结果绘制出来看看:

import matplotlib.pyplot as plt
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(loss) + 1)
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.show()

上面代码运行后结果如下:

屏幕快照 2018-09-14 上午11.43.26.png

从上图看,网络预测的结果在0.4和0.3之间,也就是说网络预测的精确度还不如人的直觉好。当前网络存在一个问题,就是它把有时间次序的数据条目一下子碾平,从而使得数据之间的时间联系消失了,可是时间信息对结果的预测非常重要,如果像上面做法,先把多条数据集合在一起传入网络,就会使得数据的时间特性消失,而时间是影响判断结果的一个重要变量。

这回我们使用反复性神经网络,因为这样的网络能够利用数据间存在的时间联系来分析数据潜在规律进而提升预测的准确性,这次我们使用的反复性网络叫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)

代码运行后,我们绘制出来的训练效果如下图:

屏幕快照 2018-09-17 下午3.35.21.png

从上图我们看到,蓝色实线在循环次数为4的时候,网络对校验数据判断的误差达到了接近0.26,这已经远远好于由人的直觉猜测的0.29错误率,这次改进相当明显。这次改进显示出深度学习对数据模式的抽取能力比人的直觉要好很多,同时也表明反复性网络对数据的识别能力要好于我们以前开发的全连接网络。0.26转换为错误值大概是2.3左右,也就是网络对一小时后的温度预测与实际值相差2.3度。

从上图我们也看出,网络对训练数据的识别准确率不断提升,对校验数据的识别准确率越来越差,两种分道扬镳很明显,也就是说网络出现了过度拟合。以前我们处理过度拟合时的办法是把权重随机清零,但是这种方式不能直接使用到反复性网络上,因为网络中很多链路权重在用于记录不同数据在时间上的内在关联,如果随机把这些权重清零,就会破坏网络对数据在时间上关联性的认识,从而造成准确率的下降。在2015年时研究贝叶斯深度学习的博士生Yarin Gal 发现了处理反复性网络过度拟合的方法,那是每次都将同样的若干比例权重清零,而不是随机清零,而这种清零机制内内嵌在keras框架中。相关代码如下:

model = Sequential()
model.add(layers.GRU(32, dropout=0.2, recurrent_dropout=0.2, 
                    input_shape=(None, float_data.shape[-1])))
model.add(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)

上面代码运行后,网络的训练效果如下:

屏幕快照 2018-09-18 下午4.08.09.png

从上图实现和点线的发展趋势不断重合,也就是网络对校验数据的识别正确率跟训练数据的正确率一样不断提高,因此过度拟合的现象消失了。至此我们就把LSTM和GRU这两种反复性网络在具体实例上的应用展示完成,如果你运行过上面代码会发现,普通CPU的机子运行代码起来效率很慢,它再次证明了算力和数据是人工智能中两道极难迈过去的坎儿。

更详细的讲解和代码调试演示过程,请点击链接

更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:


这里写图片描述
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容

  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 31,916评论 2 89
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,977评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,710评论 2 59
  • HashMap链表转为红黑树的临界值为8的原因 参考文章:深入理解哈希表Jdk1.8中的HashMap实现原理Ja...
    青年马土豆阅读 1,637评论 0 2
  • 今天天气不错心情也好 但是没有暴饮暴食哈 还是管住了自己的嘴 总感觉自己身体轻快了 没有以前那么沉重 这是一个好现...
    没困落夫斯基阅读 248评论 1 1