一、项目简介
1.问题描述
比特币价格预测器是一项极具前瞻性和实用性的项目。在这个数字化时代,区块链技术的不断发展推动着加密货币市场的蓬勃发展。随着越来越多的数字货币涌现,并且市场波动性增加,对比特币价格的准确预测变得至关重要。
2.预期解决方案
通过参考英特尔的类似实现方案,预测比特币价格趋势的可靠性和准确性,以及预测模型的推理速度,可以为全球数字货币市场的安全性和可持续性提供有力支持。这里,我们将分类准确度和推理时间作为评估比特币价格预测器的主要依据。
3.数据集
你可以在此处下载数据集要求。
4.需求
需要使用 英特尔® ONEAPI AI分析工具包。
二、数据探索
1.数据查看
Modin是一个Python第三方库,可以通过并行来处理大数据集。其中语法与pandas相近,拥有出色的性能弥补了pandas处理大型数据集的缺陷。
而Dask 是一个用于分析计算的灵活的并行计算库,实现大型多维数据集分析的更快执行以及加速和扩展数据科学制作流程或工作流程的强大工具。
#!pip install modin dask
import modin.pandas as pd
import os
os.environ["MODIN_ENGINE"] = "dask"
from modin.config import Engine
Engine.put("dask")
import pandas as pd
# 读取 CSV 文件
data = pd.read_csv('./extracted_contents/bitcoin_price_Training - Training.csv')
print('数据规模:{}\n'.format(data.shape))
display(data.head())
data=data.infer_objects()
data.info()
2.查看数据统计性描述
3.数据可视化
折线图
import pandas as pd
import matplotlib.pyplot as plt
# 读取CSV文件
data = pd.read_csv('./extracted_contents/bitcoin_price_Training - Training.csv')
# 将'Date'列转换为日期格式
data['Date'] = pd.to_datetime(data['Date'])
# 将带有逗号的数字列转换为数字数据,将'-'替换为NaN
data['Volume'] = data['Volume'].replace('-', float('nan')).str.replace(',', '').astype(float)
data['Market Cap'] = data['Market Cap'].replace('-', float('nan')).str.replace(',', '').astype(float)
# 创建图形和坐标轴
fig, ax = plt.subplots(figsize=(10, 6))
# 绘制每日 Open、High、Low、Close 价格折线图
for price_type in ['Open', 'High', 'Low', 'Close']:
ax.plot(data['Date'], data[price_type], '-', label=price_type + " Price")
ax.set_title("Bitcoin Price Change Trends (By Day)")
ax.set_xlabel("Date")
ax.set_ylabel("Price")
ax.legend()
ax.grid(True)
# 设置横坐标仅显示年份
ax.xaxis.set_major_locator(plt.matplotlib.dates.YearLocator())
ax.xaxis.set_major_formatter(plt.matplotlib.dates.DateFormatter('%Y'))
plt.tight_layout()
plt.show()
# 按照日期分组,并使用每组的第一个日期作为该组的代表日期
data = data.set_index('Date').resample('D').first().reset_index()
# 绘图
fig, axes = plt.subplots(nrows=4, ncols=4, figsize=(20, 20))
# 每日 Open、High、Low、Close 价格折线图
for i, price_type in enumerate(['Open', 'High', 'Low', 'Close']):
for j, interval in enumerate(['D', 'M', 'Q', 'Y']):
if interval == 'Y':
data_interval = data.set_index('Date').resample(interval).mean().loc['2013':'2017']
else:
data_interval = data.set_index('Date').resample(interval).mean()
axes[i, j].plot(data_interval.index, data_interval[price_type], '-', label=price_type + " Price")
axes[i, j].set_title(f"Bitcoin {price_type} Price Change Trends\n(By {interval})")
axes[i, j].set_xlabel("Date")
axes[i, j].set_ylabel(f"{price_type} Price")
axes[i, j].legend()
axes[i, j].grid(True)
# 设置横坐标范围为实际年份范围
min_year = data['Date'].dt.year.min()
max_year = data['Date'].dt.year.max()
for ax in axes.flat:
ax.set_xlim(pd.Timestamp(f'{min_year}-01-01'), pd.Timestamp(f'{max_year}-12-31'))
ax.xaxis.set_major_formatter(plt.matplotlib.dates.DateFormatter('%Y'))
plt.tight_layout()
plt.show()
稳定性检测与时间序列检测
import pandas as pd
import matplotlib.pyplot as plt
import statsmodels.api as sm
# 读取CSV文件
data = pd.read_csv('./extracted_contents/bitcoin_price_Training - Training.csv')
# 将'Date'列转换为日期格式
data['Date'] = pd.to_datetime(data['Date'])
# 将带有逗号的数字列转换为数字数据,将'-'替换为NaN
data['Volume'] = data['Volume'].replace('-', float('nan')).str.replace(',', '').astype(float)
data['Market Cap'] = data['Market Cap'].replace('-', float('nan')).str.replace(',', '').astype(float)
# 按照日期分组,并使用每组的第一个日期作为该组的代表日期
data = data.set_index('Date').resample('D').first().reset_index()
# 计算每月的平均数据
data_monthly = data.set_index('Date').resample('M').mean()
# 进行季节性分解
decomposition = sm.tsa.seasonal_decompose(data_monthly['Close'], model='additive')
plt.figure(figsize=[15, 7])
decomposition.plot()
plt.title("Seasonal Decomposition of Bitcoin Close Price (Monthly Data)")
plt.show()
数据变化
由于连续的响应变量不满足正态分布,所以数据需要进行Box-Cox变换,代码如下:
# 读取CSV文件
data = pd.read_csv('./extracted_contents/bitcoin_price_Training - Training.csv')
# 将'Date'列转换为日期格式
data['Date'] = pd.to_datetime(data['Date'])
# 将带有逗号的数字列转换为数字数据,将'-'替换为NaN
data['Volume'] = data['Volume'].replace('-', float('nan')).str.replace(',', '').astype(float)
data['Market Cap'] = data['Market Cap'].replace('-', float('nan')).str.replace(',', '').astype(float)
# 按照日期分组,并使用每组的第一个日期作为该组的代表日期
data = data.set_index('Date').resample('D').first().reset_index()
# 计算每月的平均数据
data_monthly = data.set_index('Date').resample('M').mean()
# 应用Box-Cox转换
data_monthly['Close_box'], lmbda = stats.boxcox(data_monthly['Close'])
# 进行Dickey-Fuller检验
result = sm.tsa.stattools.adfuller(data_monthly['Close_box'])
print("Dickey–Fuller test: p=%f" % result[1])
由于时间序列季节对数据的影响,所以季节差异化需要考虑,代码如下:
# 创建 prices_box_diff 列,计算加权价格的差分
data['prices_box_diff'] = data['Close'] - data['Close'].shift(12)
# 使用 Dickey-Fuller 测试检验 prices_box_diff 列是否具有单位根
p_value = sm.tsa.stattools.adfuller(data['prices_box_diff'].dropna())[1]
print("Dickey–Fuller test: p=%f" % p_value)
散点图
import pandas as pd
import matplotlib.pyplot as plt
# 读取CSV文件
data = pd.read_csv('./extracted_contents/bitcoin_price_Training - Training.csv')
# 将'Date'列转换为日期格式
data['Date'] = pd.to_datetime(data['Date'])
# 检查 'Date' 列的数据类型
print(data.info())
# 创建图形和坐标轴
fig, ax = plt.subplots(figsize=(10, 6))
# 绘制散点图
ax.scatter(data['Date'], data['Close'], alpha=0.5)
ax.set_title("Bitcoin Price Scatter Plot")
ax.set_xlabel("Date")
ax.set_ylabel("Close Price")
plt.tight_layout()
plt.show()
三、数据预处理
确定标签
考虑到以准确率和推理时间作为评判依据,将问题要转化为分类问题,我们为数据打上标签,增加一列Y用于表示,0表示涨,1表示跌,如果相同默认为跌
# 读取 CSV 文件
file_path = "./extracted_contents/bitcoin_price_Training - Training.csv"
data = pd.read_csv(file_path)
# 计算 Y 列
data['Y'] = (data['Close'].shift(-1) > data['Close']).astype(int)
# 将修改后的数据写入文件
data.to_csv(file_path, index=False)
print("Y 列已成功添加并写入文件。")
检查缺失值和重复值
import pandas as pd
# 读取CSV文件
data = pd.read_csv('./extracted_contents/bitcoin_price_Training - Training.csv')
# 查找重复值
duplicate_rows = data[data.duplicated()]
print("重复行数:", duplicate_rows.shape[0])
# 查找缺失值
missing_values = data.isnull().sum()
print("\n缺失值统计:\n", missing_values)
无缺失值。
数据平衡性检查
四、模型拟合
import plotly.io as pio
from sklearn.preprocessing import RobustScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve, auc, precision_recall_curve, average_precision_score
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import numpy as np
# 导入时间模块
import time
# 设置默认渲染器
pio.renderers.default = "iframe"
from sklearn.preprocessing import RobustScaler
from sklearn.impute import SimpleImputer
def prepare_train_test_data(train_data, test_data, target_col):
"""
准备训练集和测试集数据
"""
scaler = RobustScaler()
# 移除日期列
train_data = train_data.drop('Date', axis=1)
test_data = test_data.drop('Date', axis=1)
# 将非数字值替换为 NaN
train_data = train_data.replace('-', np.nan)
test_data = test_data.replace('-', np.nan)
# 转换为浮点数
train_data = train_data.replace(',', '', regex=True).astype(float)
test_data = test_data.replace(',', '', regex=True).astype(float)
# 使用中位数填充缺失值
imputer = SimpleImputer(strategy='median')
train_data = pd.DataFrame(imputer.fit_transform(train_data), columns=train_data.columns)
test_data = pd.DataFrame(imputer.transform(test_data), columns=test_data.columns)
# 准备训练集数据
X_train = train_data.drop(target_col, axis=1)
y_train = train_data[target_col]
X_train_scaled = scaler.fit_transform(X_train)
# 准备测试集数据
X_test = test_data.drop(target_col, axis=1)
y_test = test_data[target_col]
X_test_scaled = scaler.transform(X_test)
print("Train Shape: {}".format(X_train_scaled.shape))
print("Test Shape: {}".format(X_test_scaled.shape))
return X_train_scaled, X_test_scaled, y_train, y_test
# 从 CSV 文件中加载训练集和测试集数据
train_data = pd.read_csv("./extracted_contents/bitcoin_price_Training - Training.csv")
test_data = pd.read_csv("./extracted_contents/bitcoin_price_1week_Test - Test.csv")
# 准备训练集和测试集数据
X_train, X_test, y_train, y_test = prepare_train_test_data(train_data=train_data, test_data=test_data, target_col='Y')
def plot_model_res(model_name, y_test, y_prob):
"""
可视化模型结果
"""
intel_pal = ['#0071C5', '#FCBB13']
fpr, tpr, _ = roc_curve(y_test, y_prob)
roc_auc = auc(fpr, tpr)
precision, recall, _ = precision_recall_curve(y_test, y_prob)
auprc = average_precision_score(y_test, y_prob)
fig = make_subplots(rows=1, cols=2,
shared_yaxes=True,
subplot_titles=['Receiver Operating Characteristic<br>(ROC) Curve',
'Precision-Recall Curve<br>AUPRC = {:.3f}'.format(auprc)])
fig.add_trace(go.Scatter(x=np.linspace(0,1,11), y=np.linspace(0,1,11),
name='Baseline',mode='lines',legendgroup=1,
line=dict(color="Black", width=1, dash="dot")), row=1,col=1)
fig.add_trace(go.Scatter(x=fpr, y=tpr, line=dict(color=intel_pal[0], width=3),
hovertemplate = 'True positive rate = %{y:.3f}, False positive rate = %{x:.3f}',
name='AUC = {:.4f}'.format(roc_auc),legendgroup=1), row=1,col=1)
fig.add_trace(go.Scatter(x=recall, y=precision, line=dict(color=intel_pal[0], width=3),
hovertemplate = 'Precision = %{y:.3f}, Recall = %{x:.3f}',
name='AUPRC = {:.4f}'.format(auprc),showlegend=False), row=1,col=2)
fig.update_layout(title="{} ROC and Precision-Recall Curves".format(model_name),
hovermode="x unified", width=900, height=500,
xaxis1_title='False Positive Rate (1 - Specificity)',
yaxis1_title='True Positive Rate (Sensitivity)',
xaxis2_title='Recall (Sensitivity)', yaxis2_title='Precision (PPV)',
legend=dict(orientation='v', y=.07, x=.45, xanchor="right",
bordercolor="black", borderwidth=.5))
fig.show()
def plot_distribution(y_prob, color=['#7AB5E1', '#FCE7B2']):
plot_df = pd.DataFrame.from_dict({
'Class 0': (len(y_prob[y_prob <= 0.5]) / len(y_prob)) * 100,
'Class 1': (len(y_prob[y_prob > 0.5]) / len(y_prob)) * 100
}, orient='index', columns=['pct'])
fig = go.Figure()
fig.add_trace(go.Pie(
labels=plot_df.index,
values=plot_df.pct,
hole=0.45,
text=plot_df.index,
sort=False,
showlegend=False,
marker=dict(colors=color, line=dict(color='#0071C5', width=2.5)),
hovertemplate="%{label}: <b>%{value:.2f}%</b><extra></extra>"
))
fig.update_layout(
title='Predicted Target Distribution',
width=700,
height=450,
uniformtext_minsize=15,
uniformtext_mode='hide'
)
fig.show()
五、OneApi组件
利用Intel AI Analytics Toolkit中Machine Learning模块中的intel extension for Scikit-learn进行模型拟合。主要是使用了支持向量机分类来进行拟合。
import time
## 初始化 SVC 模型 ##
svc = SVC(probability=True) # 启用概率估计功能
## 调整超参数 ##
strat_kfold = StratifiedKFold(n_splits=3, shuffle=True, random_state=21)
grid_search = RandomizedSearchCV(svc, param_distributions=parameters,
cv=strat_kfold, n_iter=5, scoring='roc_auc',
verbose=1, n_jobs=-1, random_state=21)
grid_search.fit(X_train, y_train)
print("Done!\nBest hyperparameters:", grid_search.best_params_)
print("F1分数: {:.4f}".format(grid_search.best_score_))
## Get the best model ##
best_svc_model = grid_search.best_estimator_
## Calculate predictions ##
y_prob = best_svc_model.predict_proba(X_test)[:, 1]
y_pred = best_svc_model.predict(X_test) # 预测测试数据的标签
## Evaluate the model ##
auc_score = roc_auc_score(y_test, y_prob)
f1 = f1_score(y_test, y_pred) # 计算 F1 分数
## Plot model results ##
plot_model_res(model_name='SVC', y_test=y_test, y_prob=y_prob)
plot_distribution(y_prob)
## Print model results ##
svc.fit(X_train, y_train) # 在这之前先对模型进行拟合
svc_pred = svc.predict(X_test)
# 预测标签
svc_pred = best_svc_model.predict(X_test)
# 真实标签
true_labels = y_test.values
# 测试集推理时间
start_time = time.time()
svc_pred = best_svc_model.predict(X_test)
end_time = time.time()
inference_time = end_time - start_time
print("测试集推理时间:", inference_time, "秒")
print("预测结果:")
print("预测标签:", svc_pred.astype(int))
print("真实标签:", true_labels.astype(int))
六、训练效果与预测结果
七、进一步工作
1、可以采用不同的模型拟合
2、采用不同的数据平衡策略,获取更好的模型拟合效果
3、改用回归模型预测实际价格会更有实际意义
4、可以使用XGBOOST探索更好的预测效果
5、特征提取可以更详细准确一些,比如均值等等
八、总结
通过参与本次校企合作课程,我深入了解了Intel的oneAPI在机器学习与数据挖掘领域的应用。在作业实践中,我亲身体验了Intel AI Analytics Toolkit工具对问题解决的提升。通过使用Intel AI Analytics Toolkit中的Machine Learning模块中的Intel extension for Scikit-learn进行模型拟合,我意识到了oneAPI对人工智能领域的重要贡献。
oneAPI平台不仅兼顾了机器学习和深度学习,而且为开发者和应用者提供了极大的便利。它整合了各种人工智能技术,并提供了统一的软件架构和编程接口,使得跨多种硬件架构的应用开发变得更加简单和高效。在实践中,Intel AI Analytics Toolkit中的Machine Learning模块和扩展库为我提供了针对性的工具,帮助我更有效地进行模型拟合和问题解决。
通过这次学习和实践,我深刻认识到了人工智能领域的不断发展和创新。我将继续探索和应用Intel的oneAPI平台,为解决实际问题做出更多的贡献。