Spike: CPR 按压手位检测准确率实测
Spike ID: 002 日期: 2026-06-18(创建),2026-07-22(Simulator 预验证) 状态: Simulator 多图预验证未通过准确率门;Qwen3-VL-4B 已选为固定图片切片,CPU 软件门通过、Metal 失败;已补全智评 production 单来源示范帧预验证,人工标注准确率门仍未执行
假设
待验证假设:固定版本 Qwen3-VL-2B-Instruct-MNN 能否在有人工标签的 CPR 练习图片集上可靠判断 hand_position_correct。目标阈值不能用 spike-001 的单图输出或 JSON 合法率替代。
计划中的正式方法
- 收集 20 张经 CPR 专业人员标注的模拟人练习照片(10 张手位正确、10 张手位偏移)
- 固定模型、prompt、图片校验值和运行后端,逐张保存原始 observation
- 计算 precision / recall / F1,并单列
unknown和非法 JSON - 覆盖俯拍/侧拍、光照、遮挡、手套/无手套条件
本轮没有取得满足以上条件的数据集,因此没有计算准确率。以下结果只是正式门之前的失败预验证。
环境与来源锁定
| 项目 | 值 |
|---|---|
| 运行环境 | iPhone 17 Pro / iOS 26.2 Simulator,arm64 |
| 后端 | 候选多图筛选使用 MNN CPU;选定 4B 后在收尾阶段补跑一次 Metal,结果见 G 节 |
| MNN | 3.6.0 / cc20f672af9e177e2fa338c332dc097de2fc9264 |
| 模型 | taobao-mnn/Qwen3-VL-2B-Instruct-MNN / 9e49ec71ded22500a997ed0f9961e1e92b85bbc9 |
| 对照模型 | taobao-mnn/Qwen3-VL-4B-Instruct-MNN / 5c00738180f120067d51306ecf365015cdbbabcf |
| 4B 权重校验 | llm.mnn.weight SHA-256 387576d52e7a297e3e6de837c60c25e364cd62c6244f878633097064bea0a245;visual.mnn.weight SHA-256 b48c901ffe64d1615da93992f3c0508ecf51856b016c424cd96de27114e85694 |
| 第二对照模型 | taobao-mnn/MiniCPM-V-4-MNN / b0ec85ec6f4d1f85df144087bbcfc66a221a3b33 |
| MiniCPM 权重校验 | llm.mnn.weight SHA-256 3d17c4db06d1ed9d35a01843cd50e2ea3fee08b4701018be4872eae0c1bc38e1;visual.mnn.weight SHA-256 fb73e88834fb7234f4b9927a68996341690c78a56597fde1740444f6c2d65d9d;embeddings_bf16.bin SHA-256 2cf70b1293338944249e84c36b2c8612d580e8bcc2d34a44c876da304785aa00 |
| 第三对照模型 | taobao-mnn/FastVLM-1.5B-Stage3-MNN / 1b83a8355ad07035720c6836fdcf758f139ac119 |
| FastVLM 权重校验 | llm.mnn.weight SHA-256 af811ed37d4312d59ac0cc6aa1cdddef7798d38dc35c80b199675c895fd56ac2;visual.mnn.weight SHA-256 c046ceee9795cca65ae8e0d7e5dbab3973c63c800422e860582a5f8136bbeef6;llm.mnn.json SHA-256 d9de6060121b2e4b5d744a4f0ed893749c8a7433447b7a74b9b61d79e020c4a2 |
| Commons 图片 | Cardiopulmonary resuscitation training 分类中 20 张 CC / 公有领域照片 |
| CPR-Coach 代码 | Shunli-Wang/CPR-Coach / 0fad0643e354f9262cb00a11b5b84528be28828a |
| CPR-Coach 数据 revision | b8667b8fd6a70ac63cc09fc6b763a9459b7c160a |
| 作者 demo | SHA-256 d8401c0d...d524fa4;从中裁 1 张 standard 和 2 张错误动作定性样本 |
| CPR-Coach annotation | SHA-256 72d99337...0951e4;确认 1 个 Correct + 13 个单错误动作 |
CPR-Coach 作者仓库的 MIT LICENSE 明确覆盖代码仓库,但 Hugging Face 数据卡没有声明媒体 license;本轮没有把数据集媒体或裁图写入 Git,也不把代码许可外推成媒体许可。完整数据约 449GB,最小的单文件 14 类动作包仍约 9.68GB,因此未为一次预验证下载整包。
手位定义参考 2025 AHA Adult Basic Life Support:成人按压时一只手掌根放在胸部中央(胸骨下半部到下三分之一),另一只手叠放其上。这只是 prompt 术语来源,不构成样本人工标签或医疗验收。
Simulator 预验证结果
A. 现有双字段 schema:20 张 Commons CPR 照片
现有 schema:
{"signal":"hand_position_correct","observation":"yes|no|unknown"}| 指标 | 结果 |
|---|---|
| 严格合法 JSON | 20/20 |
| observation | yes=17、no=0、unknown=3 |
| 完整输出 p50 / p95 | 2303.2 ms / 2619.6 ms |
| 首 token p50 / p95 | 1984.9 ms / 2315.5 ms |
这 20 张照片没有专业手位标签,不能据此计算准确率。但语义复核已发现明确的 fail-closed 反例:CPR Training Chiasma 画面中讲师只在模型旁做手势,没有双手接触胸部,模型仍输出 yes。因此 100% JSON 合法只证明软件格式稳定,不证明 observation 可靠。
B. visibility + correctness 联合 schema:23 张
尝试增加模型自报字段 hand_position_visibility=clear|unclear,并由解析器只接受以下组合:
clear + yesclear + nounclear + unknown
测试集为上述 20 张 Commons 图片,加作者 demo 中裁出的 1 张 standard 和 2 张错误动作定性样本。
| 版本 | 严格合法 | 原始有效回答分布 | p50 / p95 |
|---|---|---|---|
| visibility v1 | 22/23 | 22 个均为 clear + yes | 1616.0 / 1686.3 ms |
| visibility v2(unknown 优先、默认保守) | 16/23 | 16 个仍为 clear + yes;其余 7 个因额外引号 fail closed | 1694.4 / 1760.3 ms |
两个作者错误动作样本和讲师手势样本仍被模型自报为 clear + yes。v2 增加的 unknown 全部来自 JSON 语法失败,不是视觉语义改善。该 schema 降低了格式合法率,也没有提供独立安全门,未合入运行时代码。
C. 独立 visibility-only 第一问:6 张关键样本
为排除联合任务干扰,单独询问 hand_position_evaluable,覆盖近景、远景、讲师手势、作者 standard 和两个作者错误动作样本。结果 6/6 都只输出裸字符串 yes,没有遵守要求的 JSON schema;冷调用 3488.9 ms,后续 1075.2–1216.2 ms。
因此把同一个生成模型的自报 visibility 放在第一阶段,既不能形成独立证据,也不能阻止肯定偏置。两阶段串行只会增加延迟,当前没有继续集成价值。
D. Qwen3-VL-4B 兼容性与 23 图筛选
为判断 2B 的肯定偏置是否主要受模型容量限制,使用相同双字段 schema、相同 23 张图片和 CPU 后端测试官方 4B 转换包。初始配置沿用 PracticeMate 的 use_mmap=true,但没有显式设置 mmap_size;MNN 3.6.0 此参数默认值为 1024 MB。
初始结果不是普通质量下降,而是稳定的运行时错误:23/23 次调用都生成 64 个 !,严格 JSON 为 0/23,p50 / p95 为 4332.6 / 4437.0 ms。以下社区线索与该现象相关,但不能直接当成本项目修复:
- MNN issue #3343 在 iOS 等平台记录过连续
!,维护者建议尝试precision=normal和更新运行时代码 - issue #4346 报告 Qwen 视觉模型在
memory=low时乱码,作者称memory=normal可恢复 - issue #4104 与 PR #4133 记录 Qwen3-VL 交错 MRoPE 支持缺失;MNN 3.6.0 源码已经包含相应 Qwen3-VL 路径
- issue #4485 与 PR #4502 记录量化 scale 版本差异和 KV cache 元数据导致重复输出;PR #4502 已包含在 3.6.0
本机逐项对照结果:
| 配置 | 严格 JSON | observation | p50 / p95 | 结果 |
|---|---|---|---|---|
| mmap 默认 1024 MB,low / low | 0/23 | 23 个解析失败后 fail closed 为 unknown | 4332.6 / 4437.0 ms | 原始输出均为 64 个 ! |
mmap 默认 1024 MB,precision=normal | 0/23 | 同上 | 4475.4 / 4917.7 ms | 无改善 |
mmap 默认 1024 MB,memory=normal | 0 | 无输出 | 未完成 | 加载期 SIGSEGV,栈顶为 MNN::ConvolutionCommon::load;不是 jetsam |
| 关闭 mmap | 23/23 | yes=12、no=1、unknown=10 | 2274.8 / 2374.1 ms | 输出恢复;稳态物理内存约 3.31 GB |
| mmap 4096 MB,low / low | 23/23 | yes=12、no=1、unknown=10 | 2194.0 / 2224.7 ms | 输出恢复;稳态物理内存约 183 MB |
mmap 4096 MB 的冷加载为 2808.9 ms,首 token p50 / p95 为 1796.8 / 1837.4 ms,视觉预处理 p50 / p95 为 445.1 / 463.9 ms。23 次过程中 physical footprint 从 180,966,968 增至 183,359,176 bytes,没有单调持续增长。
关键样本表现也不再是近乎全肯定:讲师只做手势的 09 返回 unknown,作者 standard 返回 yes,两个作者错误动作样本均返回 unknown。这满足本轮候选筛选门,但样本没有完整专业金标准,不能据此计算准确率或宣布 4B 可用于医疗判断。
根因边界:本轮已经证明触发条件是 MNN 3.6.0 的 1024 MB 默认 mmap 池与该 4B 包组合;关闭 mmap 或把池扩到 4096 MB 均恢复相同输出。4B 的 LLM 与视觉权重合计约 2.95 GB,明显高于默认池,这与结果一致;但没有上游 issue 明确确认内部越界机制,因此不把更深层实现原因写成已证实事实。
4096 MB 映射还会生成逻辑 4 GB、实际约 2.8 GB 的缓存文件。加上约 3 GB 模型本体,Simulator 数据成本接近 5.8 GB。它适合继续做 Simulator 质量筛选,但在真实 iPhone 上仍须重新验证磁盘、内存、发热与 jetsam,当前不应全局硬编码到现有 2B 路径。
E. MiniCPM-V-4 兼容性失败过程
第二个候选使用相同 23 张图片、相同 prompt、严格解析器和 CPU 后端。模型文件约 2.83 GB,其中 LLM 权重约 2.14 GB;因此先保留默认 1024 MB mmap 形成基线,再用 4096 MB 做单变量对照,避免把缓存池不足误判成模型能力问题。
| 配置 | 严格 JSON | p50 / p95 | 首 token p50 / p95 | 视觉处理 p50 / p95 |
|---|---|---|---|---|
| mmap 默认 1024 MB | 0/23 | 4859.3 / 5759.6 ms | 3898.7 / 3968.6 ms | 2040.4 / 2074.7 ms |
| mmap 4096 MB | 0/23 | 4913.1 / 6015.9 ms | 3955.3 / 4127.3 ms | 2059.3 / 2213.8 ms |
两轮逐图输出模式基本一致,说明这次失败不是 Qwen4B 那种 mmap 池不足导致的坏权重读取:
- 部分图片只输出
The hand position is correct.,没有 JSON - 部分图片先给自然语言结论,再输出 Markdown code fence 包裹的 JSON
- 部分输出缺字段、拼接多个互相矛盾的判断,或重复
hand_position_correct直到截断 - 明显不可评估的讲师手势样本 09,以及两个作者错误动作样本 22、23,都直接声称手位正确
默认 mmap 轮 physical footprint 从 219,861,984 增至 227,448,112 bytes;4096 MB 轮从 198,235,176 增至 205,395,272 bytes,未见单调持续增长。但 4096 MB 配置仍生成逻辑 4 GB、实际约 2.3 GB 的缓存文件,没有换来格式、语义或速度改善。
XCTest 进程显示通过,只代表 23 次 MNN 调用都返回了 receipt;产品严格解析结果全部是 invalid_json -> unknown。该候选同时未通过有效 JSON 比例、负例行为和 p95 不超过 5 秒三道筛选门,因此停止调 prompt、停止继续集成,也不放宽解析器接受 Markdown 或从自然语言中抽取 JSON。
F. FastVLM-1.5B-Stage3 兼容性失败过程
第三个候选是官方 8-bit MNN 转换包,文件总计 1,228,202,109 bytes,其中 LLM 权重 1,082,352,098 bytes、视觉权重 139,436,578 bytes。测试继续使用相同 23 张图片、相同 prompt、严格解析器和 CPU 后端;三张作者裁图通过先前保存的 SHA-256 复核,避免样本重建时偷换输入。
下载阶段先暴露出一个官方仓库包装异常:.gitattributes 的 *.mnn.* 会命中直接存入 Git 的真实 llm.mnn.json,因此 clone 报 Encountered 1 file that should have been a pointer, but wasn't,git lfs fsck 也报 unexpectedGitObject。四个真正的 LFS 对象能按固定 revision 下载并通过对象 SHA-256;本轮通过排除 *.mnn.json 完成权重拉取,但不把该仓库状态写成“LFS 全量校验通过”。
基线权重合计略高于 MNN 3.6.0 的 1024 MB 默认 mmap 池,因此在原始输出为空后,只增加一次 2048 MB 单变量对照:
| 配置 | 严格 JSON | 原始输出 | p50 / p95 | 首 token p50 / p95 | 视觉处理 p50 / p95 |
|---|---|---|---|---|---|
| mmap 默认 1024 MB | 0/23 | 23 个均只有换行 | 1551.8 / 1607.4 ms | 1551.8 / 1607.4 ms | 867.7 / 898.0 ms |
| mmap 2048 MB | 0/23 | 23 个均只有换行 | 1596.9 / 1645.0 ms | 1596.9 / 1645.0 ms | 878.6 / 919.4 ms |
两轮都成功加载模型、完成视觉计算并返回 23 个 receipt,但第一次写入就是换行,随后立即结束,没有任何可解析或可复核的视觉判断。2048 MB mmap 没有恢复输出,说明这不是 Qwen4B 那种扩大池即可解除的损坏;当前证据只能定位为该转换包与 MNN 3.6.0 现有多模态调用路径不兼容,不能进一步断言是 FastVLM 模型本身失效。
默认 mmap 轮 physical footprint 从 151,818,368 增至 157,716,800 bytes;2048 MB 轮从 161,173,632 增至 165,761,272 bytes,未见单调持续增长。2048 MB 配置生成逻辑 2 GB、实际约 1.2 GB 的缓存文件。由于严格 JSON 仍为 0/23,停止 prompt 调整和产品集成;速度门通过不能抵消完全无有效输出。
G. 4B 选择与产品 profile 收尾
候选筛选后,固定图片切片选择 Qwen3-VL-4B。代码锁定模型 ID、revision、目录名和 4096 MB mmap,并把 mmap 缓存 namespace 绑定 backend、模型 revision 与 mmap 大小,避免 2B、4B 和其他候选共用旧缓存。首次编译门暴露 Objective-C NSUInteger 到 Swift UInt 的类型不匹配;将 profile 常量显式声明为 UInt 后通过,未绕过编译或测试。
正式默认 profile 在同一 iPhone 17 Pro / iOS 26.2 Simulator 上重跑固定 CPR 图片:
| 收据 | 结果 |
|---|---|
| CPU 严格 JSON | 10/10;全部为合法 unknown,没有强提醒 |
| CPU 冷加载 | 3336.5 ms |
| CPU 完整输出 p50 / p95 | 2318.4 / 4077.0 ms;p95 包含首轮冷路径 |
| CPU 首 token p50 / p95 | 1905.3 / 3653.6 ms |
| CPU 视觉处理 p50 / p95 | 451.9 / 2221.7 ms |
| CPU physical footprint | 首次完成 256,022,024 bytes,末次 260,675,128 bytes,区间最大 263,624,248 bytes;这不是峰值 |
| CPU mmap 缓存 | 逻辑 4 GB,实际约 2.8 GB |
| Metal 单次 | 加载 1517.8 ms,完整输出 4737.1 ms,完成时 physical footprint 2,730,040,544 bytes;输出乱码并以 invalid_json -> unknown 失败 |
| Metal 缓存 | 主映射逻辑 4 GB,另有三个逻辑 1 GB Metal weight 文件;实际约 2.3 GB |
因此本轮只把 Simulator CPU 固定图片软件链路 切到 4B;Metal 保持不可用。4B 的选择依据是相同 23 图下格式稳定、明显负例不再盲目肯定且延迟低于 5 秒,不是专业标注准确率。它仍固定 confidence=0、alert_tier=low、examiner confirmation,不接实时相机、自动阶段推进或医疗强提醒。
H. 全智评 production 示范帧预验证
本轮按只读范围经受控中转机盘点全智评 production,生产 HEAD 为 e7880fc5c07caf8f142ac5c64978eb56f870b9e5,checkout clean,严格公网 backend/frontend health 均通过。没有修改 production、数据库或 OSS,也没有把凭据、对象 key、assignment、用户标识、项目名或服务器路径带回仓库。
数据盘点与失败收据
CPR 模板名称/来源文件匹配到 3 个模板、6 个项目、80 条未删除提交,其中 4 条是示范操作、76 条是学生视频。本轮明确不读取或下载学生视频,只检查示范操作对象:
- 首次聚合 SQL 对
extra_image_keys直接调用jsonb_array_length,因 JSON 标量报错;加类型保护后发现 76 行为非数组 JSON(可能包含 JSONnull),这只证明探针必须 fail closed,不能据此断言业务数据损坏。 - 4 条示范记录中,3 条的原始和转码 OSS 对象都不存在,真实返回
NoSuchKey;只有 1 条 275 秒示范视频仍有可读取的 35,284,952-byte 转码对象。 - 因此本轮是同一来源的多时间点测试,不是多来源数据集。数据库记录数量不能替代实际媒体可用性。
从唯一可用示范视频的 10%–90% 时间点抽取 9 张 960×540 JPEG。首次 760×260 裁剪仍在俯身画面顶部留下局部头部,相关推理结果作废;最终输入统一裁为 crop=760:220:100:320,人工检查只保留假人胸腹、双手和器材区域。9 张最终派生图的排序后 SHA-256 manifest 为 a1d2992f7dea4109c3b1b721c2b195599e312b75a9abf0e2abe7aad93faad3f1。原始帧、派生图、manifest 与完整日志只留在本机临时目录,不进入 Git。
这次人工裁剪降低了直接身份暴露,但不等于独立的 deidentification verification、授权/撤回核验、专业标签复核或治理准入;这些帧不能加入正式 EvalSuite,也不能用作训练材料。
最终脱敏裁图结果
使用选定的 Qwen3-VL-4B、MNN 3.6.0、4096 MB mmap、Simulator CPU profile。新增的 sample-set XCTest 默认 skip,只有 Application Support 中本机模型、样本目录和显式 marker 同时存在时才加载一次模型并逐图执行;图片仍不打包进 App 或仓库。
| 收据 | 结果 |
|---|---|
| 环境 | iPhone 17 Pro / iOS 26.2 Simulator,arm64,CPU |
| 样本 | 1 个示范视频的 9 个脱敏时间点;无学生媒体、无专业金标准 |
| receipt / 严格 JSON | 9/9 / 9/9 |
| observation | yes=1、no=0、unknown=8;最早时间点为 yes,其余为 unknown |
| 冷加载 | 3121.8 ms |
| 完整输出 p50 / p95 | 2361.1 / 3221.9 ms |
| 首 token p50 / p95 | 1961.2 / 2813.2 ms |
| vision stage p50 / p95 | 470.7 / 1411.9 ms |
| physical footprint | 首次完成 216,487,432 bytes,末次 270,784,200 bytes,区间最大 271,161,032 bytes;不是 Instruments 峰值,单轮不能证明无泄漏 |
| 软件路由 | 9/9 observation 均构造 Live PracticeEvent,固定 confidence=0、low tier,并断言进入 examiner confirmation |
| XCTest | 1/1 passed,测试方法用时 25.336 秒 |
输出说明 4B 在这批局部 CPR 帧上格式稳定且大部分选择保守 unknown,但不能解释为检测准确:样本都来自同一视频,没有专家逐帧标签,也没有明确负例;相邻按压画面只在一个时间点给出 yes,已经暴露出单帧视角/时点敏感性。该结果只扩展了“软件行为正确”的收据,不提升医疗准确率结论。
结论与证据边界
| 问题 | 结论 |
|---|---|
| 部署成功 | 本轮只再次证明 Simulator CPU 能连续完成多图推理;不代表真实 iPhone 部署 |
| 软件行为正确 | 现有严格解析器能把非法 JSON 转成 unknown,所有结果仍固定走 low tier / examiner confirmation |
| 医疗检测准确率 | 未验证;当前无人工金标准,且多张明显不可评估或错误动作图片仍输出 yes |
| visibility 安全门 | 失败;同一生成模型自报 visibility 不是独立信号,相关代码已回退 |
| 4B 候选 | 已选为固定图片切片;Simulator CPU 软件门通过、Metal 失败;尚未通过专业标注准确率门或真机资源门 |
| 全智评 production 样本 | 只读盘点到 4 条示范记录但仅 1 条媒体可用;最终 9 帧为单来源脱敏预验证,9/9 严格 JSON、1 yes / 8 unknown,不能计算准确率 |
| MiniCPM-V-4 候选 | 淘汰;0/23 严格 JSON,负例仍肯定,mmap 4096 MB 后 p95 仍超过 5 秒 |
| FastVLM-1.5B 候选 | 当前 MNN 3.6.0 调用路径下淘汰;两种 mmap 配置均为 0/23 严格 JSON,原始输出只有换行 |
| Live UI | 不扩展;不得把当前 observation 用于自动医疗提醒或阶段推进 |
当前结果支持用 Qwen3-VL-4B 保留严格 JSON 软件垂直切片,但不支持把 hand_position_correct 作为 v0.1 自动医疗检测能力。产品能力必须保持关闭或仅供考官查看。
后续降级方案
- 先取得许可明确、由 CPR 专业人员复核的 10 正 / 10 负固定集;没有金标准不继续调 prompt
- 将手位判断改为专用判别式视觉任务,优先评估可在 MNN 运行的手部/人体关键点或小型分类器;Qwen 只保留场景描述与不确定项辅助
- 新方案必须分别报告
evaluable覆盖率、yes/no 的 precision / recall / F1、unknown 比例和格式失败率 - 在医疗准确率门通过前,维持
confidence=0、alert_tier=low和 examiner confirmation,不接实时摄像头循环
来源
- CPR-Coach 作者仓库
- CPR-Coach Hugging Face 数据集
- Wikimedia Commons CPR training 分类
- 2025 AHA Adult Basic Life Support
- MNN LLM inference configuration
- MNN FastVLM-1.5B-Stage3 官方转换包
- MNN issue #3343: iOS output 全是叹号
- MNN issue #3992: Qwen3-VL-4B 量化后推理异常
- MNN issue #4104: Qwen3-VL 端侧重复输出
- MNN issue #4346: low memory 下 Qwen 输出乱码
- MNN issue #4485: Qwen3-VL 量化与版本兼容
- MNN PR #4502: per-RTM KV cache meta 修复