简介这份压缩包面向计算机视觉开发者和研究人员提供基于PaddleDetection的人脸检测与情绪识别完整模型资源适用于人机交互、智能安防、广告推荐等场景。包内包含检测模型配置、训练与推理脚本、文档说明等可帮助读者快速上手人脸定位及快乐、悲伤、愤怒、惊讶、恐惧、厌恶、中立七类基本情绪分类任务。资源共1748个文件涵盖Python脚本、YAML配置文件、预训练参数、Markdown文档及图片样例等压缩包整体约603MB目录结构清晰适合在飞桨框架下开展二次开发或微调。检测部分覆盖YOLO、SSD、Faster R-CNN等常用模型情绪识别部分提供基于VGG、ResNet等架构的预训练模型并附有示例代码与部署相关文件便于从数据准备到模型预测的完整流程实践。目前已有373人学习下载适合需要快速落地视觉方案的开发者参考。1. 人脸检测和情绪识别模型 PaddleDetection.zip为什么一个压缩包装着两套模型做视频分析类项目的开发者很容易被人脸检测和情绪识别这两个词同时吸引又同时在落地时卡住PaddleDetection 能输出的只是「人脸框」框里的人到底是开心、生气还是走神它给不出答案。这个 zip 包把两件事装在了一起解压后通常能看到检测模型目录、情绪分类模型目录和各自配套的推理脚本。适合谁适合手里有摄像头数据、想在本地把「先检到脸、再判断表情」这条链路跑通的人。如果你只是想搜一个现成的 API这个包不解决你的问题如果你想自己掌控模型、数据和阈值这套东西值得花时间解包。2. 先跑通人脸检测PaddleDetection 环境与最小推理命令2.1 模型选型人脸检测不是拿个通用检测模型就行PaddleDetection 里能做人脸检测的模型不止一个典型的有 PyramidBox 系列和 BlazeFace。很多人上来就挑 map 最高的模型这个思路在小目标人脸上会翻车。PyramidBox 的 640 输入版在小脸和中脸场景上表现稳但模型体积大、推理慢BlazeFace 轻快适合边缘盒子但密集人群里漏检率会明显高。我一般这样选型单路摄像头、画面里人脸小于 40 像素优先 PyramidBox 640多路视频流或嵌入式部署选轻量模型并把输入分辨率固定在 320 或 416。PaddleDetection 的配置文件把 backbone、neck、head 和 anchor 参数都写在 yml 里换模型不等于换整个代码改-c指向的配置文件就行。解压 zip 后先别急着改参数看目录里给了哪个检测模型的 inference 文件。.pdmodel结尾的是静态图模型可以直接给 Paddle Inference 用如果只有.pdparams训练权重就得走一步模型导出。常见的包会两者都给推理脚本默认读 inference 文件训练权重放在weights/下备用。2.2 最小推理一张图的检测命令和输出解读先不碰训练把检测跑通。假设 zip 包解压到PaddleDetection/目录里面带了一份训练好的检测权重。用自带预测脚本跑一张测试图命令是这样的python tools/infer.py \ -c configs/pyramidbox/pyramidbox_640.yml \ -o weightsweights/face_det.pdparams \ --infer_imgtest_face.jpg \ --output_diroutput/ \ --draw_threshold0.6这条命令里-c指定模型配置-o里的weights覆盖配置文件里的权重路径--draw_threshold控制画框时保留多少置信度以上的目标。跑完去output/目录看图检测框画得过于密就把阈值往上调到 0.7一个人脸上出现好几个框多半是 NMS 没把重复框压干净。如果包里的推理脚本不是tools/infer.py而是单独一个demo_detect.py命令形式会不同但套路一样读图、前向推理、后处理画框。注意看入参里有没有--use_gpuCPU 环境下不显式关掉Paddle 会尝试初始化 CUDA 并报错。提示--infer_img只认图片路径想测摄像头得用--infer_video。包里的脚本不一定实现了视频输入检查一下再动手。3. 解包落地从训练权重到可部署 inference 模型的转换3.1 模型导出从 pdparams 到 pdmodel 的转换zip 包里如果只给了.pdparams训练权重推理环境就加载不了。PaddleDetection 的训练产物是动态图参数部署时要先导出成静态图模型。常见做法是用套件自带的导出脚本python tools/export_model.py \ -c configs/pyramidbox/pyramidbox_640.yml \ -o weightsweights/face_det.pdparams \ --output_dirinference_model导出成功后inference_model/下会生成model.pdmodel、model.pdiparams和infer_cfg.yml三个文件。infer_cfg.yml里的draw_threshold是默认画框阈值use_dynamic_shape控制输入尺寸是否可变。导出时如果报 shape 相关错误多半是配置里用了固定 scale 的 anchor 生成逻辑把TestReader里的image_shape改成固定值就好了。导出后最好验证一下模型能不能正常推理。用 Paddle Inference 加载的方式和训练完全不同写一个极简验证脚本import paddle.inference as paddle_infer config paddle_infer.Config( inference_model/model.pdmodel, inference_model/model.pdiparams) config.enable_memory_optim() predictor paddle_infer.create_predictor(config) print(inference model loaded)这段代码先构造推理配置再创建 predictor。enable_memory_optim()打开显存优化部署时建议保留如果是在内存紧张的老机器上加了反而可能抖动到时候去掉再看。3.2 阈值设置人脸置信度、NMS、类别过滤跑通导出只是第一步真正影响检测质量的参数有三个置信度阈值、NMS 阈值、输入尺寸。PaddleDetection 的推理脚本通常支持从命令行覆盖这些配置项。置信度阈值低了会把背景当成人脸画面上到处是框高了人脸稍微侧一点就漏检。做实时情绪识别时我的经验是把检测置信度放到 0.65~0.75理由是后续情绪分类模型吃的是人脸图块检测框稍有偏移切出来的图就不准宁缺毋滥。python tools/infer.py \ -c configs/pyramidbox/pyramidbox_640.yml \ -o weightsweights/face_det.pdparams \ --infer_imggroup_face.jpg \ --draw_threshold0.7 \ --nms_threshold0.45nms_threshold大一点重复框保留得多小一点重叠的人脸可能被误删。密集人群场景建议 0.4~0.5单人场景 0.5 即可。这两个参数属于典型的「改一个不够要配着改」阈值调高了漏检调低了误检NMS 再跟着收紧整体效果才会收敛。类别过滤这个坑最容易忽略。PyramidBox 这类人脸模型输出只有一个类别没有过滤问题但如果你拿通用检测模型来跑人脸输出里会混进 person、cat 之类的东西。包里的推理脚本一般靠infer_cfg.yml里的labels列表判断类别名跑出来的框不对先看这个文件里的类别表再决定是改配置还是加一行类别白名单过滤。4. 情绪识别怎么接从检测框到分类结果的完整推理链路4.1 情绪识别为什么要单独做检测头与分类头的分工有人问能不能直接在 PaddleDetection 的人脸检测模型后面加一个情绪分类头技术上可行但工程上不建议。检测和分类的监督信号完全不同检测头要学「框住脸」情绪分类要学「脸里面的表情是什么」。硬合成一个多任务模型需要同时标注框和情绪标签数据成本翻倍而且检测数据里的表情分布未必均匀训练容易失衡。我见过某组件化方案就是把两个模型分开部署检测模型只出框情绪模型只吃框。两边可以各自换模型、单独调阈值排查问题时边界也清晰。检测模型还可以换成任意轻量模型情绪模型那边完全不受影响。这个「两个模型串一条链路」的架构比单模型自适应性强得多。情绪分类模型普遍做法是轻量分类网络输入人脸图块输出 7 类概率——生气、厌恶、恐惧、开心、伤心、惊讶、中性。zip 包里的情绪模型目录通常就包含这 7 类的标签映射文件。拿到的第一件事不是跑图而是打开标签文件确认分类顺序否则后面画标签时很容易张冠李戴。4.2 检测结果切块与情绪分类推理脚本把两个模型串起来的核心代码是检测模型输出框坐标按框从原图裁剪人脸图块再送进情绪模型。以下是用 Paddle Inference 同时加载两个模型的推理脚本骨架import cv2 import numpy as np import paddle.inference as paddle_infer def load_predictor(model_path, params_path): config paddle_infer.Config(model_path, params_path) config.enable_memory_optim() return paddle_infer.create_predictor(config) def run_face_detect(predictor, image): input_names predictor.get_input_names() input_handle predictor.get_input_handle(input_names[0]) input_handle.copy_from_cpu(image[np.newaxis, :, :, :]) predictor.run() output_names predictor.get_output_names() output_handle predictor.get_output_handle(output_names[0]) return output_handle.copy_to_cpu() det_predictor load_predictor(face_det/model.pdmodel, face_det/model.pdiparams) emo_predictor load_predictor(emotion/model.pdmodel, emotion/model.pdiparams) img cv2.imread(test_face.jpg) rgb_img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) boxes run_face_detect(det_predictor, rgb_img) for box in boxes: x1, y1, x2, y2, score int(box[0]), int(box[1]), int(box[2]), int(box[3]), box[4] if score 0.65: continue face_crop rgb_img[y1:y2, x1:x2] face_resized cv2.resize(face_crop, (112, 112)) emotion_probs run_face_detect(emo_predictor, face_resized) emotion_id np.argmax(emotion_probs[0]) print(fface at ({x1},{y1}) score{score:.2f} emotion{emotion_id})这段代码有四个关键点。第一检测模型输出格式是[N, 6]每行分别是类别 id、置信度、左上角 x/y、右下角 x/y实际顺序可能因导出配置不同而变先打印一行的内容确认。第二裁剪坐标直接用检测框的原始像素坐标但输入给检测模型时图片可能被 resize 过如果推理脚本内部改了尺寸框坐标要按缩放比例换算回原图。第三情绪模型输入尺寸要跟训练时一致训练用的 112 就 resize 到 112换尺寸等于换数据分布准确率直接掉。第四run_face_detect函数名是我临时起的情绪模型的前向调用和检测模型完全一致都是 copy 数据、run、取输出所以复用同一个函数没问题。帧率问题是串行推理最大的隐患。检测模型跑一帧可能要 30ms情绪模型又要 20ms加起来 50ms勉强能到 20FPS。多路摄像头同时跑就直接掉到个位数。常见做法是检测不每帧都跑每隔 5 帧检测一次中间帧沿用最近一次检测框。这样检测耗时摊薄情绪模型每帧照跑CPU 占用能降一半以上。注意检测框偶尔会跳到背景上情绪模型照样会输出一个结果。接线上游最好加一个「检测框面积变化超过 30% 不采信」的规则比单纯调阈值省心。5. 落地避坑5 个 PaddleDetection 实战中反复踩到的问题5.1 模型加载报错把 inference model 当训练模型取用现象直接拿.pdparams文件传给paddle.jit.load报错说不认识这个参数结构或者加载成功但推理结果全零。原因训练权重和推理模型是两种格式。.pdparams是动态图参数只存权重推理模型是静态图结构加权重必须成对使用.pdmodel和.pdiparams。解决先跑一遍第 3 章的导出命令把.pdparams转成inference_model/目录再加载。这个坑基本每个新手都会踩一次看到 zip 里只有训练权重时别急着改脚本先补导出这一步。5.2 小脸漏检anchor 与输入分辨率不匹配现象一张多人合影正中间的人脸检测出来了角落里两三张侧脸根本没框把图放大后同样位置又能检到。原因输入分辨率太低时小脸对应的像素区域太小anchor 覆盖不到。PaddleDetection 的 anchor 是按配置文件里输入尺寸设计的输入 320 时最小 anchor 只能框住约 40 像素的目标。解决把输入尺寸提到 640 或者 768检测置信度相应放宽 0.05。代价是推理耗时上涨实测单帧耗时可能从 20ms 涨到 60ms。如果场景固定是会议室摄像头可以试试裁掉无用区域再放大比整体放大更划算。5.3 情绪标签闪烁光照变化和单帧噪声现象同一个人坐在同一个位置情绪标签在开心和中性之间来回跳一分钟能跳十几次。原因情绪分类模型是单帧输入光照变化、头部轻微转动都会造成分类概率明显变化。argmax 取最大概率时两个类别的概率只要差 0.01 就会换标签。解决给情绪输出加滑动窗口。维护一个长度为 5 的队列取最近 5 帧概率平均后再取 argmax。这个方法对摄像头场景的提升很明显实现成本只有一段 20 行的代码。要再稳一点就把窗口拉长到 10但表情变化时反应会慢半拍看业务取舍。5.4 CPU 推理掉帧MKLDNN 与输入尺寸的配合现象代码在 GPU 机器上跑得好好的换到纯 CPU 的服务器上推理耗时翻了 10 倍画面卡成幻灯片。原因模型没有针对 CPU 做优化。Paddle Inference 在 CPU 上默认不开 MKLDNN卷积算子走的通用实现速度远不如优化后的核。解决给推理配置加一行config.enable_mkldnn()再搭配config.set_cpu_math_library_num_threads(4)。实测常见检测模型能提速 2 到 4 倍。如果还卡就把输入分辨率从 640 降到 416并在情绪模型侧把输入从 112 降到 96。注意 MKLDNN 对部分算子支持不完整开启后如果出现精度异常先关掉再排查。5.5 检测框边界溢出人脸图块超出图像范围现象情绪识别前裁剪人脸时程序报index out of bounds或者切出来的图片边缘是黑的。原因检测模型输出的框坐标可能超出原图边界比如人脸在画面边缘时框的右下角坐标比图片尺寸大。解决裁剪之前做一次 clip。用x1 max(0, x1)、y2 min(image_height, y2)这样的方式把框限制在图像范围内。这个代码写起来很简单但很多人会忘而且只在部分图上触发属于典型的「测试时没事、上线就崩」的问题。6. 用一个视频脚本验证整套链路检测框、情绪标签和耗时一起看6.1 端到端验证脚本单张图片验证不了稳定性我习惯把检测加情绪识别直接接到视频上跑一段。写一个简单脚本输出每帧的检测框数量、情绪标签和平均耗时用这个指标判断模型在真实场景里能不能用。import cv2 import time cap cv2.VideoCapture(test_video.mp4) face_count_total 0 frame_count 0 time_start time.time() while True: ret, frame cap.read() if not ret: break boxes run_face_detect(det_predictor, frame) face_crops [crop_face(frame, b) for b in boxes if b[4] 0.65] emotions [np.argmax(run_face_detect(emo_predictor, c)[0]) for c in face_crops] for emotion_id in emotions: print(fframe {frame_count}: emotion {emotion_id}) frame_count 1 face_count_total len(boxes) if frame_count 100: break avg_time (time.time() - time_start) / frame_count print(favg inference time per frame: {avg_time * 1000:.1f} ms)看结果时重点看两个数字平均耗时和情绪标签跳变次数。耗时超过 100ms 说明部署环境扛不住考虑降分辨率或改轻量模型。标签跳变率超过 20%别急着调模型先检查检测框稳不稳——框一抖情绪跟着乱。我后来在项目里养成的习惯是每换一个场景先跑一个 30 秒的 demo 视频把情绪标签的变化率打印出来而不是只盯着单张准确率。单张准确率高不代表场景里可用标签跳变才是上线前最该解决的事。希望帮到你。本文还有配套的精品资源点击获取