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

LSTM模型预测结果左移且准确率恒为0的问题排查求助

问题:LSTM模型预测结果偏移+准确率始终为0的排查与解决

我查过不少关于LSTM预测结果偏移的帖子,但没找到可行方案。想问下我的Sequential_Input_LSTM函数里的数据切片逻辑有没有问题?另外模型准确率一直是0,不向1收敛,这个异常该怎么解决?


模型代码

import pandas as pd
import utilities
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import sklearn as sk
import pathlib
from ItemIdEnum import item
from sklearn.preprocessing import MinMaxScaler, StandardScaler
from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_absolute_percentage_error
from sklearn.model_selection import train_test_split
from sklearn.model_selection import TimeSeriesSplit
import seaborn as sns
from statsmodels.graphics.gofplots import qqplot
from scipy.stats import norm, uniform


def Sequential_Input_LSTM(df, input_sequence):
    df_np = df.to_numpy()
    X = []
    y = []
    
    for i in range(len(df_np) - input_sequence):
        row = [a for a in df_np[i:i + input_sequence]]
        X.append(row)
        label = df_np[i + input_sequence]
        y.append(label)
        
    return np.array(X), np.array(y)

def createLSTM(itemName: str, data: pd.DataFrame, n_input: int, n_features: int, epochs: int, batch_size: int, save: bool, savePlot: bool) -> None:
    cwd = pathlib.Path().cwd()
    path = cwd.joinpath("LSTM_models/"+itemName)
    try:
        path.mkdir(parents=True, exist_ok=False)
    except FileExistsError as e:
        print(e)
        pass
    
    early_stop = tf.keras.callbacks.EarlyStopping(monitor = 'loss', patience = 5)
    data = utilities.convertFromZuluTime(data)
    data = utilities.removeOutliers(data)

    data = data.drop(columns=['range','universe_id','http_last_modified'])
    data['issued'] = pd.to_datetime(data['issued'],origin='unix',unit='D')
    date_time = pd.to_datetime(data['issued'])
    data.set_index('issued',inplace=True)
    plt.figure()
    data['price'].hist()
    plt.savefig(f'{path/itemName}_predata_standarize_hist.png')
    plt.figure()
    data['price'].plot(ylabel='Price')
    plt.savefig(f'{path/itemName}_predata_standarize_plot.png')
    xFeat = data
   
    sc =MinMaxScaler()
    X_ft = sc.fit_transform(xFeat.values)
    X_ft = pd.DataFrame(X_ft, index=xFeat.index, columns=xFeat.columns)
    plt.figure()
    data['price'].hist()
    plt.savefig(f'{path/itemName}_postdata_standarize_hist.png')
    plt.figure()
    data['price'].plot()
    plt.savefig(f'{path/itemName}_postdata_standarize_plot.png')
    n_input = n_input  

    df_min_model_data = X_ft['price']

    X, y = Sequential_Input_LSTM(df_min_model_data, n_input)
    trainSplit = 0.8
    splitIDX = int(np.floor(len(X)*trainSplit))
    dateIndex = date_time
    XTrain, xTest = X[:splitIDX], X[splitIDX:]
    yTrain, yTest = y[:splitIDX], y[splitIDX:]
    XTrainDates, xTestDates = dateIndex[:splitIDX], dateIndex[splitIDX+10:]


    n_features = n_features

    lstm = tf.keras.models.Sequential()
    lstm.add(tf.keras.layers.InputLayer((n_input,n_features)))
    lstm.add(tf.keras.layers.LSTM(100,return_sequences=True,activation='relu'))
    lstm.add(tf.keras.layers.Dropout(0.5))
    lstm.add(tf.keras.layers.LSTM(100,return_sequences=True,activation='relu'))
    lstm.add(tf.keras.layers.LSTM(50))
    lstm.add(tf.keras.layers.Dense(50, activation='relu', kernel_initializer='he_normal'))
    lstm.add(tf.keras.layers.Dense(1))
    lstm.compile(loss='mean_squared_error',optimizer='adam', metrics=[tf.keras.metrics.RootMeanSquaredError(),tf.keras.metrics.Accuracy()])
    lstm.summary()

    history = lstm.fit(XTrain,yTrain,epochs=epochs,batch_size=batch_size,shuffle=False,validation_data=(xTest,yTest), callbacks = [early_stop] )
    lstm.evaluate(xTest,yTest,verbose=0) # type: ignore
    if save == True:
        tf.keras.models.save_model(lstm, "somePath")
    
    test_predictions1 = lstm.predict(xTest).flatten()

    X_test_list = []
    for i in range(len(xTest)):
        X_test_list.append(xTest[i][0])
    
    test_predictions_df1 = pd.DataFrame({'X_test':list(X_test_list), 
                                    'LSTM Prediction':list(test_predictions1)})

    test_predictions_df1.plot(title=f'{itemName} LSTM Prediction vs Actual',ylabel='Price')
    plt.show()
    
    if savePlot == True:
        plt.figure()
        test_predictions_df1.plot(title=f'{itemName} LSTM Prediction vs Actual',ylabel='Price')
        plt.savefig(f'{path/itemName}_prediction_plot.png')

        print(history.history.keys())
        plt.figure()
        plt.plot(history.history['loss'],label='loss')
        plt.plot(history.history['val_loss'],label='val_loss')
        plt.title('loss')
        plt.ylabel('loss')
        plt.xlabel('epoch')
        plt.legend()
        plt.savefig(f'{path/itemName}_loss_plot.png')
        plt.figure()
        plt.plot(history.history['accuracy'],label='accuracy')
        plt.plot(history.history['val_accuracy'],label='val_accuracy')
        plt.title('accuracy')
        plt.ylabel('accuracy_values')
        plt.xlabel('epoch')
        plt.legend()
        plt.savefig(f'{path/itemName}_accuracy_plot.png')
        plt.figure()
        plt.plot(history.history['root_mean_squared_error'],label='root_mean_squared_error')
        plt.plot(history.history['val_root_mean_squared_error'],label='val_root_mean_squared_error')
        plt.title('root_mean_squared_error')
        plt.ylabel('root_mean_squared_values')
        plt.xlabel('epoch')
        plt.legend()
        plt.savefig(f'{path/itemName}_root_mean_squared_error_plot.png')

相关图表

  • RMSE曲线:
    RMSE曲线
  • Loss曲线:
    Loss曲线
  • 准确率曲线:
    准确率曲线
  • 预测值与测试值对比:
    预测值与测试值对比

问题解答

一、数据切片逻辑检查

你的Sequential_Input_LSTM函数滑动窗口的核心逻辑没问题:用前input_sequence个时间步的数据预测下一个时间步的价格,符合时间序列预测的常规构造方式。但存在维度匹配错误:

  • 你输入的df_min_model_data是单特征(price)的一维Series,转成numpy数组后是一维结构,生成的X形状为(样本数, input_sequence),但LSTM要求输入必须是(样本数, 时间步长, 特征数)的三维结构(这里特征数为1)。
  • 修复方法:生成X后增加特征维度:
    X, y = Sequential_Input_LSTM(df_min_model_data, n_input)
    X = np.expand_dims(X, axis=-1)  # 形状变为(样本数, input_sequence, 1)
    
    不修复这个问题的话,LSTM会错误地把每个时间步当成多个特征处理,直接导致预测结果偏移或不收敛。

二、准确率始终为0的原因与解决

这个问题是指标用错了:

  • 你用的tf.keras.metrics.Accuracy()是分类任务的指标,用来判断预测值和真实值是否完全相等,但你的任务是回归(预测连续价格),连续数值几乎不可能和真实值完全一致,所以准确率永远为0。
  • 修复方法:替换为回归任务的专属指标,比如平均绝对误差、平均绝对百分比误差:
    lstm.compile(loss='mean_squared_error',optimizer='adam', 
                 metrics=[tf.keras.metrics.RootMeanSquaredError(),
                          tf.keras.metrics.MeanAbsoluteError()])
    

三、其他可能导致异常的问题

  1. 归一化可视化错误:代码中保存的归一化后图表用的是原始data['price'],不是归一化后的X_ft['price'],导致无法验证归一化效果,需修改为:
    plt.figure()
    X_ft['price'].hist()
    plt.savefig(f'{path/itemName}_postdata_standarize_hist.png')
    plt.figure()
    X_ft['price'].plot()
    plt.savefig(f'{path/itemName}_postdata_standarize_plot.png')
    
  2. LSTM激活函数不合适:你给LSTM层用了relu激活,LSTM的默认激活是tanh,relu在递归结构中更容易出现梯度消失,建议改回tanh:
    lstm.add(tf.keras.layers.LSTM(100,return_sequences=True,activation='tanh'))
    
  3. 模型结构过复杂:三层LSTM+Dense层的结构,若数据集不大,容易出现过拟合或收敛困难,可尝试简化结构(比如去掉一层LSTM,减少神经元数量)。
  4. 日期索引切片错误:测试集日期切片xTestDates = dateIndex[splitIDX+10:]无依据,正确的切片应对应y的索引:
    XTrainDates, xTestDates = dateIndex[n_input:splitIDX+n_input], dateIndex[splitIDX+n_input:]
    
  5. 预测值未逆归一化:当前预测的是归一化后的价格,若要和原始价格对比,需用sc.inverse_transform转换回原始尺度:
    test_predictions_original = sc.inverse_transform(test_predictions1.reshape(-1,1))
    yTest_original = sc.inverse_transform(yTest.reshape(-1,1))
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 01:38:10