人工智能與量化投資--深度學習前沿技術在股價預測中的應用

在本文中,我將創建一個預測股票價格變動的完整流程。按照我們的思路走下去,我們將獲得一些非常好的結果。爲此,我們將使用生成對抗網絡(GAN)與LSTM(一種遞歸神經網絡)作爲生成器,並使用卷積神經網絡CNN作爲裁決器。我們使用LSTM的原因很明顯,我們正在嘗試預測時間序列數據。爲什麼我們使用GAN,特別是CNN作爲裁決器?這是一個很好的問題:稍後會有特別的部分來說明這個問題。

當然,我們將詳細介紹每個步驟,但最困難的部分是GAN:成功訓練GAN的非常困難的部分是獲得正確的超參數集。出於這個原因,我們將使用貝葉斯優化(連同高斯過程)和深度強化學習(DRL)來決定何時以及如何改變GAN的超參數。在創建強化學習時,我將使用該領域的最新進展,例如Rainbow和PPO。

我們將使用許多不同類型的輸入數據。除了股票的歷史交易數據和技術指標,我們將使用NLP的最新進展(BERT遷移學習)來創建情緒分析(作爲基本面分析的一部分),用於提取整體趨勢方向的傅立葉變換,用於識別其他高級特徵的棧式自編碼器,用於查找相關資產的特徵投資組合,用於股票函數近似的自迴歸積分移動平均值(ARIMA)等等,以便捕獲儘可能多的關於股票的信息,模式,依賴關係等。衆所周知,數據越多越好。預測股票價格變動是一項非常複雜的任務,因此我們對股票的瞭解越多(從不同的角度來看),我們的盈利就越高。

爲了創建所有的神經網絡,我們將使用MXNet及其高級API - Gluon,並在多個GPU上進行訓練。

注意:雖然我試圖詳細瞭解了數學和幾乎所有算法和技術背後的機制,但本文不會明確地解釋機器/深度學習或股票市場如何運作。我們的目的是分享如何使用不同的技術和算法來準確預測股票價格變動,並且給出在每一步使用每種技術的原因和背後的理由。

目錄

1、概述

2、數據

2.1、關聯資產

2.2、技術指標

2.3、基本面分析

2.3.1、Transformer的雙向嵌入表示 - BERT

2.4、用於趨勢分析的傅立葉變換

2.5、ARIMA作爲特徵

2.6、統計學檢查

2.6.1、異方差性,多重共線性,序列相關性

2.7、特徵工程

2.7.1、特徵對於XGBoost的重要性

2.8、使用棧式自編碼器提取高級特徵

2.8.1、激活函數 - GELU(高斯誤差)

3、生成對抗網絡(GAN)

3.1、爲什麼GAN用於股市預測

3.2、Metropolis-Hastings GAN和Wasserstein GAN

3.3、生成器 - 單層RNN

3.3.1、LSTM還是GRU

3.3.2、LSTM的架構

3.3.3、學習率調度程序

3.3.4、如何防止過擬合和偏差 - 方差權衡

3.4、裁決器 - 一維CNN

3.4.1、爲什麼CNN可以做裁決器?

3.4.2、CNN的架構

3.5、超參數

4、超參數優化

4.1、針對超參數優化的強化學習

4.1.1、強化學習理論

4.1.1.1、Rainbow

4.1.1.2、PPO

4.1.2、進一步深入強化學習

4.2、貝葉斯優化

4.2.1、高斯過程

5、結果

6、接下來的研究內容?

7、免責聲明

1、概述

準確預測股票市場是一項複雜的任務,因爲特定股票在特定方向上有數百萬個事件和前提條件。因此,我們需要能夠捕獲儘可能多的這些前提條件。我們還需要做出幾個重要假設:1)市場不是100%隨機,2)歷史會重演,3)市場遵循人們的理性行爲,4)市場是“完美的”。不過,請閱讀底部的免責聲明。

我們將嘗試預測高盛(紐約證券交易所代碼:GS)的價格走勢。爲此,我們將使用2010年1月1日至2018年12月31日的每日收盤價(訓練數據爲7年,驗證數據爲2年)。

2、數據

我們需要了解影響GS的股價上漲或下跌的因素。這是人們所關心的問題。因此,我們需要儘可能多地合併信息(從不同方面和角度描繪股票)。 (我們將使用1585天的日線數據來訓練各種算法(我們擁有70%的訓練數據)並預測接下來的680天(測試數據)。然後我們將預測結果與測試數據分類進行比較(我們將其稱爲特徵)在後面的章節中我們會詳細介紹,我們將使用的特徵包括:

關聯資產 - 這些是其他資產(任何類型,不一定是股票,如商品,外匯,指數,甚至固定收益證券)。像高盛這樣的大公司顯然不會“生活”在一個孤立的世界中 - 它依賴於許多外部因素並與之互動,包括競爭對手,客戶,全球經濟,地緣政治形勢,財政和貨幣政策,融資情況等。詳情將在後面列出。

技術指標 - 很多投資者都遵循技術指標。我們將最受歡迎的指標作爲獨立特徵。其中包括 - 7日和21日均線,指數均線,動量,布林通道,MACD。

基本面分析 - 一個非常重要的特徵,表明股票是上漲還是下跌。基本面分析有兩個方面可以爲我們所用:1)使用10-K和10-Q報告分析公司業績,分析ROE和市盈率等(我們不會使用此報告),2)新聞 - 可能新聞可以指示可能在特定方向上移動股票的即將發生的事件。我們將閱讀高盛的所有每日新聞,並提取當天對高盛的總體情緒是正面的,中立的還是負面的(我們的打分會介於0-1之間)。由於許多投資者都會仔細閱讀新聞並根據新聞做出投資決策(當然不是所有人),如果今天高盛的消息非常積極,那麼股票將在明天激增。這裏的重點在於,我們將以這些特徵的權重作爲我們以後判斷股票趨勢的依據。稍後會詳細介紹。

爲了創建準確的情緒預測,我們將使用神經語言處理(NLP)。我們將使用BERT - 谷歌最近宣佈的NLP方法用於情感分類股票新聞情緒提取的遷移學習。

傅立葉變換 - 除了每日收盤價,我們還將創建傅里葉變換,以概括多個長期和短期趨勢。使用這些變換,我們將消除大量噪聲(隨機的)並創建真實股票移動的近似值。趨勢近似可以幫助LSTM網絡更準確地選擇其預測趨勢。

自迴歸整合移動平均線(ARIMA) - 這是預測時間序列數據未來值的最流行技術之一(在神經網絡流行之前)。我們把它也加上,看看它是否是一個重要的預測特徵。

棧式自編碼器 - 經過數十年的研究後,人們發現了大多數上述特徵(基本面分析,技術分析等)。但也許我們錯過了一些東西。也許由於大量的數據點,事件,資產,圖表等,人們無法理解隱藏的相關性。使用棧式自編碼器(神經網絡),我們可以使用計算機的強大功能,可能找到新的影響股票走勢的特徵。即使我們無法用人類語言理解這些功能,我們也會在GAN中使用它們。

深度無監督學習用於期權定價中的異常檢測。我們將再使用一項特徵 - 每天我們都會爲高盛股票增加90天看漲期權的價格。期權定價本身結合了大量數據。期權合約的價格取決於股票的未來價值(分析師也試圖預測價格,以便爲看漲期權提供最準確的價格)。使用深度無監督學習(自組織映射),我們將嘗試發現每天定價中的異常情況。異常(例如定價的急劇變化)可能有利於LSTM來做股票價格的模式識別。

現在我們有了這麼多的特徵,接下來我們需要執行幾個重要的步驟:

對數據的“質量”進行統計檢查。如果我們創建的數據存在缺陷,那麼無論我們的算法有多複雜,結果都不會很理想。檢查包括確保數據不會受到異方差性,多重共線性或串行相關性的影響。

創建特徵權重。如果某個特徵(例如另一個股票或技術指標)對我們想要預測的股票沒有權重,那麼我們就不需要在神經網絡的訓練中使用它。我們將使用XGBoost(eXtreme Gradient Boosting),一種增強樹迴歸算法來創建權重。

作爲我們數據準備的最後一步,我們還將使用主成分分析(PCA)創建Eigen投資組合,以減少自編碼器創建的特徵的維數。


2.1、關聯資產

如前所述,我們將使用其他股票數據作爲特徵,而不僅僅是GS。

那麼其他股票是否會影響GS的股票走勢?良好地瞭解公司,其業務線,競爭格局,依賴關係,供應商和客戶類型等對於選擇正確的關聯資產非常重要:

首先是與GS類似的公司。我們將把JPMorgan Chase和Morgan Stanley等添加到數據集中。

作爲一家投資銀行,高盛(Goldman Sachs)依賴於全球經濟。經濟不景氣或波動意味着沒有併購或首次公開募股,也可能是有限的自營交易收益。這就是爲什麼我們將包括全球經濟指數。此外,我們將包括LIBOR(美元和英鎊計價)利率,因爲分析師可能會考慮經濟的衝擊來設定這些利率以及其他FI證券。

每日波動率指數(VIX) - 理由同上。

綜合指數 - 如納斯達克和紐約證券交易所(來自美國),FTSE100(英國),日經225(日本),恆生和BSE Sensex(亞太指數)指數。

貨幣 - 全球貿易多次反映貨幣如何變動,因此我們將使用一攬子貨幣(如美元兌日元,英鎊兌美元等)作爲特徵。

總的來說,我們在數據集中有72個其他資產 - 每個資產的日線數據。

2.2、技術指標

我們已經介紹了什麼是技術指標以及我們使用它們的原因,讓我們直接來看代碼。我們將僅爲GS創建技術指標。

""" Function to create the technical indicators """

def get_technical_indicators(dataset):

# Create 7 and 21 days Moving Average

dataset['ma7'] = dataset['price'].rolling(window=7).mean()

dataset['ma21'] = dataset['price'].rolling(window=21).mean()

# Create MACD

dataset['26ema'] = pd.ewma(dataset['price'], span=26)

dataset['12ema'] = pd.ewma(dataset['price'], span=12)

dataset['MACD'] = (dataset['12ema']-dataset['26ema'])

# Create Bollinger Bands

dataset['20sd'] = pd.stats.moments.rolling_std(dataset['price'],20)

dataset['upper_band'] = dataset['ma21'] + (dataset['20sd']*2)

dataset['lower_band'] = dataset['ma21'] - (dataset['20sd']*2)

# Create Exponential moving average

dataset['ema'] = dataset['price'].ewm(com=0.5).mean()

# Create Momentum

dataset['momentum'] = dataset['price']-1

return dataset

所以我們每個交易日都有技術指標(包括MACD,布林帶等)。我們共有12項技術指標。

2.3、基本面分析

對於基本面分析,我們將對所有關於GS的每日新聞進行情緒分析。最後使用sigmoid,結果將在0和1之間。分數越接近0 - 新聞越消極(接近1表示積極情緒)。對於每一天,我們將創建平均每日分數(作爲0到1之間的數字)並將其添加到特徵。

2.3.1、Transformer的雙向嵌入表示 - BERT

爲了將新聞分類爲正面或負面(或中性),我們將使用BERT,一種預訓練的NLP模型。

MXNet / Gluon已經提供預先訓練的BERT模型。我們只需要實例化它們並添加兩個(任意數量)Dense層,並使用softmax。

import bert

BERT和NLP部分的細節不在本文的範圍內,如果您有興趣,可以自行dig一番!

2.4、用於趨勢分析的傅立葉變換

傅立葉變換使用函數創建一系列正弦波(具有不同的幅度和幀)。組合時,這些正弦波接近原始函數。

我們將使用傅里葉變換來提取GS的全球和本地趨勢,並對其進行一些去噪。那麼讓我們看看它是如何工作的。

""" Code to create the Fuorier trasfrom """

data_FT = dataset_ex_df[['Date', 'GS']]

close_fft = np.fft.fft(np.asarray(data_FT['GS'].tolist()))

fft_df = pd.DataFrame({'fft':close_fft})

fft_df['absolute'] = fft_df['fft'].apply(lambda x: np.abs(x))

fft_df['angle'] = fft_df['fft'].apply(lambda x: np.angle(x))

plt.figure(figsize=(14, 7), dpi=100)

fft_list = np.asarray(fft_df['fft'].tolist())

for num_ in [3, 6, 9, 100]:

fft_list_m10= np.copy(fft_list); fft_list_m10[num_:-num_]=0

plt.plot(np.fft.ifft(fft_list_m10), label='Fourier transform with {} components'.format(num_))

plt.plot(data_FT['GS'], label='Real')

plt.xlabel('Days')

plt.ylabel('USD')

plt.title('Figure 3: Goldman Sachs (close) stock prices & Fourier transforms')

plt.legend()

plt.show()

我們使用傅里葉變換的組件越多,逼近函數越接近實際股票價格。我們使用傅里葉變換來提取長期和短期趨勢,因此我們將使用具有3、6和9個分量的變換。您可以推斷出具有3個組件的轉換表示長期趨勢。

用於去噪數據的另一種技術稱爲小波變換。小波和傅立葉變換給出了類似的結果,因此我們只使用傅里葉變換。

2.5、ARIMA作爲特徵

ARIMA是一種預測時間序列數據的技術。我們將展示如何使用它,儘管ARIMA不會作爲我們的最終預測,我們將使用它作爲一種技術來對股票進行一些去噪並(可能)提取一些新的模式或特徵。

error = mean_squared_error(test, predictions)

print('Test MSE: %.3f' % error)

output >>> Test MSE: 10.151

ARIMA給出了實際股票價格的非常好的近似值。我們將通過ARIMA使用預測價格作爲LSTM的輸入特徵,因爲正如我們之前提到的,我們希望捕獲儘可能多的關於高盛的特徵和模式。我們測試了MSE(均方誤差),結果不太差(考慮到我們確實有很多測試數據),但是,我們只會將它用作LSTM中的一個特徵。

2.6、統計學檢查

確保數據質量良好對我們的模型非常重要。爲了確保我們的數據合適,我們將執行幾個簡單的檢查,以確保我們實現和觀察的結果確實是真實的,而不是由於基礎數據分佈導致一些邏輯性的錯誤。

2.6.1、異方差性,多重共線性,序列相關性

當誤差項(迴歸的預測值與實際值之間的差異)取決於數據時,會出現條件異方差性 - 例如,當數據點(沿x軸)增長時,誤差項會增大。

多重共線性是指誤差項(也稱爲殘差)相互依賴的時間。

串行相關是指一個數據(特徵)是另一個特徵的條件(或完全依賴)。

我們這裏不會給出代碼,因爲它很簡單,我們的重點更多地放在深度學習部分,但數據質量必須有保障。

2.7、特徵工程

print('Total dataset has {} samples, and {} features.'.format(dataset_total_df.shape[0], dataset_total_df.shape[1]))

output >>> Total dataset has 2265 samples, and 112 features.

在添加所有類型的數據(相關資產,技術指標,基本面分析,傅里葉和Arima)之後,我們有2265天內的112個特徵(訓練數據只有1585天) 。

我們還將用自編碼器生成更多特徵。

2.7.1、特徵對於XGBoost的重要性

擁有如此多的功能,我們必須考慮它們是否真正表明了GS股票的方向。例如,我們在數據集中包含美元計價的LIBOR利率,因爲我們認爲LIBOR的變化可能表明經濟的變化,反過來,這可能表明GS股票行爲的變化。但我們需要用實踐說話。有許多方法可以測試特徵重要性,但我們將使用XGBoost,因爲它在分類和迴歸問題中都有相當不錯的表現。

由於數據集非常大,爲了演示,我們將僅使用技術指標。在實際特徵重要性測試中,所有選定的特徵都被證明很重要,因此我們在訓練GAN時不會去掉任何特徵。

regressor = xgb.XGBRegressor(gamma=0.0,n_estimators=150,base_score=0.7,colsample_bytree=1,learning_rate=0.05)

xgbModel = regressor.fit(X_train_FI,y_train_FI, eval_set = [(X_train_FI, y_train_FI), (X_test_FI, y_test_FI)], verbose=False)

fig = plt.figure(figsize=(8,8))

plt.xticks(rotation='vertical')

plt.bar([i for i in range(len(xgbModel.feature_importances_))], xgbModel.feature_importances_.tolist(), tick_label=X_test_FI.columns)

plt.title('Figure 6: Feature importance of the technical indicators.')

plt.show()

結果很明確,MA7,MACD和BB是其中的重要特徵。

我遵循相同的邏輯來對整個數據集執行特徵重要性測試,訓練花費的時間更長,結果更難以歸納。

2.8、使用棧式自編碼器提取高級特徵

在我們研究自編碼器之前,我們先討論激活函數。

2.8.1、激活函數 - GELU(高斯誤差)

最近提出的GELU,全稱爲Gaussian Error Linear Unites。在論文中,作者展示了使用GELU優於ReLU的幾個實例。 gelu也用於BERT,用於新聞情緒分析的NLP方法。

我們將使用GELU作爲自編碼器。

我在MXNet中實現了GELU。如果您按照代碼並將act_type ='relu'更改爲act_type ='gelu',則無法使用,除非您更改MXNet的實現。

讓我們來看一下GELU,ReLU和LeakyReLU(最後一個主要用於GAN - 我們也使用它)。

def gelu(x):

return 0.5 * x * (1 + math.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * math.pow(x, 3))))

def relu(x):

return max(x, 0)

def lrelu(x):

return max(0.01*x, x)

plt.figure(figsize=(15, 5))

plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=.5, hspace=None)

ranges_ = (-10, 3, .25)

plt.subplot(1, 2, 1)

plt.plot([i for i in np.arange(*ranges_)], [relu(i) for i in np.arange(*ranges_)], label='ReLU', marker='.')

plt.plot([i for i in np.arange(*ranges_)], [gelu(i) for i in np.arange(*ranges_)], label='GELU')

plt.hlines(0, -10, 3, colors='gray', linestyles='--', label='0')

plt.title('Figure 7: GELU as an activation function for autoencoders')

plt.ylabel('f(x) for GELU and ReLU')

plt.xlabel('x')

plt.legend()

plt.subplot(1, 2, 2)

plt.plot([i for i in np.arange(*ranges_)], [lrelu(i) for i in np.arange(*ranges_)], label='Leaky ReLU')

plt.hlines(0, -10, 3, colors='gray', linestyles='--', label='0')

plt.ylabel('f(x) for Leaky ReLU')

plt.xlabel('x')

plt.title('Figure 8: LeakyReLU')

plt.legend()

plt.show()

注意:在後面的文章中,我將嘗試使用U-Net,並嘗試利用卷積層並提取(並創建)關於股票的模式的更多特徵。現在,我們將只使用一個僅由Dense層組成的簡單自編碼器。

我們回到自編碼器的討論。

注意:我將在以後的文章中探討的一件事是刪除解碼器中的最後一層。通常,在自編碼器中,編碼器的數量==解碼器的數量。但是,我們希望提取更高級別的特徵(而不是創建相同的輸入),因此我們可以跳過解碼器中的最後一層。我們實現了這一點,在訓練期間創建了具有相同層數的編碼器和解碼器,但是當我們創建輸出時,我們去掉了最後一層,因爲這樣解碼器能包含更高級別的特徵。

我們從自編碼器中創建了112個特徵。由於我們只想擁有高級特徵(整體模式),我們將使用主成分分析(PCA)在新創建的112個特徵上創建特徵投資組合。這將減少數據的維度(列數)。 Eigen組合的描述能力將與原始的112個特徵相同。

注意,這純粹是實驗性的。我並非100%確定我描述的邏輯成立。但是人工智能和深度學習不就是一場徹頭徹尾的實驗嗎?

3、生成對抗網絡(GAN)

GAN如何運作?

如前所述,本文的目的不是詳細解釋深度學習背後的數學,而是展示其應用。當然,在我看來,對基礎和細節的理解是非常必要的。因此,我們將嘗試介紹GAN如何工作,以便讀者完全理解使用GAN預測股票價格變動的理由。如果您對GAN有經驗,請跳過此部分和下一部分(並跳到第4.2節)。

GAN網絡由兩個模型組成 - 生成器(G)和裁決器(D)。訓練GAN的步驟如下:

使用隨機數據(噪聲表示爲z),生成器試圖“生成”與真實數據無法區分或非常接近的數據。其目的是學習真實數據的分佈。

隨機生成的數據被擬合到裁決器中,裁決器充當分類器並試圖瞭解數據是來自生成器還是真實數據。 D估計輸入樣本到真實數據集的(分佈)概率。

然後,G和D的Loss通過生成器傳播回來。因此,生成器的Loss取決於生成器和裁決器。這幫助Generator瞭解真實數據分佈。如果生成器在生成真實數據(具有相同分佈)方面做得不好,則裁決器的工作將很容易區分生成與真實數據集。因此,裁決器的Loss將非常小。裁決器Loss越小,生成器Loss則越大。這使得創建裁決器比較困難,因爲裁決器太優秀會導致巨大的生成器Loss,使得生成器無法學習。

該過程一直持續到Discriminator無法再區分生成數據與實際數據區。

當組合在一起時,D和G像是在玩minmax遊戲(生成器試圖欺騙裁決器,即最小化z〜pz(z)[log(1-D(G(G) z)))]。裁決器想要通過最大化x〜pr(x)[logD(x)]來分離來自生成器D(G(z))的數據。但是,由於具有各自的損失函數,它是不清楚兩者如何匯合在一起(這就是爲什麼我們使用普通GAN的一些變種,例如Wasserstein GAN)。

注意:我不會在本文中包含GAN背後的完整代碼和強化學習部分。

3.1、爲什麼GAN用於股市預測

生成對抗網絡(GAN)主要用於創建逼真的圖像,繪畫和視頻剪輯。在我們的案例中,沒有很多GAN用於預測時間序列數據的應用。然而,主要思想應該是相同的 - 我們希望預測未來的股票變動。在未來,GS股票的模式和行爲應該或多或少相同(除非它開始以完全不同的方式運作,或者經濟急劇變化)。因此,我們希望“生成”未來的數據,這些數據將具有與我們已有的相似(當然不完全相同) - 歷史交易數據。所以,從理論上講,它應該有效。

在我們的例子中,我們將使用LSTM作爲時間序列生成器,並使用CNN作爲裁決器。

3.2、Metropolis-Hastings GAN和Wasserstein GAN

注意:接下來的內容我們假定讀者有一些GAN的經驗。

I、Metropolis-Hastings GAN

Uber的工程團隊最近對傳統GAN進行了改進,名爲Metropolis-Hastings GAN(MHGAN)。優步的方法背後的想法(正如他們所說)與谷歌和加州大學伯克利分校創建的另一種方法有點類似,稱爲判別式拒絕採樣(DRS)。基本上,當我們訓練GAN時,我們使用裁決器(D)的唯一目的是更好地訓練生成器(G)。通常,在訓練GAN之後我們不再使用D.然而,MHGAN和DRS嘗試使用D來選擇由G生成的接近實際數據分佈的樣本(MHGAN之間的微小差異是使用馬爾可夫鏈蒙特卡羅(MCMC)進行採樣)。

MHGAN採用從G生成的K個樣本。然後它依次運行K輸出並遵循接受規則(從裁決器創建)決定是接受當前樣本還是保留最後接受的樣本。最後保留的輸出是被認爲是G的實際輸出的輸出。

注意:MHGAN最初由優步在pytorch中實現。我只把它移植到MXNet / Gluon。

II、Wasserstein GAN

訓練GAN非常困難。模型可能永遠不會收斂,模式崩潰很容易發生。我們將使用名爲Wasserstein GAN。

同樣,我們不會詳細介紹,但最值得注意的點是:

我們知道GAN背後的主要目標是讓Generator開始將隨機噪聲轉換爲我們想要模仿的某些給定數據。因此,在GAN中,比較兩個分佈之間的相似性的想法是非常必要的。兩個最廣泛使用的指標是:

KL散度(Kullback-Leibler) - DKL(p‖q)=∫xp(x)logp(x)q(x)dx。當p(x)等於q(x)時,DKL爲零。

JS Divergence(Jensen-Shannon)。 JS散度以0和1爲界,與KL散度不同,它是對稱的,更平滑的。當損失從KL轉爲JS分歧時,GAN訓練取得了重大成功。

WGAN使用Wasserstein距離,W(pr,pg)=1Ksup‖f‖L≤Kx~pr[f(x)] - x~pg[f(x)](其中sup代表supremum),作爲損失函數(也稱爲地球移動者的距離,因爲它通常被解釋爲將一堆沙子移動到另一堆,兩個堆具有不同的概率分佈,在轉換期間使用最小能量)。與KL和JS差異相比,Wasserstein度量給出了一個平滑的度量(沒有突然的跳躍)。這使得它更適合在梯度下降期間創建穩定的學習過程。

此外,與KL和JS相比,Wasserstein距離幾乎無處不在。衆所周知,在反向傳播過程中,我們會區分損失函數以創建漸變,從而更新權重。因此,具有可微分的損失函數是非常重要的。

這是本文最難的部分。混合WGAN和MHGAN花了我三天時間。

3.3、生成器 - 單層RNN

3.3.1、LSTM還是GRU

如前所述,生成器使用LSTM(RNN)。 RNN用於時間序列數據,因爲它們跟蹤所有先前的數據點,並且可以捕獲隨時間發展的模式。由於RNN的特點,RNN很多時候都會受到梯度消失的影響 - 也就是說,在訓練期間權重變化變得非常之小,以至於網絡無法收斂到最小的損失(相反的狀況有時也會出現 - 梯度變得太大。這稱爲梯度爆炸,解決方法非常簡單 - 如果梯度開始超過某個常數,截斷它。GRU和長短期記憶(LSTM)解決了這兩個問題。它們兩者之間的最大區別是:1)GRU有2個Gate(更新和復位),LSTM有4個(更新,輸入,忘記和輸出),2)LSTM維持內存狀態,而GRU沒有, 3)LSTM在輸出Gate之前用非線性(sigmoid),GRU不用。

在大多數情況下,LSTM和GRU在準確性方面給出了類似的結果,但GRU的計算密度要低得多,因爲GRU的可訓練參數更少。不過LSTM應用更爲廣泛。

3.3.2、LSTM的架構

我們使用的LSTM架構非常簡單 - 一個LSTM層具有112個輸入單元(因爲我們在數據集中有112個特徵)和500個隱藏單元,一個Dense層具有1個輸出 - 每天的價格。初始化使用Xavier,我們將使用L1 Loss(這是L1正則化的平均絕對誤差損失)。

注意 - 在代碼中,您可以看到我們使用Adam(學習率爲.01)作爲優化器。大家暫時可以不用過多關注 - 有一節特別專門解釋我們使用的超參數以及我們如何優化這些超參數。

gan_num_features = dataset_total_df.shape[1]

sequence_length = 17

class RNNModel(gluon.Block):

def __init__(self, num_embed, num_hidden, num_layers, bidirectional=False, sequence_length=sequence_length, **kwargs):

super(RNNModel, self).__init__(**kwargs)

self.num_hidden = num_hidden

with self.name_scope():

self.rnn = rnn.LSTM(num_hidden, num_layers, input_size=num_embed, bidirectional=bidirectional, layout='TNC')

self.decoder = nn.Dense(1, in_units=num_hidden)

def forward(self, inputs, hidden):

output, hidden = self.rnn(inputs, hidden)

decoded = self.decoder(output.reshape((-1,self.num_hidden)))

return decoded, hidden

def begin_state(self, *args, **kwargs):

return self.rnn.begin_state(*args, **kwargs)

lstm_model = RNNModel(num_embed=gan_num_features, num_hidden=500, num_layers=1)

lstm_model.collect_params().initialize(mx.init.Xavier(), ctx=mx.cpu())

trainer = gluon.Trainer(lstm_model.collect_params(), 'adam', {'learning_rate': .01})

loss = gluon.loss.L1Loss()

我們在LSTM層中使用500個神經元並使用Xavier初始化。正則化使用L1。讓我們看看LSTM裏面有什麼。

print(lstm_model)

output >>>

RNNModel(

(rnn): LSTM(112 -> 500, TNC)

(decoder): Dense(500 -> 1, linear)

)

正如我們所看到的,LSTM的輸入是112個特徵(dataset_total_df.shape [1]),然後是LSTM層中的500個神經元,然後轉換爲單個輸出 - 股票價格值。

LSTM背後的邏輯是:我們需要17天(sequence_length)天數據(數據是每天GS股票的股票價格+當天所有其他特徵 - 相關資產,情緒等)並試圖預測第18天。然後我們將17天的窗口移動一天,並再次預測第18天。我們在整個數據集上進行迭代(當然是批量)。

在另一篇文章中,我將探討vanilla LSTM以及如何優化,例如:

使用雙向LSTM層 - 理論上,向後(從數據集的末尾到開頭)可能以某種方式幫助LSTM找出股票運行的模式。

使用棧式RNN架構 - 多個LSTM層。然而,我們可能最終過度擬合模型,因爲我們沒有大量數據(我們只有1,585天的數據)。

GRU - 如前所述,GRU的cell要簡單得多。

將注意力機制添加到RNN。

--------------------------------------------

3.3.3、學習率調度程序

最重要的超參數之一是學習率。在訓練神經網絡時,爲幾乎所有優化器(例如SGD,Adam或RMSProp)設置學習速率至關重要,因爲它既控制收斂速度又控制網絡的最終性能。最簡單的學習率策略之一是在整個培訓過程中具有固定的學習率。選擇較小的學習速率可以使優化器找到好的解決方案,但這是以限制收斂速度爲代價的。隨着時間訓練的推移改變學習率是比較好的折中方法。

schedule = CyclicalSchedule(TriangularSchedule, min_lr=0.5, max_lr=2, cycle_length=500)

iterations=1500

plt.plot([i+1 for i in range(iterations)],[schedule(i) for i in range(iterations)])

plt.title('Learning rate for each epoch')

plt.xlabel("Epoch")

plt.ylabel("Learning Rate")

plt.show()

3.3.4、如何防止過擬合和偏差 - 方差權衡

特徵衆多的神經網絡尤其需要防止過度擬合。

我們使用幾種技術來防止過度擬合:

首先確保數據質量。我們已經進行了統計檢查,並確保數據不會受到多重共線性或串行自相關的影響。此外,我們對每個特徵進行了權重檢查。最後還要注意初始特徵選擇(例如,選擇相關資產,技術指標等),這是通過一些關於股票市場運作方式背後的機制的領域知識來完成的。

正則化(或權重懲罰)。兩種最廣泛使用的正則化技術是LASSO(L1)和Ridge(L2)。 L1增加了平均絕對誤差,L2增加了平均誤差。簡單的解釋差異就是,L1同時進行變量選擇和參數收縮,而L2僅進行參數收縮並最終保留模型中的所有係數。在存在相關變量的情況下,L2可能是首選。此外,L2在最小二乘估計具有較高方差的情況下效果最佳。因此,這取決於我們的模型目標。兩種類型的正則化的影響是完全不同的。雖然它們都會對大權重進行懲罰,但L1正則化會導致零不可微函數。 L2正則化有利於較小的權重,但L1正則化有利於權重變爲零。因此,使用L1正則化,您可以得到一個稀疏模型 - 一個參數較少的模型。在這兩種情況下,L1和L2正則化模型的參數“收縮”,但在L1正則化的情況下,收縮直接影響模型的複雜性(參數的數量)。準確地說,L2在最小二乘估計具有較高方差的情況下效果最佳。 L1對異常值更穩健,在數據稀疏時使用,並創建特徵權重。我們將使用L1。

Dropout。 Dropout層隨機刪除隱藏圖層中的節點。

Dense-sparse-dense訓練。

Early stopping。

構建複雜神經網絡時的另一個重要需考慮因素是偏差 - 方差權衡。基本上,訓練神經網絡時我們得到的誤差是偏差,方差和不可約誤差的函數 - σ(由噪聲和隨機性引起的誤差)。權衡的最簡單公式是:誤差=偏差^ 2 +方差+σ。

偏差。偏差表現了模型的泛化能力。高偏差(欠擬合)意味着模型不能很好地處理未知數據。

方差。方差衡量模型對數據集變化的敏感性。高方差表示過度擬合。

3.4、裁決器 - 一維CNN

3.4.1、爲什麼CNN可以做裁決器?

我們通常將CNN用於與圖像相關的工作(分類,上下文提取等)。它們在提取特徵方面非常強大。例如,在狗的圖像中,第一個卷積層將檢測邊緣,第二個將開始檢測圓,第三個將檢測到鼻子。在我們的例子中,數據點形成小趨勢,小趨勢形成大趨勢,大趨勢形成模式。 CNN檢測特徵的能力可用於提取有關GS股票價格變動模式的信息。

使用CNN的另一個原因是CNN在空間數據上運行良好 - 這意味着彼此更接近的數據點彼此之間的相關性更高,而不是數值相關。對於時間序列數據,這也適用。在我們的例子中,每個數據點(對於每個特徵)是連續的每一天。很自然地假設彼此距離越近,彼此之間的相關性就越大。需要考慮的一件事(雖然這項工作沒有涉及)是週期性以及它如何改變(如果有的話)CNN。

注意:與本文中的許多其他部分一樣,使用CNN進行時間序列數據是實驗性的。我們將檢查結果,而不提供數學或其他研究。結果可能因使用不同的數據,激活函數等而有所不同。

3.4.2、CNN架構

如下是用MXNet打印的CNN。

Sequential(

(0): Conv1D(None -> 32, kernel_size=(5,), stride=(2,))

(1): LeakyReLU(0.01)

(2): Conv1D(None -> 64, kernel_size=(5,), stride=(2,))

(3): LeakyReLU(0.01)

(4): BatchNorm(axis=1, eps=1e-05, momentum=0.9, fix_gamma=False, use_global_stats=False, in_channels=None)

(5): Conv1D(None -> 128, kernel_size=(5,), stride=(2,))

(6): LeakyReLU(0.01)

(7): BatchNorm(axis=1, eps=1e-05, momentum=0.9, fix_gamma=False, use_global_stats=False, in_channels=None)

(8): Dense(None -> 220, linear)

(9): BatchNorm(axis=1, eps=1e-05, momentum=0.9, fix_gamma=False, use_global_stats=False, in_channels=None)

(10): LeakyReLU(0.01)

(11): Dense(None -> 220, linear)

(12): Activation(relu)

(13): Dense(None -> 1, linear)

)

3.5、超參數

我們將跟蹤和優化的超參數是:

batch_size:LSTM和CNN的批大小

cnn_lr:CNN的學習率

stride:CNN中的步長

lrelu_alpha:GAN中LeakyReLU的alpha

batchnorm_momentum:CNN中批量正則化的動量

padding:CNN中的padding

kernel_size:CNN中的內核大小

Dropout:LSTM中的Dropout

filters:初始filter數量

我們將訓練200多個epoch。

4、超參數優化

在200個epoch的GAN訓練之後,我們記錄MAE(這是LSTM中的LOSS函數,GG)並將其作爲獎勵值傳遞給強化學習,該學習將決定是否改變繼續訓練的超參數。如稍後所述,該方法嚴格用於試驗RL。

如果RL決定它將更新超參數,它將調用貝葉斯優化庫,該庫將提供下一個最佳預期的超級參數集

4.1、針對超參數優化的強化學習

爲什麼我們在超參數優化中使用強化學習?因爲股市一直在變化。即使我們設法訓練我們的GAN和LSTM以創建非常準確的結果,結果可能僅在一段時間內有效。意思是,我們需要不斷優化整個過程。爲了優化流程,我們可以:

添加或刪除特徵(例如添加可能相關的新股票或貨幣)

改善我們的深度學習模式。改進模型的最重要方法之一是通過超參數。一旦找到了一組超參數,我們需要決定何時更改它們以及何時使用已知的集合。此外,股票市場代表一個連續的空間,取決於數百萬參數。

注意:本文整個強化學習部分的目的面向研究。我們將使用GAN作爲環境探索不同的RL方法。在不使用RL的情況下,我們可以通過多種方式在深度學習模型上成功執行超參數優化。

注意:接下來的內容假定您對RL有一些瞭解 - 尤其是Policy optimization和Q-learning。

4.1.1、強化學習理論

在不解釋RL的基礎知識的情況下,我們將詳細介紹我們實現的具體方法。我們將使用無模型RL算法,原因很明顯我們不瞭解整個環境,因此沒有明確的模型來說明環境是如何工作的,我們不需要預測股票價格變動。我們將使用無模型RL的兩個流行的實現 - Policy optimization和Q學習。

Q-learning - 在Q-learning中我們學習從一個給定的狀態採取行動的值。 Q值是採取行動後的預期回報。我們將使用Rainbow,它是七種Q學習算法的組合。

Policy optimization - 在Policy optimization中,我們學習從給定狀態採取的操作。 (如果我們使用像Actor / Critic這樣的方法),我們也會學習處於給定狀態的值。我們將使用Proximal Policy Optimization。

構建RL算法的一個關鍵點是準確設置獎勵。它必須捕獲環境的所有方面以及代理與環境的交互。我們將獎勵R定義爲:

獎勵= 2 * lossG + lossD + accuracyG,

其中lossG,accuracyG和lossD分別是Generator的損失和準確性,以及Discriminator的損失。環境是GAN和LSTM訓練的結果。不同代理可以採取的行動是如何更改GAN的D和G網絡的超參數。

4.1.1.1、Rainbow

什麼是Rainbow?

Rainbow是一種基於Q學習的非策略深度強化學習算法,它將七種算法結合在一起:

DQN。 DQN是Q學習算法的擴展,其使用神經網絡來表示Q值。與監督(深度)學習類似,在DQN中,我們訓練神經網絡並嘗試最小化損失函數。我們通過隨機抽樣過渡(狀態,行動,獎勵)來訓練網絡。例如,這些層不僅可以是完全連接的層,也可以是卷積層。

Double Q learning。雙QL處理Q學習中的大問題,即高估偏差。

Prioritized replay。在vanilla DQN中,所有轉換都存儲在重放緩衝區中,並均勻地對此緩衝區進行採樣。然而,並非所有過渡在學習階段都同樣有益(這也使得學習效率低,因爲需要更多的epoch)。Prioritized replay不是均勻採樣,而是使用分佈,該分佈爲先前迭代中具有較高Q損失的樣本提供更高的概率。

Dueling networks。它通過使用兩個單獨的流(即,具有兩個不同的微型神經網絡)來改變Q learning架構。一個流用於value,一個用於advantage。它們都共享一個卷積編碼器。棘手的部分是流的合併 - 它使用了一個特殊的聚合器。

Multi-step learning。差異在於它使用N步返回計算Q值所以更準確。

分佈式RL。 Q學習使用平均估計Q值作爲目標值。但是,在許多情況下,Q值在不同情況下可能不同。分佈式RL可以直接學習(或近似)Q值的分佈,而不是對它們求平均值。同樣,數學要複雜得多,但對我們而言,好處是Q值的採樣更精確。

Noisy Nets。基本DQN實現了一個簡單的ε-貪婪機制來進行探索。這種探索方法有時效率低下。 Noisy Nets解決這個問題的方法是添加一個有噪聲的線性層。隨着時間的推移,網絡將學習如何忽略噪聲。但是這種學習在空間的不同部分以不同的速度進行。

4.1.1.2、PPO

近端策略優化(PPO)是一種無策略優化模型的強化學習。比實現其他算法要簡單得多,效果非常好。

我們爲什麼要使用PPO? PPO的一個優點是它直接學習策略,而不是間接地通過值(Q學習使用Q值來學習策略的方式)。它可以在連續action空間中很好地工作,這在我們的使用案例中是合適的,並且可以(通過平均值和標準偏差)學習分佈概率(如果將softmax作爲輸出)。

策略梯度方法的問題在於它們對步長選擇極其敏感 - 如果它很小,則需要太長時間(二階導數矩陣);如果它很大,會有很多噪聲顯著降低性能。由於策略的變化(以及獎勵和觀察變化的分佈),輸入數據是非平穩的。與監督學習相比,不當的步驟可能會更具破壞性,因爲它會影響下次訪問的整個分佈。 PPO可以解決這些問題。更重要的是,與其他一些方法相比的優點例如:

與ACER相比,它需要額外的代碼來保持非策略相關性,還需要一個replay緩衝區,或者對代理目標函數施加約束的TRPO(舊的和新的之間的KL分歧)要複雜得多)。這種約束用於控制變化過多的策略 - 這本身就會造成不穩定。 PPO通過利用剪切(在[1-ε,1 +ε]之間)代理目標函數和修改目標函數來減少計算(由約束創建),因爲具有非常大的更新。

與TRPO相比,它與在值和策略函數或輔助損失之間共享參數的算法兼容(儘管PPO也具有信任區域PO的增益)。

注意:爲了我們的訓練,我們不會過多地研究和優化RL方法,PPO和其他方法。相反,我們將採用可用的方法,並嘗試適應我們的GAN,LSTM和CNN模型的超參數優化過程。

4.1.2、進一步深入強化學習

進一步探索強化學習的一些想法:

接下來我將介紹的第一件事是使用增強隨機搜索(鏈接)作爲替代算法。該算法的作者已經設法獲得與其他最先進的方法(如PPO)類似的獎勵結果,但速度平均快15倍。

選擇獎勵函數非常重要。可以嘗試使用不同的函數作爲替代。

使用好奇心作爲exploration策略。

創建多agent體系結構。

4.2、貝葉斯優化

我們也可以使用貝葉斯優化來代替網格搜索,我們可以使用現有的庫。

4.2.1、高斯過程

5、結果

6、接下來的研究內容?

接下來,我將嘗試創建一個RL環境來測試交易算法,以決定何時以及如何交易。 GAN的輸出將是環境中的參數之一。

7、免責聲明

本文中提供的任何內容均不構成任何特定證券,證券組合,交易或投資策略適合任何特定人士的建議。期貨,股票和期權交易涉及巨大的損失風險,並不適合每個投資者。期貨,股票和期權的估值可能會波動,因此客戶的損失可能超過其原始投資。

使用所有交易策略需要您自擔風險。

還有許多細節需要探索 - 選擇數據特徵,選擇算法,調整算法等等。本文花了我2個星期才完成。而其實還有很多未涉及的方法。

人工智能與深度學習做量化請關注:AI量化(https://t.zsxq.com/RvfY37y) 星球限時免費,如需加入,請私信我獲得免費邀請碼!

零基礎學習Python與深度學習應用請關注星球:Python與深度學習 https://t.zsxq.com/bUFayZ3

微信公衆號:QTechAI

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章