> ## Documentation Index
> Fetch the complete documentation index at: https://dragonwingdocs.qualcomm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 把一个自定义 YOLO 检测器从 Jetson 移植到 Dragonwing（第 3 部分，共 7 部分）

> 把一个自定义 YOLO 检测器从 TensorRT 引擎迁移到 Qualcomm QAIRT/QNN 上下文二进制文件，并在每个阶段进行验证。

<hr style={{ border: "none", borderTop: "1px solid #eee", margin: "0 0 2rem" }} />

<div style={{ display: "flex", justifyContent: "space-between", gap: "1rem", marginBottom: "2rem", flexWrap: "wrap" }}>
  <a href="/zh/tutorials/day-0-on-dragonwing-first-model" style={{ color: "#31017D", fontWeight: 600, textDecoration: "none" }}>← 上一篇：第 2 部分</a>
  <a href="/zh/tutorials/quantization-is-the-migration-step-people-underestimate" style={{ color: "#31017D", fontWeight: 600, textDecoration: "none" }}>下一篇：第 4 部分 →</a>
</div>

假设你有一个带自定义检测器和 TensorRT `.engine` 的 Jetson 应用。这是大多数 Jetson 团队真正在乎的迁移案例：

```text theme={null}
我训练了一个自定义模型。
它在 Jetson 上运行。
我有一个 TensorRT 引擎。
我怎么让它在 Dragonwing 上运行？
```

简短的答案：

> 最佳实践：恢复源模型并为 Qualcomm 重建部署工件，而不是把 TensorRT 引擎当成可移植的。

在本文中，假设我们贯穿的案例研究是：一个用于 Jetson 智能摄像头应用中的自定义 YOLO 人脸检测器。在 Jetson 上，它可能通过 PyTorch、Ultralytics、TensorRT 或 DeepStream 运行。在 Dragonwing 上，我们将通过 ONNX、QAIRT/QNN、量化、上下文二进制文件和应用侧后处理来重建部署路径。

在开始之前：

```text theme={null}
[ ] 源检查点或干净的 ONNX 导出
[ ] 验证图像/视频和预期的产品指标
[ ] 具代表性的校准图像
[ ] 预处理和后处理代码
[ ] 标签/类别映射
[ ] 目标开发板、SoC、OS/BSP 和 QAIRT/QNN SDK 版本
[ ] 延迟/FPS/功耗目标
```

***

## 迁移路径

一次实用的自定义模型迁移是一组关口，而不是一条魔法转换命令：

```text theme={null}
0. 盘点 Jetson 应用和工件
1. 恢复源模型
2. 检查 AI Hub 是否有兼容模型
3. 导出静态 ONNX 并转换为 Qualcomm 工件
4. 处理不受支持的算子
5. 使用代表性数据进行量化
6. 构建目标特定的 QNN 上下文二进制文件
7. 部署模型和运行时包
8. 在设备上运行推理
9. 与 ONNX FP32 基线进行验证
10. 把后处理移入应用
11. 在功能验证之后再做基准测试
```

这看起来可能比 Jetson 路径更长，这个差异本身也是产品故事的一部分。最快的 Jetson 路径通常以更少可见的硬件特定阶段到达推理。Qualcomm 的自定义模型路径把更多工作暴露出来：转换、量化、上下文生成、打包和后端验证。

自定义算子和专门的 HTP 工作并不是每个模型的常规前置条件。它们是升级路径：

```text theme={null}
AI Hub / 已验证的运行时工件 -> 跳过大部分转换工作
受支持的 ONNX 图                   -> 使用标准流水线
不受支持的算子/模式               -> 先重写；再升级到自定义算子/UDO 或专家协助
```

每一道关口都消除一类故障，但也增加上手时间。这就是先检查 AI Hub 并在移植生产检测器之前先证明一个已知良好模型的原因。

***

## 步骤 0：盘点你拥有的东西

先把可移植的源工件与 Jetson 特定的部署工件分开。

有用的工件：

```text theme={null}
best.pt / checkpoint.pth
model.onnx
TensorFlow SavedModel
TFLite 模型
预处理代码
后处理代码
标签 / 类别映射
验证数据集
校准图像
准确率 SLA
延迟 / FPS / 功耗目标
```

迁移期间需要替换的 Jetson 特定工件：

```text theme={null}
TensorRT .engine / .plan
TensorRT INT8 校准缓存
CUDA 预处理内核
DeepStream 配置（原样）
JetPack 锁定的 Docker 镜像
```

如果你只有这个：

```text theme={null}
model.engine
```

那么下一步最好的方式是恢复训练检查点或一份中性的导出比如 ONNX。

那是最重要的迁移关口：

```text theme={null}
有源模型？继续
只有 TensorRT 引擎？先恢复源
```

***

## 步骤 1：恢复并导出源模型

对于 YOLO 检测器，从训练检查点开始：

```text theme={null}
best.pt
```

在禁用 NMS 的情况下导出静态 ONNX：

```bash theme={null}
yolo export \
  model=best.pt \
  format=onnx \
  imgsz=640 \
  opset=17 \
  simplify=True \
  dynamic=False \
  nms=False
```

对于 HTP/NPU 部署，静态形状是最简单的起点。对于一个标准的 YOLO 图像模型，这通常意味着：

```text theme={null}
input: [1, 3, 640, 640]
```

一开始把 NMS 排除在导出的图之外。在应用中控制后处理更容易，尤其是当你需要比较 CPU、GPU 和 HTP 输出时。

导出后，在动 Qualcomm 工具之前先验证 ONNX：

```bash theme={null}
python3 -c "import onnx; onnx.checker.check_model('best.onnx')"
```

然后用 ONNX Runtime FP32 创建一个黄金输出。保存这个输出。你将把之后的每个阶段与它对比。

如果 ONNX 导出或验证失败，请先修复它，再去动 QNN：

| 症状                        | 首先查看的地方                   |
| ------------------------- | ------------------------- |
| 导出失败                      | 不受支持的框架算子、训练包装器、opset 不匹配 |
| 形状错误                      | 动态批次/图像尺寸、缺少静态输入形状        |
| ONNX Runtime 与 PyTorch 不同 | 预处理、布局、简化、导出的 NMS         |
| 转换器稍后拒绝该图                 | 不受支持的算子、动态形状、图内后处理        |

最短原则：先让 PyTorch 和 ONNX Runtime 匹配。QNN 无法修复一个糟糕的 ONNX 导出。

***

## 步骤 2：先检查 AI Hub

在构建自定义转换流水线之前，检查你的模型架构在 AI Hub 中是否已存在。

对于 YOLO 类模型：

```bash theme={null}
python3 -m venv ~/qaihub-venv
source ~/qaihub-venv/bin/activate
pip install qai-hub-models

qai-hub-models perf yolov8-det \
  --device "Dragonwing IQ-9075 EVK"

qai-hub-models perf yolov8-det \
  --device "Dragonwing IQ-9075 EVK" \
  --runtime qnn_dlc
```

当前 Dragonwing 文档以 AI Hub IoT 模型目录为模型可用性的最新来源。它包括对 LLM、VLM、检测、分割、分类、嵌入、音频、深度、恢复和机器人的已验证覆盖。

如果你的确切模型不可用，一个兼容的架构可能仍然有用：

```text theme={null}
先用 AI Hub 模型完成应用集成
在流水线运行后再换入自定义模型
```

这能避免同时调试转换和应用集成。

***

## 步骤 3：把 ONNX 转换为 Qualcomm 工件

Qualcomm QAIRT/QNN 转换与 `trtexec` 的形态不同。

Jetson 常常感觉像一步构建：

```bash theme={null}
trtexec --onnx=model.onnx --saveEngine=model.engine --fp16
```

Qualcomm 把工作分为两条通道之一：

```text theme={null}
本地 QNN SDK 通道：
  ONNX -> QNN .cpp/.bin -> 模型 .so -> 上下文二进制文件

AI Hub / DLC 风格通道：
  AI Hub 工件或 DLC -> QNN/QAIRT 运行时路径 -> 在受支持处生成上下文二进制文件
```

公共的 Dragonwing 文档常展示本地 QNN 路径为 `qnn-onnx-converter` 后接 `qnn-model-lib-generator`。AI Hub 和某些 SDK 流程可能给你的是 DLC。请使用与你 SDK 版本匹配的流程，并用 `--help` 验证准确的标志。

本地 QNN 模型库流程：

```bash theme={null}
"$QAIRT_SDK_ROOT/bin/x86_64-linux-clang/qnn-onnx-converter" \
  --input_network best.optimized.onnx \
  --output_path out/best.cpp \
  --input_dim "images" 1,3,640,640

"$QAIRT_SDK_ROOT/bin/x86_64-linux-clang/qnn-model-lib-generator" \
  -c out/best.cpp \
  -b out/best.bin \
  -o out/libs \
  -t x86_64-linux-clang
```

现在在主机 CPU 上验证转换后的 FP32 工件。这能在量化引入噪声之前捕捉到转换问题。

```bash theme={null}
"$QAIRT_SDK_ROOT/bin/x86_64-linux-clang/qnn-net-run" \
  --model out/libs/x86_64-linux-clang/libbest.so \
  --backend "$QAIRT_SDK_ROOT/lib/x86_64-linux-clang/libQnnCpu.so" \
  --input_list calibration_input_list.txt \
  --output_dir out/cpu_validation/output/
```

建议的关口：

```text theme={null}
FP32 QNN 工件 vs ONNX Runtime FP32：
  cosine >= 0.999
  SQNR >= 30 dB
```

如果失败，我们建议先修复导出、图形状、预处理或不受支持算子的问题，再进行量化。

***

## 步骤 4：处理不受支持的算子

这是 Qualcomm 上手差距可能变得可见的地方。不要把它藏在一条通用的"调试转换器"指令后面。记录算子、图中位置、受影响的后端和回退成本；然后按以下顺序处理：

如果转换器拒绝了某个算子，请按以下顺序：

```text theme={null}
1. 图重写
2. QNN Custom Op Package / UDO
3. CPU 回退
```

当不受支持的算子有等价的受支持算子时，图重写通常是最好的第一次尝试。自定义算子工作量更大但能让模型保留在预期的后端上。CPU 回退对于低频或非关键部分可以接受，但要记录延迟代价。

我们的最佳实践是在选择修复方式之前对失败进行分类。

有用的失败分桶：

| 失败          | 分桶                            | 路线             |
| ----------- | ----------------------------- | -------------- |
| 转换器拒绝算子     | `C1_UNSUPPORTED_OP_TYPE`      | 图重写或自定义算子      |
| HTP 不兼容的模式  | `C2_HTP_INCOMPATIBLE_PATTERN` | 修复图模式          |
| 准确率低于阈值     | `E3_ACCURACY_REGRESSION`      | 量化重试梯度         |
| CPU 回退层     | `P1_CPU_FALLBACK_DETECTED`    | 图优化            |
| 上下文构建因内存失败  | `G2_VTCM_EXCEEDED`            | 减少 VTCM 或改变精度  |
| SDK/SoC 不匹配 | `H3_ABI_MISMATCH`             | 为目标 SoC/SDK 重建 |
| 未知          | `Z1_UNCLASSIFIED`             | 保留证据并升级        |

对于 `Z1_UNCLASSIFIED`，最安全的路径是在升级之前保存日志、模型、输入、输出和工具版本。这为真正的修复保留了证据。

***

## 步骤 5：使用真实校准数据进行量化

如果你想要高效的 HTP/NPU 执行，量化是核心。

TensorRT 校准缓存在这里没有用。它是 TensorRT 特定的。为 Qualcomm 重新校准。

对于 YOLO 人脸检测器，使用来自目标环境的真实图像：

```text theme={null}
目标摄像头帧
正常光照
低光
逆光
近距离人脸
远距离人脸
部分遮挡
空场景
拥挤场景
如果预期有则包含运动模糊
```

一个小型初始校准集可能是 64-200 张图像。如果图像错过了真实的工作条件，更多不会自动更好。有代表性胜过随机。

在公共的 QNN 转换器流程中，静态量化通常通过向 `qnn-onnx-converter` 传递 `--input_list calibration_input_list.txt` 来完成，然后用 `qnn-model-lib-generator` 编译生成的图：

```bash theme={null}
"$QAIRT_SDK_ROOT/bin/x86_64-linux-clang/qnn-onnx-converter" \
  --input_network best.optimized.onnx \
  --output_path out/best_a8w8.cpp \
  --input_list calibration_input_list.txt \
  --input_dim "images" 1,3,640,640

"$QAIRT_SDK_ROOT/bin/x86_64-linux-clang/qnn-model-lib-generator" \
  -c out/best_a8w8.cpp \
  -b out/best_a8w8.bin \
  -o out/libs \
  -t x86_64-linux-clang
```

如果 AI Hub 或你的 QAIRT SDK 给你的是量化后的 DLC，请保持同样的验证关口，并用 `libQnnModelDlc.so` 把该 DLC 送入上下文二进制步骤。

来自当前文档的重要陷阱：

> `a8w16` 不是有效的 HTP 模式。请使用 `a8w8`、`a16w8`、`a16w16` 或 `fp16`。

如果准确率下降，请使用重试梯度而不是随机改标志：

| 顺序 | 策略         | 示例标志 / 操作                                                                     |
| -: | ---------- | ----------------------------------------------------------------------------- |
|  1 | 非对称激活      | `--act_quantizer_schema asymmetric`                                           |
|  2 | 逐通道 + 增强校准 | `--use_per_channel_quantization --act_quantizer_calibration enhanced`         |
|  3 | 混合精度       | 标记敏感层                                                                         |
|  4 | 精度升级       | 在评估后尝试 `a16w8`、`a16w16` 或 `fp16`                                              |
|  5 | 异常值裁剪      | `--act_quantizer_calibration percentile --percentile_calibration_value 99.99` |
|  6 | QAT        | 升级到 AIMET QAT                                                                 |

使用第一个通过你准确率 SLA 的策略。

***

## 步骤 6：构建上下文二进制文件

QNN 上下文二进制文件是这条路径的目标部署工件。

先确定目标 SoC：

```text theme={null}
IQ-9075 / QCS9075 -> dsp_arch=v73
IQ-8275 / QCS8275 -> dsp_arch=v75
```

从与你确切模型和目标匹配的 SDK 示例中设置 `dsp_arch`、VTCM 和其他后端选项；未经验证不要跨 SoC 复制调优值。在已发布的命令中，包含经过测试的 `context_config.json` 或指引读者到该开发板使用的确切 SDK 示例配置。

构建上下文二进制文件：

```bash theme={null}
"$QAIRT_SDK_ROOT/bin/x86_64-linux-clang/qnn-context-binary-generator" \
  --model out/libs/x86_64-linux-clang/libbest_a8w8.so \
  --backend "$QAIRT_SDK_ROOT/lib/x86_64-linux-clang/libQnnHtp.so" \
  --output_dir out/context_binary/ \
  --binary_file best_ctx \
  --config_file context_config.json
```

如果你的输入是 AI Hub 或 DLC 工件，请使用 SDK 的 DLC 模型加载路径，通常是 `--model libQnnModelDlc.so --dlc_path out/best_a8w8.dlc`，配以相同的后端和配置。

预期输出：

```text theme={null}
best_ctx.bin
```

这个工件对目标敏感。当你更换 SDK、BSP、SoC、精度或模型时都要重建。

如果上下文生成失败，先检查小的地方：

| 症状                 | 首先查看的地方                              |
| ------------------ | ------------------------------------ |
| 后端加载失败             | 错误的主机/目标库或缺少 `ADSP_LIBRARY_PATH` 等价物 |
| HTP 编译失败           | 错误的 `dsp_arch`、陈旧的后端配置、不受支持的图模式      |
| 内存/VTCM 错误         | 减小模型大小、精度、批次或 VTCM 设置                |
| 在 CPU 上运行但 HTP 上不行 | 不受支持的算子路径或量化/后端不匹配                   |
| 在旧镜像上工作，现在失败       | SDK/BSP/运行时漂移；重建工件                   |

***

## 步骤 7：部署运行时包

一次部署不仅仅是模型文件。它还需要兼容的运行时库和配置。

至少期望一个像这样的包：

```text theme={null}
best_ctx.bin
bin/
  qnn-net-run 或应用二进制文件
lib/                         # ARM64 主机侧库，通过 LD_LIBRARY_PATH 查找
  libQnnHtp.so
  libQnnHtpV73Stub.so        # IQ-9/QCS9075 示例
  libQnnHtpNetRunExtensions.so
  libQnnSystem.so
dsp/                         # DSP/skel 库，通过 ADSP_LIBRARY_PATH 查找
  libQnnHtpV73Skel.so
configs/
  backend_ext.json
  htp_settings.json
input_list.txt
```

验证你部署到设备的是 ARM64 库：

```bash theme={null}
file lib/aarch64-oe-linux-gcc11.2/libQnnHtpNetRunExtensions.so
```

你想要的是：

```text theme={null}
ELF 64-bit LSB shared object, ARM aarch64
```

复制该包：

```bash theme={null}
scp -r deployment_package/ ubuntu@DEVICE_IP:/data/qairt_runtime/
```

对 Linux 目标使用 `scp`。把 `adb push` 保留给 Android 目标。

***

## 步骤 8：在设备上运行推理

在设备上：

```bash theme={null}
cd /data/qairt_runtime

export LD_LIBRARY_PATH=/data/qairt_runtime/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
export ADSP_LIBRARY_PATH="/data/qairt_runtime/dsp;/usr/lib/rfsa/adsp;/dsp"

./bin/qnn-net-run \
  --retrieve_context best_ctx.bin \
  --backend libQnnHtp.so \
  --input_list input_list.txt \
  --output_dir inference_results/ \
  --config_file configs/backend_ext.json
```

保持 `backend_ext.json` 最小化：

```json theme={null}
{
  "backend_extensions": {
    "shared_library_path": "libQnnHtpNetRunExtensions.so",
    "config_file_path": "configs/htp_settings.json"
  }
}
```

保持顶层的后端扩展文件与你已安装的 QAIRT 版本所示的 schema 一致。把图和设备调优放入被引用的 HTP 配置文件，而不是在 `backend_extensions` 旁边新造同级键。从匹配的 SDK 示例开始，并逐步添加设置。

***

## 步骤 9：对照正确的基线进行验证

始终对照 ONNX Runtime FP32 黄金基线，而不是 TensorRT FP16。

为什么？TensorRT 的输出已经包含了 NVIDIA 特定的图变换和精度行为。中立的参考是源模型的导出。

建议的验证关口：

| 阶段                 | 关口                                  |
| ------------------ | ----------------------------------- |
| ONNX 简化一致性         | cosine >= 0.9999                    |
| FP32 Qualcomm 工件验证 | cosine >= 0.999，SQNR >= 30 dB       |
| 量化/上下文输出           | cosine >= 0.99，SQNR >= 20 dB，加上应用指标 |
| YOLO 应用指标          | 对验证集的 mAP/召回率/精确率                   |

一个小的比较辅助脚本：

```python theme={null}
import numpy as np

ref = np.load("ort_float_output.npy").flatten()
test = np.fromfile("device_output.raw", dtype=np.float32).flatten()

cosine = np.dot(ref, test) / (np.linalg.norm(ref) * np.linalg.norm(test))
sqnr = 10 * np.log10(
    np.sum(ref ** 2) / (np.sum((ref - test) ** 2) + 1e-10)
)

print(f"Cosine: {cosine:.4f}")
print(f"SQNR: {sqnr:.1f} dB")
```

对于目标检测，张量相似度不够。你还需要任务级指标：

```text theme={null}
人脸召回率
每帧的误报数
如果存在带标签的验证数据则 mAP
NMS 后的框一致性
延迟 / FPS / 功耗
```

***

## 步骤 10：把 YOLO 后处理移入应用

Jetson 的 Python 示例常常把 YOLO 解码和 NMS 隐藏在 Ultralytics 之后。一旦你导出和部署，你可能会收到原始张量。

让后处理显式化：

```text theme={null}
原始输出解码
置信度阈值
类别过滤
NMS
把框缩放回原始帧
```

对于只有人脸的模型，保持简单。只有一个类别。只有在你真的需要时，通用的 COCO 后处理器才值得加入。

同时锁定预处理：

```text theme={null}
letterbox vs resize
RGB vs BGR
0..1 vs mean/std 归一化
NCHW vs NHWC
uint8 vs float32 输入
```

预处理不匹配可能看起来像量化问题。在指责加速器之前先对预处理进行数值验证。

***

## 步骤 11：只有在功能验证之后再做基准测试

先证明 HTP 工作：

```bash theme={null}
qnn-platform-validator --backend dsp --testBackend
```

然后带剖析运行：

```bash theme={null}
export ADSP_LIBRARY_PATH="/data/qairt_runtime/dsp;/usr/lib/rfsa/adsp;/dsp"

./bin/qnn-net-run \
  --retrieve_context best_ctx.bin \
  --backend libQnnHtp.so \
  --perf_profile burst \
  --profiling_level detailed \
  --input_list input_list.txt \
  --output_dir outputs/
```

在主机上解析：

```bash theme={null}
scp ubuntu@DEVICE_IP:/data/qairt_runtime/outputs/qnn-profiling-data.log ./

$QAIRT_SDK_ROOT/bin/x86_64-linux-clang/qnn-profile-viewer \
  --input_log qnn-profiling-data.log \
  --reader $QAIRT_SDK_ROOT/lib/x86_64-linux-clang/libQnnHtpProfilingReader.so
```

查看加速器执行时间、CPU 回退层和瓶颈算子。如果模型延迟好但应用 FPS 差，瓶颈在推理之外。

| 症状             | 可能的瓶颈                      |
| -------------- | -------------------------- |
| QNN 剖析很快，应用很慢  | 摄像头、预处理、后处理、显示或编码          |
| CPU 负载高        | 拷贝、回退算子、Python 胶水或 NMS/后处理 |
| HTP 时间低但 FPS 低 | 流水线饥饿或排队                   |
| 延迟尖峰           | 队列、同步、热节流或内存压力             |

捕获内存变化量而不是仅仅原始峰值：

```bash theme={null}
ssh ubuntu@DEVICE_IP "cat /proc/meminfo | grep MemAvailable" > mem_baseline.txt
```

然后在推理期间测量并报告与空闲的差值。

***

## YOLO 迁移的基准测试计划

在两台设备上使用相同的输入片段、预处理契约、后处理阈值和准确率集。在做出平台声明之前，请捕获这个矩阵：

| 平台                       | 工件             | 运行时        | 精度             | 测量            |
| ------------------------ | -------------- | ---------- | -------------- | ------------- |
| Jetson Orin Nano/Orin NX | `best.engine`  | TensorRT   | FP16/INT8      | 延迟、FPS、功耗、准确率 |
| Dragonwing 目标            | `best_ctx.bin` | QNN/HTP    | a8w8           | 延迟、FPS、功耗、准确率 |
| Dragonwing 目标            | FP32 路径        | CPU/GPU 回退 | 受支持处 FP32/FP16 | 正确性/回退行为      |

如果你引用外部基准数字，请把它们标为源文档参考，并与你自己的设备结果分开。基准方法比借用的头条数字更重要。

***

## 常见陷阱

| 陷阱                 | 修复                                |
| ------------------ | --------------------------------- |
| 只有 `.engine`，没有源模型 | 先恢复 `.pt`、`.onnx` 或训练检查点          |
| 使用动态 ONNX 形状       | 为 HTP 路径导出静态形状                    |
| 与 TensorRT 输出对比    | 与 ONNX Runtime FP32 基线对比          |
| 重用 TRT 校准缓存        | 用 Qualcomm 工具重新校准                 |
| 合成校准张量             | 使用真实的目标领域图像                       |
| 运行时库架构不匹配          | 对库执行 `file`；目标构建应为 ARM aarch64    |
| 把上下文二进制文件当成可移植的    | 在 SoC/SDK/BSP/模型变更时重建             |
| 跳过 HTP 验证器         | 在基准测试前运行 `qnn-platform-validator` |
| 隐藏的 YOLO 后处理       | 在应用代码中让解码/NMS 显式化                 |
| 未知的错误分桶            | 保留日志并升级                           |

***

## 这次迁移真正改变了什么

应用目标保持不变：

```text theme={null}
摄像头帧 -> 人脸框 -> 叠加/仪表板
```

部署机制变化了：

```text theme={null}
Jetson:
  best.pt -> best.engine -> TensorRT/CUDA

Dragonwing:
  best.pt -> ONNX -> QNN 模型库或 DLC 风格工件 -> 上下文二进制文件 -> QNN/HTP
```

最大的思维转变是模型部署变成了一条分阶段验证的流水线。每个阶段都有工件和关口。这不是仪式。这是你避免同时调试量化、算子支持、预处理、运行时库和应用代码的方式。

在下一篇文章中，我们将聚焦最被低估的阶段：量化。那是准确率退化通常出现的地方，是校准数据质量重要的地方，也是"能跑"和"能交付"之间的差异变得明显的地方。
