Kaggle網(wǎng)站流量預(yù)測(cè)任務(wù)第一名解決方案:從模型到代碼詳解時(shí)序預(yù)測(cè)

大數(shù)據(jù)

作者:Artur Suilin

下面我們將簡(jiǎn)要介紹 Artur Suilin 如何修正 GRU 以完成網(wǎng)站流量時(shí)序預(yù)測(cè)競(jìng)賽。

預(yù)測(cè)有兩個(gè)主要的信息源:

局部特征。我們看到一個(gè)趨勢(shì)時(shí),希望它會(huì)繼續(xù)(自回歸模型)朝這個(gè)趨勢(shì)發(fā)展;看到流量峰值時(shí),知道它將逐漸衰減(滑動(dòng)平均模型);看到假期交通流量增加,就知道以后的假期也會(huì)出現(xiàn)流量增加(季節(jié)模型)。全局特征。如果我們查看自相關(guān)(autocorrelation)函數(shù)圖,就會(huì)注意到年與年之間強(qiáng)大的自相關(guān)和季節(jié)間的自相關(guān)。

大數(shù)據(jù)

我決定使用 RNN seq2seq 模型進(jìn)行預(yù)測(cè),原因如下:

RNN 可以作為 ARIMA 模型的自然擴(kuò)展,但是比 ARIMA 更靈活,更具表達(dá)性。RNN 是非參數(shù)的,大大簡(jiǎn)化了學(xué)習(xí)。想象一下對(duì) 145K 時(shí)序使用不同的 ARIMA 參數(shù)。任何外源性的特征(數(shù)值或類別、時(shí)間依賴或序列依賴)都可以輕松注入該模型。seq2seq 天然適合該任務(wù):我們根據(jù)先前值(包括先前預(yù)測(cè))的聯(lián)合概率(joint probability)預(yù)測(cè)下一個(gè)值。使用先前預(yù)測(cè)可保持模型穩(wěn)定,因?yàn)檎`差會(huì)在每一步累積,如果某一步出現(xiàn)極端預(yù)測(cè),則有可能毀了所有后續(xù)步的預(yù)測(cè)質(zhì)量?,F(xiàn)在的深度學(xué)習(xí)出現(xiàn)了太多的炒作。

特征工程

RNN 足夠強(qiáng)大來發(fā)現(xiàn)和學(xué)習(xí)自身特征。模型的特征列表如下:

pageviews:原始值經(jīng)過 log1p() 的轉(zhuǎn)換得到幾乎正態(tài)的時(shí)序內(nèi)值分布,而不是偏態(tài)分布。agent, country, site:這些特征從網(wǎng)頁 url 中提取,然后經(jīng)過 One-Hot 編碼。day of week:捕捉每周的季節(jié)效應(yīng)。year-to-year autocorrelation, quarter-to-quarter autocorrelation:捕捉各年、各季度的季節(jié)效應(yīng)。page popularity:高流量和低流量頁面具有不同的流量變化模式,該特征(pageviews 的中間值)幫助捕捉流量規(guī)模。pageviews 特征丟失了規(guī)模信息,因?yàn)槊總€(gè) pageviews 序列被獨(dú)立歸一化至零均值和單位方差。lagged pageviews:之后將具體介紹。

特征預(yù)處理

所有特征(包括 One-Hot 編碼的特征)被歸一化至零均值和單位方差。每個(gè) pageviews 序列被獨(dú)立歸一化。

時(shí)間依賴特征(自相關(guān)性、國家等)被「拉伸」至?xí)r序長度,即每天重復(fù)使用 tf.tile() 命令。

模型在來自初始時(shí)序的隨機(jī)固定長度樣本上進(jìn)行訓(xùn)練。例如,如果初始時(shí)序長度是 600 天,我們使用 200 天的樣本進(jìn)行訓(xùn)練,那么我們可以在前 400 天中隨意選擇開始采樣的樣本。

該采樣工作是一種有效的數(shù)據(jù)增強(qiáng)機(jī)制:訓(xùn)練代碼在每一步隨機(jī)選擇每次時(shí)序的開始點(diǎn),生成無限量的幾乎不重復(fù)的數(shù)據(jù)。

模型的核心技術(shù)

模型主要由兩部分組成,即編碼器和解碼器。

大數(shù)據(jù)

編碼器為 cuDNN GRU,cuDNN 要比 TensorFlow 的 RNNCells 快大約 5 到 10 倍,但代價(jià)就是使用起來不太方便,且文檔也不夠完善。

解碼器為 TF GRUBlockCell,該 API 封裝在 tf.while_loop() 中。循環(huán)體內(nèi)的代碼從上一步獲得預(yù)測(cè),并加入到當(dāng)前時(shí)間步的輸入特征中。

處理長時(shí)間序列

LSTM/GRU 對(duì)于相對(duì)較短的序列(100-300 項(xiàng)以內(nèi))來說是非常好的解決方案。但對(duì)于較長的序列來說,LSTM/GRU 仍然有效,只不過會(huì)逐漸遺忘較早時(shí)間步所包含的信息。Kaggle 競(jìng)賽的時(shí)間序列長達(dá) 700 多天,所以我們需要找一些方法來「加強(qiáng)」GRU 的記憶力。

我們第一個(gè)方法先是考慮使用一些注意力機(jī)制。注意力機(jī)制可以將過去較長距離的有用信息保留到當(dāng)前 RNN 單元中。對(duì)于我們的問題,最簡(jiǎn)單高效的注意力方法是使用固定權(quán)重的滑動(dòng)窗口注意力機(jī)制。它在較長距離的過去時(shí)間步上有兩個(gè)重要的點(diǎn)(考慮長期的季節(jié)性),即 1 年前和 1 個(gè)季度前。

大數(shù)據(jù)

我們可以采用 current_day – 365 和 current_day – 90 這兩個(gè)時(shí)間點(diǎn)的編碼器輸出,并將它們饋送到全連接層以降低維度,并將結(jié)果加入到解碼器的輸入特征中。這個(gè)解決方案雖然簡(jiǎn)單卻大大降低了預(yù)測(cè)誤差。

隨后我們將重要的點(diǎn)與它們的近鄰求均值,并借此減少噪聲和補(bǔ)償不均勻的間隔(閏年和不同長度的月份):attn_365 = 0.25 * day_364 + 0.5 * day_365 + 0.25 * day_366。

但隨后我們意識(shí)到 0.25、0.5、0.25 是一個(gè)一維卷積核(length=3),我們可以自動(dòng)學(xué)習(xí)更大的卷積核以檢測(cè)過去重要的點(diǎn)。

最后,我們構(gòu)建了一個(gè)非常大的注意力機(jī)制,它會(huì)查看每一個(gè)時(shí)間序列的「指紋」(指紋由較小的卷積網(wǎng)絡(luò)產(chǎn)生),并決定應(yīng)該注意哪些點(diǎn)和為較大卷積核生成權(quán)重。這個(gè)應(yīng)用于解碼器輸出的較大卷積核會(huì)為每一個(gè)預(yù)測(cè)的日期生成一個(gè)注意力特征。雖然最后沒有使用這種方法,但這個(gè)注意力機(jī)制仍然保留在代碼中,讀者可以在模型代碼中找到它。

注意,我們并沒有使用經(jīng)典的注意力方案(Bahdanau 或 Luong 注意力機(jī)制),因?yàn)榻?jīng)典的注意力機(jī)制應(yīng)該在每個(gè)預(yù)測(cè)步上使用所有的歷史數(shù)據(jù)點(diǎn)從頭開始計(jì)算,因此這種方法對(duì)于較長時(shí)間序列(約兩年的天數(shù))來說太耗時(shí)間了。所以我們的方案將會(huì)對(duì)所有數(shù)據(jù)點(diǎn)進(jìn)行一次卷積,對(duì)所有預(yù)測(cè)時(shí)間步使用相同的注意力權(quán)重(這也是缺點(diǎn)),這樣的方案計(jì)算起來要快很多。

因?yàn)槲覀儗?duì)注意力機(jī)制的復(fù)雜度感到不太滿意,因此我們?cè)噲D完全移除注意力機(jī)制,并將一年前、半年前、一季度前重要的數(shù)據(jù)點(diǎn)作為編碼器和解碼器的附加特征。這樣的結(jié)果是非常令人驚訝的,甚至在預(yù)測(cè)質(zhì)量方面都要比帶注意力機(jī)制的模型略勝一籌。因此我們最好的公開分?jǐn)?shù)都是僅使用滯后(lagged)數(shù)據(jù)點(diǎn)實(shí)現(xiàn)的,它們都沒有使用注意力機(jī)制。

大數(shù)據(jù)

滯后數(shù)據(jù)點(diǎn)另一個(gè)重要的優(yōu)勢(shì)是,模型可以使用更短的編碼器而不需要擔(dān)心損失過去的信息,因?yàn)檫@些信息現(xiàn)在明確地包含在特征中。在采用這種方法后,即使我們編碼器的長度是 60 到 90 天,結(jié)果也是完全可以接受的,而以前需要 300-400 天的長度才能獲得相同的性能。此外,更短的編碼器就等于更快速的訓(xùn)練和更少的信息損失。

損失和正則化

SMAPE(競(jìng)賽用的目標(biāo)損失函數(shù))因其在零值周圍不穩(wěn)定的行為而無法直接使用(當(dāng)真值為零的時(shí)候,損失函數(shù)是階躍函數(shù);預(yù)測(cè)值也為零的時(shí)候,則損失函數(shù)不確定)。

我使用經(jīng)平滑處理的可微 SMAPE 變體,它在所有實(shí)數(shù)上都表現(xiàn)良好:

epsilon = 0.1summ = tf.maximum(tf.abs(true) + tf.abs(predicted) + epsilon, 0.5 + epsilon)smape = tf.abs(predicted – true) / summ * 2.0?

另一個(gè)選擇是在 log1p(data) 上的 MAE 損失函數(shù),它很平滑,且訓(xùn)練目標(biāo)與 SMAPE 非常接近。

最終預(yù)測(cè)取最接近的整數(shù),負(fù)面預(yù)測(cè)取零。

我嘗試使用論文《Regularizing RNNs by Stabilizing Activations》中經(jīng)正則化的 RNN 激活值,因?yàn)?cuDNN GRU 的內(nèi)部權(quán)重?zé)o法直接正則化(也可能是我沒有找到正確的方法)。穩(wěn)性損失(Stability loss)不起作用,激活損失可以為較小損失權(quán)重(1e-06..1e-05)帶來少許改進(jìn)。

訓(xùn)練和驗(yàn)證

我使用 COCOB 優(yōu)化器(詳見論文《Training Deep Networks without Learning Rates Through Coin Betting》)結(jié)合梯度截?cái)噙M(jìn)行訓(xùn)練。COCOB 嘗試預(yù)測(cè)每個(gè)訓(xùn)練步的最優(yōu)學(xué)習(xí)率,因此我完全不必調(diào)整學(xué)習(xí)率。它的收斂速度也比傳統(tǒng)的基于動(dòng)量的優(yōu)化器快得多,尤其是在第一個(gè) epoch 上,可以讓我及早停止不成功的實(shí)驗(yàn)。

有兩種方式可以將時(shí)序分割為訓(xùn)練和驗(yàn)證數(shù)據(jù)集:

Walk-forward 分割。這實(shí)際上不是分割:我們?cè)谕暾麛?shù)據(jù)集上訓(xùn)練和驗(yàn)證,但使用不同的時(shí)間跨度。驗(yàn)證用的時(shí)間跨度比訓(xùn)練用時(shí)間跨度前移一個(gè)預(yù)測(cè)間隔。Side-by-side 分割。這是主流機(jī)器學(xué)習(xí)傳統(tǒng)的分割模型。數(shù)據(jù)集被分割成兩個(gè)獨(dú)立的部分,一個(gè)用于訓(xùn)練,另一個(gè)用于驗(yàn)證。

大數(shù)據(jù)

兩種方式我都試了,但對(duì)于這個(gè)任務(wù)來說 Walk-forward 更好,因?yàn)樗c競(jìng)賽目標(biāo)直接相關(guān):使用歷史值預(yù)測(cè)未來值。但是該分割破壞了時(shí)序結(jié)尾的數(shù)據(jù)點(diǎn),使得訓(xùn)練準(zhǔn)確預(yù)測(cè)未來的模型變得困難。

具體來說:比如,我們有 300 天的歷史數(shù)據(jù),想預(yù)測(cè)接下來 100 天的數(shù)據(jù)。如果我們選擇 walk-forward 分割,我們必須使用前 100 天的數(shù)據(jù)用于真實(shí)訓(xùn)練,后面 100 天的數(shù)據(jù)用于訓(xùn)練模式的預(yù)測(cè)(運(yùn)行解碼器、計(jì)算損失),再后面 100 天的數(shù)據(jù)用于驗(yàn)證,最后 100 天用于對(duì)未來值真正進(jìn)行預(yù)測(cè)。因此,我們實(shí)際上可以使用 1/3 的數(shù)據(jù)點(diǎn)來訓(xùn)練,最后一個(gè)訓(xùn)練數(shù)據(jù)點(diǎn)和第一個(gè)預(yù)測(cè)數(shù)據(jù)點(diǎn)之間隔了 200 天。間隔太大了,因?yàn)橐坏┪覀冸x開某個(gè)訓(xùn)練數(shù)據(jù),預(yù)測(cè)質(zhì)量將出現(xiàn)指數(shù)級(jí)下降(不確定性增加)。使用 100 天差距訓(xùn)練的模型預(yù)測(cè)質(zhì)量相對(duì)較好。

Side-by-side 分割所需要的計(jì)算力更少,因?yàn)樗诙它c(diǎn)時(shí)并不會(huì)消耗數(shù)據(jù)點(diǎn)。但是對(duì)于我們的數(shù)據(jù),模型在驗(yàn)證集上的性能與在訓(xùn)練集上的性能是強(qiáng)相關(guān)的,并且與將來的實(shí)際模型性能幾乎不具有相關(guān)性。換而言之,并行分割對(duì)于我們的問題基本上是沒有什么作用的,它只是復(fù)制了在訓(xùn)練數(shù)據(jù)集上觀察到的模型損失。

我僅使用驗(yàn)證集(帶有前向分步分割)進(jìn)行模型調(diào)優(yōu),預(yù)測(cè)未來數(shù)值的最終模型只是在盲目的模式中進(jìn)行訓(xùn)練,沒有使用任何驗(yàn)證集。

降低模型方差

優(yōu)于強(qiáng)噪音數(shù)據(jù)的輸入,模型不可避免地具有高方差。坦白講,我很驚訝 RNN 居然從噪音數(shù)據(jù)中學(xué)習(xí)到了東西。

在不同 seed 上訓(xùn)練的相同模型具有不同的表現(xiàn),有時(shí)模型甚至在「不幸」的 seed 上變得發(fā)散。訓(xùn)練期間,表現(xiàn)也會(huì)逐步地發(fā)生很大波動(dòng)。依靠純粹的運(yùn)氣很難贏得比賽,因此我決定采取行動(dòng)降低方差。

大數(shù)據(jù)

我不知道哪個(gè)訓(xùn)練步驟最適合預(yù)測(cè)未來(但前數(shù)據(jù)的驗(yàn)證結(jié)果與未來數(shù)據(jù)的結(jié)果只有弱相關(guān)關(guān)系),所以我不能使用提前停止。但是我知道近似區(qū)域,其中模型(可能)進(jìn)行了充分訓(xùn)練,但(可能)沒有開始過擬合。我決定把這個(gè)最佳區(qū)域設(shè)置為 10500 到 11500 次迭代區(qū)間內(nèi),并且從這個(gè)區(qū)域的每第 10000 個(gè)步驟保存 10 個(gè)檢查點(diǎn)。相似地,我決定在不同的 seed 上訓(xùn)練 3 個(gè)模型,并從每個(gè)模型中保存檢查點(diǎn)。因此我一共有 30 個(gè)檢查點(diǎn)。降低方差、提升模型性能的一個(gè)眾所周知的方法是 ASGD(SGD 平均)。它很簡(jiǎn)單,并在 TensorFlow 中得到很好的支持。我們必須在訓(xùn)練期間保持網(wǎng)絡(luò)權(quán)重的移動(dòng)平均值,并在推斷中使用這些平均權(quán)重,而不是原來的權(quán)重。

三個(gè)模型的結(jié)合表現(xiàn)不錯(cuò)(在每個(gè)檢查點(diǎn)上使用平均模型權(quán)重的 30 個(gè)檢查點(diǎn)的平均預(yù)測(cè))。我在排行榜上(針對(duì)未來數(shù)據(jù))獲得了相較于歷史數(shù)據(jù)上的驗(yàn)證大致相同的 SMAPE 誤差。

理論上講,你也可以把前兩種方法用作集成學(xué)習(xí),但我主要用其降低方差。

超參數(shù)調(diào)節(jié)

很多模型參數(shù)(層的數(shù)量、深度,激活函數(shù),dropout 系數(shù)等)能夠(并且應(yīng)該)被調(diào)節(jié)從而獲得更優(yōu)的模型表現(xiàn)。手動(dòng)調(diào)節(jié)乏味且費(fèi)時(shí),所以我決定自動(dòng)化該過程,并使用 SMAC3 搜索超參數(shù)。下面是 SMAC3 的一些優(yōu)勢(shì):

支持條件參數(shù)(例如,為每層聯(lián)合調(diào)節(jié)層數(shù)和 dropout;如果 n_layers > 1,第二層上的 dropout 將被調(diào)節(jié))明確處理模型方差。SMAC 在不同種子上訓(xùn)練每個(gè)模型的若干個(gè)實(shí)例,如果實(shí)例在相同種子上訓(xùn)練還要對(duì)比模型。如果它在所有相同種子上優(yōu)于另一個(gè)模型,則該模型獲勝。

與我的期望相反,超參數(shù)搜索并沒有建立定義明確的全局最小。所有的最佳模型大致具有相同的性能,但參數(shù)不同。可能 RNN 模型對(duì)于這個(gè)任務(wù)來說太具有表現(xiàn)力了,并且最好的模型得分更多依賴于模型架構(gòu)上的數(shù)據(jù)信噪比。不管怎樣,最好的參數(shù)設(shè)置依然可以在 hparams.py 文件中找到。

極客網(wǎng)企業(yè)會(huì)員

免責(zé)聲明:本網(wǎng)站內(nèi)容主要來自原創(chuàng)、合作伙伴供稿和第三方自媒體作者投稿,凡在本網(wǎng)站出現(xiàn)的信息,均僅供參考。本網(wǎng)站將盡力確保所提供信息的準(zhǔn)確性及可靠性,但不保證有關(guān)資料的準(zhǔn)確性及可靠性,讀者在使用前請(qǐng)進(jìn)一步核實(shí),并對(duì)任何自主決定的行為負(fù)責(zé)。本網(wǎng)站對(duì)有關(guān)資料所引致的錯(cuò)誤、不確或遺漏,概不負(fù)任何法律責(zé)任。任何單位或個(gè)人認(rèn)為本網(wǎng)站中的網(wǎng)頁或鏈接內(nèi)容可能涉嫌侵犯其知識(shí)產(chǎn)權(quán)或存在不實(shí)內(nèi)容時(shí),應(yīng)及時(shí)向本網(wǎng)站提出書面權(quán)利通知或不實(shí)情況說明,并提供身份證明、權(quán)屬證明及詳細(xì)侵權(quán)或不實(shí)情況證明。本網(wǎng)站在收到上述法律文件后,將會(huì)依法盡快聯(lián)系相關(guān)文章源頭核實(shí),溝通刪除相關(guān)內(nèi)容或斷開相關(guān)鏈接。

2017-12-06
Kaggle網(wǎng)站流量預(yù)測(cè)任務(wù)第一名解決方案:從模型到代碼詳解時(shí)序預(yù)測(cè)
作者:Artur Suilin 下面我們將簡(jiǎn)要介紹 Artur Suilin 如何修正 GRU 以完成網(wǎng)站流量時(shí)序預(yù)測(cè)競(jìng)賽。 預(yù)測(cè)有兩個(gè)主要的信息源:

長按掃碼 閱讀全文