矩池云 | 利用LSTM框架实时预测比特币价格
温馨提示:本案例只作为学习研究用途,不构成投资建议。
比特币的价格数据是基于时间序列的,因此比特币的价格预测大多采用 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 镜像之中,感兴趣的用户可以直接体验。