【python量化】大幅提升预测性能,将NSTransformer用于股价预测
寫在前面
NSTransformer模型來自NIPS 2022的一篇paper《Non-stationary Transformers: Exploring the Stationarity in Time Series Forecasting》。NSTransformer的目的主要是為了解決其他方法出現(xiàn)過平穩(wěn)化處理的問題。其通過提出序列平穩(wěn)化以及去平穩(wěn)化注意力機(jī)制可以使得模型面向提升預(yù)測性能的角度進(jìn)行平穩(wěn)化處理,相比于Transformer的變體,NSTransformer在預(yù)測性能方面實(shí)現(xiàn)了大幅度的提升。下面的這篇文章主要帶大家了解一下NSTransformer的基本原理,并使用作者開源的NSTransformer代碼,并將其用于股票價(jià)格預(yù)測當(dāng)中。
1
NSTransformer模型
由于Transformer的全局范圍的建模能力,使其在時(shí)間序列預(yù)測中顯示出巨大的力量。然而,在聯(lián)合分布隨時(shí)間變化的非穩(wěn)態(tài)真實(shí)世界數(shù)據(jù)上,它們的性能可能會退化得很可怕。以前的研究主要采用平穩(wěn)化技術(shù)來削弱原始序列的非平穩(wěn)特性,以提高預(yù)測能力。但是,被剝奪了內(nèi)在非平穩(wěn)性的平穩(wěn)序列對于現(xiàn)實(shí)世界中的突發(fā)事件預(yù)測的指導(dǎo)意義不大。這個(gè)問題,在本文中被稱為過平穩(wěn)化(over-stationarization),導(dǎo)致Transformer對不同序列產(chǎn)生無差別的時(shí)序關(guān)注,阻礙了深度模型的預(yù)測能力。為了解決序列的可預(yù)測性和模型能力之間的困境,作者提出了Non-stationary Transformer (NSTransformer)作為一個(gè)通用框架,其中有兩個(gè)相互依賴的模塊。序列平穩(wěn)化(Series Stationarization)和去平穩(wěn)化注意力(De-stationary Attention)。具體來說,序列平穩(wěn)化模塊統(tǒng)一了每個(gè)輸入的統(tǒng)計(jì)特性,并將輸出轉(zhuǎn)換為恢復(fù)的統(tǒng)計(jì)特性,以提高可預(yù)測性。為了解決過平穩(wěn)化問題,去平穩(wěn)化注意力被設(shè)計(jì)出來,通過近似于從原始序列中學(xué)到的可區(qū)分的注意,將內(nèi)在的非平穩(wěn)信息恢復(fù)為時(shí)間依賴。作者提出的NSTransformer框架在很大程度上提升了主流Transformer模型的變體的預(yù)測性能,相比于Transformer,MSE降低了49.43%,相比于Informer,降低了47.34%,相比于Reformer,降低了46.89%。
模型框架
NSTransformer遵循先前在時(shí)間序列預(yù)測中使用的Transformer架構(gòu),采用標(biāo)準(zhǔn)的編碼器-解碼器結(jié)構(gòu),其中編碼器從過去的數(shù)據(jù)中提取信息,而解碼器則通過對過去的歷史信息進(jìn)行過匯總來實(shí)施預(yù)測。典型的NSTransformer是通過對Transformer的輸入和輸出進(jìn)行序列平穩(wěn)化處理,并用提出的非平穩(wěn)注意機(jī)制取代self-attention,這可以提高基礎(chǔ)模型的非平穩(wěn)序列的預(yù)測能力。總之,NSTransformer主要包括下面兩個(gè)模塊:序列平穩(wěn)化模塊以及去平穩(wěn)注意力模塊。
NSTransformer的基本框架
序列平穩(wěn)化
序列平穩(wěn)化主要模塊兩個(gè)模塊,一個(gè)是歸一化(Normalization)模塊,另一個(gè)是反歸一化(De-Normalization)模塊。首先,歸一化模塊通過一個(gè)滑動窗口的形式對每一維時(shí)間序列數(shù)據(jù)進(jìn)行歸一化處理,這樣窗口化的方式可以將每一個(gè)相鄰窗口內(nèi)的數(shù)據(jù)都具有相同的均值跟方差,以消除序列之間尺度上的差異性,并增加輸入數(shù)據(jù)在時(shí)序上的分布穩(wěn)定性。歸一化的過程是:
在模型預(yù)測結(jié)束之后,反歸一化模塊利用歸一化時(shí)記錄的均值跟方差信息,用來將模型的輸出映射回原來的尺度,以恢復(fù)歸一化時(shí)損失的信息。反歸一化的過程是:
通過這兩個(gè)階段的變換,模型將接收到平穩(wěn)的輸入,這些輸入遵循穩(wěn)定的分布,更容易泛化。這樣的設(shè)計(jì)還使模型對時(shí)間序列具有平移不變性和尺度不變性,從而有利于真實(shí)序列的預(yù)測。
序列平穩(wěn)化過程(圖片來自:https://zhuanlan.zhihu.com/p/587665491)
去平穩(wěn)化注意力
去平穩(wěn)化注意力機(jī)制去平穩(wěn)化注意力機(jī)制的過程如下圖所示:
去平穩(wěn)化注意力機(jī)制(圖片來自:https://zhuanlan.zhihu.com/p/587665491)
如前面所提到的,過平穩(wěn)化問題是由內(nèi)在的非平穩(wěn)信息的消失引起的,這將使模型無法捕捉到用于預(yù)測的事件性時(shí)間依賴。因此,作者試圖近似從原始的非平穩(wěn)序列中去學(xué)習(xí)注意力。下面是Transformer計(jì)算注意力的原始公式:
對于輸入時(shí)間序列x,計(jì)算它的均值跟方差得到:
除此之外,為了簡化分析這里假設(shè)了用于嵌入的前饋層在時(shí)間維度是線性的,基于線性假設(shè)可以推導(dǎo)出??,??與對平穩(wěn)化后時(shí)間序列計(jì)算得到的??跟??的關(guān)系是:
其中,??與??是??跟??在時(shí)間維度的均值。這樣從原始時(shí)間序列中計(jì)算注意力機(jī)制的公式,可以被從平穩(wěn)化后的時(shí)間序列中計(jì)算的??跟??替代為下面的公式,具體的推導(dǎo)可以參考原文的附錄。
這樣得到的注意力公式不僅包含了原始時(shí)間序列的信息,也包含了經(jīng)過平穩(wěn)化后的序列信息。之后為了恢復(fù)對非平穩(wěn)序列的原始注意力,這里作者將消失的非平穩(wěn)信息重新引入到非平穩(wěn)序列的計(jì)算中。為了得到所需要的計(jì)算值,作者引入了去平穩(wěn)因子???跟??,它們都是通過兩個(gè)多層感知器從非平穩(wěn)時(shí)間序列中計(jì)算得到的。進(jìn)而,去平穩(wěn)化注意力的計(jì)算方式可以得到如下形式:
這樣,它既能利用平穩(wěn)序列的可預(yù)測性,又能保持原始序列固有的時(shí)間依賴性。
2
環(huán)境配置
本地環(huán)境:
Python?3.7 IDE:Pycharm庫版本:
numpy 1.18.1 pandas 1.0.3 sklearn 0.22.2 matplotlib 3.2.1 torch?1.10.1NSTransformer源碼Github鏈接:
https://github.com/thuml/Nonstationary_Transformers
3
代碼實(shí)現(xiàn)
NSTransformer的官方代碼實(shí)現(xiàn)借鑒了Informer的代碼,因此,與之前的推文【python量化】將Informer用于股價(jià)預(yù)測類似,首先將NSTransformer的源碼下載到本地,然后將我們的數(shù)據(jù)集放到某個(gè)路徑下,這里仍然用到了上證指數(shù)14到17年四年的開高低收成交量數(shù)據(jù)。在將NSTransformer的代碼用于我們的股票數(shù)據(jù)預(yù)測任務(wù)時(shí),同樣需要明確的是,我們的任務(wù)是基于高低收成交量來預(yù)測收盤價(jià),這是一個(gè)多變量輸入,單變量輸出的預(yù)測任務(wù)。所以主要需要修改下面幾個(gè)參數(shù)的設(shè)置。當(dāng)需要進(jìn)行不同預(yù)測任務(wù)時(shí),如增加某些特征,預(yù)測多個(gè)目標(biāo)變量等則可通過修改features,target,enc_in,dec_in以及c_out參數(shù)進(jìn)行實(shí)現(xiàn)。NSTransformer的主要參數(shù)與Informer類似,主要包括:
model: ns_transformer,可以選擇其他模型包括Informer,Transformer以及Autoformer。 data: 設(shè)置為custom,用于調(diào)用Dataset_Custom類,從而可以自定義數(shù)據(jù)集。 root_path:指定數(shù)據(jù)集存放的文件夾。 data_path:指定csv數(shù)據(jù)集的名稱。 features:設(shè)置為MS,這是因?yàn)槲覀兪怯瞄_高低收成交量來預(yù)測收盤價(jià),所以是多變量輸出來預(yù)測單變量。 target:表示預(yù)測變量,設(shè)置為Close,對應(yīng)我們csv文件預(yù)測變量的列名。 freq:表示預(yù)測頻率,設(shè)置為d,因?yàn)槲覀冇玫降氖侨站€級別的數(shù)據(jù)。 seq_len:表示輸入encoder的序列長度,這里設(shè)置為20。 label_len:表示輸入decoder中的token的長度,這里設(shè)置為10,即通過前10個(gè)真實(shí)值來輔助decoder進(jìn)行預(yù)測。 pred_len:表示預(yù)測序列的長度,這里設(shè)置為5,即表示預(yù)測后5個(gè)時(shí)刻的序列值。 enc_in:表示encoder的輸入維度,這里設(shè)置為5,因?yàn)槲覀冇玫搅碎_高低收以及成交量5個(gè)特征。 dec_in:表示decoder的輸入維度,同enc_in。 c_out:表示輸出序列的維度,這里設(shè)置為1,因?yàn)槲覀兊哪繕?biāo)變量只有收盤價(jià)。 moving_avg:?移動平均的窗口大小。 p_hidden_dims:去平穩(wěn)性映射器的隱層維度。 p_hidden_layers:映射器的層數(shù)。其他參數(shù)像模型層數(shù),維度之類的可以根據(jù)自己的電腦配置進(jìn)行修改。下面是進(jìn)行上證指數(shù)預(yù)測實(shí)驗(yàn)的參數(shù)配置:
args.is_training = 1 args.model_id = 'test' args.model='ns_Transformer' # model name, options: [ns_Transformer, Transformer]# data loader args.data = 'custom' # dataset type args.root_path ='./data/stock/' # root path of the data file args.data_path ='SH000001.csv' # data file args.features='MS' # forecasting task, options:[M, S, MS]; M:multivariate predict multivariate, S:univariate predict univariate, MS:multivariate predict univariate' args.target='Close' # 'target feature in S or MS task' args.freq = 'd' # freq for time features encoding, options:[s:secondly, t:minutely, h:hourly, d:daily, b:business days, w:weekly, m:monthly], you can also use more detailed freq like 15min or 3h' args.checkpoints ='./checkpoints/' # 'location of model checkpoints'# forecasting task args.seq_len = 20 # input sequence length args.label_len = 10 # start token length args.pred_len = 5 # prediction sequence lengthargs.# model define args.enc_in = 5 # encoder input size args.dec_in = 5 # decoder input size args.c_out = 1 # output size args.d_model = 256 # dimension of model args.n_heads = 4 # num of heads args.e_layers = 2 # num of encoder layers args.d_layers = 1 # num of decoder layers args.d_ff = 256 # dimension of fcn args.moving_avg = 5 # window size of moving average args.factor = 1 # attn factor args.distil= True # whether to use distilling in encoder, using this argument means not using distillingargs.dropout = 0.05 # dropout args.embed = 'timeF' # time features encoding, options:[timeF, fixed, learned] args.activation = 'gelu' # 'activation' args.output_attention = True # help='whether to output attention in encoder args.do_predict = True # whether to predict unseen future data# optimization args.num_workers = 0 # data loader num workers args.itr = 1 # experiments times args.train_epochs = 20 # train epochs args.batch_size = 32 # batch size of train input data args.patience = 3 # early stopping patience args.learning_rate = 0.0001 # optimizer learning rate args.des = 'test' # exp description args.loss = 'mse' # loss function args.lradj = 'type1' # adjust learning rate args.use_amp = False # use automatic mixed precision training# GPU args.use_gpu = False # use gpu args.gpu = 0 # gpu args.use_multi_gpu = False args.devices = '0,1,2,3' # device ids of multile gpus args.seed = 2021 # random seed# de-stationary projector params args.p_hidden_dims = [128, 128] # hidden layer dimensions of projector (List) args.p_hidden_layers = 2 # number of hidden layers in projector按照設(shè)置的參數(shù),然后對模型進(jìn)行訓(xùn)練跟測試:
Exp = Exp_Mainif args.is_training:for ii in range(args.itr):# setting record of experimentssetting = '{}_{}_{}_ft{}_sl{}_ll{}_pl{}_dm{}_nh{}_el{}_dl{}_df{}_fc{}_eb{}_dt{}_{}_{}'.format(args.model_id,args.model,args.data,args.features,args.seq_len,args.label_len,args.pred_len,args.d_model,args.n_heads,args.e_layers,args.d_layers,args.d_ff,args.factor,args.embed,args.distil,args.des, ii)exp = Exp(args) # set experimentsprint('>>>>>>>start training : {}>>>>>>>>>>>>>>>>>>>>>>>>>>'.format(setting))exp.train(setting)print('>>>>>>>testing : {}<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'.format(setting))exp.test(setting)if args.do_predict:print('>>>>>>>predicting : {}<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'.format(setting))exp.predict(setting, True)torch.cuda.empty_cache() else:ii = 0setting = '{}_{}_{}_ft{}_sl{}_ll{}_pl{}_dm{}_nh{}_el{}_dl{}_df{}_fc{}_eb{}_dt{}_{}_{}'.format(args.model_id,args.model,args.data,args.features,args.seq_len,args.label_len,args.pred_len,args.d_model,args.n_heads,args.e_layers,args.d_layers,args.d_ff,args.factor,args.embed,args.distil,args.des, ii)exp = Exp(args) # set experimentsprint('>>>>>>>testing : {}<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'.format(setting))exp.test(setting, test=1)torch.cuda.empty_cache()下面是在CPU上的訓(xùn)練過程。經(jīng)過訓(xùn)練,可以看出模型的訓(xùn)練集上的loss不斷下降,由于加入了early stop機(jī)制,所以經(jīng)過6個(gè)epoch模型就停止訓(xùn)練了。經(jīng)過在測試集上的測試,NSTransformer實(shí)現(xiàn)了0.0027的mse跟0.0415的mae(歸一化后的結(jié)果)。需要注意的是,模型經(jīng)過訓(xùn)練跟測試之后,會在當(dāng)前路徑的./checkpoints中保存模型參數(shù),在./results/{settings}/下生成pred.npy以及true.npy文件用來分別存放測試集上的預(yù)測結(jié)果跟ground truth。
Use CPU >>>>>>>start training : test_ns_Transformer_custom_ftMS_sl20_ll10_pl5_dm256_nh4_el2_dl1_df256_fc1_ebtimeF_dtTrue_test_0>>>>>>>>>>>>>>>>>>>>>>>>>> train 659 val 95 test 191 Epoch: 1 cost time: 1.7922062873840332 Epoch: 1, Steps: 20 | Train Loss: 0.0605105 Vali Loss: 0.0028209 Test Loss: 0.0028931 Validation loss decreased (inf --> 0.002821). Saving model ... Updating learning rate to 0.0001 Epoch: 2 cost time: 1.569800615310669 Epoch: 2, Steps: 20 | Train Loss: 0.0374645 Vali Loss: 0.0030634 Test Loss: 0.0028165 EarlyStopping counter: 1 out of 3 Updating learning rate to 5e-05 Epoch: 3 cost time: 1.5588312149047852 Epoch: 3, Steps: 20 | Train Loss: 0.0328267 Vali Loss: 0.0024335 Test Loss: 0.0027673 Validation loss decreased (0.002821 --> 0.002434). Saving model ... Updating learning rate to 2.5e-05 Epoch: 4 cost time: 1.5059726238250732 Epoch: 4, Steps: 20 | Train Loss: 0.0296629 Vali Loss: 0.0031771 Test Loss: 0.0028892 EarlyStopping counter: 1 out of 3 Updating learning rate to 1.25e-05 Epoch: 5 cost time: 1.6665441989898682 Epoch: 5, Steps: 20 | Train Loss: 0.0289504 Vali Loss: 0.0029475 Test Loss: 0.0027112 EarlyStopping counter: 2 out of 3 Updating learning rate to 6.25e-06 Epoch: 6 cost time: 1.5528459548950195 Epoch: 6, Steps: 20 | Train Loss: 0.0285680 Vali Loss: 0.0029404 Test Loss: 0.0027616 EarlyStopping counter: 3 out of 3 Early stopping >>>>>>>testing : test_ns_Transformer_custom_ftMS_sl20_ll10_pl5_dm256_nh4_el2_dl1_df256_fc1_ebtimeF_dtTrue_test_0<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< test 191 test shape: (5, 32, 5, 1) (5, 32, 5, 1) test shape: (160, 5, 1) (160, 5, 1) mse:0.0027673370204865932, mae:0.04154161363840103調(diào)用模型的predict()方法可以直接預(yù)測所有數(shù)據(jù)后面的未知數(shù)據(jù),因?yàn)檫@里預(yù)測長度為5,所以直接調(diào)用就相當(dāng)于預(yù)測后5天的收盤價(jià)走勢了。預(yù)測的結(jié)果會保存在./results/{settings}/下面的real_prediction.npy文件中。
exp = Exp(args) exp.predict(setting, True) #?the?prediction?will?be?saved?in?./results/{setting}/real_prediction.npy prediction?=?np.load('./results/'+setting+'/real_prediction.npy') plt.figure() plt.plot(prediction[0,:,-1]) plt.show()最后將完整的測試集上的預(yù)測結(jié)果進(jìn)行可視化。
plt.figure() plt.plot(trues[:,0,-1].reshape(-1), label='GroundTruth') plt.plot(preds[:,0,-1].reshape(-1), label='Prediction') plt.legend() plt.show()除此之外,NSTransformer的源碼中也提供了Transformer、Informer以及Autoformer的實(shí)現(xiàn)。為了直觀展示這幾種模型之間的預(yù)測表現(xiàn),我們采用相同的超參數(shù)對另外幾種模型進(jìn)行實(shí)驗(yàn),并將其可視化的結(jié)果進(jìn)行展示。需要注意是這里為了方便實(shí)驗(yàn),每種模型的超參沒有進(jìn)行尋優(yōu)而是統(tǒng)一采用了NSTransformer模型的實(shí)驗(yàn)配置。
Transformer
Informer
Autoformer
從可視化結(jié)果中可以看出相比于其他Transformer模型,NSTransformer實(shí)現(xiàn)了最好的擬合效果。最后統(tǒng)計(jì)這幾種模型的MSE跟MAE(未反歸一化),從誤差上也可以看出NSTransformer實(shí)現(xiàn)了最低的預(yù)測誤差,其次是Autoformer,Informer以及Transformer。
NSTransformer: mse:0.0027673370204865932, mae:0.04154161363840103 Transformer: mse:0.0147677231580019, mae:0.10331685841083527 Informer: mse:0.01395915262401104,?mae:0.09441611915826797 Autoformer: mse:0.008317378349602222, mae:0.072861030697822574
總結(jié)
時(shí)間序列的非平穩(wěn)性是現(xiàn)實(shí)時(shí)間序列數(shù)據(jù)中存在的重要特性,尤其是對金融時(shí)間序列來說。而現(xiàn)有大多數(shù)方法都會對數(shù)據(jù)做平穩(wěn)化處理來提升預(yù)測性能,然而,平穩(wěn)化處理的過程也會導(dǎo)致一部分信息的丟失。而NSTransformer通過設(shè)計(jì)的序列平穩(wěn)化以及去平穩(wěn)化注意力機(jī)制,使得它既能利用平穩(wěn)序列的可預(yù)測性,又能保持原始序列固有的時(shí)間依賴性。文中通過將NSTransformer與其他模型包括Transformer,Informer以及Autoformer的實(shí)驗(yàn)對比,進(jìn)一步證實(shí)了NSTransformer的預(yù)測性能。因此,在面對非平穩(wěn),高噪聲的股票數(shù)據(jù)預(yù)測中,NSTransformer或許可以取得較好的預(yù)測表現(xiàn)。
本文內(nèi)容僅僅是技術(shù)探討和學(xué)習(xí),并不構(gòu)成任何投資建議。
參考文獻(xiàn):
Liu, Y., Wu, H., Wang, J., & Long, M. (2022). Non-stationary Transformers: Exploring the Stationarity in Time Series Forecasting. In Advances in Neural Information Processing Systems.
NeurIPS2022 | NSTransformers: 非平穩(wěn)時(shí)間序列的通用預(yù)測框架 - 游凱超的文章 - 知乎 https://zhuanlan.zhihu.com/p/587665491
獲取完整代碼與數(shù)據(jù)以及其他歷史文章完整源碼與數(shù)據(jù)可加入《人工智能量化實(shí)驗(yàn)室》知識星球。
《人工智能量化實(shí)驗(yàn)室》知識星球
加入人工智能量化實(shí)驗(yàn)室知識星球,您可以獲得:(1)定期推送最新人工智能量化應(yīng)用相關(guān)的研究成果,包括高水平期刊論文以及券商優(yōu)質(zhì)金融工程研究報(bào)告,便于您隨時(shí)隨地了解最新前沿知識;(2)公眾號歷史文章Python項(xiàng)目完整源碼;(3)優(yōu)質(zhì)Python、機(jī)器學(xué)習(xí)、量化交易相關(guān)電子書PDF;(4)優(yōu)質(zhì)量化交易資料、項(xiàng)目代碼分享;(5)跟星友一起交流,結(jié)交志同道合朋友。(6)向博主發(fā)起提問,答疑解惑。
總結(jié)
以上是生活随笔為你收集整理的【python量化】大幅提升预测性能,将NSTransformer用于股价预测的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 某计算机系学生请假条
- 下一篇: 树莓派python蓝牙_树莓派的蓝牙操作