You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加特征/加深神经网络后MSE上升的原因及优化方案求助

比特币5分钟价格预测模型异常:复杂模型效果劣于简单模型的原因及优化方案

我用PyTorch构建了预测5分钟后比特币价格的神经网络,实验中发现反常现象:增加输入特征或加深网络结构时,模型MSE反而显著上升:

  • 单特征单层线性模型:训练集MSE≈2150,测试集≈2050
  • 三特征单层线性模型:训练/测试集MSE均上升约100
  • 全12特征深层神经网络:MSE升至≈20000,加入Dropout后效果更差

后续实现CLR(循环学习率)算法调整学习率,损失曲线更稳定,但最优模型仍是最简单的单特征线性模型。求该现象的原因及进一步优化建议。


现象原因分析

  1. 特征质量问题:新增的特征可能和目标变量(5分钟后比特币价格)相关性极低,甚至是噪声特征。线性模型中加入无关特征会引入额外方差,导致MSE上升;深层网络则会过度拟合这些噪声特征,使得泛化能力急剧下降。
  2. 特征未做标准化/归一化:从代码看,所有特征直接输入模型,没有做标准化处理。不同特征的量纲差异会导致模型训练时权重更新失衡,尤其是深层网络,容易出现梯度消失/爆炸,导致模型无法收敛到最优解。
  3. 深层网络结构过度复杂:你的深层网络有7层全连接层,参数数量远超过任务所需,而比特币价格短期预测本身具有很强的随机性,数据中的有效信号有限,复杂模型会过度拟合训练数据中的噪声,导致测试集MSE暴增。
  4. Dropout使用不当:Dropout的作用是抑制过拟合,但如果模型本身还没学到有效特征就加Dropout,会进一步破坏模型的学习能力,导致性能下降。你设置的Dropout概率(0.05)过低,对抑制过拟合作用有限,反而增加了训练的随机性。

优化方案

1. 特征工程优化

  • 特征筛选:计算每个特征与目标变量dq的皮尔逊相关系数,只保留相关性较高(比如绝对值>0.1)的特征;也可以用互信息、方差膨胀因子(VIF)剔除冗余/无关特征。
  • 特征标准化:对所有输入特征做Z-score标准化((X - mean) / std)或Min-Max归一化,确保特征量纲一致,加速模型收敛。
  • 构造有效特征:基于现有特征构造时间序列相关特征,比如:
    • 过去N个时间段的价格均值/波动率
    • 价格的差分(当前价-前一时间段价格)
    • 技术指标(如RSI、MACD、布林带等)

2. 模型结构调整

  • 简化深层网络:减少全连接层的数量和神经元个数,比如先尝试2-3层全连接层(如12→64→32→1),逐步增加复杂度,观察性能变化。
  • 改用时间序列专用模型:比特币价格是时间序列数据,全连接网络无法捕捉时序依赖,可尝试:
    • LSTM/GRU:专门处理时序数据的循环神经网络,能捕捉价格的时序关联
    • Transformer(如Temporal Fusion Transformer):适合多变量时序预测,能处理长期依赖和特征交互
  • 尝试线性模型变种:比如带正则化的线性模型(Ridge/Lasso),在单特征线性模型基础上加入L2/L1正则,进一步提升泛化能力。

3. 训练策略优化

  • 正则化调整:如果用深层网络,可尝试增大Dropout概率(比如0.2-0.5),或加入L2正则(在优化器中设置weight_decay=1e-4),抑制过拟合。
  • 学习率调优:CLR已经让损失曲线稳定,但可以尝试更小的初始学习率(比如1e-4),或用学习率衰减(如StepLR、ReduceLROnPlateau),让模型更平缓地收敛。
  • 数据增强:对时序数据做简单增强,比如加入少量高斯噪声,或对特征做随机缩放,提升模型的泛化能力。
  • 交叉验证:改用时间序列交叉验证(而非随机划分train/test),避免数据泄露,更准确评估模型的泛化能力。

4. 损失函数与评估指标

  • 比特币价格预测是回归任务,除了MSE,可尝试MAE(平均绝对误差)作为损失函数,对异常值更鲁棒;同时用MAPE(平均绝对百分比误差)评估模型的相对误差,更贴合实际业务场景。

数据预处理代码

df.head(2)

X = df.drop('dq', axis=1)
y = df['dq']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

X_train = torch.from_numpy(X_train.values.astype('float32'))
X_test = torch.from_numpy(X_test.values.astype('float32'))
y_train = torch.from_numpy(y_train.values.astype('float32')).reshape(-1, 1)
y_test = torch.from_numpy(y_test.values.astype('float32')).reshape(-1, 1)

device = torch.device('mps')

DataFrame示例(前2行)

单特征单层模型训练代码

class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.fc = nn.Linear(1, 1)  # Linear layer

    def forward(self, x):
        return self.fc(x)
    
model = MyModel()
early_stopping = EarlyStopping(patience=5, verbose=True)

criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters())

# Prepare the data
X_train_avg = ((X_train[:, 1:2] + X_train[:, 1:2]) / 2.0)
X_test_avg = ((X_test[:, 1:2] + X_test[:, 1:2]) / 2.0)

model = model.to(device)
X_train_avg = X_train_avg.to(device)
y_train = y_train.to(device)
X_test_avg = X_test_avg.to(device)
y_test = y_test.to(device)

train_data = TensorDataset(X_train_avg, y_train)
test_data = TensorDataset(X_test_avg, y_test)

train_loader = DataLoader(train_data, batch_size=32, shuffle=True, pin_memory=True)
test_loader = DataLoader(test_data, batch_size=32)

num_epochs = 40

for epoch in range(num_epochs):
    model.train()
    total_loss = 0
    total_batches = len(train_loader)
    pbar = tqdm(train_loader)
    for batch_idx, (inputs, targets) in enumerate(pbar):
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, targets)
        loss.backward()
        optimizer.step()
        total_loss = total_loss + loss.item()
        if (batch_idx + 1) % 100 == 0:
            pbar.set_description(f'Epoch {epoch+1}/{num_epochs}, Loss: {total_loss/batch_idx:.4f}')
        if batch_idx == total_batches - 1:
            model.eval()
            with torch.no_grad():
                test_loss = sum(criterion(model(inputs), targets) for inputs, targets in test_loader)
                test_loss /= len(test_loader)
            pbar.set_description(f'Epoch {epoch+1}/{num_epochs}, Loss: {total_loss/(1+batch_idx):.4f}, TestLoss: {test_loss:.4f}')
        
    early_stopping(test_loss, model)

    if early_stopping.early_stop:
        print("Early stopping")
        break

单特征模型损失:训练集约2150,测试集约2050

三特征单层模型代码

class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.fc = nn.Linear(3, 1)  # Linear layer

    def forward(self, x):
        return self.fc(x)
    
model = MyModel()
early_stopping = EarlyStopping(patience=5, verbose=True)

criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters())

# Prepare the data
X_train_avg = (X_train[:, 1::4] / 2.0 + X_train[:, 3::4] / 2.0)
X_test_avg = (X_test[:, 1::4] / 2.0 + X_test[:, 3::4] / 2.0)

三特征模型损失:较单特征模型上升约100

全特征深层模型代码

class MyModel(nn.Module):
    def __init__(self, input_dim):
        super(MyModel, self).__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_dim, 256),
            nn.ReLU(),
#             nn.Dropout(p=0.05),
            nn.Linear(256, 128),
            nn.ReLU(),
#             nn.Dropout(p=0.05),
            nn.Linear(128, 64),
            nn.ReLU(),
#             nn.Dropout(p=0.05),
            nn.Linear(64, 64),
            nn.ReLU(),
            nn.Linear(64, 32),
            nn.ReLU(),
            nn.Linear(32, 16),
            nn.ReLU(),
            nn.Linear(16, 1)
        )

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

model = MyModel(12)
early_stopping = EarlyStopping(patience=20, verbose=True)

criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters())

model = model.to(device)
X_train = X_train.to(device)
y_train = y_train.to(device)
X_test = X_test.to(device)
y_test = y_test.to(device)

深层模型损失:约20000,加入Dropout后更糟


内容的提问来源于stack exchange,提问作者Schmied

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 18:35:55