#-------------------------------main.py-----------------------------
# -*- coding: utf-8 -*-
"""
比特币价格分析
任务:搭建深度神经网络LSTM,使用比特币的历史价格数据对其未来价格进行预测。
"""
from keras.models import load_model
import os
import pandas as pd
import matplotlib.pyplot as plt
import config
import utils
# 是否加载已经训练好的模型
IS_LOAD_MODEL = True
def main():
"""
主函数
"""
# 加载数据集
all_daily_df = utils.load_data()
# 对时序数据进行差分操作
stationary_df = utils.make_stationary_seq(all_daily_df, vis=False)
# 构建可用于模型的数据
train_data, test_data = utils.make_data_for_model(stationary_df)
# 归一化数据
y_scaler, X_train, y_train, X_test, y_test = utils.scale_data(train_data, test_data)
if not IS_LOAD_MODEL:
# 训练LSTM模型
lstm_model = utils.fit_lstm(X_train, y_train)
else:
# 加载LSTM模型
if os.path.exists(config.model_file):
lstm_model = load_model(config.model_file)
else:
print('{}模型文件不存在'.format(config.model_file))
return
# 验证模型
test_dates = test_data.index.tolist()
pred_daily_df = pd.DataFrame(columns=['True Value', 'Pred Value'], index=test_dates)
pred_daily_df['True Value'] = all_daily_df[config.raw_label_col]
for i, test_date in enumerate(test_dates):
X = X_test[i].reshape(1, -1) # 将一天的数据特征转成行向量
y_pred = utils.forecast_lstm(lstm_model, X)
# scale反向操作,恢复数据范围
rescaled_y_pred = y_scaler.inverse_transform(y_pred.reshape(-1, 1))[0, 0]
# 差分反向操作,恢复数据的值:加上前一天的真实标签
previous_date = test_date - pd.DateOffset(days=1)
recoverd_y_pred = rescaled_y_pred + all_daily_df.loc[previous_date][config.raw_label_col]
# 保存数据
pred_daily_df.loc[test_date, 'Pred Value'] = recoverd_y_pred
print('Date={}, 真实值={}, 预测值={}'.format(test_date,
all_daily_df.loc[test_date][config.raw_label_col],
recoverd_y_pred))
# 保存结果
pred_daily_df.to_csv(os.path.join(config.output_path, 'pred_daily_df.csv'))
pred_daily_df.plot()
plt.savefig(os.path.join(config.output_path, 'pred_daily_df.png'))
plt.show()
if __name__ == '__main__':
main()
#----------------------------------utils.py--------------------------------------
# -*- coding: utf-8 -*-
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
import config
def load_data():
"""
加载数据集,进行按天重采样,并且将时间设置为索引
返回:
- proc_daily_df:处理后的数据集
"""
data_df = pd.read_csv(config.data_file)
data_df['date'] = pd.to_datetime(data_df['Timestamp'], unit='s').dt.date
data_df.set_index('date', inplace=True)
data_df.index = pd.to_datetime(data_df.index)
# 确保按时间排序
data_df.sort_index(inplace=True)
# 按天重采样
daily_data_df = data_df.resample('D').mean()
daily_data_df.dropna(inplace=True)
# 选取使用的列
proc_daily_df = daily_data_df[config.stats_cols + [config.raw_label_col]]
print(proc_daily_df.head())
return proc_daily_df
def make_stationary_seq(data_df, vis=True):
"""
对时序数据进行差分,达到平稳
参数:
- data_df: 时序数据
- vis: 是否可视化时序数据
返回:
- stationary_df: 平稳化的时序数据
"""
# 差分操作
stationary_df = data_df.diff()
if vis:
data_df.plot(y=config.raw_label_col, title='Raw Data')
stationary_df.plot(y=config.raw_label_col, title='Stationary Data')
plt.show()
return stationary_df
def make_data_for_model(data_df):
"""
构造训练集与测试集,并且使用t-1时刻的价格作为一个特征
参数:
- data_df: 时序数据
返回:
- train_data: 训练数据
- test_data: 测试数据
"""
# 使用t-1时刻的价格作为一个特征
data_df[config.label_col] = data_df[config.raw_label_col].shift(-1)
data_df.fillna(0, inplace=True)
use_cols = config.stats_cols + [config.raw_label_col, config.label_col]
# year_start_pred - 1 前(包括)的时序数据作为训练数据
train_data = data_df.loc[:str(config.year_start_pred - 1)][use_cols]
# # year_start_pred 后(包括)的时序数据作为测试数据
test_data = data_df.loc[str(config.year_start_pred):][use_cols]
print('训练数据样本个数:{}'.format(train_data.shape[0]))
print('测试数据样本个数:{}'.format(test_data.shape[0]))
return train_data, test_data
def scale_data(train_data, test_data):
"""
对所有数据值做归一化到[-1, 1]
参数:
- train_data: 训练数据
- test_data: 测试数据
返回:
- y_scaler: 标签(预测值)的scaler
- scaled_X_train: 归一化后的训练数据特征
- scaled_y_train: 归一化后的训练数据标签
- scaled_X_test: 归一化后的测试数据特征
- scaled_X_test: 归一化后的测试数据标签
"""
# 特征上的scaler
X_scaler = MinMaxScaler(feature_range=(-1, 1))
# 特征列
feat_cols = config.stats_cols + [config.raw_label_col]
# 数据特征
X_train = train_data[feat_cols].values
X_test = test_data[feat_cols].values
# 归一化后的数据特征
scaled_X_train = X_scaler.fit_transform(X_train)
scaled_X_test = X_scaler.transform(X_test)
# 标签的scaler
y_scaler = MinMaxScaler(feature_range=(-1, 1))
# 数据标签
y_train = train_data[config.label_col].values.reshape(-1, 1)
y_test = test_data[config.label_col].values.reshape(-1, 1)
# 归一化后的数据标签
scaled_y_train = y_scaler.fit_transform(y_train)
scaled_y_test = y_scaler.transform(y_test)
return y_scaler, scaled_X_train, scaled_y_train, scaled_X_test, scaled_y_test
def fit_lstm(X, y):
"""
训练LSTM模型
参数:
- X: 数据特征
- y: 数据标签
返回:
- model: 训练好的模型
"""
n_sample = X.shape[0] # 样本个数
n_feat_dim = X.shape[1] # 特征维度
# shape: (样本个数, time step, 特征维度)
X = X.reshape(n_sample, config.timestep, n_feat_dim)
# 构建模型
model = Sequential()
model.add(LSTM(config.nodes,
batch_input_shape=(config.batch_size, config.timestep, n_feat_dim),
stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
print('开始训练...')
for i in range(config.nb_epoch):
print('已迭代{}次(共{}次) '.format(i + 1, config.nb_epoch))
model.fit(X, y, epochs=1, batch_size=config.batch_size, verbose=0, shuffle=False)
model.reset_states()
# 在所有训练样本上运行一次,构建cell状态
model.predict(X, batch_size=config.batch_size)
# 保存模型
model.save(config.model_file)
return model
def forecast_lstm(model, X):
"""
预测
参数:
- model: 模型
- X: 数据特征
返回:
- y_pred: 预测值
"""
n_sample = X.shape[0]
n_feat_dim = X.shape[1]
X = X.reshape(n_sample, config.timestep, n_feat_dim)
y_pred = model.predict(X, batch_size=config.batch_size)[0]
return y_pred
#------------------------config.py---------------------------
# -*- coding: utf-8 -*-
"""
"""
import os
# 数据集路径
data_file = './data/coinbaseUSD_1-min_data_2014-12-01_to_2018-01-08.csv'
# 结果保存路径
output_path = './output'
if not os.path.exists(output_path):
os.makedirs(output_path)
# 数据的统计值列
stats_cols = ['Open', 'High', 'Low', 'Close', 'Volume_(BTC)', 'Volume_(Currency)']
# 原始数据的标签列
raw_label_col = 'Weighted_Price'
# 用于模型训练的标签列
label_col = 'Label_Price'
# 开始预测年份
year_start_pred = 2017
# 模型存放路径
model_file = os.path.join(output_path, 'trained_lstm_model.h5')
# LSTM模型参数
timestep = 1
nodes = 4
batch_size = 1
nb_epoch = 10