TensorFlow Models 仓库 DeepLab FAQ 深度解析:骨干网络扩展、自定义数据集、BN 微调与多卡训练实战
【免费下载链接】modelsModels and examples built with TensorFlow项目地址: https://gitcode.com/GitHub_Trending/mode/models
本文以 research/deeplab/g3doc/faq.md(官方 FAQ)为骨架,逐条展开其背后对应的 DeepLab 训练/评估脚本与源码实现。读者在读完本文后,将能够独立完成:更换网络骨干(ResNet 等)、接入自定义分割数据集、复现官方精度基线、合理配置output_stride/atrous_rates/eval_crop_size、微调 BatchNorm 参数,以及排查多 GPU 同步 / 异步训练缓慢等问题。
1. FAQ 定位与适用代码范围
本 FAQ 服务于仓库中的 DeepLab 语义分割实现,主体代码位于 research/deeplab:
- 训练入口 train.py
- 评估入口 eval.py
- 公共 flag 定义 common.py
- 骨干网络工厂 core/feature_extractor.py
- 数据集构建脚本 datasets 与数据集描述 data_generator.py
需要注意一个前提:FAQ 覆盖的 train.py / eval.py 等脚本依赖tensorflow.contrib、tf.app.flags、tf.contrib.slim等 TF 1.x 接口,阅读后续命令与配置时应以本项目源码实际内容为准,并留意运行环境的 TensorFlow 版本前提。
FAQ 的 9 个问题可划分为四类主题:模型扩展(Q1/Q3/Q4)、数据接入(Q2)、训练调优(Q5/Q8)、分布式与复现(Q6/Q7/Q9)。下面按主题聚类展开,而不是机械复述问题。
2. 更换网络骨干(ResNet、Xception、MobileNet 与 NAS 系列)
Q1:除了自带的骨干(如 Xception),想用 ResNet 等网络怎么办?
结论:官方 FAQ 给出的做法是直接修改 core/feature_extractor.py 以支持更多骨干。而在当前仓库实际版本中,该文件已内置networks_map(见 feature_extractor.py 第 232-248 行),预置骨干包括:
| 模型系列 | 可用变体 |
|---|---|
| MobileNet | mobilenet_v2、mobilenet_edgetpu、mobilenet_v3_large_seg、mobilenet_v3_small_seg |
| ResNet | resnet_v1_18/18_beta、resnet_v1_50/50_beta、resnet_v1_101/101_beta |
| Xception | xception_41、xception_65、xception_71 |
| NAS | nas_pnasnet、nas_hnasnet |
也就是说,如果你想要的 ResNet 变体已在这个列表内,并不需要修改源码,只需在训练 / 评估命令中通过公共 flag--model_variant=resnet_v1_50_beta指定即可(该 flag 定义于 common.py)。DeepLab 的feature_extractor主分发逻辑位于 feature_extractor.py 第 609-676 行,它会按model_variant名称把请求路由到对应网络工厂,并统一接收output_stride、multi_grid、depth_multiplier、is_training、fine_tune_batch_norm等高层参数;同时每个系列使用独立的arg_scope(如 ResNet 使用batch_norm_decay=0.95、batch_norm_epsilon=1e-5,Xception 使用decay=0.9997、epsilon=1e-3)。
若确实需要全新骨干,需要理解 get_network 的注册机制:模型名必须是networks_map的键,否则会抛出ValueError: Unsupported network(对应校验逻辑在 feature_extractor.py 第 696-697 行)。因此扩展新网络的标准流程是:在networks_map中注册新的名字→网络函数,并在arg_scopes_map中提供配套的arg_scope,然后为新网络补齐对应的预处理函数映射,最后在feature_extractor主分发函数中新增一个elif分支。参考 core/resnet_v1_beta.py、core/xception.py 即可看到既有网络的注册实现范式。
不同骨干在更换时还需联动修改几个配套 flag,FAQ 与源码注释给出的约定如下:
resnet_v1_{50,101}_beta官方 checkpoint 要求--multi_grid=[1,2,4](见 common.py 中multi_grid的注释);xception_65推荐--atrous_rates=[6,12,18](output_stride=16)或[12,24,36](output_stride=8);mobilenet_v2系列不设置atrous_rates,即decoder_output_stride=None;--model_variant默认值是mobilenet_v2(见 common.py)。
3. 在自定义数据集上训练
Q2:想在其它数据集上训练怎么办?
结论:FAQ 建议修改build_{cityscapes,voc2012}_data.py与segmentation_dataset.py来构建自己的数据集。结合当前仓库源码,这条路径可分为两个清晰步骤:
第一步:把自有数据转成 TFRecord。数据集目录 datasets 提供了参考脚本:
- build_voc2012_data.py:把 PASCAL VOC 2012 组织成 TFRecord,配套的自动化脚本是 download_and_convert_voc2012.sh;
- build_cityscapes_data.py:Cityscapes 版本,配套 convert_cityscapes.sh;
- build_data.py:被上述脚本复用的公共转换函数(解析图像与标注、生成 sharded TFRecord)。
你的数据集在转换时可以参考build_voc2012_data.py顶部注释约定的目录结构(JPEGImages/SegmentationClass/ImageSets/Segmentation分层)以及 TFRecord 中每个 Example 应包含的字段(如image/encoded、语义标注等)。
第二步:在数据集注册表中登记新数据集。FAQ 所提到的segmentation_dataset.py在当前仓库中已迁入 deprecated/segmentation_dataset.py(标记为废弃),当前活跃的数据集描述文件是 datasets/data_generator.py。它以DatasetDescriptornamedtuple 描述数据集三项属性(见 data_generator.py 第 58-69 行):
splits_to_sizes:各 split(train / val / test)的样本数;num_classes:语义类别数(含背景,PASCAL 为 21,Cityscapes 为 19,ADE20K 为 151);ignore_label:忽略标签值(PASCAL 与 Cityscapes 均为 255,ADE20K 为 0)。
仓库内置的三个数据集描述符(data_generator.py 第 71-100 行)可作为模板:
| 数据集 | 主要 splits | num_classes | ignore_label |
|---|---|---|---|
| cityscapes | train_fine: 2975、val_fine: 500、train_coarse: 22973 … | 19 | 255 |
| pascal_voc_seg | train: 1464、train_aug: 10582、trainval: 2913、val: 1449 | 21 | 255 |
| ade20k | train: 20210、val: 2000 | 151 | 0 |
新数据集只需仿照这组描述符,把它加入 data_generator.py 第 102-106 行的_DATASETS_INFORMATION字典,再通过--dataset=xxx、--dataset_dir=xxx、--train_split/--eval_split指定,即可复用 DeepLab 完整的数据预处理(缩放、裁剪、随机翻转等)管线。
4. 训练增强集与 DenseCRF 两个“为什么”
Q3:PASCAL VOC 增强训练集(augmented training set)从哪里来?
官方 FAQ 指出:PASCAL VOC 增强训练集由 Bharath Hariharan 等人提供(论文Semantic Contours from Inverse Detectors,ICCV 2011),由其项目主页发布;若在研究中使用了该数据集应引用该论文。这也是 data_generator.py 中train_aug: 10582这个分卷的由来——即官方 1464 张train加上 SBD 提供的增强标注后扩展到 10582 张。仓库 local_test.sh 与 local_test_mobilenetv2.sh 默认下载的初始化 checkpoint(deeplabv3_pascal_train_aug系列)正是在这一增强集上预训练的。
Q4:为什么实现里不包含 DenseCRF?
FAQ 的答复很坦诚:作者并未尝试后处理 DenseCRF,因此不包含在实现中;感兴趣者可参考 Philipp Krähenbühl 与 Vladlen Koltun 的论文Efficient Inference in Fully Connected CRFs with Gaussian Edge Potentials(NIPS 2011)。这说明 DeepLab 官方发布的精度数据并未叠加 DenseCRF 后处理,用户若追求更高指标可以自行在 logits/概率图上外接 CRF,但这不在仓库提供范围内。
5. 自己训练时如何微调 BatchNorm(Q5 逐条展开)
Q5:想自己训练并同时微调 BatchNorm 参数,应该怎么做?
FAQ 首先给出了一条省资源捷径:直接从官方已训好 BN 参数的 checkpoint 开始微调——使用较小学习率、设置fine_tune_batch_norm=false,并因为学习率小而拉长训练步数。在此基础上,FAQ 给出了四条自训建议,逐条对应源码中的 flag:
调大输出步长:设置
--output_stride=16,甚至32;同时必须按比例调整 ASPP 空洞率。例如output_stride=32时 FAQ 建议--atrous_rates=3,6,9;作为对照,output_stride=16的 Xception 常用[6,12,18]。其原理是增大 output_stride 会降低骨干末端特征图的分辨率下采样倍数,从而降低整体计算量。尽可能多用 GPU、加大 batch:训练脚本的多卡(clone)机制由 train.py 的部署相关 flag 控制,详见下文第 6 节。需要特别注意的是 train.py 中的 batch 整除校验:
assert FLAGS.train_batch_size % config.num_clones == 0 clone_batch_size = FLAGS.train_batch_size // config.num_clones即
train_batch_size必须能被 GPU 数量整除,真正的每卡批大小是整除结果。缩小训练裁剪尺寸:把 train.py 的
train_crop_size(默认"513,513")调小,例如 513×513 甚至 321×321,以换取更大 batch。换用更轻量的骨干:如 MobileNet-v2(
--model_variant=mobilenet_v2)。
围绕 BN 微调,源码还揭示了几个容易踩坑的配套约束,一并列出:
- batch 与 BN 的关系:train.py 第 123-127 行 注释明确指出:当
fine_tune_batch_norm=True时,batch size 至少要大于 12(大于 16 更佳),否则应设fine_tune_batch_norm=false以省显存; - learning rate 经验值:train.py 第 93-96 行 注释给出:在 PASCAL 增强训练集(
train_aug)上全量训练用base_learning_rate=0.007;在trainval上微调用0.0001; - weight decay 经验值:train.py 第 129-132 行 注释给出:MobileNet-v2 / Xception 系列用
0.00004,ResNet 系列用0.0001; fine_tune_batch_norm的默认值为 True(train.py 第 171 行),而该 flag 会最终影响骨干网络中is_training的传递——见 feature_extractor.py 第 619/637/646/666 行,只有is_training and fine_tune_batch_norm同时成立时 BN 层才处于训练态更新滑动均值。
6. 多机异步训练与多 GPU 部署
Q6:如何异步训练?
FAQ 给出的做法是:在 train.py 中设置num_replicas(训练机器数)与num_ps_tasks(参数服务器数,FAQ 建议num_ps_tasks ≈ num_replicas / 2),详见slim.deployment.model_deploy。当前仓库中该模块位于 research/slim/deployment 目录下。
对应到 train.py 的实际用法:
--num_clones:每台机器上复制的模型份数(即单机多卡数,默认 1);--clone_on_cpu:是否把 clone 放在 CPU 上;--num_replicas:参与训练的 worker(机器)总数,默认 1;--num_ps_tasks:参数服务器数量,默认 0 表示参数由 worker 本地维护,非 0 才进入 PS 异步架构;--task:当前进程的任务 ID;--master:TensorFlow server 的 BNS 地址;--startup_delay_steps:各 replica 启动的错峰步数,默认 15,训练时会按task * startup_delay_steps计算延迟(见 train.py 第 393 行)。
训练主函数通过model_deploy.DeploymentConfig(...)(train.py 第 276-281 行)统一管理输入、变量、优化器的设备摆放,再由create_clones+optimize_clones完成 clone 创建与梯度聚合。所谓“异步”即多个 worker / PS 并存时不再做全局同步的梯度等待。
7. 复现官方精度:从 local_test.sh 开始
Q7:用了官方 checkpoint 也复现不出论文精度?
FAQ 给出的排查法门是:先跑通官方脚本,再逐步改动、一次只改一处。仓库恰好提供了两个开箱即用的基线复现脚本:
- local_test.sh:Xception_65 骨干;
- local_test_mobilenetv2.sh:MobileNet-v2 骨干。
两个脚本的执行流程基本一致(在research/deeplab目录下运行):
- 先执行
python model_test.py(MobileNet 版为-v)验证 PYTHONPATH 是否已包含research与research/slim(脚本通过export PYTHONPATH=$PYTHONPATH:\pwd`:`pwd`/slim实现,注意脚本会cd ..` 到 research 目录); - 调用 download_and_convert_voc2012.sh 下载并转换 PASCAL VOC 2012 数据;
- 下载官方初始化 checkpoint(Xception 版为
deeplabv3_pascal_train_aug_2018_01_04.tar.gz,MobileNet 版为deeplabv3_mnv2_pascal_train_aug_2018_01_29.tar.gz); - 以
train_split=trainval、train_crop_size=513,513、train_batch_size=4、fine_tune_batch_norm=true训练 10 步(仅冒烟验证); - 在完整 val 集(1449 张)上评估;
- 可视化、导出 frozen graph。
两个脚本的注释里写明了基于官方 checkpoint 应复现的参考指标(作为基线核对锚点):
- Xception_65 版预期mIOU ≈ 82.20%(local_test.sh 注释);
- MobileNet-v2 版预期mIOU ≈ 75.34%(local_test_mobilenetv2.sh 注释)。
如果你连这些官方数值都对不上,说明问题大概率出在环境、数据转换或 flag 组合而非模型本身;此时按 FAQ 建议“一次只改一处”逐步对比即可定位差异。更多公开 checkpoint 的清单可参考 research/deeplab/g3doc/model_zoo.md,PASCAL 与 Cityscapes 的专项用法分别见 research/deeplab/g3doc/pascal.md 与 research/deeplab/g3doc/cityscapes.md。
8. eval_crop_size 的计算规则
Q8:评估时eval_crop_size该设多大?
FAQ 给出的规则:DeepLab 评估采用整图推理(whole-image inference),因此:
eval_crop_size = output_stride × k + 1,其中 k 为整数,取值应使结果尺寸略大于数据集中最大的图像边长。
其原因是 DeepLab 的空洞卷积输出空间分辨率与输入满足输入尺寸 = output_stride × 整数 + 1的关系,只有满足这一约束才能让全分辨率(无下采样丢信息)的卷积对齐成立。FAQ 给出两个可直接套用的案例:
| 数据集 | 图像尺寸 | output_stride 相关约定 | 推荐 eval_crop_size |
|---|---|---|---|
| PASCAL VOC | 最大边 512 | 16×32+1=513 | 513×513 |
| Cityscapes | 统一 1024×2048 | 16×64+1=1025、16×128+1=2049 | 1025×2049 |
与代码对应:eval_crop_size是 eval.py 中的DEFINE_listflag(默认也是"513,513"),它在构建评估数据集(eval.py 第 98 行)与模型ModelOptions(eval.py 第 116 行)时被解析成[height, width],并进一步用于给输入张量set_shape(eval.py 第 121-125 行),保证静态图中整图推理与度量计算的一致性。
同理,训练侧的train_crop_size与评估侧可以不同(例如训练用小图、评估用整图大尺寸),这也是 train.py 中atrous_rates/output_stride注释 特别指出的“train 与 eval 可各自独立设置”。
9. 多 GPU 训练变慢的排查
Q9:为什么多 GPU 训练反而很慢?
FAQ 给出的首要排查方向是数据输入管线(preprocessing)线程不足,并建议把num_readers从默认值调大,例如改为 4(原文档链接指向 train.py 中num_readers = 4的设置处)。
对照当前仓库源码:在 train.py 构建训练数据集的位置,data_generator.Dataset(...)的调用中num_readers=4是硬编码值,并非命令行 flag;也就是说本仓库版本默认就已采用 4 个 reader。如果自训仍觉输入瓶颈,可在此处与num_readers相邻的批处理参数(batch size、shuffle、repeat、scale augmentation 范围等)上做调优——需要说明的是,从源码结构看,num_readers这个量在当前版本并未暴露为 flag,需修改源码实现,改动前请务必先跑通第 7 节的基线脚本。作为对照,评估侧 eval.py 构建数据集时使用的是num_readers=2,可结合eval_interval_secs(默认 5 分钟,见 eval.py 第 51-52 行)判断评估循环是否拖累了整体节奏。
更广义地说,“多卡变慢”还应检查第 6 节提到的train_batch_size是否能被num_clones整除(整除校验失败会直接报错),以及每卡实际 batch 是否过小导致 BN 统计与计算吞吐同时劣化。
10. FAQ 相关参考论文
以下为官方 FAQ 在正文与参考文献中引用的文献,此处以可检索的题录形式给出(原始文档中附有论文/数据集官方页面链接):
- Deep Residual Learning for Image Recognition—— Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun,CVPR 2016(ResNet 骨干出处)。
- Semantic Contours from Inverse Detectors—— Bharath Hariharan, Pablo Arbelaez, Lubomir Bourdev, Subhransu Maji, Jitendra Malik,ICCV 2011(PASCAL 增强训练集 SBD 出处)。
- Efficient Inference in Fully Connected CRFs with Gaussian Edge Potentials—— Philipp Krähenbühl, Vladlen Koltun,NIPS 2011(DenseCRF 后处理出处)。
小结:把 FAQ 与源码对照阅读后会发现,绝大多数问题的答案都收敛到几条核心约束上——output_stride与输入尺寸的对齐(Q8)、ASPP 空洞率与骨干/步长的联动(Q1/Q5)、BN 训练态与 batch size 的联动(Q5)、部署粒度与 batch 整除性(Q6/Q9)。先跑通 local_test.sh 建立基线,再按本节归纳的规则逐条修改 flag,是使用本仓库 DeepLab 实现最稳妥的实践路径。
【免费下载链接】modelsModels and examples built with TensorFlow项目地址: https://gitcode.com/GitHub_Trending/mode/models
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考