☰
RetinaNet人脸检测训练配置全解析:从FPN到AMP混合精度
2026/10/21 3:17:28 网站建设 项目流程

看到“_1”这个尾标,我第一反应就是训练目录下第一次正式实验的记录文件。这种命名方式好处是信息密度极高:RetinaNet 是模型家族,R101_FPN 指明骨干和颈部结构,8x8 说明批量配置,AMP 是精度策略,200轮是训练长度,COCO数据集是数据协议,最后那个_1就是实验版本。展开来讲,这就是一套完整的人脸检测训练方案,同时覆盖了模型结构、显存规划、训练速度和收敛设计四个维度的取舍。整篇内容我就围绕这套配置逐项拆,顺便把每个参数为什么这么定、改的时候要注意什么讲清楚,无论你是刚开始跑检测模型,还是已经训练过几版想调优,都能直接参考。

1. RetinaNet 这个骨架,为什么适合做人脸检测

1.1 单阶段模型在密集场景下的取舍逻辑

RetinaNet 是典型的一阶段检测器。所谓一阶段,就是直接把目标分类和边框回归一次性输出,训练阶段所有 anchor 同时参与监督,推理阶段也不需要像两阶段模型那样先用 RPN 挑一批候选框再细分类。两阶段模型的精度通常更高,但代价是计算链路更长、速度更慢;单阶段模型胜在结构简单、推理快,而 RetinaNet 的厉害之处在于用 Focal Loss 把单阶段模型的精度短板给补了回来,所以它才能在 COCO 这类通用检测基准上站住脚。

人脸检测这个任务有个天然特点:人群密集、目标数量多、尺度变化大。一张监控画面里可能有几十张脸,如果用两阶段模型,Region Proposal 阶段就要处理大量候选框,整体吞吐量会很吃亏;而单阶段模型每个位置直接预测,配合合理设计的 anchor,可以在一遍前向里把整张图的检测结果全拿到。实际工程里,人脸检测往往还要串后面的关键点对齐、人脸识别,检测这一步越快,留给后续模块的推理时间越充裕。

RetinaNet 在单阶段模型里还有一个优势:它设计了一套多尺度检测头,配合 FPN 把高层的语义信息和低层的细节信息都利用起来。人脸这种目标不像车辆、家具那样有丰富的内部纹理,很多信息集中在轮廓、眼睛、鼻子这些局部特征上,所以模型能不能同时抓到小尺寸的整体形状和局部细节,很影响最终效果。RetinaNet 的 FPN 结构正好擅长这件事,这也是我在人脸任务上优先选它的原因。

1.2 Focal Loss 到底解决了什么问题

Focal Loss 是 RetinaNet 的核心创新,理解它之前先看传统交叉熵在目标检测里的尴尬处境。一张训练图会生成成千上万个 anchor,其中只有极少数覆盖到真实目标,绝大多数 anchor 都是背景。这些海量背景样本在训练时贡献的梯度加起来非常夸张,模型很容易被“这里没有目标”这种简单判断带偏,真正难分的、贴着目标边缘的样本反而得不到足够的注意力。

Focal Loss 的公式是 FL = -alpha * (1 - p)^gamma * log(p)。其中 p 是模型对正确类别的预测概率,gamma 是聚焦参数,默认取 2.0。核心机制很简单:如果一个样本很容易被正确分类,p 接近 1,那么 (1 - p)^gamma 趋近于 0,这个样本的损失权重就被压得很低;如果是难分类的样本,无论是因为遮挡、模糊还是尺度太小,p 不高,权重就保留得相对大。这样一来,大量简单负样本的梯度被抑制,模型把注意力集中在真正的难点上。

人脸检测里 Focal Loss 的效果尤其明显。人脸检测训练数据里,背景区域占比非常大,一张图中人脸覆盖的像素面积往往不足 5%,剩下的都是皮肤、衣物、建筑、绿化这些背景。如果不做任何损失加权,模型学到“全部预测为背景”就已经能取得很低的损失;Focal Loss 配合 alpha 的类别平衡,等于强制模型把一部分梯度预算分给前景样本,这才能在密集场景下保持对小目标的敏感度。

alpha 的默认值是 0.25。这个值在 COCO 通用检测上表现不错,但人脸数据里前景占比更少,很多人脸任务里会把 alpha 调高到 0.5 甚至 0.75,因为 alpha 的本质是给正样本损失一个更高的放大系数。具体调多少要看你的数据集正负样本比例,我的经验是先用默认值跑到第一个基线,如果 AP50 上不去、而误检又主要在背景区域,再把 alpha 往上调。

1.3 FPN:让不同大小的脸都在合适的特征层上被看见

FPN 全称是 Feature Pyramid Network,它解决的核心问题是目标尺度跨度大。RetinaNet 默认从 ResNet 的不同阶段取特征,输出 P3、P4、P5、P6、P7 这五个金字塔层,对应的 stride 分别是 8、16、32、64、128。层数低的特征图分辨率高、保留细节多,负责小目标;层数高的特征图分辨率低但语义信息强,负责大目标。

人脸检测为什么对 FPN 依赖特别强?因为人脸尺度的变化实在太剧烈。自拍镜头下的人脸可能占据画面四分之一,监控画面中人脸可能只有 20 像素宽,这两种情况如果只用单一特征层,几乎不可能同时处理好。FPN 的做法是把高层语义通过自顶向下路径逐层传递,再用横向连接把同分辨率的浅层特征融合进来,让每一层特征既保留足够的细节又具备语义能力。

在训练配置上,FPN 的输出通道通常统一设成 256。一开始我总想加大通道数,比如 512,觉得特征表达能力强一些总归更好。实测下来收益很小,反而让显存和计算量明显上涨。FPN 的输出通道是给多尺度共享检测头用的,所有层共用同一组头参数,通道数保持一致才合理。真正影响人脸检测效果的不是通道加宽,而是最底层特征图的 stride 能不能覆盖到你关心的目标尺寸范围。后面在调优部分我会专门讲 P2 的问题。

2. R101 与 8x8、AMP、200轮,这些配置背后的账

2.1 为什么选了 ResNet101 而不是 ResNet50

ResNet101 比 ResNet50 多了差不多两层深度的残差块,参数量从大约 25.6M 涨到 44.5M,整个检测模型的总参数量也会相应提升。很多人在选骨干网络时只看精度,忽略了一个事实:更深的网络未必在所有人脸数据集上都稳定带来提升,尤其是在数据量不大的情况下,ResNet101 反而容易过拟合。

我个人的经验是,ResNet101 相对 ResNet50 的提升主要体现在两个地方。第一是目标本身的细节差异很小的时候,比如人脸在监控画面里面积小、纹理信息弱,更深的网络能提取出更抽象的语义线索,帮助模型把一张模糊的小脸从相似的背景纹理中区分出来。第二是在多尺度特征融合中,深层的语义信息更可靠,FPN 自顶向下传播时,能给浅层特征补充更有判别力的大范围上下文。

不过 ResNet101 的代价也摆在明面上。显存占用更高、训练时间更长、推理时延更大。对于已经明确人脸目标尺度不大、场景复杂度高的任务,我建议直接上 ResNet101 作为精度基线,因为后续如果从 ResNet101 换到 ResNet50,AP50 大概会掉 1 到 2 个点,但推理速度确实能快不少。部署阶段如果有帧率压力,再砍回 ResNet50 也不迟。

对比项ResNet50ResNet101
骨干参数量约 25.6M约 44.5M
整体模型内存占用较低明显更高
训练同等条件耗时的相对关系1.0约 1.5
对小目标特征提取能力中等更强
推理速度较快较慢
适合场景帧率优先、算力受限精度优先、有足够显存

2.2 8x8 批量配置的真实含义与显存估算

很多第一次看到“8x8”的人会以为是 batch size 等于 8,其实这种写法最常见的含义是 8 张卡、每卡 8 张样本,总有效批量是 64。在实验记录里写“8x8”比写“batch_size=64”信息更完整,因为它把数据并行方式也透露了:每个 GPU 独立算 8 张图的梯度,然后做一次全归约同步,再共同更新一次权重。

为什么每卡只放 8 张而不是更大?核心限制是显存。RetinaNet + ResNet101 + FPN 在输入短边 800、长边不超过 1333 的情况下,FP32 单卡处理 1 张图的训练显存大概在 6 到 8 GB,这还没算上优化器状态和反向传播时的激活保存。如果每卡塞 8 张,粗略估算就奔着 14 到 18 GB 去了。上了 AMP 之后,激活和梯度都能压下来不少,每卡 8 张任务在 16 GB 显存的卡上勉强能跑,32 GB 的卡才能跑得比较从容。

还有一点容易忽略,多尺度训练会让显存峰值明显波动。训练时如果对输入图像做随机缩放,比如短边在 640 到 960 之间随机,大尺寸样本对应的激活值会大很多,峰值显存可能比固定尺寸高 20% 以上。我建议上线前先用两三张图跑一次短迭代,盯一下实际显存占用再决定输入尺寸范围。

如果你的卡显存有限,不用一次性把每卡样本数提到 8。小批量加梯度累积也能模拟大批量。比如每卡 2 张、累积 4 次,等效批量同样是 64。这种做法有效批量是上去了,但 BN 层的统计仍然基于 2 张图的小批量,如果模型对人体目标统计敏感,还是建议配合同步 BN 使用。

2.3 AMP 混合精度:提速机制与容易踩的坑

AMP 是 Automatic Mixed Precision 的缩写,原理是训练时让权重保持 FP32,前向和反向计算中大部分算子用 FP16,同时用动态缩放保护梯度精度。FP16 最大的优势是显存减半、计算速度更快,尤其是现代 GPU 上有 Tensor Core,对 FP16 矩阵乘法的加速非常明显,训练时间经常能降到原来的 60% 到 70%。

但 FP16 有个硬伤:数值表示范围比 FP32 小很多。FP32 能表示的最小正数大约是 1e-38 量级,FP16 的精度只有大约 5.96e-8,指数部分只有 5 位。当梯度数值很小的时候,直接存 FP16 会直接下溢出变成 0,这会导致参数更新丢失,损失曲线诡异地上不去。AMP 的解决办法是给损失乘一个大缩放因子,比如 65536,把梯度在反向传播前先放大,存成 FP16 时有效数值区间变大,计算完梯度后再除回去。

动态缩放因子的维护是自动的:训练过程中如果连续一段时间没有出现溢出,缩放因子会逐渐变大;一旦检测到溢出导致损失变成无穷大,就立即减半回退。这就是为什么训练日志里偶尔会看到某一步 loss 变成 inf,但下一步又恢复正常,不用太慌。

实际使用 AMP 有一个我反复提醒自己的原则:梯度裁剪和 AMP 不是冲突关系,而是要配合使用。人脸检测头的分类损失数值波动相对大,如果损失缩放后梯度出现极端大值,反向传播更容易溢出。我会把梯度裁剪设在 max_norm=35 附近,既避免极端梯度,又不会过分干扰正常收敛。

另一个容易踩的坑是检查点存储。AMP 训练时模型里既有 FP32 master 权重,又有 FP16 的影子权重,如果框架只保存了影子权重,下次加载微调时精度就会受影响。我建议保存完整模型参数和 optimizer state,确保恢复训练时 master 权重能被正确加载。

2.4 200 轮训练的学习率规划:为什么长训和平常不一样

COCO 检测任务里常见的是 12 epoch 或 24 epoch 的训练计划,200 轮在这个语境下显然是长训练。长训练适合什么情况?一是数据集本身大、目标尺度分布广,模型需要更长时间才能充分遍历难样本;二是用了多尺度增强、随机裁剪这类数据增强策略,模型见到的有效样本变多,需要更多迭代才能收敛。

200 轮如果还用固定步长衰减,比如在 160 轮和 180 轮降两次学习率,容易出现前 150 轮进展缓慢、最后 20 轮学习率降太快导致来不及收敛的情况。长训练我更推荐余弦退火学习率。学习率从峰值缓慢下降,随着训练推进梯度更新步长越来越温和,对后期专门处理难样本非常有利。余弦退火的下降过程是平滑的,不会像阶梯下降那样在两个阶段之间出现明显的损失跳变。

学习率峰值怎么定?最常用的是线性缩放规则:基础学习率乘上有效批量与基准批量的比值。一个常见的检测配置在 batch size 16 时用学习率 0.01,那么 total batch 64 时,0.01 * 64 / 16 = 0.04。这个值能作为起步点,但不是绝对的,如果你发现损失震荡厉害,就先把它下调一半试试。

200 轮的另一个问题是检查点文件很大、数量很多。ResNet101 + FPN 的检测模型加优化器状态,单个检查点常常超过 500MB,每 10 轮存一个就意味着几十个 GB。我的做法是每 10 轮存一次完整检查点,同时额外保留验证集上指标最好的一个,训练结束只留下最后一个完整检查点和 best 检查点,其他全部删掉。

3. COCO 数据集在人脸检测场景里的两种用法

3.1 直接用 COCO 预训练权重,还是自制 COCO 格式数据集

很多第一次做人脸检测的人会问:COCO 数据集里没有人脸检测的完整标注,为什么标题里写 COCO?这个问题得拆成两层看。第一层,COCO 预训练权重是关键。RetinaNet 在 COCO 全量数据上预训练后,骨干网络已经学会了通用的边缘、纹理、物体形状等视觉特征,这些知识迁移到人脸任务上很有价值,尤其是小脸这种共享底层特征丰富的目标。我从 COCO 预训练权重开始做人脸微调,收敛速度明显快于从随机初始化开始,而且最终 AP 也更高。

第二层,所谓的“COCO数据集”也可能指自定义人脸数据按照 COCO 的 json 标注格式整理。COCO 格式是目前最通用的检测数据交换格式,很多开源检测框架原生支持直接训练,不需要额外写数据加载器。如果你手里是 WIDER Face 这类原始格式的人脸数据,通常第一步就是转成 COCO 格式的 json,方便后续跑各种框架和工具。

我自己实际操作中,会把预训练权重和自定义数据两条线索合起来用:模型从 COCO 预训练权重初始化,训练数据用 COCO json 格式组织好的人脸数据集。这样既享受了通用特征的迁移红利,又不受 COCO 原始类别的限制,标题里写 COCO 也没毛病。

3.2 COCO 标注格式里那些容易感染的坑

COCO 格式的核心是 images 和 annotations 两个数组。每张图有一个 image id,一个文件名和宽高信息;每条标注记录包含 image_id、category_id、bbox、area、iscrowd 等字段。bbox 的格式是 [x, y, width, height],注意是浮点数,而且坐标原点是左上角。人脸检测里很多人会把检测框标成矩形框,这没问题,但要确保 width 和 height 不能出现 0 或负值,否则数据加载时直接报错。

category_id 是最容易错的地方。COCO 原始数据里背景类别 id 是 0 吗?不是,COCO 的类别 id 从 1 开始。很多检测框架内部把所有类别作为 0 到 num_classes-1 的索引,然后默认索引 0 是背景。如果你自定义数据集时把 face 的 category_id 设成 0,框架会把 face 当成背景类别,训练出来模型会输出一团乱。正确的做法是 category 列表里只保留 face 一项,让它的标签 id 为 1,框架层面映射过去才对应类别索引 0。

iscrowd 字段也值得留意。COCO 里 crowd 标注代表的是密集人群这类不可分实例,eval 的时候通常会被忽略或特殊处理。人脸检测数据集如果保留了 crowd 标注,在训练和评估时要清楚框架怎么处理。有些框架会把这些区域在损失计算中直接 mask 掉,有些则只在评估时忽略,配置不一致会导致指标对比失真。

3.3 人脸检测的指标选择:AP、AP50 和 AR

COCO 协议的评价指标是 AP@[0.5:0.95],也就是在 IOU 阈值从 0.5 到 0.95 每间隔 0.05 计算一次 AP,然后取平均。这个指标对边界框的精度要求很高,人脸检测中如果检测框稍微松动一点,IOU 就会掉到 0.75 以下,所以 AP@0.5:0.95 这个值通常比通用物体检测还要难看。

人脸检测更常看的其实是 AP50,也就是 IOU 阈值 0.5 下的 AP。人脸框没有严格的边界,只要中心点和覆盖范围大致对得上,0.5 的 IOU 已经能说明框得不错。我在报告实验结果时,一般会给出 AP50 和 AP@0.5:0.95 两组数,前者代表实际可用性,后者反映定位精度,两者差距过大就说明框的位置还不够稳。

还有一个容易忽视的指标是 AR 平均召回率。人脸检测实际场景里,漏检的影响往往比误检更严重,一个系统如果大量漏掉人脸,后续的人脸识别再强也白搭。所以我训练完会专门看一眼验证集上的召回率,尤其是小尺寸人脸区间的召回率。如果大脸的 AP 很高但小脸 AR 很低,那问题多半出在特征图底层步长或者 anchor 尺度覆盖上,而不是分类能力。

4. 从配置文件到完整训练链路

4.1 环境准备与工程结构

这套训练配置的硬件底线是 8 张显存不低于 16GB 的 GPU,软件方面需要的是 Python 3.8 以上、PyTorch 1.8 以上、对应的 CUDA 版本,以及一个支持 COCO 评测和 AMP 训练的开源检测框架。工程目录里我习惯分成 configs、data、work_dirs、tools 四块,configs 放所有实验配置,data 放数据集和预训练权重,work_dirs 按日期和实验名建子目录,tools 放自己写的转换脚本和评估脚本。

数据目录建议单独放在一块读写速度快的 SSD 上。人脸检测的训练数据往往是几万张 jpg,如果从机械硬盘读取,CPU 会成为瓶颈,GPU 利用率跑到一半就上不去了。我实测过,训练数据从机械硬盘换到 NVMe SSD 之后,每 100 步的训练耗时能降低 15% 到 20%,即使 GPU 本身没变。

4.2 关键配置参数逐个过

配置文件的写法虽然不同框架略有区别,但关键模块是相通的。我一般会从主干开始配,再补上 Neck、Head、优化器、数据管道这几个块。下面这段是简化后的关键配置,你可以根据自己的框架做映射。

# 模型部分 model = dict( type='RetinaNet', backbone=dict( type='ResNet', depth=101, frozen_stages=0, # 微调时不冻结骨干 norm_eval=False ), neck=dict( type='FPN', in_channels=[256, 512, 1024, 2048], out_channels=256, start_level=1, # 从 ResNet 的第二阶段开始 num_outs=5 # P3-P7 ), bbox_head=dict( type='RetinaHead', num_classes=1, anchor_generator=dict( type='AnchorGenerator', strides=[8, 16, 32, 64, 128], ratios=[0.5, 1.0, 2.0] # 人脸框更扁或更方,可调 ), loss_cls=dict( type='FocalLoss', alpha=0.25, gamma=2.0 ), loss_bbox=dict( type='SmoothL1Loss', beta=0.11 ) ) ) # 优化器与学习率 optimizer = dict( type='SGD', lr=0.04, # 按 8x8 总批量 64 推算 momentum=0.9, weight_decay=0.0001 ) lr_config = dict( policy='CosineAnnealing', warmup='linear', warmup_iters=500, warmup_ratio=1.0 / 3 ) # 混合精度 fp16 = dict(loss_scale='dynamic') # 数据管道 train_pipeline = [ dict(type='LoadImageFromFile'), dict(type='LoadAnnotations', with_bbox=True), dict(type='Resize', img_scale=(1333, 800), keep_ratio=True), dict(type='RandomFlip', flip_ratio=0.5), ]

有几点想特别解释一下。第一,frozen_stages 我设成了 0,也就是骨干网络全部参与训练。如果你用 COCO 预训练权重做微调,前几十轮可以把前两层冻住节省显存,但要警惕冻结过多会让特征和任务适配不够。第二,anchor 的 ratios 默认是 [0.5, 1.0, 2.0],人脸框整体偏竖长或方正,根据你的实际数据分布,改成 [0.8, 1.0, 1.5] 这类贴近人脸比例的配置,比盲目增加 anchor 数量更有效。第三,warmup_iters 500 在长训练里看似不多,但能用非常小的学习率让模型先站稳,避免第一轮就把预训练权重冲坏。

数据管道里 Resize 这一步特别关键。短边 800、长边 1333 是 COCO 的默认设置,人脸检测如果要照顾小目标,建议把短边提高到 900 或 1000,但显存成本会同步上涨,要和 AMP、批量设置一起权衡。多尺度训练也需要在这里加一个 RandomResize 范围,让模型对各种分辨率输入更鲁棒。

4.3 训练启动与监控

训练启动前一定要先做一次小规模自检。我的流程是取训练集前 8 张图,把批量设成 2,只跑 20 步迭代,确认 loss 能正常回传、显存不炸、写入日志没有报错。这一步能在五分钟内暴露大部分环境问题,远比直接开全量训练发现失败更有性价比。

全量训练启动后,我会开三个监控指标:loss 曲线、GPU 利用率和显存占用。损失曲线在 warmup 阶段出现轻微上升是正常现象,因为学习率从零爬升,权重开始被扰动,这不代表发散了;等到 warmup 结束,如果 500 步内 loss 还没开始下降,那就要怀疑学习率太大、数据加载异常或者预训练权重没加载进去。GPU 利用率稳定在 90% 以上说明计算没有瓶颈,如果经常掉到 70% 以下,大概率是数据加载线程不够或者磁盘读取慢。

训练到中期,建议每隔一段时间看一眼验证集上的小目标 AP。人脸检测的难点永远是那批小脸,模型对大脸的指标可能很快刷到高位,而小脸 AP 往往要到训练后半程才逐渐抬头。如果快结束小脸 AP 还是纹丝不动,不要硬加轮数,先检查 anchor 尺度和输入分辨率,这两个因素不改,练到 400 轮也很难突破。

4.4 测试评估与导出

测试评估阶段要区分验证集和测试集。验证集用来做检查点筛选和超参调优,测试集只在最终确定模型后跑一次,避免在测试集上反复试参导致过拟合。COCO 评测协议提供了标准 AP 计算工具,我建议直接把每次验证结果输出成一个 json 文件,方便后面对比实验。

人脸检测模型部署时经常要导出成 ONNX 或 TensorRT。导出阶段容易出问题的是 anchor 解码和 NMS 后处理,检测框架在训练时把 anchor 解码和 NMS 放在模型外,导出时要么显式将其合入网络,要么在部署代码里用相同的参数重新实现。NMS 的 IOU 阈值默认 0.5,人脸检测如果目标重叠比较严重,可以适当调到 0.6,减少重叠人脸被吞掉的情况;但不要追高到 0.8,否则误检会明显增加。

导出测试还有一个小技巧:先在 PyTorch 原生推理跑一遍,再用导出后的模型跑同样的测试图,比较检测框数量和 AP 是否接近。两者差异如果超过 0.5%,通常就是导出时某个算子不兼容、精度下降或者后处理参数不一致,这种问题越早发现越省事。

5. 训练中经常遇到的现象和排查思路

5.1 从损失曲线看模型状态

损失曲线是最直接的诊断窗口。训练前期 loss 不降,我会依次查三件事:学习率是不是太大导致震荡,预训练权重路径是不是写错导致模型随机初始化,以及数据标注类别 id 是不是从 1 开始。这三个问题合起来能解释绝大多数“训练了很久 loss 纹丝不动”的情况。

训练中段 loss 出现断崖式下跌,听起来像好事,实际要警惕。有一种情况是某个难样本在长时间训练后终于被模型“想通”,损失自然下降,这是正常的;另一种是动态 loss scale 在调整时改变了损失数值的绝对大小,看起来骤降,但实际精度没有变化。遇到这种情况,我会同时看验证集 AP,以 AP 为准,不要被损失曲线的绝对值变化带偏。

训练后期 loss 降得很慢,是收敛的正常表现。人脸检测尤其是长训练,后期主要由难分样本和小目标贡献梯度,损失曲线会趋于平缓。这时更需要关注的是验证集指标是否还在稳步上升,如果训练 loss 还在降但验证 AP 开始下降,说明模型开始过拟合,需要提前停止或增强数据扩充。

5.2 显存、速度和精度三类问题的速查

现象常见原因排查与解决思路
启动训练直接 OOM批量太大或输入尺寸过大先关 AMP、关多尺度测试峰值;还不行就减批量或缩短边
训练几步后 OOM多尺度增强把最大输入撑爆限制随机缩放的上限,或对最大输入单独调小批量
loss 出现 NaN动态 scaling 失效或学习率过大查日志里溢出次数;降学习率,打开梯度裁剪
loss 不下降预训练权重没加载或类别配置错误打印模型输出的类别数和 loss 初始值;核对 category_id
GPU 利用率低CPU 数据加载成瓶颈增加 num_workers,换 SSD,检查数据解码耗时
AP 一直很低anchor 尺度与目标尺寸不匹配小脸改成更小的 anchor 尺度,或提高输入分辨率
检测框大量重叠NMS 阈值过低提高 IOU 阈值到 0.6 左右,同时检查类别输出是否混乱

训练速度慢还有一个隐蔽来源:验证集评测代码里如果用了复杂的数据增强或者单卡串行处理,每轮验证可能就要十几分钟,200 轮累计下来非常可观。长实验我会把验证频率从每 1 轮改成每 5 轮或每 10 轮一次,训练速度立刻就看得出差别。

5.3 长训练实验的管理习惯

200 轮训练动辄跑几天,中间任何一次断电、进程被杀都会造成损失。我习惯用后台进程加日志落盘的方式启动训练,并开启自动恢复功能,这样训练中断后可以从最近一个检查点无缝接上,而不是从头开始。恢复训练前要确认学习率调度器的状态和累计训练轮数都对得上,直接从检查点里的 iteration 读取。

实验命名也需要强迫症一点。配置文件、训练日志、检查点目录、指标结果,全部用同一个实验标识,标题里那个“_1”其实就很有用,它代表这是该配置的第一版。改动任何参数时,哪怕只改了一个 anchor ratio,也建议新建一个实验标识,不然后期比对实验效果时根本分不清哪份结果对应哪套参数。

6. 从这套实验延伸出去的几条真实心得

如果让我只保留一条经验,那就是确认每个超参数为什么存在,再决定改不改它。这套 RetinaNet + R101 + FPN + AMP + 200 轮的组合,每个词都有明确职责:FPN 负责多尺度覆盖,AMP 负责在显存和速度之间找平衡,200 轮配合余弦退火负责把难样本充分消化。直接照搬这套组合通常能跑出不错的基线,但它更适合中大型人脸数据集。

实际场景里,如果人脸以监控小脸为主,我下一步会优先加 P2 层。RetinaNet 默认从 stride 8 开始检测,等于最小 anchor 面积约 32 像素见方,更小的脸基本依赖高分辨率输入硬扛。加入 P2 意味着 stride 4 的特征图参与检测,小脸召回率能明显提升,但代价是计算量显著增加,需要同步验证显存和推理速度。另一个方向是自定义 anchor scale,把默认从 32 开始改成从 4 或 8 开始,效果有时比加 P2 更直接。

还有一点我在测过很多次后体会很深:模型训练指标好,不等于部署效果好。人脸检测后面通常接着人脸识别,识别模块对检测框的位置精度要求很高,AP50 拉满但框的中心偏移几个像素,很可能导致后续特征对齐失败。所以训练时我会刻意在验证过程中统计一下检测框中心与标注中心的平均偏差,这个指标比单看 AP50 更能反映后续链路稳定性。

这套配置本身的扩展空间也很大。201 轮、202 轮、不同随机种子、更大输入分辨率,其实都可以在同一套工程里快速跑出来。关键是把每个实验的记录做完整,标题里那一长串参数命名就是很好的模板,它就是最好的实验日志。下次回看结果时,不需要翻半天配置,只看文件名的几个词,就能知道自己当时正在验证什么。

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

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

立即咨询