资讯详情

HRNet关键点检测实战:从COCO格式到自有数据训练全指南

📅 2026/10/21 4:21:08 | 华诺云谱 👁 阅读
HRNet关键点检测实战:从COCO格式到自有数据训练全指南
deep-high-resolution-net.pytorch 这个 PyTorch 版本的高分辨率网络工程算是做关键点检测最常被翻牌的仓库之一。很多人下载下来拿 COCO 官方权重跑 demo 没问题一旦换成自己标的数据就开始懵标注格式怎么转、配置文件改哪里、loss 为什么不降、mAP 为什么是 0。我前前后后用这个仓库踩过不少坑也帮身边几个朋友调过今天就把“用自己的数据训练 HRNet 关键点检测”这条完整链路拆开讲清楚从数据准备到调参避坑照着做基本能跑通。它解决的核心问题其实不复杂关键点检测就是给定一张图回归出若干个语义关键点的位置比如人脸关键点、手部关键点、工业零件的角点。这个仓库把网络结构、训练流程、评估脚本都写好了你要做的主要是两件事把自己的数据整理成它认得出来的格式把配置文件改成符合你任务的参数。这篇文章就是围绕这两件事展开的适合有少量 PyTorch 基础的研发同学也适合第一次跑关键点模型、想快速出结果的初学者。1. HRNet凭什么是关键点检测的主流选择1.1 全程保持高分辨率而不是走“先降再升”的老路接触过检测和分割任务的读者应该对编码器-解码器结构很熟悉先用卷积逐步下采样把分辨率从 1/2 降到 1/4、1/8、1/16提取语义特征再通过上采样把分辨率恢复回来做逐像素预测。这种做法在图像分类上没问题因为分类只需要全局语义不需要精确的空间位置。但关键点检测恰恰是空间精度敏感的任务。一个点偏了几个像素AP 就会掉一大截。传统的 ResNet 骨干把特征图缩到 1/16 甚至 1/32再做上采样或反卷积恢复很多空间细节在这个过程中已经丢了等于是把一张高清图缩成缩略图再放大边缘和位置信息自然模糊。HRNet 的思路完全反过来从输入开始就保持 1/4 分辨率的高分辨率分支同时并行跑 1/8、1/16、1/32 的低分辨率分支并且在高、低分辨率之间反复做特征融合。低分辨率分支提供语义高分辨率分支留着细节两者通过交换单元不停互相“校准”。整个过程里高分辨率特征从头到尾都没有被压缩到很低的分辨率空间信息丢得少这对关键点这种强位置依赖的任务特别合适。打个比方以前的方法是“先眯着眼看全局再凑近找细节”HRNet 是“一只眼睛看全景一只眼睛盯细节左右脑一直交换信息”最后的判断自然更准。1.2 和 Hourglass、SimpleBaseline 这些方案怎么选实际做项目时用来做关键点的开源方案不少我简单排一下常见路线方案核心思想优点缺点Hourglass重复做“下采样-上采样-跳跃连接”的沙漏结构多尺度信息丰富精度不错计算量大推理偏慢训练不稳SimpleBaselineResNet 提特征后接反卷积简单直观、好改好训特征分辨率低小目标一般HRNet并行多分辨率分支反复融合精度高、空间定位准显存占用偏高、训练时间长对于自己的数据我建议优先用 HRNet。它省去了很多调结构的时间官方在 COCO 关键点上的效果摆在那里仓库里也有完整的训练和测试脚本属于“性价比最高的那个选择”。如果算力非常紧张而且对精度要求不高可以考虑 SimpleBaseline如果对模型可解释性和稳定训练要求高再考虑 Hourglass。但大多数场景下HRNet 的精度收益值得你为它多花一点显存。这里多说一句deep-high-resolution-net.pytorch 这个仓库其实也是很多后续开源项目的地基。它的目录结构清晰数据层、网络层、训练逻辑分开哪怕你想换数据集、换评测方式改动点也很集中适合用来理解整个关键点检测的训练闭环。2. 训练前准备环境、代码与数据集2.1 把环境装好避免版本返工这个仓库的年代相对较早依赖不算复杂核心就是 PyTorch 加几个科学计算库。我实际验证下来的一套比较稳的组合是conda create -n hrnet python3.8 -y conda activate hrnet pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install numpy scipy pyyaml tqdm tensorboardPyTorch 版本不必追新1.8 到 2.x 都能跑关键是 CUDA 要和显卡驱动匹配。装完先跑一下官方提供的数据和命令确认环境没问题再进行自己的数据训练。很多人一上来就用自己的数据跑出问题了也不知道是环境问题还是数据问题排查成本反而更高。显卡要求方面训练 256x192 输入的 HRNet-w32 大概需要 8GB 以上显存batch size 设为 16 左右时。如果没有大显存显卡可以先看第 4 章里关于降低 batch size、开梯度累积的操作用小显存也能跑通。2.2 标注工具与 COCO 关键点格式关键点检测最麻烦的往往不是网络而是数据格式。这个仓库默认读取的是 COCO 关键点格式所以我建议你不管用什么工具标注最后都统一转成 COCO 格式再训练。我习惯先用开源标注工具类似 labelme 这类把关键点标出来再写脚本转换成 COCO JSON。标注之前一定要把关键点顺序定死比如人脸任务定成“左眼、右眼、鼻尖、左嘴角、右嘴角”那么所有图片都必须按这个顺序导出来模型的输出顺序就依赖这个约定。这个顺序一旦搞乱了后续的 loss 计算、评估全部跟着乱。COCO 格式核心包含三个字段images每张图的 id、文件名、宽高annotations每个目标实例的标注信息包括所属 image_id、bbox、keypoints、num_keypoints 等categories类别定义包含关键点名称列表和连接关系。一个 annotations 条目大致长这样{ segmentation: [], num_keypoints: 5, area: 15875.0, iscrowd: 0, keypoints: [368, 214, 2, 459, 213, 2, 398, 265, 2, 347, 292, 2, 472, 291, 1], image_id: 1, bbox: [310, 180, 210, 150], category_id: 1, id: 1 }keypoints 数组是每三个数一组分别是一个点的x 坐标、y 坐标、可见性标志。可见性标志含义是0 表示这个点在图片中未标注1 表示被遮挡但位置是估计的2 表示可见且已标注。训练时网络只会对 v0 的位置计算监督信息所以这个标志不要乱填。categories 大概是{ id: 1, name: object, keypoints: [left_eye, right_eye, nose, left_mouth, right_mouth], skeleton: [[1, 2], [3, 4], [3, 5]] }skeleton 是可选的部分评测脚本不依赖它但建议填上后续做可视化或画骨架方便。bbox 的格式是[x, y, width, height]表示目标框左上角坐标和宽高。它在训练时用来做裁剪和归一化所以一定要包住所有关键点并且尽量贴合目标范围。如果 bbox 和关键点范围对不上训练时会把关键点裁出画面loss 直接异常。2.3 数据目录与预训练权重放置这个仓库通常按下面这种目录结构寻找数据data/ └── coco/ ├── annotations/ │ └── person_keypoints_train2017.json └── images/ └── train2017/ ├── 000000000001.jpg └── ...不要被 coco 这个名字吓到它只是一个约定的目录名。你可以把目录命名为data/coco里面放自己的 json 和图片。至于 json 名称尽量沿用仓库默认的person_keypoints_train2017.json这样改代码最少。如果不愿意改名字也可以在配置文件里指定路径但说实话把文件名凑成仓库期望的格式是最省事的做法。预训练权重同样重要。直接用随机初始化的权重训练 HRNet收敛速度很慢而且小数据集上效果会差很多。建议下载官方在 ImageNet 上预训练好的 HRNet-w32 权重放到类似lib/models/pytorch/imagenet/hrnetv2_w32_imagenet_pretrain.pth的路径下然后在配置里把MODEL.INIT_WEIGHTS指过去。w32 的权重文件不大一次下载后面可以反复用。3. 改配置从官方参数迁移到自己的数据3.1 必须改的核心字段num_joints、SIGMA 和类别官方默认是 COCO 17 个关键点人体。换成自己的任务以后最需要改的是网络的输出通道数也就是MODEL.NUM_JOINTS。这个值必须等于你自己标注的关键点数量比如我做过一个工业零件检测只标了 5 个角点那就把NUM_JOINTS改成 5。它直接决定最后 heatmap 预测的通道数一个是 17一个是 5如果忘记改训练时会直接报维度不匹配的错误。第二个要重点理解的是DATASET.SIGMA。关键点检测通常不是直接回归坐标而是为每个关键点生成一张高斯热图让网络预测这张热图。SIGMA 就控制了高斯核的半径大小SIGMA 越大热图上的亮斑越大网络学起来越容易但定位精度会变模糊SIGMA 越小热图越尖精度上限高但训练难度也大。官方 COCO 用 256x192 输入时默认是 2这个值在大多数场景下可以直接用。如果你的关键点挨得很近比如人脸的五官位置密集可以试着改成 1.5 或者更小一点点配合更大的输入分辨率来提升精度。第三个容易被忽略的是DATASET.DATASET以及DATASET.ROOT。前者告诉训练脚本当前数据集类型沿用 coco 即可后者是数据根目录。一般改 Root 指向你存放图片和 json 的父目录就行。3.2 一份可以直接套用的 yaml 示例deep-high-resolution-net.pytorch 的配置都在experiments/目录下以 yaml 形式存在。下面是一个根据我自己项目经验整理的简化版配置字段含义都写在注释里GPUS: - 0 WORKERS: 4 PRINT_FREQ: 100 DATASET: DATASET: coco ROOT: data/coco/ JSON_PATH: annotations/person_keypoints_train2017.json IMG_PATH: images/train2017 SIGMA: 2 FLIP: true ROT_FACTOR: 30 SCALE_FACTOR: 0.25 MODEL: NAME: pose_hrnet NUM_JOINTS: 5 PRETRAINED: lib/models/pytorch/imagenet/hrnetv2_w32_imagenet_pretrain.pth TARGET_TYPE: gaussian EXTRA: STAGE2: NUM_MODULES: 1 NUM_BRANCHES: 2 BLOCK: BASIC NUM_CHANNELS: - 32 - 64 NUM_CHANNELS_RATIO: - 1 - 1 STAGE3: NUM_MODULES: 4 NUM_BRANCHES: 3 BLOCK: BASIC NUM_CHANNELS: - 32 - 64 - 128 NUM_CHANNELS_RATIO: - 1 - 1 - 1 STAGE4: NUM_MODULES: 3 NUM_BRANCHES: 4 BLOCK: BASIC NUM_CHANNELS: - 32 - 64 - 128 - 256 NUM_CHANNELS_RATIO: - 1 - 1 - 1 - 1 TRAIN: BATCH_SIZE: 24 SHUFFLE: true BEGIN_EPOCH: 0 END_EPOCH: 210 LR: 0.001 LR_STEP: [170, 200] LR_FACTOR: 0.1 OPTIMIZER: adam MOMENTUM: 0.9 WD: 0.0001 TEST: BATCH_SIZE: 8 BBOX_THRE: 1.0 FLIP_TEST: true这里说明一下上面配置片段里JSON_PATH、IMG_PATH这类字段的具体拼写可能和仓库版本有出入你在实际使用时要对照仓库默认的 yaml 微调重点是理解每个字段的含义。例如END_EPOCH: 210是官方常见设置如果只是验证流程可以临时把END_EPOCH改成 5跑完一轮快速确认链路没问题再改回大 epoch。类别数量的关键点数据集 json 中 categories 的 keypoints 列表长度必须等于MODEL.NUM_JOINTS两处不一致时训练脚本可能不会报错但评估结果一定混乱。3.3 预训练权重加载与类别数变化使用自己的数据后NUM_JOINTS一般不等于 17所以不能直接原样加载 COCO 的 17 点模型。更稳妥的路线是加载 ImageNet 分类预训练模型这个模型只包含骨干网络的特征提取部分没有最后的 heatmap 输出头。这样加载时不会出现“最后一层通道不匹配”的问题。加载过程中如果输出一大堆 missing keys 和 unexpected keys可以分段看missing keys 往往是分类头或者 head 层正常unexpected keys 往往是旧的 head 参数正常如果连主干卷积层的 key 都大量匹配不上那就可能是权重文件本身下载错了或者网络名称版本不一样。我自己常用的处理方法是写个小脚本只加载state_dict中键名匹配的部分import torch pretrained torch.load(hrnetv2_w32_imagenet_pretrain.pth, map_locationcpu) model_state model.state_dict() matched {k: v for k, v in pretrained.items() if k in model_state and v.shape model_state[k].shape} model_state.update(matched) model.load_state_dict(model_state)这段代码会忽略形状对不上的层保留随机初始化的输出层。对于转用自己的小数据集来说这是最省心也最不容易出错的办法。4. 实操过程把训练跑通4.1 训练命令与命令行参数覆盖环境、数据、配置都准备好以后训练命令本身很简单。以仓库约定结构为例通常是python tools/train.py --cfg experiments/coco/hrnet/w32_256x192_adam_lr1e-3.yaml--cfg后面指定你改好的 yaml 文件。如果你不想每次改 yaml也可以直接在命令行覆盖关键参数。这类工程一般会支持类似下面的覆盖方式python tools/train.py --cfg experiments/your_config.yaml \ GPUS 0,1 \ TRAIN.BATCH_SIZE 32 \ DATASET.ROOT data/coco/命令行覆盖的好处是方便做实验时对比不同 batch size 或学习率不用反复创建一堆 yaml 文件。第一次跑的时候我建议先做一个“冒烟测试”把END_EPOCH设为 2PRINT_FREQ设为 10数据只放几十张图确认流程能完整跑完、loss 正常打印、模型能保存。这一步能把绝大多数低级问题快速暴露出来比如 json 路径写错、图片读取失败、网络维度问题。等冒烟通过再上全部数据正式训练会省很多时间。4.2 训练日志到底该看什么这个仓库在训练时通常会打印类似这样的日志[epoch 1/210] [iter 1/100] lr: 0.001, loss: 0.0423, acc: 0.31很多人看到 loss 从 0.04 开始就慌了觉得 loss 怎么这么小其实这里的 loss 是 heatmap 上的 MSE均方误差。输入图片被缩放到 256x192 后生成的热图尺寸一般是 64x48高斯亮点在热图上只占很小一片区域大部分位置的 target 是 0所以初始 loss 很小是正常的不必担心。随着训练推进你应该关注两个趋势训练 loss 是不是整体下降验证集的 acc 或 AP 是不是逐步升高。acc 在这里通常表示预测关键点与真值在阈值范围内的比例不一定是最终的 mAP但趋势能说明模型在学习。训练过程中的模型保存频率可以在配置里设置一般会有SAVE_FREQ之类的字段。保存路径默认通常是output/目录里面会区分best_model.pth和final_model.pth。我建议每次实验前把输出目录名改清楚比如加个日期和关键点数量不然训练几次之后目录里一堆模型分不清谁是谁。4.3 测试与评估判断模型真实水平训练完成后用测试脚本评估模型python tools/test.py --cfg experiments/your_config.yaml \ TEST.MODEL_FILE output/your_experiment_dir/best_model.pth这个仓库的输出会包含多组关键点检测指标比如 AP0.5、AP0.75、AR 等。对于自己的数据一定要先看单张图片的可视化结果而不是只盯着 mAP。可视化能告诉你模型是否存在系统性的偏移、是否漏检、是否把遮挡点的判断搞错了这些信息比一个孤立的 AP 数字更直观。我通常会写一个简单的可视化脚本从测试集里挑几张图把标注点画成绿色、预测点画成红色对比一下。如果预测点和真值点整体偏移得厉害往往说明预处理或归一化有问题而不是网络有问题。5. 把准确率往上提的调参经验5.1 从 SIGMA 到输入分辨率精度上限在于热图的“尖”与“平”关键点检测的精度上限很大程度上由 heatmap 的分辨率和 SIGMA 共同决定。热图越小每个像素代表的物理空间越大定位就越粗。官方默认的 256x192 输入对应 1/4 分辨率输出也就是 64x48 的热图对于人体关键点来说性价比很高。但如果你觉得关键点偏移比较大优先把输入分辨率提到 384x288同时把DATASET.SIGMA从 2 降到 1.5 到 1.2 试试。SIGMA 和分辨率的组合有一个经验区间分辨率越高SIGMA 可以适当调小让目标热图更尖。但不能无限小太小会导致 target 中几乎没有正样本像素loss 很难下降。具体调参时可以在验证集上看 AP0.75 这个高精度阈值它对热图的“尖”程度最敏感。AP0.75 上去了但 AP0.5 掉了一般说明热图变得太激进需要适当回调 SIGMA。5.2 数据增强和测试时增强要善用这个仓库在训练时默认会做随机旋转、随机缩放、水平翻转等增强对应配置里的ROT_FACTOR、SCALE_FACTOR、FLIP。旋转角度一般在正负 30 度以内缩放因子 0.25 左右。对于关键点任务旋转和缩放增强非常有效因为它在不改变语义的前提下迫使模型学会位置变化。测试时水平翻转也建议打开对应配置里的FLIP_TEST: true。原理很简单图片水平翻转后再预测一次把预测关键点翻回来与原始预测做平均能显著提升稳定性特别是在目标左右对称的任务上。这个增强没有额外训练成本只是测试时间大约翻倍几乎不白嫖。需要注意一点如果任务里关键点有极强的左右语义差异如文本方向、特殊手势翻转增强反而可能引入错误样本。此时可以把FLIP设为 false或者只做小幅度的翻转。5.3 “看起来正常但效果很差”的几种原因有一类问题特别消耗耐心训练 loss 在降可视化偶尔也准但 mAP 一直很低。我遇到过的典型原因有这几个。第一个是标注可见性标志写错。如果所有关键点的可见性都填了 2包括大量实际被遮挡的点模型会被迫去预测不存在位置自然拉低精度。第二个是 bbox 和关键点不匹配比如 bbox 只框住了局部训练时裁剪后关键点被切掉导致模型学的是残缺样本。第三个是 JSON 里的关键点顺序和配置里的NUM_JOINTS顺序不一致模型输出的第一个通道对应的是“左眼”但 label 里第一个点实际是“右眼”loss 也会降但最终效果一定是乱的。遇到这种问题我的排查顺序是先可视化三到五张训练样本的预处理结果确认热图中心的点和标注点位置对齐再跑一个过拟合实验单张图训练几十个 iter看看 loss 能不能收敛到接近 0如果过拟合实验正常再检查测试集和训练集的数据分布差异。6. 常见问题与排查速查6.1 错误现象对照表下面这个表是我实际训练中整理的高频问题按频率排序现象常见原因解决办法报错 KeyError 或读取 json 失败JSON 路径或字段名不对检查 yaml 中DATASET.ROOT、json 文件名确认 images/annotations 结构CUDA out of memorybatch size 太大或分辨率太高调小TRAIN.BATCH_SIZE或把输入分辨率降到 256x192loss 一直不降学习率太大或标注顺序错了先可视化预处理热图再检查关键点顺序调低 LR 到 1e-4 试跑训练 loss 很低但测试 mAP 很低过拟合、bbox 不匹配、可见性标志错误检查训练集测试集分布、增加数据、检查 bbox加载预训练权重一堆报错权重和模型结构不匹配用 3.3 节的匹配加载方式或重新下载 ImageNet 权重预测点整体偏移固定像素裁剪中心和尺度参数异常检查预处理阶段 affine transform 的中心点是否用 bbox 中心关键点出现明显镜像错位翻转增强和关键点顺序定义冲突检查FLIP配置必要时关闭翻转增强训练到中途 loss 突然变 NaN学习率过高、数据里有异常值降低 LR检查 JSON 里是否有空关键点或越界坐标6.2 两个值得单独说明的坑显存不够时先别急着换小模型。很多人一 OOM 就把 batch size 从 32 改成 2结果 loss 剧烈震荡以为是模型问题。实际上可以试试把输入分辨率降到 192x160或者打开梯度累积。梯度累积的效果不需要改代码通常是每次小 batch 算完梯度后累积几步再更新一次等价于加大了 batch size显存占用却不变。如果仓库不能直接配置梯度累积也可以暂时用较小的 batch size 配上更低的学习率先把流程跑通。小 batch 的梯度噪声大训练起来更“抖”学习率适当打个八折或对折就好。不要忽略验证集上的单张可视化。我之前有过一次经历训练了 50 个 epochAP 一直卡在 0.5 左右看指标以为模型学得还行。后来把预测结果画出来才发现模型对右上角的点系统性偏离两三个像素肉眼看着像“贴边”但精确阈值的 AP0.75 永远上不去。这个问题靠调 SIGMA 和输入分辨率彻底解决了。指标只能告诉你“好坏”可视化才能告诉你“哪里坏”这两件事缺一不可。6.3 小数据集怎么尽量训练好如果自己的标注数据只有几百张图不用指望从零训出一个高精度模型。比较务实的路线是先加载 ImageNet 预训练权重固定住骨干网络只训练最后的 heatmap 头若干 epoch等 loss 明显下降后再放开全部层微调。固定骨干的方法不一定要改代码可以把学习率调得很低比如骨干部分用 1e-5head 部分用 1e-3配合数据增强效果通常比直接全局微调稳定。这个仓库的优化器是 Adam对不同层设置学习率需要自己写点逻辑但实现并不难把参数按名字分组分别放进两个 param group再传给优化器就行。数据增强在小数据集上的作用会被放大。旋转、缩放、翻转和轻微的颜色抖动一起上相当于免费扩充数据量。关键点检测不像分类任务那样对颜色敏感颜色扰动适度即可旋转和缩放才是主要“战斗力”。如果标注时间允许尽量把困难样本和极端姿态也标一部分比单纯增加相似样本有用得多。最后再分享一点个人的实操习惯我每次接一个新的关键点检测任务不管数据多简单都会先定一份“标注规范文档”把关键点名称、顺序、可见性判定标准、边界点处理方式全部写清楚然后拿 20 张图试标确认大家理解一致后再批量标注。这个步骤看起来和调参无关但实际对最终效果的影响比任何超参都大。标注顺序一错后面所有训练和评估都是浪费。训练时我习惯把三个东西固定在每次实验里预训练权重、随机种子、验证集划分。换任何一个实验对比都不公平。模型跑通以后再根据可视化结果做针对性优化先解决“整体偏移”再解决“个别点漂移”最后再抠 SIGMA 这种细节。顺序反了很容易陷入调参调到头晕的窘境。希望这篇内容能帮你少走几步弯路。如果刚开始跑建议先花半天时间把数据格式和冒烟测试打通再花半天把完整训练跑一遍后面的调参就都是增量工作了。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑