news 2026/9/16 14:46:54

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

作者头像

张小明

前端开发工程师

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

简介:面向PyTorch初学者和需要完成课程设计或期末作业的学生,这套基于全连接神经网络与Adam优化器的天气温度回归预测项目源码,以历史天气数据预测温度为具体任务,完整演示了从数据准备、网络搭建、模型训练到结果可视化的回归流程,能帮助读者建立基础直觉。压缩包共11个文件,涵盖Python主程序main.py、温度样本数据temps.csv、多张训练结果可视化图(样本数据折线图、实际温度与预测温度对比图、损失函数输出值图)以及.idea工程配置文件,整体约218KB,结构精简,适合逐行阅读。其中png图片可直观显示数据分布与预测效果,方便快速核验。项目已有340人浏览学习,作者打磨至评审分95分以上,经严格调试可稳定运行。这份源码不仅适合作为课程设计报告的有力支撑,也可作为初学者动手复现回归预测模型的起点,代码量适中,便于二次改造与扩展实验。

1. 用Pytorch全连接神经网络做温度回归,为什么这个组合是入门首选

我最近处理一个气温预测需求,数据是一张8000行的气象观测表,列包括时刻、湿度、气压、风速和气温。一开始我动过用LSTM的念头,但仔细看数据后发现,下一小时的气温与过去几小时的观测之间并不存在强序列依赖,这时候全连接神经网络完全能接住这个任务。在PyTorch里挂上一个三层全连接网络,配合Adam优化器,用MSE做损失,训练到第20轮,验证集R²就过了0.93。这个成绩说明两件事:回归预测不一定要上重型时序模型;而全连接网络加Adam这套组合在中小规模数据集上,往往比想象中更能打。适合的人群很明确:会写基础Python、想用PyTorch跑通第一个回归项目的开发者和数据分析师。整个思路照着“构建数据管道、定义网络、选优化器、循环训练”展开,下面每一步都能直接抄。

2. 用Pytorch准备温度回归数据:从安装环境到构建DataLoader

2.1 安装Pytorch与基础环境配置

我拿到温度回归项目源码时,第一步是先把环境隔离好,避免把系统Python搞乱。常见做法是先用conda创建一个干净的虚拟环境,然后安装pytorch以及项目用到的几个库。以下命令在本地跑通了这个流程:

conda create -n temp_forecast python=3.10 conda activate temp_forecast pip install torch pandas numpy scikit-learn matplotlib

第一条命令创建名为temp_forecast的环境,python指定为3.10,不主动装conda内的pytorch,为的就是走pip后面对版本更好控制;第二条激活环境;第三条把训练所需的基础库一次性装齐。需要GPU训练的话,pytorch安装命令不能照抄上面,应该打开pytorch官网,选择自己的操作系统和CUDA版本后复制对应指令。CPU版本也能跑通这个项目,训练时间只是慢一点,所以环境这块不用卡太久。对于刚接触pytorch的读者,我建议先不要追求最新版本,而是用官网给出的稳定版本组合,这也是pytorch环境搭建中最省心的做法。

2.2 构造天气温度数据集与归一化处理

这个项目的数据源通常长成下面这样,我直接用pandas读进来。常见字段如下:

字段含义类型
hour一天内的小时数int
humidity相对湿度float
pressure气压float
wind_speed风速float
temp气温float

其中temp是要预测的标签,其余四列是特征。实际项目中可能还有更多特征,例如云量、辐射,处理逻辑都一样。先把特征和目标值拆开,然后对特征做标准化。拿到数据不要急着喂给模型,先看df.info()和df.isnull().sum(),如果字段缺失,样本量够就直接dropna,不够就按列均值填充,这是数据预处理里最容易犯错的一步。

import pandas as pd from sklearn.preprocessing import StandardScaler df = pd.read_csv("weather.csv") feature_cols = ["hour", "humidity", "pressure", "wind_speed"] target_col = "temp" X = df[feature_cols].values y = df[target_col].values scaler = StandardScaler() X_scaled = scaler.fit_transform(X)

这里要特别注意:StandardScaler只能fit在训练集上,测试集或验证集的预处理要使用同一个scaler做transform,否则会引入数据泄露。我示例里为了展示流程,先对全量数据做了标准化,工程上更稳妥的顺序是先划分训练集和验证集,再用训练集拟合scaler。目标值y没有归一化,因为它是连续温度值,量级在零点几到几十之间,MSE在这种量级下不会出现梯度爆炸。如果目标值特别大,比如预测价格达到十万级,那就要考虑对y也做归一化,预测后再还原。

2.3 用DataLoader划分训练集和验证集

在pytorch中,数据加载建议用Dataset和DataLoader完成,而不是直接把numpy数组塞进模型。把张量包成TensorDataset最省事:

from torch.utils.data import DataLoader, TensorDataset import torch from sklearn.model_selection import train_test_split X_tensor = torch.tensor(X_scaled, dtype=torch.float32) y_tensor = torch.tensor(y, dtype=torch.float32).view(-1, 1) X_train, X_val, y_train, y_val = train_test_split( X_tensor, y_tensor, test_size=0.2, random_state=42) train_ds = TensorDataset(X_train, y_train) val_ds = TensorDataset(X_val, y_val) train_loader = DataLoader(train_ds, batch_size=64, shuffle=True) val_loader = DataLoader(val_ds, batch_size=128, shuffle=False)

参数说明:batch_size=64表示每个批次取64个样本,数值增大训练速度快但梯度更平滑,减小的话收敛不稳定;shuffle=True在每个epoch开始前打乱训练样本,让梯度下降不因数据顺序产生偏置;验证集不做shuffle,评估结果更稳定。view(-1, 1)把y从一维形状变为二维列向量,这是为了让输出形状和模型输出一致。random_state=42是保证每次划分结果相同,后面复现实验时还要继续固定pytorch自身的随机种子。数据量如果只有几百条,batch_size可以降到16或32;8000条用64比较适中。

3. 全连接神经网络结构与Adam优化器:模型设计的4个关键点

3.1 全连接神经网络的层数、宽度与激活函数

全连接神经网络也叫多层感知机,对于表格型气象数据,三层结构通常就够:输入层接特征,中间两个隐藏层,最后输出一个神经元。层数不是越多越好,温度预测这类任务数据量不大,太深的网络很容易过拟合。我的起点配置是64个神经元的隐藏层加32个神经元的隐藏层,激活函数选ReLU。ReLU计算快且能缓解梯度消失;相比sigmoid或tanh,它不会在深层网络里把梯度乘到接近0。

隐藏层宽度取决于特征数量。特征只有4个时,64个神经元已经提供了充足的表达能力;如果特征扩到几十个,可以把第一层扩到128或256。经验法则是第一层隐藏层宽度在输入维度的8到32倍之间,第二层取第一层的一半左右。这并不精准,但有一个有用的方向:先用较宽模型看是否能过拟合训练集,能过拟合再缩窄,不能就加宽。温度回归场景下一般不需要BatchNorm,因为网络层数浅,输入已经标准化,加BatchNorm反而让训练初期的损失曲线更难解释。

3.2 Adam优化器参数:学习率、betas、eps、weight_decay

Adam是最常用的梯度优化器,它兼顾了SGD的简单和动量/RMSProp的自适应能力。在pytorch基础框架里,创建Adam只需要一行代码,但参数含义值得记一下:

参数默认值作用常见调整
lr0.001学习率,控制每步更新幅度1e-3起步,loss不降则降到1e-4
betas(0.9, 0.999)一阶矩和二阶矩的指数衰减系数一般保持默认
eps1e-8防止分母除零的小常数数据量很小时可调为1e-7
weight_decay0参数本身的正则惩罚过拟合时设0.0001

注意,weight_decay和L2正则的实现位置在不同优化器里有差异,但在Adam中通常就是对参数加一个与权重同向的惩罚项,效果是让大权重变小。对温度回归场景,如果发现模型在验证集上抖动,先检查lr,再考虑weight_decay。优化器这一步,很多人只设置lr,其他都用默认值,我在项目源码里看到的也大多如此。比较稳妥的做法是先把lr设为0.001,跑10个epoch看训练loss,如果不降再降低到0.0001,不要一开始就叠加花哨的调度策略。Adam在梯度稀疏、数据噪声中等的情况下表现稳定,这也是它成为全连接网络默认选择的原因。

3.3 定义模型和优化器的PyTorch代码

用pytorch实现全连接网络,常见有两种方式:直接写nn.Sequential,或者定义继承nn.Module的类。为了后续加早停、保存和复现,我倾向于自定义类:

import torch.nn as nn import torch.optim as optim class TempMLP(nn.Module): def __init__(self, in_features): super().__init__() self.net = nn.Sequential( nn.Linear(in_features, 64), nn.ReLU(), nn.Linear(64, 32), nn.ReLU(), nn.Linear(32, 1) ) def forward(self, x): return self.net(x) model = TempMLP(in_features=X_train.shape[1]) optimizer = optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-5) criterion = nn.MSELoss()

代码说明:nn.Linear第一个参数是上一层的输出维度,第二个是当前层神经元数。输入特征数通过X_train.shape[1]传入,这样更换数据集时不需要改模型类。最后的nn.Linear(32, 1)没有接激活函数,因为回归任务输出的是连续温度值,如果用sigmoid会压缩到0~1,反而破坏量纲。optimizer.Adam里传model.parameters(),优化器只更新模型带梯度的参数;weight_decay=1e-5作为初始正则值,可以在过拟合时调大。criterion选MSE,对应温度回归的损失函数,均方误差会对大误差给予更高惩罚,正好符合温度预测不希望出现极端偏差的需求。

4. 训练流程、回归评估与过拟合排查

4.1 训练循环和MSE损失

pytorch训练循环的固定套路是:前向计算、计算loss、清零梯度、反向传播、更新参数。我写一个代表单epoch的函数:

def train_one_epoch(model, loader, criterion, optimizer): model.train() total_loss = 0 for xb, yb in loader: pred = model(xb) loss = criterion(pred, yb) optimizer.zero_grad() loss.backward() optimizer.step() total_loss += loss.item() * xb.size(0) return total_loss / len(loader.dataset)

逻辑说明:model.train()让dropout和BatchNorm进入训练状态,本项目用不到但应当保留。每个批次算出pred后和yb比较得到loss,调用optimizer.zero_grad()清掉上一次梯度,否则pytorch默认会累积梯度,导致参数更新方向错乱。loss.backward()反向传播得到每个参数的梯度,optimizer.step()按照Adam规则更新权重。total_loss按样本数加权求和,最后除以总样本数,返回该epoch的平均loss。后面训练主循环就可以直接调用这个函数,再配合验证函数观察是否过拟合。

4.2 验证与回归指标:R2和MAE

回归预测不能只看loss,因为MSE带平方,数量级不直观。我一般会同时计算R2和MAE:

import numpy as np from sklearn.metrics import r2_score, mean_absolute_error def evaluate(model, loader): model.eval() preds, trues = [], [] with torch.no_grad(): for xb, yb in loader: pred = model(xb) preds.append(pred.numpy()) trues.append(yb.numpy()) preds = np.concatenate(preds) trues = np.concatenate(trues) r2 = r2_score(trues, preds) mae = mean_absolute_error(trues, preds) return r2, mae

这里解释model.eval()与with torch.no_grad()的区别:eval切换模型内部状态,no_grad让张量不记录梯度,节省内存且加速推理。R2的计算是趋势拟合程度的度量,最大值1,越接近1越好;0表示模型只相当于预测均值;负数说明比直接取均值还差。MAE用绝对误差,单位与温度一致,比如1.0表示平均偏差1摄氏度,对天气预测来说,MAE在0.5摄氏度以内已经是可用的水平。实际运行时,如果发现R2为负,首先检查数据预处理是否有泄漏,其次检查学习率是否过大导致loss爆掉。测试集上R2在0.9以上时,基本可以认为模型已经学到了主要温度变化规律。

4.3 拟合问题与调参技巧:早停、学习率调整、正则化

温度回归项目常见的拟合问题可以按下表排查:

现象可能原因应对方法
训练loss下降,验证loss上升过拟合加weight_decay、减少网络宽度、早停
训练和验证loss都高学习率偏大或模型容量不足调低lr、增加一层或加宽
验证loss震荡不稳定学习率偏高或batch_size偏小调低lr、增大batch_size

早停是实现“验证集指标不升就停止”的常见手段。我通常设置patience=10,连续10个epoch验证集R2没有刷新就停止并恢复最佳权重:

best_r2 = -1 patience = 10 wait = 0 for epoch in range(200): train_loss = train_one_epoch(model, train_loader, criterion, optimizer) val_r2, val_mae = evaluate(model, val_loader) print(f"epoch {epoch:03d} loss {train_loss:.4f} r2 {val_r2:.4f} mae {val_mae:.4f}") if val_r2 > best_r2: best_r2 = val_r2 torch.save(model.state_dict(), "best_model.pt") wait = 0 else: wait += 1 if wait >= patience: print(f"early stop at epoch {epoch}") break model.load_state_dict(torch.load("best_model.pt"))

代码里epoch最多200,如果一直没刷新到更高的R2,超过patience就终止训练。注意保存权重用state_dict而不是整个模型,这样文件更小,也避免版本兼容问题。训练结束后无论是否早停,都把best_model.pt加载回来继续评估,避免用最后一轮的过拟合权重。学习率调整可以在后面再加,最基础的是手动的lr * 0.1,或者用torch.optim.lr_scheduler.ReduceLROnPlateau,但在数据量小、早停生效快的情况下,这步不是必需品。

5. 让温度回归源码可复现:模型保存、可视化与随机种子

5.1 保存和加载模型

源代码包组织比较规范时,一般会把训练、评估、预测拆开,训练脚本里保存的不应只有模型权重,还要把特征列和缩放器一起保存,否则预测新数据时无法还原预处理流程。

torch.save({ "model_state_dict": model.state_dict(), "scaler": scaler, "feature_cols": feature_cols }, "temperature_model.pt")

加载时也要同时恢复这三项:

checkpoint = torch.load("temperature_model.pt") model.load_state_dict(checkpoint["model_state_dict"]) model.eval() scaler = checkpoint["scaler"] feature_cols = checkpoint["feature_cols"]

注意:加载模型后默认处于训练模式,需要手动model.eval()切到推理模式。如果还有优化器状态,比如想恢复训练接着跑,可以把optimizer.state_dict也存进去,但在温度回归项目里通常没必要。保存scaler对象是很多初学者最容易漏掉的,预测新的一行数据时如果不做同样的标准化,模型输出的温度会偏移一个量级。

5.2 绘制真实值与预测值曲线

回归预测的质量需要直观检验。选测试集前100条样本画折线图,能看出模型是否跟得上趋势:

import matplotlib.pyplot as plt plt.figure(figsize=(10, 4)) plt.plot(trues[:100], label="true") plt.plot(preds[:100], label="pred") plt.legend() plt.show()

如果两条线贴合度高,说明模型抓住了主要趋势;如果pred明显滞后于true,往往是因为训练不充分或特征不足。除了看曲线,还可以用plt.hist(trues - preds, bins=50)画残差分布,残差应围绕0对称,如果偏向一侧说明存在系统偏差。实际部署时还会用当前小时的特征拼接下一时刻的真实值做滚动预测,但本项目的目标是训练出能泛化的回归模型,所以验证集上的曲线和残差已经足够说明问题。

5.3 固定随机种子复现实验

最后一步也是源码工程里容易被忽略的一步:固定随机种子。PyTorch在参数初始化、DataLoader的shuffle时都会使用随机数,不固定的话,重新训练出来的结果可能上下浮动。在项目入口处加上这个函数:

import random import numpy as np import torch def set_seed(seed=42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) set_seed(42)

这段代码覆盖了Python内置随机库、NumPy和PyTorch的CPU/GPU随机流。注意要在创建DataLoader之前调用,因为shuffle的随机状态来自全局RandomSampler。固定随机种子后,模型每次训练应该能在小数点后几位保持一致,这样其他人运行项目源码时,也能复现出与文档一致的温度预测结果。这也是我拿到“基于Pytorch全连接神经网络优化器Adam实现天气温度的回归预测项目源码”这类压缩包时,最先检查的三个位置之一:环境配置、数据划分、随机种子是否齐备。

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

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

Unity编辑器触摸模拟器:用鼠标+Ctrl调试双指手势的完整方案

做手游的朋友应该都有过这种经历:功能在手机上跑得好好的,一回到编辑器就什么都测不了。尤其是我最近在做的这个项目,捏合缩放地图、双指旋转相机、滑动操作列表,这些交互全部写在了旧式Input系统的触摸接口上。代码里清一色的Inp…

作者头像 李华
网站建设 2026/9/16 14:46:01

STM32C542 PWM实战:频率精度、占空比平滑与引脚复用避坑指南

1. 项目概述:为什么STM32C542的PWM调试总让人卡在“调不动”这一步?你手头那块刚焊好的STM32C542开发板,LED灯亮了,串口能打印,但一到PWM输出就犯难——示波器上波形要么根本不出,要么频率死在1kHz动不了&a…

作者头像 李华
网站建设 2026/9/16 14:45:40

PyInstaller打包exe一键反编译:从pyc提取到源码还原全解析

简介:面向Python开发者和逆向分析人员,这份一键反编译工具能直接处理PyInstaller打包生成的exe文件,将其还原为可阅读的py源码,适合需要找回丢失源码、分析他人程序逻辑或学习exe反编译流程的人群。工具以两个核心Python脚本组成完…

作者头像 李华
网站建设 2026/9/16 14:44:09

SSM+Vue绿色农产品推广网站开发实战:从数据建模到部署验收

简介:这是一份面向Java开发者、毕业设计学生及绿色农业信息化研究者的完整项目资源,基于SSM(SpringSpringMVCMyBatis)框架构建后端,结合Vue.js实现前端交互,覆盖绿色农产品推广网站的需求分析、系统设计、编…

作者头像 李华
网站建设 2026/9/16 14:43:37

电机正反转控制的5种实战方法与选型指南

1. 电机正反转不是“按个开关”那么简单你拆过电风扇、修过洗衣机、调过3D打印机的步进电机,或者只是在Arduino入门套件里让小车往前走两步又倒回来——这些场景背后,全绕不开一个基础但极易被轻视的问题:怎么让电机可靠、安全、可控地正转和…

作者头像 李华