【intel校企合作课程】基于机器学习的比特币价格预测

1.作业要求

1.1.问题描述

       比特币价格预测器是一项极具前瞻性和实用性的项目。在这个数字化时代,区块链技术的不断发展推动着加密货币市场的蓬勃发展。随着越来越多的数字货币涌现,并且市场波动性增加,对比特币价格的准确预测变得至关重要。

1.2.预期解决方案

       通过参考英特尔的类似实现方案,预测比特币价格趋势的可靠性和准确性,以及预测模型的推理速度,可以为全球数字货币市场的安全性和可持续性提供有力支持。

1.3数据集

链接:https://pan.baidu.com/s/1OGOkair_6QPikjD3GXKu3A 
提取码:1023 

2.数据处理

        数据集分为训练集和测试集

2.1查看数据集

# 加载数据集
data = pd.read_csv("./bitcoin_price_Training - Training.csv")
print(data)

运行结果 

Date:日期,表示数据记录的日期。
open:开盘价,表示当天比特币交易开始时的价格。
High:最高价,表示当天比特币交易过程中的最高价格。
Low:最低价,表示当天比特币交易过程中的最低价格。
Close:收盘价,表示当天比特币交易结束时的价格。
Volume:交易量,表示当天的比特币交易量,即交易了多少比特币。
Marketcap:市值,表示当天比特币的市场总价值,即当前比特币的总市值。 

我们以Close为标签,其他为特征。

2.2 处理数据

        处理数据分为处理非数值数据,分析数据相关性,数据填充,以及增加数据

2.2.1处理非数值数据

        由上一步查看数据集可知,数据集中带有字符串,以及有“-”字符,在代入模型之前一定要处理非数值数据,否则将会“寸步难行”。处理的方法有:1.直接删除无关列,比如此处的Date列;2.转数值类型或nan。

# 将异常值替换为 NaN
data.replace('-', np.nan, inplace=True)

# 将 Volume 列转换为字符串类型
data['Volume'] = data['Volume'].astype(str)

# 替换逗号并转换为浮点数类型
data['Volume'] = data['Volume'].str.replace(',', '').astype(float)

# 将 Market Cap 列转换为字符串类型
data['Market Cap'] = data['Market Cap'].astype(str)

# 替换逗号并转换为浮点数类型
data['Market Cap'] = data['Market Cap'].str.replace(',', '').astype(float)

# 删除非数值类型的列
data = data.drop(columns=['Date'])

 2.2.2分析数据相关性

import seaborn as sns
import matplotlib.pyplot as plt

# 计算相关系数矩阵
corr_matrix = data.corr()

# 画出相关系数热力图
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm')
plt.title('Correlation Heatmap')
plt.show()

# 计算各个特征与 Close 之间的相关系数
close_corr = data.corr()['Close'].drop('Close')

# 绘制条形图
plt.figure(figsize=(10, 6))
close_corr.plot(kind='bar', color='skyblue')
plt.title('Correlation of Features with Close')
plt.xlabel('Features')
plt.ylabel('Correlation with Close')
plt.xticks(rotation=45)
plt.show()

        由热力图和相关性条形图可知,各特征对Close的相关性较高,无需删掉某些特征。

2.2.3数据填充

查看空值

# 检查每一列中的 NaN 值数量
nan_counts = data.isnull().sum()

print("NaN values in each column:")
print(nan_counts)

运行结果

        可见训练集中Volume列中存在243个缺失值,首先要对想办法填充这两百多个空值,通过查看数据集可知,这两百多个空值是连续的,而比特币的走势与时间相关,不能直接使用均值或者均值之类,此处使用的是移动均值的方法。

  • 计算移动窗口内的平均值,排除当前位置的空值。
  • 确定窗口的起始索引和结束索引,以便获取窗口内的数据。
  • 从数据中提取窗口内的非空值,并计算它们的平均值。
  • 将缺失值替换为移动平均值。
# 设定滑动窗口的大小
window_size = 10
# 寻找最后一个非空值的位置
last_non_null_index = data['Volume'].last_valid_index()

# 从最后一个非空值的位置开始遍历数据并填充缺失值
for i in range(last_non_null_index + 1, len(data)):
    if pd.isnull(data.at[i, 'Volume']):
        # 计算移动窗口内的平均值,排除当前位置的空值
        start_index = max(i - window_size, 0)
        end_index = i
        non_null_values = data.loc[start_index:end_index, 'Volume'].dropna()
        if len(non_null_values) > 0:  # 检查窗口内是否存在非空值
            moving_average = non_null_values.mean()
            # 填充缺失值为移动平均值
            data.at[i, 'Volume'] = moving_average
            


 再次查看,数据填充完成

2.2.4增加数据

查看数据分布

# 创建一个包含所有子图的图形
fig, axes = plt.subplots(nrows=1, ncols=len(data.columns), figsize=(16, 6))

# 遍历每个列并绘制对应的值分布图
for i, column in enumerate(data.columns):
    if data[column].dtype == 'object':
        # 对于非数值类型的列,绘制计数条形图
        data[column].value_counts().plot(kind='bar', color='skyblue', ax=axes[i])
    else:
        # 对于数值类型的列,绘制密度图
        sns.kdeplot(data[column], shade=True, color='skyblue', ax=axes[i])
    axes[i].set_title(f'Distribution of {column}')
    axes[i].set_xlabel(column)
    axes[i].set_ylabel('Frequency')

plt.tight_layout()
plt.show()

         可见数据分布很不均衡,这样会导致模型过拟合,泛化能力差,所以需要平衡数据,由于是回归模型,过采样,欠采样,smote都不适合,此处使用的是生成对抗网络生成一些数据,扩大数据集。

导入相关包

import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
from sklearn.preprocessing import MinMaxScaler

 数据归一化,导入数据集

scaler = MinMaxScaler(feature_range=(-1, 1))
scaled_data = scaler.fit_transform(data.values)
tensor_data = torch.tensor(scaled_data, dtype=torch.float32)
batch_size = 64
data_loader = DataLoader(TensorDataset(tensor_data), batch_size=batch_size, shuffle=True)

定义生成器和判别器模型

# 定义生成器模型
class Generator(nn.Module):
    def __init__(self, input_dim, output_dim):
        super(Generator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(input_dim, 128),
            nn.ReLU(),
            nn.Linear(128, 256),
            nn.ReLU(),
            nn.Linear(256, output_dim),
            nn.Tanh()
        )

    def forward(self, x):
        return self.model(x)

# 定义判别器模型
class Discriminator(nn.Module):
    def __init__(self, input_dim):
        super(Discriminator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(input_dim, 256),
            nn.LeakyReLU(0.2),
            nn.Linear(256, 128),
            nn.LeakyReLU(0.2),
            nn.Linear(128, 1),
            nn.Sigmoid()
        )

    def forward(self, x):
        return self.model(x)

定义GAN模型

# 定义GAN模型
class GAN(nn.Module):
    def __init__(self, generator, discriminator):
        super(GAN, self).__init__()
        self.generator = generator
        self.discriminator = discriminator

    def forward(self, x):
        return self.generator(x)

初始化生成器和判别器

# 初始化生成器和判别器
input_dim = 100
output_dim = len(data.columns)
generator = Generator(input_dim, output_dim)
discriminator = Discriminator(output_dim)
gan = GAN(generator, discriminator)

# 定义损失函数和优化器
criterion = nn.BCELoss()
optimizer_g = optim.Adam(generator.parameters(), lr=0.0002, betas=(0.5, 0.999))
optimizer_d = optim.Adam(discriminator.parameters(), lr=0.0002, betas=(0.5, 0.999))

训练GAN模型

# 训练GAN模型
num_epochs = 100
for epoch in range(num_epochs):
    for i, real_data in enumerate(data_loader):
        # 训练判别器
        discriminator.zero_grad()
        real_data = real_data[0]
        real_labels = torch.ones(real_data.size(0), 1)
        real_output = discriminator(real_data)
        real_loss = criterion(real_output, real_labels)
        noise = torch.randn(real_data.size(0), input_dim)
        fake_data = generator(noise)
        fake_labels = torch.zeros(real_data.size(0), 1)
        fake_output = discriminator(fake_data.detach())
        fake_loss = criterion(fake_output, fake_labels)
        d_loss = real_loss + fake_loss
        d_loss.backward()
        optimizer_d.step()

        # 训练生成器
        generator.zero_grad()
        noise = torch.randn(real_data.size(0), input_dim)
        fake_data = generator(noise)
        fake_output = discriminator(fake_data)
        g_loss = criterion(fake_output, real_labels)
        g_loss.backward()
        optimizer_g.step()

        # 输出训练信息
        if i % 100 == 0:
            print('[%d/%d][%d/%d] D_loss: %.4f, G_loss: %.4f' 
                  % (epoch, num_epochs, i, len(data_loader), d_loss.item(), g_loss.item()))

查看训练结果

        可见生成器的损失较低,可以生成符合要求的数据,需要注意的是,由于每次生成的数据不一样,可以多次尝试,直到得到最优值。 

 合成数据,这次合成的是800个数据,也可以根据需要调整

# 生成合成数据
noise = torch.randn(800, input_dim)  # 生成随机噪声
synthetic_data = generator(noise).detach().numpy()  # 生成合成数据

# 反归一化处理
synthetic_data_unscaled = scaler.inverse_transform(synthetic_data)
# 转换为DataFrame
synthetic_df_unscaled = pd.DataFrame(synthetic_data_unscaled, columns=data.columns)

print(synthetic_df_unscaled)

查看生成数据

合并数据集

# 将合成数据和原始数据按行合并
combined_data = pd.concat([synthetic_df_unscaled, data], ignore_index=True)

print(combined_data)

        需要注意的是,由于数据集与模型训练息息相关,此步骤应多次尝试,直到生成最合适的数据集并将其与越数据集合并,将其投入后面的训练。 

3模型训练并预测

        本例使用长短时记忆和Intel® Optimization for XGBoost中的XGBRegressor两个模型做预测并对比。

3.1LSTM模型

3.1.1模型介绍

       长短期记忆网络(Long Short-Term Memory,LSTM)是一种常用于处理序列数据的深度学习模型。它是循环神经网络(Recurrent Neural Network,RNN)的一种变体,专门设计用于解决传统RNN中的梯度消失和梯度爆炸等问题。

       LSTM通过引入一个特殊的记忆单元结构来解决这些问题。这个记忆单元结构能够在长时间序列数据中保持信息,并选择性地忘记或更新这些信息。其时间序列的性质非常适合本案例。

3.1.2模型训练

归一化

from sklearn.preprocessing import MinMaxScaler
# 数据归一化处理
scaler = MinMaxScaler(feature_range=(-1, 1))
scaled_data = scaler.fit_transform(combined_data['Close'].values.reshape(-1, 1))

构建时间序列

# 构建时间序列数据
def create_sequences(data, seq_length):
    X, y = [], []
    for i in range(len(data) - seq_length):
        X.append(data[i:i+seq_length])
        y.append(data[i+seq_length])
    return np.array(X), np.array(y)

数据预处理 

# 定义序列长度
seq_length = 1

# 将数据转换为时间序列数据
X, y = create_sequences(scaled_data, seq_length)

# 划分训练集和测试集
train_size = int(len(X) * 0.8)
X_train, X_test = X[:train_size], X[train_size:]
y_train, y_test = y[:train_size], y[train_size:]

转换Pytorch张量

# 转换为PyTorch张量
X_train = torch.from_numpy(X_train).type(torch.Tensor)
X_test = torch.from_numpy(X_test).type(torch.Tensor)
y_train = torch.from_numpy(y_train).type(torch.Tensor)
y_test = torch.from_numpy(y_test).type(torch.Tensor)

构建LSTM模型

# 构建LSTM模型
class LSTMModel(nn.Module):
    def __init__(self, input_dim, hidden_dim, num_layers, output_dim):
        super(LSTMModel, self).__init__()
        self.hidden_dim = hidden_dim
        self.num_layers = num_layers
        self.lstm = nn.LSTM(input_dim, hidden_dim, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_dim, output_dim)

    def forward(self, x):
        h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_dim).requires_grad_()
        c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_dim).requires_grad_()
        out, (hn, cn) = self.lstm(x, (h0.detach(), c0.detach()))
        out = self.fc(out[:, -1, :])
        return out

定义参数

input_dim = 1
hidden_dim = 32
num_layers = 2
output_dim = 1
num_epochs = 250
learning_rate = 0.001

model = LSTMModel(input_dim, hidden_dim, num_layers, output_dim)
loss_function = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

训练模型并可视化

# 训练模型
train_losses = []
test_losses = []

for epoch in range(num_epochs):
    outputs = model(X_train)
    optimizer.zero_grad()
    loss = loss_function(outputs, y_train)
    loss.backward()
    optimizer.step()
    
    # 计算训练集和测试集的损失
    test_outputs = model(X_test)
    train_losses.append(loss.item())
    test_loss = loss_function(test_outputs, y_test)
    test_losses.append(test_loss.item())
    
    
    print(f'Epoch {epoch+1}, Train Loss: {loss.item()}, Test Loss: {test_loss.item()}')

# 可视化训练集和测试集的损失
plt.plot(train_losses, label='Train Loss')
plt.plot(test_losses, label='Test Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training and Testing Loss')
plt.legend()
plt.show()

查看训练结果

        可见训练效果较好,验证效果更佳

3.2使用LSTM模型预测

加载测试集

# 加载数据集
test_data = pd.read_csv("./bitcoin_price_1week_Test - Test.csv")
print(test_data)

预处理测试集数据(与训练集一致)

# 将异常值替换为 NaN
test_data.replace('-', np.nan, inplace=True)

# 将 Volume 列转换为字符串类型
test_data['Volume'] = test_data['Volume'].astype(str)

# 替换逗号并转换为浮点数类型
test_data['Volume'] = test_data['Volume'].str.replace(',', '').astype(float)

# 将 Market Cap 列转换为字符串类型
test_data['Market Cap'] = test_data['Market Cap'].astype(str)

# 替换逗号并转换为浮点数类型
test_data['Market Cap'] = test_data['Market Cap'].str.replace(',', '').astype(float)

test_data = test_data.drop(columns=['Date'])

print(test_data)

归一化

# 数据归一化处理
test_scaler = MinMaxScaler(feature_range=(-1, 1))
test_scaled_data = test_scaler.fit_transform(test_data['Close'].values.reshape(-1, 1))

数据预处理

# 定义序列长度
test_seq_length = 1

# 将数据转换为时间序列数据
test_data_X, test_data_y = create_sequences(test_scaled_data, test_seq_length)

转化成Pytorch张量

# 转换为PyTorch张量

test_data_X = torch.from_numpy(test_data_X).type(torch.Tensor)

test_data_y = torch.from_numpy(test_data_y).type(torch.Tensor)

预测时间

import time
# 记录预测开始时间
start_time_predict = time.time()
# 预测模型
model.eval()

test_predict = model(test_data_X)

# 记录预测结束时间
end_time_predict = time.time()

# 输出预测时间
predict_time = end_time_predict - start_time_predict
print(f'Prediction Time: {predict_time} seconds')

结果输出

反归一化处理,恢复数据原状,便于后续计算

# 反归一化处理
test_predict = test_scaler.inverse_transform(test_predict.detach().numpy())
test_data_y = test_scaler.inverse_transform(test_data_y.detach().numpy())

 计算平均绝对误差

from sklearn.metrics import mean_absolute_error

# 计算平均绝对误差
mae = mean_absolute_error(test_data_y, test_predict)
print("Mean Absolute Error:", mae)

输出结果

       此结果意味着预测结果与真实值之间有125左右的误差,这个对于股市预测或者价格预测来说,是一个很大的误差,有可能会导致严重后果。

3.3 XGBRegressor模型

3.3.1模型介绍

       Intel® Optimization for XGBoost是一个针对XGBoost模型进行优化的软件包,旨在提高XGBoost在Intel处理器上的性能和效率。XGBoost是一种高效的梯度提升框架,用于解决各种回归和分类问题。XGBRegressor是XGBoost框架中用于回归任务的一个特定的类。

       XGBRegressor类提供了一种简单而灵活的方法来构建和训练回归模型。它使用梯度提升树(Gradient Boosting Decision Trees,GBDT)的技术,在训练过程中迭代地构建多个决策树,并将它们组合成一个强大的集成模型。

XGBRegressor具有以下一些重要的特性和功能:

  1. 高效性能:XGBoost使用高度优化的算法和数据结构,在处理大规模数据集时能够获得出色的性能表现。

  2. 灵活性:XGBRegressor支持多种参数设置和调优选项,用户可以根据自己的需求和数据特征进行调整。

  3. 特征重要性:XGBRegressor能够提供有关输入特征的重要性信息,帮助用户理解模型的工作原理和关键特征。

  4. 并行处理:XGBoost利用并行计算的能力,可以在多核CPU上并行处理数据和训练模型,加速训练过程。

  5. 可解释性:XGBRegressor生成的模型具有一定的可解释性,用户可以理解每个决策树的决策过程,从而更好地理解模型的预测行为。

3.3.2模型训练

       模型训练包含贝叶斯优化和训练模型两部分,正是利用其灵活性,使用贝叶斯优化来调整模型参数,以获得最佳的训练效果和预测效果。

3.3.2.1贝叶斯优化

       贝叶斯优化是一种用于优化目标函数的迭代优化方法,其目标是找到使目标函数达到最小值或最大值的输入参数组合。它基于贝叶斯推断和高斯过程回归的理论,通过在优化过程中逐步构建对目标函数的后验概率分布的模型,来指导搜索过程。

以下是贝叶斯优化的主要特点和步骤:

  1. 基于模型的优化:贝叶斯优化通过在优化过程中构建一个对目标函数的后验概率分布进行建模的模型。这个模型通常是高斯过程(Gaussian Process)或其他概率模型,用于表示目标函数的不确定性。

  2. 探索和利用:在每一次迭代中,贝叶斯优化通过权衡探索未探索区域和利用已知最优区域之间的权衡来选择下一个参数组合。它通过在参数空间中选择具有最大不确定性的点来探索新的潜在最优点,并利用已知的最优点来调整搜索方向。

  3. 代理模型:贝叶斯优化使用一个代理模型来近似目标函数,并在优化过程中不断更新和优化这个模型。这个代理模型能够在参数空间中建立一个对目标函数的映射,从而在搜索过程中指导下一步的选择。

  4. 高效的迭代优化:贝叶斯优化通常具有较高的收敛速度和效率,因为它能够在每一步中根据目标函数的不确定性来有针对性地选择下一个参数组合,从而有效地探索和利用参数空间。

  5. 适用性广泛:贝叶斯优化适用于各种类型的目标函数,包括连续型、离散型和混合型的目标函数。它在优化超参数选择、模型调优、自动机器学习等领域都有广泛的应用。

        此处需要调整的参数是L1和L2正则化参数,由于多次尝试可知,模型在训练时效果较优,但是预测效果较差,即模型存在过拟合的问题,所以需要引入L1和L2正则化参数,具体代码如下

from skopt import BayesSearchCV
from skopt.space import Real, Integer

# 准备特征和标签
X = data.drop(columns=['Close'])
y = data['Close']

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.7, random_state=42)

# 设置搜索空间
search_spaces = {
    'reg_alpha': Real(1e-6, 1e+2, prior='log-uniform'),
    'reg_lambda': Real(1e-6, 1e+2, prior='log-uniform'),
    
}

# 使用 Intel® Optimization for XGBoost
xgb_reg = xgb.XGBRegressor(n_estimators=150)

# 设置贝叶斯搜索
opt = BayesSearchCV(xgb_reg, search_spaces, n_iter=50, cv=3)

# 开始搜索最佳参数
start_time = time.time()
opt.fit(X_train, y_train)
end_time = time.time()

# 打印最佳参数和最佳评分
print("Best Parameters:", opt.best_params_)
print("Best Score:", opt.best_score_)

# 计算搜索时间
search_time = end_time - start_time
print("Search Time:", search_time, "seconds")

# 在测试集上进行预测并计算均方误差
y_pred = opt.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
print("Mean Absolute Error:", mae)

 最佳参数组合

        得到最佳参数组合为'reg_alpha', 31.57387470542842), ('reg_lambda', 9.102536560459421e-05) ,可能会有不同的组合,以实际运行结果为准。

3.3.2.2训练模型

数据准备

# 准备特征和标签
X = combined_data.drop(columns=['Close'])

y = combined_data['Close']

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.6, random_state=42)

代入正则化参数训练模型

# 设置训练轮次
num_round = 150

# 设置正则化参数
alpha = 31.57387470542842  # L1正则化参数
lambda_val = 9.102536560459421e-05  # L2正则化参数

# 使用 Intel® Optimization for XGBoost
xgb_reg = xgb.XGBRegressor(n_estimators=num_round, reg_alpha=alpha, reg_lambda=lambda_val, )

# 训练模型,并在测试集上进行预测
eval_set = [(X_train, y_train), (X_test, y_test)]  # 用于评估模型的数据集
xgb_reg.fit(X_train, y_train, eval_set=eval_set, eval_metric=["rmse"], verbose=False)

# 提取训练过程中的损失值
results = xgb_reg.evals_result()
train_error = results['validation_0']['rmse']
val_error = results['validation_1']['rmse']

# 输出训练误差和验证误差
for i in range(num_round):
    print(f"Iteration {i+1}: Train RMSE = {train_error[i]}, Validation RMSE = {val_error[i]}")

输出结果

可视化误差

# 可视化训练误差和验证误差
plt.plot(train_error, label='Train')
plt.plot(val_error, label='Validation')
plt.xlabel('Iterations')
plt.ylabel('RMSE')
plt.title('Training and Validation Error')
plt.legend()
plt.show()

3.4使用XGBRegressor模型预测

数据提取

# 提取测试数据的标签列
yy = test_data['Close']
Xx = test_data.drop(columns=['Close'])  # 移除标签列,只保留特征

预测并计时

# 开始预测并计时
start_time = time.time()
y_pred = xgb_reg.predict(Xx)
end_time = time.time()

# 计算预测时间
prediction_time = end_time - start_time

# 计算mae
mae = mean_absolute_error(yy, y_pred)

print("Prediction Time:", prediction_time, "seconds")
print("mean_absolute_error:", mae)

输出结果

        此结果意味着预测结果与真实值之间有15.58的差距,在动辄几千的Close之间,这个误差还能接受。 

3.5结果对比

Prediction Timemean_absolute_error
LSTM模型0.0010001659393310547s125.577065
XGBRegressor模型0.002007007598876953s15.580554966517854
  1. 预测时间:使用XGBRegressor模型的预测时间为0.002007007598876953秒,比LSTM模型慢了0.001秒,但在本案例中由于test数据集数据量少,比较推理时间并无太大意义。

  2. 平均绝对误差:XGBRegressor模型的平均绝对误差为15.580554966517854,相比之下,LSTM模型的平均绝对误差为125.577065。这表明XGBRegressor模型在预测精度方面明显优于LSTM模型,能够更准确地预测目标变量的数值,这对于许多实际应用场景来说至关重要。

4结论

        Intel® Optimization for XGBoost在提高预测效率和预测精度方面表现出色为用户提供了一个高效、快速且准确的回归建模工具,尤其在参数优化方面,可大幅度提高模型性能,可应用于各种实际问题的解决中。

  • 14
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值