1? RNN神經(jīng)網(wǎng)絡(luò)底層邏輯介紹

1.1 輸入層、隱藏層和輸出層

1.2 損失函數(shù)定義


根據(jù)誤差函數(shù)性質(zhì),對于回歸問題,大多數(shù)建立的是基于距離形式的均方誤差函數(shù)或者絕對誤差函數(shù),如果是分類問題,我們一般會選擇交叉熵這類函數(shù)!
?

1.3 梯度下降與鏈?zhǔn)椒▌t求導(dǎo)


這里的推導(dǎo)比較復(fù)雜,為了讓大家能理解到整個模型思想而不是存粹學(xué)術(shù)研究,只做重點(diǎn)介紹!且激活函數(shù)簡化!

那么同理,很容易我們將解決:

2  對于梯度消散(爆炸)的原理解釋

一般 RNN 模型,會因為在鏈?zhǔn)椒▌t中存在梯度消散(爆炸)的問題,所以我們要發(fā)展新的變種來解決這種問題,那么這梯度問題到底在哪呢?仔細(xì)發(fā)現(xiàn)在上一節(jié)的(*)式推導(dǎo)過程中,對于隱藏層求導(dǎo),我們繼續(xù)對(*)式改寫可得:

3  LSTM底層理論介紹

為了更好的捕獲時序中間隔較大的依賴關(guān)系,基于門控制的長短記憶網(wǎng)絡(luò)(LSTM)誕生了!

所謂“門”結(jié)構(gòu)就是用來去除或者增加信息到細(xì)胞狀態(tài)的能力。這里的細(xì)胞狀態(tài)是核心,它屬于隱藏層,類似于傳送帶,在整個鏈上運(yùn)行,信息在上面流傳保持不變會變得很容易!
上圖 非常形象生動描繪了 LSTM 核心的“三門結(jié)構(gòu)”。紅色圈就是所謂的遺忘門,那么在??時刻如下公式表示(如果我們真理解了 RNN 邏輯,LSTM 理解起來將變得比較輕松):

藍(lán)圈輸入門有

綠圈輸出門有

3.1 sigmoid激活函數(shù)的意義

4  建模預(yù)測存在“右偏移”怎么辦!

為了做對比實(shí)驗,我們還會選擇之前時序文章所對應(yīng)的實(shí)際銷量數(shù)據(jù)!我們將基于 keras 模塊構(gòu)建自己的 LSTM 網(wǎng)絡(luò)進(jìn)行時序預(yù)測。

4.1 構(gòu)建一般LSTM模型,當(dāng)我們選擇步長為1時,先給出結(jié)果如下

正常建立 LSTM 模型預(yù)測會出現(xiàn)如上預(yù)測值右偏現(xiàn)象,盡管 r2 或者 MSE 很好,但這建立的模型其實(shí)是無效模型!


4.2 原因與改進(jìn)


當(dāng)模型傾向于把上一時刻的真實(shí)值作為下一時刻的預(yù)測值,導(dǎo)致兩條曲線存在滯后性,也就是真實(shí)值曲線滯后于預(yù)測值曲線,如上圖 那樣。之所以會這樣,是因為序列存在自相關(guān)性,如一階自相關(guān)指的是當(dāng)前時刻的值與其自身前一時刻值之間的相關(guān)性。因此,如果一個序列存在一階自相關(guān),模型學(xué)到的就是一階相關(guān)性。而消除自相關(guān)性的辦法就是進(jìn)行差分運(yùn)算,也就是我們可以將當(dāng)前時刻與前一時刻的差值作為我們的回歸目標(biāo)。
而且從之前文章做的白噪聲檢驗也發(fā)現(xiàn),該序列確實(shí)存在很強(qiáng)的自相關(guān)性!如下圖 所示。

5  改進(jìn)模型輸出

我們看下模型最終輸出結(jié)果:

5.1 經(jīng)典時序模型下的最優(yōu)輸出結(jié)果

此結(jié)果的全局 MSE=4401.02 大于 LSTM 網(wǎng)絡(luò)的 MSE=2521.30,由此可見當(dāng)我們優(yōu)化 LSTM 模型后,一定程度上時序建模比 ARIMA 或者 ARIMA-GARCH 要優(yōu)!
LSTM 預(yù)測理論跟 ARIMA 也是有區(qū)別的,LSTM 主要是基于窗口滑動取數(shù)據(jù)訓(xùn)練來預(yù)測滯后數(shù)據(jù),其中的 cell 機(jī)制會由于權(quán)重共享原因減少一些參數(shù);ARIMA 模型是根據(jù)自回歸理論,建立與自己過去有關(guān)的模型。兩者共同點(diǎn)就是能很好運(yùn)用序列數(shù)據(jù),而且通過不停迭代能無限預(yù)測下去,但預(yù)測模型還是基于短期預(yù)測有效,長期預(yù)測必然會導(dǎo)致偏差很大,而且有可能出現(xiàn)預(yù)測值趨于不變的情況。

6  最終代碼

from keras.callbacks import LearningRateScheduler
from sklearn.metrics import mean_squared_error
from keras.models import Sequential
import matplotlib.pyplot as plt
from keras.layers import Dense
from keras.layers import LSTM
from keras import optimizers
import keras.backend as K
import tensorflow as tf
import pandas as pd
import numpy as np

plt.rcParams['font.sans-serif']=['SimHei']##中文亂碼問題!
plt.rcParams['axes.unicode_minus']=False#橫坐標(biāo)負(fù)號顯示問題!

###初始化參數(shù)
my_seed = 369#隨便給出個隨機(jī)種子
tf.random.set_seed(my_seed)##運(yùn)行tf才能真正固定隨機(jī)種子

sell_data = np.array([2800,2811,2832,2850,2880,2910,2960,3023,3039,3056,3138,3150,3198,3100,3029,2950,2989,3012,3050,3142,3252,3342,3365,3385,3340,3410,3443,3428,3554,3615,3646,3614,3574,3635,3738,3764,3788,3820,3840,3875,3900,3942,4000,4021,4055])
num_steps = 3##取序列步長
test_len = 10##測試集數(shù)量長度
S_sell_data = pd.Series(sell_data).diff(1).dropna()##差分
revisedata = S_sell_data.max()
sell_datanormalization = S_sell_data / revisedata##數(shù)據(jù)規(guī)范化

##數(shù)據(jù)形狀轉(zhuǎn)換,很重要??!
def data_format(data, num_steps=3, test_len=5):
    # 根據(jù)test_len進(jìn)行分組
    X = np.array([data[i: i + num_steps]
                  for i in range(len(data) - num_steps)])
    y = np.array([data[i + num_steps]
                  for i in range(len(data) - num_steps)])

    train_size = test_len
    train_X, test_X = X[:-train_size], X[-train_size:]
    train_y, test_y = y[:-train_size], y[-train_size:]
    return train_X, train_y, test_X, test_y

transformer_selldata = np.reshape(pd.Series(sell_datanormalization).values,(-1,1))
train_X, train_y, test_X, test_y = data_format(transformer_selldata, num_steps, test_len)
print('\033[1;38m原始序列維度信息:%s;轉(zhuǎn)換后訓(xùn)練集X數(shù)據(jù)維度信息:%s,Y數(shù)據(jù)維度信息:%s;測試集X數(shù)據(jù)維度信息:%s,Y數(shù)據(jù)維度信息:%s\033[0m'%(transformer_selldata.shape, train_X.shape, train_y.shape, test_X.shape, test_y.shape))

def buildmylstm(initactivation='relu',ininlr=0.001):

    nb_lstm_outputs1 = 128#神經(jīng)元個數(shù)
    nb_lstm_outputs2 = 128#神經(jīng)元個數(shù)
    nb_time_steps = train_X.shape[1]#時間序列長度
    nb_input_vector = train_X.shape[2]#輸入序列
    model = Sequential()
    model.add(LSTM(units=nb_lstm_outputs1, input_shape=(nb_time_steps, nb_input_vector),return_sequences=True))
    model.add(LSTM(units=nb_lstm_outputs2, input_shape=(nb_time_steps, nb_input_vector)))
    model.add(Dense(64, activation=initactivation))
    model.add(Dense(32, activation='relu'))
    model.add(Dense(test_y.shape[1], activation='tanh'))

    lr = ininlr
    adam = optimizers.adam_v2.Adam(learning_rate=lr)
    def scheduler(epoch):##編寫學(xué)習(xí)率變化函數(shù)
        # 每隔epoch,學(xué)習(xí)率減小為原來的1/10
        if epoch % 100 == 0 and epoch != 0:
            lr = K.get_value(model.optimizer.lr)
            K.set_value(model.optimizer.lr, lr * 0.1)
            print('lr changed to {}'.format(lr * 0.1))
        return K.get_value(model.optimizer.lr)
    model.compile(loss='mse', optimizer=adam, metrics=['mse'])##根據(jù)損失函數(shù)性質(zhì),回歸建模一般選用”距離誤差“作為損失函數(shù),分類一般選”交叉熵“損失函數(shù)
    reduce_lr = LearningRateScheduler(scheduler)
    ###數(shù)據(jù)集較少,全參與形式,epochs一般跟batch_size成正比
    ##callbacks:回調(diào)函數(shù),調(diào)取reduce_lr
    ##verbose=0:非冗余打印,即不打印訓(xùn)練過程
    batchsize = int(len(sell_data) / 5)
    epochs = max(128,batchsize * 4)##最低循環(huán)次數(shù)128
    model.fit(train_X, train_y, batch_size=batchsize, epochs=epochs, verbose=0, callbacks=[reduce_lr])

    return model

def prediction(lstmmodel):

    predsinner = lstmmodel.predict(train_X)
    predsinner_true = predsinner * revisedata
    init_value1 = sell_data[num_steps - 1]##由于存在步長關(guān)系,這里起始是num_steps
    predsinner_true = predsinner_true.cumsum()  ##差分還原
    predsinner_true = init_value1 + predsinner_true

    predsouter = lstmmodel.predict(test_X)
    predsouter_true = predsouter * revisedata
    init_value2 = predsinner_true[-1]
    predsouter_true = predsouter_true.cumsum()  ##差分還原
    predsouter_true = init_value2 + predsouter_true

    # 作圖
    plt.plot(sell_data, label='原始值')
    Xinner = [i for i in range(num_steps + 1, len(sell_data) - test_len)]
    plt.plot(Xinner, list(predsinner_true), label='樣本內(nèi)預(yù)測值')
    Xouter = [i for i in range(len(sell_data) - test_len - 1, len(sell_data))]
    plt.plot(Xouter, [init_value2] + list(predsouter_true), label='樣本外預(yù)測值')
    allpredata = list(predsinner_true) + list(predsouter_true)
    plt.legend()
    plt.show()

    return allpredata

mymlstmmodel = buildmylstm()
presult = prediction(mymlstmmodel)

def evaluate_model(allpredata):

    allmse = mean_squared_error(sell_data[num_steps + 1:], allpredata)
    print('ALLMSE:',allmse)

evaluate_model(presult)

上述代碼可直接復(fù)制使用,關(guān)鍵地方本人都有注釋,如有不清楚地方可以多多交流,也許此模型還有優(yōu)化地方,可多多交流。對于 LSTM 建模,數(shù)據(jù)維度轉(zhuǎn)換是必要步驟,大家要認(rèn)真理解!

7  總結(jié)

任何模型都不是萬能的,重點(diǎn)是要有發(fā)現(xiàn)問題和解決問題的能力。
小數(shù)據(jù)建模往往比大數(shù)據(jù)要更難,更要思考。
對于深度模型學(xué)習(xí),本人還是強(qiáng)烈建議要大致懂模型的內(nèi)涵和原理,有條件甚至可以自己推導(dǎo)一遍或者簡單實(shí)現(xiàn)下梯度下降算法、損失函數(shù)構(gòu)建等等,否則很難解決真正的問題。

文章轉(zhuǎn)自微信公眾號@算法進(jìn)階

上一篇:

時序預(yù)測的深度學(xué)習(xí)算法介紹

下一篇:

一文梳理金融風(fēng)控建模全流程(Python)
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊

多API并行試用

數(shù)據(jù)驅(qū)動選型,提升決策效率

查看全部API→
??

熱門場景實(shí)測,選對API

#AI文本生成大模型API

對比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個渠道
一鍵對比試用API 限時免費(fèi)

#AI深度推理大模型API

對比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費(fèi)