
简介适用于浏览器端人脸识别开发的 face-api.js 预训练模型包面向需要使用人脸检测、关键点定位、表情识别、年龄性别预测及人脸比对等功能的 JavaScript 开发者。资源共包含 18 个文件以 json 权重清单和多个 shard 分片模型文件为主按任务类型覆盖 ssd_mobilenetv1、tiny_face_detector、face_landmark_68 及 tiny 版本、mtcnn、face_expression、age_gender、face_recognition 等常见模型整包约 10.33MB无需额外下载即可直接配套 face-api.js 使用。目前已有 2118 人学习下载。压缩包内模型文件与权重清单一一对应加载时只需按官方 API 调用 loadFaceDetectionModel、loadFaceLandmarkModel 等即可读取模型选择涵盖精度与速度不同档位便于开发者兼顾实时性与准确率。对于希望在浏览器端快速实现人脸识别功能、又不想从零训练模型的 Web 开发者来说这份资源能省去繁琐的模型收集与配置环节直接进入业务开发。 上个月帮朋友调一个浏览器端的人脸考勤 Demo前端代码看着都通了卡在模型加载上整整一下午face-api.js 在控制台报错加载不到 weights manifest。后来才发现根本不是识别逻辑的问题而是模型目录没有放到静态资源能访问的位置。这个库就是这么个脾气——它本身只是外壳真正干活的是挂在faceapi.nets下面的几组神经网络你少放一个权重文件或者路径没写对整个页面就停在空白状态。这篇文章我按实际踩坑顺序把 face-api.js 的模型使用完整拆一遍适合刚接触 face-api.js 的前端同学也给被路径、阈值、识别精度折磨过的人做个参考。不管你是想做人脸登录、表情统计还是视频流里的陌生人识别这套模型的使用逻辑基本一致跑通一次就能套到大部分项目里。1. 模型文件怎么放loadFromUri 的路径规则与部署细节先说一个很多人搞错的概念face-api.js 的“模型”并不是打包在 JS 库内部而是以一组权重文件的形式跟着 npm 包一起分发。你安装face-api.js后node_modules/face-api.js/weights里存的才是模型本体调用loadFromUri或loadFromDisk只是把这些二进制权重加载进 TensorFlow.js 运行时。所以你的第一步不是写 API而是先把权重文件“搬”到静态资源能访问的目录里。1.1 模型清单哪些权重是必须的face-api.js 官方和社区常用的模型大致分六类模型对应网络作用价值轻量检测模型tinyFaceDetector快速检测人脸框实时视频场景首选精确检测模型ssdMobilenetv1更稳重的人脸检测多人、小脸、复杂背景更稳关键点模型faceLandmark68Net输出人脸 68 个关键点用于对齐、姿态估计、表情分析识别模型faceRecognitionNet生成 128 维人脸特征向量判断“是不是同一个人”表情模型faceExpressionNet输出表情分类概率表情识别业务年龄性别模型ageGenderNet预测年龄和性别统计类分析对于基础的人脸识别业务真正必需的是一个检测模型、一个关键点模型、一个识别模型。表情、年龄性别属于附加依赖需要时再用loadFromUri加载不需要就别加载因为每个模型都会带额外的加载时间和推理开销。1.2 复制权重与路径解析如果你用 Vite 或 Vue CLI静态目录是public如果用 Create React App也是public如果用原生 Webpack则需要通过 copy 插件把权重目录送到dist根目录。把node_modules/face-api.js/weights整个目录复制到项目根目录的public/models是最常见的做法mkdir -p public/models cp -r node_modules/face-api.js/weights/* public/models/然后加载await faceapi.nets.tinyFaceDetector.loadFromUri(/models); await faceapi.nets.faceLandmark68Net.loadFromUri(/models); await faceapi.nets.faceRecognitionNet.loadFromUri(/models);这里三个小坑我全踩过loadFromUri(/models)里的斜杠开头是相对站点根的绝对路径。一旦前端路由变成多级比如/admin/manage你写./models就会解析成/admin/models直接 404。复制权重时不要只复制.json文件。weights_manifest.json只是索引真正的参数都在同名的 shard 文件里。漏了 shard浏览器会报“找不到 shard”的错。用 Node.js 做后端推理时改用loadFromDisk传入文件系统路径而不是 URL。浏览器环境和 Node 环境的加载姿势不一样别混用。这些路径细节看似基础但实际项目里 90% 的“模型加载失败”根因都在这里。所以每次出问题优先检查目录而不是急着改识别代码。2. 检测到识别一条流水线三个模型如何串联使用加载完模型之后真正调用的方式比命名看起来简单但背后的流水线顺序对理解识别结果很重要。通常我们写的代码是这样const detections await faceapi .detectAllFaces(video, new faceapi.TinyFaceDetectorOptions({ inputSize: 416, scoreThreshold: 0.5 })) .withFaceLandmarks() .withFaceDescriptors();这段代码看着只有一句话实际上执行了三层推理首先检测模型扫描整帧图片输出若干个人脸框。接着关键点模型在每个框内定位 68 个关键点比如眼睛、鼻尖、嘴角位置。最后识别模型根据对齐后的人脸区域生成 128 维特征向量。为什么要先经过关键点模型再做识别因为人脸识别对“人脸是否对齐”非常敏感。一张侧脸照片和一张正脸照片如果直接拿原始像素做特征提取脸的角度差异会掩盖长相差异。face-api.js 内部会用 68 个关键点做人脸仿射变换把眼睛、鼻子、嘴巴拉到一个相对标准的位置再交给识别模型。所以.withFaceDescriptors()的前置条件一定是.withFaceLandmarks()你不加关键点模型识别就无从谈起。2.1 检测模型选型tinyFaceDetector 还是 ssdMobilenetv1很多新手会在这两个模型之间纠结我给个实际对比维度tinyFaceDetectorssdMobilenetv1模型量级小适合首屏快速加载大需要更多加载时间推理速度快适合实时视频较慢但识别边界更稳小脸和密集人群容易漏检相对更稳适用场景摄像头实时、移动端离线照片、高精度场景我的经验是网页端做“摄像头实时识别”无脑选 tinyFaceDetector。它牺牲了极端情况下的精度但换来的是在普通笔记本上也能跑出流畅效果。如果你在处理离线照片审核或者希望尽可能把画面角落的小脸也框出来再考虑 ssdMobilenetv1。另外tinyFaceDetector 的inputSize是用来控制内部扫描分辨率的我一般从416起步。输入尺寸越大越容易检测小脸但推理时间会上升。建议准备一组样图做 AB 测试不要盲调参数。2.2 关键点模型的不可替代性很多人以为关键点模型只为了画 68 个点做可视化这低估了它的作用。它既是识别前的人脸对齐基础也是过滤无效人脸的重要手段。比如一张过曝、完全看不到五官的脸关键点模型输出的置信度会掉得很厉害你可以用它来判断“这不是一张合格的人脸”。在注册人脸库的时候我会对关键点置信度做阈值判断过滤掉低头、闭眼、侧脸等样本之后再让特征向量进入库整体精度才会稳。这个思路不仅适用于 face-api.js其他深度模型做人脸识别时也同样成立。3. 看懂输出数字特征向量、阈值和相似度识别模型输出的 descriptor 是 128 个浮点数组成的数组。你没办法直接看懂它但本质上它代表了一张脸的数学抽象。判断两个人是不是同一个标准做法是算两个描述子之间的欧氏距离。距离越小长相越接近距离超过阈值就当成不是同一人。face-api.js 也封装好了匹配器const labeledDescriptors [ new faceapi.LabeledFaceDescriptors(张三, [descriptorA, descriptorB]), new faceapi.LabeledFaceDescriptors(李四, [descriptorC]), ]; const matcher new faceapi.FaceMatcher(labeledDescriptors, 0.6); for (const { descriptor } of detections) { const result matcher.findBestMatch(descriptor); console.log(result.label, result.distance); }这里label是“匹配到的人名”distance是未知描述子和已知描述子之间的最小距离。FaceMatcher 的第二个参数是距离阈值小于阈值才认为匹配成功所有距离都大于阈值时label会返回unknown。3.1 阈值别抄作业要做距离分布实验face-api.js 的文档里经常出现 0.6 这个默认值实际上它不等于“任何项目都合适”。0.6 是比较宽松的阈值适合“同一人多次识别的场景”比如固定机位考勤但它也会提高误识率——不同人如果长得像距离可能也低于 0.6。我的做法是提前采集两组数据一组是同一人的各角度照片一组是不同人的对比照片分别计算距离画出距离分布。从两组数据的交界区域选阈值才能兼顾误拒和误识。像门禁、支付这类风险偏高的业务我会把阈值压到 0.45 甚至更低只是做表情签到 Demo0.55 到 0.6 就很顺手。3.2 量化加载loadFromUri 的第二个参数face-api.js 的 loadFromUri 支持一个量化开关await faceapi.nets.faceRecognitionNet.loadFromUri(/models, true); // 或 await faceapi.nets.tinyFaceDetector.loadFromUri(/models, true);第二个参数为true加载的是经过 int8 量化后的权重体积小很多移动端加载和解码更快。代价是权重精度下降输出向量可能发生轻微抖动识别阈值需要重新按上面的方法测一遍。我的态度是面向用户的产品“弱网下能快速把模型加载完”这件事往往比那一点点识别精度更影响体验。所以我会优先用量化版本再用真实业务数据验证阈值如果发现误识明显再换回非量化模型对比。4. 模型加载失败排查404、跨域和重复加载加载模型出问题一般不是“模型坏了”而是工程环境没配好。遇到控制台报错先别急着改代码按下面这条链路排查。4.1 从控制台报错顺藤摸瓜假设页面控制台冒出这样的网络错误Failed to fetch resource: http://localhost:8080/models/face_recognition_model-weights_manifest.json顺序做四件事先在 Network 面板里过滤model确认哪个 URL 返回 404。如果 URL 里的路径和你预期的目录不一致多半是相对路径被路由吞了。改成站点根绝对路径或者通过构建工具的 base 配置拼出完整路径。检查静态目录中是否有这个文件。很多人把权重放在src/models下但这里不会被构建工具处理服务端根本访问不到。Vite 和 CRA 的默认静态目录都是public要把权重放进去。打开weights_manifest.json看paths字段。这个文件记录的是 shard 文件名如果复制时改了目录层级模型同样会加载失败。检查跨域和权限。如果模型放在第三方 OSS 或 CDN浏览器会在加载阶段直接拦截报 CORS 错误。这是“本地好好的一上生产就挂”的高发原因。4.2 重复加载模型和内存增长的坑还有一个很隐蔽的问题路由本文还有配套的精品资源点击获取