恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

天气识别项目实战:PyTorch迁移学习与数据增强全流程解析

  • 首页
  • 资讯中心
  • /
  • 天气识别项目实战:PyTorch迁移学习与数据增强全流程解析

相关资讯

基于YOLO的交通信号灯识别系统:从数据标注到边缘部署实战 2026/10/18 4:31:21
中老年驼奶粉年销过亿操盘复盘:不靠低价,靠信任与渠道 2026/10/18 4:31:21
自然梯度法:策略优化中的KL约束与单调提升保障 2026/10/18 4:26:21

最新资讯

动态域名解析DDNS实战:从原理到自建脚本的完整指南
GESP 2025年3月 真题题解 | 等差矩阵(循环结构)
GESP 2023年9月 买文具 题解|这题分支结构的坑一定要避开
流量审判已死,证据时代开始
Hive分区表设计与订单数据更新全流程实战指南
context-mode设计思路:告别if-else,用上下文模式管理多场景逻辑

今日推荐

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本周热门

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

天气识别项目实战:PyTorch迁移学习与数据增强全流程解析

发布时间:2026/10/18 4:31:21
天气识别项目实战:PyTorch迁移学习与数据增强全流程解析 第T3周的天气识别项目我总算把它完整跑通了。坦白说天气识别听起来就是“给照片分类”这种最基础的事情但真正动手之后才发现它比教科书上那些猫狗分类要狡猾得多。同样是天空的图片多云和雾天之间的边界非常模糊雨天的玻璃水渍又会让特征分布变得很乱。我以为一天能搞定结果调数据清洗和图增强就花了两个晚上。这篇东西就是想把我在这个项目里从数据、模型到调参的整个思路写下来给正在做图像分类入门、尤其是准备做迁移学习实战的朋友做个参考。如果你已经会写一点Python碰过PyTorch的基本API那这篇内容应该刚好能接上你的进度。我做这个项目的时候最直观的感受是天气识别真正的难点不在模型结构而在怎么把“天气”这个概念用数据表达清楚。下面的内容我会按照项目推进的顺序来讲从任务拆解、数据准备、模型选型到训练脚本、评估指标和调参心得全部都是实际操作过的记录和代码片段。你完全可以照着这份流程把自己的数据集换进去。1. 天气识别别被名字骗了这个分类问题比想象中复杂1.1 任务到底是什么天气识别本质上是一个多分类图像任务输入是一张照片输出是它属于哪一类天气。我做的时候定义了六个类别晴天、多云、阴天、雨天、雪天、雾天。这个分类粒度在公开的天气图像项目里很常见也和日常拍照场景对得上。但如果你真去翻数据集会发现很多图的标签本身就有争议比如一张远处有山、近处是薄云的图片有人标多云有人标雾天。这种标签噪声在刚开始做图像分类的时候往往会被忽略但它对模型上限的影响非常直接。图片分类任务在深度学习中属于有监督学习。我们给模型看大量打好标签的图片让它自己学习这些图片的视觉模式。这个过程有点像教一个小孩认水果你说“这是苹果”他记住了红色和圆形你说“这是梨”他记住了黄色和椭圆。但天气比水果复杂因为“下雨”不是单一物体而是天空中、地面上、甚至玻璃上的综合痕迹。模型要把这些多重信息压缩成一个类别判断所以前期数据质量决定了后面所有环节的收益。1.2 为什么要动手做一次如果你正在按课程顺序学深度学习这个项目很可能会出现在前三周。位置安排是有道理的第T3周通常刚讲完CNN的基本概念还没到目标检测或者语义分割那种复杂度。天气识别刚好是一个“能读懂、能改、能快速验证”的图片分类项目前后端逻辑都简单适合把数据加载、模型微调、训练循环、评估指标这一整套流程串起来。而且天气识别应用场景很实际。自动驾驶需要判断天气以调节传感器策略户外活动推荐系统需要知道实时天气老照片整理工具也可以按天气给照片分组。做完这个项目你可以把同一个框架直接套到其他分类任务上只需要换数据和最后一层的类别数。也就是说你做的不只是一个天气分类器而是一套可以反复使用的图像分类训练流程。这点我觉得比模型准确率本身更重要。2. 数据准备先把手里的图理清楚再动手训练2.1 数据集整理与目录划分我的数据来自一个开源天气图像合集但这篇文章不依赖具体来源你完全可以用自己收集的照片。关键不是数据集有多大而是目录结构和标签必须工整。PyTorch里有个非常省心的工具torchvision.datasets.ImageFolder它会自动按照文件夹名字读标签结构长这样data/ ├── train/ │ ├── sunny/ │ ├── cloudy/ │ ├── overcast/ │ ├── rainy/ │ ├── snowy/ │ └── foggy/ ├── val/ │ └── ... └── test/ └── ...我在做之前把原始图片按类别分别放进了train、val、test三个目录里。第一次整理时我偷懒只分了train和val后来测试模型泛化能力时才发现没有真正的测试集只能拿验证集顶替导致最终报告里的准确率虚高了不少。这是新手特别容易踩的坑项目再小也要留出独立的测试集数量可以少但绝对不能没有。图片数量上我的每个类别训练图像大概是800到1200张不等整体有一万多张。这个规模说大不大说小不小。如果类别之间分布差异很大比如晴天有1500张、雪天只有200张那就需要处理类别不平衡问题。我这次的数据还算均衡晴天和雨天略多但雪天和雾天偏少后面会提到我用数据增强和类别权重去弥补。2.2 数据增强与预处理数据增强是天气识别里最需要动脑的一环。常见的翻转、裁剪、颜色抖动都能做但要考虑天气照片的特性。我一开始使用了随机旋转后来发现出问题了有些天空照片旋转90度之后地面上的影子方向变得反物理学模型反而学到了错误规律。所以后来我把旋转角度限制在10度以内只做小角度扰动同时保留了水平翻转因为一张照片左右翻转并不会改变天气类别。我最终用的训练预处理流程是缩放到224x224、随机水平翻转、小角度旋转、颜色亮度/对比度/饱和度扰动、转成Tensor、归一化。下面这段代码可以直接跑from torchvision import transforms transform_train transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(), transforms.RandomRotation(10), transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])关于归一化参数我在这里多解释一句。很多教程直接告诉你用上面这组数字你也许想知道为什么。这组mean和std来自一个大规模通用图像数据集是所有图片像素通道的统计值。当我们要使用在“那个数据集”上预训练好的模型权重时最好沿用同一套归一化参数否则模型看到的输入分布和它训练时不一致特征提取会变差。就算你的图像整体偏暗或者偏蓝也不要乱改这组参数可以先跑一版再根据效果决定。验证集和测试集不要做随机增强只需要Resize、ToTensor和Normalize。这一点非常容易忽略我见过有人把RandomHorizontalFlip也放在验证集里结果验证集的loss一直震荡其实那不是模型的错是数据增强造成了不确定性。3. 模型与训练框架选型3.1 从零训练还是迁移学习到了选模型这一步你会面临两条路一是自己搭一个小型CNN从零训练二是加载在大规模通用图像数据集上预训练好的模型再微调。在课程第T3周这个阶段我强烈建议选择迁移学习。原因很简单你手里的天气数据通常只有几千到一两万张这点量从零训练一个能真正理解“雾天”这种复杂纹理的模型很容易陷入欠拟合或者过拟合的尴尬。迁移学习的思路则不同预训练模型已经学会了很多通用视觉特征比如边缘、纹理、形状你不需要从零教它认识“纹理是什么”只需要让它在你的天气数据上做最后的微调。打个生活化的比方从零训练一个模型就像让一个从没见过相机的人直接拍摄专业风光片他连光圈快门都不懂更别说构图。迁移学习相当于你请来一个已经拍过很多题材的摄影师他来拍普通风景时只需要熟悉一下现场光线就能出片。模型带着已经成熟的底层视觉能力来到你的任务里只需要重新学习你特定的类别规律。我这次选择的是ResNet-18它在分类任务里非常经典网络深度适中在单张GPU上训练速度也快。对于更便宜的环境也可以挑MobileNetV3体积小、速度快几乎可以在CPU上完成推理。如果追求更高精度ResNet-50或者EfficientNet也行但训练时间会明显增加。做这个项目是用“跑通”为目的不太需要无脑堆大模型。3.2 搭建分类头与损失函数加载预训练模型之后最关键的一步是替换最后一层全连接输出。ResNet-18最后一层是model.fc它的输入维度是一个固定的特征数输出是1000个类别。我们需要把它换成自己的类别数量也就是6。下面是我修改分类头的代码import torch.nn as nn import torchvision.models as models model models.resnet18(weightsmodels.ResNet18_Weights.DEFAULT) num_ftrs model.fc.in_features model.fc nn.Linear(num_ftrs, 6)需要注意的是PyTorch新版本里weights参数的写法更规范它会明确告诉你加载的是哪个预训练权重。如果你用的是旧版本代码可能会见到pretrainedTrue这种写法建议统一成新版语法后续维护更省心。损失函数在单标签多分类任务里基本都是CrossEntropyLoss。你不需要自己写Softmax和负对数似然PyTorch的CrossEntropyLoss已经把这两步合并了。这意味着你不需要在模型最后一层再手动加Softmax只要把原始logits传进去就好。这一点是新手容易疑惑的地方我一开始也搞错了以为要先用Softmax把输出变成概率再算loss其实重复做一次反而会干扰训练。4. 训练代码这样写才不容易翻车4.1 数据加载与批次设置训练之前要准备DataLoader。ImageFolder会把图片读取成(PIL.Image, label)的格式配合之前定义好的transform每个样本在加载时都会经过预处理。我用的批次大小是64因为ResNet-18在224x224输入下64张图片组合在一起显存占用大概在7GB到9GB之间如果你的显卡只有6GB显存可以把batch size降到32。from torch.utils.data import DataLoader from torchvision import datasets train_dataset datasets.ImageFolder(rootdata/train, transformtransform_train) val_dataset datasets.ImageFolder(rootdata/val, transformtransform_val) train_loader DataLoader(train_dataset, batch_size64, shuffleTrue, num_workers4, pin_memoryTrue) val_loader DataLoader(val_dataset, batch_size64, shuffleFalse, num_workers4, pin_memoryTrue)shuffleTrue是必须的尤其是在训练开始之前你要确保每批次的数据不是按文件夹顺序连续取出来的。如果忘了shuffle模型可能前几十个batch都在看晴天后几十个batch全变成雨天每个batch之间分布差异很大loss会跳得让人怀疑人生。验证集不需要shuffle因为验证过程只关心固定顺序下的统计结果。num_workers这个参数很多人会忽略。它控制加载图片用的进程数默认是0意味着在主线程里读图非常慢。我通常设成4或8但如果设太高而机器CPU核心不够反而会拖慢训练。这里建议先设成4如果并行出错再逐步调低。4.2 训练循环与验证逻辑训练循环看起来很机械但有几个细节决定了你最后能不能拿到一个能用的模型。我把核心训练脚本简化到了最低程度方便你理解主流程device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr0.001) scheduler torch.optim.lr_scheduler.StepLR(optimizer, step_size5, gamma0.1) best_acc 0.0 epochs 15 for epoch in range(epochs): model.train() train_loss 0.0 for inputs, labels in train_loader: inputs, labels inputs.to(device), labels.to(device) optimizer.zero_grad() outputs model(inputs) loss criterion(outputs, labels) loss.backward() optimizer.step() train_loss loss.item() * inputs.size(0) model.eval() val_correct 0 val_total 0 with torch.no_grad(): for inputs, labels in val_loader: inputs, labels inputs.to(device), labels.to(device) outputs model(inputs) _, preds torch.max(outputs, 1) val_correct (preds labels).sum().item() val_total labels.size(0) val_acc val_correct / val_total print(fEpoch {epoch1}/{epochs} | train_loss: {train_loss/len(train_dataset):.4f} | val_acc: {val_acc:.4f}) if val_acc best_acc: best_acc val_acc torch.save(model.state_dict(), best_model.pth) scheduler.step()这里有个很重要的习惯每个epoch结束都要切到model.eval()模式。训练时Dropout和BatchNorm的行为跟推理时不一样如果你一直在train模式下做验证验证准确率会偏低而且不稳定。PyTorch里的model.train()和model.eval()切换不会修改模型参数但能改变层的行为我见过不少人在这里翻车。优化器我用了Adam初始学习率0.001。Adam的好处是自适应学习率参数更新相对稳定对初学者很友好。如果你用SGD可能需要更精细的学习率和momentum设置。学习率调度我这里用了StepLR每5个epoch把学习率乘以0.1这样可以让模型在后期用小步长做精细调整避免loss在最低点附近来回震荡。如果你发现验证准确率一直上不去可以先检查学习率是不是过大或者过小。保存模型时我选择保存state_dict而不是整个模型对象。这样文件更小加载的时候也方便你把结构重新定义成你需要的类别数。如果保存整个模型torch.save(model, ...)后续加载时经常会碰上一堆反序列化问题没必要给自己找麻烦。5. 调参与结果评估准确率不是唯一指标5.1 混淆矩阵与逐类指标训练结束后准确率如果停在85%左右你可能会觉得还行。但只盯着整体准确率很容易掩盖问题。我这次最终的验证准确率大约91%听起来不差但看混淆矩阵才发现雾天的召回率特别低大量雾天图片被模型分到了多云。这其实不奇怪雾天和多云在视觉上都是大范围低对比度天空区分难度本来就大。如果你只汇报一个Acc数字这个缺陷就完全看不出来。所以评估阶段我强烈建议打印混淆矩阵。以下代码基于验证集运行可以把每个类别的预测情况可视化出来from sklearn.metrics import confusion_matrix import numpy as np all_preds [] all_labels [] model.eval() with torch.no_grad(): for inputs, labels in val_loader: inputs inputs.to(device) outputs model(inputs) _, preds torch.max(outputs, 1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(labels.numpy()) cm confusion_matrix(all_labels, all_preds) print(cm)拿到混淆矩阵后你可以计算每一类的精确率、召回率和F1。精准率代表预测为该类别的样本中有多少是真的召回率代表真正为该类别的样本中有多少被成功找出来。天气识别对这两者的偏好取决于场景如果做灾害预警你宁可把晴天误判成雨天也不希望漏掉雨天如果做相册分类召回率和精准率就要更均衡。我这次雾天类别识别得比较差于是专门做了一次针对性增广把雾天图片额外做了低饱和度处理和轻微模糊效果提升很明显。5.2 让模型再听话一点的调参技巧模型表现不够好最先想到的可能是加大训练轮数。但在天气识别这种任务里训练15到20个epoch之后提升会非常有限。更有效的做法往往有三个调整损失函数权重、针对困难类别做额外数据增强、微调分类头学习率。类别不平衡的情况下可以在CrossEntropyLoss里传一个weight参数给样本量少的类别更高的权重。我在雪天图片只有晴天一半的情况下将雪天权重设为1.5到2.0这样模型在预测雪天时如果犯错会付出更大的损失相当于逼它多关注雪天这个类别。权重的具体数值不需要太精确大致和样本数量的倒数成比例就够。还有一个小技巧冻结部分预训练层。如果你只是做普通微调冻结前几层参数是可以的因为它们学到的是通用低层特征比如边缘、纹理这些内容在绝大多数图像任务里都一样。初始训练时只更新后面的层能减少参数量、防止过拟合训练几个epoch之后再解冻全部层做全模型微调。我这次前5轮冻结了ResNet-18的前两个stage之后解冻效果比全程全量更新更稳定。6. 我踩过的坑和调试心得6.1 数据泄漏的严重教训第一次跑完训练验证准确率高达95%我当时特别兴奋结果跑独立测试集时准确率掉到了78%。重新排查后发现问题出在数据预处理上我在划分train、val、test之前就把所有图片统一做了一遍同样的Resize处理。这听起来没什么但Resize本身会改变图片信息如果test集中某些图片和train集来自同一个原始文件只是副本模型就等于在训练时见过测试集的一部分。后来我把所有划分放到最前面训练集只做训练预处理验证集和测试集在它们自己目录下单独做推理等操作才把准确率恢复到正常水平。这类数据泄漏问题非常容易被新手忽略而且带来的症状特别隐蔽训练损失正常下降验证集表现完美一到真实场景就崩盘。所以做任何数据流水线时都要问自己一个问题测试集的信息有没有在任何训练步骤中被用到哪怕只是简单的均值归一化如果你是用整批数据计算的均值那也算泄漏。正确做法是先用训练集算好统计值再套用到验证集和测试集。6.2 准确率飙升不一定是好事还有一个让我印象很深的坑出现在代码调试阶段训练了不到3个epoch验证集准确率就冲到了99%。我当时以为是模型强无敌后来发现是我把验证集的标签直接复制了一份给测试集做预测代码里有个不应该存在的变量复用。这种错误很蠢但哪怕是有经验的人在熬夜调试时也很容易犯。准确率曲线太完美甚至比人工标注的可靠程度还高那大概率是流程写错了。正确的调整方式是一次只改变一个变量。比如你想试更强的数据增强那就固定模型和优化器不动只改transform你想换ResNet-50那就别动数据增强。这样每次实验之间的差异不会互相影响你也能清楚知道是哪个改动带来了收益。我后来用一个小实验记录表把每次修改的内容、验证准确率、混淆矩阵里的薄弱类别都记下来调参效率高了很多。6.3 关于模型保存和加载的小建议训练完成后你要么会拿去演示要么会继续开发。我建议你把“最佳模型”和“最后一轮模型”分开保存不要只留最后一个best_model.pth。因为最佳模型是基于验证集选择的而验证集本身也有偏差最后一轮模型偶尔在测试集上的表现会更好。两个都留着做测试评估时各跑一遍选测试集上表现更好的那个作为最终版本。加载模型前确认你的类别顺序和训练时完全一致。ImageFolder按文件夹名字排序生成数字标签比如cloudy可能对应0foggy对应1。如果你在推理时用另一个脚本手动加载图片很容易把类别顺序对错导致所有预测全部偏移。我踩过这个坑后来干脆写了一个class_to_idx.json文件保存类别和数字标签的映射推理时读它再也不用猜。还有一个体验上的小技巧训练过程中每隔几个epoch保存一次检查点包括优化器状态。因为训练太长的时候一旦中断你还能从检查点恢复不用从头开始。我第一次训练时忘了保存程序跑到一半显卡驱动崩了整整6个小时的进度全丢从那以后我再也不省这一步了。这个项目的最终结果对我来说不是模型准确率达到多少而是我终于把一套图像分类的标准流程牢牢记在了脑子里。下次再拿到一个陌生分类任务我不会先纠结该用哪个网络而是会先去检查数据、划分、预处理和评估链路是否干净。这些习惯比任何单一模型都重要如果你照着这篇内容做一遍我相信你也会和我有同样的感受。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号