K230 nncase开发指南
1. 概述
1.1 什么是nncase
nncase是一个为 AI 加速器设计的神经网络编译器, 目前支持的 target有cpu/K210/K510/K230等.
nncase提供的功能
- 支持多输入多输出网络,支持多分支结构
- 静态内存分配,不需要堆内存
- 算子合并和优化
- 支持 float 和uint8/int8量化推理
- 支持训练后量化,使用浮点模型和量化校准集
- 平坦模型,支持零拷贝加载
nncase支持的神经网络模型格式
- tflite
- onnx
1.2 nncase架构

nncase软件栈包括compiler和runtime两部分。
Compiler: 用于在PC上编译神经网络模型,最终生成kmodel文件。主要包括importer, IR, Evaluator, Quantize, Transform优化, Tiling, Partition, Schedule, Codegen等模块。
- Importer: 将其它神经网络框架的模型导入到nncase中
- IR: 中间表示, 分为importer导入的Neutral IR(设备无关)和Neutral IR经lowering转换生成的Target IR(设备相关)
- Evaluator: Evaluator提供IR的解释执行能力,常被用于Constant Folding/PTQ Calibration等场景
- Transform: 用于IR转换和图的遍历优化等
- Quantize: 训练后量化, 对要量化的tensor加入量化标记, 根据输入的校正集, 调用 Evaluator进行解释执行, 收集tensor的数据范围, 插入量化/反量化结点, 最后优化消除不必要的量化/反量化结点等
- Tiling: 受限于NPU较低的存储器容量,需要将大块计算进行拆分. 另外, 计算存在大量数据复用时选择Tiling参数会对时延和带宽产生影响
- Partition: 将图按ModuleType进行切分, 切分后的每个子图会对应RuntimeModule, 不同类型的RuntimeModule对应不同的Device(cpu/K230)
- Schedule: 根据优化后图中的数据依赖关系生成计算顺序并分配Buffer
- Codegen: 对每个子图分别调用ModuleType对应的codegen,生成RuntimeModule
Runtime: 集成于用户App, 提供加载kmodel/设置输入数据/KPU执行/获取输出数据等功能.
1.3 开发环境
1.3.1 操作系统
支持的操作系统包括Ubuntu 18.04/Ubuntu 20.04
1.3.2 软件环境
| 序号 | 软件 | 版本号 |
|---|---|---|
| 1 | python | 3.6/3.7/3.8/3.9/3.10 |
| 2 | pip | >=20.3 |
| 3 | numpy | 1.19.5 |
| 4 | onnx | 1.9.0 |
| 5 | onnx-simplifier | 0.3.6 |
| 6 | Onnxoptimizer | 0.2.6 |
| 7 | Onnxruntime | 1.8.0 |
| 8 | dotnet-runtime | 7.0 |
1.3.3 硬件环境
K230 evb
2. 编译模型APIs(Python)
nncase提供了Python APIs, 用于在PC上编译神经网络模型
2.1 支持的算子
2.1.1 tflite算子
| Operator | Is Supported |
|---|---|
| ABS | Yes |
| ADD | Yes |
| ARG_MAX | Yes |
| ARG_MIN | Yes |
| AVERAGE_POOL_2D | Yes |
| BATCH_MATMUL | Yes |
| CAST | Yes |
| CEIL | Yes |
| CONCATENATION | Yes |
| CONV_2D | Yes |
| COS | Yes |
| CUSTOM | Yes |
| DEPTHWISE_CONV_2D | Yes |
| DIV | Yes |
| EQUAL | Yes |
| EXP | Yes |
| EXPAND_DIMS | Yes |
| FLOOR | Yes |
| FLOOR_DIV | Yes |
| FLOOR_MOD | Yes |
| FULLY_CONNECTED | Yes |
| GREATER | Yes |
| GREATER_EQUAL | Yes |
| L2_NORMALIZATION | Yes |
| LEAKY_RELU | Yes |
| LESS | Yes |
| LESS_EQUAL | Yes |
| LOG | Yes |
| LOGISTIC | Yes |
| MAX_POOL_2D | Yes |
| MAXIMUM | Yes |
| MEAN | Yes |
| MINIMUM | Yes |
| MUL | Yes |
| NEG | Yes |
| NOT_EQUAL | Yes |
| PAD | Yes |
| PADV2 | Yes |
| MIRROR_PAD | Yes |
| PACK | Yes |
| POW | Yes |
| REDUCE_MAX | Yes |
| REDUCE_MIN | Yes |
| REDUCE_PROD | Yes |
| RELU | Yes |
| PRELU | Yes |
| RELU6 | Yes |
| RESHAPE | Yes |
| RESIZE_BILINEAR | Yes |
| RESIZE_NEAREST_NEIGHBOR | Yes |
| ROUND | Yes |
| RSQRT | Yes |
| SHAPE | Yes |
| SIN | Yes |
| SLICE | Yes |
| SOFTMAX | Yes |
| SPACE_TO_BATCH_ND | Yes |
| SQUEEZE | Yes |
| BATCH_TO_SPACE_ND | Yes |
| STRIDED_SLICE | Yes |
| SQRT | Yes |
| SQUARE | Yes |
| SUB | Yes |
| SUM | Yes |
| TANH | Yes |
| TILE | Yes |
| TRANSPOSE | Yes |
| TRANSPOSE_CONV | Yes |
| QUANTIZE | Yes |
| FAKE_QUANT | Yes |
| DEQUANTIZE | Yes |
| GATHER | Yes |
| GATHER_ND | Yes |
| ONE_HOT | Yes |
| SQUARED_DIFFERENCE | Yes |
| LOG_SOFTMAX | Yes |
| SPLIT | Yes |
| HARD_SWISH | Yes |
2.1.2 onnx算子
| Operator | Is Supported |
|---|---|
| Abs | Yes |
| Acos | Yes |
| Acosh | Yes |
| And | Yes |
| ArgMax | Yes |
| ArgMin | Yes |
| Asin | Yes |
| Asinh | Yes |
| Add | Yes |
| AveragePool | Yes |
| BatchNormalization | Yes |
| Cast | Yes |
| Ceil | Yes |
| Celu | Yes |
| Clip | Yes |
| Compress | Yes |
| Concat | Yes |
| Constant | Yes |
| ConstantOfShape | Yes |
| Conv | Yes |
| ConvTranspose | Yes |
| Cos | Yes |
| Cosh | Yes |
| CumSum | Yes |
| DepthToSpace | Yes |
| DequantizeLinear | Yes |
| Div | Yes |
| Dropout | Yes |
| Elu | Yes |
| Exp | Yes |
| Expand | Yes |
| Equal | Yes |
| Erf | Yes |
| Flatten | Yes |
| Floor | Yes |
| Gather | Yes |
| GatherElements | Yes |
| GatherND | Yes |
| Gemm | Yes |
| GlobalAveragePool | Yes |
| GlobalMaxPool | Yes |
| Greater | Yes |
| GreaterOrEqual | Yes |
| GRU | Yes |
| Hardmax | Yes |
| HardSigmoid | Yes |
| HardSwish | Yes |
| Identity | Yes |
| InstanceNormalization | Yes |
| LayerNormalization | Yes |
| LpNormalization | Yes |
| LeakyRelu | Yes |
| Less | Yes |
| LessOrEqual | Yes |
| Log | Yes |
| LogSoftmax | Yes |
| LRN | Yes |
| LSTM | Yes |
| MatMul | Yes |
| MaxPool | Yes |
| Max | Yes |
| Min | Yes |
| Mul | Yes |
| Neg | Yes |
| Not | Yes |
| OneHot | Yes |
| Pad | Yes |
| Pow | Yes |
| PRelu | Yes |
| QuantizeLinear | Yes |
| RandomNormal | Yes |
| RandomNormalLike | Yes |
| RandomUniform | Yes |
| RandomUniformLike | Yes |
| ReduceL1 | Yes |
| ReduceL2 | Yes |
| ReduceLogSum | Yes |
| ReduceLogSumExp | Yes |
| ReduceMax | Yes |
| ReduceMean | Yes |
| ReduceMin | Yes |
| ReduceProd | Yes |
| ReduceSum | Yes |
| ReduceSumSquare | Yes |
| Relu | Yes |
| Reshape | Yes |
| Resize | Yes |
| ReverseSequence | Yes |
| RoiAlign | Yes |
| Round | Yes |
| Rsqrt | Yes |
| Selu | Yes |
| Shape | Yes |
| Sign | Yes |
| Sin | Yes |
| Sinh | Yes |
| Sigmoid | Yes |
| Size | Yes |
| Slice | Yes |
| Softmax | Yes |
| Softplus | Yes |
| Softsign | Yes |
| SpaceToDepth | Yes |
| Split | Yes |
| Sqrt | Yes |
| Squeeze | Yes |
| Sub | Yes |
| Sum | Yes |
| Tanh | Yes |
| Tile | Yes |
| TopK | Yes |
| Transpose | Yes |
| Trilu | Yes |
| ThresholdedRelu | Yes |
| Upsample | Yes |
| Unsqueeze | Yes |
| Where | Yes |
2.2 APIs
目前编译模型APIs支持tflite/onnx等格式的深度学习模型。
2.2.1 CompileOptions
【描述】
CompileOptions类, 用于配置nncase编译选项,各属性说明如下
| 属性名称 | 类型 | 是否必须 | 描述 |
|---|---|---|---|
| target | string | 是 | 指定编译目标, 如’cpu’, ‘k230’ |
| dump_ir | bool | 否 | 指定是否dump IR, 默认为False |
| dump_asm | bool | 否 | 指定是否dump asm汇编文件, 默认为False |
| dump_dir | string | 否 | 前面指定dump_ir等开关后, 这里指定dump的目录, 默认为”” |
| input_file | string | 否 | onnx模型超过2GB时,用于指定参数文件路径,默认为”” |
| preprocess | bool | 否 | 是否开启前处理,默认为False。以下参数仅在 preprocess=True时生效 |
| input_type | string | 否 | 开启前处理时指定输入数据类型,默认为”float”。当 preprocess为 True时,必须指定为”uint8”或者”float32” |
| input_shape | list[int] | 否 | 开启前处理时指定输入数据的shape,默认为[]。当 preprocess为 True时,必须指定 |
| input_range | list[float] | 否 | 开启前处理时指定输入数据反量化后的浮点数范围,默认为[ ]。当 preprocess为 True且 input_type为 uint8时,必须指定 |
| input_layout | string | 否 | 指定输入数据的layout,默认为”” |
| swapRB | bool | 否 | 是否在 channel维度反转数据,默认为False |
| mean | list[float] | 否 | 前处理标准化参数均值,默认为[0,0,0] |
| std | list[float] | 否 | 前处理标准化参数方差,默认为[1,1,1] |
| letterbox_value | float | 否 | 指定前处理letterbox的填充值,默认为0 |
| output_layout | string | 否 | 指定输出数据的layout, 默认为”” |
| shape_bucket_enable | bool | 是 | 是否开启ShapeBucket功能,默认为False。在 dump_ir=True时生效 |
| shape_bucket_range_info | Dict[str, [int, int]] | 是 | 每个输入shape维度信息中的变量的范围,最小值必须大于等于1 |
| shape_bucket_segments_count | int | 是 | 输入变量的范围划分为几段 |
| shape_bucket_fix_var_map | Dict[str, int] | 否 | 固定shape维度信息中的变量为特定的值 |
2.2.1.1 前处理流程说明
目前暂不支持自定义前处理顺序,可以根据以下流程示意图,选择所需要的前处理参数进行配置。

参数说明:
-
input_range为输入数据类型为定点时,反量化后的浮点数范围。a. 输入数据类型为uint8,range为[0,255],
input_range为[0,255],则反量化的作用只是进行类型转化,将uint8的数据转化为float32,mean和std参数仍然按照[0,255]的数据进行指定。b. 输入数据类型为uint8,range为[0,255],
input_range为[0,1],则反量化会将定点数转化为浮点数[0,1],mean和std参数需要按照0~1的数据进行指定。
-
input_shape为输入数据的shape,layout为input_layout,现在支持字符串("NHWC"、"NCHW")和index两种方式作为input_layout,并且支持非4D的数据处理。 当按照字符串形式配置input_layout时,表示输入数据的layout;当按照index形式配置input_layout时,表示输入数据会按照当前配置的input_layout进行数据转置,即input_layout为Transpose的perm参数。

output_layout同理,如下图所示。

2.2.1.2 动态shape参数说明
ShapeBucket是针对动态shape的一种解决方案,会根据输入长度的范围以及指定的段的数量来对动态shape进行优化。该功能默认为false,需要打开对应的option才能生效,除了指定对应的字段信息,其他流程与编译静态模型没有区别。
- onnx
在模型的shape中会有些维度为变量名字,这里以一个onnx模型的输入为例
tokens: int64[batch_size, tgt_seq_len] step: float32[seq_len, batch_size]
shape的维度信息中存在seq_len,tgt_seq_len,batch_size这三个变量。 首先是batch_size,虽然是变量的但实际应用的时候固定为3,因此在fix_var_map中添加batch_size = 3,在运行的时候会将这个维度固定为3。 seq_len,tgt_seq_len两个是实际会发生改变的,因此需要配置这两个变量的实际范围,也就是range_info的信息。segments_count是实际分段的数量,会根据范围等分为几份,对应的编译时间也会相应增加几倍。
以下为对应的编译参数示例:
compile_options = nncase.CompileOptions()
compile_options.shape_bucket_enable = True
compile_options.shape_bucket_range_info = {"seq_len": [1, 100], "tgt_seq_len": [1, 100]}
compile_options.shape_bucket_segments_count = 2
compile_options.shape_bucket_fix_var_map = {"batch_size": 3}
- tflite
tflite的模型与onnx不同,shape上暂未标注维度的名称,目前只支持输入中具有一个维度是动态的,并且名称统一配置为-1,配置方式如下:
compile_options = nncase.CompileOptions()
compile_options.shape_bucket_enable = True
compile_options.shape_bucket_range_info = {"-1":[1, 100]}
compile_options.shape_bucket_segments_count = 2
compile_options.shape_bucket_fix_var_map = {"batch_size" : 3}
配置完这些选项后整个编译的流程和静态shape一致。
2.2.1.3 参数配置示例
实例化CompileOptions,配置各属性的值。
compile_options = nncase.CompileOptions()
compile_options.target = "cpu" #"k230"
compile_options.dump_ir = True # if False, will not dump the compile-time result.
compile_options.dump_asm = True
compile_options.dump_dir = "dump_path"
compile_options.input_file = ""
# preprocess args
compile_options.preprocess = False
if compile_options.preprocess:
compile_options.input_type = "uint8" # "uint8" "float32"
compile_options.input_shape = [1,224,320,3]
compile_options.input_range = [0,1]
compile_options.input_layout = "NHWC" # "NHWC" ”NCHW“
compile_options.swapRB = False
compile_options.mean = [0,0,0]
compile_options.std = [1,1,1]
compile_options.letterbox_value = 0
compile_options.output_layout = "NHWC" # "NHWC" "NCHW"
# Dynamic shape args
compile_options.shape_bucket_enable = False
if compile_options.shape_bucket_enable:
compile_options.shape_bucket_range_info = {"seq_len": [1, 100], "tgt_seq_len": [1, 100]}
compile_options.shape_bucket_segments_count = 2
compile_options.shape_bucket_fix_var_map = {"batch_size": 3}
2.2.2 ImportOptions
【描述】
ImportOptions类, 用于配置nncase导入选项
【定义】
class ImportOptions:
def __init__(self) -> None:
pass
【示例】
实例化ImportOptions, 配置各属性的值
#import_options
import_options = nncase.ImportOptions()
2.2.3 PTQTensorOptions
【描述】
PTQTensorOptions类, 用于配置nncase PTQ选项
| 名称 | 类型 | 描述 |
|---|---|---|
| calibrate_method | string | 否 |
| samples_count | int | 否 |
| finetune_weights_method | string | 否 |
| quant_type | string | 否 |
| w_quant_type | string | 否 |
| quant_scheme | string | 否 |
| quant_scheme_strict_mode | bool | 否 |
| export_quant_scheme | bool | 否 |
| export_weight_range_by_channel | bool | 否 |
- 混合量化参数说明
- quant_scheme:导入量化参数配置文件的路径
- quant_scheme_strict_mode:是否严格按照quant_scheme执行量化
- export_quant_scheme:是否导出量化参数配置文件
- export_weight_range_by_channel:是否导出
bychannel形式的weights量化参数,为了保证量化效果,该参数建议设置为True
具体使用流程见 MixQuant说明
【示例】
# ptq_options
ptq_options = nncase.PTQTensorOptions()
ptq_options.samples_count = 6
ptq_options.finetune_weights_method = "NoFineTuneWeights"
ptq_options.quant_type = "uint8"
ptq_options.w_quant_type = "uint8"
ptq_options.set_tensor_data(generate_data(input_shape, ptq_options.samples_count, args.dataset))
ptq_options.quant_scheme = ""
ptq_options.quant_scheme_strict_mode = False
ptq_options.export_quant_scheme = True
ptq_options.export_weight_range_by_channel = True
compiler.use_ptq(ptq_options)
2.2.4 set_tensor_data
【描述】
设置tensor数据
【定义】
def set_tensor_data(self, data: List[List[np.ndarray]]) -> None:
reshape_data = list(map(list, zip(*data)))
self.cali_data = [RuntimeTensor.from_numpy(
d) for d in itertools.chain.from_iterable(reshape_data)]
【参数】
| 名称 | 类型 | 描述 |
|---|---|---|
| data | List[List[np.ndarray] | 读取的校准数据 |
【返回值】
无
【示例】
# ptq_options
ptq_options = nncase.PTQTensorOptions()
ptq_options.samples_count = 6
ptq_options.set_tensor_data(generate_data(input_shape, ptq_options.samples_count, args.dataset))
compiler.use_ptq(ptq_options)
2.2.5 Compiler
【描述】
Compiler类, 用于编译神经网络模型
【定义】
class Compiler:
_target: _nncase.Target
_session: _nncase.CompileSession
_compiler: _nncase.Compiler
_compile_options: _nncase.CompileOptions
_quantize_options: _nncase.QuantizeOptions
_module: IRModule
2.2.6 import_tflite
【描述】
导入tflite模型
【定义】
def import_tflite(self, model_content: bytes, options: ImportOptions) -> None:
self._compile_options.input_format = "tflite"
self._import_module(model_content)
【参数】
| 名称 | 类型 | 描述 |
|---|---|---|
| model_content | byte[] | 读取的模型内容 |
| import_options | ImportOptions | 导入选项 |
【返回值】
无
【示例】
model_content = read_model_file(model)
compiler.import_tflite(model_content, import_options)
2.2.7 import_onnx
【描述】
导入onnx模型
【定义】
def import_onnx(self, model_content: bytes, options: ImportOptions) -> None:
self._compile_options.input_format = "onnx"
self._import_module(model_content)
【参数】
| 名称 | 类型 | 描述 |
|---|---|---|
| model_content | byte[] | 读取的模型内容 |
| import_options | ImportOptions | 导入选项 |
【返回值】
无
【示例】
model_content = read_model_file(model)
compiler.import_onnx(model_content, import_options)
2.2.8 use_ptq
【描述】
设置PTQ配置选项.
- K230默认必须使用量化。