tensorflow/models 官方 Legacy 目标检测模块实战指南:RetinaNet、Mask R-CNN 与 ShapeMask 的 TPU/GPU 分布式训练
本文围绕 official/legacy/detection 模块的训练指南展开,系统讲解如何在该仓库中用 TPU 或 GPU 分布式策略训练 RetinaNet、Mask R-CNN 和 ShapeMask 三类检测/实例分割模型,覆盖完整的命令行参数、YAML 配置文件与行内 params_override 用法,并结合入口文件 main.py、配置工厂 configs/factory.py 与模型实现 retinanet_model.py 剖析参数如何落到网络结构与损失函数上。读完你可以直接照抄命令启动训练、按数据集改写配置,并理解每个关键超参的默认值与底层作用。
1. 模块定位与目录结构
该模块位于仓库的 official/legacy/detection 目录下,是 TensorFlow 2 时代的目标检测模型实现。需要特别注意,官方 README 开头给出了弃用警告:该目录后续将被 vision/beta/ 中的新实现取代,因此它更适合作为研究检测模型训练全流程(数据解析、骨干网络、多尺度特征、目标头、分布式训练器、COCO 评估)的完整参考实现,而不是新项目的唯一长期依赖。
从源码结构看,整个模块按职责分成了清晰的子包:
| 目录 | 职责 |
|---|---|
| configs/ | 基础配置模板 BASE_CFG 与各模型(retinanet/maskrcnn/shapemask/olnmask)的差异化配置、配置工厂 |
| dataloader/ | TFRecord 数据读取、锚框生成与各模型专属 parser(数据解码与增广) |
| modeling/ | 模型定义(RetinanetModel、MaskrcnnModel、ShapeMaskModel 等)、架构工厂(backbone/FPN/heads)、损失函数、优化器 |
| evaluation/ | COCO 评估器(box、box_and_mask 等) |
| executor/ | 分布式训练执行器 DetectionDistributedExecutor |
| ops/ | NMS、ROI、后处理算子 |
| utils/ | 框/掩码/类别工具函数 |
统一入口是 official/legacy/detection/main.py,README 中所有训练命令都以它为目标。
2. 前置准备
按照 official/legacy/detection/README.md 的说法,准备工作分两步:
- 获取代码:从 TensorFlow models 仓库克隆代码,或使用预装好的 Google Cloud VM(镜像中已放在
~/models)。本文引用仓库内文件时,对应本地相对路径为official/legacy/detection/...。 - 环境要求:在 Google Cloud 上使用 TensorFlow 2.1+,然后安装依赖:
sudo apt-get install -y python-tk && \
pip3 install -r ~/models/official/requirements.txt
其中依赖清单即仓库中的 official/requirements.txt。python-tk 是为可视化/图像相关依赖服务的;requirements.txt 一次性安装该模块运行所需的全部 Python 包。
3. 命令行接口与参数体系
在动手训练前,理解入口的参数解析链路非常关键,因为 README 中所有命令都是这条链路的产物。
3.1 关键命令行标志
main.py 中定义了核心标志(除公共标志外):
| 标志 | 默认值 | 说明 |
|---|---|---|
--mode |
train |
运行模式:train、eval 或 eval_once |
--model |
retinanet |
要运行的模型:retinanet、mask_rcnn 或 shapemask |
--strategy_type |
公共标志 | 分布策略:tpu、mirrored、one_device 等 |
--tpu |
公共标志 | TPU 任务名(strategy_type=tpu 时使用) |
--num_gpus |
公共标志 | GPU 数量(mirrored/one_device 时使用) |
--model_dir |
公共标志 | 模型 checkpoint 与 summary 的输出目录 |
--config_file |
无 | 指向 YAML/JSON 配置文件,用于覆盖参数 |
--params_override |
无 | 行内 YAML/JSON 格式的覆盖参数,优先级最高 |
--training_file_pattern |
无 | 训练数据路径,可替代配置文件中的同名字段 |
--eval_file_pattern |
无 | 评估数据路径 |
--checkpoint_path |
无 | 仅 eval_once 模式使用,指向单个 checkpoint |
--enable_xla |
False |
在 GPU 上启用 XLA |
--log_steps |
公共标志 | 每 N 步打印训练耗时统计 |
3.2 参数生成与覆盖顺序
main.py 的 run() 展示了参数装配顺序,这也是理解 --params_override 语法的钥匙:
params = config_factory.config_generator(FLAGS.model) # 1. 按 --model 取默认配置
params = params_dict.override_params_dict( # 2. 叠加 --config_file
params, FLAGS.config_file, is_strict=True)
params = params_dict.override_params_dict( # 3. 叠加 --params_override
params, FLAGS.params_override, is_strict=True)
params.override({ # 4. 强制注入运行环境参数
'strategy_type': FLAGS.strategy_type,
'model_dir': FLAGS.model_dir,
'strategy_config': executor.strategy_flags_dict(),
}, is_strict=False)
params.use_tpu = (params.strategy_type == 'tpu') # 5. 保持 use_tpu 与策略同步
要点有三:
- 配置工厂:configs/factory.py 根据
--model的值返回RETINANET_CFG、MASKRCNN_CFG、SHAPEMASK_CFG或OLNMASK_CFG。因此即使省略--params_override中的type字段,--model=mask_rcnn也能正确建模。 - 严格模式覆盖:
is_strict=True意味着--config_file和--params_override只能写已存在的参数键,写错字段会直接报错,这是防呆设计。 - 非 TPU 自动降级:当
strategy_type != 'tpu'时,入口会自动把architecture.use_bfloat16和norm_activation.use_sync_bn强制改为False(main.py L197-L205)。所以 README 的行内示例中 GPU 场景才写use_bfloat16: False、use_tpu: False——写与不写都会被修正到一致状态。
3.3 基础配置模板中的默认超参
所有模型配置都继承自 configs/base_config.py 的 BASE_CFG,以下是与训练最相关的默认值(可被配置文件覆盖):
| 参数路径 | 默认值 | 说明 |
|---|---|---|
train.batch_size |
64 | 全局批大小(TPU 8 芯场景的常用设定) |
train.total_steps |
22500 | 训练总步数 |
train.iterations_per_loop |
100 | 每个训练循环迭代数 |
train.optimizer |
momentum 0.9 + nesterov | 优化器类型与参数 |
train.learning_rate |
step 策略:初始 0.08,在 15000/20000 步衰减到 0.008/0.0008,warmup 500 步 | 学习率调度 |
train.checkpoint.path / prefix |
空 | 从分类预训练权重恢复的路径与前缀 |
train.l2_weight_decay |
1e-4 | L2 正则强度 |
train.train_file_pattern |
空 | 训练 TFRecord 文件模式(必填) |
eval.batch_size |
8 | 评估批大小 |
eval.eval_samples |
5000 | 一次评估最多读取的样本数 |
eval.min_eval_interval |
180 | 两次评估的最小间隔(秒) |
eval.type |
box |
评估器类型,Mask 系模型会改成 box_and_mask |
eval.val_json_file |
空 | COCO 格式 ground truth JSON |
architecture.backbone |
resnet |
骨干网络:resnet 或 spinenet |
architecture.multilevel_features |
fpn |
多尺度特征:fpn 或 identity |
architecture.num_classes |
91 | 类别数,含索引 0 的背景类 |
anchor.num_scales / aspect_ratios / anchor_size |
3 / [1.0, 2.0, 0.5] / 4.0 | 锚框参数 |
resnet.resnet_depth |
50 | ResNet 深度 |
spinenet.model_id |
49 |
SpineNet 规格 |
fpn.fpn_feat_dims |
256 | FPN 通道数 |
postprocess.score_threshold / nms_iou_threshold / max_total_size |
0.05 / 0.5 / 100 | 推理后处理阈值 |
各模型配置在此之上再做差异覆盖,例如 retinanet_config.py 中 RetinaNet 的 Focal Loss 参数:focal_loss_alpha: 0.25、focal_loss_gamma: 1.5、huber_loss_delta: 0.1、box_loss_weight: 50——这组取值正对应 README 参考文献《Focal Loss for Dense Object Detection》(Lin 等,ICCV 2017)的论文配方。
4. 在 TPU 上训练 RetinaNet
4.1 原版 ResNet-50 RetinaNet
TPU_NAME="<your GCP TPU name>"
MODEL_DIR="<path to the directory to store model files>"
RESNET_CHECKPOINT="<path to the pre-trained Resnet-50 checkpoint>"
TRAIN_FILE_PATTERN="<path to the TFRecord training data>"
EVAL_FILE_PATTERN="<path to the TFRecord validation data>"
VAL_JSON_FILE="<path to the validation annotation JSON file>"
python3 ~/models/official/legacy/detection/main.py \
--strategy_type=tpu \
--tpu="${TPU_NAME?}" \
--model_dir="${MODEL_DIR?}" \
--mode=train \
--params_override="{ type: retinanet, train: { checkpoint: { path: ${RESNET_CHECKPOINT?}, prefix: resnet50/ }, train_file_pattern: ${TRAIN_FILE_PATTERN?} }, eval: { val_json_file: ${VAL_JSON_FILE?}, eval_file_pattern: ${EVAL_FILE_PATTERN?} } }"
说明:
- 预训练 ResNet-50 检测格式 checkpoint(
resnet50-2018-02-07.tar.gz)可从cloud-tpu-checkpoints的 GCS 桶(路径model-garden-vision/detection/下)下载。 checkpoint.prefix: resnet50/用于把分类 checkpoint 中的变量名映射到检测网络中的 ResNet 子图,见 modeling/checkpoint_utils.py。- README 特别强调:detection 目录下的 ResNet 实现与 image_classification 目录下的 ResNet 实现并不相同,两者的 checkpoint 互不兼容,只能使用检测侧专用导出的版本。
从源码看,train.checkpoint 与 train.frozen_variable_prefix 配合工作:base_config.py 中提供了 RESNET_FROZEN_VAR_PREFIX 正则,用于冻结 ResNet-50 的 conv1 与 conv2_x 层——注释解释了动机:低层特征(边缘等)无需为检测任务重新微调,冻结后可提升训练速度且精度略有收益。
4.2 SpineNet-49 骨干的 RetinaNet
TPU_NAME="<your GCP TPU name>"
MODEL_DIR="<path to the directory to store model files>"
TRAIN_FILE_PATTERN="<path to the TFRecord training data>"
EVAL_FILE_PATTERN="<path to the TFRecord validation data>"
VAL_JSON_FILE="<path to the validation annotation JSON file>"
python3 ~/models/official/legacy/detection/main.py \
--strategy_type=tpu \
--tpu="${TPU_NAME?}" \
--model_dir="${MODEL_DIR?}" \
--mode=train \
--params_override="{ type: retinanet, architecture: {backbone: spinenet, multilevel_features: identity}, spinenet: {model_id: 49}, train_file_pattern: ${TRAIN_FILE_PATTERN?} }, eval: { val_json_file: ${VAL_JSON_FILE?}, eval_file_pattern: ${EVAL_FILE_PATTERN?} } }"
关键覆盖项为 architecture.backbone: spinenet 与 spinenet.model_id: 49。这里的 multilevel_features: identity 值得一提:SpineNet 本身已经内置多尺度输出(P2–P5),所以用 identity.py 中的 Identity 直通模块替代 FPN 是合理的组合。这一点可以从 architecture/factory.py 印证:multilevel_features_generator 只支持 fpn 与 identity 两种取值,选 identity 时直接返回恒等函数。
4.3 用 YAML 配置文件训练自定义 RetinaNet
更复杂的修改建议写成 YAML 文件。创建 my_retinanet.yaml,至少包含:
# my_retinanet.yaml
type: 'retinanet'
train:
train_file_pattern: <path to the TFRecord training data>
eval:
eval_file_pattern: <path to the TFRecord validation data>
val_json_file: <path to the validation annotation JSON file>
然后启动:
TPU_NAME="<your GCP TPU name>"
MODEL_DIR="<path to the directory to store model files>"
python3 ~/models/official/legacy/detection/main.py \
--strategy_type=tpu \
--tpu="${TPU_NAME?}" \
--model_dir="${MODEL_DIR?}" \
--mode=train \
--config_file="my_retinanet.yaml"
注意此例中 RetinaNet 是 --model 的默认值,故可省略该标志。RetinaNet 专属可覆盖字段还有(见 retinanet_config.py):
retinanet_parser:output_size: [640, 640]、match_threshold/unmatched_threshold: 0.5(锚框正负样本匹配阈值)、aug_rand_hflip: True、max_num_instances: 100等;retinanet_head:num_convs: 4、num_filters: 256、use_separable_conv: False;retinanet_loss:Focal Loss 与 Huber Loss 的四个超参。
5. 在 GPU 上训练 RetinaNet
GPU 训练与 TPU 的差异集中在分布策略:多卡用 mirrored(对应 tf.distribute.MirroredStrategy),单卡用 one_device(对应 tf.distribute.OneDeviceStrategy),并额外指定 --num_gpus。
多卡示例(主机挂载 8 张 GPU):
MODEL_DIR="<path to the directory to store model files>"
python3 ~/models/official/legacy/detection/main.py \
--strategy_type=mirrored \
--num_gpus=8 \
--model_dir="${MODEL_DIR?}" \
--mode=train \
--config_file="my_retinanet.yaml"
单卡示例:
MODEL_DIR="<path to the directory to store model files>"
python3 ~/models/official/legacy/detection/main.py \
--strategy_type=one_device \
--num_gpus=1 \
--model_dir="${MODEL_DIR?}" \
--mode=train \
--config_file="my_retinanet.yaml"
README 还给出了行内配置(YAML 或 JSON 均可)的完整例子,这里保留原文以便直接改造:
python3 ~/models/official/legacy/detection/main.py \
--model_dir=<model folder> \
--strategy_type=one_device \
--num_gpus=1 \
--mode=train \
--params_override="eval:
eval_file_pattern: <Eval TFRecord file pattern>
batch_size: 8
val_json_file: <COCO format groundtruth JSON file>
predict:
predict_batch_size: 8
architecture:
use_bfloat16: False
train:
total_steps: 1
batch_size: 8
train_file_pattern: <Eval TFRecord file pattern>
use_tpu: False
"
多主机(multi-host)训练时,入口会按副本数自动缩放批大小:main.py L112-L115 中,当 num_workers >= 2 时把 train_input_fn 的批大小除以 strategy.num_replicas_in_sync,即配置文件里的 batch_size 始终是全局批大小,框架负责切分到各主机。
6. 在 TPU 上训练 Mask R-CNN
Mask R-CNN 与 RetinaNet 走同一条参数管线,区别是必须显式传 --model=mask_rcnn,且配置里多了 RPN/Fast R-CNN/Mask 三套头与 ROI 相关参数。
6.1 原版 ResNet-50 Mask R-CNN
TPU_NAME="<your GCP TPU name>"
MODEL_DIR="<path to the directory to store model files>"
RESNET_CHECKPOINT="<path to the pre-trained Resnet-50 checkpoint>"
TRAIN_FILE_PATTERN="<path to the TFRecord training data>"
EVAL_FILE_PATTERN="<path to the TFRecord validation data>"
VAL_JSON_FILE="<path to the validation annotation JSON file>"
python3 ~/models/official/legacy/detection/main.py \
--strategy_type=tpu \
--tpu=${TPU_NAME} \
--model_dir=${MODEL_DIR} \
--mode=train \
--model=mask_rcnn \
--params_override="{train: { checkpoint: { path: ${RESNET_CHECKPOINT}, prefix: resnet50/ }, train_file_pattern: ${TRAIN_FILE_PATTERN} }, eval: { val_json_file: ${VAL_JSON_FILE}, eval_file_pattern: ${EVAL_FILE_PATTERN} } }"
同样使用该检测专用 ResNet-50 checkpoint(与分类侧不兼容的说明见第 4.1 节)。
6.2 SpineNet-49 Mask R-CNN
TPU_NAME="<your GCP TPU name>"
MODEL_DIR="<path to the directory to store model files>"
TRAIN_FILE_PATTERN="<path to the TFRecord training data>"
EVAL_FILE_PATTERN="<path to the TFRecord validation data>"
VAL_JSON_FILE="<path to the validation annotation JSON file>"
python3 ~/models/official/legacy/detection/main.py \
--strategy_type=tpu \
--tpu="${TPU_NAME?}" \
--model_dir="${MODEL_DIR?}" \
--mode=train \
--model=mask_rcnn \
--params_override="{architecture: {backbone: spinenet, multilevel_features: identity}, spinenet: {model_id: 49}, train_file_pattern: ${TRAIN_FILE_PATTERN?} }, eval: { val_json_file: ${VAL_JSON_FILE?}, eval_file_pattern: ${EVAL_FILE_PATTERN?} } }"
6.3 用配置文件训练自定义 Mask R-CNN
创建 my_maskrcnn.yaml,最小字段集:
# my_maskrcnn.yaml
train:
train_file_pattern: <path to the TFRecord training data>
eval:
eval_file_pattern: <path to the TFRecord validation data>
val_json_file: <path to the validation annotation JSON file>
TPU_NAME="<your GCP TPU name>"
MODEL_DIR="<path to the directory to store model files>"
python3 ~/models/official/legacy/detection/main.py \
--strategy_type=tpu \
--tpu=${TPU_NAME} \
--model_dir=${MODEL_DIR} \
--mode=train \
--model=mask_rcnn \
--config_file="my_maskrcnn.yaml"
从 maskrcnn_config.py 可以看到 Mask R-CNN 相对基础模板的显著差异,这些都可以按需覆盖:
architecture:min_level: 2、max_level: 6(比 RetinaNet 的 3–7 高一层特征分辨率)、include_mask: True、mask_target_size: 28;maskrcnn_parser:输入output_size: [1024, 1024],rpn_match_threshold: 0.7、rpn_batch_size_per_im: 256、rpn_fg_fraction: 0.5、mask_crop_size: 112;rpn_head / frcnn_head / mrcnn_head:RPN 两卷积、Fast R-CNN 两 FC(fc_dims: 1024)、Mask 头四卷积,均 256 通道;roi_proposal:训练态rpn_pre_nms_top_k: 2000、rpn_post_nms_top_k: 1000、rpn_nms_threshold: 0.7;测试态test_rpn_pre_nms_top_k: 1000等;roi_sampling:num_samples_per_image: 512、fg_fraction: 0.25、fg_iou_thresh: 0.5;eval.type: 'box_and_mask':评估时同时出 box 与 mask 指标。
模型侧这些参数由 maskrcnn_model.py 消费,各头分别由 architecture/factory.py 中的 rpn_head_generator、fast_rcnn_head_generator、mask_rcnn_head_generator 实例化。
7. 在 GPU 上训练 Mask R-CNN
与 RetinaNet 的 GPU 流程完全同构,只是多带 --model=mask_rcnn:
# 多卡(8 GPU)
MODEL_DIR="<path to the directory to store model files>"
python3 ~/models/official/legacy/detection/main.py \
--strategy_type=mirrored \
--num_gpus=8 \
--model_dir=${MODEL_DIR} \
--mode=train \
--model=mask_rcnn \
--config_file="my_maskrcnn.yaml"
# 单卡
MODEL_DIR="<path to the directory to store model files>"
python3 ~/models/official/legacy/detection/main.py \
--strategy_type=one_device \
--num_gpus=1 \
--model_dir=${MODEL_DIR} \
--mode=train \
--model=mask_rcnn \
--config_file="my_maskrcnn.yaml"
行内配置示例(注意 Mask R-CNN 版多了 --model=mask_rcnn,且 total_steps 示例为 1000):
python3 ~/models/official/legacy/detection/main.py \
--model_dir=<model folder> \
--strategy_type=one_device \
--num_gpus=1 \
--mode=train \
--model=mask_rcnn \
--params_override="eval:
eval_file_pattern: <Eval TFRecord file pattern>
batch_size: 8
val_json_file: <COCO format groundtruth JSON file>
predict:
predict_batch_size: 8
architecture:
use_bfloat16: False
train:
total_steps: 1000
batch_size: 8
train_file_pattern: <Eval TFRecord file pattern>
use_tpu: False
"
8. ShapeMask:形状先验驱动的实例分割
ShapeMask 在该模块中用于验证"形状先验(shape priors)能否降低实例分割训练成本"。它复用 ResNet-50 + FPN + 类 RetinaNet 头做检测,再叠三个专用头:形状先验头、粗掩码头、细掩码头(见 architecture/factory.py 的三个 generator)。
8.1 TPU 上训练 ResNet-50 ShapeMask
TPU_NAME="<your GCP TPU name>"
MODEL_DIR="<path to the directory to store model files>"
RESNET_CHECKPOINT="<path to the pre-trained Resnet-50 checkpoint>"
TRAIN_FILE_PATTERN="<path to the TFRecord training data>"
EVAL_FILE_PATTERN="<path to the TFRecord validation data>"
VAL_JSON_FILE="<path to the validation annotation JSON file>"
SHAPE_PRIOR_PATH="<path to shape priors>"
python3 ~/models/official/legacy/detection/main.py \
--strategy_type=tpu \
--tpu=${TPU_NAME} \
--model_dir=${MODEL_DIR} \
--mode=train \
--model=shapemask \
--params_override="{train: { checkpoint: { path: ${RESNET_CHECKPOINT}, prefix: resnet50/ }, train_file_pattern: ${TRAIN_FILE_PATTERN} }, eval: { val_json_file: ${VAL_JSON_FILE}, eval_file_pattern: ${EVAL_FILE_PATTERN} } shapemask_head: {use_category_for_mask: true, shape_prior_path: ${SHAPE_PRIOR_PATH}} }"
形状先验文件(kmeans_class_priors_91x20x32x32.npy,91 类 × 每类 20 个 32×32 的 K-means 聚类中心)可从 cloud-tpu-checkpoints 的 GCS 桶 shapemask/ 路径下载。shapemask_head.shape_prior_path 指向它,use_category_for_mask: true 表示按类别选择对应的先验。
从 shapemask_config.py 看,ShapeMask 默认配置与基础模板的差异包括:train.total_steps: 45000、学习率衰减点 [30000, 40000]、冻结变量前缀 SHAPEMASK_RESNET_FROZEN_VAR_PREFIX(加载分类权重后冻结 ResNet 浅层)、数据增广 aug_scale_min/max: 0.8/1.2(相比 RetinaNet 的 1.0/1.0 更激进,因为分割任务需要更多尺度鲁棒性),以及 shapemask_loss 中 shape_prior_loss_weight: 0.1、coarse_mask_loss_weight: 1.0、fine_mask_loss_weight: 1.0 的三段损失权重。
另外该配置定义了硬性约束(shapemask_config.py L91-L95):
shapemask_head.mask_crop_size == shapemask_parser.mask_crop_size
shapemask_head.upsample_factor == shapemask_parser.upsample_factor
shapemask_parser.outer_box_scale == architecture.outer_box_scale
即 head 与 parser 的 mask_crop_size(32)、upsample_factor(4)以及 outer_box_scale(1.25)必须保持一致,否则参数校验会失败。这是覆盖自定义配置时最容易踩的坑。
8.2 用配置文件训练 ShapeMask
my_shapemask.yaml 示例(README 保留了完整的字段注释):
# my_shapemask.yaml
train:
train_file_pattern: <path to the TFRecord training data>
total_steps: <total steps to train>
batch_size: <training batch size>
eval:
eval_file_pattern: <path to the TFRecord validation data>
val_json_file: <path to the validation annotation JSON file>
batch_size: <evaluation batch size>
shapemask_head:
shape_prior_path: <path to shape priors>
TPU_NAME="<your GCP TPU name>"
MODEL_DIR="<path to the directory to store model files>"
python3 ~/models/official/legacy/detection/main.py \
--strategy_type=tpu \
--tpu=${TPU_NAME} \
--model_dir=${MODEL_DIR} \
--mode=train \
--model=shapemask \
--config_file="my_shapemask.yaml"
8.3 GPU 上的 ShapeMask
多卡与单卡命令与前面一致,仅 --model=shapemask:
# 多卡
MODEL_DIR="<path to the directory to store model files>"
python3 ~/models/official/legacy/detection/main.py \
--strategy_type=mirrored \
--num_gpus=8 \
--model_dir=${MODEL_DIR} \
--mode=train \
--model=shapemask \
--config_file="my_shapemask.yaml"
# 单卡
MODEL_DIR="<path to the directory to store model files>"
python3 ~/models/official/legacy/detection/main.py \
--strategy_type=one_device \
--num_gpus=1 \
--model_dir=${MODEL_DIR} \
--mode=train \
--model=shapemask \
--config_file="my_shapemask.yaml"
行内配置示例:
python3 ~/models/official/legacy/detection/main.py \
--model_dir=<model folder> \
--strategy_type=one_device \
--num_gpus=1 \
--mode=train \
--model=shapemask \
--params_override="eval:
eval_file_pattern: <Eval TFRecord file pattern>
batch_size: 8
val_json_file: <COCO format groundtruth JSON file>
train:
total_steps: 1000
batch_size: 8
train_file_pattern: <Eval TFRecord file pattern>
use_tpu: False
"
9. 训练后的评估
训练完成后切换 --mode=eval 复用同一入口。README 给出的 TPU 评估命令(以 ShapeMask 为例):
python3 /usr/share/models/official/legacy/detection/main.py \
--strategy_type=tpu \
--tpu=${TPU_NAME} \
--model_dir=${MODEL_DIR} \
--mode=eval \
--model=shapemask \
--params_override="{eval: { val_json_file: ${VAL_JSON_FILE}, eval_file_pattern: ${EVAL_FILE_PATTERN}, eval_samples: 5000 } }"
MODEL_DIR必须指向训练时保存的 ShapeMask 模型路径(eval模式会从model_dir读取最新 checkpoint 周期性评估,见 main.py L148-L155 的evaluate_from_model_dir调用)。- GPU 上评估把
strategy_type换成mirrored(并指定num_gpus)即可。 eval_samples: 5000即基础配置eval.eval_samples的默认值,控制单次评估读取的样本上限。val_json_file对 COCO 数据集是必需的(COCO 格式的 ground truth JSON 可在 COCO 官网获取);对自数据集则可不提供——README 指出自数据集的 ground truth 可以直接编码在 TFRecord 文件内。
此外入口还支持 --mode=eval_once --checkpoint_path=<path> 对单个 checkpoint 一次性评估(main.py L156-L167),便于在多个实验目录间对比。
10. 数据输入与模型构建的源码印证
把 README 的命令落到代码层,调用链是:命令行 → 配置工厂 → 模型工厂 → 架构工厂 → 执行器。
10.1 数据读取
train_file_pattern / eval_file_pattern 最终由 dataloader/input_reader.py 的 InputFn 消费:
tf.data.Dataset.list_files展开文件模式,训练时打乱文件顺序;- 多输入管道场景下按
ctx.num_input_pipelines分片(train.input_sharding/eval.input_sharding控制); interleave以cycle_length=32并发读取 TFRecord(tf.data.TFRecordDataset);- 训练集额外
repeat()+shuffle(1000);评估集按eval_samples(即num_examples)take()截断; map(parser_fn)调用模型专属 parser 完成解码与增广,最后batch(batch_size, drop_remainder=True)。
parser 由 dataloader/factory.py 按 architecture.parser 字段选择(RetinaNet 用 retinanet_parser,Mask R-CNN 用 maskrcnn_parser,ShapeMask 用 shapemask_parser),TFRecord 字段解码在 dataloader/tf_example_decoder.py。这解释了为什么配置文件必须提供 train_file_pattern——它是唯一的数据入口(train_dataset_type 固定为 tfrecord)。
10.2 模型前向与损失
以 RetinaNet 为例,retinanet_model.py 展示了配置参数如何变成网络:
# 构造函数:backbone / FPN / head 全部来自架构工厂
self._backbone_fn = factory.backbone_generator(params)
self._fpn_fn = factory.multilevel_features_generator(params)
self._head_fn = factory.retinanet_head_generator(params)
# 前向:输入 -> 骨干 -> 多尺度特征 -> 分类/回归头(按 min_level~max_level 分层输出)
backbone_features = self._backbone_fn(inputs, is_training=...)
fpn_features = self._fpn_fn(backbone_features, is_training=...)
cls_outputs, box_outputs = self._head_fn(fpn_features, is_training=...)
# 损失:Focal 分类损失 + box_loss_weight × Huber 框损失 + L2 正则
model_loss = cls_loss + self._box_loss_weight * box_loss
total_loss = model_loss + l2_regularization_loss
其中输入层的 dtype 由 use_bfloat16 决定(tf.bfloat16 if self._use_bfloat16 else tf.float32),bfloat16 路径下头部输出会先转回 float32 再算损失——这就是 TPU 命令默认 use_bfloat16: True 而 GPU 命令显式写 False 的底层原因。anchors_per_location 则直接等于 anchor.num_scales × len(anchor.aspect_ratios)(默认 3×3=9),修改锚框参数时两处会同步生效。
10.3 训练执行
run() 构造好 train_input_fn / eval_input_fn 后交给 executor/detection_executor.py 的 DetectionDistributedExecutor,它封装了 Keras 分布式训练循环、checkpoint 保存与 min_eval_interval 触发的周期性评估,total_steps 到达即停止。
11. 实操注意事项汇总
结合 README 与源码,以下是最容易出错的点:
- checkpoint 兼容性:detection 侧 ResNet 与 image_classification 侧 ResNet 实现不同,分类预训练权重必须走
train.checkpoint: { path: ..., prefix: resnet50/ }的映射加载,且只能用检测侧导出的 checkpoint。 - 参数键必须真实存在:
--config_file与--params_override都是严格模式覆盖,拼错字段名会直接抛错而非静默忽略。 - ShapeMask 的等值约束:head 与 parser 之间的
mask_crop_size、upsample_factor、outer_box_scale必须保持一致,否则params.validate()失败。 - GPU 上别指望 bfloat16:入口会强制关闭
use_bfloat16与use_sync_bn,显式写它们只是让意图可见。 num_classes含背景:architecture.num_classes: 91是 COCO 的 80 个前景类 + 1 个背景类(索引 0)+ 其他约定类别的结果,换数据集时注意调整。- 评估数据 vs 训练数据的 sharding:默认
train.input_sharding: False、eval.input_sharding: True,即训练时全副本共享文件列表、评估时分片读取以加速。 - 弃用提示:如第 1 节所述,官方计划用
vision/beta/的新实现取代本模块,新工程建议先评估官方 vision 目录下的检测方案(仓库中official/vision及official/projects下有若干后继检测项目),本模块的价值在于其参数体系、分布式训练与评估流程的完整参考。
12. 参考
- Focal Loss for Dense Object Detection. Tsung-Yi Lin, Priya Goyal, Ross Girshick, Kaiming He, Piotr Dollár. ICCV 2017(RetinaNet 的 Focal Loss 理论依据,README 原文引用的参考文献)。
延伸阅读建议从 official/legacy/detection/README.md 出发,配合 configs/base_config.py、modeling/factory.py 与 dataloader/input_reader.py 通读一遍,即可完整掌握该模块"配置驱动 + 工厂构建 + 分布式执行"的设计骨架。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00