溫馨提示:本案例隻作為學習研究用途,不構成投資建議。
比特币的價格資料是基于時間序列的,是以比特币的價格預測大多采用LSTM模型來實作。
長期短期記憶(LSTM)是一種特别适用于時間序列資料(或具有時間 / 空間 / 結構順序的資料,例如電影、句子等)的深度學習模型,是預測加密貨币的價格走向的理想模型。
本文主要寫了通過LSTM進行資料拟合,進而預測比特币的未來價格。
import需要使用的庫
import pandas as pd
import numpy as np
from sklearn.preprocessing import MinMaxScaler, LabelEncoder
from keras.models import Sequential
from keras.layers import LSTM, Dense, Dropout
from matplotlib import pyplot as plt
%matplotlib inline
資料分析
資料加載
讀取BTC的日交易資料
data = pd.read_csv(filepath_or_buffer="btc_data_day")
檢視資料可得,現在的資料一共有1380條,資料由Date、Open、High、Low、Close、Volume(BTC)、Volume(Currency)、Weighted Price這幾列組成。其中除去Date列以外,其餘的資料列都是float64資料類型。
data.info()
檢視下前10行的資料
data.head(10)
資料可視化
使用matplotlib将Weighted Price繪制出來,看下資料的分布跟走勢。在圖中我們發現了有一段資料0的部分,我們需要确認下資料是否有異常。
plt.plot(data['Weighted Price'], label='Price')
plt.ylabel('Price')
plt.legend()
plt.show()
異常資料處理
先檢視下資料是否含有nan的資料,可以看到我們的資料中沒有nan的資料
data.isnull().sum()
Date 0
Open 0
High 0
Low 0
Close 0
Volume (BTC) 0
Volume (Currency) 0
Weighted Price 0
dtype: int64
再檢視下0資料,可以看到我們的資料中含有0值,我們需要對0值做下處理
(data == 0).astype(int).any()
Date False
Open True
High True
Low True
Close True
Volume (BTC) True
Volume (Currency) True
Weighted Price True
dtype: bool
data['Weighted Price'].replace(0, np.nan, inplace=True)
data['Weighted Price'].fillna(method='ffill', inplace=True)
data['Open'].replace(0, np.nan, inplace=True)
data['Open'].fillna(method='ffill', inplace=True)
data['High'].replace(0, np.nan, inplace=True)
data['High'].fillna(method='ffill', inplace=True)
data['Low'].replace(0, np.nan, inplace=True)
data['Low'].fillna(method='ffill', inplace=True)
data['Close'].replace(0, np.nan, inplace=True)
data['Close'].fillna(method='ffill', inplace=True)
data['Volume (BTC)'].replace(0, np.nan, inplace=True)
data['Volume (BTC)'].fillna(method='ffill', inplace=True)
data['Volume (Currency)'].replace(0, np.nan, inplace=True)
data['Volume (Currency)'].fillna(method='ffill', inplace=True)
(data == 0).astype(int).any()
Date False
Open False
High False
Low False
Close False
Volume (BTC) False
Volume (Currency) False
Weighted Price False
dtype: bool
再看下資料的分布跟走勢,這個時候曲線已經非常的連續
plt.plot(data['Weighted Price'], label='Price')
plt.ylabel('Price')
plt.legend()
plt.show()
訓練資料集和測試資料集劃分
将資料歸一化到0-1
data_set = data.drop('Date', axis=1).values
data_set = data_set.astype('float32')
mms = MinMaxScaler(feature_range=(0, 1))
data_set = mms.fit_transform(data_set)
以2:8劃分測試資料集跟訓練資料集
ratio = 0.8
train_size = int(len(data_set) * ratio)
test_size = len(data_set) - train_size
train, test = data_set[0:train_size,:], data_set[train_size:len(data_set),:]
建立訓練資料集跟測試資料集,以1天作為視窗期來建立我們的訓練資料集跟測試資料集。
def create_dataset(data):
window = 1
label_index = 6
x, y = [], []
for i in range(len(data) - window):
x.append(data[i:(i + window), :])
y.append(data[i + window, label_index])
return np.array(x), np.array(y)
train_x, train_y = create_dataset(train)
test_x, test_y = create_dataset(test)
定義模型并訓練
這次我們使用一個簡單的模型,這個模型結構如下1. LSTM2. Dense。
這裡需要對LSTM的inputh shape做下說明, Input Shape的輸入次元為(batch_size, time steps, features)。其中,time steps值的是資料輸入的時候的時間視窗間隔,這裡我們使用1天作為時間視窗,并且我們的資料都是日資料,是以這裡我們的time steps為1。
長短期記憶(Long short-term memory, LSTM)是一種特殊的RNN,主要是為了解決長序列訓練過程中的梯度消失和梯度爆炸問題,這裡先簡單介紹下LSTM。
從LSTM的網絡結構示意圖中,可以看到LSTM其實是一個小型的模型,他包含了3個sigmoid激活函數,2個tanh激活函數,3個乘法,1個加法。
細胞狀态
細胞狀态是LSTM的核心,他是上圖中最上面的那根黑線, 在這根黑線下面是一些門,我們在後面介紹。細胞狀态會根據每個門的結果,來得到更新。下面我們介紹下這些門,你就會了解細胞狀态的流程。
LSTM網絡能通過一種被稱為門的結構對細胞狀态進行删除或者添加資訊。門能夠有選擇性的決定讓哪些資訊通過。門的結構是一個sigmoid層和一個點乘操作的組合。因為sigmoid層的輸出是0-1的值,0表示都不能通過,1表示都能通過。一個LSTM裡面包含三個門來控制細胞狀态。下面我們來一一介紹下這些門。
遺忘門
LSTM的第一步就是決定細胞狀态需要丢棄哪些資訊。這部分操作是通過一個稱為忘記門的sigmoid單元來處理的。我們來看下動畫示意圖,

我們可以看到,遺忘門通過檢視$h_{l-1}$和$x_{t}$資訊來輸出一個0-1之間的向量,該向量裡面的0-1值表示細胞狀态$C_{t-1}$中的哪些資訊保留或丢棄多少。0表示不保留,1表示都保留。
數學表達式: $f_{t}=\sigma\left(W_{f} \cdot\left[h_{t-1}, x_{t}\right]+b_{f}\right)$
輸入門
下一步是決定給細胞狀态添加哪些新的資訊,這個步驟是通過輸入門開完成的。我們先來看下動畫示意圖,
我們看到了$h_{l-1}$和$x_{t}$的資訊又被放入了一個遺忘門(sigmoid)跟輸入門(tanh)中。因為遺忘門的輸出結果是0-1的值,是以,如果遺忘門輸出的是0的話,輸入門後的結果$C_{i}$将不會被添加到目前的細胞狀态中,如果是1,會全部的被添加到細胞狀态中,是以這裡的遺忘門的作用是将輸入門的結果選擇性的添加到細胞狀态中。
數學公式為: $C_{t}=f_{t} * C_{t-1}+i_{t} * \\tilde{C}_{t}$
輸出門
更新完細胞狀态後需要根據$h_{l-1}$和$x_{t}$輸入的和來判斷輸出細胞的哪些狀态特征,這裡需要将輸入經過一個稱為輸出門的sigmoid層得到判斷條件,然後将細胞狀态經過tanh層得到一個-1~1之間值的向量,該向量與輸出門得到的判斷條件相乘就得到了最終該RNN單元的輸出, 動畫示意圖如下
def create_model():
model = Sequential()
model.add(LSTM(50, input_shape=(train_x.shape[1], train_x.shape[2])))
model.add(Dense(1))
model.compile(loss='mae', optimizer='adam')
model.summary()
return model
model = create_model()
history = model.fit(train_x, train_y, epochs=80, batch_size=64, validation_data=(test_x, test_y), verbose=1, shuffle=False)
plt.plot(history.history['loss'], label='train')
plt.plot(history.history['val_loss'], label='test')
plt.legend()
plt.show()
train_x, train_y = create_dataset(train)
test_x, test_y = create_dataset(test)
預測
predict = model.predict(test_x)
plt.plot(predict, label='predict')
plt.plot(test_y, label='ground true')
plt.legend()
plt.show()
目前利用機器學習預測比特币長期價格走勢還是非常困難的,本文隻能作為學習案例使用。該案例之後會上線與矩池雲的Demo鏡像之中,感興趣的使用者可以直接體驗。