☰
卫星云图识别实战:PyTorch实现分类、回归与分割
2026/9/28 5:30:59 网站建设 项目流程

简介:这是一份面向计算机视觉课程的完整大作业项目,以卫星云层图像的理解与识别为核心任务,包含可直接运行的Python源码、说明文档、答辩PPT与实验报告。项目代码经测试可运行,适合计算机、人工智能、电子信息等专业学生用于课程设计、毕设或初期项目演示,也适合初学者对照学习图像分类与深度学习的完整流程。压缩包共140个文件,大小62.33MB,其中以70个Python脚本为主干,另有44个编译生成的pyc文件、6个shell脚本、5个CSV数据文件、3个YAML配置及实验报告PDF与PPT等,涵盖数据预处理、模型训练、测试评估和结果可视化等环节。已有239人浏览学习,这是一份典型的课程作业与毕设参考资源。配套内容不仅包含模型测试CSV与结果图片,还提供README说明和实验报告,方便快速复现;完整目录结构清晰,下载后按文档指引即可运行和修改,能够帮助理解卫星云图识别的数据划分、模型设计与结果分析全过程。

1. 卫星云图“理解与识别”,一门大作业的信息量藏在后半句

拿到“卫星云层图像的理解与识别”这个题目,别急着把它当成一个普通的图像分类任务。云图不是猫狗照片,它没有清晰的物体边界,层云、积云、卷云之间是渐变过渡,同一片区域在不同时刻、不同波段下长得完全不一样。所以这个标题后半句的“理解与识别”才是真正的难点:既要判断云的类型,又要估计云量,还要能逐像素标出云层范围。对计算机视觉课程大作业来说,它最大的优点是数据公开、基线清晰、可以一路从图片分类做到语义分割,最后还能用热力图把模型行为讲清楚,正好把一份大作业做成能写进简历的项目经历。适合动手能力一般但想完整走一遍深度学习图像识别流程的同学。

2. 任务和指标先立住:云层图像识别不是只会贴标签

2.1 把“理解”拆成三个子问题:分类、云量回归与分割

拿到这种题目,第一件事不是下模型,而是把“理解与识别”翻译成计算机视觉能做的具体任务。最常见的拆法是三个子问题一起上,既能撑起大作业的工作量,又不会因为目标太分散而失控。

第一个是云图分类。给定一张云图,判断它是晴空、卷云、积云还是层云,本质是一个图像识别问题。第二个是云量回归。模型输出一个 0 到 1 之间的数值,表示该区域被云覆盖的比例,这是气象里非常实用的指标。第三个是云层分割。逐像素判断每个位置是云还是晴空,对云层边缘不规则、分布零散的情况特别有效。

这三个子问题不是并列独立的关系。我的做法是把它们做成一个共享主干、多输出的结构:前面共用一个卷积网络提特征,后面分叉出分类头、回归头和分割头。训练时三个损失一起回传,好处是共享部分能学到云层的通用纹理和空间结构,单个任务不容易过拟合。如果你只想用最少的代码跑通,那就先做分类加云量回归,二者共用同一个特征提取器,改动量最小;分割作为加分项放到后面补。

2.2 公开数据怎么找、自建目录怎么组织

卫星云图数据集不像 ImageNet 那么好找,但也不是没有。常见的公开来源包括 Himawari-8/9 卫星的 L1B 数据、MODIS 云掩膜产品,以及一些带标注的天气影像数据集。这些数据通常需要自己去官网申请或按波段拼接,所以大作业里比较务实的做法是:选一个已有的公开云图子集,把它整理成自己的数据目录,而不是真的去拉一整年的卫星原始数据。

我一般这样组织数据目录:

data/ ├── train/ │ ├── clear/ # 晴空 │ ├── cirrus/ # 卷云 │ ├── cumulus/ # 积云 │ └── stratus/ # 层云 ├── val/ │ └── (same class folders) └── test/ └── (same class folders)

分类任务可以直接用torchvision.datasets.ImageFolder读取,省去写自定义 Dataset 的麻烦。分割任务则需要另外放一份像素级标注,我习惯把标注做成 PNG 掩膜,文件名与原始图像一一对应:

data/masks/train/ ├── 000001.png ├── 000002.png └── 000003.png

掩膜里云层像素标为 255,背景标为 0。这样训练时读取原图和掩膜,计算逐像素损失就行。

统计类别分布这一步千万别省。卫星云图里晴空和层云往往占大头,积云、卷云样本偏少,如果不做统计,后面训练时类别不平衡会非常明显。

2.3 先定评估指标再碰模型:准确率不是唯一答案

大作业答辩时被问得最多的问题就是“你这个模型到底好不好?”如果你只回答准确率 90%,老师追问一句“测试集里云量大的样本表现如何”就可能卡壳。所以我在动手之前就固定三个子任务各自的评估指标。

分类任务我用top-1 accuracy加macro-F1。卫星云图类别不平衡,准确率容易虚高,macro-F1 对少数类更公平,两个指标一起报更有说服力。云量回归任务用 MAE 和 R²,MAE 衡量平均误差,R² 衡量拟合程度。分割任务用 mIoU(均交并比)和 pixel accuracy,其中 mIoU 是必须报的,pixel accuracy 在云层占比很小的时候会虚高,单独看容易误判。

一个更关键的技巧是:把所有指标写进实验记录表的同一行。每个模型配置对应一行,包含准确率、macro-F1、MAE、mIoU 四个数字。这个习惯会在大作业后期写实验报告时救你一命,因为你会发现很多模型“感觉差不多”,只有把指标并排摆开才看得出哪个真正值得保留。

3. 用PyTorch跑通分类与分割:模型选型、数据加载到训练管线

3.1 ResNet做分类、U-Net做分割:为什么这两个是默认答案

卫星云图大作业不是打比赛,目标是稳定跑通、结果可解释、答辩讲得清楚。所以我不会选 ViT、Swin Transformer 这类大模型,而是选验证成本最低的两个基线:ResNet 和 U-Net。

分类用 ResNet-18 或 ResNet-34。理由有三个:预训练权重成熟,训练收敛快;网络结构教科书级,写进报告好讲;参数量适中,普通笔记本的 GPU 就能跑。ResNet-18 的参数量约 1100 万,在 224×224 输入下单卡训练很快。在云图这种纹理占主导、物体边界不清晰的数据上,太深的网络收益很小,还容易过拟合,所以我一般从 ResNet-18 起步,验证集不涨再换 ResNet-34。

分割用 U-Net。它结构简单,自带跳跃连接,对小样本的遥感影像分割非常友好。DeepLab 系虽然精度更高,但空洞卷积和 ASPP 模块调参空间大,在大作业的时间预算里没必要。U-Net 的跳跃连接能保留云层边缘的细节,这对云层分割特别重要,因为云的边界往往是渐变的,下采样太狠就找不回来了。

回归任务不需要单独设计网络,直接在 ResNet 的 pool 层后面接一个全连接层输出 1 个神经元,sigmoid 激活压到 0 到 1 之间就是云量预测。这样三个任务共享一个编码器,代码维护最省事。

3.2 云图数据加载与预处理:通道、尺寸与归一化

云图的原始数据是多波段的,通常包含可见光、短波红外、水汽等多个通道。大作业里最稳妥的做法是选三个通道合成一张伪彩色图,按三通道 RGB 处理。常见组合是把可见光作为红通道、近红外作为绿通道、红外作为蓝通道,让不同云型在颜色上有区分度。用三通道的好处是能直接用 ImageNet 预训练权重,不用改模型第一层。

预处理代码我这样写:

from torchvision import transforms train_transform = transforms.Compose([ transforms.RandomResizedCrop(224, scale=(0.8, 1.0)), transforms.RandomHorizontalFlip(p=0.5), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) val_transform = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ])

这段代码里有两个值得注意的地方。第一,训练集用 RandomResizedCrop 而不是单纯 Resize,这样能把云的局部纹理放大,增加样本多样性。第二,归一化直接沿用 ImageNet 的均值方差。虽然云图的像素分布和自然图像不一样,但用统一的 ImageNet 统计值能让预训练权重更好迁移,我只是在后续训练中让 BN 层自适应调整。

尺寸上 224×224 是分类任务的常用值,分割任务我会用 256×256 作为输入,因为 U-Net 要求尺寸能被 2 的整数次幂整除,256 是 2 的 8 次方,下采样过程不会出现尺寸对齐错误。

3.3 训练循环与关键参数:损失函数、优化器与学习率策略

这一节是整套代码的核心,也是最容易出问题的地方。我先给出一个精简但完整的训练循环骨架,再逐个解释参数。

import torch import torch.nn as nn from torch.optim import AdamW from torch.optim.lr_scheduler import CosineAnnealingLR model = init_model(num_classes=4) # 分类+回归共享主干 optimizer = AdamW(model.parameters(), lr=1e-4, weight_decay=1e-4) scheduler = CosineAnnealingLR(optimizer, T_max=50, eta_min=1e-6) ce_loss = nn.CrossEntropyLoss() mse_loss = nn.MSELoss() for epoch in range(50): model.train() running_loss = 0.0 for images, labels, cloud_fraction in train_loader: images = images.to(device) labels = labels.to(device) cloud_fraction = cloud_fraction.to(device) logits, cloud_pred = model(images) loss_cls = ce_loss(logits, labels) loss_reg = mse_loss(cloud_pred.squeeze(), cloud_fraction) loss = loss_cls + 0.5 * loss_reg optimizer.zero_grad() loss.backward() optimizer.step() running_loss += loss.item() scheduler.step() val_acc, val_f1 = evaluate(model, val_loader) print(f"epoch {epoch} | loss {running_loss:.4f} | val_acc {val_acc:.4f} | val_f1 {val_f1:.4f}")

训练目的只在分类和回归时,我用 0.5 作为回归损失的权重,避免两个任务量纲差异导致回归任务喧宾夺主。分开训练分割模型时,使用CrossEntropyLoss加DiceLoss的混合损失。单独 CE 在云层像素占比小的时候容易全判为背景,叠加 Dice 损失等于把区域重叠度直接纳入优化目标,能明显改善边缘细节。

优化器默认 AdamW,学习率 1e-4。预训练模型用过大学习率容易把已有的特征破坏掉,1e-4 是我在云图这种非自然图像上反复试出来的安全值。调度器用余弦退火,T_max设为总 epoch 数,让学习率从 1e-4 平滑降到 1e-6,后期收敛更稳。

batch size 建议 16 起步。显存不够就降到 8,但不要为了提速硬开 64。更大的 batch 在云图这种类别不平衡的数据上会让少数类的梯度被稀释。

3.4 保存结果与实验日志:让每次训练都能回放

训练代码跑通只是第一步,真正拉开差距的是实验管理习惯。每跑一次实验,我至少保存三样东西:模型权重、命令行参数、指标结果。权重文件命名带时间戳,比如resnet18_cls_reg_epoch50_acc0.93.pth,避免把不同配置的模型覆盖掉。

指标记录我推荐用最朴素的 CSV 追加写入,不引入额外工具:

import csv with open("exp_log.csv", "a", newline="") as f: writer = csv.writer(f) writer.writerow([timestamp, model_name, epoch, lr, val_acc, val_f1, mae, mIoU])

这个小文件就是你的实验日志。后期写报告时,把 CSV 里每一行对应一段文字描述,实验章节基本就成型了。比起截图一堆训练曲线,这个表在答辩现场更实用,老师问“你这个正则化加了有没有用”时,你直接指着一行数据说“加了之后 mIoU 从 0.71 涨到 0.74”,比任何口头解释都有说服力。

4. 交付一套能直接跑的源码四件套:目录、命令与文档

4.1 源码目录:把数据、模型、工具分开

标题里写明了交付物是“python源码+文档说明+报告ppt+实验报告”,所以源码结构必须让人一眼看懂。我采用一个极少在课程作业里见到但工作里非常常见的组织方式:

cloud_understanding/ ├── README.md ├── requirements.txt ├── configs/ │ └── train.yaml ├── data/ │ ├── train/ │ └── masks/ ├── models/ │ ├── __init__.py │ ├── backbone.py # ResNet + 分类回归头 │ └── unet.py # 分割网络 ├── utils/ │ ├── metrics.py # mIoU、macro-F1 │ ├── losses.py # DiceLoss │ └── logger.py # CSV 实验日志 ├── train.py ├── infer.py └── visualize.py

每个文件职责单一,看名字就知道是干什么的。train.py只管训练,infer.py只管加载模型做推理,visualize.py负责把预测结果画出来。初学者最容易犯的错误是把所有逻辑写在一个几百行的 python 文件里,跑通了却没法解释。如果答辩时老师指着一个函数问它是干什么的,你不需要翻到几百行以外去找答案,这种组织方式本身就是文档。

requirements.txt只需要列核心依赖,torch、torchvision、opencv-python、numpy、pyyaml。版本号不锁死,写最低版本就行,方便在不同机器上复现。

4.2 训练与推理的最小命令:从源码到结果

一个有说服力的大作业源码,应该允许别人用一行命令从头跑完整个流程。我会把超参数放在 YAML 配置文件里,命令行入口保持精简:

python train.py --config configs/train.yaml --task cls_reg python train.py --config configs/train.yaml --task seg

训练脚本内部用yaml.safe_load读取配置,把--task参数作为区分分类回归任务和分割任务的开关。这样一个入口同时支持两个子任务,源码的可读性比复制两份训练脚本好很多。

推理命令更加简单:

python infer.py --ckpt checkpoints/resnet18_cls_reg_epoch50_acc0.93.pth \ --image data/test/cloud_001.png \ --output output/

infer.py做的事情是加载权重、预处理单张图片、前向传播、把分类结果和云量数值写到屏幕和 JSON 文件里。这一步的作用是让源码的最终产物可视化,让使用者在没有训练条件的情况下也能验证模型是活的。

4.3 文档说明、报告PPT、实验报告各写什么:边跑边记

文档说明对应 README,是给陌生人看的使用指南。我写 README 只需要四块内容:项目是什么、数据怎么放、训练和推理命令、目录结构说明。命令必须是能直接复制的,不能是“大概是这样”的描述。一份合格的 README 应该让拿到源码的人在十分钟内把模型跑起来。

报告 PPT 参照计算机视觉课程经典汇报结构走:问题定义、数据展示、方法架构、实验对比、结论。每页最好只放一个核心信息,实验对比那页放三到四组基线模型的数据表格,不要贴满损失曲线。

实验报告是把 README、PPT 和 CSV 实验日志串成完整叙事的地方。我的经验是实验报告必须回答三个问题:为什么选这个模型、参数怎么调的、效果比基线好多少。这三个问题的答案全部应该来自你实际跑过的实验记录,绝不能事后编。所以最优做法是边跑边记,每次改一个参数就在 CSV 里加一行,写报告时直接引用。

5. 卫星云图识别避坑实录:五个我踩过的坑与修正

5.1 云量回归指标好看,分割结果却是一团糊

现象:回归任务的 MAE 降到 0.03,看着非常优秀,但把模型的注意力可视化后,发现它只是记住了整张图的平均亮度,完全没捕捉到云的纹理和位置。分割模型的 mIoU 也只有 0.4 左右。

原因:云量回归是一个全局平均意义上的任务,模型只要输出一个整体比例就能把损失降到很低,不需要理解云的局部结构。单独训练回归头时,网络很容易走捷径,学到“亮度高则云量多”这种粗糙映射。

解决:把回归和分割联合训练,或者在回归 head 之前从分割解码器接入特征。如果只想做回归,就额外加一个辅助分类任务,强制网络提取类别层面的特征,回归分支只在共享特征上做预测。

5.2 训练验证准确率90%+,换一张云图就翻车

现象:训练集和验证集准确率都超过 90%,自我感觉良好。结果从测试集里随机抽一张没见过的云图,模型直接判错,而且错得很离谱。

原因:数据划分的时候我只用了 random split,同一颗卫星、同一天拍摄的相邻云图被同时分到了训练集和验证集。云图相邻帧之间纹理高度相似,验证集成了“记忆测试”而不是“泛化测试”。

解决:划分数据要按时间切片,按天或者按小时块划分。例如前 80% 的时间切片进训练集,后 20% 进验证集,保证任何一张验证图在时间上都不与训练图相邻。这个操作对遥感类数据极其重要,大作业里加了时间划分类别后,验证准确率会掉到 70% 左右,但这才是模型真实能力的反映。

5.3 模型把整张图判成晴空:类别不平衡的连锁反应

现象:训练 Loss 下降很顺利,但分割结果几乎全黑,也就是所有像素都被判为背景。mIoU 里云层那一类的 IoU 接近 0。

原因:云层在大多数云图里只占 20% 到 30% 的像素。模型发现把所有像素都判为晴空,损失依然很低,因为 70% 以上的像素本来就是背景。优化器当然选择最简单的路。

解决:先用 DiceLoss 替换单纯的 CE Loss。然后统计训练集里云层像素的占比,给损失函数加权重。经验做法是把背景权重设为云层权重的 0.3 到 0.5 倍,让模型在粗粒度上先学会哪儿有云,再通过微调修正边缘细节。这个操作做一次下来,云层的 IoU 通常能从 0.1 级别跳到 0.6 级别。

5.4 ImageNet预训练权重在云图上不灵:迁移学习也要分领域

现象:加载了 ImageNet 预训练权重,训练 50 个 epoch,分类准确率卡在 60% 上下,怎么调学习率都上不去。

原因:ImageNet 学到的特征是物体边缘、轮廓、颜色渐变,自然图像占据画面中心的物体有明确边界。云图是完全不规则的纹理场,没有“主体物体”的概念,全局纹理统计才是关键。预训练权重的前几层卷积核和云图分布差异太大。

解决:一个有效的方法是加载预训练权重后,把前几层卷积的参数也设置为可训练,不要冻结。我试过冻结前两层的效果明显更差。另外,训练时对云图使用更强的基础增广,比如随机旋转 90 度、多尺度缩放,让模型摆脱自然图像的“上方”先验。这类增广在自然图像上会损坏语义,在云图上却非常合适,因为云没有“头朝上”的概念。

5.5 实验报告里的曲线和提交代码对不上:版本管理的血泪经验

现象:写实验报告时打开训练曲线截图,发现模型结构和最后提交的代码对不上,某个层的参数名不同,损失曲线复现不出来。

原因:训练中途改了模型结构,没有重新跑完整实验;或者保存的权重文件被覆盖,原配置丢失。大作业时间线拉长以后,这个坑几乎是必然踩的。

解决:现在我的习惯是在训练脚本里把模型的state_dict结构和代码仓库的 commit 信息写在一起。每次训练启动时自动把model.__class__.__name__、所有超参数和当前代码的 git commit hash 记录到实验日志里。这样任何时候翻出实验结果,都能找到对应的代码版本。就算做不了 git,至少也要在 CSV 里加一列写清楚config文件名。

6. 用CAM热力图给答辩报告做增色:收盘小技巧

模型跑通、指标合格,大作业还剩最后一件事:证明模型不是黑匣子。这一步我用 Grad-CAM 做类激活热力图,把“模型为什么这么判断”直观地画出来,放进实验报告和 PPT 里会非常加分。

import cv2 import torch from torchvision import transforms def grad_cam(model, image_tensor, target_class): model.eval() features = {} def hook_fn(module, input, output): features["activation"] = output target_layer = model.layer4[-1] hook = target_layer.register_forward_hook(hook_fn) logits = model(image_tensor) model.zero_grad() score = logits[0, target_class] score.backward() grads = target_layer.weight.grad # 简化示意,实际需取 activation 梯度 hook.remove() return features["activation"], grads

实际使用时用torch.autograd.grad取最后一个卷积层输出的梯度,按通道求平均得到权重,再和激活图加权求和,最后过 ReLU 和归一化生成热力图。把热力图叠加到原始云图上半透明渲染,就能看到模型判断这张图是积云时,主要依据的是画面右下角的高亮纹理区域,而不是整张图的平均亮度。

这个操作在大作业里有三个直接收益。第一,它在答辩现场比任何指标都直观,老师看到热力图聚焦在云的边缘和纹理区,会立刻认可模型学到了语义特征。第二,它能反向检验模型是否在走捷径,如果热力图集中在图像角落的水印上,你就要警惕数据集有问题。第三,它让实验报告从“我跑了一个模型”升级为“我理解了这个模型的行为”。

我现在的习惯是:拿到任何一张云图先跑一次可视化,看一眼热力图再决定要不要继续调参。这个习惯帮我在这类数据上避开了很多反复训练却毫无进展的弯路。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询