第10章 部署端侧模型并发布v0.5

v0.3已经取得经过版本化的模型发布包。本章将模型部署到真实设备。项目先完成与参考环境一致的单窗口推理,发布v0.4。随后把连续输出转化为受控产品事件,发布v0.5。

v0.3模型包
→ 设备构建
→ 基准输入
→ 端侧推理与数值核对
→ 资源和时延测量
→ v0.4
→ 连续窗口
→ 事件策略
→ 端云通知
→ v0.5

模型转换、量化和算法评价由《人工智能数据科学》深入讲授。模型算子、存储和设备调试由《感知与异构计算系统原型》深入讲授。本章说明模型构建物怎样进入固件,以及运行结果怎样验证。资源不足时,应形成跨课程变更。AI生成的代码仍受Git工作流约束。

服务器推理便于集中更新模型,却依赖网络并传输更多数据。端侧推理可以降低响应时延和数据外传,但受到内存、算力和功耗限制。选择运行位置不是单纯的速度比较,还要同时考虑隐私、维护和故障恢复。

完成本章后,应当能够:

10.1 建立模型集成议题

10.1.1 分成两个可验证检查点

本章建立两个里程碑检查点:

检查点 目标 版本
M1 模型在设备端加载,基准输出一致,资源可测 v0.4.0
M2 连续传感器窗口形成受控事件并完成端云反馈 v0.5.0

不要在一个议题中同时处理模型加载、性能优化、事件策略、用户界面和硬件更换。建议拆分:

#51 生成并编译模型构建物
#52 实现ModelRunner接口
#53 完成设备基准样例测试
#54 测量端侧资源与时延
#55 实现EventPolicy状态机
#56 完成真实场景端云验证

#51—#54完成v0.4,#55—#56完成v0.5。

10.1.2 固定集成基线

在每个议题中记录:

产品基线:v0.3.0
model_version:model-0.3.0
model_sha256:<实际值>
dataset_version:dataset-0.2.0
硬件修订:<实际值>
工具链版本:<实际值>

后续若模型或硬件变化,必须建立新基线,不能继续沿用旧测试结果。

10.2 让构建系统读取模型清单

10.2.1 单一信息来源

下列信息只在模型发布包中维护权威版本:

固件构建从模型清单生成必要常量,避免在多个头文件中手工复制:

model-manifest.json
→ 构建前检查
→ 生成只读模型元数据
→ 编译固件

例如,构建脚本生成到build/generated/model_metadata.h:

constexpr char kModelVersion[] = "model-0.3.0";
constexpr std::size_t kInputElements = /* 从模型清单生成 */;
constexpr std::size_t kOutputElements = /* 从模型清单生成 */;

build/中的生成文件不提交到Git;生成器、模型清单和测试进入Git。任何人从同一提交和模型包都应得到同一份生成结果。

10.2.2 从手工复制到自动生成

在多个源文件中手工复制模型参数,容易形成彼此矛盾的副本。编译可能仍然成功,但运行时会使用错误的形状、标签或版本。由模型清单生成只读配置,可以使构建结果受同一信息来源约束。

自动生成也增加了构建链环节。生成器、输入文件和输出规则都要进入测试。若生成过程不可复现,自动化只会把手工差错变成批量差错。

10.2.3 把模型二进制变成固件可用形式

部分嵌入式工具链需要把模型二进制转换为C/C++数组。转换应由构建脚本自动执行:

python tools/project.py prepare-model model-0.3.0
python tools/project.py build-firmware

转换前核对SHA-256,转换后核对数组字节数与原文件大小。不要把未经核查的下载文件直接编译,也不要让AI把数十万字节数组粘贴进源代码差异。

10.3 定义稳定的推理接口

10.3.1 模型运行器隔离框架细节

接口示意:

struct InferenceResult {
  bool valid;
  std::uint32_t inference_time_us;
  const float* scores;
  std::size_t score_count;
  const char* model_version;
};

class ModelRunner {
 public:
  virtual bool begin() = 0;
  virtual InferenceResult predict(const DataWindow& window) = 0;
  virtual ModelResourceUsage resources() const = 0;
  virtual ~ModelRunner() = default;
};

上层事件策略只依赖标准分数和类别,不直接调用某个推理框架。若以后更换模型格式或芯片加速器,主要变化限制在ModelRunner实现和构建配置。

10.3.2 初始化失败不能继续产生事件

begin()至少检查:

任何检查失败,设备进入明确故障状态,不使用未初始化分数继续运行。错误日志包含错误类别、模型版本和固件提交,不输出敏感信息。

10.4 保持预处理一致

模型使用的输入不是“任意传感器数组”。设备端必须复现训练与评价使用的处理:

原始样本
→ 通道顺序
→ 单位变换
→ 校准
→ 缺失值处理
→ 窗口
→ 缩放/归一化
→ 类型转换或量化
→ 模型张量

preprocessing.json规定每一步。审查时检查:

[ ] 设备顺序与训练顺序一致
[ ] 单位只转换一次
[ ] 窗口长度和步长一致
[ ] 定点或量化缩放来自模型包
[ ] 饱和和越界行为明确
[ ] 缺失或错误样本不会被静默填成正常值
[ ] 预处理版本写入运行日志

若设备端无法实现某个训练处理,应由数据与硬件课程共同决定新的可部署方案,并发布新模型版本;不能在集成代码中近似替代却沿用旧模型版本。

10.5 完成设备基准样例测试

10.5.1 先绕开实时采样

第一次端侧推理使用模型包中的golden-input.bin:

基准样例输入
→ 设备预处理或规定的输入阶段
→ ModelRunner
→ 设备输出
→ 与golden-output.json比较

这样可以先判断模型导入、张量类型和运行时是否正确,不把真实传感器波动混入故障定位。

课程工具执行:

python tools/project.py build-firmware --profile golden
python tools/project.py flash
python tools/project.py monitor

设备日志至少输出:

firmware_commit=<实际提交>
model_version=model-0.3.0
model_sha256=<实际散列前缀或完整值>
input_shape=<实际值>
output_shape=<实际值>
golden_test=PASS/FAIL
max_error=<实际测量>

不得在教材或报告中预填PASS和误差值。记录必须来自实际设备。

10.5.2 失败时按层定位

现象 首先检查
模型无法加载 散列、文件大小、运行时版本、算子
输入维度错误 模型清单、生成元数据、窗口配置
输出类别数错误 输出张量和labels.txt
数值差异大 预处理、量化参数、字节顺序
偶发崩溃 张量内存、缓冲区生命周期、栈
主机通过设备失败 端侧运行时支持和资源

把第一条设备错误、模型清单和相关源文件交给AI分析。不要让AI在没有定位的情况下同时更改预处理、模型运行时和内存配置。

10.6 测量端侧资源

10.6.1 资源报告绑定版本与条件

docs/verification/model-resource-model-0.3.0.md记录:

# model-0.3.0端侧资源报告

- 固件提交:
- 模型SHA-256:
- 硬件修订:
- 构建配置:
- 编译器/工具链:
- 测量日期:

| 指标 | 目标 | 实际 | 测量方法 | 结果 |
|---|---:|---:|---|---|
| 固件闪存 | <目标> | <实际> | 构建映射文件 | 通过/不通过 |
| 模型字节数 | <目标> | <实际> | 文件/数组检查 |  |
| 推理内存 | <目标> | <实际> | 运行时或映射 |  |
| 峰值RAM | <目标> | <实际> | 指定测量方法 |  |
| 单次推理时间 | <目标> | <实际统计> | 设备计时 |  |
| 工作周期功耗 | <目标> | <实际> | 硬件课程方法 |  |

“使用较少内存”没有复查价值。数字必须带硬件、构建配置、模型和测量方法。

10.6.2 推理时间使用多次测量

预热后运行规定次数,至少记录:

单次最快结果不能代表产品运行。统计方法由课程环境统一,避免各组只报告最有利数字。

10.6.3 资源不满足时选择正确责任接口

模型过大或算子不支持
→ 数据科学课程评估模型结构、量化或替代模型

固件存在重复缓冲和无关依赖
→ 本课程建立受限优化议题

硬件资源与产品需求长期不匹配
→ 硬件课程重新比较主控或加速方案

产品时延目标不合理
→ 回到PRD和预算评审,不直接修改数字

每种改变都可能影响模型指标、物料清单、功耗和供货。使用技术决策记录说明候选、证据、决定和重新评估条件。

10.7 发布v0.4端侧推理基线

v0.4检查:

[ ] 模型由模型清单驱动构建
[ ] 模型SHA-256与v0.3一致
[ ] 设备初始化检查完整
[ ] 基准样例输入在真实设备运行
[ ] 输出在批准容差内
[ ] 闪存、RAM和时延有实际报告
[ ] 资源满足目标或有批准的处置议题
[ ] README.md说明设备推理方法和限制

创建标签:

git tag -a v0.4.0 -m "v0.4.0 verified on-device inference"
git push origin v0.4.0

v0.4只证明端侧推理接口成立。连续窗口和产品事件策略在下面继续实现。

10.8 把模型分数转化为产品事件

10.8.1 模型输出与事件不同

模型每个窗口输出一组分数:

window_101 → target_event: 0.82
window_102 → target_event: 0.91
window_103 → target_event: 0.88

产品不能简单地为每个高分窗口发送通知。事件策略要处理短时波动、连续窗口重叠和重复通知。

基本状态机:

IDLE
  │ 分数达到候选条件
  ↓
CANDIDATE
  │ 连续确认满足
  ↓
CONFIRMED ──产生一次EVENT-001──→ COOLDOWN
  ↑                                  │
  └────候选失败回IDLE                └──冷却结束回IDLE

10.8.2 平滑分数

一种简单方法是指数平滑:

s_t = α × p_t + (1 - α) × s_(t-1)

平滑参数不由AI随意选择。数据科学课程依据验证数据提出候选值。产品团队结合响应速度和误报风险确定初始值,并在配置中记录版本。

10.8.3 连续确认和冷却

事件策略配置示例:

{
  "policy_version": "event-policy-0.5.0",
  "model_version": "model-0.3.0",
  "target_label": "target_event",
  "smoothing": {
    "method": "exponential",
    "alpha": null
  },
  "trigger_threshold": null,
  "consecutive_windows": null,
  "cooldown_ms": null
}

null表示模板尚未填写,正式配置不得保留空值。每个参数都要有:

10.8.4 事件携带完整决策身份

EVENT-001增加或关联:

model_version
policy_version
firmware_version
event_type
confidence或决策证据摘要

用户端不需要展示所有内部字段,但服务器必须能够追溯一次事件由哪一模型和策略产生。

10.9 测试事件策略

10.9.1 先用固定分数序列测试

为状态机建立确定性输入:

序列 目的 预期
全部低于阈值 反例 不产生事件
单次短暂高分 抖动 不满足连续确认
连续高分达到数量 正例 只产生一次事件
冷却期连续高分 重复控制 不产生第二次事件
冷却结束后连续高分 时间边界 产生新事件
模型结果无效 故障 不产生事件并记录错误

这些测试验证策略逻辑,不评价模型。

10.9.2 再用回放数据测试

使用带标签的受控窗口回放:

python tools/project.py event-policy-test --dataset <测试清单>

输出实际事件数量、对应窗口、模型版本和策略版本。数据科学课程分析错报与漏报,本课程核对事件身份和状态转换。

10.9.3 最后使用真实设备和场景

在批准场景中执行:

真实传感器
→ 连续窗口
→ 端侧模型
→ 事件策略
→ EVENT-001
→ 服务器
→ 用户反馈

记录:

测试失败时先判断是信号、模型、策略、通信还是呈现问题,避免让AI在错误层修改。

10.10 管理重构、冲突与回退

10.10.1 功能变化与重构分开

端侧模型接入后,固件可能出现重复缓冲、过长函数或耦合接口。先让v0.4行为通过测试,再建立独立重构议题:

重构前:固定标签或提交 + 基准样例测试 + 资源报告
→ 小范围结构调整
→ 相同基准样例测试
→ 相同事件策略测试
→ 新资源报告

重构提交不应顺便改变阈值、模型或用户行为。若资源结果变化,必须说明原因。

10.10.2 冲突要维护模型接口语义

模型分支和事件策略分支可能同时修改标签、输出结构或main.cpp。解决时核对:

“保留双方代码”可能导致同一模型运行两次或同一窗口触发两条事件。

10.10.3 回退使用已发布构建物

若新模型或策略在试用中产生严重问题:

  1. 停止扩大部署;
  2. 记录当前固件、模型、策略和事件证据;
  3. 选择最近一个已验证组合;
  4. 通过配置或新提交恢复该组合;
  5. 重新运行基准样例、策略和端云测试;
  6. 发布新的修复版本。

不要覆盖model-0.3.0文件后继续沿用原版本号。已发布构建物内容不可改变。

10.11 约束AI完成模型集成

实施请求:

任务:实现model-0.3.0的ModelRunner。

读取:
- 模型清单、预处理、标签和基准样例文件
- firmware/include/model_runner.h
- firmware/config/data_window.json
- 当前构建配置

允许修改:
- firmware/src/model_runner.cpp
- 模型运行器所需的构建文件
- 对应测试

要求:
1. 输入输出形状从生成元数据取得;
2. 初始化逐项检查并返回明确错误;
3. 使用有界静态内存;
4. 不修改模型二进制、模型清单、标签和窗口配置;
5. 不增加未批准依赖;
6. 先运行主机构建,再等待人工烧录;
7. 不提交、不推送。

差异审查:

[ ] 没有硬编码另一套模型参数
[ ] 数组和张量边界有检查
[ ] 模型数据生命周期正确
[ ] 错误路径不会返回旧分数
[ ] 资源大小能在构建或运行日志中取得
[ ] 日志不输出大量原始个人数据
[ ] 所有变化对应当前议题

10.12 发布v0.5

更新README.md、模型卡的设备验证部分、资源预算、需求追踪、事件策略说明和CHANGELOG.md。

发布检查:

[ ] v0.4基准样例与资源检查仍通过
[ ] 真实传感器连续推理稳定
[ ] 策略固定序列测试通过
[ ] 回放测试有实际结果
[ ] 真实场景端云闭环完成
[ ] 每条事件可追溯模型和策略
[ ] 错报、漏报和限制如实记录
[ ] 回退方法已经验证
[ ] 没有改变已发布模型文件

创建:

git switch main
git pull --ff-only
git tag -a v0.5.0 -m "v0.5.0 on-device model and event policy"
git push origin v0.5.0

10.13 本章小结

本章完成了模型从发布包到真实设备的工程集成。v0.4固定模型加载、基准样例一致性和资源测量基线;v0.5把连续模型分数转化为带版本身份的产品事件,并接回v0.1的服务器和用户反馈链路。

AI用于实现受限接口、分析实际构建错误和辅助检查差异。模型文件、资源证据、策略参数、测试和Git版本共同决定成果能否进入主分支。

第11章将在手工检查的基础上建立分层测试。GitLab持续集成将自动检查契约、模型包、服务器和固件构建。敏感信息检查也将纳入合并条件。

10.14 综合实践

  1. 为模型部署建立集成议题,列出模型身份、目标设备、资源预算、允许文件和验证方法。说明哪些问题应转交另外两门课程。
  2. 在两种可行运行位置之间进行比较。使用时延、内存、网络、隐私和维护证据形成技术决策记录。
  3. 设计一组基准输入输出和资源测量记录。在目标设备执行后,分析与参考环境的差异及其可能边界。
  4. 自行设计事件状态机和固定分数序列。序列应覆盖确认、冷却、恢复和边界值,并说明参数依据。
  5. 选择一次重构或模型更新,形成回归测试、回退步骤和版本关系,分别发布v0.4.0与v0.5.0证据。