温馨提示:本案例只作为学习研究用途,不构成投资建议。
比特币的价格数据是基于时间序列的,因此比特币的价格预测大多采用LSTM模型来实现。
长期短期记忆(LSTM)是一种特别适用于时间序列数据(或具有时间 / 空间 / 结构顺序的数据,例如电影、句子等)的深度学习模型,是预测加密货币的价格走向的理想模型。
本文主要写了通过LSTM进行数据拟合,从而预测比特币的未来价格。
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镜像之中,感兴趣的用户可以直接体验。