keras深度學習預測比特幣走勢
一,專案目標
利用以往資料對比特幣走勢進行預測,使用卷積神經網路進行模型建設,後期通過修整優化模型效果,提升正確率
二,專案條件
專案要求使用的庫已經打包成requirements.txt
檔案在附件
三,專案具體步驟
1.匯入資料
比特幣以往的資料儲存在bitcoin_historical_prices.csv檔案中,檔案裡面一共有7個變數: - 日期:觀察日期 - ISO-周:給定年的週數 - 開盤價:一定時間內的開市價 - 最高價:一定時間內的最高價 - 最低價:一定時間內的最低價 - 收盤價:一定時間內的收盤價 - 交易量:一定時間內的交易數量 - 市場資本化值:這是由市場上限=價格X迴圈供應量計算的
2.資料整理
為了避免以往的資料影響 我們只取得2016年以後的資料
以收盤價close和成交量作為主要資料
# 首先 我們只保留最近的資料 bitcoin_recent = bitcoin[bitcoin['date'] >= '2016-01-01'] # 讓我們只保留接近和體積變數。我們可以在另一個時間使用其他變數。 bitcoin_recent = bitcoin_recent[['date', 'iso_week', 'close', 'volume']] # 對資料進行規範化 bitcoin_recent['close_point_relative_normalization'] = bitcoin_recent.groupby('iso_week')['close'].apply( lambda x: normalizations.point_relative_normalization(x))
*資料的標準化(normalization)是將資料按比例縮放,使之落入一個小的特定區間。在某些比較和評價的指標處理中經常會用到,去除資料的單位限制,將其轉化為無量綱的純數值,便於不同單位或量級的指標能夠進行比較和加權。
# 劃分訓練集和測試集 boundary = int(0.8 * bitcoin_recent['iso_week'].nunique()) train_set_weeks = bitcoin_recent['iso_week'].unique()[0:boundary] test_set_weeks = bitcoin_recent[~bitcoin_recent['iso_week'].isin(train_set_weeks)]['iso_week'].unique() # 現在建立單獨的資料集 train_dataset = bitcoin_recent[bitcoin_recent['iso_week'].isin(train_set_weeks)] test_dataset = bitcoin_recent[bitcoin_recent['iso_week'].isin(test_set_weeks)]
3.建立模型
我們的資料集包含日常觀察和每個觀察影響未來的觀察。此外,我們有興趣預測未來一週(即七天)的比特幣價格。由於這些原因,我們選擇了引數週期週期長度和數字觀測值如下:
-
period_length:用作訓練輸入的週期的大小。我們的時期是在不同的星期組織的。我們將使用7天的時間來預測未來的一週。
-
number_of_observations:我們的資料集有多少個不同的週期?我們在資料集中有77周可用,鑑於我們將使用最後一週在每個時期測試LSTM網路,我們將使用77-1=76個週期對其進行訓練。
from keras.models import Sequential
from keras.layers.recurrent import LSTM
from keras.layers.core import Dense, Activation
period_length = 7
number_of_periods = 76
def build_model(period_length, number_of_periods, batch_size=1):
"""
Builds an LSTM model using Keras. This function
works as a simple wrapper for a manually created
model.
Parameters
----------
period_length: int
The size of each observation used as input.
number_of_periods: int
The number of periods available in the
dataset.
batch_size: int
The size of the batch used in each training
period.
Returns
-------
model: Keras model
Compiled Keras model that can be trained
and stored in disk.
"""
model = Sequential()
model.add(LSTM(
units=period_length,
batch_input_shape=(batch_size, number_of_periods, period_length),
input_shape=(number_of_periods, period_length),
return_sequences=False, stateful=False))
# 常用層Dense層
model.add(Dense(units=period_length))
# Activation層
model.add(Activation("linear"))
# 編譯模型以供訓練
model.compile(loss="mse", optimizer="rmsprop")
return model
# 模型儲存
model = build_model(period_length=period_length, number_of_periods=number_of_periods)
model.save('bitcoin_lstm_v0.h5')
常用層引數:
引數:
units:大於0的整數,代表該層的輸出維度。
activation:啟用函式,為預定義的啟用函式名(參考啟用函式),或逐元素(element-wise)的Theano函式。如果不指定該引數,將不會使用任何啟用函式(即使用線性啟用函式:a(x)=x)
use_bias: 布林值,是否使用偏置項
kernel_initializer:權值初始化方法,為預定義初始化方法名的字串,或用於初始化權重的初始化器。參考initializers
bias_initializer:偏置向量初始化方法,為預定義初始化方法名的字串,或用於初始化偏置向量的初始化器。參考initializers
kernel_regularizer:施加在權重上的正則項,為Regularizer物件
bias_regularizer:施加在偏置向量上的正則項,為Regularizer物件
activity_regularizer:施加在輸出上的正則項,為Regularizer物件
kernel_constraints:施加在權重上的約束項,為Constraints物件
bias_constraints:施加在偏置上的約束項,為Constraints物件
Activation層引數
activation:將要使用的啟用函式,為預定義啟用函式名或一個Tensorflow/Theano的函式。參考啟用函式
編譯模型引數
optimizer:優化器,為預定義優化器名或優化器物件,參考優化器
loss:目標函式,為預定義損失函式名或一個目標函式,參考目標函式
metrics:列表,包含評估模型在訓練和測試時的效能的指標,典型用法是metrics=['accuracy']如果要在多輸出模型中為不同的輸出指定不同的指標,可像該引數傳遞一個字典,例如metrics={'ouput_a': 'accuracy'}
sample_weight_mode:如果你需要按時間步為樣本賦權(2D權矩陣),將該值設為“temporal”。預設為“None”,代表按樣本賦權(1D權)。如果模型有多個輸出,可以向該引數傳入指定sample_weight_mode的字典或列表。在下面fit函式的解釋中有相關的參考內容。
kwargs:使用TensorFlow作為後端請忽略該引數,若使用Theano作為後端,kwargs的值將會傳遞給 K.function
4.構建深度學習系統
1.讀取之前構建的訓練集和測試集
train = pd.read_csv('data/train_dataset.csv')
data = create_groups(train['close_point_relative_normalization'].values)
# 把資料分成7行1列的結構
X_train = data[:-1,:].reshape(1, 76, 7)
Y_validation = data[-1].reshape(1, 7)
2.匯入模型
model = load_model('bitcoin_lstm_v0.h5')
3.模型訓練
history = model.fit(
x=X_train, y=Y_validation,
batch_size=32, epochs=100)
4.做出預測
# 預測下次函式
def denormalize(series, last_value):
result = last_value * (series + 1)
return result
predictions = model.predict(x=X_train)[0]
last_weeks_value = train[train['date'] == train['date'].max()]['close'].values[0]
denormalized_prediction = denormalize(predictions, last_weeks_value)
5.畫出影象
pd.DataFrame(denormalized_prediction).plot(linewidth=2, figsize=(6, 4), color='#d35400', grid=True)
6.得出結論
由圖可知,下週會漲30%左右
5.重新評估模型
方法一: 採用MSE
# 載入訓練集和測試集
test = pd.read_csv('data/test_dataset.csv')
train = pd.read_csv('data/train_dataset.csv')
# 劃分資料
train_data = create_groups(
train['close_point_relative_normalization'].values)
test_data = create_groups(
test['close_point_relative_normalization'].values)
X_train, Y_train = split_lstm_input(train_data)
# 匯入模型
model = load_model('bitcoin_lstm_v0.h5')
model_history = train_model(model=model, X=X_train, Y=Y_train, epochs=100, version=0, run_number=0)
LSTM模型評價
讓我們來評估我們的模型是如何針對未知資料進行的。我們的模型在76周內被訓練來預測接下來的幾周,即7天的序列。當我們開始這個專案時,我們將原始資料集劃分為一個測試集和一個驗證集。現在我們將採用最初訓練的網路——包含76周——並用它從驗證集中預測所有19周。
為了做到這一點,我們需要一個76周的序列作為預測的資料。為了以連續的方式獲得資料,我們將訓練集和驗證集結合起來,然後從系列開始移動76個視窗直到結束-1。我們最後留下一個,因為這是我們可以做的最終目標預測。
在這些迭代中的每一個,我們的LSTM模型產生7天的預測。我們接受這些預測並把它們分開。然後,我們將預測的系列與驗證集中的所有周進行比較。我們這樣做是通過計算MSE和MAPE的最後一系列
# 把訓練集和驗證集結合起來
combined_set = np.concatenate((train_data, test_data), axis=1)
evaluated_weeks = []
for i in range(0, test_data.shape[1]):
input_series = combined_set[0:,i:i+77]
X_test = input_series[0:,:-1].reshape(1, input_series.shape[1] - 1, 7)
Y_test = input_series[0:,-1:][0]
result = model.evaluate(x=X_test, y=Y_test, verbose=0)
evaluated_weeks.append(result)
ax = pd.Series(evaluated_weeks).plot(drawstyle="steps-post",
figsize=(14,4),
linewidth=2,
color='#2c3e50',
grid=True,
title='Mean Squared Error (MSE) for Test Data')
y = [i for i in range(0, len(evaluated_weeks))]
yint = range(min(y), math.ceil(max(y))+1)
plt.xticks(yint)
ax.set_xlabel("Predicted Week")
ax.set_ylabel("MSE")
得到影象:
模型結果解釋 MSE對於我們的問題來說是一個很好的損失函式,但其結果很難解釋。我們使用兩個實用函式來幫助解釋我們的結果:根均方誤差(rmse())和平均絕對誤差(mape())。我們將執行這些功能的觀察和預測系列。
方法二:predict()方法
# 構建一個新的檔案
predicted_weeks = []
for i in range(0, test_data.shape[1]):
input_series = combined_set[0:,i:i+76]
predicted_weeks.append(model.predict(input_series))
predicted_days = []
for week in predicted_weeks:
predicted_days += list(week[0])
combined = pd.concat([train, test])
last_day = datetime.strptime(train['date'].max(), '%Y-%m-%d')
list_of_days = []
for days in range(1, len(predicted_days) + 1):
D = (last_day + timedelta(days=days)).strftime('%Y-%m-%d')
list_of_days.append(D)
predicted = pd.DataFrame({
'date': list_of_days,
'close_point_relative_normalization': predicted_days
})
combined['date'] = combined['date'].apply(
lambda x: datetime.strptime(x, '%Y-%m-%d'))
predicted['date'] = predicted['date'].apply(lambda x: datetime.strptime(x, '%Y-%m-%d'))
observed = combined[combined['date'] > train['date'].max()]
將預測值與實際值比較
def plot_two_series(A, B, variable, title):
"""
Plots two series using the same `date` index.
Parameters
----------
A, B: pd.DataFrame
Dataframe with a `date` key and a variable
passed in the `variable` parameter. Parameter A
represents the "Observed" series and B the "Predicted"
series. These will be labelled respectivelly.
variable: str
Variable to use in plot.
title: str
Plot title.
"""
plt.figure(figsize=(14,4))
plt.xlabel('Real and predicted')
ax1 = A.set_index('date')[variable].plot(
linewidth=2, color='#d35400', grid=True, label='Observed', title=title)
ax2 = B.set_index('date')[variable].plot(
linewidth=2, color='grey', grid=True, label='Predicted')
ax1.set_xlabel("Predicted Week")
ax1.set_ylabel("Predicted Values")
h1, l1 = ax1.get_legend_handles_labels()
h2, l2 = ax2.get_legend_handles_labels()
plt.legend(l1+l2, loc=2)
plt.show()
plot_two_series(observed, predicted,
variable='close_point_relative_normalization',
title='Normalized Predictions per Week')
得到影象: