学习初稿 · 第 24 课。 本篇已开放网页阅读,用于先建立完整认识。文中手算值和示意记录不代表真实训练成绩;真实数据训练、预训练权重和板端测试以本篇具体说明为准,本篇已附代码练习,实际验证范围见文末与包内记录。
从一张文件图片走向连续输入
训练程序处理静态数据,设备面对的是连续帧、缓冲区、时间戳和错误状态。即使同一模型离线准确率很好,也可能因图像格式、数据生命周期或排队策略在现场失效。
本课不假定某一块板子的 SDK 接口。先建立通用验收方法,再衔接已有的海思系列。没有目标设备测试记录之前,不给出“某型号能够达到多少帧”的结论。
先保存一帧,再接实时循环
摄像头原始输出可能是 YUV、RGB 或其他格式,还可能存在行步长与对齐。缓冲区占用不一定等于宽×高×通道数,读取时必须依据真实格式和 stride。
先保存一帧及其格式说明,把它交给 PC 与板端相同的处理逻辑。依次比较解码或颜色转换结果、缩放后的图片、最终输入张量及模型输出。这样能区分模型差异与相机每次拍摄内容不同。
例如 YUV 到 RGB 的矩阵、范围约定,RGB 与 BGR 顺序、缩放插值和 letterbox 填充都可能影响结果。训练预处理如果使用裁剪,设备却直接拉伸,网络看到的几何分布也变了。
写一张完整接口表
| 边界 | 必须确认的信息 |
|---|---|
| 相机到预处理 | 格式、尺寸、行步长、时间戳、缓冲生命周期 |
| 预处理到模型 | 布局、数据类型、颜色、数值范围、对齐 |
| 模型到后处理 | 输出名称、形状、精度、类别及坐标含义 |
| 后处理到应用 | 有效性、类别或框、分数、错误状态与输入时间 |
应用收到的结果应对应明确帧号或时间戳,不能把旧帧的预测标成当前画面。模型加载、内存分配和设备初始化通常在循环外完成,避免每帧反复创建资源。
队列策略决定延迟是否会失控
相机每秒 30 帧,模型每秒只能处理 10 帧,如果把所有帧无限排队,输出会越来越旧。实时观察场景可能采用有界队列或只保留最新帧;需要逐帧留档的任务则要降低采集率或提高处理能力。
丢帧策略没有统一答案,必须由任务决定。若计算目标轨迹,还要考虑时间间隔变化,而不是把所有帧都假定为等间距。异步推理时,提交后不能立刻复用仍被设备访问的输入缓冲。
分开统计四段耗时
端到端延迟包括等待输入、预处理、推理、后处理与输出。只计设备执行调用附近的时间,可能漏掉传输和队列;若接口异步,甚至只测到了提交耗时。
可以先学习如何报告中位数和较高分位。下面是人为构造的毫秒数据,用来演示 nearest-rank 分位数,不代表设备测量:
|
少量样本的高分位很不稳定。正式测量要固定预热、测试时长、输入、线程和电源模式,并说明分位计算方式。首次加载延迟与稳定运行延迟分开报告。
验收顺序从数值到系统
第一步,PC 浮点模型在固定输入上的结果正确;第二步,导出模型在同样输入上数值一致;第三步,量化模型的质量损失可接受;第四步,设备使用同一张量时输出符合容差;第五步,接入相机预处理;最后做长时间运行与异常处理。
这样出现问题时能回到最近一个通过的边界。若从相机直接跳到最终报警,再同时修改预处理、量化和阈值,就无法确定是哪一项真正起作用。
稳定性测试应观察内存是否持续增长、温度和降频、队列是否积压、输入中断后能否恢复、停止时资源是否正确释放。准确率、延迟和功耗条件都达到任务要求,才可以说当前方案完成验收。
接到已有海思系列
通用部分理解后,再进入平台工具与文档地图,核对芯片、工具和 SDK;通过模型输入检查明确接口;最后参考板端最小推理闭环与性能排查完成具体平台操作。
这些文章的存在不代表本篇新模型已经在板上验证。实际模型名称、工具版本、配置、参考输入、结果与日志都应归档到本次实验,而不是复用其他项目的成功描述。
练习
- 相机 30 FPS、推理 10 FPS,队列无限长会怎样?
- PC 与板端比较时,两边各自拍一张“差不多”的图,能否直接判定数值误差?
- 模型推理 10 ms,能否宣称整个产品延迟是 10 ms?
参考答案
等待帧持续增加,结果越来越陈旧,需要明确吞吐或丢帧策略。输入不同无法直接比较后端数值,应先用同一固定张量。完整延迟还包括采集、排队、转换、传输和输出,必须说明计时边界。
到这里,分类学习、不同视觉任务与部署验证已经连起来。最后回看《视觉模型的发展历史、主流分类与当前格局》,把见过的运算、任务和技术路线放回全景中。
配套代码练习
实验:输入颜色契约与队列延迟
查看完整代码:main.py
"""输入颜色契约与队列延迟。运行方式与练习见 README.md。"""
import math
# 离线单像素帧,不访问摄像头;BGR 红色应转成 RGB 的 [1,0,0]。
bgr = [0,0,255]
rgb = list(reversed(bgr))
normalized = [v/255 for v in rgb]
assert normalized == [1.0,0.0,0.0]
print('RGB:',normalized)
# 确定性离散事件模拟:每 33ms 来一帧,每次推理耗时 50ms。
arrivals = list(range(0,3300,33))
def simulate(latest):
now, i, latencies, dropped = 0,0,[],0
while i<len(arrivals):
now = max(now,arrivals[i])
chosen = i
if latest:
while chosen+1<len(arrivals) and arrivals[chosen+1]<=now: chosen += 1
dropped += chosen-i
now += 50
latencies.append(now-arrivals[chosen])
i = chosen+1
ordered = sorted(latencies)
return len(latencies),dropped,sum(latencies)/len(latencies),ordered[math.ceil(.95*len(ordered))-1]
fifo,latest = simulate(False),simulate(True)
print('策略: 完成数/丢帧数/平均延迟/P95毫秒')
print('FIFO:',fifo,'保留最新:',latest)
assert latest[3]<fifo[3]
assert latest[0]+latest[1]==len(arrivals)
在项目根目录执行;下载包解压后,在解压目录执行 python3 main.py。
cd 'content/posts/AI-深度学习/从零搭建视觉模型/24-摄像头输入与部署验收/code' && python3 main.py
本篇已附独立实验包,在代码卡片中查看源码、下载或复制运行命令。解压后执行 python3 main.py;依赖安装见包内 README.md。
观察目标: FIFO 延迟持续累积;保留最新帧降低 P95 但会丢帧。这里没有实际摄像头或 NPU 性能结论。
动手修改: 把推理耗时改为 20ms,观察两种策略差距;设备验收时用真实时间戳替换模拟到达时间。
所有输入均在本篇代码中构造;运行范围和验证记录见包内 verification.md。
