完整原始碼可在微信公眾號:「01二進位制」後臺回覆:「股市分析」獲取
閱讀此文前建議先閱讀《找物件的過程中,我竟然理解了什麼是機器學習!》
前言
相信大家這幾天或多或少的都開始關注到股市了,雖然我還不是很懂裡面的一些套路,但是從最近各個公眾號的推送裡面,我也看到最近的股市確實是形勢大好。對很多人來說,股票就和房價一樣,他的升與降牽動著眾多人的心。這幾天很多qq群、微信群都開始討論起股票了,各位坊間大神也紛紛開始預測各種股票走勢了。
股票價格是典型的時間序列資料(簡稱時序資料),會受到經濟環境、政府政策、人為操作多種複雜因素的影響,所以說股票價格預測是一件非常唬人的事情。但是基於歷史資料,以股票價格為例,介紹如何對時序資料進行預測,仍然值得一做。
不過今天我們不聊股市,因為我也不是很懂,今天我們來聊聊我們知道的東西。如今深度學習在金融市場的應用越來越廣泛,我們能否利用已有的歷史資料通過深度學習的方式來進行預測呢?
準備工作
答案自然是可以的,雖然我們無法非常準確的進行預測,但是作為一個深度學習入手的專案是完完全全可以的。
實驗環境
老樣子,以免出現因環境導致的問題,先將實驗環境列出:
- MacOS 10.14.3
- Python 3.6.8(Anaconda)
- Jupyter Notebook
- 使用的包有:
- TensorFlow
- Keras
- matplotlib
- pandas
資料
此次實驗,我們採用STATWORX的S&P 500
股價資料,該資料集爬取自Google Finance API,已經進行過缺失值處理。他們的官方網站是:www.statworx.com/。
資料集可在微信公眾號:「01二進位制」後臺回覆:「股市資料集」獲取
資料預覽
在這裡我們還是使用pandas,主要用於資料清洗和整理
import pandas as pd
data=pd.read_csv('data/data_stocks.csv')
data.info()
複製程式碼
執行data.info()
可以檢視特徵的概要:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 41266 entries, 0 to 41265
Columns: 502 entries, DATE to NYSE.ZTS
dtypes: float64(501), int64(1)
memory usage: 158.0 MB
複製程式碼
從上述結果可知:該資料集資料共502列,41266行,502列分別為:
DATE
:該行資料的時間戳SP500
:可以理解為大盤指數- 其他:可以理解為500支個股的股價
檢視資料的前五行
data.head()
複製程式碼
繪製大盤趨勢折線圖
plt.plot(data['SP500'])
複製程式碼
結果如下:
Tips:此次實驗的除錯環境為Jupyter Notebook,我們最好在開頭匯入
matplotlib
包的時候在加一行%matplotlib inline
,這樣就可以在Jupyter Notebook中內嵌繪圖,並且省略掉plt.show()這一步。
資料整理
在這裡,我們需要將資料集分為訓練和測試資料。分配比為8:2,即訓練資料包含總資料集的80%。當然在這之前我們需要先將DATE
這個無關變數捨去。
data.drop('DATE', axis=1, inplace=True)
data_train = data.iloc[:int(data.shape[0] * 0.8), :]
data_test = data.iloc[int(data.shape[0] * 0.8):, :]
複製程式碼
我們來檢視一下訓練集和測試集的shape
:
資料歸一化
將屬性縮放到一個指定的最大和最小值(通常是(-1,1))之間,這可以通過sklearn.preprocessing.MinMaxScaler
類實現。
使用這種方法的目的包括:
-
對於方差非常小的屬性可以增強其穩定性。
-
維持稀疏矩陣中為0的條目。
scaler = MinMaxScaler(feature_range=(-1, 1))
scaler.fit(data_train)
data_train = scaler.transform(data_train)
data_test = scaler.transform(data_test)
複製程式碼
預測
在此次預測過程中,我採用TensorFlow這個深度學習框架,它是目前領先的深度學習和神經網路計算框架。這一部分推薦有基礎的人閱讀,在這推薦我還在整理的TensorFlow系列,有興趣的可以瞭解下。
這裡我們需要解決的問題是:使用當前時刻的500支個股股價,預測當前時刻的大盤指數。即一個迴歸問題,輸入共500維特徵,輸出一維,即[None, 500] => [None, 1]
設定X與Y
X_train = data_train[:, 1:]
y_train = data_train[:, 0]
X_test = data_test[:, 1:]
y_test = data_test[:, 0]
複製程式碼
這裡的x和y是已經分類好的資料集,只是用X和Y兩個變數表示,可以理解為函式裡面的X和Y。
設定超引數
input_dim = X_train.shape[1]
output_dim = 1
hidden_1 = 1024
hidden_2 = 512
hidden_3 = 256
hidden_4 = 128
batch_size = 256
epochs = 10
複製程式碼
這裡我們設定了幾個超引數,input_dim
表示輸入資料的維度,即500。output_dim
表示輸出資料的維度,即1。在該模型中設定了4層隱藏層,第一層包含1024個神經元,略大於輸入大小的兩倍。 後續的隱藏層總是前一層的一半大小,即分別為512,256和128個神經元。 每個後續層的神經元數量的減少壓縮了網路在先前層中識別的資訊。 當然,其他網路架構和神經元配置也是可能的,只是由於本文只是一個入門的專案所以並未深究。
在機器學習中,超引數是在開始學習過程之前設定值的引數,而不是通過訓練得到的引數資料。通常情況下,需要對超引數進行優化,給學習機選擇一組最優超引數,以提高學習的效能和效果。
設定佔位符(placeholder)
X = tf.placeholder(shape=[None, input_dim], dtype=tf.float32)
Y = tf.placeholder(shape=[None], dtype=tf.float32)
複製程式碼
為了適應我們的模型,我們需要兩個佔位符:X(神經網路的輸入)和Y(神經網路的輸出)。
設定神經網路
根據之前設定好的超引數進行神經網路的配置,其中w為權重,b為偏置值
# 第一層
W1 = tf.get_variable('W1', [input_dim, hidden_1], initializer=tf.contrib.layers.xavier_initializer(seed=1))
b1 = tf.get_variable('b1', [hidden_1], initializer=tf.zeros_initializer())
# 第二層
W2 = tf.get_variable('W2', [hidden_1, hidden_2], initializer=tf.contrib.layers.xavier_initializer(seed=1))
b2 = tf.get_variable('b2', [hidden_2], initializer=tf.zeros_initializer())
# 第三層
W3 = tf.get_variable('W3', [hidden_2, hidden_3], initializer=tf.contrib.layers.xavier_initializer(seed=1))
b3 = tf.get_variable('b3', [hidden_3], initializer=tf.zeros_initializer())
# 第四層
W4 = tf.get_variable('W4', [hidden_3, hidden_4], initializer=tf.contrib.layers.xavier_initializer(seed=1))
b4 = tf.get_variable('b4', [hidden_4], initializer=tf.zeros_initializer())
# 輸出層
W5 = tf.get_variable('W5', [hidden_4, output_dim], initializer=tf.contrib.layers.xavier_initializer(seed=1))
b5 = tf.get_variable('b5', [output_dim], initializer=tf.zeros_initializer())
複製程式碼
瞭解輸入層,隱藏層和輸出層之間所需的變數尺寸非常重要。作為多層感知器(MLP,這裡使用的網路型別)的一個經驗法則,前一層的第二維是當前層中權重矩陣的第一維。這可能聽起來很複雜,但實質上只是每個圖層都將其輸出作為輸入傳遞到下一圖層。偏差維度等於當前圖層的權重矩陣的第二維度,其對應於該層中的神經元的數量。
設定網路體系結構
在定義所需的權重和偏置變數後,需要指定網路拓撲結構和網路結構。因此,佔位符(資料)和變數(權重和偏置)需要組合成一個連續矩陣乘法系統。
h1 = tf.nn.relu(tf.add(tf.matmul(X, W1), b1))
h2 = tf.nn.relu(tf.add(tf.matmul(h1, W2), b2))
h3 = tf.nn.relu(tf.add(tf.matmul(h2, W3), b3))
h4 = tf.nn.relu(tf.add(tf.matmul(h3, W4), b4))
out = tf.transpose(tf.add(tf.matmul(h4, W5), b5))
複製程式碼
此外,網路的隱藏層需要被啟用函式啟用。啟用函式是網路體系結構的重要組成部分,因為它們將非線性引入到系統中。這裡採用最常見的ReLU啟用函式。
下圖說明了網路架構。 該模型由三個主要構建塊組成。 輸入層,隱藏層和輸出層。 該體系結構稱為前饋網路。 前饋表示該批資料僅從左向右流動。 其他網路架構,例如遞迴神經網路,也允許資料在網路中“向後”流動。
設定損失函式(loss function)和優化器(Optimizer)
loss = tf.reduce_mean(tf.squared_difference(out, Y))
optimizer = tf.train.AdamOptimizer().minimize(loss)
複製程式碼
這一部分沒啥好說的,記住這麼寫就可以了,想了解的話可以去我的TensorFlow中瞭解下。
執行訓練過程
在定義好神經網路的佔位符,結構,損失函式函式和優化器之後,我們就可以開始對模型進行訓練了。訓練資料集分為n / batch_size批次,這些批次按順序送入網路。 此時佔位符X和Y開始起作用。 它們儲存輸入和目標資料,並將它們作為輸入和目標呈現給網路。
資料X分批次流經網路,直到到達輸出層。 在那裡,TensorFlow將模型預測與當前批次中實際觀察到的目標Y進行比較。 之後,TensorFlow進行優化步驟並更新與所選學習方案相對應的網路引數。 更新了權重和偏差後,對下一批進行取樣,並重復該過程。 該過程將繼續,直到所有批次都已呈現給網路。對所有資料進行一次全面掃描被稱為一個epoch(輪)。
一旦達到了最大數量的epochs或使用者定義的另一個停止標準,網路的訓練就會停止。
with tf.Session() as sess:
# 初始化所有變數
sess.run(tf.global_variables_initializer())
for e in range(epochs):
# 將資料打亂
shuffle_indices = np.random.permutation(np.arange(y_train.shape[0]))
X_train = X_train[shuffle_indices]
y_train = y_train[shuffle_indices]
for i in range(y_train.shape[0] // batch_size):
start = i * batch_size
batch_x = X_train[start : start + batch_size]
batch_y = y_train[start : start + batch_size]
sess.run(optimizer, feed_dict={X: batch_x, Y: batch_y})
if i % 50 == 0:
print('MSE Train:', sess.run(loss, feed_dict={X: X_train, Y: y_train}))
print('MSE Test:', sess.run(loss, feed_dict={X: X_test, Y: y_test}))
y_pred = sess.run(out, feed_dict={X: X_test})
y_pred = np.squeeze(y_pred)
plt.plot(y_test, label='test')
plt.plot(y_pred, label='pred')
plt.title('Epoch ' + str(e) + ', Batch ' + str(i))
plt.legend()
plt.show()
複製程式碼
執行結果為:
MSE Train: 9.56518e-05
MSE Test: 0.0025863606
MSE Train: 6.0618047e-05
MSE Test: 0.0025002975
MSE Train: 0.00014856807
MSE Test: 0.0017371146
MSE Train: 0.00016200903
MSE Test: 0.0025396077
MSE Train: 0.00010259051
MSE Test: 0.0030134947
MSE Train: 7.979905e-05
MSE Test: 0.0023832247
MSE Train: 5.92488e-05
MSE Test: 0.0032762515
MSE Train: 8.747634e-05
MSE Test: 0.004848172
MSE Train: 8.5051965e-05
MSE Test: 0.0032768336
複製程式碼
最後測試集的loss在0.003左右,可以說是比較精確了。
視覺化訓練結果:
有很多方法可以進一步改善這一結果:增加隱藏層和改進神經元的設計,選擇不同的初始化和啟用方案,提前停止等等。 此外,不同型別的深度學習模型,例如迴圈神經網路,可以在此任務上實現更好的效能。 但是,這不是這篇介紹性文章的範圍。有興趣的小夥伴可以自行查詢資料。
結論
正如開頭所說,股票的價格會受到經濟環境、政府政策、人為操作多種複雜因素的影響,真正想要預測股市走向單靠這篇文章裡面所敘述的遠遠不夠,本文旨在結合時下熱點進行一次有關TensorFlow的技術推薦。
TensorFlow的釋出是深度學習研究中的一個里程碑事件。作為一個學生,筆者也在積極的學習中,有興趣學習的小夥伴可以在公眾號後臺(就是文末的那個公眾號)回覆「TensorFlow視訊」獲取一份質量較高的TensorFlow視訊,也可以新增我的微信一起交流進步。
完整原始碼可在微信公眾號:「01二進位制」後臺回覆:「股市分析」獲取
參考資料
“萬水千山都是情,給個關注行不行?”