Maltium 模态平台文档

欢迎来到 Maltium 模态平台!本平台是一个面向真实生产环境的端侧 AI 模型训练、推理与部署的一站式竞赛/评测平台。

与传统机器学习平台不同,Maltium 不仅评估模型的准确率——你需要在计算资源受限的端侧 AI 计算环境中完成一整套解决方案,包括数据准备、模型设计、格式转换、量化优化和工程化部署。

本文档包含以下内容:

快速上手教程

我们将通过完成 #1. MNIST 手写数字识别 来演示完整的开发流程。如果你已有丰富的模型训练经验,可以直接跳到导出 ONNX 模型部分。大部分题目也提供了基准解决方案(Baseline),你可以直接在此基础上优化。

一、训练模型

1.1 题目数据格式

点击题目页面中的「下载数据」按钮即可获得训练数据。训练数据始终满足以下结构:

├── train
│   ├── input
│   │   ├── 0001.npy
│   │   ├── 0002.npy
│   │   └── ...
│   └── output
│        ├── 0001.npy
│        ├── 0002.npy
│        └── ...
  • train/input 目录包含训练数据的输入部分。
  • train/output 目录包含对应的标签(ground truth)。
  • 每个输入文件与输出文件通过文件名一一对应,例如 0001.npy 的输入对应 0001.npy 的标签。

1.2 编写训练代码

以下是一个基于 PyTorch 的完整训练示例:

import os
import tqdm
import torch
import numpy as np

class NpyDataset(torch.utils.data.Dataset):
    def __init__(self, input_dir, output_dir):
        self.input_dir = input_dir
        self.output_dir = output_dir
        self.file_names = sorted(os.listdir(input_dir))

    def __len__(self):
        return len(self.file_names)

    def __getitem__(self, idx):
        file_name = self.file_names[idx]
        input_data = np.load(os.path.join(self.input_dir, file_name)).astype(np.float32)
        output_data = np.load(os.path.join(self.output_dir, file_name)).astype(np.float32)
        input_tensor = torch.from_numpy(input_data).unsqueeze(0)
        output_tensor = torch.from_numpy(output_data)
        return input_tensor, output_tensor

class CNN(torch.nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv_layers = torch.nn.Sequential(
            torch.nn.Conv2d(1, 32, kernel_size=3, padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(2),
            torch.nn.Conv2d(32, 64, kernel_size=3, padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(2)
        )
        self.fc_layers = torch.nn.Sequential(
            torch.nn.Flatten(),
            torch.nn.Linear(64 * 7 * 7, 128),
            torch.nn.ReLU(),
            torch.nn.Linear(128, 10)
        )

    def forward(self, x):
        x = self.conv_layers(x)
        x = self.fc_layers(x)
        return x

# --- 1. 配置参数 ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
batch_size = 32
learning_rate = 0.001
epochs = 5

# --- 2. 准备数据 ---
dataset = NpyDataset(input_dir='train/input', output_dir='train/output')
train_loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True)

# --- 3. 实例化模型、损失函数和优化器 ---
model = CNN().to(device)
criterion = torch.nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

# --- 4. 训练循环 ---
for epoch in range(epochs):
    model.train()
    running_loss = 0.0
    for inputs, labels in tqdm.tqdm(train_loader):
        inputs, labels = inputs.to(device), labels.to(device)
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        running_loss += loss.item()
    print(f"Epoch [{epoch+1}/{epochs}], Loss: {running_loss/len(train_loader):.4f}")

print("训练完成!")

二、导出 ONNX 模型

2.1 为什么要导出为 ONNX?

在真实的工业端侧设备(手机芯片、自动驾驶计算卡等)上,直接运行 PyTorch 环境通常是不现实的。ONNX(Open Neural Network Exchange)是一种开放的通用模型格式,推理环境更加轻量级,不再需要安装数 GB 的 PyTorch,并且可以被进一步优化(量化、TensorRT 加速等)。

2.2 从 PyTorch 导出

import torch

model = model.eval().to('cpu')  # 确保模型在 CPU 上且处于评估模式
dummy_input = torch.randn(1, 1, 28, 28)  # 模拟输入,形状必须与模型输入一致
torch.onnx.export(
    model, dummy_input, "model.onnx",
    export_params=True,
    opset_version=11   # 推荐使用 ONNX 算子集版本 11
)

运行完成后,当前目录下会生成 model.onnx 文件。

三、编写推理脚本并提交

3.1 评测环境数据结构

评测系统会在容器中创建以下目录结构:

./data/
  input/      # 测试输入数据(.npy 文件)
  output/     # 你的程序需要在此写入预测结果(.npy 文件)

3.2 使用 aidqnn SDK 编写推理脚本

平台内置了 aidqnn 推理 SDK,提供了高度封装的接口来加载各种模型(ONNX、TFLite、QNN)并执行推理。创建 inference.py

import os
import numpy as np
from aidqnn import AutoModel

INPUT_DIR = './data/input'
OUTPUT_DIR = './data/output'

model = AutoModel("model.onnx", device="cpu")  # 加载 ONNX 模型

input_files = [f for f in os.listdir(INPUT_DIR) if f.endswith('.npy')]
for filename in input_files:
    input_path = os.path.join(INPUT_DIR, filename)
    output_path = os.path.join(OUTPUT_DIR, filename)
    input_data = np.load(input_path).astype(np.float32)
    input_data = input_data[np.newaxis, np.newaxis, :, :]  # 添加 batch 和 channel 维度

    output = model(input_data)  # 输出 shape: (1, 10)

    result = np.zeros([10,], dtype=np.float32)
    result[np.argmax(output)] = 1.0  # 转为 one-hot 编码
    np.save(output_path, result)

3.3 创建入口脚本

在同目录下创建 start.sh

#!/bin/bash
python3 inference.py

3.4 打包与提交

⚠️ 注意 提交时请确保只选中文件(model.onnxinference.pystart.sh)而非包含这些文件的整个目录进行压缩。ZIP 压缩包的根目录中必须直接包含 start.sh,否则评测系统将无法正确识别提交内容。

将上述三个文件打包为 ZIP 文件,在题目页面点击「提交」即可。

3.5 提交规范汇总

  • 提交格式为 ZIP 压缩包,根目录必须包含 start.sh
  • 输出文件名必须与输入文件名完全相同(如 input/0001.npyoutput/0001.npy)。
  • 必须处理 ./data/input/ 中的所有 .npy 文件。
  • 输出数据的 shape 和 dtype 必须符合题目要求的格式。
  • 程序的总运行时间(包括模型加载和所有样本推理)受题目设定的时间限制约束。
  • 可以在代码中输出调试信息到 stdout/stderr,不影响评测。
  • ZIP 中不得包含 aidqnn.py 文件(该文件由评测系统自动注入)。

四、GPU 加速

4.1 端侧 GPU 概述

端侧设备上的 GPU 通常集成在 SoC(系统级芯片)中,与 PC 上的 NVIDIA / AMD GPU 在架构和性能上有很大不同——功耗限制下,计算能力和内存带宽更低。不过即便如此,使用端侧 GPU 进行推理通常仍然显著优于 CPU。

4.2 使用 AIMO 平台转换模型

  1. 登录 AIMO 平台,进入「模型优化」功能模块。
  2. 上传 model.onnx 文件。
  3. 选择目标设备为 Qualcomm QCS6490(Maltium 平台默认评测设备)。
  4. 目标框架选择 Qualcomm QNN 2.31
  5. 确认模型输入输出信息无误后,直接点击「提交」(GPU 不需要量化)。
  6. 下载转换完成的模型文件。
💡 提示 也可以选择 TFLite 等框架进行 GPU 计算,但不同框架对模型结构和算子支持有不同要求。一般推荐优先使用 QNN,兼容性最好。AIMO 的更多使用细节请参阅其官方文档

4.3 使用 GPU 模型进行推理

在下载的压缩包中,找到后缀含 aarch64.gcc9_4.so 的文件。修改推理脚本:

model = AutoModel("libmodel_qcs6490_fp32.qnn231.aarch64.gcc9_4.so.amf", device="gpu")  # 唯一修改

将新的模型文件、修改后的 inference.pystart.sh 重新打包提交即可。

📝 备注 对于 MNIST 这类简单任务,GPU 提升不明显(瓶颈在数据 I/O)。在计算密集型任务中,GPU 的性能优势会更加显著。

五、NPU 量化推理

5.1 什么是 NPU?

NPU(Neural Processing Unit,神经处理单元)是专为 AI 任务设计的计算单元。NPU 主要面向量化运算优化——将 float32 权重和激活值映射到 int8 甚至 int4,避免复杂浮点运算,从而大幅提升计算效率和功耗比。

📝 关于量化精度损失 量化不是免费午餐,通常会带来一定精度损失。但通过量化感知训练(QAT)或后训练量化(PTQ)等技术,通常可以将精度损失控制在极小范围内。

5.2 准备校准数据

校准数据用于分析模型各层的激活值分布,确定量化参数。通常直接使用训练数据的一部分即可:

import os
import numpy as np

input_dir = 'train/input'
calibration_dir = 'calib_data'
os.makedirs(calibration_dir, exist_ok=True)

input_files = sorted(os.listdir(input_dir))
num_calib_samples = 100  # 选择 100 个样本

for i in range(num_calib_samples):
    file_name = input_files[i]
    input_data = np.load(os.path.join(input_dir, file_name)).astype(np.float32)
    input_data = input_data[np.newaxis, np.newaxis, :, :]  # 匹配模型输入形状
    input_data.tofile(os.path.join(calibration_dir, file_name + ".raw"))  # 保存为原始二进制格式

5.3 在 AIMO 平台上进行量化

  1. 上传 model.onnx,选择 Qualcomm QNN 2.31
  2. 在参数设置页面中开启量化选项
  3. 量化精度选择 int8,均衡方式选择 CLE(或使用默认设置)。
  4. 选择「raw 模式」校准辅助数据,上传整个 calib_data 目录。
  5. 确认后点击「提交」,等待转换完成。

5.4 使用量化模型

在下载的文件中找到后缀含 ctx.bin 的文件,修改推理脚本:

model = AutoModel("model_qcs6490_w8a8.qnn231.ctx.bin.amf", device="npu")  # 使用 NPU
# 注意:只有量化模型才能指定 device="npu"
⚠️ 重要 即使模型内部是量化的,输入数据仍然需要是 float32 类型。SDK 会在底层自动进行量化处理。输出同样是 float32

出题指南

本节面向题目创建者,介绍如何在 Maltium 平台上创建、配置和发布一道 AI 评测题目。

测试数据规范

压缩包目录结构

测试数据必须以 ZIP 压缩包形式上传,且严格遵循以下结构:

test/
  input/
    0001.npy
    0002.npy
    ...
  output/
    0001.npy
    0002.npy
    ...

详细要求

  • 必须是有效的 ZIP 文件。解压后根目录下为 test/inputtest/output 两个文件夹。
  • 仅支持 .npy 文件。文件名需一一对应(如 input/0001.npy 对应 output/0001.npy)。
  • 若某输入文件缺少同名标签文件,该样本会被跳过且不计入总分。
  • 如果 input 目录无任何有效样本对,将判定为数据错误。
  • 不同任务类型要求不同的输出 shape 和数据类型——详见评测指标参考

任务类型与评测配置

添加题目页面中,你需要配置以下关键字段:

选择任务领域和类型

平台支持四大领域共 14 种任务类型:

领域任务类型可选评测指标输出格式
计算机视觉 (CV) 图像分类 (Image Classification) Macro F-Beta (默认)
Micro F-Beta
Subset Accuracy
[C] — C个类别的multi-hot编码
计算机视觉 (CV) 目标检测 (Object Detection) mAP @ IoU (默认)
[M, 6] — M为最大框数,6代表 [x1, y1, x2, y2, confidence, class_id]
计算机视觉 (CV) 语义分割 (Semantic Segmentation) mIoU (默认)
[H, W] — 输出与原图相同分辨率,每个像素对应一个类别id
计算机视觉 (CV) 关键点检测 (Keypoint Detection) OKS (默认)
[K, 3] — K个关键点,3代表 [x, y, visibility]
自然语言处理 (NLP) 文本分类 (Text Classification) Macro F-Beta (默认)
Micro F-Beta
Subset Accuracy
[C] — C个类别的multi-hot编码
自然语言处理 (NLP) 序列标注 (Sequence Labeling) Entity-level F1 (默认)
[L] — L为文本最大长度,输出每个token的类别id
自然语言处理 (NLP) 抽取式问答 (Extractive QA) Token-level F1 (默认)
Exact Match
[2] — 输出答案在原文中的 [start_index, end_index]
自然语言处理 (NLP) 结构化输出 (Structured Output) Schema Validation Gate + Field Exact Match (默认)
String — 合法的 JSON 字符串
音频 (Audio) 音频分类 (Audio Classification) Macro F-Beta (默认)
Micro F-Beta
Subset Accuracy
[C] — C个类别的multi-hot编码
音频 (Audio) 语音识别 (ASR) WER (默认)
CER
String — 识别出的文本序列
音频 (Audio) 语音增强 (Speech Enhancement) SI-SDRi (默认)
PESQ
STOI
[T] — 干净的波形数据
结构化数据 (Tabular) 结构化数据分类 (Tabular Classification) Macro F-Beta (默认)
Micro F-Beta
Subset Accuracy
[C] — C个类别的multi-hot编码
结构化数据 (Tabular) 时序预测 (Time Series Forecasting) MAPE (默认)
[T, D] — 预测未来 T 个时间步长,每个步长 D 个特征
结构化数据 (Tabular) 排序推荐 (Ranking) NDCG @ K (默认)
[N] — N 个候选物品的相关性/点击率预估分数

评测指标参数

部分评测指标支持自定义参数。例如:

  • Macro/Micro F-Beta:可配置 beta 值(默认 1.0),beta < 1 偏重精确率,beta > 1 偏重召回率。
  • mAP:可配置 IoU 阈值范围 iou_start(默认 0.5)、iou_end(0.95)、iou_step(0.05)。
  • NDCG:可配置 K 值(默认 10)。
  • PESQ / STOI / SI-SDRi:可配置 sampling_rate(默认 16000)。

成就等级与阈值

每道题目设置三个准确率阈值,根据提交得分判定成就等级:

成就等级默认阈值描述
卓越级 (Excellent)≥ 95%生产可用,极少错误
预期级 (Expected)≥ 80%满足实际需求
及格级 (Pass)≥ 60%基本功能可用
未入级 (Fail)< 60%功能不完整,无奖励

具体阈值可由出题者在题目设置页面自定义配置。

资源限制设置

出题者可以为每道题目设置以下资源限制:

限制项说明
时间限制 (Time Limit)总运行时间限制(秒),包括模型加载、数据读写和所有样本推理。超时判定为 TLE
内存限制 (Memory Limit)Docker 容器内存上限(MB)。超出判定为 MLE
输出大小限制 (Output Size Limit)输出目录的最大大小(MB)。超出判定为 OLE
输出时间限制 (Output Time Limit)收集输出结果的最大时间(秒)。

评测状态码

状态含义
Accepted (AC)得分达到及格阈值
Wrong Answer (WA)程序正常退出但得分不足
Partially Correct部分正确
TLE超时
MLE内存超限
OLE输出超限
RE运行时错误(脚本异常退出)
File Error无效 ZIP、缺少 start.sh 等文件格式问题
System Error评测系统内部错误,请联系管理员

评测指标参考

所有评测指标返回的分数均归一化到 [0, 1] 区间,然后转换为百分制(0-100%)。每个测试样本独立评测并累加,最终得分 = (总分 ÷ 样本数) × 100%。

计算机视觉 (CV)

Macro F-Beta

适用任务:图像分类、文本分类、音频分类、结构化数据分类

输出格式[C],C 个类别的 multi-hot 编码

对每个类别分别计算精确率 P 和召回率 R,再计算 F-Beta 分数,最后对所有类别取宏平均

计算公式:

$$F_\beta = \frac{(1 + \beta^2) \cdot P \cdot R}{\beta^2 \cdot P + R + \varepsilon}$$

其中阈值取 0.5:预测值 > 0.5 视为正类。对每个类别计算 TP、FP、FN 后独立得出该类的 F-Beta,然后取所有有效类别的平均值。

参数类型默认值说明
betafloat1.0平衡精确率和召回率。beta < 1 偏重精确率,beta > 1 偏重召回率

Micro F-Beta

适用任务:图像分类、文本分类、音频分类、结构化数据分类

输出格式[C],C 个类别的 multi-hot 编码

将所有类别的 TP、FP、FN 全局汇总后再计算 F-Beta。与 Macro 不同,Micro 不区分类别,样本多的类别对结果影响更大。

参数类型默认值说明
betafloat1.0同 Macro F-Beta

Subset Accuracy

适用任务:图像分类、文本分类、音频分类、结构化数据分类

输出格式[C],C 个类别的 multi-hot 编码

最严格的多标签评测方式——只有当预测结果与标签在所有类别上完全一致时才计为正确。阈值取 0.5。

mAP(平均精度均值)

适用任务:目标检测

输出格式[M, 6],M 个框,每行 [x1, y1, x2, y2, confidence, class_id]

标准 VOC / COCO 风格的 mAP 计算:

  1. 按置信度降序排列预测框。
  2. 在每个 IoU 阈值下,逐框匹配 GT(贪心匹配,已匹配的 GT 不可复用)。
  3. 计算累积精确率-召回率曲线,通过插值法计算每个类别的 AP。
  4. 对所有 IoU 阈值 和 所有类别取平均得到最终 mAP。
参数类型默认值说明
iou_startfloat0.5起始 IoU 阈值
iou_endfloat0.95终止 IoU 阈值
iou_stepfloat0.05IoU 阈值步长
💡 注意事项
  • 边界框坐标格式为 [x1, y1, x2, y2](左上角和右下角)。
  • 置信度范围为 [0, 1]。
  • 全零行会被自动过滤,无检测时使用全零填充而非省略
  • 预测和标签均要求至少 6 列,否则返回 0 分。

mIoU(平均交并比)

适用任务:语义分割

输出格式[H, W],每个像素为整数类别索引

基于混淆矩阵计算每个类别的 IoU(交集 / 并集),然后取所有有效类别的平均值。

参数类型默认值说明
ignore_labelint255忽略标签值,标注为该值的像素不参与计算
📝 注意 输出必须是整数类别索引,不是概率图。类别编号从 0 开始。

OKS(目标关键点相似度)

适用任务:关键点检测

输出格式[K, 3],K 个关键点,每行 [x, y, visibility]

计算预测关键点与 GT 之间的距离,通过高斯核归一化到 [0, 1]:

$$\text{OKS}_k = \exp\!\left(-\frac{d_k^2}{2 \cdot (\text{scale} \times 0.1)^2}\right)$$

其中 d_k 是欧氏距离,scale 由可见关键点的边界范围决定。最终取所有可见关键点的平均 OKS。

自然语言处理 (NLP)

Entity-level F1

适用任务:序列标注(NER 等)

输出格式[L],L 为文本最大长度,每个位置为整数类别标签(0 为 O/背景)

基于实体级匹配(chunk match):首先提取连续相同标签的 span(起止位置+标签类别),然后计算预测 span 集合与 GT span 集合之间的精确率、召回率和 F1。

📝 说明 标签值 0 被视为背景(O 标签),不参与 span 提取。只有当 span 的起止位置和标签类别完全一致时才算命中。

Token-level F1

适用任务:抽取式问答

输出格式[2],即 [start_index, end_index]

计算预测答案 span 与标准答案 span 在 token 级别的重叠度:

$$\text{Precision} = \frac{\text{overlap\_len}}{\text{pred\_len}}, \quad \text{Recall} = \frac{\text{overlap\_len}}{\text{gt\_len}}, \quad F_1 = \frac{2PR}{P + R}$$

Exact Match

适用任务:抽取式问答

输出格式[2],即 [start_index, end_index]

预测结果与标签完全一致返回 1.0,否则返回 0.0。

Schema Validation Gate + Field Exact Match

适用任务:结构化输出

输出格式:合法的 JSON 字符串

先将预测和标签字符串解析为 JSON 对象,然后递归比较所有键值对是否完全一致。Schema 不一致(键不同)或值不匹配均返回 0。

💡 提示 确保输出是有效的 JSON 字符串,推荐使用 json.dumps() 生成标准格式。

音频 (Audio)

WER(词错误率)

适用任务:语音识别 (ASR)

输出格式:文本字符串

使用编辑距离(Levenshtein distance)计算词级错误率,然后转换为分数:

$$\text{WER} = \frac{\text{edit\_distance}(\text{pred\_words},\; \text{gt\_words})}{\text{len}(\text{gt\_words})}$$

$$\text{Score} = \max(0,\; 1 - \text{WER})$$

CER(字符错误率)

适用任务:语音识别 (ASR)

输出格式:文本字符串

与 WER 类似,但在字符级别计算编辑距离。计算前会移除空格。

$$\text{CER} = \frac{\text{edit\_distance}(\text{pred\_chars},\; \text{gt\_chars})}{\text{len}(\text{gt\_chars})}$$

$$\text{Score} = \max(0,\; 1 - \text{CER})$$

SI-SDRi(尺度不变信号失真比改进量)

适用任务:语音增强

输出格式[T],干净的波形数据

计算增强后信号的 SI-SDR 相对于原始混合信号的改进量:

$$\text{SI-SDR}(\hat{s}, s) = 10 \cdot \log_{10}\frac{\lVert\alpha \cdot s\rVert^2}{\lVert\hat{s} - \alpha \cdot s\rVert^2}$$

$$\text{SI-SDRi} = \text{SI-SDR}(\text{enhanced}, \text{clean}) - \text{SI-SDR}(\text{mixture}, \text{clean})$$

$$\text{Score} = \operatorname{clip}\!\left(\frac{\text{SI-SDRi}}{20},\; 0,\; 1\right)$$

典型 SI-SDRi 范围为 0-20 dB,归一化到 [0, 1]。如果没有混合信号输入数据,则回退到绝对 SI-SDR 归一化。

参数类型默认值说明
sampling_rateint16000采样率(Hz),仅用于日志记录

PESQ(感知语音质量评估)

适用任务:语音增强

输出格式[T],干净的波形数据

ITU-T P.862 标准。PESQ 原始输出范围 [-0.5, 4.5],归一化方式:

$$\text{Score} = \operatorname{clip}\!\left(\frac{\text{PESQ}_{\text{raw}} + 0.5}{5.0},\; 0,\; 1\right)$$

参数类型默认值说明
sampling_rateint1600016000 使用宽带(wb)模式,其他使用窄带(nb)模式

STOI(短时客观可懂度)

适用任务:语音增强

输出格式[T],干净的波形数据

STOI 原始输出范围 [0, 1],直接作为分数使用。

参数类型默认值说明
sampling_rateint16000采样率(Hz)
⚠️ 音频任务注意事项
  • 保持音频采样率一致,预测信号长度必须与标签完全相同。
  • PESQ 和 STOI 依赖额外的 Python 库(pesqpystoi),评测环境已预装。

结构化数据 (Tabular)

MAPE(平均绝对百分比误差)

适用任务:时序预测

输出格式[T, D],预测未来 T 个时间步长、每步 D 个特征

$$\text{MAPE} = \operatorname{mean}\!\left(\frac{|\text{gt} - \text{pred}|}{|\text{gt}| + \varepsilon}\right), \quad \text{Score} = e^{-\text{MAPE}}$$

MAPE 越低,分数越接近 1。

NDCG @ K(归一化折损累计增益)

适用任务:排序推荐

输出格式[N],N 个候选物品的相关性/点击率预估分数

根据预测分数降序排列,取前 K 个计算 DCG 并除以理想 DCG:

$$\text{DCG@}K = \sum_{i=0}^{K-1} \frac{\text{rel}_i}{\log_2(i+2)}, \quad \text{NDCG@}K = \frac{\text{DCG@}K}{\text{IDCG@}K}$$

参数类型默认值说明
kint10取前 K 个结果计算 NDCG

aidqnn SDK 参考

aidqnn 是 Maltium 平台的高层 Python 推理 SDK,对底层 aidlite C++ 接口进行了深度封装,提供极简 API 来加载和运行 AI 模型。

核心特性

  • 极简加载:单行代码加载模型文件或 AIMO 导出文件夹。
  • 多框架支持:无缝支持 ONNX(cpu)、TFLite(cpugpu)和 QNN(cpugpunpu)。
  • 智能排布修正:QNN 优化可能改变张量轴顺序(如 NCHW → NHWC),SDK 能根据配置文件自动完成转置。
  • 环境自适应:自动识别 AidLux 社区版与企业版,智能切换 QNN 的 Remote/Local 推理模式。

框架与设备支持矩阵

模型类型文件扩展名支持的设备
ONNX.onnxcpu
TFLite.tflitecpu, gpu
QNN.so, .bin, .amf, .aidemcpu, gpu, npu

AutoModel

AutoModel 是推荐的模型加载入口,提供两种使用方式:从 AIMO 导出文件夹加载(from_path)和从单个模型文件加载(直接构造)。

AutoModel.from_path()

AutoModel.from_path(model_path, device=None, **kwargs) → AidModel

从 AIMO 平台导出的模型文件夹中自动推断最佳配置并加载模型。这是最推荐的加载方式。

参数类型必填说明
model_pathstr | PathLikeAIMO 导出文件夹的路径。不要重命名或删除其中的文件
devicestr | None目标设备。传 None 则自动推断:检测到 ctx.bin 则使用 "npu",否则默认 "gpu"
**kwargs其他关键字参数会透传给 AidModel.__init__(),如 is_remote

自动推断逻辑

  1. 扫描文件夹内的文件,按优先级依次匹配 QNN → TFLite → ONNX。
  2. 对于 QNN 模型:
    • 自动查找 *_net.json 配置文件。
    • 如果检测到 ctx.bin 文件,强制使用 device="npu"
    • 自动检测 AidLux 版本:社区版使用 Remote 模式(选择 aarch64.ndk.so),企业版使用 Local 模式(选择 aarch64.gcc9_4.so)。
⚠️ 注意 传给 from_path 的文件夹必须是直接从 AIMO 平台下载并解压后的原始文件夹。SDK 依赖文件夹内的文件结构(ctx.bin.so*_net.json 等)自动推断配置。
from aidqnn import AutoModel

model = AutoModel.from_path("./model_save_path")
output = model(input_data)

AutoModel() 直接构造

AutoModel(model_path, model_type=None, device="cpu", **kwargs) → AidModel

从单个模型文件加载。SDK 会根据文件扩展名自动推断模型类型。如果推断失败,可以手动指定 model_type

参数类型必填说明
model_pathstr | PathLike模型文件路径
model_typestr | None模型类型:"onnx""tflite""qnn"。传 None 则自动推断。
devicestr目标设备。默认 "cpu"。必须在该模型类型的支持设备列表中。
**kwargs透传给 AidModel.__init__()。重要参数包括 model_configis_remoteinput_shapesoutput_shapes
from aidqnn import AutoModel

# ONNX(仅支持 CPU)
onnx_model = AutoModel("model.onnx", device="cpu")

# TFLite(支持 CPU/GPU)
tflite_model = AutoModel("model.tflite", device="gpu")

# QNN(强烈建议传 model_config)
qnn_model = AutoModel(
    "model_aarch64.ndk.so",
    model_config="model_net.json",
    device="gpu"
)
⚠️ 手动加载 QNN 模型时 强烈建议同时传入 model_config*_net.json 文件路径)。QNN 优化时经常做通道排布转换(如 NCHW → NHWC)。不传 config 则需手动 np.transpose,传入后 SDK 自动处理。

AidModel

AidModel 是底层模型类,通常通过 AutoModel 间接创建。支持上下文管理器协议。

AidModel.__init__()

AidModel(model_path, model_type="qnn", device="npu", is_remote=None, input_shapes=None, output_shapes=None, model_config=None)
参数类型必填说明
model_pathstr | PathLike模型文件路径。文件必须存在,否则抛出 FileNotFoundError
model_typestr模型框架:"qnn"(默认)、"onnx""tflite"
devicestr目标计算设备:"npu"(默认)、"gpu""cpu"
is_remotebool | NoneQNN 推理模式。True = Remote 模式,False = Local 模式,None = 自动检测(社区版默认 Remote,企业版默认 Local)。非 QNN 模型强制为 False
input_shapestuple[tuple[int, ...], ...] | None条件必填输入张量形状元组。Remote 模式下必须提供。Local 模式下可自动推断。
output_shapestuple[tuple[int, ...], ...] | None条件必填输出张量形状元组。与 input_shapes 同理。
model_configstr | NoneQNN 配置文件路径(*_net.json)。传入后自动推断 shapes 和轴格式。强烈建议为 QNN 模型提供

初始化流程详解

  1. 如果提供了 model_config,解析其中的 converter_command 获取输入/输出张量名称和形状。
  2. 根据 model_type 和环境变量 AIDLUX_TYPE 确定推理模式(Local/Remote)。
  3. 创建 aidlite 底层 interpreter 并加载模型。
  4. 从模型本身(Local 模式)或 config 中获取输入/输出 tensor 信息。
  5. 如果提供了 model_config,加载轴格式信息,为每个 tensor 设置自动转置参数。
📝 关于 Remote 模式 社区版 AidLux(手机上下载的版本)仅支持 Remote 模式。Remote 模式下 input_shapesoutput_shapes 必须显式提供,无法自动推断。

AidModel.__call__()

model(*inputs, **kwargs) → np.ndarray | tuple[np.ndarray, ...]

执行模型推理。支持三种传参方式:

方式一:位置传参

output = model(input1, input2)   # 顺序必须与模型定义一致

方式二:字典传参

output = model({"input_1": data1, "input_2": data2})  # 键名须与模型输入名匹配

方式三:关键字传参

output = model(input_1=data1, input_2=data2)

返回值

  • 单输出模型:直接返回 np.ndarray
  • 多输出模型:返回 tuple[np.ndarray, ...]
# 单输出
result = model(input_data)                  # np.ndarray

# 多输出
out1, out2 = multi_output_model(input_data) # tuple

推理流程

  1. 参数解析:将位置参数、字典或关键字参数统一为有序输入序列。
  2. Shape 校验:校验每个输入的形状(考虑轴格式转换后的期望形状)。
  3. 轴格式转换:如果配置了自动转置,使用 np.moveaxis 将输入从原始格式转换为 QNN 内部格式。
  4. 数据喂入:转换为 bytes 后通过 set_input_tensor 设置。
  5. 推理执行:调用 invoke()
  6. 输出收集:读取输出 tensor,reshape 为输出形状,并做逆轴格式转换。
⚠️ 形状校验规则 所有框架(ONNX、TFLite、QNN)均不支持动态形状。输入数据的 shape 必须与模型导出时固化的 shape 完全一致,否则抛出 ValueError

轴格式自动转换

QNN 在优化模型时可能改变张量的通道排布(Channel Last 优化),例如:

原始格式优化后格式场景
NCHWNHWC4D 张量(图像等)
NCFNFC3D 张量(序列等)

当传入 model_config 参数时,SDK 会自动:

  • 解析配置中每个张量的 axis_formatsrc_axis_format 字段。
  • 推理前:将输入数据从用户的原始格式(如 NCHW)自动转置为 QNN 内部格式(如 NHWC)。
  • 推理后:将输出数据从 QNN 内部格式自动转置回用户期望的原始格式。

这意味着你的预处理和后处理代码可以始终按照模型的原始设计编写,完全不需要关心 QNN 做了什么轴变换。

📝 技术细节 如果配置中有 permute_order_to_src 字段但没有 axis_format,SDK 会使用该排列顺序直接作为转置参数。

开发者须知

静态形状限制

当前所有框架均不支持动态形状。输入数据的 shape 必须与模型导出时固化的 shape 完全一致。

数据类型安全

  • QNN 模型的输入数据必须为 np.float32,否则抛出 ValueError
  • 对于量化模型,虽然内部使用 int8,但输入输出仍为 float32——SDK 自动处理量化/反量化。
  • 在 AIMO 转换模型时,尽量确保模型输入输出的数据类型为 float32,以避免不可预料的结果和潜在的精度损失。

社区版 vs 企业版

社区版(normal)企业版
QNN 模式仅 RemoteLocal + Remote
推荐模式RemoteLocal(更低延迟)
非量化模型文件选含 aarch64.ndk.so 的文件选含 aarch64.gcc9_4.so 的文件
量化模型文件统一选含 ctx.bin 的文件
自动检测SDK 通过环境变量 AIDLUX_TYPE 自动判断

输入名称处理

模型输入名中的 ./ 会被替换为 _,以数字开头的名称会自动加 _ 前缀。在使用字典或关键字传参时,请使用处理后的名称。

常见错误及排查

错误信息原因与解决方案
Unknown device设备名不在支持列表中。检查 device 参数。
input_shapes and output_shapes MUST be explicitly providedRemote 模式下必须提供 shapes。手动指定或传入 model_config
QNN model expects float32 inputs输入数据类型错误。确保使用 .astype(np.float32)
shape mismatch输入 shape 与模型期望不符。检查数据维度。
Input tensor count mismatch输入数量与模型不匹配。检查模型需要几个输入。
start.sh not foundZIP 根目录缺少 start.sh。确保直接压缩文件而非目录。
aidqnn.py not allowed in submissionZIP 中不要包含 aidqnn.py,由系统自动注入。
未在文件夹中找到 QNN 必需的配置文件from_path 的文件夹缺少 *_net.json。确保使用原始 AIMO 导出目录。
用户指定了 'npu' 设备,但文件夹下缺少 ctx.bin 文件NPU 需要量化模型。请先在 AIMO 上执行量化。

上下文管理器与资源释放

AidModel 支持 with 语句,退出时自动释放底层资源。也可以让 Python GC 自动清理。

with AutoModel("model.onnx", device="cpu") as model:
    output = model(input_data)
# 离开 with 块后,底层资源自动释放