简介:Cityscapes数据集(一)是面向城市街景理解与自动驾驶应用的计算机视觉资源,包含来自30个欧洲城市的高分辨率RGB图像及对应的精细像素级标注,适合机器学习、深度学习研究者用于语义分割模型训练与评估。这一部分为数据集gtFine子集的json标注文件集合,共2000个json文件,压缩包大小约730MB。这些json文件采用polygons多边形格式,精确勾勒道路、建筑、行人、车辆、交通标志等30个类别的轮廓,覆盖晴天、阴天、雨天等多种时段与天气条件,是监督学习所必需的真实标签。目前已有2130人学习下载,可用于训练U-Net、DeepLab等主流分割网络,也可用于验证集性能测试、数据增强或多模态融合研究。对从事智能交通、无人驾驶感知算法开发的工程师与高校师生而言,这份精细标注数据能有效省去自行采集与标注的繁杂流程,直接服务于模型调优、论文实验与竞赛项目,有助于提升模型对复杂城市环境的泛化能力。 做语义分割的这几年,几乎每个接触过自动驾驶感知或图像分割方向的人,都会在某个时刻打开 cityscapes 数据集——它不是最早的城市街景数据集,却硬生生靠着统一的标注规范和足够大的规模,成了语义分割领域绕不开的 benchmark。我最初跑分割模型时,第一个正式训练的数据集也是它,当时在官网注册、下载、解压、配置环境,一来一回折腾了一整天,踩了不少坑。这篇先把最基础也最关键的部分讲透:cityscapes 到底装了什么、目录结构怎么理解、怎么用 mmsegmentation 跑通训练,再附上我实际调参过程中遇到的一堆问题,方便你少走弯路。
1. 先搞清楚:Cityscapes 到底装了什么
1.1 数据规模与任务定位
Cityscapes 是奔驰、达姆施塔特工业大学等机构联合发布的城市场景理解数据集,采集自德国的多个城市,一共包含约 25000 帧视频图像。它最有价值的地方在于:其中 5000 张图像拥有高质量的精细像素级标注(fine annotation),另外 20000 张拥有粗糙标注(coarse annotation)。官方把精细标注部分又划分为 train(2975 张)、val(500 张)、test(1525 张)三份,粗糙标注则只有 train_extra 和 val 两个子集。
这套数据覆盖了语义分割、实例分割、全景分割、深度估计等多项任务。日常我们讨论“cityscapes 数据集”时,绝大多数情况指的是语义分割,并且只用其中 19 个类别进行评估,而非原始的 30 类。因为官方在评估时对部分细分类别做了合并或忽略,比如 terrain 和 vegetation 会合并评估,摩托车和自行车等也基于实际用途调整。最终模型输出的 logits 一般就是 19 通道,对应骑手、行人、汽车、卡车、公交车、火车、摩托车、自行车、天空、建筑、墙体、围栏、杆子、交通灯、交通标志、植被、地面、人、机动车道等常用类别。
为什么要专门强调这一点?因为很多新手下载完数据后,直接去看 gtFine 目录里的 PNG 标注图,发现颜色花花绿绿,数一下有 30 多种颜色,就对不上模型需要的 19 类,第一反应以为是数据损坏或版本不对。实际上只是没有搞清楚原始类别和训练类别之间的映射关系,这部分我后面会详细说。
1.2 和 Cityscapes 常对比的几份数据
我经常被问到一个问题:既然有 CamVid、BDD100K 这些同样面向自动驾驶的数据集,为什么还要优先学 Cityscapes?简单做一张对比表,你就能看明白各自的定位:
| 数据集 | 图像数量 | 标注质量 | 类别数 | 特点 |
|---|---|---|---|---|
| Cityscapes | 25000 帧(5000 精细标注) | 精细,像素级多边形标注 | 30 类(评估常用 19 类) | 城市街景,标注规范,学术界最常用 benchmark |
| CamVid | 701 帧 | 像素级 | 32 类(常用 11 类) | 数据量小,适合快速验证 |
| BDD100K | 10 万帧 | 像素级,部分弱标注 | 19 类 | 数据量大,场景来自多个国家,但标注质量参差 |
| Mapillary Vistas | 25000 张 | 像素级 | 66 类 | 覆盖范围广,类别细,但获取有商业限制 |
Cityscapes 最大的优势是标注质量和组织规范度非常高,图像分辨率为 1024×2048,在自动驾驶场景中属于比较标准的街景视角。它适合用来做模型选型、算法对比,也是大多数论文汇报 mIoU 的默认数据集。如果你想验证某个新想法,直接用 Cityscapes 跑一个相对小的模型(比如 segformer、deeplabv3+)在单卡上也能出结果;如果只是想验证代码跑通,CamVid 更轻量,但论文说服力就差多了。
2. 下载与目录结构:别在第一步卡住
2.1 获取数据的正确姿势
Cityscapes 的下载不像 MNIST 或 CIFAR 那样一行代码自动拉取,官方要求研究者先在其官网注册账号,同意数据集许可协议后,才能进入下载页面。这里提醒一句:官方下载渠道只对学术研究和教育用途免费,如果用于商业项目,需要单独联系版权方获取商业授权,这一点在课题立项前就要确认清楚,避免后续产生合规风险。
我自己当时第一次下载时,因为嫌官网注册麻烦,去网上找过别人分享的压缩包,结果解压到一半发现文件缺失,标注文件和图片对不上,非常耽误时间。后来老老实实回到官网注册下载,虽然要填机构信息、用途说明,但下载下来的包是完整且校验一致的。如果你所在的高校或公司已经购买了相关数据服务,也可以直接从学校数据集服务器、实验室内部共享存储里拷贝,这是比个人下载更高效的途径,只要确认数据来源合规即可。
下载页面会提供多个 zip 包,建议按需选择:
- leftImg8bit_trainvaltest.zip:左侧摄像头 8bit 彩色图像,训练、验证、测试全集。
- gtFine_trainvaltest.zip:精细标注全集。
- gtCoarse.zip:粗糙标注全集,量比较大,如果用 Cityscapes 做预训练或半监督任务再下载,普通跑分割模型可以跳过。
另外官方还提供 leftImg8bit_trainextra.zip、camera_trainvaltest.zip 等扩展包,前者对应粗标注的额外训练图,后者是相机参数,做单目深度估计时会用到。常规语义分割只需要前两个包,约 11GB 左右。
2.2 目录结构与文件命名规则
下载解压后,目录结构大致如下:
cityscapes/ ├── leftImg8bit/ │ ├── train/ │ │ ├── aachen/ │ │ │ ├── aachen_000000_000019_leftImg8bit.png │ │ │ └── ... │ │ ├── bochum/ │ │ └── ... │ ├── val/ │ └── test/ └── gtFine/ ├── train/ │ ├── aachen/ │ │ ├── aachen_000000_000019_gtFine_color.png │ │ ├── aachen_000000_000019_gtFine_instanceIds.png │ │ ├── aachen_000000_000019_gtFine_labelIds.png │ │ ├── aachen_000000_000019_gtFine_labelTrainIds.png │ │ ├── aachen_000000_000019_gtFine_polygons.json │ │ └── ... ├── val/ └── test/文件命名的格式比较统一,都是“城市_序列号_帧号_类型后缀.png”。重点拆解一下 gtFine 目录下的几个标注文件,这直接关系到训练数据怎么喂给模型:
- gtFine_labelIds.png:单通道 PNG,每个像素存储的是原始类别 ID(0~33),对应 30 多个具体类别。
- gtFine_labelTrainIds.png:单通道 PNG,已经将原始类别 ID 映射到训练使用的 19 类 ID,值是 0~18,背景或忽略区域为 255。
- gtFine_instanceIds.png:单通道 PNG,存储实例级标注,车辆、行人等每个独立物体会分配不同 ID,用于实例分割。
- gtFine_color.png:三通道彩色可视化图,专为人眼查看准备的。
- gtFine_polygons.json:包含每个对象的多边形坐标、标签等原始标注信息,做精细化分析或转换标注格式时用。
很多同学一开始在 mmsegmentation 里加载数据,发现模型 Loss 不下降,或者训练出的分割图完全混乱,有很大概率是用错了标注文件。训练时要用 labelTrainIds.png,而不是 labelIds.png,更不是 color.png。因为 labelIds 里的 0~33 并不连续,且类别映射与模型输出维度不一致;color.png 是三通道彩色图,直接当单通道 label 读进去会直接报 shape 错误或类别爆炸。
3. 用 mmsegmentation 训练 Cityscapes 的完整实操
3.1 环境准备
mmsegmentation 是基于 PyTorch 的语义分割工具箱,底层依赖 mmcv,版本匹配是新手最容易翻车的地方。建议直接用 mim 安装:
conda create -n openmmlab python=3.8 -y conda activate openmmlab pip install torch==1.13.1 torchvision==0.14.1 --index-url https://download.pytorch.org/whl/cu116 pip install -U openmim mim install mmcv-full==1.7.1 git clone -b v0.30.0 https://github.com/open-mmlab/mmsegmentation.git cd mmsegmentation pip install -e .为什么特意指定版本?因为 mmsegmentation 0.x 系列与 mmcv-full 1.x 系列兼容性最成熟,文档和教程也多。如果你用最新版 mmsegmentation 1.x,请按官方文档安装对应 mmcv 2.x,接口差异比较大,网上很多教程会失效。安装完毕后验证一下:
python -c "import mmcv, mmseg; print(mmcv.__version__, mmseg.__version__)"如果打印出版本号,说明环境基本就绪。
3.2 数据准备与目录软链接
mmsegmentation 默认从data/cityscapes目录读取数据。最简单的方式是在项目根目录创建 data 目录,然后把解压后的两个文件夹软链接进去:
mkdir -p data ln -s /path/to/your/leftImg8bit data/cityscapes/leftImg8bit ln -s /path/to/your/gtFine data/cityscapes/gtFine注意 Cityscapes 有自己的目录组织方式,但 mmsegmentation 的 CityscapesDataset 会自动查找data/cityscapes/leftImg8bit/train和data/cityscapes/gtFine/train。这里有个小坑:如果你下载的是leftImg8bit_trainvaltest.zip,解压出来就是leftImg8bit这个文件夹,名字别改错,否则死活读取不到数据。
另外建议检查一下 gtFine 中是否有_labelTrainIds.png文件。官方下载包里已经有这个文件,但如果你是从旧版本或其他转换工具得到的标注,可能只有 labelIds,需要手动转换。mmsegmentation 并没有内置“从 labelIds 自动转 labelTrainIds”的逻辑,所以一旦缺少该文件,训练时会报错或者类别数量对不上。
手动转换的参考脚本也不复杂,官方 GitHub 仓库里有tools/convert_datasets/cityscapes.py,可以直接执行:
python tools/convert_datasets/cityscapes.py data/cityscapes --nproc 8这个脚本会遍历 gtFine 目录,将 labelIds 映射为 labelTrainIds,同时生成train.txt、val.txt等文件列表。如果你想从零开始自己写转换逻辑,核心就是一张 34 长度的映射表:
ignore: -1 road: 0 sidewalk: 1 building: 2 wall: 3 fence: 4 pole: 5 traffic light: 6 traffic sign: 7 vegetation: 8 terrain: 9 sky: 10 person: 11 rider: 12 car: 13 truck: 14 bus: 15 train: 16 motorcycle: 17 bicycle: 18原始类别中不属于以上 19 类的,统一映射为 255(忽略类别),在损失函数中不参与梯度计算。
3.3 修改配置文件
mmsegmentation 提供了很多现成配置,比如经典的deeplabv3plus_r101-d8_4xb4-80k_cityscapes-512x1024.py这类。日常可以基于它改。需要重点理解几个字段:
dataset_type = 'CityscapesDataset' data_root = 'data/cityscapes/' img_norm_cfg = dict( mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)训练 pipeline 中,最关键的几个操作为:
train_pipeline = [ dict(type='LoadImageFromFile'), dict(type='LoadAnnotations'), dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)), dict(type='RandomCrop', crop_size=(512, 1024), cat_max_ratio=0.75), dict(type='RandomFlip', prob=0.5), dict(type='PhotoMetricDistortion'), dict(type='Normalize', **img_norm_cfg), dict(type='Pad', size=(512, 1024), pad_val=0, seg_pad_val=255), dict(type='DefaultFormatBundle'), dict(type='Collect', keys=['img', 'gt_semantic_seg']), ]这里解释一下cat_max_ratio=0.75的作用。Cityscapes 街景图像中,天空、建筑这类大类别经常占据大量面积,如果随机裁剪时不做限制,会导致某些 batch 里全是背景类,模型学不到小类别(行人、摩托)的特征。这个参数限制单个类别在裁剪区域内的最大占比,超过 0.75 会重新裁剪,相当于一种类别平衡策略,我实际训练时把它保留,mIoU 比去掉它高 1~2 个点。
数据加载器部分:
data = dict( samples_per_gpu=2, workers_per_gpu=4, train=dict( type=dataset_type, data_root=data_root, img_dir='leftImg8bit/train', ann_dir='gtFine/train', pipeline=train_pipeline), val=dict( type=dataset_type, data_root=data_root, img_dir='leftImg8bit/val', ann_dir='gtFine/val', pipeline=test_pipeline), test=dict( type=dataset_type, data_root=data_root, img_dir='leftImg8bit/val', ann_dir='gtFine/val', pipeline=test_pipeline))特别注意ann_dir='gtFine/train'指向的是 gtFine 根目录,mmsegmentation 会在该目录下自动寻找*_labelTrainIds.png文件。如果你想用gtCoarse做预训练,可以把ann_dir换成gtCoarse/train,但仍要注意粗标注文件命名中的_labelTrainIds.png是否存在。
3.4 训练与评估
配置改好后,启动训练:
python tools/train.py configs/deeplabv3plus/deeplabv3plus_r101-d8_4xb4-80k_cityscapes-512x1024.py --work-dir work_dirs/deeplabv3plus_cityscapes训练命令的核心参数80k表示迭代 8 万次。为什么是 8 万而不是直接按 epoch 数设置?因为 Cityscapes 的 train 集只有 2975 张,如果 batch size 为 8,一个 epoch 大约 372 次迭代,80k 次迭代相当于 215 个 epoch,足够模型充分收敛。实际上我用单张 V100 训练 deeplabv3+(ResNet-101 骨架)大约需要 12~16 小时,具体时间取决于输入分辨率、batch size 和 GPU 型号。如果显卡一般,可以把max_iters=80000调低到 40000,mIoU 可能低 1~2 个点,但已经能看出模型效果。
训练结束后,验证:
python tools/test.py configs/deeplabv3plus/deeplabv3plus_r101-d8_4xb4-80k_cityscapes-512x1024.py work_dirs/deeplabv3plus_cityscapes/best_mIoU_iter_80000.pth --eval mIoUmmsegmentation 会自动在验证集上计算 mIoU。需要注意的是,官方排行榜要求以 1024×2048 原始分辨率输入测试,而很多教程配置里默认训练分辨率为 512×1024,这会导致本地验证分数和论文报告分数有差距。为了接近官方分数,可以在 test pipeline 里把img_scale=(2048, 1024)设置成原始分辨率,同时开启 TTA(Test Time Augmentation),也就是水平翻转测试。我实测同样的模型,从 512×1024 提升到 1024×2048 输入,mIoU 能涨 3 到 5 个点,说明分辨率对分割精度影响非常大。
4. 训练时常见的坑与排查实录
4.1 类别对应不上的经典事故
我在前面反复强调 labelTrainIds,就是因为这部分实际踩过坑。有一次我训练用的标注还是 labelIds 转换前的版本,模型输出的 19 类概率图看起来很正常,但可视化出来颜色完全错位,行人的地方预测成建筑,车辆的地方预测成植被。排查了半天,打印数据集返回的gt_semantic_seg才发现像素值范围是 0~33,而不是 0~18,问题一目了然。
如果你也遇到类似情况,建议先做一步快速自查:随便加载一张训练数据,打印标注图的 unique 值:
from mmseg.datasets import build_dataset from mmseg.apis import inference_segmentor, init_segmentor # 这里更简单的做法是直接读 labelTrainIds.png import numpy as np from PIL import Image mask = np.array(Image.open('data/cityscapes/gtFine/val/aachen/aachen_000000_000019_gtFine_labelTrainIds.png')) print(np.unique(mask))如果输出包含 255,说明忽略区域没问题;如果输出最大值超过 18,说明你加载的是 labelIds,需要重新转换。
另一个相关坑是:有些工具包转换出的 mask 是int32或float32,而模型输入要求int64,mmseg 内部会自动处理,但如果你自己写 DataLoader 就很容易出错,报错信息通常是RuntimeError: Expected a Long tensor。遇到这个问题,直接用mask.long()转换即可。
4.2 显存不足与 BatchSize 调整
Cityscapes 原始分辨率是 1024×2048,在 512×1024 输入下,单张 ResNet-101 的显存占用已经不小了。直接用官方默认的samples_per_gpu=2,在 11GB 显存的 2080Ti 上训练基本秒 OOM。我自己的处理方案是:
- 先调小
samples_per_gpu到 1,如果还 OOM,再把crop_size从(512, 1024)降到(512, 512)。 - 保持总 batch size 不变的话,可以开启梯度累积,用
optimizer_config=dict(type='GradientCumulativeOptimizerHook', cumulative_iters=4),相当于每 4 个 step 更新一次参数。 - 开启混合精度训练,mmseg 支持
--amp参数,例如:
python tools/train.py configs/xxx.py --amp不过混合精度在部分显卡上会略微损失精度,先确认 GPU 支持 tensor core 再开启。我自己的经验是,如果只是做算法验证,没必要为了省一两小时显存去折腾 AMP,降低分辨率更省心。
4.3 验证结果和排行榜对不上的原因
本地验证集 mIoU 和官方排行榜分数对不上,属于 Cityscapes 新手一定会遇到的困惑,主要原因有:
- 输入分辨率不同。排行榜要求 1024×2048 原始分辨率,而你本地可能用了 512×1024,这会导致几十分之一的差距被放大。
- 测试增强。官方评估允许 TTA,mmseg 的
--tta参数可以启用水平翻转增强,本地不开 TTA 自然分数偏低。 - 模型权重筛选策略。我见过有人用最后一个 iter 的权重进行验证,而不是用 best mIoU 权重,导致分数波动较大。训练时建议每隔 2000 次迭代验证一次,保存
best_mIoU模型,后续统一用这个权重测试。 - 类别忽略区域处理。如果验证代码没有正确忽略 255 区域,会把 ignored pixels 也算进准确率,结果虚高,但这种虚高在论文投稿时没有说服力。mmseg 的
--eval mIoU自带 ignore,基本不会出问题。
我在一次实验中,本地 val mIoU 是 76.3,提交到官网评测却只有 74.8,后来发现是因为官网评测用更严格的边界处理(mask 边缘 1 像素收缩后再评估),属于正常现象,不必过度惊慌。
4.4 体验总结与一个建议
最后分享一点个人体会。Cityscapes 是一个“看起来简单、跑起来处处是细节”的数据集。虽然下载、配置、训练这条流程我已经走过很多次,但每次换新模型、新框架,还是会遇到新的坑,尤其是数据路径、类别映射、评估协议这三个环节,最容易出幺蛾子。建议第一次跑通的人,不要急着追求高分,先把默认配置原封不动跑完 40k 迭代,确认流程没问题后,再逐步解锁大分辨率、强增强、多尺度测试这些提点手段,后面任何一步调整都更容易定位问题。
如果你正在折腾 Cityscapes,后面可以继续关注这个系列,下一篇我准备拆一拆 Cityscapes 训练进阶的提点技巧,比如伪标签半监督、类别不平衡处理、多尺度融合这些实用手段,以及对应的消融实验怎么做才有效。
本文还有配套的精品资源,点击获取