news 2026/9/12 3:08:44

基于PyTorch全连接神经网络的温度回归预测实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于PyTorch全连接神经网络的温度回归预测实战

简介:面向天气温度回归预测这一典型连续值预测场景,该项目基于Pytorch构建全连接神经网络,采用Adam优化器完成模型训练与温度拟合。代码源自个人大作业,评审分达95分以上,经过严格调试可直接运行,适合有一定Python基础、希望入门Pytorch回归任务的开发者学习借鉴。资源包共11个文件,体积仅218KB,包含Python主程序、CSV格式温度样本数据、3张可视化结果图(样本温度折线图、损失函数输出图、实际温度与预测温度对比图),以及gitignore、license等工程配置文件,xml与iml为IDE项目描述,整体结构清晰、便于快速定位核心代码。已有340人学习下载,通过学习可系统了解从数据加载、网络层设计、损失计算到Adam优化配置的完整实现,并能借助实际温度与预测温度对比图、损失变化图直观评估模型效果,对掌握回归预测项目开发流程和PyTorch实践均有帮助。

1. 温度回归预测与全连接网络的选型逻辑

在机房温度监控、冷库温控这类场景里,温度预测并不一定要上时序模型。把过去24小时的温度记录展开成滞后特征,一个三层的全连接神经网络配上Adam优化器,通常就能解释测试集上八到九成的温度方差,训练时间按分钟计。这个项目做的就是这件事:基于PyTorch全连接神经网络与Adam优化器,对temps.csv中的温度序列做回归预测,main.py单文件覆盖数据加载、样本构造、模型训练、指标评估和核心图表的输出。它不适合拿来炫技,但很适合作为回归预测的第一个可复现基线——刚学完PyTorch基础、需要一份能跑通的课程大作业源码的开发者,用这份代码起步是有效的参考。

2. 数据探索与样本构造:从temps.csv到可训练的滞后特征

2.1 解压后先看哪些文件

拿到压缩包后,先不要急着运行main.py。目录里的.ideamisc.xmlmodules.xmlinspectionProfiles是PyCharm的工程配置,换机器直接删除也不影响运行,天气预测.iml同理。真正有价值的是以下三类:

  • temps.csv:原始温度时间序列数据
  • main.py:加载数据、构造样本、定义模型、训练、评估和绘图的完整流程
  • 三张PNG图:样本数据温度折线图.png损失函数输出值.png实际温度vs预测温度 折线图.png,这些图帮助你快速验证训练结果是否正常

.gitignore里一般会忽略__pycache__.idea和虚拟环境目录,如果你要在自己的仓库里维护这份代码,建议保留。

2.2 读取温度数据与缺失值检查

温度回归的第一步永远是确认数据形状和缺失情况。数据通常是两列:时间戳date和温度值temp,也可能只有一列温度数值序列。用pandas读取后打印头几行和统计信息:

import pandas as pd df = pd.read_csv("temps.csv", parse_dates=["date"]) print(df.head()) print(df.info()) print(df.isnull().sum())

parse_dates会把时间列解析成datetime类型,便于后续画折线图和对齐索引。检查缺失值这一步容易被跳过,但温度传感器上报中断产生的空值,会让滞后特征构造阶段的dropna()静默删除过多样本,导致训练样本数骤减。这里需要明确统计量纲:df.describe()里的meanstd决定后续标准化参数,务必记录,因为预测结果还原真实温度时还要用到。

2.3 把温度序列转为有监督回归样本

温度序列是典型的自相关数据——今天的温度大概率接近昨天同一时刻。常见做法是用滑动窗口构造滞后特征,将无监督的时间序列转换为(特征, 标签)的回归样本。

def series_to_supervised(data, win_size=24): cols = [] for i in range(win_size, 0, -1): cols.append(data.shift(i)) df_s = pd.concat(cols, axis=1) df_s.columns = [f"lag_{i}" for i in range(win_size, 0, -1)] df_s["target"] = data return df_s.dropna()

shift(i)把温度序列整体下移i个位置,lag_24是24小时前的温度,target是当前时刻待预测的真实温度。dropna()去掉头部窗口内没有完整历史记录的样本。这一设计抓住了温度回归与普通回归的差异:样本间存在时间依赖,不能像MNIST那样随机打乱。

窗口大小的选择直接影响模型容量。温度数据按小时采集时,24小时覆盖一个完整昼夜周期,模型容易学到"当前温度与昨天同时刻温度强相关"这类规律;如果数据粒度是分钟级,窗口可能要扩到48甚至72。

数据粒度建议窗口大小理由
小时级24覆盖一个昼夜周期
半小时级48与24小时周期对应
分钟级60~120短时惯性为主,窗口过大带来噪声

2.4 标准化与时间顺序切分

温度值量纲对全连接网络的收敛速度影响很大。输入输出都做标准化,可以让Adam在更平坦的误差曲面上搜索。这里的关键区别是所有标准化参数只从训练集拟合:

from sklearn.preprocessing import StandardScaler scaler = StandardScaler() train_size = int(len(df_s) * 0.8) train_df, test_df = df_s.iloc[:train_size], df_s.iloc[train_size:] scaler.fit(train_df) train_scaled = scaler.transform(train_df) test_scaled = scaler.transform(test_df)

不能先对整个数据集fit再切分,否则测试集的均值和方差信息泄漏到训练过程,验证指标会虚高。另一个常见误用是train_test_split(shuffle=True),在温度预测里这样随机打散样本,会让模型在训练时见到"未来"的数据。时间序列任务要按时间顺序切分:前80%做训练,后20%做验证。切分后,把特征列和标签列拆成numpy数组,再转换成PyTorch张量并封装为TensorDataset

3. 模型定义与Adam优化器:全连接网络的层数、损失函数与参数配置

3.1 为什么全连接网络对滞后特征有效

温度序列的短程自相关性很强,lag_1lag_24这24个特征已经携带了目标值的大部分信息。全连接网络本质上是在学习这些滞后特征到目标温度的非线性映射,比如夜间降温曲线的凹性、白天升温的饱和趋势,这些都能被ReLU激活函数的分段线性组合逼近。相比LSTM或Transformer,全连接网络没有序列建模的归纳偏置,但它收敛快、训练稳定,在小样本温度回归任务上往往是性价比最高的选择。

3.2 用nn.Module定义一个三层全连接网络

import torch.nn as nn class TemperatureMLP(nn.Module): def __init__(self, input_dim, hidden_dims=(128, 64), dropout=0.1): super().__init__() layers = [] last_dim = input_dim for h in hidden_dims: layers.append(nn.Linear(last_dim, h)) layers.append(nn.ReLU()) layers.append(nn.Dropout(dropout)) last_dim = h layers.append(nn.Linear(last_dim, 1)) self.net = nn.Sequential(*layers) def forward(self, x): return self.net(x)

这里用nn.Sequential把线性层、ReLU和Dropout串联起来,结构清晰且便于调试。输入维度input_dim等于窗口大小win_size,因为每个样本只有一列温度特征。中间层用128和64两个隐藏维度,对几千条样本规模的数据集不会过参数化。Dropout(0.1)只在训练时生效,model.eval()后自动关闭,防止验证阶段误伤预测结果。

输入维度输出维度激活/操作
Linear24128ReLU + Dropout(0.1)
Linear12864ReLU + Dropout(0.1)
Linear641无激活

3.3 损失函数与回归指标的取舍

回归任务的默认损失函数是nn.MSELoss(),即均方误差。它和F.mse_loss等价,封装了均值和张量维度处理。预测值与真实温度偏差的平方被放大,所以异常温度毛刺会主导梯度方向——如果数据里有传感器尖峰噪声,建议先清洗再训练。评估阶段我习惯额外计算RMSE和R²,它们相对损失值更可读,具体实现放在第5章。

3.4 Adam优化器的参数配置与固定随机种子

import torch model = TemperatureMLP(input_dim=win_size) optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) criterion = nn.MSELoss() torch.manual_seed(42)

Adam的核心是每个参数维护一阶动量m_t和二阶动量v_t,用指数移动平均估计梯度的一阶矩和二阶矩,并做偏差校正。相比SGD,它对学习率不那么敏感,尤其适合全连接网络这种参数量不大但梯度尺度不一的场景。lr=1e-3是Adam的默认配置,在温度回归这类平滑目标上收敛稳定。

Adam的betas参数需要了解但不必经常改:betas=(0.9, 0.999)表示一阶动量衰减为0.9、二阶动量衰减为0.999。eps=1e-8是防止除零的数值稳定项。weight_decay可选,设为1e-5能起到L2正则化作用,对抗小样本过拟合,我通常会在验证loss出现轻微上升时开启。随机种子固定在main.py开头,保证每次运行得到相同的初始化权重和数据划分,排查问题时才能对比不同版本的结果。

4. 训练循环与超参数调优:loss曲线、早停与常见收敛问题

4.1 训练循环的完整实现

epochs = 200 train_losses, val_losses = [], [] for epoch in range(epochs): model.train() total_loss = 0.0 for xb, yb in train_loader: optimizer.zero_grad() pred = model(xb) loss = criterion(pred.squeeze(), yb) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) optimizer.step() total_loss += loss.item() * xb.size(0) if (epoch + 1) % 10 == 0: model.eval() val_pred = model(xv_tensor) val_loss = criterion(val_pred.squeeze(), yv_tensor).item() train_losses.append(total_loss / len(train_loader.dataset)) val_losses.append(val_loss) print(f"Epoch {epoch+1}/{epochs} train_loss: {train_losses[-1]:.6f} val_loss: {val_loss:.6f}")

optimizer.zero_grad()必须在loss.backward()之前调用,否则每个batch的梯度会累加,等于用大了一倍的batch size训练,表现为loss周期性震荡。clip_grad_norm_把整个参数集的梯度L2范数截断到1.0,防止个别异常样本造成梯度爆炸;如果你的数据已经标准化,这个设置一般不会被触发,但留着它能在温度尖峰数据上避免训练发散。

4.2 验证流程与loss曲线输出

验证阶段严禁打开梯度计算。model.eval()切换BatchNorm和Dropout的行为,torch.no_grad()则彻底停掉自动求导,省显存、提速。验证用同一批固定数据xv_tensor,保证每个epoch对比的是同一分布,这样loss曲线才有可比性。

每10个epoch记录一次val_loss,用matplotlib把两条曲线画到损失函数输出值.png中:

import matplotlib.pyplot as plt plt.plot(range(10, epochs + 1, 10), train_losses, label="train_loss") plt.plot(range(10, epochs + 1, 10), val_losses, label="val_loss") plt.xlabel("epoch") plt.ylabel("MSE") plt.legend() plt.savefig("损失函数输出值.png", dpi=150)

ReduceLROnPlateau可以在验证loss连续多次不下降时自动降低学习率,这对Adam后期收敛很有帮助——Adam前期收敛快,动态调整学习率比固定lr=1e-3更稳。

4.3 超参数推荐与调优方向

超参数推荐值过大时表现过小时表现
learning_rate1e-3训练不稳定,loss震荡收敛慢,200轮不够用
batch_size32收敛噪声大,占用显存多收敛太慢,每轮迭代次数少
hidden_dim(128, 64)过拟合,训练时间变长表达力不足,欠拟合
dropout0.1欠拟合,信息丢失过拟合,验证loss变高
epochs200后期过拟合未收敛,loss仍下降

温度回归数据量通常不大,batch_size设为32即可。如果你发现训练loss下降但val_loss升高,说明模型开始记忆噪声,优先增大dropout到0.2或减少隐藏层宽度,而不是降低epochs。反过来如果训练loss始终高位,先检查自己是否忘了做标准化,再确认lr是否过小。

4.4 训练阶段三个需要停下来的信号

第一个信号是验证loss连续20轮不降反升,这是过拟合的明确警报,此时训练loss往往仍在缓慢下降——模型在背训练样本。第二个信号是前10轮训练loss完全不动,多半是学习率过小或数据量纲问题。第三个信号是loss值出现nan,属于学习率过大导致的梯度爆炸,把lr从1e-3降到1e-4重跑即可。早停可以直接写在训练循环里:

best_val_loss = float("inf") patience = 20 wait = 0 # 每个epoch结束后更新早停逻辑 if val_loss < best_val_loss: best_val_loss = val_loss wait = 0 torch.save(model.state_dict(), "best_model.pt") else: wait += 1 if wait >= patience: break

best_model.pt保存的是历史最优权重,而不是最后一个epoch的模型。这在温度回归里很重要,因为Adam后期会在最优解附近小幅震荡,最优权重往往出现在第60到100轮之间,早停配合best_model.pt不让前面学到的规律被后期震荡覆盖。

5. 预测结果验证:逆标准化、R²/RMSE计算与误差修正技巧

5.1 推理阶段输出与实际温度对比图

评估预测效果时,要把标准化后的预测值还原到真实温度量纲,否则计算出的指标无法直观反映"差几度"。加载保存的最优权重,关闭dropout后做推理:

model.load_state_dict(torch.load("best_model.pt")) model.eval() with torch.no_grad(): y_pred_scaled = model(xv_tensor).squeeze().numpy() y_pred = scaler.inverse_transform(y_pred_scaled.reshape(-1, 1)).ravel() y_true = scaler.inverse_transform(yv_tensor.numpy().reshape(-1, 1)).ravel()

inverse_transform用训练集拟合的均值和标准差还原温度。注意这里必须用同一个scaler对象,不能用重新fit的scaler,否则输出量纲会偏离真实温度范围。绘图时x轴用测试集的时间索引,画两条折线,一条是实际温度,一条是预测温度,重叠程度越高说明回归效果越好。

5.2 回归指标的计算口径

温度预测的误差单位是摄氏度,这是RMSE比MSE更直观的原因。R²表示模型解释了测试集温度方差的百分比,接近1说明预测和真实值高度同步:

from sklearn.metrics import mean_squared_error, r2_score rmse = mean_squared_error(y_true, y_pred, squared=False) r2 = r2_score(y_true, y_pred) print(f"RMSE: {rmse:.2f}°C R²: {r2:.4f}")

注意RMSE和训练loss的数值不能直接对比——训练loss来自标准化空间,RMSE来自温度原始空间,两者量纲不同。如果你发现RMSE在3°C以上,先检查预测曲线是否整体滞后于真实曲线。

5.3 温度峰谷位置滞后误差的修正技巧

全连接网络基于滞后特征做回归,它对剧烈温度变化的响应天然偏慢:真实温度在清晨快速上升时,预测值往往还在前几个时刻的低位区间。一个实用的修正技巧是计算残差的时间分布:

import numpy as np residual = y_true - y_pred mean_residual = np.mean(residual) print(f"平均偏差: {mean_residual:.2f}°C")

如果mean_residual明显不为0,说明模型存在系统性偏差。修正方法是给预测值加一个偏置项y_pred_corrected = y_pred + mean_residual,这个操作的原理是:时序回归在多个时段的滞后误差有正有负,但温升/温降不对称时,整体偏差会向一个方向累积。通常修正后RMSE能下降0.2到0.5°C。对于峰值滞后问题,可以额外统计残差绝对值最大的时段,如果集中在凌晨和正午,说明模型对昼夜突变捕捉不足,这时增加lag_24特征的权重或把窗口从24扩到48是更直接的解决路径。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 3:06:04

Godot 4.3.4安装汉化与首个2D场景实操指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 3:05:45

Flutter跨平台开发实战:从小众景点App到鸿蒙适配全流程复盘

去年底我接了个挺有意思的项目&#xff1a;做一款专门挖掘城市周边小众景点的 App。需求听起来简单&#xff0c;但有个关键约束——除了 Android 和 iOS&#xff0c;客户还要求适配鸿蒙系统。团队里有人提议三个平台各写一套原生代码&#xff0c;我算了一笔账&#xff1a;三套代…

作者头像 李华
网站建设 2026/9/12 3:03:16

中文车牌识别实战:从YOLO检测到LPRNet识别与系统部署

简介&#xff1a;面向计算机相关专业毕业设计及深度学习初学者的中文车牌识别与管理系统项目包。基于深度学习实现车牌检测、字符分割与识别&#xff0c;并配有简洁美观的图形管理界面。压缩包共16个文件&#xff0c;包含9个Python脚本&#xff08;模型训练、核心识别、界面及视…

作者头像 李华