
InsightFace Sub-Center ArcFace 实战放宽类内约束的亚中心损失、并行训练框架与 MS1MV0-Drop75 数据清洗全流程【免费下载链接】insightfaceState-of-the-art 2D and 3D Face Analysis Project项目地址: https://gitcode.com/GitHub_Trending/in/insightfaceSub-Center ArcFaceECCV 2020通过引入亚中心超参数loss_K放宽 ArcFace 的类内紧凑性约束使模型对大规模噪声网络人脸数据更鲁棒。本文基于 InsightFace 仓库recognition/subcenter_arcface目录下的原始实现完整拆解亚中心损失的符号图构造、ParallModule并行训练框架如何消化额外显存开销以及“先训 Sub-Center ArcFace、再用 drop.py 剔除噪声、最后重训标准 ArcFace”的三步训练流程读完后你可以理解该方法的完整落地链路从 sample_config.py 中的配置项到 train_parall.py 的损失构建再到 drop.py 的逐身份样本筛选逻辑。1. 动机用一个超参数放宽类内紧凑性约束原始文档 README 给出的动机非常凝练We introduce one extra hyperparameter (subcenter numberloss_K) to ArcFace to relax the intra-class compactness constraint. In our experiments, we findloss_K3can achieve a good balance between accuracy and robustness.其背景是标准 ArcFace 要求同一身份的 512 维特征尽量聚拢到单一类中心上这在干净数据集上是优点但在标注噪声率约 50% 的大规模网络数据如 MS1MV0上类中心会被大量错误标注的样本拖偏类内紧凑性约束反而成为噪声的放大器。Sub-Center ArcFace 的做法是给每个身份分配K个类中心subcenters样本只需落在离它最近的那个子中心附近即可——同一身份的不同外观簇可以分别由不同子中心承载类内约束被显著松弛从而提升鲁棒性。文档结论是loss_K3在精度与鲁棒性之间取得良好平衡该值在仓库配置中也是默认值见 sample_config.py 的config.loss_K 3。2. 损失构建原理K 倍类中心权重矩阵与 max 池化从源码结构看亚中心机制的全部数学实现集中在 train_parall.py 的get_symbol_arcface()L120-L178。其构造方式如下K 倍权重矩阵fc7 权重变量fc7_%d_weight的 shape 被声明为(args.ctx_num_classes * config.loss_K, config.emb_size)即每个身份的K个子中心权重直接平铺在类方向上L127-L131_weight mx.symbol.Variable(fc7_%d_weight % args._ctxid, shape(args.ctx_num_classes * config.loss_K, config.emb_size), lr_multconfig.fc7_lr_mult, wd_multconfig.fc7_wd_mult) nweight mx.symbol.L2Normalization(_weight, modeinstance) # 每个子中心分别 L2 归一化 nembedding mx.symbol.L2Normalization(embedding, modeinstance) fc7 mx.sym.FullyConnected(datanembedding, weightnweight, no_biasTrue, num_hiddenargs.ctx_num_classes * config.loss_K, namefc7_%d % args._ctxid) if config.loss_K 1: sim_s3 mx.symbol.reshape(fc7, (-1, args.ctx_num_classes, config.loss_K)) sim mx.symbol.max(sim_s3, axis2) # 对每个身份的 K 个子中心相似度取最大值 fc7 simmax 池化是亚中心损失的核心reshape 成(batch, num_classes, K)后沿axis2取mx.symbol.max等价于“每个身份只认与其最接近的子中心”这正是文档中 Sub-Center ArcFace 示意图所表达的“一对多类中心”关系。ArcFace 角度间隔 margin 照常叠加当loss_m1 ! 1.0 or loss_m2 ! 0.0 or loss_m3 ! 0.0时对 gt one-hot 位置的相似度做arccos → 乘 m1 → 加 m2 → cos → 减 m3的角度 margin 变换最后乘以温度系数loss_sL149-L172。各 margin 的默认值在 sample_config.py 中定义损失配置loss_nameloss_sloss_m1loss_m2loss_m3含义arcfacemargin_softmax64.01.00.50.0角度间隔 m0.5默认cosfacemargin_softmax64.01.00.00.35余弦间隔nsoftmaxmargin_softmax64.01.00.00.0普通 softmaxgenerate_config()L206-L224会把选定的 loss/network/dataset 配置段合并进全局config因此--loss arcface时loss_K3与loss_m20.5同时生效。推理侧完全不受 K 影响训练结束后只需提取嵌入层输出fc1_outputbackbone 的 512 维特征子中心仅存在于训练用的 fc7 权重中——drop.py 与 verification.py 都是取出all_layers[fc1_output]单独构建推理 Module 的。3. ParallModule把 K 倍类权重摊到多张 GPU 上的并行训练框架原文档“Implementation”一节指出Sub-Center ArcFace 的训练流程与 ArcFace 基本相同额外增加的显存开销“可以被我们的并行框架轻松缓解”。这套并行框架就是 parall_module_local_v1.py 中的ParallModule其设计动机在loss_K1时更为突出为什么显存会爆炸若 num_classes8.5 万、emb_size512、K3fc7 权重矩阵就是25.5 万 × 512个浮点数再加上 softmax 的(batch, num_classes*K)激活矩阵单卡放不下。框架的解决方案对应文档中的 framework 图类空间按 worker/GPU 切分train_parall.py 中ctx_num_classes ceil(num_classes / (num_workers * ctx_num))每张 GPU 只负责一段连续的类区间_ctx_class_startparall_module_local_v1.py每张卡各自持有一个只含本段类权重的 arcface Module含本段的 fc7 子矩阵。前向backbone Module 在全部 GPU 上跑数据并行得到全局 fc1 嵌入随后把完整的 fc1广播给每个 GPU 的 arcface Module各模块用局部类偏移后的 label_label self.global_label - self._ctx_class_start[i]只计算本段类的 fc7margin 输出forward。反向这是手写逻辑最密集的部分backward。全局 softmax 的 max 与 sum 通过跨模块聚合先取各模块 fc7 输出的行最大值拼成ctx_fc7_max再nd.max(..., axis1)得到全局 max数值稳定的 log-sum-exp 技巧各模块输出减全局 max 后nd.exp再求和得到全局 sum随后每个模块对exp(logit-global_max)/global_sum - one_hot求导_module.backward()只反传本段 fc7 的梯度各段对 fc1 的输入梯度累加local_fc1_grad ctx_fc1_grad后统一回传 backbone。参数路由set_params/get_params按前缀把fc7_%d_weight路由到对应 GPU 的 arcface ModuleL117-L152update()中先更新 backbone 再更新各 arcface Module最后mx.nd.waitall()同步L425-L430。从源码结构看forward()中每个 arcface Module 的输入是fc1全量每卡本地只有ctx_num_classes*K行权重参与点积因此单卡 fc7 权重显存 num_classes/K_gpu 数 × K × 512K 带来的 3 倍开销被类切分摊薄了——这就是文档所说“parallel framework 缓解显存”的具体含义。单机场景走num_workers 1分支导入parall_module_local_v1train_parall.py多机分布式则走parall_module_dist。4. 三步训练流程总览MS1MV0 → Drop75 → MS1MV0-Drop75原文档“Training Steps”给出的完整流程如下本文后续三节逐一展开Step 1在 MS1MV0 上训练 Sub-Center ArcFaceloss_K3Step 2丢弃非主导子中心non-dominant subcenters并剔除高置信噪声样本余弦距离 75°python drop.py --data ms1mv0-path --model step-1-pretrained-model --threshold 75 --k 3 --output ms1mv0-drop75-pathStep 3在新得到的MS1MV0-Drop75数据集上训练标准 ArcFace。原文档同时给出了训练集 MS1MV0标注噪声率约 50%的下载渠道百度网盘 code8ql0、Dropbox 直链以及预训练模型与日志的下载地址百度网盘 code3jsh、Google Drive 直链。注意文档中的外链以仓库 README 原文为准本文不重复列出复现时请确认这些外部资源仍有效。5. Step 1 训练细节数据格式、迭代器与检查点策略数据格式。训练读取 recordio 索引数据集data/train.rectrain.idxtrain_parall.py。rec 文件采用“头记录 每身份一条索引”的结构idx 0 的 header 记录[id索引起点, 身份区起点]身份区间内每条 header 记录该身份图片的[起始idx, 结束idx]。image_iter.py 的FaceImageIter正是解析header.flag 0的头记录来建立全局图片索引序列shuffleTrue时每 epoch 打乱。该格式同时也是 Step 2 输出MS1MV0-Drop75的格式保证 Step 3 可直接复用同一套迭代器。数据集配置。sample_config.py 提供两个数据集段dataset.emore../datasets/faces_emorenum_classes85742即 MS1MV0默认与dataset.retina../datasets/ms1m-retinaface-t1num_classes93431。--dataset emore即对应 MS1MV0。常用训练参数默认值来源sample_config.py 与 train_parall.py参数默认值说明--networkr100默认 ResNet-100fresnetemb 512--lossarcfacemargin_softmaxs64、m20.5--lr0.1初始学习率SGD momentum0.9、wd0.0005--lr-steps100000,160000,220000按全局 batch 步数阶梯式乘 0.1 降学习率--per-batch-size128每卡 batch总 batch per_batch_size × GPU 数--verbose2000每 2000 batch 做一次验证集测试--ckpt31必要时刻保存2总是保存3只保留最新一次--kvstoredevice多卡参数同步策略检查点选择逻辑。_batch_callbacktrain_parall.py每verbose步在val_targets默认lfw、cfp_fp、agedb_30需数据目录下存在对应.bin文件上跑 10 折验证当最后一个 target 的 Accuracy-Flip 达到历史最高或总分创新高时保存模型保存的 symbol 只有fc1_outputL382-L388即只存嵌入层验证了“子中心权重只用于训练”的设计。验证实现见 verification.py图片正/翻转各推理一次特征相加归一化后做 10 折阈值搜索求 ROC 精度。6. Step 2 深度剖析drop.py 的亚中心主导判定与 75° 噪声过滤drop.py 是整个方法链中最体现 Sub-Center ArcFace 特性的工具脚本命令行参数及默认值如下L195-L211参数默认值说明--data/bigdata/faces_ms1m_fullMS1MV0 数据目录含 train.rec/idx--model../Evaluation/IJB/pretrained_models/r50-arcfacesc-msf-k3z/model,2Step 1 的 Sub-Center ArcFace 检查点格式prefix,epoch--threshold75余弦角度阈值度样本与主导子中心夹角超过该值即判为噪声--k3亚中心数必须与训练时的 loss_K 一致--batch-size16每卡推理 batch自动乘以 GPU 数--output/bigdata/ms1m_full_k3drop075清洗后数据输出目录脚本内部流程可归纳为五个关键步骤加载嵌入模型从检查点取出fc1_output单独构建推理 ModuleL113-L118与训练时的 fc7 结构完全解耦。重组亚中心权重按fc7_0_weight、fc7_1_weight…顺序从arg_params中拼接各 GPU 的分段权重L99-L109L2 归一化后 reshape 成(-1, K, 512)得到每个身份的 K 个子中心L110-L112。逐身份提特征get_embedding()L21-L73按身份区间的 recordio 索引批量前向输出归一化特征。主导子中心判定 高置信噪声剔除L157-L186核心逻辑cos_thresh np.cos(np.pi * args.threshold / 180.0) # 75° - cos 阈值 ... sim np.dot(subcenters, _x) # 每个样本与 K 个子中心的相似度 mc np.argmax(sim) K_stat[mc] 1 # 统计各子中心承载的样本数 dominant_index np.argmax(K_stat) # 该身份的主导子中心 dominant_center subcenters[dominant_index] sim np.dot(x, dominant_center) idx np.where(sim cos_thresh)[0] # 只保留与主导子中心夹角 75° 的样本注意两处语义其一“Drop non-dominant subcenters”体现在只用 dominant_center 做过滤——非主导子中心承载的样本不再代表该身份的主外观簇其二“high-confident noisy data (75 degrees)”指样本与主导子中心的余弦夹角超过 75°相似度低于cos(75°)这类远离类主簇的样本大概率是错标噪声。若某身份所有样本全部低于阈值len(idx) 0该身份整段丢弃。 5.写出新 recordio通过 rec_builder.py 的SeqRecBuilder把保留的图片以重排后的连续 label0..N-1写入output/train.rec train.idx并生成property文件num_classes, 112, 112。这就是MS1MV0-Drop75数据集的由来其结构与原始 MS1MV0 完全兼容可直接作为--dataset指向的新路径。运行前提脚本依赖CUDA_VISIBLE_DEVICES环境变量决定 GPU 数量L85-L93并依赖 mxnet、sklearn、easydict、opencv 等环境模型检查点必须来自loss_K3的 Step 1 训练否则fc7_%d_weight的行数无法被--k整除 reshape 会失败。7. Step 3在 MS1MV0-Drop75 上重训标准 ArcFace清洗后的数据集去掉了约 50% 噪声率下的最可疑样本此时不再需要亚中心直接训练标准 ArcFaceloss_K1即mx.symbol.max分支被跳过退化为普通角度间隔 softmax即可获得更强的类内紧凑性。从源码结构看train_parall.py 与 parall_module_local_v1.py 对loss_K是参数化处理的config.loss_K 1时 fc7 权重就是ctx_num_classes × 512的标准形状因此无需改动代码只要将config.loss_K置 1或换用仓库 recognition/arcface_mxnet 的 train.py 流程并在sample_config.py的 dataset 段新增指向ms1mv0-drop75-path的数据集配置即可。验证目标仍是lfw、cfp_fp、agedb_30三个 bin检查点选择策略与 Step 1 相同。8. 相关文件索引与引用本实现涉及的仓库文件均相对仓库根目录文件作用recognition/subcenter_arcface/README.md方法动机、三步流程与预训练模型说明recognition/subcenter_arcface/sample_config.pyloss_K、网络/数据集/损失/默认超参配置recognition/subcenter_arcface/train_parall.py训练入口亚中心 fc7 符号构建、类切分、验证与存盘recognition/subcenter_arcface/parall_module_local_v1.pyParallModule并行前向/反向/参数路由recognition/subcenter_arcface/image_iter.pyrecordio 数据迭代器shuffle/镜像/色彩抖动recognition/subcenter_arcface/drop.py主导子中心判定与 75° 噪声剔除脚本recognition/subcenter_arcface/common/rec_builder.py清洗后 recordio 写出SeqRecBuilderrecognition/subcenter_arcface/common/verification.pyLFW/CFP-FP/AgeDB-30 10 折验证recognition/README.mdInsightFace 人脸识别方法总览SubCenter ArcFace 条目方法来源为 ECCV 2020 论文引用格式与原文档一致inproceedings{deng2020subcenter, title{Sub-center ArcFace: Boosting Face Recognition by Large-scale Noisy Web Faces}, author{Deng, Jiankang and Guo, Jia and Liu, Tongliang and Gong, Mingming and Zafeiriou, Stefanos}, booktitle{Proceedings of the IEEE Conference on European Conference on Computer Vision}, year{2020} }适用前提与限制本实现基于 MXNetmx.mod.Module接口面向单机/多机 GPU 训练drop.py要求模型 checkpoint 的fc7权重可按--k维切分数据链路依赖 recordio 格式与lfw/cfp_fp/agedb_30验证 bin 文件。若使用 PyTorch 生态训练标准 ArcFace仓库同时提供了 recognition/arcface_torch可与本文的 Drop75 清洗流程组合使用。【免费下载链接】insightfaceState-of-the-art 2D and 3D Face Analysis Project项目地址: https://gitcode.com/GitHub_Trending/in/insightface创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考