ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

300张手机人脸图训练性别分类器:TensorFlow SSD实战与避坑指南

300张手机人脸图训练性别分类器:TensorFlow SSD实战与避坑指南 简介这份资源面向计算机视觉方向的科研人员、算法工程师与深度学习入门者提供一套可直接用于性别检测与分类训练的人脸数据集。数据源自真实手机采集共300张高质量人脸图片按woman与man两个子集完成分类与标注可用于人脸检测、特征提取与性别分类等算法实验也适合作为课程设计或模型验证的素材。压缩包共505个文件约339.41MB除jpg、png图片外还包含大量py脚本、config与proto配置文件、pb模型文件、pbtxt标签映射、checkpoint权重及ipynb笔记覆盖SSD、MTCNN等检测框架的配置与训练流程并附带mov、mp4演示视频与record数据便于复现与二次开发。目前已有59人学习下载。整体目录结构清晰既能支撑性别分类模型的训练与调参也为结合目标检测、聚类与跟踪实现人流统计提供了可参考的实现路径。1. 300 张手机人脸图够不够训一个性别分类器先说结论如果你打算从零训一个 ResNet 级别的性别分类网络300 张不够但如果你要做的是「在已有 backbone 上做二分类微调」或者「验证一条性别检测流水线能不能跑通」这份按 woman / man 分好类的 300 张真实手机采集人脸图反而是个省事的起点。它解决的不是「数据量」问题而是「数据脏」问题——真实手机拍摄意味着光照、角度、肤色、遮挡都带着生活气息不是实验室摆拍那种干净到失真的样本这对模型泛化能力的影响比多堆几千张网图更实在。这份资源的核心价值在于「已分类 已标注 真实场景」三件事同时成立。目录里 woman 和 man 两个子集直接对应二分类标签省掉了你自己写脚本按文件名分桶的功夫图片来自手机采集意味着分辨率、压缩噪声、白平衡漂移这些真实干扰都在训出来的模型不会一上手机就翻车。它适合两类人一是刚入门深度学习、想找一个能完整跑通「数据加载 → 人脸检测 → 特征提取 → 性别分类」链路的小数据集二是手里已经有 SSD、MTCNN 这类检测器想快速验证性别分支效果、不想在数据清洗上耗时间的工程师。配套的 config 文件列表ssd_mobilenet_v2、ssdlite_mobilenet_v1/v2、ssd_inception_v2 等说明这套资源原本是挂在 TensorFlow Object Detection API 体系下用的所以下面我按这个技术栈来讲PyTorch 用户也能照着思路平移。2. 从目录结构到 TensorFlow Record把 300 张图喂进 SSD 流水线2.1 先看清手里有什么目录、config 与标签约定拿到资源后别急着写训练脚本先花五分钟把目录结构和 config 文件对一遍。典型布局是根目录下两个分类文件夹外加一组.config文件。woman 和 man 这两个文件夹名本身就是标签来源但要注意文件夹名不等于模型输出的类别索引索引顺序取决于你生成 label map 时怎么排。我一般会先固定一个label_map.pbtxt把 woman 设为 1、man 设为 2或者反过来然后全程不再改避免训练到一半发现标签错位。config 文件那一串名字看着多其实分两类ssd_mobilenet_v2_quantized_320x320_open_image_v4.config这类是量化版适合后续部署到算力受限的设备ssd_mobilenet_v1_coco.config、ssd_inception_v2_coco.config是常规训练配置。选哪个取决于你的目标——如果只是验证性别分类能不能收敛用ssd_mobilenet_v2_coco.config改一改最快如果最终要上手机直接拿量化版 config 起步省得后期再折腾量化感知训练。这里有个容易忽略的点config 里的num_classes默认是 90COCO 类别数你必须改成 2否则训练时分类头维度对不上报错信息还不一定直白。提示动手前先把 woman / man 两个文件夹里的图片数量点一遍确认加起来是 300 张左右。如果数量对不上先排查是不是有隐藏文件或缩略图混进去了这种脏数据在生成 TFRecord 时不会报错但会悄悄拉低模型效果。2.2 生成 TFRecord脚本、参数与两个必改项TensorFlow Object Detection API 训练 SSD 系列模型第一步是把图片和标注转成 TFRecord。这份资源已经做好了分类划分所以标注信息可以直接从文件夹名推导不需要额外的 XML 或 JSON。下面这个脚本是我常用的写法逻辑是遍历两个分类文件夹把每张图的路径、类别、尺寸写进 TFRecord。import os import io import tensorflow as tf from PIL import Image # 两个分类文件夹文件夹名即类别名 CLASSES [woman, man] DATA_DIR ./dataset # 数据集根目录 OUTPUT_DIR ./tfrecord # TFRecord 输出目录 def create_tf_example(image_path, class_id): with tf.io.gfile.GFile(image_path, rb) as fid: encoded fid.read() image Image.open(io.BytesIO(encoded)) width, height image.size # 整张图作为一个人脸区域bbox 归一化到 [0,1] # 如果后续要接检测任务这里应替换为真实人脸框 xmin, ymin, xmax, ymax 0.0, 0.0, 1.0, 1.0 feature_dict { image/height: tf.train.Feature( int64_listtf.train.Int64List(value[height])), image/width: tf.train.Feature( int64_listtf.train.Int64List(value[width])), image/filename: tf.train.Feature( bytes_listtf.train.BytesList(value[os.path.basename(image_path).encode(utf8)])), image/source_id: tf.train.Feature( bytes_listtf.train.BytesList(value[os.path.basename(image_path).encode(utf8)])), image/encoded: tf.train.Feature( bytes_listtf.train.BytesList(value[encoded])), image/format: tf.train.Feature( bytes_listtf.train.BytesList(value[bjpeg])), image/object/bbox/xmin: tf.train.Feature( float_listtf.train.FloatList(value[xmin])), image/object/bbox/ymin: tf.train.Feature( float_listtf.train.FloatList(value[ymin])), image/object/bbox/xmax: tf.train.Feature( float_listtf.train.FloatList(value[xmax])), image/object/bbox/ymax: tf.train.Feature( float_listtf.train.FloatList(value[ymax])), image/object/class/label: tf.train.Feature( int64_listtf.train.Int64List(value[class_id])), } return tf.train.Example(featurestf.train.Features(featurefeature_dict)) def main(): os.makedirs(OUTPUT_DIR, exist_okTrue) writer tf.io.TFRecordWriter(os.path.join(OUTPUT_DIR, gender.record)) count 0 for idx, cls in enumerate(CLASSES, start1): cls_dir os.path.join(DATA_DIR, cls) for fname in os.listdir(cls_dir): if not fname.lower().endswith((.jpg, .jpeg, .png)): continue path os.path.join(cls_dir, fname) example create_tf_example(path, idx) writer.write(example.SerializeToString()) count 1 writer.close() print(f共写入 {count} 条样本) if __name__ __main__: main()这段脚本有两个地方必须按你的实际情况改。第一CLASSES的顺序决定了class_idwoman 在前就是 1、man 是 2这个顺序要和后面 label map 完全一致否则模型学出来的「1」和你想的不是一回事。第二bbox 我暂时写成了整图[0,0,1,1]因为这份资源是分类数据集没有逐张的人脸框标注。如果你要拿它训 SSD 检测器得先用 MTCNN 或 OpenCV 的人脸检测器把每张图的人脸框跑出来再回填到xmin/ymin/xmax/ymax四个字段里否则检测头学不到有效定位。常见做法是先用cv2.CascadeClassifier或mtcnn批量生成框存成 CSV再在脚本里读进来替换那四个变量。2.3 改 confignum_classes、fine_tune_checkpoint 与 batch sizeTFRecord 生成后复制一份ssd_mobilenet_v2_coco.config出来改。需要动的字段不多但每个都关键字段默认值改成原因num_classes902只有 woman / man 两类fine_tune_checkpoint空预训练 ckpt 路径300 张图从零训必过拟合train_input_reader.input_pathCOCO record你的 gender.record指向刚生成的 TFRecordlabel_map_pathCOCO label map你的 label_map.pbtxt类别索引要对齐batch_size248 或 16300 张图大 batch 梯度噪声大fine_tune_checkpoint是这份小数据集能不能训出东西的分水岭。300 张图如果从随机初始化开始模型大概率记住每一张而不是学特征验证集准确率会在 50% 附近晃。挂上 COCO 预训练的 SSD MobileNet V2 权重后backbone 已经会提通用特征你只需要微调分类头收敛快且稳。batch_size调小是因为样本少一个 epoch 才 300 张batch 24 意味着每个 epoch 只有十几个 step梯度更新次数太少反而学不动。注意label_map.pbtxt里id必须从 1 开始不能从 0 开始这是 TF OD API 的历史约定。写成 0 会导致训练时类别索引越界或静默错位排查起来很费时间。3. 训练、评估与导出300 张图上的收敛判断与踩坑记录3.1 启动训练与观察 loss 曲线config 改好后训练命令本身不复杂# 在 TensorFlow Object Detection API 根目录下执行 python model_main_tf2.py \ --pipeline_config_path./configs/ssd_mobilenet_v2_gender.config \ --model_dir./training/gender_model \ --num_train_steps5000 \ --alsologtostderrnum_train_steps设 5000 是我在 300 张图上的经验值。太少比如 1000分类头还没学稳太多比如 20000就开始过拟合验证 loss 会先降后升。判断收敛不要只看 total loss要同时看Loss/classification_loss和Loss/localization_loss——分类数据集里定位 loss 意义不大重点盯分类 loss 是否降到 0.1 以下并趋于平缓。如果分类 loss 卡在 0.6 以上不动八成是 label map 和 TFRecord 里的 class_id 没对齐或者num_classes忘了改。评估用model_main_tf2.py加--checkpoint_dir参数跑或者单独用eval.py。300 张图建议按 8:2 切训练和验证别全拿去训。切分时注意 woman 和 man 要各自按比例切不能随机切完发现验证集里全是 man那样评估指标没有参考意义。3.2 导出 frozen graph 与推理验证训练到验证准确率稳定后导出推理图python exporter_main_v2.py \ --input_typeimage_tensor \ --pipeline_config_path./configs/ssd_mobilenet_v2_gender.config \ --trained_checkpoint_dir./training/gender_model \ --output_directory./exported/gender_frozen导出后在saved_model目录下会得到可直接加载的模型。推理时把图片 resize 到 config 里指定的 320x320 或 300x300归一化到 [0,1]送进模型拿detection_classes和detection_scores。这里有个实际使用中的细节因为训练时 bbox 是整图模型输出的框会覆盖整张图你只需要取分数最高的类别作为性别判断即可不用太在意框的位置。如果分数普遍偏低比如最高才 0.5说明训练不充分或验证集和训练集分布差异大回头检查切分是否随机、图片是否有损坏。3.3 避坑记录300 张图训练时最容易翻车的四件事现象一训练 loss 从第一步就是 nan。原因通常是学习率太高或者 TFRecord 里有尺寸为 0 的损坏图片。解决方法是把 config 里learning_rate_base从默认的 0.08 降到 0.01 甚至 0.005同时用 PIL 批量打开所有图片做一次完整性检查把打不开的删掉重新生成 TFRecord。现象二验证准确率一直在 50% 左右像在瞎猜。这是二分类最典型的「标签没对上」症状。原因可能是 label map 里 woman 和 man 的 id 与 TFRecord 生成时的class_id顺序相反也可能是评估脚本读错了 label map。解决方法是拿一张已知是 woman 的图单独跑推理看输出类别 id 是不是 1不是就回头对一遍两个文件的顺序。现象三训练能跑但导出模型后推理报维度错误。多半是导出时用的 config 和训练时不一致比如训练改了num_classes2导出却用了原始 COCO config。解决方法是导出前把 config 再 diff 一遍确认num_classes、input_size和训练时完全一致。现象四模型在训练集上准确率 95%换一批新图就掉到 60%。300 张图的多样性终究有限过拟合是常态。缓解手段包括加数据增强随机裁剪、亮度抖动、水平翻转把fine_tune_checkpoint换成更贴近人脸域的预训练权重或者干脆把这份数据当验证集去补充更多同分布图片再训。别指望 300 张图训出一个能上生产的模型它的定位是验证链路和快速原型。4. 从二分类到人流统计把性别分支接进检测跟踪链路这份资源的摘要里提到人流统计——Faster R-CNN 检测、Mean-shift 聚类、卡尔曼滤波跟踪。单看 300 张静态图当然做不了人流统计但性别分类分支可以挂到那条链路上检测器负责逐帧找人脸或人体跟踪器负责给每个人分配稳定 ID性别分类器只在 ID 首次出现时跑一次把结果缓存下来后续帧直接复用。这样既省算力又避免同一人性别在帧间跳变。具体做法是用 SSD 或 Faster R-CNN 做人体检测把每个人体框裁出来送进你刚训好的性别分类模型得到 woman / man 标签后写进跟踪器的状态字典。卡尔曼滤波负责预测下一帧位置Mean-shift 负责在特征空间里做聚类关联。这里的关键参数是「性别判定阈值」——我一般设 0.7低于这个分数就不写标签等后续帧分数上来再补避免误判污染统计结果。验证方法也简单找一段多人走过的视频跑完统计男女人数和肉眼计数对比误差在 10% 以内就算这条链路可用。从那以后我每次拿到小规模分类数据集都会先跑一遍「单图推理 标签对齐检查」再开训练这个习惯帮我省掉了至少三次通宵排查标签错位的血泪经验。希望这份 300 张图的资源和上面的流程能帮你把性别检测这条链路先跑通再谈优化。本文还有配套的精品资源点击获取
返回列表