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

PyTorch LSTM预测谷歌股价时输出与目标趋势一致但缩放异常

LSTM股价预测缩放比例异常问题排查

问题描述

我用PyTorch搭建LSTM模型预测谷歌股价,训练后预测结果与目标值趋势一致,但缩放比例明显偏差。模型参数应为:输入维度1,隐藏层维度200,层数1,后续连接全连接层输出1。训练前用MinMaxScaler.fit_transform()做归一化,预测后用inverse_transform()还原,但问题依旧;尝试为训练/验证/测试集分别使用MinMaxScaler处理,结果趋势匹配但缩放问题仍存在。

数据处理代码

# get close prices from dataset
df_close =pd.DataFrame( df['Close'])
df_close_values = df_close.values 

# normalize data using MinMaxScaler
mmscaler = MinMaxScaler(feature_range=(0,1))
df_close_scaled = mmscaler.fit_transform(df_close_values)

# Sequence Lenght 
sequence_length = 25

# divide data into train, validation and test data
len_data = df_close_values.shape[0]
len_train_data = int(len_data * 0.8)
len_val_data = int((len_data - len_train_data)/2)
len_test_data = len_data - len_train_data - len_val_data

train_data = df_close_scaled[0:len_train_data]
val_data = df_close_scaled[len_train_data-sequence_length:len_train_data+len_val_data]
test_data = df_close_scaled[len_train_data+len_val_data-sequence_length:]

# Function to divide data into x and y
def partition_dataset(sequence_length, train_df):
    x, y = [], []
    data_len = train_df.shape[0]
    for i in range(sequence_length, data_len):
        x.append(train_df[i-sequence_length:i,:]) 
        y.append(train_df[i,0]) 
    
    # Convert the x and y to numpy arrays
    x = np.array(x, dtype=np.float32)
    y = np.array(y, dtype=np.float32)
    return x, y

x_train, y_train = partition_dataset(sequence_length, train_data)
x_val, y_val = partition_dataset(sequence_length, val_data)
x_test, y_test = partition_dataset(sequence_length, test_data)

数据形状:
x_train.shape , y_train.shape = (2554, 25, 1) (2554,)
x_val.shape , y_val.shape = (322, 25, 1) (322,)
x_test.shape , y_test.shape = (323, 25, 1) (323,)

LSTM模型代码

class LSTMPredictor(nn.Module):
  def __init__(self, input_size=50, hidden_size=1, num_layers=1, output_size=1, bidirectional=1, dropout=1.0, device='cuda'):
    super().__init__()

    # Atributes
    self.device      = 'cuda'
    self.num_layers  = num_layers
    self.hidden_size = hidden_size
    self.D           = True if bidirectional==2 else False
    self.output_size = output_size
    self.dropout     = dropout
    
    # define LSTM layer
    self.lstm = nn.LSTM(input_size    = input_size, 
                        hidden_size   = self.hidden_size,
                        num_layers    = self.num_layers,
                        bidirectional = self.D,
                        batch_first   = True,
                        dropout       = dropout)
    
    # define fully connected (MLP)
    self.fully_connected = nn.Linear(self.hidden_size,
                                     self.output_size)
    self.dropout = nn.Dropout(p=0.2)

  def forward(self, x, hidden=None):

    # Propagate input through LSTM
    output, (h, _) = self.lstm(x)
    
    out = self.fully_connected(output[:,-1])
  
    return out

截图说明

  • 目标值(蓝色)与预测值(橙色)对比:预测值整体趋势与目标值匹配,但数值缩放比例偏差明显,橙色线波动幅度远小于蓝色线
  • 预测值放大图:可见预测值波动趋势,但数值范围与真实值不符
  • 更新后截图:趋势匹配依旧,但缩放问题未解决,预测值数值区间仍偏离真实值

问题原因排查

1. 模型初始化参数完全错误

你定义的LSTMPredictor类初始化参数与实际需求完全不匹配:

  • 实际输入维度为1,但代码默认input_size=50,且实例化时未覆盖该参数,导致LSTM输入维度与实际输入(batch, 25, 1)不匹配,模型未在正确维度上训练
  • 实际需要隐藏层维度200,但代码默认hidden_size=1,模型容量严重不足,仅能学习趋势,无法拟合数值幅度
  • dropout=1.0意味着所有神经元都会被丢弃,完全不合理,dropout取值应在0-1之间(例如0.2)

2. 归一化使用错误

  • 初始用整个数据集做fit_transform会导致数据泄露,正确做法是仅在训练集上fit scaler,再用同一个scaler对验证集、测试集做transform
  • 尝试分数据集单独使用MinMaxScaler,会导致各子集缩放基准不同,模型学习的是子集相对缩放,还原后自然与真实值缩放比例不一致

3. 数据划分边界的潜在问题

验证集和测试集划分时从len_train_data-sequence_length取数据,虽保证了序列长度,但如果还原时未对应原始数据集的正确区间,可能加剧数值偏差(次要问题,核心为模型参数和归一化)

修复方案

  1. 修正模型初始化参数
    实例化模型时传入正确参数:
model = LSTMPredictor(input_size=1, hidden_size=200, num_layers=1, dropout=0.2, bidirectional=1)

同时简化bidirectional判断逻辑,不需要双向LSTM时直接设为False更清晰。

  1. 修正归一化流程
# 仅在训练集上拟合scaler
mmscaler = MinMaxScaler(feature_range=(0,1))
train_data_scaled = mmscaler.fit_transform(df_close_values[0:len_train_data])
# 验证集、测试集用同一个scaler做转换
val_data_scaled = mmscaler.transform(df_close_values[len_train_data:len_train_data+len_val_data])
test_data_scaled = mmscaler.transform(df_close_values[len_train_data+len_val_data:])

# 重新划分序列(确保基于对应区间的缩放后数据)
train_data = train_data_scaled
val_data = np.concatenate([train_data_scaled[-sequence_length:], val_data_scaled], axis=0)
test_data = np.concatenate([val_data_scaled[-sequence_length:], test_data_scaled], axis=0)

x_train, y_train = partition_dataset(sequence_length, train_data)
x_val, y_val = partition_dataset(sequence_length, val_data)
x_test, y_test = partition_dataset(sequence_length, test_data)
  1. 检查逆转换的输入形状
    模型输出为一维数组,inverse_transform要求输入为二维数组,还原时需先reshape:
y_pred_scaled = model(x_test).detach().cpu().numpy()
y_pred = mmscaler.inverse_transform(y_pred_scaled.reshape(-1, 1))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 01:55:20