简介:基于LSTM的气温预测与可视化Python源码,配套文档说明,是一份适合期末大作业、课程设计的高分参考实现,同时适合Python学习者作为时序预测项目的实战练手材料。资源压缩包共16个文件,以8个Python脚本为主体,覆盖天气数据爬取、数据预处理、LSTM模型定义与训练、气温预测及可视化展示等完整流程;另有4个编译缓存文件、2个Excel数据文件和2个Markdown说明文档,整包约723KB,结构简洁、便于部署。所有代码均含详细注释,模块划分清晰;配套文档对项目背景、运行环境、参数配置和实验过程做了说明,使用说明文档还提供快速开始指引,新手也能轻松复现。不仅可作为期末大作业、课程设计的直接模板,也可用于扩展新气象数据、对比不同时序模型或调整网络结构,省去从零搭建的时间。已有179人学习下载,是兼顾完整性与易用性的气温预测入门示例。
1. 气温预测大作业选型:为什么是 LSTM,而不是 ARIMA 或者普通神经网络?
如果你正盯着“基于LSTM的气温预测及可视化python源码+文档说明(高分期末大作业)”这个标题找现成代码,先停一下。我的建议是:不要把下载来的源码直接改名交上去,评阅老师一眼就能看出是不是你写的。但把这个标题当成一个完整的技术方案来做,你完全可以在一个周末内跑通一条可靠的气温预测流水线——数据清洗、滑窗切分、LSTM 模型训练、预测曲线可视化、误差分析,最后整理成一份能拿高分的大作业文档。LSTM 在气温这类单变量时间序列上确实有用,不是因为它在所有指标上碾压 ARIMA,而是因为它能让你把“序列建模”的完整流程走一遍——数据处理、网络结构、训练调参、结果评估全都能在同一个项目里展示,这才是课程设计真正想考察的东西。这套笔记就是按这个目标来写的,适合你正在准备期末大作业,同时也想把 LSTM 的细节真正弄明白。
2. 气温序列预处理:从原始 CSV 到 LSTM 能吃的样本
2.1 为什么原始气温序列不能直接喂进 LSTM
气温预测是一个标准的时间序列任务。它的特殊性在于:序列本身有强烈的周期性和自相关性——今天 30 度,明天大概率 29 度,而不是突然 15 度。LSTM 能捕捉这种时间依赖,但它并不理解“日期”,它只认识数值矩阵。所以你要做的第一件事是:把一维的气温序列改造成监督学习所需的二维样本,即[样本数, 时间步长, 特征数]这样的张量。
常见做法是“滑窗”(sliding window),用过去 24 小时的气温去预测未来 1 小时的气温。这里的 24 就是time_step,也就是 LSTM 展开的步数。选择多大的窗口直接决定模型能看到多长的历史。气温序列的周期是 24 小时,所以窗口至少应该覆盖一个完整周期;如果你预测的是未来 6 小时,窗口可以适当扩大到 48 或 72。窗口太小模型看不到周期,窗口太大训练成本涨、收益递减,这个后面避坑章会细讲。
2.2 数据清洗中的三个常见坑:缺失值、时间戳、异常点
先从你拿到的数据说起。NOAA 的公开气象站数据、国内气象数据网导出的 CSV,或者老师给的数据集,格式上做的事情是一样的:日期列 + 气温列。但原始数据几乎必然有缺失值,比如某个小时的记录为空,或者出现明显异常值——夏季某天突然蹦出一个零下 20 度,这大概率不是真实气温而是传感器故障。
缺失值处理我一般不会用均值填充,而是用前后时刻的线性插值,气温变化是连续的,邻近时刻的值远比全局均值有参考意义。异常值则用滚动中位数检测:如果某个点偏离其前后 12 小时的中位数超过 3 倍标准差,就把它替换成插值结果。时间戳处理也很关键,如果你用 pandas 读取 CSV,一定要把日期列解析成datetime类型并按它排序,否则数据一旦乱序,整个序列的自相关性就会被破坏。
import pandas as pd import numpy as np # 读取原始数据,假设有两列:timestamp、temperature df = pd.read_csv('temperature_data.csv', parse_dates=['timestamp']) df = df.sort_values('timestamp').reset_index(drop=True) # 缺失值处理:按时间顺序线性插值 df['temperature'] = df['temperature'].interpolate(method='linear', limit_direction='both') # 异常值检测:滚动中位数 + 3倍标准差 rolling_median = df['temperature'].rolling(window=24, center=True).median() rolling_std = df['temperature'].rolling(window=24, center=True).std() mask = (df['temperature'] - rolling_median).abs() > 3 * rolling_std df.loc[mask, 'temperature'] = rolling_median[mask] print(f"总样本数: {len(df)}, 异常点替换数: {mask.sum()}")这里有两个参数你可以根据自己的数据改:window=24表示用前后 24 小时的中位数作为局部基准,如果你的数据是分钟级的,就要改成 1440;3 * rolling_std是异常阈值,数据本身波动大就放宽到 4,波动小可以收紧到 2.5。原则是宁可少替换也不要误伤真实的气温突变。
2.3 归一化:为什么 MinMaxScaler 是 LSTM 气温预测的默认选择
LSTM 内部用的是 tanh 和 sigmoid 激活函数,它们的输出范围分别是 (-1, 1) 和 (0, 1)。如果输入是 35 度这样的原始数值,经过激活函数后梯度很容易饱和,训练会变得非常慢甚至不收敛。所以归一化不是可选项,是必选项。
对气温序列,我通常用MinMaxScaler缩放到 [0, 1] 区间。你可能会问为什么不用StandardScaler(标准化到均值 0 方差 1),两者都行,但气温数据通常没有严重的重尾分布,MinMax 的 [0, 1] 区间和 LSTM 的输出范围更匹配,反归一化的时候也更直观。
归一化有一个极易踩的坑:必须用训练集的统计量来转换验证集和测试集。如果你对全部数据一起 fit,再切分训练测试,测试集的信息就泄漏进了训练过程,最终评估结果会虚高,答辩时一问就露馅。
from sklearn.preprocessing import MinMaxScaler # 注意:先切分再归一化,避免数据泄漏 train_size = int(len(df) * 0.8) train_df = df.iloc[:train_size] test_df = df.iloc[train_size:] scaler = MinMaxScaler(feature_range=(0, 1)) train_scaled = scaler.fit_transform(train_df[['temperature']]) test_scaled = scaler.transform(test_df[['temperature']]) # 保存 scaler,预测完要反归一化回真实温度 import joblib joblib.dump(scaler, 'scaler.save')这里train_size用 80% 做训练、20% 做测试是时间序列任务里最常见的比例,但要注意必须按时间顺序切,不能随机打乱。scaler.save是你预测阶段的“后悔药”,没有它你预测出来的结果永远停留在 [0, 1] 区间,根本没法跟真实气温对比。
2.4 滑窗切分:构造监督学习样本的核心代码
归一化之后的气温序列还是个一维数组,LSTM 需要的是三维张量。写一个滑窗函数,把序列切成长度为time_step的输入片段和对应的预测目标。这里有个隐藏逻辑你要想清楚:每个样本的x是过去time_step个时刻的气温,y是未来某个时刻的气温。如果你做单步预测,y就是下一个时刻;如果做多步预测,y可以是一段序列,但这样做模型的输出维度会变,复杂度上升一个等级。作为大作业,我建议先用单步预测把流程跑通,拿到可靠结果后再尝试多步。
def create_sequences(data, time_step=24, pred_step=1): X, y = [], [] for i in range(len(data) - time_step - pred_step + 1): X.append(data[i:i + time_step, 0]) y.append(data[i + time_step:i + time_step + pred_step, 0]) return np.array(X), np.array(y) # 假设气温是小时级数据,用过去24小时预测未来1小时 time_step = 24 pred_step = 1 X_train, y_train = create_sequences(train_scaled, time_step, pred_step) X_test, y_test = create_sequences(test_scaled, time_step, pred_step) # 重塑为 LSTM 输入格式:(样本数, 时间步, 特征数) X_train = X_train.reshape(X_train.shape[0], X_train.shape[1], 1) X_test = X_test.reshape(X_test.shape[0], X_test.shape[1], 1) print(f"训练集形状: {X_train.shape}, 测试集形状: {X_test.shape}")time_step=24是小时级气温数据的经验起点值,一天有 24 个小时,窗口正好掠过两个相邻的日周期,模型能同时看到今天的这个时刻和昨天的同一时刻。如果你的数据是 10 分钟一条,24 就只覆盖 4 小时,远不够;同理,如果是天级数据,一个周期是 365 天,24 天的窗口对年周期来说太短,这种情况建议用 30 或 60 天窗口加多变量特征。pred_step=1表示前向预测 1 步,想预测未来 6 小时就改成 6,但输出层维度也要同步调整。
3. 搭建 LSTM 气温预测模型:网络结构、损失函数与训练策略
3.1 单变量 LSTM 还是多变量 LSTM:期末大作业怎么选
在这个大作业的语境里,最常见的方案是用单一的气温序列作为输入,也就是特征数=1。但如果你想让模型效果更好、论文文档里有更多可写的内容,可以升级为多变量输入:把湿度、气压、风速、降水量一起作为特征喂进去。气温不是孤立变化的,湿度高往往意味着体感温度高,气压骤降通常预示降温。多变量输入时X_train.shape变成(样本数, 时间步, 特征数),特征数从 1 变成 4 或 5,模型结构不需要改,但训练收敛会更慢,数据对齐也更麻烦。
我给你的建议是:如果你只有气温数据,就直接做单变量,这一点不丢人,单变量做扎实了照样高分;如果老师给了附带的气象要素数据,就做多变量,同时在文档里加一段“特征相关性分析”——用 pandas 的corr()算一下各个特征与气温的相关系数,选相关系数高的几个作为输入,这个细节非常加分。
3.2 网络结构设计:两层 LSTM 加 Dropout 是稳妥起点
LSTM 模型的结构,我一般会从“一层 LSTM + 一层 Dense”起步,但作为期末大作业,强烈建议你至少做成两层 LSTM。理由有两个:一是两层结构能让模型学到不同时间尺度上的依赖关系——第一层捕捉局部波动,第二层捕捉周期性趋势;二是两层结构在文档里能写出更多的设计思考和实验对比。这正好回应了“LSTM神经网络 模型代码”这类搜索诉求,不是堆层数,而是有理由地加深。
第一层 LSTM 用return_sequences=True,把完整的隐状态序列传给第二层;第二层不返回序列,只返回最后时间步的隐状态,然后接一个全连接层输出预测值。Dropout 加在两层 LSTM 之间,取值 0.2 左右,防止过拟合。损失函数用mse(均方误差),气温预测是回归任务,不用交叉熵。
from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau model = Sequential() model.add(LSTM(units=64, return_sequences=True, input_shape=(time_step, X_train.shape[2]))) model.add(Dropout(0.2)) model.add(LSTM(units=32, return_sequences=False)) model.add(Dropout(0.2)) model.add(Dense(units=pred_step)) model.compile(optimizer='adam', loss='mse', metrics=['mae']) model.summary()units=64是第一层 LSTM 的记忆单元数,它决定了模型内部状态向量的维度。64 对于小时级气温数据来说是一个够用但不浪费的起点;数据量大(超过 5 万条)可以提到 128,数据少(几千条)就降到 32,不然容易过拟合。第二层units=32负责把第一层提取到的抽象特征压缩成更紧凑的表示,再交给输出层。两层 unit 数量从大到小,像漏斗一样,这是时序模型最常见的设计。input_shape里的X_train.shape[2]就是特征数,单变量为 1,多变量为几就填几。
3.3 训练配置:epochs、batch_size、早停与学习率衰减
训练 LSTM 有四个超参数,几乎决定了你最终结果的上下限:epochs、batch_size、EarlyStopping和ReduceLROnPlateau。先说不推荐的写法:model.fit(X_train, y_train, epochs=100, batch_size=32)硬跑 100 轮。LSTM 训练到第 20 轮可能就已经收敛,剩下 80 轮全在过拟合,验证集 loss 一路飙升。
正确做法是搭配早停。EarlyStopping监控验证集 loss,patience=10表示连续 10 轮没有改善就停止训练,这样你既不用反复试 epochs,又能拿到那个“刚好收敛”的模型。ReduceLROnPlateau监控验证集 loss 连续 5 轮不降时自动把学习率乘以 0.5,这能在模型进入平台期后继续往下探一点。
early_stop = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True) reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=5, min_lr=1e-5) history = model.fit( X_train, y_train, validation_data=(X_test, y_test), epochs=100, batch_size=32, callbacks=[early_stop, reduce_lr], verbose=1 )batch_size=32是内存和梯度稳定性的平衡点。调大能加快训练,但梯度的随机性减小,模型更容易陷入局部最优;调小则收敛慢但不稳定。如果你在 CPU 上跑,batch_size保持 32 就好;如果是 GPU 且显存充足,可以提到 64 或 128 以充分利用并行计算。restore_best_weights=True这个参数一定要加,它表示训练结束后自动回退到验证集表现最好的那一轮权重,而不是用最后一轮——这是很多人容易忽略的细节。
3.4 预测与反归一化:把数值变回“温度”
模型训练完成后,predict输出的结果是在 [0, 1] 区间内的归一化值,要用之前保存的scaler反变换回真实的摄氏度,才能跟原始数据画在同一张图上比较。这里要注意scaler.inverse_transform期望的输入形状是(样本数, 1),而model.predict输出形状是(样本数, pred_step),当pred_step=1时形状刚好匹配,但多步预测时要做 reshape。
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score # 模型预测 y_pred_scaled = model.predict(X_test) # 反归一化 y_pred = scaler.inverse_transform(y_pred_scaled.reshape(-1, 1)).flatten() y_true = scaler.inverse_transform(y_test.reshape(-1, 1)).flatten() # 评估指标 mae = mean_absolute_error(y_true, y_pred) rmse = np.sqrt(mean_squared_error(y_true, y_pred)) r2 = r2_score(y_true, y_pred) print(f"MAE: {mae:.2f}°C, RMSE: {rmse:.2f}°C, R²: {r2:.4f}")三个指标各有侧重:MAE(平均绝对误差)比较直观,就是你平均预测偏差几度,3 度以内算不错;RMSE(均方根误差)对大的偏差更敏感,如果某些时刻预测严重偏离,RMSE 会明显大于 MAE;R² 是拟合优度,越接近 1 越好,但气温预测的 R² 通常不会特别高,0.85 以上就算是相当好的结果了。这三个指标要同时写进大作业文档,比单独贴一个 loss 曲线有说服力得多。
4. 可视化:用 Matplotlib 画出预测曲线、误差分布与训练过程
4.1 可视化在期末大作业里扮演的角色
很多同学把可视化理解为“画一张图交差”,但在 LSTM 气温预测这个大作业里,可视化其实是你的调试工具和打分依据。一个训练好的模型,loss 曲线能告诉你有没有收敛,预测对比图能告诉你模型是不是在认真预测还是偷懒直接复制前一天的数据,残差分布能告诉你模型在哪些时段系统性偏差。这些图放到文档里,既展示了你的工程能力,也暴露了你的思考深度——评阅老师看重的恰恰是后者。所以这个章节我不会只教你“画图”,而是告诉你每张图应该怎么看、看出什么问题。
4.2 第一张图:真实值 vs 预测值时序对比
画这张图有一个关键决策:画多少条数据。如果把测试集全部 2000 条数据都画出来,曲线会挤成一团,肉眼看不出预测质量。我一般只画最近 200~300 个时间步的对比,或者每隔几个点采样一次,让曲线的波峰波谷清晰可辨。注意把真实值画成黑色实线、预测值画成红色虚线,图例、轴标签、标题一个都不能少,这张图直接决定评委对你的第一印象。
import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] # 处理中文显示 plt.rcParams['axes.unicode_minus'] = False # 正常显示负号 # 只展示最近200个时间步 plot_range = 200 plt.figure(figsize=(12, 5)) plt.plot(y_true[-plot_range:], label='真实气温', color='black', linewidth=1) plt.plot(y_pred[-plot_range:], label='LSTM预测', color='red', linestyle='--', linewidth=1) plt.xlabel('时间步(小时)') plt.ylabel('气温(°C)') plt.title('LSTM气温预测结果对比') plt.legend() plt.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('prediction_result.png', dpi=150) plt.show()这里有个小知识点要提醒你:plot_range=200不是随便选的,它应该恰好覆盖几个完整的气温周期。小时级数据画 200 个点就是 200 小时,约 8 天,足够展现多个昼夜循环,评阅老师能直观看到预测曲线在白天和夜间的跟随能力。dpi=150是论文插图的最低清晰度要求,如果是要打印出来的文档建议调到 300。
4.3 第二张图:训练与验证 Loss 曲线
这张图的解读价值常被忽略。合格的 loss 曲线应该是:训练 loss 和验证 loss 都单调下降,并且两者最终靠拢;如果验证 loss 在下降一段时间后反弹升高,说明过拟合开始,早停应该触发。画这张图的代码直接复用history对象就行,不需要重新训练。
plt.figure(figsize=(10, 4)) plt.plot(history.history['loss'], label='训练损失', color='#1f77b4') plt.plot(history.history['val_loss'], label='验证损失', color='#ff7f0e') plt.xlabel('Epoch') plt.ylabel('MSE Loss') plt.title('LSTM训练过程损失变化') plt.legend() plt.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('loss_curve.png', dpi=150) plt.show()你训练完拿到这张图后,先别急着放文档。先检查两点:第一,训练损失有没有在最后几轮还在明显下降?如果有,说明epochs=100不够或者早停的patience设小了,训练被提前截断;第二,验证损失曲线是不是比训练损失低?如果是,多半是你的验证集划分出了泄漏——回顾一下你有没有在归一化之前就先切分数据。这个自查习惯能帮你避免答辩现场的尴尬。
4.4 第三张图:预测残差分布与自相关性分析
第三张图是大作业拿高分的“隐藏加分项”,因为大部分同学只会画前两张图。残差就是y_true - y_pred,画成散点图或直方图,可以直观判断模型是否有系统性偏差。比如残差直方图如果明显偏移 0,说明模型整体偏高或偏低;如果残差在某个时段(如中午)集中变大,说明模型对高温时段捕捉能力不足。更进一步,你还可以算残差的自相关系数,残差如果有明显的周期性,说明模型没有把气温的周期信息学完,这时应该增大time_step或增加 LSTM 层数。
residuals = y_true - y_pred plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) plt.hist(residuals, bins=50, edgecolor='black', alpha=0.7, color='#2ca02c') plt.xlabel('预测误差(°C)') plt.ylabel('频次') plt.title('预测残差分布直方图') plt.subplot(1, 2, 2) plt.scatter(y_true, y_pred, s=8, alpha=0.5, color='#d62728') plt.plot([y_true.min(), y_true.max()], [y_true.min(), y_true.max()], 'k--', linewidth=1) plt.xlabel('真实气温(°C)') plt.ylabel('预测气温(°C)') plt.title('真实值 vs 预测值散点图') plt.tight_layout() plt.savefig('residual_analysis.png', dpi=150) plt.show()残差直方图里,bins=50对几千条预测结果足够细分;散点图里的黑色虚线是 y=x 的理想预测线,点越贴近这条线说明预测越准。注意散点图如果发现高温段(比如 30 度以上)的点整体偏离理想线,而且偏向下方,说明模型对极端高温存在低估——这是 LSTM 处理气温数据的一个典型行为,因为高温样本在数据集中占比少,模型学会了“平均化”。
5. 避坑:LSTM 气温预测的五个经典翻车现场
5.1 数据泄漏:归一化在切分之前做,测试集指标虚高
现象:训练完成后测试集 MAE 只有 0.5°C,你觉得这模型神了,结果提交答辩时老师现场换了另一段时间的数据一跑,误差直接翻倍。
原因:你用了scaler.fit_transform(全部数据),让归一化统计量包含了测试集的信息。模型在训练时等于提前“看到”了测试集数值的大致分布,这叫数据泄漏(data leakage)。这在 LSTM 时间序列项目里是最容易被忽略的错误,因为代码不报错,loss 正常下降,指标反而更好看。
解决:严格按时间顺序先df.iloc[:80%]切分,再对训练集fit,然后用训练好的 scaler 分别transform训练集和测试集。逻辑上就是一句话:任何从数据中学到的统计量,都只能从训练集里学。
5.2 预测曲线比真实值滞后一拍,看起来像“复制昨天的温度”
现象:画出来的预测曲线和真实曲线形态几乎一致,但有肉眼可见的时间滞后——真实温度已经升高了,预测值还停留在低点。
原因:这是单步 LSTM 最经典的失败模式,学术上叫“预测均值回归”。模型发现,最优策略是“预测值等于上一个时间步的真实值”——因为气温是一个强自相关过程,上一时刻的温度本来就是对下一时刻最好的估计。模型偷懒了,它学到了这个捷径,没有真正学到昼夜周期的内在规律。
解决:先看你的time_step是不是太小,24 以下的话先加大到 48;其次检查数据中是否有趋势项,如果有,可以先做一阶差分(diff())把趋势去掉再喂给模型,预测完再累加回去;另外,报告指标时除了 MAE 还可以看“预测方向准确率”——即预测值的变化方向和真实变化方向是否一致,这个指标能暴露滞后问题。
5.3 训练 loss 不降或者直接 NaN,模型根本没学起来
现象:model.fit跑了几十个 epoch,loss 纹丝不动;或者 loss 在第几个 epoch 突然变成nan,然后一路飘红。
原因:loss=nan的最常见原因是学习率过大导致梯度爆炸,Adam 优化器虽然自带自适应学习率,但初始学习率设成 0.1 还是会翻车;第二个原因是输入数据里有无穷大或缺失值,interpolate之后如果时间戳有重复或者有空值没有被处理,NaN 就会混进训练数据;第三个原因是MinMaxScaler在某个特征的方差为 0 时(比如冬天整月都是 0 度)会除零。
解决:给model.compile的优化器显式设置学习率,optimizer=tf.keras.optimizers.Adam(learning_rate=0.001);训练前加一行断言np.isnan(X_train).sum(),确保输入没有 NaN;检查 scaler 是否单独保存并且测试集的transform没有重新fit。这三点排查顺序从高频到低频,命中率很高。
5.4 训练集 R² 0.95,测试集 R² 0.6,过拟合到没边
现象:训练集上的评估指标非常好,但测试集一塌糊涂。
原因:LSTM 参数量远大于普通全连接网络,两层 64+32 个单元,加上各种门控权重,参数量轻松破万。如果你的训练数据只有几千条,模型完全有能力“背下来”训练集而不是“理解”规律。
解决:优先级从高到低:第一,把 Dropout 从 0.2 提到 0.3~0.4;第二,把第一层 LSTM 的 units 从 64 降到 32;第三,早停的patience从 10 降到 5,减少过拟合训练轮次;第四,如果数据量实在太小(少于 3000 条),考虑把两层 LSTM 减为一层,效果反而更好。
5.5 多步预测结果发散,预测值一路衰减到 0
现象:用模型做未来 48 小时的多步预测时,预测值越来越平,最终趋近于某个常数,根本看不出昼夜波动。
原因:这是递归多步预测的误差累积问题。每一步预测都有误差,误差进入下一步作为输入,误差被不断放大,模型输出逐渐收敛到训练集中气温的均值。LSTM 没有外部反馈机制来纠正这个漂移。
解决:两个方向。第一个是改用“直接多步预测”,即输出层节点数直接设为pred_step,一次预测出未来 48 小时的完整曲线,不要用上一步的输出作为下一步的输入。第二个是引入外部特征,比如把“一天中的第几个小时”作 a 周期性编码特征加到输入里,模型就能持续感知昼夜相位。作为期末大作业,我建议先做单步预测把主流程跑通,把多步当作扩展实验写进文档的“未来工作”部分,既能展示设计能力又不会把自己卡死在误差累积上。
6. 让结果经得起复现:滑动预测验证与实验记录技巧
验证一个 LSTM 气温预测模型是否真的可靠,只用静态测试集是不够的。因为气温序列是非平稳的,模型可能在夏季数据上表现优秀、秋季数据上表现平平。常用的方法是“滚动预测验证”(rolling forecast validation):每天只预测未来 1 个时间步,然后当天真实值出来后又把它拼进历史窗口,继续预测下一天。这样虽然每一步都是单步预测,但模型始终在使用最新的观测数据,和真实业务里“每天早上预测当天最高气温”完全一致。
滚动验证的代码并不复杂,但完成这个验证后,你要在文档里留下三样东西:第一,滚动预测整个测试集期间的 MAE 和 RMSE;第二,按月份或季度分组的误差表,看看模型在哪类天气下最不稳定;第三,记录模型预测的滞后现象有没有在滚动模式下减轻。这三样东西组合起来,比任何单张图都更能证明你的模型不是“调参调出来的巧合”。
最后的文档整理,有个我自己的血泪建议:不要等到代码全部写完才开始写文档,边写代码边记录每个超参数的实验结论。比如time_step=24试过、time_step=48也试过,两者对比误差差多少,写进文档里就是“参数敏感性分析”;归一化泄漏的坑踩过、修好之后误差下降了多少,写进文档里就是“问题与改进”。这些真实过程比任何编造的理论分析都更有说服力,也更能撑起“文档说明”这个标题。评阅老师想看到的不是完美的模型,而是你能把模型从坏到好的过程讲清楚。希望这篇笔记能帮你把整套流程走通,少踩几个我当年踩过的坑。
本文还有配套的精品资源,点击获取