# 神经网络模型存档

## 概述

神经网络模型存档是我们自己的格式，它将模型可执行文件和配置文件打包成一个.tar.xz档案。 配置文件编码了模型所需的架构、预处理和后处理以及其他相关信息，以便在我们的生态系统中无缝实现。
例如，当使用[ModelConverter](https://docs.luxonis.com/software-v3/ai-inference/conversion/rvc-conversion/offline/modelconverter.md)工具或通过[Hub中的详细转换](https://docs.luxonis.com/cloud/hubai/model-registry/detailed-conversion.md)转换模型时，简化了转换参数的指定，或在DepthAI流水线中部署模型时处理预处理和后处理。

> 神经网络模型存档始终描述存储在其中的模型可执行文件。 如果存档包含
> `ONNX`
> 模型，则
> `config.json`
> 中的值应与该
> `ONNX`
> 模型匹配。 如果存档包含编译后的
> `RVC`
> 模型，则
> `config.json`
> 中的值应描述该编译模型。

## 模型可执行文件

模型可执行文件构成用于推理的实际模型。
如果存档用于转换，则文件需采用[ModelConverter](https://docs.luxonis.com/software-v3/ai-inference/conversion/rvc-conversion/offline/modelconverter.md)支持的一种格式：

 * ONNX (.onnx),
 * OpenVINO IR (.xml 和 .bin), 或
 * TensorFlow Lite (.tflite)。

如果存档旨在在我们的设备上进行推理，则文件需要采用与目标运行的RVC平台要求相匹配的RVC编译格式：

 * 二进制大对象 (.blob 或 .superblob) 用于RVC2和RVC3平台，或
 * 深度学习容器 (.dlc) 用于RVC4平台。

## 配置

config.json文件编码了配置模式版本以及一个表示模型的字典，包含输入、输出、头部和元数据部分。

### 获取值的途径

当手动构建ONNX神经网络模型存档时，最常见的数据来源包括：

 * 原始训练或推理代码，用于预处理和输出语义，
 * [Netron](https://netron.app/)，用于张量名称、形状和布局，
 * 以及onnxruntime，用于输入输出名称、形状和数据类型。

### 输入

此部分配置模型的输入流。 定义为Input字典列表。每个包含以下字段：

 * name (str) - 输入层的名称。从存档中的模型复制。
 * dtype (str) - 输入张量数据类型（例如float32或uint8）。应与模型输入张量期望的数据类型匹配。
 * input_type (str) - 输入数据类型（'raw'或'image'）。
 * shape (list of ints) - 输入数据的形状，以整数列表表示（例如[H,W]、[H,W,C]、[N,H,W,C]等）。应与模型输入张量形状完全匹配。
 * layout (str) - 输入数据维度的字符码解释（例如NCHW）。应与模型张量形状使用的顺序匹配。
 * preprocessing (dict) - 应用于输入数据的预处理。
   * mean (list of floats) - 按通道顺序的均值。顺序取决于模型训练时的通道顺序。
   * scale (list of floats) - 按通道顺序的标准化值。顺序取决于模型训练时的通道顺序。
   * reverse_channels (bool) - 如果为True，模型输入为RGB；否则为BGR。已弃用，将在未来版本中被dai_type标志取代。
   * interleaved_to_planar (bool)：如果为True，模型输入为交错格式(NHWC)；否则为平面格式(NCHW)。已弃用，将在未来版本中被dai_type标志取代。
   * dai_type (str)：DepthAI输入类型，由DepthAI读取以自动设置流水线。

> `mean`
> 和
> `scale`
> 应描述NN Archive中存储的模型所期望的预处理。如果原始预处理是
> `input = (input / 255.0 - mean) / std`
> ，则将
> `255 * mean`
> 存储为
> `mean`
> ，
> `255 * std`
> 存储为
> `scale`
> 。在
> `RGB`
> 和
> `BGR`
> 之间切换时不要交换mean和scale的值；只需重新排列通道以匹配模型期望的编码。

### 输出

此部分配置模型的输出流。 定义为Output字典列表。每个包含以下字段：

 * name (str) - 输出层的名称。
 * dtype (str) - 输出数据的数据类型（例如float32）。
 * shape (list of ints) - 输出张量的形状。
 * layout (str) - 输出张量维度的字符码解释（例如NC或NCHW）。

### 头部

此部分配置应用于模型输出的后处理步骤。 定义为Head字典列表。每个包含以下字段：

 * parser (str) - 负责对模型输出进行后处理的depthai-nodes解析器名称。
 * outputs (list of str) - 应输入到解析器的输出名称列表。如果为None，则输入所有输出。
 * metadata
   (dict)：头部特定的元数据。有关更多信息，请参阅此[源代码](https://github.com/luxonis/luxonis-ml/blob/main/luxonis_ml/nn_archive/config_building_blocks/base_models/head_metadata.py)。

Heads 部分是可选的。如果未定义，则默认输出为原始数据（无需后处理）。 当模型输出需要语义解释时（例如分类的类别名称，或检测/分割的解析器专用元数据），应使用 heads。

### 元数据

本部分表示模型的元数据。 定义为一个 Metadata 字典，包含以下字段：

 * name（字符串）- 模型名称。
 * path（字符串）- 模型可执行文件的路径（例如 'model.onnx'）。 该路径相对于存档根目录。 如果是 OpenVINO IR 模型，请仅提供 .xml 文件的路径，并确保 .bin 文件位于同一路径下。
 * precision（字符串）- 模型权重的精度（例如 float32 或 float16）。 这是存储的模型文件本身的精度，而非单个输入或输出张量的数据类型。

此外，可在此部分添加任意字段以帮助理解模型。

## 生成

要生成 NN Archive，请遵循以下步骤：

 * 通过从头训练或从现有来源获取（注意模型格式）来准备模型可执行文件。

 * 准备 config.json。 以下是一个简单的单输入/输出 ONNX 模型配置示例，无需任何预处理（mean=0, scale=1, reverse_channels=False）或后处理要求：

```json
{
    "config_version": null,
    "model": {
        "metadata": {
            "name": "ModelName",
            "path": "model_name.onnx",
            "precision": "float32"
        },
        "inputs": [
            {
                "name": "input_layer_name",
                "dtype": "float32",
                "input_type": "image",
                "shape": [
                    1,
                    3,
                    256,
                    256
                ],
                "layout": "NCHW",
                "preprocessing": {
                    "mean": [
                        0.0,
                        0.0,
                        0.0
                    ],
                    "scale": [
                        1.0,
                        1.0,
                        1.0
                    ],
                    "reverse_channels": false,
                    "interleaved_to_planar": null
                }
            }
        ],
        "outputs": [
            {
                "name": "output",
                "dtype": "float32",
                "shape": [
                    1,
                    3
                ],
                "layout": "NC"
            }
        ],
        "heads": []
    }
}
```

我们可以通过扩展 heads 部分来添加后处理步骤。 以下是一个具有三个分类类别的模型头部示例：

```json
{
    ...
    "model": {
        ...
        "heads": [
            {
                "parser": "ClassificationParser",
                "metadata": {
                    "postprocessor_path": null,
                    "classes": [
                        "Class1",
                        "Class2",
                        "Class3"
                    ],
                    "n_classes": 3,
                    "is_softmax": true
                },
                "outputs": [
                    "output"
                ]
            }
        ]
    }
}
```

 * 安装 luxonis-ml 并运行 Archive Generator：

```python
from luxonis_ml.nn_archive.archive_generator import ArchiveGenerator
from luxonis_ml.nn_archive.config import CONFIG_VERSION
import json

cfg_path = ... # 配置数据 JSON 的字符串路径。
with open(cfg_path, "r") as file:
    cfg_dict = json.load(file)
cfg_dict["config_version"] = CONFIG_VERSION # 从 luxonis-ml 设置配置版本

generator = ArchiveGenerator(
    archive_name=..., # 生成的存档的字符串名称。
    save_path=..., # 要保存存档文件的字符串路径。
    cfg_dict=cfg_dict,
    executables_paths=... # 相关模型可执行文件的字符串路径列表。
    )

generator.make_archive() # 存档文件保存到指定的 save_path
```

## 多阶段模型

模型有时由多个阶段组成。 一个常见的例子是两阶段模型，包含主模型（第一阶段）和后处理器（第二阶段），每个都有自己的可执行文件。 我们也支持将此类模型打包到 NN Archive 中。 首先按照上述步骤定义第一阶段的配置文件。 第二阶段模型只需在
Heads.Metadata 部分设置 postprocessor_path 参数即可定义。 它应指向第二阶段模型的可执行文件（路径必须相对于存档根目录）。 NN Archive 可以使用 ArchiveGenerator
构建。请确保第一阶段和第二阶段模型的可执行文件路径都提供在 executables_paths 参数中。

> 请注意，配置文件仅与第一阶段模型相关，无法为第二阶段模型定义任何信息。 如有必要，建议构建两个独立的NN Archive。
