Files
crpto_predictor/train_classification_full.py
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

183 lines
6.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""分类任务:使用2020-2025全部数据训练,2026年测试"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import numpy as np
import pandas as pd
import pandas_ta as ta
from sklearn.preprocessing import StandardScaler
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
from data.fetcher import BinanceDataFetcher
from models.dlinear.model import DLinear, DLinearConfig
from training.trainer import Trainer
from training.config import TrainingConfig
import json
import pickle
class ClassificationPreprocessor:
"""分类任务预处理器:预测涨(1)或跌(0)"""
def __init__(self, lookback=168, forecast_horizon=1):
self.lookback = lookback
self.forecast_horizon = forecast_horizon
self.scaler = StandardScaler()
self.is_fitted = False
def add_features(self, df):
"""添加技术指标"""
df = df.copy()
df['rsi'] = ta.rsi(df['close'], length=14)
macd = ta.macd(df['close'])
if macd is not None and not macd.empty:
df['macd'] = macd.iloc[:, 0]
df['macd_hist'] = macd.iloc[:, 1]
df['macd_signal'] = macd.iloc[:, 2]
return df
def create_labels(self, df):
"""创建二分类标签:1=上涨, 0=下跌"""
df = df.copy()
df['future_price'] = df['close'].shift(-self.forecast_horizon)
df['future_return'] = (df['future_price'] - df['close']) / df['close']
# 二分类:大于0为上涨(1),否则为下跌(0)
df['label'] = (df['future_return'] > 0).astype(int)
return df[:-self.forecast_horizon]
def preprocess(self, df, fit=True):
"""完整预处理流程"""
df = self.add_features(df)
df = self.create_labels(df)
df = df.dropna().reset_index(drop=True)
feature_cols = ['open', 'high', 'low', 'close', 'volume',
'rsi', 'macd', 'macd_hist', 'macd_signal']
if fit:
scaled_features = self.scaler.fit_transform(df[feature_cols])
self.is_fitted = True
self.feature_columns = feature_cols
else:
if not self.is_fitted:
raise ValueError("Must fit before transform")
scaled_features = self.scaler.transform(df[feature_cols])
X, y = [], []
for i in range(len(scaled_features) - self.lookback):
X.append(scaled_features[i:i + self.lookback])
y.append(df['label'].iloc[i + self.lookback])
return np.array(X), np.array(y), df
def main():
print("="*80)
print("分类任务:2020-2025全量训练 → 2026测试")
print("="*80)
# ========== 步骤1: 获取训练数据(2020-2025) ==========
print("\n[1/6] 获取2020-2025年训练数据...")
fetcher = BinanceDataFetcher()
df_train = fetcher.fetch_ohlcv(
symbol='BTC',
timeframe='1h',
start_date='2020-01-01',
end_date='2025-12-31',
use_cache=True
)
print(f"✓ 训练数据量: {len(df_train):,} 行")
print(f"✓ 时间范围: {df_train['timestamp'].min()} 至 {df_train['timestamp'].max()}")
print(f"✓ 价格: ${df_train['close'].iloc[0]:,.2f} → ${df_train['close'].iloc[-1]:,.2f}")
# ========== 步骤2: 预处理训练数据 ==========
print("\n[2/6] 预处理训练数据(分类任务)...")
preprocessor = ClassificationPreprocessor(lookback=168, forecast_horizon=1)
X_train, y_train, processed_train = preprocessor.preprocess(df_train, fit=True)
print(f"✓ 训练序列: {len(X_train):,}")
print(f"✓ 特征数量: {len(preprocessor.feature_columns)}")
print(f"✓ 特征列表: {preprocessor.feature_columns}")
# 标签分布
n_up = (y_train == 1).sum()
n_down = (y_train == 0).sum()
print(f"\n✓ 标签分布:")
print(f" 上涨(1): {n_up:,} ({n_up/len(y_train)*100:.1f}%)")
print(f" 下跌(0): {n_down:,} ({n_down/len(y_train)*100:.1f}%)")
# 计算类别权重
pos_weight = n_down / n_up if n_up > 0 else 1.0
print(f" pos_weight: {pos_weight:.3f}")
# ========== 步骤3: 创建数据加载器 ==========
print("\n[3/6] 创建数据加载器...")
train_dataset = TensorDataset(
torch.FloatTensor(X_train),
torch.FloatTensor(y_train)
)
# 用10%作为验证集(内部分割)
train_size = int(0.9 * len(train_dataset))
val_size = len(train_dataset) - train_size
train_subset, val_subset = torch.utils.data.random_split(
train_dataset, [train_size, val_size]
)
train_loader = DataLoader(train_subset, batch_size=64, shuffle=True)
val_loader = DataLoader(val_subset, batch_size=64, shuffle=False)
print(f" 训练样本: {train_size:,}")
print(f" 验证样本: {val_size:,}")
# ========== 步骤4: 创建模型 ==========
print("\n[4/6] 创建DLinear分类模型...")
model_config = DLinearConfig(
input_dim=9,
lookback=168,
task='classification', # 分类任务
hidden_dim=256,
dropout=0.1,
moving_avg_kernel=25
)
model = DLinear(model_config)
print(f"✓ 模型参数: {model.count_parameters():,}")
print(f"✓ 任务类型: 二分类(预测涨/跌)")
print(f"✓ 损失函数: BCEWithLogitsLoss")
# ========== 步骤5: 训练 ==========
print("\n[5/6] 开始训练...")
train_config = TrainingConfig(
batch_size=64,
max_epochs=50,
learning_rate=0.001,
early_stopping_patience=15,
lr_scheduler='plateau',
save_best=True,
checkpoint_dir='./models/checkpoints/dlinear_classification_2026'
)
trainer = Trainer(
model,
train_config,
task='classification',
pos_weight=pos_weight # 使用类别权重
)
history = trainer.fit(train_loader, val_loader)
# ========== 步骤6: 保存 ==========
print("\n[6/6] 保存模型...")
with open('./models/checkpoints/dlinear_classification_2026/preprocessor.pkl', 'wb') as f:
pickle.dump(preprocessor, f)
print(f"✓ 模型保存至: ./models/checkpoints/dlinear_classification_2026/")
print("\n" + "="*80)
print("✓ 训练完成!准备在2026年数据上测试...")
print("="*80)
if __name__ == "__main__":
main()