1. 程式人生 > >LSTM和GRU網路的高階運用例項

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的機子執行程式碼起來效率很慢,它再次證明了算力和資料是人工智慧中兩道極難邁過去的坎兒。

更詳細的講解和程式碼除錯演示過程,請點選連結

更多技術資訊,包括作業系統,編譯器,面試演算法,機器學習,人工智慧,請關照我的公眾號:
這裡寫圖片描述