Files
crpto_predictor/train_dlinear_regression.py
T
crypto_predictorandClaude Sonnet 4.5 9bbdc8d42b Initial commit: BTC 1h prediction model (53.10% accuracy)
🎯 项目成果:
- 准确率: 53.10% (2026年测试集)
- 任务类型: 二分类 (预测涨/跌)
- 特征: OHLCV + RSI + MACD (9个特征)
- 训练数据: 2020-2025年BTC小时数据 (52,351序列)
- 测试数据: 2026年1-29日 (484序列)

📊 性能指标:
- 精确率: 51.79%
- 召回率: 49.36%
- F1分数: 0.5054
- 预测分布: 非常平衡 (偏差仅-11个)

🔬 实验历史:
- 尝试了5种方案 (回归/分类, 不同数据划分)
- 分类任务优于回归任务 (53.10% vs 49.5%)
- 全量训练避免了过拟合问题

📦 主要模型文件:
- models/checkpoints/dlinear_classification_2026/
  - best_model.pt (最佳模型)
  - preprocessor.pkl (预处理器)
  - test_2026_results.json (测试结果)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-29 21:27:15 +08:00

259 lines
11 KiB
Python

"""使用回归任务训练DLinear模型 - 预测未来1小时的实际价格变化百分比"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from data.fetcher import BinanceDataFetcher
from data.preprocessor import CryptoPreprocessor, split_data
from data.dataset import create_dataloaders
from models.dlinear.model import DLinear, DLinearConfig
from training.trainer import Trainer
from training.config import TrainingConfig
import json
import numpy as np
def main():
print("="*80)
print("DLinear回归模型训练 - 预测未来1小时价格变化百分比")
print("="*80)
# 步骤1: 获取2020-2025年完整数据
print("\n[1/8] 从Binance获取BTC 2020-2025年完整数据...")
fetcher = BinanceDataFetcher()
df = fetcher.fetch_ohlcv(
symbol='BTC',
timeframe='1h',
start_date='2020-01-01',
end_date='2025-12-31',
use_cache=True
)
print(f"✓ 已获取 {len(df)} 行数据(约{len(df)/8760:.1f}年)")
print(f"✓ 时间范围:{df['timestamp'].min()} 至 {df['timestamp'].max()}")
# 分析数据覆盖的市场周期
print(f"\n✓ 价格变化分析:")
print(f" 起始价格: ${df['close'].iloc[0]:,.2f} (2020年)")
print(f" 结束价格: ${df['close'].iloc[-1]:,.2f} (2025年)")
total_return = (df['close'].iloc[-1] - df['close'].iloc[0]) / df['close'].iloc[0]
print(f" 总收益率: {total_return*100:+.2f}%")
# 按年分析
df['year'] = df['timestamp'].dt.year
print(f"\n✓ 年度表现分析(确保包含牛熊周期):")
for year in sorted(df['year'].unique()):
year_df = df[df['year'] == year]
if len(year_df) > 100:
year_return = (year_df['close'].iloc[-1] - year_df['close'].iloc[0]) / year_df['close'].iloc[0]
trend = "🚀 牛市" if year_return > 0.3 else ("🔴 熊市" if year_return < -0.3 else "📊 震荡")
print(f" {year}年: {year_return*100:+7.2f}% {trend}")
# 步骤2: 数据预处理(回归任务)
print("\n[2/8] 数据预处理(回归任务 - 预测未来1小时的收益率)...")
preprocessor = CryptoPreprocessor(
lookback=168, # 输入:过去168小时(7天)
forecast_horizon=1, # 预测:未来1小时
price_change_threshold=0.0 # 回归任务不使用阈值
)
X, y, processed_df = preprocessor.preprocess_pipeline(df, task='regression', fit=True)
print(f"✓ 已创建序列:X形状 = {X.shape}, y形状 = {y.shape}")
print(f"✓ 特征数量:{X.shape[-1]}")
# 分析标签分布(收益率)
print(f"\n✓ 标签统计(收益率):")
print(f" 最小值: {y.min()*100:.4f}%")
print(f" 最大值: {y.max()*100:.4f}%")
print(f" 均值: {y.mean()*100:.4f}%")
print(f" 标准差: {y.std()*100:.4f}%")
print(f" 中位数: {np.median(y)*100:.4f}%")
# 方向统计
n_positive = (y > 0).sum()
n_negative = (y <= 0).sum()
print(f"\n✓ 方向分布:")
print(f" 正收益(上涨): {n_positive} ({n_positive/len(y)*100:.1f}%)")
print(f" 负收益(下跌): {n_negative} ({n_negative/len(y)*100:.1f}%)")
# 步骤3: 划分数据集
print("\n[3/8] 划分训练/验证/测试集 (70/15/15)...")
train_data, val_data, test_data = split_data(X, y, train_split=0.7, val_split=0.15)
print(f" 训练集:{len(train_data[0])} 样本")
print(f" 收益率范围: {train_data[1].min()*100:.4f}% ~ {train_data[1].max()*100:.4f}%")
print(f" 均值: {train_data[1].mean()*100:.4f}%")
print(f" 验证集:{len(val_data[0])} 样本")
print(f" 收益率范围: {val_data[1].min()*100:.4f}% ~ {val_data[1].max()*100:.4f}%")
print(f" 均值: {val_data[1].mean()*100:.4f}%")
print(f" 测试集:{len(test_data[0])} 样本")
print(f" 收益率范围: {test_data[1].min()*100:.4f}% ~ {test_data[1].max()*100:.4f}%")
print(f" 均值: {test_data[1].mean()*100:.4f}%")
# 分析各数据集对应的时间范围
lookback = 168
train_end_idx = int(len(processed_df) * 0.7) - lookback
val_end_idx = int(len(processed_df) * 0.85) - lookback
train_df = processed_df.iloc[lookback:train_end_idx+lookback]
val_df = processed_df.iloc[train_end_idx+lookback:val_end_idx+lookback]
test_df = processed_df.iloc[val_end_idx+lookback:]
print(f"\n 训练集时间范围: {train_df['timestamp'].min()} 至 {train_df['timestamp'].max()}")
train_return = (train_df['close'].iloc[-1] - train_df['close'].iloc[0]) / train_df['close'].iloc[0]
print(f" 价格: ${train_df['close'].iloc[0]:,.0f} → ${train_df['close'].iloc[-1]:,.0f} ({train_return*100:+.1f}%)")
print(f" 验证集时间范围: {val_df['timestamp'].min()} 至 {val_df['timestamp'].max()}")
val_return = (val_df['close'].iloc[-1] - val_df['close'].iloc[0]) / val_df['close'].iloc[0]
print(f" 价格: ${val_df['close'].iloc[0]:,.0f} → ${val_df['close'].iloc[-1]:,.0f} ({val_return*100:+.1f}%)")
print(f" 测试集时间范围: {test_df['timestamp'].min()} 至 {test_df['timestamp'].max()}")
test_return = (test_df['close'].iloc[-1] - test_df['close'].iloc[0]) / test_df['close'].iloc[0]
print(f" 价格: ${test_df['close'].iloc[0]:,.0f} → ${test_df['close'].iloc[-1]:,.0f} ({test_return*100:+.1f}%)")
# 步骤4: 创建数据加载器
print("\n[4/8] 创建PyTorch数据加载器...")
train_loader, val_loader, test_loader = create_dataloaders(
train_data, val_data, test_data,
task='regression', # 回归任务
batch_size=64
)
# 步骤5: 创建模型
print("\n[5/8] 创建DLinear回归模型...")
model_config = DLinearConfig(
input_dim=X.shape[-1],
lookback=168,
task='regression', # 回归任务
hidden_dim=512,
dropout=0.1,
moving_avg_kernel=25
)
model = DLinear(model_config)
print(f"✓ 模型已创建,可训练参数:{model.count_parameters():,} 个")
print(f"✓ 任务类型:回归(预测收益率百分比)")
print(f"✓ 损失函数:MSELoss")
# 步骤6: 训练模型
print("\n[6/8] 开始训练回归模型...")
train_config = TrainingConfig(
batch_size=64,
max_epochs=50,
learning_rate=0.001,
early_stopping_patience=10,
lr_scheduler='plateau',
save_best=True,
checkpoint_dir='./models/checkpoints/dlinear_btc_regression'
)
trainer = Trainer(
model,
train_config,
task='regression' # 回归任务,不需要pos_weight
)
print(f"✓ 训练数据规模:{len(train_data[0]):,} 样本")
history = trainer.fit(train_loader, val_loader)
# 步骤7: 评估模型
print("\n[7/8] 在测试集上评估模型...")
metrics = trainer.evaluate(test_loader)
# 保存评估结果
results_path = './models/checkpoints/dlinear_btc_regression/test_metrics.json'
with open(results_path, 'w') as f:
json.dump(metrics, f, indent=2)
print(f"\n✓ 评估结果已保存至:{results_path}")
# 步骤8: 结果分析
print("\n[8/8] 回归模型性能分析...")
print("\n" + "="*80)
print("训练完成 - 回归模型测试结果")
print("="*80)
print(f"\n📊 回归指标:")
print(f" RMSE (均方根误差): {metrics.get('rmse', 0):.6f} ({metrics.get('rmse', 0)*100:.4f}%)")
print(f" MAE (平均绝对误差): {metrics.get('mae', 0):.6f} ({metrics.get('mae', 0)*100:.4f}%)")
print(f" MAPE (平均绝对百分比误差): {metrics.get('mape', 0):.2f}%")
print(f" 方向准确率: {metrics.get('directional_accuracy', 0)*100:.2f}%")
print(f"\n💰 回测结果:")
print(f" 总收益率: {metrics.get('backtest_total_return', 0)*100:.2f}%")
print(f" 最终资金: ${metrics.get('backtest_final_capital', 0):,.2f}")
print(f" 夏普比率: {metrics.get('backtest_sharpe_ratio', 0):.4f}")
max_dd = metrics.get('backtest_max_drawdown', 0)
if not np.isnan(max_dd):
print(f" 最大回撤: {max_dd*100:.2f}%")
else:
print(f" 最大回撤: N/A")
print(f" 胜率: {metrics.get('backtest_win_rate', 0)*100:.2f}%")
print(f" 交易次数: {metrics.get('backtest_num_trades', 0)}")
# 与分类任务对比
print("\n" + "="*80)
print("📊 回归 vs 分类任务对比")
print("="*80)
print(f"\n二分类模型(之前的结果):")
print(f" 准确率: 49.76%")
print(f" F1分数: 0.3898")
print(f" 预测偏差: 68.1%预测下跌(过度悲观)")
print(f" 回测收益: -3.29%")
print(f"\n回归模型(当前结果):")
print(f" 方向准确率: {metrics.get('directional_accuracy', 0)*100:.2f}%")
print(f" RMSE: {metrics.get('rmse', 0)*100:.4f}%")
print(f" MAE: {metrics.get('mae', 0)*100:.4f}%")
print(f" 回测收益: {metrics.get('backtest_total_return', 0)*100:.2f}%")
# 性能评估
print(f"\n🎯 回归模型性能评估:")
dir_acc = metrics.get('directional_accuracy', 0)
if dir_acc > 0.55:
print(f" ✅ 方向准确率 {dir_acc*100:.1f}% > 55%,模型有预测能力")
elif dir_acc > 0.50:
print(f" ⚠️ 方向准确率 {dir_acc*100:.1f}% 略优于随机,预测能力较弱")
else:
print(f" ❌ 方向准确率 {dir_acc*100:.1f}% ≤ 50%,方向预测能力不足")
rmse = metrics.get('rmse', 0)
mae = metrics.get('mae', 0)
print(f"\n 误差分析:")
print(f" RMSE = {rmse*100:.4f}% - 平均预测误差的平方根")
print(f" MAE = {mae*100:.4f}% - 平均绝对误差")
if rmse < 0.01: # 小于1%
print(f" ✅ RMSE < 1%,预测精度较高")
elif rmse < 0.02: # 小于2%
print(f" ⚠️ RMSE在1-2%之间,预测精度中等")
else:
print(f" ❌ RMSE > 2%,预测误差较大")
total_return = metrics.get('backtest_total_return', 0)
if total_return > 0.05:
print(f"\n ✅ 回测收益 {total_return*100:.2f}% > 5%,策略有盈利潜力")
elif total_return > 0:
print(f"\n ⚠️ 回测收益 {total_return*100:.2f}% > 0,策略微弱盈利")
else:
print(f"\n ❌ 回测收益 {total_return*100:.2f}% ≤ 0,策略亏损")
print("\n💡 回归任务的优势:")
print(" ✅ 提供预测幅度信息(可用于动态仓位管理)")
print(" ✅ 评估风险收益比")
print(" ✅ 更适合量化交易策略")
print("\n💡 回归任务的挑战:")
print(" ❌ 预测具体数值比预测方向更难")
print(" ❌ 对异常值敏感")
print(" ❌ 需要更多训练数据才能稳定")
print("\n" + "="*80)
print("✓ 回归模型训练完成!")
print("="*80)
if __name__ == "__main__":
main()