欢迎使用MNN文档

遇到问题请先查看文档和FAQ,如果没有答案请在Github提issue或在钉钉群提问。

MNN介绍

_images/banner.pngMNN

MNN Homepage

MNN是一个轻量级的深度神经网络引擎,支持深度学习的推理与训练。适用于服务器/个人电脑/手机/嵌入式各类设备。目前,MNN已经在阿里巴巴的手机淘宝、手机天猫、优酷等30多个App中使用,覆盖直播、短视频、搜索推荐、商品图像搜索、互动营销、权益发放、安全风控等场景。

_images/architecture.png架构图

在阿里巴巴中,MNN被用作为Walle系统中计算容器的基础模块。Walle是首个端到端、通用型、规模化产业应用的端云协同机器学习系统,发表于操作系统顶会OSDI 2022。Walle的论文中解释了MNN的关键设计理念,并提供了MNN相对于其他深度学习框架(TensorFlow, TensorFlow Lite, PyTorch, PyTorch Mobile, TVM)的benchmark测试结果。相关测试脚本和说明文档被放在“/benchmark”目录下。如果MNN或Walle的设计对你的研究或生产有所助益,欢迎引用我们的OSDI论文:

@inproceedings {proc:osdi22:walle,
    author = {Chengfei Lv and Chaoyue Niu and Renjie Gu and Xiaotang Jiang and Zhaode Wang and Bin Liu and Ziqi Wu and Qiulin Yao and Congyu Huang and Panos Huang and Tao Huang and Hui Shu and Jinde Song and Bin Zou and Peng Lan and Guohuan Xu and Fei Wu and Shaojie Tang and Fan Wu and Guihai Chen},
    title = {Walle: An {End-to-End}, {General-Purpose}, and {Large-Scale} Production System for {Device-Cloud} Collaborative Machine Learning},
    booktitle = {16th USENIX Symposium on Operating Systems Design and Implementation (OSDI 22)},
    year = {2022},
    isbn = {978-1-939133-28-1},
    address = {Carlsbad, CA},
    pages = {249--265},
    url = {https://www.usenix.org/conference/osdi22/presentation/lv},
    publisher = {USENIX Association},
    month = jul,
}

工作台

MNN官网上还可以下载MNN团队全新力作MNN工作台,涵盖开箱即用模型、可视化训练等工具,更可以一键部署到多端设备。

整体特点

轻量性

  • 主体功能(模型推理CPU+GPU)无任何依赖,代码精简,可以方便地部署到移动设备和各种嵌入式设备中。

    • iOS平台:功能全开的MNN静态库 armv7+arm64大小12MB左右,链接生成可执行文件增加大小2M左右。可裁剪主体功能后静态库大小6.1M ,链接生成可执行文件增加大小 600 KB。

    • Android平台:主体功能 armv7a - c++_shared 动态库大小800KB左右。

  • 支持采用 Mini 编辑选项进一步降低包大小,大约能在上述库体积基础上进一步降低 25% 左右。

  • 支持模型FP16/Int8压缩与量化,可减少模型50% - 75% 的体积

通用性

  • 支持 Tensorflow、Caffe、ONNX、Torchscripts 等主流模型文件格式,支持CNN / RNN / GAN / Transformer 等主流网络结构。

  • 支持多输入多输出,支持任意维度的输入输出,支持动态输入(输入大小可变),支持带控制流的模型

  • 算子丰富,支持 178 个Tensorflow Op、52个 Caffe Op、163个 Torchscipts Op、158 个 ONNX Op(ONNX 基本完整支持)

  • 支持 服务器 / 个人电脑 / 手机 及具有POSIX接口的嵌入式设备,支持使用设备的 CPU / GPU 计算,支持部分设备的 NPU 计算(IOS 11 + CoreML / Huawei + HIAI / Android + NNAPI)

  • 支持 Windows / iOS 8.0+ / Android 4.3+ / Linux  及具有POSIX接口的操作系统

高性能

  • 对iOS / Android / PC / Server 的CPU架构进行了适配,编写SIMD代码或手写汇编以实现核心运算,充分发挥 CPU的算力,单线程下运行常见CV模型接近设备算力峰值

  • 支持基于 Metal / OpenCL / Vulkan 使用移动端设备上的GPU进行推理

  • 支持基于 CUDA 使用 PC / Server 上的 NVIDIA GPU 实现更快速的推理

  • 广泛运用了 Winograd 卷积算法提升卷积性能,首次在业界工程实践中实现转置卷积的Winograd算法优化与矩阵乘的Strassen算法优化,并取得加速效果

  • 支持低精度计算( int8 / fp16 / bf16)以提升推理性能。并对 ARMv8.2 和 AVX512架构的相关指令进行了适配,这两种架构下有更好的加速效果

易用性

  • 支持使用 MNN 的算子进行常用的数值计算,覆盖 numpy 常用功能

  • 提供 MNN CV 模块,支持图像仿射变换与归一化等 MNN_CV 库,支持常用的图像处理(armv7a 架构下小于 100 k )

  • 支持各平台下的模型训练,尤其是移动端上的模型训练

  • 支持 python 调用

MNN适配的硬件架构与精度详见下表:

  • S :支持,深度优化并已有应用场景,推荐使用

  • A :支持,有初步优化或已有应用场景,可以使用

  • B :支持,无优化或在实验状态,不推荐使用

  • C :不支持

Architecture / Precision Normal FP16 BF16 Int8
CPU Native B C B B
x86/x64-SSE4.1 A B B A
x86/x64-AVX2 S B B A
x86/x64-AVX512 S B B S
ARMv7a S S (ARMv8.2) S S
ARMv8 S S (ARMv8.2) S(ARMv8.6) S
GPU OpenCL A S C C
Vulkan A A C C
Metal A S C C
CUDA A S C C
NPU CoreML B C C C
HIAI B C C B
NNAPI B C C C

工具

基于MNN (张量计算引擎),提供了一系列工具,以支持模型推理、训练和通用计算:

  • MNN-Converter:模型转换工具,由Frontends和Graph Optimize构成。前者负责支持不同的训练框架,MNN当前支持Tensorflow(Lite)、Caffe、ONNX(PyTorch/MXNet的模型可先转为ONNX模型再转到MNN)和Torchscripts;后者通过算子融合、算子替代、布局调整等方式优化图,一般离线运行。

  • MNN-Compress: 模型压缩工具,在一定的精度误差许可下,对MNN模型进行压缩,减少模型体积,提升运行性能。

  • MNN-Express :支持带控制流的模型运行,支持调用 MNN 的算子进行自定义的计算。

  • MNN-CV :类似 OpenCV ,但核心计算功能基于 MNN 实现的图像处理算法库

  • MNN-Train :MNN 训练模块,支持各平台训练

社区交流与反馈

钉钉群组:

历史论文

MNN初步版本的论文也曾在MLSys 2020上面发表。该论文主要关注MNN作为移动端机器学习推理引擎的手动算子优化。如果MNN之前对你的研究有所助益,欢迎引用MNN的MLSys论文:

@inproceedings{alibaba2020mnn,
  author = {Jiang, Xiaotang and Wang, Huan and Chen, Yiliu and Wu, Ziqi and Wang, Lichuan and Zou, Bin and Yang, Yafeng and Cui, Zongyang and Cai, Yu and Yu, Tianhang and Lv, Chengfei and Wu, Zhihua},
  title = {MNN: A Universal and Efficient Inference Engine},
  booktitle = {MLSys},
  year = {2020}
}

License

Apache 2.0

致谢

MNN参与人员:淘宝技术部、搜索工程团队、达摩院团队、优酷等集团员工。

MNN参考、借鉴了下列项目:

发布版本

2.6.0 (Latest)

新特性

  • 新增int8量化算子支持:

    • Softmax

    • Interp

    • Binary

    • Unary

    • Scale

  • OpenCL 支持 Loop 算子特定情形;

    • BatchMatMul

    • Gather

  • x86_64支持Gelu-bf16;

  • CUDA支持bf16模型推理;

  • benchmark 工具支持直接测试模型量化后的性能(不需要先用量化工具量化模型)

  • Pymnn Tensor/Var使用Tuple创建时支持混合类型数据;

  • 权值量化模型支持低内存推理模式,计算时反量化;

  • 支持ChatGLM-6B模型推理内存占用3G;

  • 支持构建了ChatGLM-MNN Android app;

优化

  • OpenCL支持高通reocrd queue ,以降低创建 GPU Command Buffer 所需的时间; Oneplus 9 机型 Benchmark 测试结果如下

Model unrecord record
resnet-v2-50.mnn 21.254 20.160
MobileNetV2_224.mnn 4.853 4.186
mobilenet-v1-1.0.mnn 6.424 5.315
nasnet.mnn 46.751 20.260
SqueezeNetV1.0.mnn 7.35 6.832
squeezenetv1.1.mnn 3.936 3.693
mobilenetV3.mnn 14.201 6.743
inception-v3.mnn 33.111 32.032
  • 稀疏卷积内存优化,降低内存占用;

  • 减少异构(CPU低精度/GPU)运行 MNN 模型时的常量内存占用;

  • CUDA优化int8算子性能;

  • 减少Permute几何计算产生的region数量;

  • 重新调整ConvolutionInt8及im2col在AVX512-VNNI下的分块大小,提升性能20%-30%;

  • X86新增bilinear/nearest sample的SIMD实现,提升ImageProcess性能 50% 左右;

Bugfix

  • 关联 Github Issue 解决

    • 修复CUDA Raster错误导致输出为0的问题;issue-2333

    • 修复OpenCL Gather算子出错的问题;issue-2424

    • 修复ImageProcess出错的问题;issue-2386

    • OpenCL支持用户选择device id; issue-2343

  • 其他 Bugfix

    • CUDA CMakeList对未支持架构增加报错信息;

    • testMNNFromOnnx脚本在模型测试正确时不启用DEBUG模式;

    • load_module_from_file中的shape_mutable默认改为True(存在子图的模型无法在False情形下运行);

    • MNNConvert使用keepInputFormat选项时,也同时将输出Tensor的format转换为原始格式

    • 修复log记录时设备为空时Crash的情况;

    • 修复BinaryOp单元测试在Windows下无法编译的问题;

    • 修复MNN_SUPPORT_DEPRECATED_OP宏不控制OptimizedComputer的问题;

    • 修复fp16多线程且分块方向为channel时convolution计算出错的问题;

    • 修复deconvolutionInt8访存越界的问题;

    • 修复TensorArrayWrite几何计算产生zero region的问题;

    • 修复CUDA depthwise conv出错的问题;

    • 修复一些文档格式、内容的错误;

    • 修复多线程下createRuntime和setGlobalConfig出错的问题;

    • 修复Vec.hpp中无用代码导致的编译失败问题;

    • 修复OpenCL对gpuDevice的assert失败的问题;

    • 修复OpenCL bianry mod出错的问题;

    • 修复CUDA argmax出错的问题;

    • 修复pymnn/example/mnn_numpy_cv_demo.py中形状不对的问题;

2.5.0

新特性

  • MNN OpenCV新增算子:

    • erode

    • convertMaps

    • remap

    • adaptiveThreshold

    • bilateralFilter

    • solve (MNN numpy新增solve)

    • normalize

    • split

    • merge

    • addWeight

  • 支持Tflite int8量化模型转换到MNN模型;

  • ARM CPU支持GELU-bf16

  • CUDA 新增算子:

  • GridSampler

  • Multi-Input Convolution

  • Multi-Input Deconvolution

  • CUDA针对多卡推理,支持用户设置运行device_id

  • 支持Deconvolution-int8

  • runSession/runSessionWithCallBack函数加锁,避免多线程调用出错

  • 支持非拓扑序ONNX模型转换

  • 支持ONNX多版本Softmax转换

重构/优化

  • 优化内存分配与回收时机,新增Session

  • 简化ONNX Slice算子模型转换

  • Cuda性能优化

  • Argmax针对dim size较大的情况性能优化

  • Softmax在channel较大时性能优化

  • MatMul算子预重排逻辑优化

  • 优化后ChatGLM模型在A10显卡上性能优于Pytorch 2.0

  • OpenCL优化,resnet测试优于OpenVINO

  • 使用intel subgroup扩展优化winogard算子,调整数据排布格式与准入条件

  • 根据输入尺寸调整conv2d算子的数据排布格式,使用intel subgroup扩展优化

  • 优化后ResNet18模型在intel UHD Graphics 630显卡上性能优于OpenVINO

  • GELU-bf16实现后性能提升

Bugfix

  • 关联 Github Issue 解决

    • 修复CPURaster 的 singleConvert 部分情况出错 issue-2264

    • 修复atan2计算错误的问题

    • 修复ONNX dynamic shape转换出错的问题 issue-2276

    • 修复i8mm时Im2col出错的问题

    • 修复CPUConvolutionDepthwise错误的问题 issue-2291

    • 修复CUDA int8编译失败的问题 issue-2321

    • 修复Onnx Loop 算子的 M 和 cond 为optional 时,转换失败的问题 issue-2267

    • 修复Raster中fastblit 中turnPackRegion 部分情况下出错的问题 issue-2337

  • 其他 Bugfix

    • 修复 onnx 子图中 identity 被优化导致 输出数和原始子图不一致的问题

    • 修复 Onnx sequense 相关算子转换问题

    • 修复 TensorArrayConcat 计算 newAxis = 1 时的问题(此时为 stack)

    • 修复 TensorArray 计算 eleSize 时,axis < 0 时计算出错的问题

    • 修复低精度计算或者 GPU 无法运行 mnn 训练模型的问题

2.4.0

新特性

  • NNAPI 支持int8 量化模型;

  • MNN OpenCL/Metal支持算子在线Fuse与代码生成;

  • 支持使用cibuildwheel构建Python Wheel包;

  • Github Action支持自动化构建多端库与Whl包;

  • (测试中)CUDA后端支持量化模型运行

重构/优化

  • CUDA优化Softmax/DepthwiseConv算子

  • 优化 KernelSize = 3x3 的 OpenCL 卷积算子性能

  • 优化了MaxPool/AvgPool的int8量化计算;

  • 移除原来的LLVMJit, C等Codegen后端;

  • 更新MNN.podspec, MNNBridge.podspec;

  • 增加GELU模块Fuse为GELU算子的功能,Vulkan 和 OpenCL 后端支持 GELU 算子

  • NetModule析构函数中增加gc函降低内存占用;

  • OpenCL支持设置推理低优先级配置;

  • OpenCL updateCache支持异步,降低阻塞时间;

  • fastTestOnnx.py / fastTestTf.py / fastTestTflite.py / fastTestTorch.py 分别更名为 testMNNFromOnnx.py / testMNNFromTf.py / testMNNFromTflite.py / testMNNFromTorch.py

  • Android Demo新增使用README文档;

Bugfix

  • 修复Android Demo运行Crash的问题;

  • 修复Metal中的onSync的Bug;

  • 修复Metal多段模型推理的Bug;

  • 修复在Windows下MNN Train的编译问题;

  • 修复perm值非法时的Crash问题;

  • 修复Pad的输入参数为负数时(此时等效为Crop),计算出错的问题

  • 修正 Relu Int8 不支持非对称量化的问题

  • 修正部分AVX2架构的机器上运行量化模型crash的问题

  • 修正Module API 运行静态模型crash的问题

  • 修正Winograd量化过程未使用相同变换矩阵的问题

  • 修正Winograd量化计算多Batch输入错误的问题

  • 修正 OpenCL Relu 算子在 AMD GPU 上段错误的问题

  • 修正 OpenCL ROIPooling 算子实现错误

2.3.0

功能完善

  • CUDA 后端支持高精度模型(设置 precision = high 时使用 FP32 计算) 和 SM60 架构

  • MNN-Train 求导优化

    • MNN-Express 支持 CONTENT 模式,该模式下基于几何计算分解算子后再构图,以降低需要实现求导的算子数

    • 支持 Raster / Loop 算子部分情况下的求导

    • 支持 GridSampler 的求导

  • OpenCL 后端支持低优先级运行模式(设置 power = low)

  • (实险中特性)Vulkan 后端增加基于Buffer内存布局的算子实现,目前基于编译宏决定用 Image内存布局还是 Buffer内存布局(MNN_VULKAN_IMAGE ,默认为 ON)

  • (实验中特性)支持分离模型结构与权重的选项

    • 模型转换为 {S}.mnn 时,添加参数 –saveExternalData ,模型权重将单独保存为二进制文件 {S}.mnn.weight

    • 模型加载运行时,通过以下方式指定权重文件路径:

      • Session API: Interpreter::setExternalFile

      • Module API: Executor::RuntimeManager::setExternalFile

重构/优化

  • 修改嵌入式上常用的 SeqLength = 1 的 ONNX LSTM 算子的模型转换实现,改为用卷积+非线性层拼接实现,以提升性能

  • 修正部分情况下 Convolution Winograd CPU 相较之前版本变慢的问题

  • 优化 VARP 的 fix 函数,避免拷贝内存

  • 对 Raster 算子的输入进行了改造,由 region 隐式输入修改为正常的多输入单输出

  • 量化计算实现中的量化/反量化过程重构为在线插入相应算子,并修正 prearrange 为 true 时,Module API 计算量化模型的结果错误问题

  • 移除 ComputeUnit / ComputeCache ,Executor 内部的计算改为使用 Session ,并延迟内存分配时机,修正模型转换过程中部分情况下占用内存过大的问题

  • 优化模型转换静态模型的导出,移除了图中无效算子

Bugfix

  • 修正 convolution transpose 3d 在 pad 为空时计算 crash 问题

  • 修正 cumsum 计算 int 输入的 bug

  • 修正 Onnx GatherND 算子转换不支持 batch_dims 的问题

  • 修正 Onnx Split 算子转换的默认值问题

  • 修正 Onnx permute 算子转换不支持 axis 为空的问题

2.2.0

框架通用性

  • MNN新增对ARMv8.6-A指令支持,支持了smmlabfmmla指令

  • MNN新增汇编预处理脚本,能够将汇编指令转换为.inst指令,降低新指令对编译器的依赖

  • 新增A16和M2 CPU family支持

  • 新增Interp3D支持

  • MNN新增NNAPI后端,能够利用Android设备上的NPU/APU/DSP进行计算;支持float32float16数据类型的模型推理。目前支持的算子如下:

    • [x] Conv2d, DepthwiseConv2d

    • [x] MaxPool2d, AvgPool2d

    • [x] Binary/Elementwise: Add, Sub, Mul, Div

    • [x] Unary: Abs, Exp, Sqrt, Rsqrt, Log, Sin, Tanh, Floor, Neg, Hardswish

    • [x] Activation: Softmax, Relu, Relu6, Prelu, Sigmoid, Elu

    • [x] Reduction: Sum, Mean, Max, Min, Prod, All, Any

    • [x] Argmax, Argmin

    • [x] Resize: Nearstneighbor, Bilinear

    • [x] Reshape, Transpose, Tile, Pad, Slice, DepthToSpace, Concat, Gether

    • [x] Scale/BatchNorm

性能优化

  • 新增ARMv8.6指令支持后,GemmInt8, GemmBF16性能提升

    • smmla实现的GemmInt8实测性能在矩阵规模为[1024, 1024, 1024]时,性能相比sdot提升为88.47%(s图中33x33项),接近理论性能(100%);模型性能提升20%左右。 _images/2_2_0_smmla1.pngsmmla1 _images/2_2_0_smmla2.pngsmmla2

    • bfmmla实现的GemmBF16实测性能在规模为[1024, 1024, 1024]时,性能相比fp16fmla提升为91.53%(图中1024,1024,1024项),接近理论性能;模型性能相比原来的bf16提升一倍以上。 _images/2_2_0_bfmmla1.pngbfmmla1 _images/2_2_0_bfmmla2.pngbfmmla2

  • 在执行Mobilenetv1时,NNAPI使用accelerator设备进行推理,在中端和高端设备上相比CPU单线程均有性能优势;在高端设备上相比CPU 4线程仍有性能优势;在其他类模型对比时,除卷积外其他算子较少的模型NNAPI均有优势,包含其他算子的模型会出现性能不如MNN-CPU的情况;在使用float16推理时,NNAPI平均性能相比MNN-CPU慢。 _images/2_2_0_nnapi1.pngnnapi _images/2_2_0_nnapi2.pngnnapi

  • CUDA性能优化,Depthwise卷积、Raster快速计算、Im2Col等优化,MobileNet/Squeezenet等模型性能提升 _images/2_2_0_cuda.pngcuda

  • 新增BinaryRelu-Fuse和对应的各后端实现,resnet模型性能提升 _images/2_2_0_bianryrelu.pngbianryrelu

其他

  • 进行了部分代码重构(包括但不限于)

    • 对于包含多个SubModule的复杂模型, 复用子模型间共性tensor,重新构建计算图和指令队列,显著降低大内存操作的耗时

  • 修复了如下 Bug(包括但不限于)

    • Onnx Resize 在指定 scale 且输入输出无法整除时,计算错误

    • 修复在不支持SSE 4.1的设备上打开SSE执行Crash的问题

    • 修复多输入Conv转换错误

    • 修复ARM82后端GridSampler在Linux上的编译错误

    • 修复Conv1dSqueezeMove在Squeeze双输入时计算出错的问题

    • 修复输入为NC4HW4时,stride计算错误的问题

    • 修复HIAI后端编译错误,Binary BUG

2.1.0

框架通用性

  • MNN-CV增加solvepnp / svd等函数实现

  • MNN-Train补充Unary/Binary/Reduction的求导实现

  • MNN-Express支持Eager模式,该模式下不保存计算图,直接计算结果,可通过Executor的lazyEval配置

    • 在C++中默认使用Lazy模式

    • 在Python中默认使用Eager模式

  • 新增基于Markdown+Sphinx的文档

性能优化

  • 服务端推理 CUDA 性能提升

    • 基于 cutlass 重新实现了矩阵乘,对卷积应用 Winograd算法优化 _images/2_1_0_cuda.pngcuda

  • MNN-CoreML后端支持输出zero-copy _images/2_1_0_coreml.pngcoreml

模型压缩

支持 Winograd Int8对kernel_size > 1的量化卷积进行优化 ,离线量化工具(C++: quantized.out,python: mnnquant)json配置文件中增加"winogradOpt": true,并将特征量化方法设置为"feature_quantize_method":"EMA"即可使用 _images/2_1_0_winograd.pngwinograd

其他

  • 进行了部分代码重构(包括但不限于)

    • MNN Metal 改为在线生成 Shader 编译,避免集成 MNN.metallib 的兼容性问题

    • 移除 CPU / Geometry / Arm82 部分冗余代码

    • 默认移除原先 TFlite - Uint8 的算子支持,但可以通过 MNN_SUPPORT_DEPRECATED_OP 宏打开

    • 移除 linux 系统下编译 Torchscript 所需要的 libTorch 库,改为编译时从网络下载

    • ScatterND 改为基于 Loop 算子实现

  • 修复了如下 Bug(包括但不限于)

    • CPU - AVX512 int8 在转换 NC4HW4 格式时内存访问越界

    • GeometryBinary 处理 NC4HW4 输入,两边Channel上对齐大小相同,但Channel不同时计算出错

    • Arm82 Interp 算子多 Batch 情况下计算出错问题

    • Windows 上 json2MNN 工具写入结果有误

    • Onnx GatherElement 算子在输入大小不确定时转换失败

    • 修复Python中Module,RuntimeManager内存泄露问题

    • 修复控制流模型转换时输出算子Name不匹配的问题

    • 修正 ROIPooling / ROIAlign 低精度计算 crash 的问题

2.0.0

框架通用性

  • 模型推理通用性增加:Torchsciprts OP 添加,Onnx OP 补齐

    • Onnx 算子数由 117 增加到 158

    • Torchscripts 算子数由 34 增加到 163

  • MNNConvert功能扩充

    • 支持模型转换正确性验证

    • 支持MNN模型与Json文件互转,方便查看与编辑模型结构

  • MNN增加统一版本号机制

    • 编译期版本宏定义

    • 运行时版本号函数

    • 模型中增加版本信息

  • 增加 MNN-CV / MNN-Numpy 功能

    • C++中提供了与OpenCV中图像编解码,图像处理用法相似的API;

    • Python中提供了与cv2/numpy基础功能用法相似的函数;

    • 支持的cv函数57个,numpy函数170个,函数列表

性能优化

  • 服务/PC端推理CPU/GPU性能大幅提升

    • CPU部分AVX512优化,多线程优化提速;

    • GPU部分CUDA移除cudnn,基于TensorCore重写; _images/2_0_0_avx512_1.pngAVX512后端单线程性能对比.png _images/2_0_0_avx512_8.pngAVX512后端8线程性能对比.png _images/2_0_0_cuda.pngCUDA性能对比.png

模型压缩

  • 新增mnncompress模型压缩工具

    • 支持基于TensorFlow 1.X和Pytorch的模型压缩,具体使用方法见文档

    • 添加压缩模型的模型转换,及相关算法的MNN底层推理支持

其他

  • 测试/Demo/Benchmark完善

    • 修正 Android Demo 的 编译Bug;

    • 增加一个使用 mnn framework 的 ios demo工程;

    • Pymnn新增离线量化Demo与测试;

    • Pymnn新增训练相关测试;

    • Pymnn中新增MNN.numpy与numpy对比的benchmark;

    • 新增MNN.cv与OpenCV对比的benchmark;

  • Bugfix(包括但不限于)

    • Pymnn修复训练相关API使用Bug;

    • 修复arm64汇编中的sp计算Bug;

    • GatherND 精度数目问题修复;

    • ZeroShape 支持完善;

    • NDK24 下 armv7a-arm82 编译错误修正;

    • benchmark metal crash修复;

    • benchmark metal crash;

    • Module 的 RuntimeManager 设置 precision = low 无效的问题修复;

    • CoreML Pooling CAFFE-PAD,Deconv修复;

    • CoreML多次执行内存占用过高问题修复;

1.2.0

框架通用性

  • 新增Torchscript模型格式支持

    • 我们注意到,大量的机器学习工程师在从TensorFlow往PyTorch迁移。推理引擎对于PyTorch模型的原生支持尤为重要。虽然MNN已经支持了ONNX格式的模型,但是考虑到PyTorch自身长期的发展趋势,基于Torchscript格式的模型比ONNX更具通用性。现在,MNNConvert支持在Mac、Windows、Linux平台下将所有的TorchVision视觉模型 转换到MNN格式。

  • 新增ARM BF16后端

    • BF16 可以给中低端手机和高端机的小核带来性能收益,并且降低内存占用。经MNN团队内部测试,BF16相对于FP32在不同机型的中低端核心(A53 A55 A53kyro A55kyro)上,不同模型有 5%-30%的优化,性能如下:_images/1_2_0_bf16.pngbf16.png

    • BF16使用方法:

      • 编译MNN时,指定-DMNN_SUPPORT_BF16=ON

      • BackendConfig中指定PrecisionMode=Precision_Low_BF16

  • 新增CoreML后端

    • 基于几何计算,MNN添加了CoreML的支持。在iPhone X之后,借助于Apple Neural Engine,相比于CPU,CoreML(ANE)在视觉模型中约有5-6倍的性能提升。

  • 几何计算的演进

    • 在1.1.0版本的几何计算的基础上,本次发布中『几何计算』增加了对于循环算子(如Gather、BatchMatMul、LSTM)的GPU后端支持。

性能优化

  • ARM 后端

    • 支持ARMv8.2指令的设备占有率随着时间的推移逐渐上升,是MNN优化的重点之一。相比于MNN 1.1.x版本,MNN 1.2.0的ARMv8.2性能在各类视觉模型中有5% ~ 20%的提升,且与业界主流引擎对比处于领先地位。_images/1_2_0_arm.pngarm.png

  • X86 后端

    • MNN集中优化了X86-AVX2上的性能。目前在主流的视觉、时序模型中,MNN-AVX2后端相比于OpenVINO由20%到440%的性能优势,与ONNXRuntime相比,则有18%到60%的性能优势 (仅MobileNet V2略逊于OpenVINO/ONNXRuntime)。取得如此性能成绩,且通用性持平或更胜一筹的前提下,相比于 Onnx / OpenVino 几十至几百M 的大小,MNN 库的体积却很小,仅 3M 不到。 此外,MNN 支持了 AVX512 / AVX512VNNI,相对于 AVX2 ,在浮点矩阵乘法有 60% 加速,Int8矩阵乘则有 200% 的加速。_images/1_2_0_x86.pngx86.png

  • OpenCL后端

    • 随着移动App中的部署的各类深度学习模型数量增多,MNN团队发现,CPU占用率居高不下,会影响App稳定性和用户体验。基于此判断,我们重点优化OpenCL后端的性能(与主流引擎相比已处于领先地位),并且与内部业务方共同设计面向GPU模型,达到性能、精度双高的模型。性能数据如下图:_images/1_2_0_opencl.pngopencl.png

模型压缩

  • ARM 浮点稀疏算子实现

    • 随着CPU性能优化的边际收益的降低,为了获得更高的性能,需要从模型结构本身着手,设计、裁剪出合适目标硬件和推理引擎的模型结构,以获得最佳的精度和性能。基于此,MNN添加了随机稀疏和半结构化稀疏算子的ARM浮点实现 (原理见 ” Fast Conv Nets ” ),如下图所示:_images/1_2_0_sparse.pngsparse.png

    • 经过MNN内部在各类机型和模型实测,随机稀疏率, 1x4半结构稀疏率 (沿输出通道OC分块,blockOC=4) 分别为0.6、 0.3时,推理性能将大于稠密实现性能。随机稀疏率0.9时,MobileNet、NasNet、SqueezeNet各类模型中,在高、中、低端手机上的加速比为1.7倍 ~ 4.5倍;1x4半结构稀疏率0.9时,加速比为1.8倍 ~ 6.1倍。

  • 离线量化精度提升

    • 离线量化工具中添加了激活非对称的支持,并且通过量化时更新BN参数,离线量化精度获得普遍提升。结合使用非对称量化+BN参数更新,MobileNet V2量化模型精度从71.05%提高到71.73%,进一步逼近浮点模型(71.90%)。

其他

  • 功能

    • 新建 MNN 表达式接口相关 demo,见pymnn/examples/MNNExpr/mobilenet_demo.py和demo/exec/{pictureRecognition_module.cpp, transformerDemo.cpp}

    • 对离线量化工具进行了重构,减少 int8 / float 互转损耗,以 shufflenet 为例可减少 20% 耗时

    • 完善模型校验工具 (tools/script/fastTest 系列)

    • 增加 Onnx 所有与 MNN 相匹配的单目 / 双目算符支持

  • 图优化

    • 新增 Gelu / Hardswish 算子融合

    • 增加 Layernorm 算子融合的范围

    • 新增 MatMul + Bias 融合,增加其转换为卷积的范围

    • 新增 Tensorflow / Tflite 的 Dilate Convolution 算子融合(SpaceToBatch + Conv + BatchToSpace)

  • Bugfix(包括但不限于)

    • 修正 StridedSlice 在 newAxis 和 begin << inputShape 情况下的实现错误

    • 修正 Eltwise MAX 的求导错误

    • 移除 MNNConvert 对 regex 的依赖(此问题导致在 gcc 4.8 环境下无法运行 Converter)

    • 修正 CPU Raster 算子对 dimension = 1 的 NC4HW4 数据格式处理错误的问题

    • 移除 MNN Python wheel 中意义不大的 mnnops (里面显示的 Op 列表不准确)

1.1.0

框架通用性

  • 几何计算

    • 几何计算是本次发布中大规模的框架重构。它将大部分算子的计算过程中与硬件后端无关部分(形状计算和几何计算)剥离出来,极大地降低了异构后端算子实现的成本。基于几何计算,MNN重写了目前所有的硬件后端。由于引入几何计算之后GPU后端算子的覆盖率的增加,在阿里巴巴内部的业务模型中,MNN GPU后端性能普遍获得约20%提升。

  • 新增后端

    • 基于几何计算机制,MNN新增了TensorRT和CUDA后端。目前已经支持常用CV模型与RNN模型。

  • ASR模型支持

    • 除了业务应用广泛的CV模型,MNN在这次发布中添加了对基于Transformer结构的ASR模型的支持。这类模型结构要求推理引擎支持Control Flow、Dynamic Shape和Zero Shape等特性。MNN在框架层面对这些特性进行了支持和完善:

      • 重构Control Flow支持方案,提供用户透明的functional control flow实现,并支持TF1.x的控制流模型转换。

      • 添加Dynamic Shape的支持,MNN将整图按照动态形状算子划分为多个分段子图。在代码层面,一个子图对应一个Module,Module支持嵌套,即整图被表达为一个由Module组成的调用树,树的叶子节点可以使用Session来执行,Session每次执行前Resize,重新进行形状推理和预分配内存。

      • Zero Shape指的是模型中某些Tensor的shape存在0值,比如 (1, 0, 256),这种情况大多是为了给while-loop中某些循环变量提供初始值而引入的。MNN在对形状推理和执行逻辑上对Zero Shape进行了支持。

性能优化

  • ARM 后端

    • 在今年5月,MNN在ARM CPU上的性能已立于业界前列。在此之后,MNN持续投入ARM CPU性能优化,在各模型和芯片上又获得了10%~20%的性能提升。性能提升之路永无止境。_images/1_1_0_arm1.pngarm1.png _images/1_1_0_arm2.pngarm2.png

  • X86 后端

    • 5月以来,MNN团队持续投入x86后端的优化,目前浮点单线程性能与行业标杆OpenVINO基本持平,部分情况 (Squeezenet v1.0) 超越。x86.png

  • OpenCL后端

    • 开启AutoTuning等一系列优化后,MNN在1.0.0的基础上,普遍有20%~100%的性能提升。具体性能数据如下:_images/1_1_0_opencl1.pngopencl1.png _images/1_1_0_opencl2.pngopencl2.png

模型压缩

新添模型压缩的仅权值量化(MNNConvert –weightQuantBits)。此功能仅对conv/matmul/LSTM的float32权值进行量化,仅优化模型大小,加载模型后会解码为float32,量化位宽可选2~8,运行速度和float32模型一致。经内部测试8bit时精度基本无损,模型大小减小4倍。

其他

  • 易用性

    • 由于OpenCL新增的AutoTuning机制、TensorRT后端初次推理的耗时较高,MNN在Interpreter上增加setCacheFile API,用于缓存GPU后端的编译优化之后的模型。

  • Bugfix(包括但不限于)

    • SpaceToBatchND , BatchToSpaceND 支持 block size / padding 作为输入(支持在输入shape 未知情况下的 Tensorflow 空洞卷积)

    • 修正 depthToSpace 和 spaceToDepth ,支持 pixelshuffle

    • 修正 1x1 卷积对于 batch 较大,width / height 较小时,性能不好的问题

    • 修正 Onnx 的 ConvTranspose output padding 支持问题

    • 修正 Onnx 的 Resize 在某些输入个数下不支持的问题

1.0.0

框架通用性

  • 表达式接口提供训练与量化功能

    • 新增C++的表达式接口(在express目录下)

    • 使用表达式接口动态构图

    • 使用表达式接口训练,训练耗时如下:_images/1_0_0_train.pngtrain.png

    • 使用表达式执行训练量化(QAT)

性能优化

  • ARM 后端

    • ARMv82使用asimdhpasimddp扩展提速接近100% _images/1_0_0_arm82.pngarm82.png

    • ARM64重新实现矩阵乘kernel,性能提升

芯片/手机 优化前(ms) 优化后(ms)
高通425/红米4A 261.12 237.89
高通652/360N5 122.94 115.80
  • X86 后端

    • 使用FMA指令进行优化,单线程性能提升40~60%

模型(Mac单线程) 优化前(ms) 优化后(ms)
resnet18 81 48
mobilenetv1 37 26

模型压缩

使用MNN QAT与Tensorflow的对比如下表所示:

模型 DataType Accuracy Model Size
Original Model float32 72.324% 13M
MNN QAT Model symm int8 72.456% 3.5M
Tensorflow QAT Model symm int8 71.1% 3.5M

其他

  • 功能

    • 新增Python Expr API

快速开始

使用MNN整体流程

在端侧应用MNN,大致可以分为三个阶段: _images/concept.pngconcept.png

训练

在训练框架上,根据训练数据训练出模型的阶段。虽然当前MNN也提供了训练模型的能力,但主要用于端侧训练或模型调优。在数据量较大时,依然建议使用成熟的训练框架,如TensorFlow、PyTorch等。除了自行训练外,也可以直接利用开源的预训练模型。

转换

将其他训练框架模型转换为MNN模型的阶段。MNN当前支持Tensorflow(Lite)、Caffe、ONNX和TorchScript的模型转换。模型转换工具可以参考编译文档使用说明。支持转换的算子,可以参考算子列表文档;在遇到不支持的算子时,可以尝试自定义算子,或在Github上给我们提交issue。此外,模型打印工具可以用于输出模型结构,辅助调试。除模型转换外,MNN也提供了模型量化工具,可以对浮点模型进行量化压缩。

推理

在端侧加载MNN模型进行推理的阶段。端侧运行库的编译请参考各平台的编译文档:iOSAndroidLinux/macOS/UbuntuWindows。我们提供了API接口文档,也详细说明了会话创建数据输入执行推理数据输出相关的接口和参数。demo/exec下提供了使用示例,如图像识别 demo/exec/pictureRecognition.cpp ,图像实例分割(人像分割)demo/exec/segment.cpp更多demo。此外,测试工具benchmark工具也可以用于问题定位。

示例工程

C++ Demo

从源码编译

姿态检测

代码位置:demo/exec/multiPose.cpp

  1. 下载原始的Tensorflow模型 pose model

  2. 使用 模型转换工具 转换为 MNN 模型

  3. 执行姿态检测

    ./multiPose.out model.mnn input.png pose.png
    

效果示例:

_images/multipose_input.pnginput.png _images/multipose_pose.pngpose.png

图像实例分割

代码位置:demo/exec/segment.cpp

下载 deeplabv3 分割模型并转换到 mnn 模型 https://storage.googleapis.com/download.tensorflow.org/models/tflite/gpu/deeplabv3_257_mv_gpu.tflite

./segment.out model.mnn input.png result.png

效果示例:

_images/segment_input.pnginput.png _images/segment_result.pngresult.png

图像识别

代码位置:demo/exec/pictureRecognition.cpp

下载 mobilenet 模型并转换为 MNN 格式 第一个参数为 MNN 模型地址 第二个参数为图像地址 追加参数则为下一张图像地址

示例:

./pictureRecognition.out moiblenet.mnn Test.jpg

效果示例:

_images/TestMe.jpgTestMe.jpg.png

输出:

Can't Find type=4 backend, use 0 instead
For Image: TestMe.jpg
386, 0.419250
101, 0.345093
385, 0.214722
347, 0.012001
346, 0.002010
348, 0.001876
294, 0.001247
349, 0.000761
354, 0.000443
345, 0.000441

第一行表示识别出可能性最大的类别编号,在相应的 synset_words.txt 去查找对应的类别,如:demo/model/MobileNet/synset_words.txt

Python Demo

Session图片分类

代码位置:pymnn/examples/MNNEngineDemo/

测试代码包含:

  • mobilenet_demo.py 使用Session进行图片分类示例

  • mobilenet_demo_2.py 使用Runtime创建Session进行图片分类示例

  • gpu_session_demo.py 使用Session的GPU后端进行图片分类示例

资源文件如下:

示例:

$ unzip mobilenet_demo.zip
$ python mobilenet_demo.py mobilenet_demo/mobilenet_v1.mnn mobilenet_demo/ILSVRC2012_val_00049999.JPEG
Load Cache file error.
expect 983
output belong to class: 983
$ python mobilenet_demo_2.py mobilenet_demo/mobilenet_v1.mnn mobilenet_demo/ILSVRC2012_val_00049999.JPEG
Load Cache file error.
MNN use low precision
<capsule object NULL at 0x7fdd0185a270> (True,)
MNN use low precision
memory_info: 22.382057MB
flops_info: 568.792175M
backend_info: 13
expect 983
output belong to class: 983
$ python gpu_session_demo.py mobilenet_demo/mobilenet_v1.mnn mobilenet_demo/ILSVRC2012_val_00049999.JPEG 
Testing gpu model calling method

Load Cache file error.
MNN use high precision
Can't Find type=3 backend, use 0 instead
Can't Find type=3 backend, use 0 instead
Run on backendtype: 13 

expect 983
output belong to class: 983

表达式图片分类

代码位置:pymnn/examples/MNNExpr

$ python mobilenet_demo.py mobilenet_demo/mobilenet_v1.mnn mobilenet_demo/ILSVRC2012_val_00049999.JPEG
expect 983
output belong to class: 983

模型量化

代码位置:pymnn/examples/MNNQuant

离线量化工具,用法参考, 资源文件下载 示例:

$ python test_mnn_offline_quant.py  --mnn_model quant_demo/mobilenet_v2_tfpb_train_withBN.mnn \
            --quant_imgs quant_demo/quant_imgs \
            --quant_model ./quant_model.mnn
output names:	 MobilenetV2/Predictions/Reshape_1
100%|██████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:46<00:00, 23.29s/it]
Epoch cost: 46.618 s.
quantized model save to ./quant_model.mnn

模型训练

代码位置:pymnn/examples/MNNTrain

测试代码包含:

  • mnist

  • mobilenet_finetune

  • module_save

  • quantization_aware_training

mnist

使用mnist数据训练模型,并测试准确率,无需下载资源,用法如下:

$ pip install mnist 
$ python train_mnist.py
train loss:  2.3346531
train loss:  0.28027835
train loss:  0.26191226
train loss:  0.09180952
train loss:  0.14287554
train loss:  0.14296289
train loss:  0.060721636
train loss:  0.037558462
train loss:  0.11289845
train loss:  0.04905951
Epoch cost: 47.505 s.
Save to 0.mnist.mnn
test acc:  96.25 %
mobilenet_finetune

这个示例展示了如何使用MobilenetV2在你的数据集上finetune一个图像分类器。 示例资源文件:

用法如下:

$ unzip model.zip train_dataset.zip test_dataset.zip
$ python mobilenet_transfer.py --model_file mobilenet_v2_tfpb_train_public.mnn --train_image_folder train_images --train_txt train.txt --test_image_folder test_images --test_txt test.txt --num_classes 1000
# TODO: 当前版本不支持FixModule无法执行该示例
AttributeError: module 'MNN.nn' has no attribute 'FixModule'
module_save

演示了模型权值的存储和加载

$ python test_save.py 
0.0004
10
quantization_aware_training

训练量化,用法如下:

$  python quant_aware_training.py --model_file quant_demo/mobilenet_v2_tfpb_train_withBN.mnn --val_image_path quant_demo/quant_imgs --val_txt quant_demo/val.txt

Android Demo

代码位置:project/android/demo

按照project/android/demo/READNE.md的步骤,首先安装开发所需工具;然后下载并转换模型;之后就可以编译成Android APP执行测试。

效果示例:

_images/android_demo.jpgandroid_demo.png

iOS Demo

模型下载与转换:

首先编译(如果已编译可以跳过)MNNConvert,操作如下:

cd MNN
mkdir build && cd build
cmake -DMNN_BUILD_CONVERTER=ON ..
make -j8

然后下载并转换模型: 切到编译了 MNNConvert 的目录,如上为 build 目录,执行

sh ../tools/script/get_model.sh

工程编译

代码位置:project/ios

使用xcode打开project/ios/MNN.xcodeproj, target选择demo,既可编译运行。

效果示例:

_images/ios_demo.jpgios_demo.png

Github Demo

欢迎开发者提供示例,可以在issue中提交自己的示例项目,审核通过后可以再此处展示

以下示例为Github开发者贡献,具体用法需参考相关代码;

编译宏介绍

MNN使用CMake构建项目,CMake中的宏定义列表如下:

宏名 宏说明
MNN_USE_SYSTEM_LIB 在使用openclvulkan时,使用系统库(ON)还是通过dlopen引入动态库(OFF),默认为OFF
MNN_BUILD_HARD 是否使用-mfloat-abi=hard,默认为OFF
MNN_BUILD_SHARED_LIBS 是否构建为动态库,默认为ON
MNN_WIN_RUNTIME_MT 在Windows上构建dll时是否使用/MT,默认为OFF
MNN_FORBID_MULTI_THREAD 是否禁止多线程,默认为OFF
MNN_OPENMP 是否使用OpenMP的线程池,该选项在Mac/iOS平台无效,默认为OFF
MNN_USE_THREAD_POOL 是否使用MNN内部线程池,默认为ON
MNN_BUILD_TRAIN 是否构建MNN的训练框架,默认为OFF
MNN_BUILD_DEMO 是否构建MNN的demo,默认为OFF
MNN_BUILD_TOOLS 是否构建MNN的测试工具,默认为ON
MNN_BUILD_QUANTOOLS 是否构建MNN的量化工具,默认为OFF
MNN_EVALUATION 是否构建MNN的评估工具,默认为OFF
MNN_BUILD_CONVERTER 是否构建MNN的转换工具,默认为OFF
MNN_SUPPORT_DEPRECATED_OP 是否支持Tflite的量化算子,默认为ON
MNN_DEBUG_MEMORY 是否开启MNN内存调试,默认为OFF
MNN_DEBUG_TENSOR_SIZE 是否开启MNN tensor size调试,默认为OFF
MNN_GPU_TRACE 是否开启MNN GPU调试,默认为OFF
MNN_PORTABLE_BUILD 尽可能链接第三方库的静态版本,以提高构建的可执行文件的可移植性,默认为OFF
MNN_SEP_BUILD 是否构建MNN的后端和表达式分离版本,只在MNN_BUILD_SHARED_LIBS=ON时生效,默认为ON
NATIVE_LIBRARY_OUTPUT 如果构建为动态库,则指定动态库的输出路径,默认为OFF
NATIVE_INCLUDE_OUTPUT 如果构建为动态库,则指定动态库的头文件路径,默认为OFF
MNN_AAPL_FMWK 是否构建MNN.framework替代*.dylib,默认为OFF
MNN_WITH_PLUGIN 是否支持Plugin算子,默认为OFF
MNN_BUILD_MINI 是否构建MNN的最小化版本,最小化版本仅支持固定形状,默认为OFF
MNN_USE_SSE 在x86上是否使用SSE指令集,默认为OFF
MNN_BUILD_CODEGEN 是否构建MNN的代码生成部分,该功能提供了算子融合与代码生成能力,为实验性功能,默认为OFF
MNN_ENABLE_COVERAGE 是否开启MNN的代码覆盖率,默认为OFF
MNN_BUILD_PROTOBUFFER 是否使用MNN中的protobuffer,默认为ON
MNN_BUILD_OPENCV 是否构建MNN的OpenCV功能,默认为OFF
MNN_INTERNAL 是否构建MNN的一些内部功能,如:日志;默认为OFF
MNN_JNI 是否构建MNN的JNI支持,默认为OFF
MNN_METAL 是否构建Metal后端,默认为OFF
MNN_OPENCL 是否构建OpenCL后端,默认为OFF
MNN_OPENGL 是否构建OpenGL后端,默认为OFF
MNN_VULKAN 是否构建Vulkan后端,默认为OFF
MNN_ARM82 是否构建Armv8.2后端,默认为OFF
MNN_ONEDNN 是否使用oneDNN,默认为OFF
MNN_AVX512 是否构建avx512后端,默认为OFF
MNN_CUDA 是否构建Cuda后端,默认为OFF
MNN_CUDA_PROFILE 是否打开CUDA profile工具,默认为OFF
MNN_CUDA_QUANT 是否打开CUDA 量化文件编译,默认为OFF
MNN_CUDA_BF16 是否打开CUDA Bf16文件编译,默认为OFF
MNN_TENSORRT 是否构建TensorRT后端,默认为OFF
MNN_COREML 是否构建CoreML后端,默认为OFF
MNN_NNAPI 是否构建NNAPI后端,默认为OFF
MNN_BUILD_BENCHMARK 是否构建MNN的性能测试,默认为OFF
MNN_BUILD_TEST 是否构建MNN的单元测试,默认为OFF
MNN_BUILD_FOR_ANDROID_COMMAND 是否使用命令行构建Android,默认为OFF
MNN_USE_LOGCAT 是否使用logcat代替printf输出日志,默认为OFF
MNN_USE_CPP11 是否使用C++11编译MNN,默认为ON
MNN_SUPPORT_BF16 是否支持BF16,默认为OFF
MNN_SSE_USE_FP16_INSTEAD 在X86平台是否使用FP16替代BF16,默认为OFF
MNN_AVX512_VNNI 是否使用avx512_vnni指令,该宏仅在MNN_AVX512=ON时生效,默认为OFF
MNN_OPENCL_SIZE_CUT 是否为了降低OpenCL大小而关闭OpenCL Buffer实现,该宏仅在MNN_OPENCL=ON时生效,默认为OFF
MNN_OPENCL_PROFILE 是否打开OpenCL Kernel性能Profile,该宏仅在MNN_OPENCL=ON时生效,默认为OFF
MNN_METALLIB_SOURCE 使用Metal时是否直接使用Metal源码,该宏仅在MNN_METAL=ON时生效,默认为ON
MNN_VULKAN_DEBUG 是否打开Vulkan的DEBUG模式,该宏仅在MNN_VULKAN=ON时生效,默认为OFF
MNN_OPENGL_REGEN 是否重新生成OpenGL Kenel,该宏仅在MNN_OPENGL=ON时生效,默认为OFF
MNN_TRT_DYNAMIC 是否通过dlopen的方式引入TRT的动态库,该宏仅在MNN_TENSORRT=ON时生效,默认为`OFF
TF_CONVERT_ORIGIN 构建的MNNConvert是否使用原始TF转换模式,该宏仅在MNN_BUILD_CONVERTER=ON时生效,默认为OFF
TFMODEL_OPTIMIZE 构建的MNNConvert是否对Tensorflow模型执行优化,该宏仅在MNN_BUILD_CONVERTER=ON时生效,默认为OFF
MNN_BUILD_TORCH 构建的MNNConvert是否支持TorchScript,该宏仅在MNN_BUILD_CONVERTER=ON时生效,默认为OFF
MNN_TRAIN_DEBUG 构建的训练模块是否支持调试,该宏仅在MNN_BUILD_TRAIN=ON时生效,默认为OFF
MNN_BUILD_TRAIN_MINI 构建删减版训练模块,不构建Datasetmodel,该宏仅在MNN_BUILD_TRAIN=ON时生效,默认为OFF
MNN_USE_OPENCV 构建的训练Demo是否使用OpenCV依赖,该宏仅在MNN_BUILD_TRAIN=ON时生效,默认为OFF
MNN_IMGPROC_COLOR 构建MNN的OpenCV功能是否开启颜色空间转换,默认为ON
MNN_IMGPROC_GEOMETRIC 构建MNN的OpenCV功能是否开启形变,默认为ON
MNN_IMGPROC_DRAW 构建MNN的OpenCV功能是否开启画图,默认为ON
MNN_IMGPROC_FILTER 构建MNN的OpenCV功能是否开启滤波,默认为ON
MNN_IMGPROC_MISCELLANEOUS 构建MNN的OpenCV功能是否开启混合,默认为ON
MNN_IMGPROC_STRUCTRAL 构建MNN的OpenCV功能是否开启结构,默认为ON
MNN_IMGPROC_HISTOGRAMS 构建MNN的OpenCV功能是否开启直方图,默认为ON
MNN_CALIB3D 构建MNN的OpenCV功能是否开启3d,默认为ON
MNN_IMGCODECS 构建MNN的OpenCV功能是否开启图像编解码,默认为OFF
MNN_CVCORE 构建MNN的OpenCV功能是否开启core功能,默认为ON
MNN_OPENCV_TEST 构建MNN的OpenCV功能是否开启单元测试,默认为OFF
MNN_OPENCV_BENCH 构建MNN的OpenCV功能是否开启性能benchmark,默认为OFF
MNN_VULKAN_IMAGE 构建MNN的Vulkan后端时采用Image内存模式,以便支持FP16和部分移动端上GPU的加速,默认为ON
MNN_LOW_MEMORY 是否支持低内存模式,支持低内存模式使用权值量化模型并设置low_memory则会使用计算时反量化,默认为OFF

主库编译

默认编译产物为:libMNN.soexpress/libMNN_Express.so

Linux/MacOS

  • 环境要求

    • cmake >= 3.10

    • gcc >= 4.9

  • 相关编译选项

    • MNN_ONEDNN 是否使用oneDNN库来加速卷积运算

    • MNN_AVX512 是否使用AVX512指令,需要gcc9以上版本编译

    • MNN_OPENCL 是否使用OpenCL后端,针对GPU设备

    • MNN_VULKAN 是否使用Vulkan后端,针对GPU设备

    • MNN_CUDA 是否使用CUDA后端,针对Nivida GPU设备

    • MNN_TENSORRT 是否使用TensorRT后端,针对Nivida GPU设备

  • 具体步骤

    1. 准备工作 (可选,修改 MNN Schema 后需要)

      cd /path/to/MNN
      ./schema/generate.sh
      ./tools/script/get_model.sh # 可选,模型仅demo工程需要
      
    2. 本地编译

      mkdir build && cd build && cmake .. && make -j8
      

Windows

  • 环境要求

    • Microsoft Visual Studio >= 2017

    • cmake >= 3.13

    • powershell

    • Ninja

  • 相关编译选项

    • Linux/MacOS

  • 具体步骤

    1. opencl/vulkan

      • *(可选)*下载GPU Caps Viewer,你可以通过这个工具来查看本机设备的详细信息(opencl、opengl、vulkan等)

      • sdk和驱动准备

    2. 编译

      • 64位编译:在设置中找到vcvars64.bat(适用于 VS 2017 的 x64 本机工具命令提示)并单击,打开VS编译x64架构程序的虚拟环境

      • 32位编译:在设置中找到vcvarsamd64_x86.bat(VS 2017的 x64_x86 交叉工具命令提示符)并单击,打开VS交叉编译x86架构程序的虚拟环境

      • 在虚拟环境中执行如下编译命令:

        cd /path/to/MNN
        ./schema/generate.ps1 # 非必须
        mkdir build && cd build
        cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DMNN_BUILD_SHARED_LIBS=OFF -DMNN_WIN_RUNTIME_MT=OFF
        ninja
        
      • 若需要编译模型转换工具,cmake 命令加上 -DMNN_BUILD_CONVERTER=ON -DMNN_BUILD_SHARED_LIBS=OFF -DMNN_WIN_RUNTIME_MT=ON

      • 若需要编译 MNN CUDA,MNN_WIN_RUNTIME_MT 和 MNN_BUILD_SHARED_LIBS 需要设成 ON ,另外加上 -DMNN_CUDA=ON: cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DMNN_BUILD_SHARED_LIBS=ON -DMNN_WIN_RUNTIME_MT=ON -DMNN_CUDA=ON

      • Windows 上建议使用 Interpreter::destroy , Tensor::destroy , Module::destroy 等方法进行 MNN 相关内存对象的析构,不要直接使用 delete (直接使用 delete 在 -DMNN_WIN_RUNTIME_MT=ON 时会出问题)

Android

  • 环境要求

    • cmake >= 3.10

    • ndk

  • 相关编译选项

    • MNN_OPENCL 是否使用OpenCL后端,OpenCL后端可以利用GPU加速

    • MNN_NNAPI 是否使用NNAPI后端,NNAPI后端会尝试使用设备上的NPU进行加速

    • MNN_ARM82 是否支持fp16推理,开启该编译选项后,在precision设成Precision_Low时,会在支持的设备(ARMv8.2 及以上架构)上启用低精度(fp16)推理,减少内存占用,提升性能

    • MNN_SUPPORT_BF16 是否支持bf16推理,开启该编译选项后,在precision设成Precision_Low_BF16 时,会启用bf16推理,减少内存占用,提升性能

  • 具体步骤

    1. NDK download下载安装NDK,建议使用最新稳定版本;

    2. 在 .bashrc 或者 .bash_profile 中设置NDK环境变量,例如:export ANDROID_NDK=/Users/username/path/to/android-ndk-r14b

    3. 执行编译

      • Android Studio 方式,全平台适用

        • 用 Android Studio 打开project/android/demo ,编译*.apk

        • unzip解压编译好的apk文件 ,lib目录下包含mnn的*so文件

      • 命令行方式,适用 linux / mac 系统

        cd /path/to/MNN
        cd project/android
        # 编译armv7动态库
        mkdir build_32 && cd build_32 && ../build_32.sh
        # 编译armv8动态库
        mkdir build_64 && cd build_64 && ../build_64.sh
        

iOS

  • 环境要求

    • xcode

  • 相关编译选项

    • MNN_METAL 是否使用Metal后端,Metal后端可以利用GPU加速

    • MNN_COREML 是否使用CoreML后端,CoreML后端可以利用ANE硬件加速

    • MNN_ARM82 是否支持fp16推理,开启该编译选项后,在precision设成Precision_Low时,会在支持的设备(ARMv8.2 及以上架构)上启用低精度(fp16)推理,减少内存占用,提升性能

  • 具体步骤

    • 在macOS下,用Xcode打开project/ios/MNN.xcodeproj,点击编译即可

其他平台交叉编译

由于交叉编译的目标设备及厂商提供的编译环境类型众多,本文恕无法提供手把手教学。 以下是大致流程,请按照具体场景做相应修改。交叉编译大致上分为以下两个步骤,即获取交叉编译器以及配置CMake进行交叉编译。

  1. 获取交叉编译工具链

    • 以Linaro工具链为例。首先从Linaro网页中按照宿主机以及交叉编译目标设备来选择合适的工具链。这里我们以arm-linux-gnueabi为例,点击网页上的链接,进入arm-linux-gnueabi页面。 按照宿主机类型(这里以X64 Linux为例)选择下载链接, 文件名形如 gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabi.tar.xz 下载后解压到任意目录。

  2. 配置交叉编译CMake

    • Toolchain法:对于常用的交叉编译配置,工具链提供方或网络上可能已经有现成的CMake Toolchain。 这种情况下使用如下命令即可:

      mkdir build
      cd build 
      cmake 其他CMake参数 /MNN/源码/路径 -DCMAKE_TOOLCHAIN_FILE=CMake/Toolchain/文件/路径
      
    • 手动配置法

      mkdir build && cd build
      cmake .. \
      -DCMAKE_SYSTEM_NAME=宿主系统,例如Linux \
      -DCMAKE_SYSTEM_VERSION=1 \
      -DCMAKE_SYSTEM_PROCESSOR=交叉编译目标处理器的信息。例如armv7或aarch64 \
      -DCMAKE_C_COMPILER=交叉编译器中C编译器的路径 \
      -DCMAKE_CXX_COMPILER=交叉编译器中C++编译器的路径
      
  3. 以Linaro ARM64为例

    • 下载aarch64交叉编译工具链

      mkdir -p linaro/aarch64
      cd linaro/aarch64
      wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabi/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabi.tar.xz
      tar xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabi.tar.xz
      
    • 构建编译

      export cross_compile_toolchain=linaro/aarch64
      mkdir build && cd build
      cmake .. \
      -DCMAKE_SYSTEM_NAME=Linux \
      -DCMAKE_SYSTEM_VERSION=1 \
      -DCMAKE_SYSTEM_PROCESSOR=aarch64 \
      -DCMAKE_C_COMPILER=$cross_compile_toolchain/bin/aarch64-linux-gnu-gcc \
      -DCMAKE_CXX_COMPILER=$cross_compile_toolchain/bin/aarch64-linux-gnu-g++
      make -j4
      

工具模块编译

模型转换工具

  • 相关编译选项

    • MNN_BUILD_CONVERTER 是否编译模型转换工具

    • MNN_BUILD_TORCH 是否支持TorchScript模型转换,MacOS下需要安装pytorch,Linux下会下载libtorch

  • 编译命令

    cmake .. -DMNN_BUILD_CONVERTER=ON -DMNN_BUILD_TORCH=ON
    
  • 编译产物

    • MNNConvert 模型转换工具

    • TestConvertResult 模型转换正确性测试工具,Windows下没有此产物,用MNNConvert对应功能替代

    • TestPassManager 模型转换工具测试用例

    • MNNDump2Json 模型转换为Json

    • MNNRevert2Buffer Json转换为模型

    • OnnxClip Onnx模型裁剪工具

训练框架

  • 相关编译选项

    • MNN_BUILD_TRAIN 是否编译训练框架

    • MNN_BUILD_TRAIN_MINI 对于移动端/嵌入式设备,建议设置MNN_BUILD_TRAIN_MINI=ON,不编译内置的DatasetModels,这部分在移动端/嵌入式设备上一般有其他解决方案

    • MNN_USE_OPENCV 部分PC上的demo有用到,与Dataset处理相关

  • 编译命令

    mkdir build && cd build
    cmake .. -DMNN_BUILD_TRAIN=ON -DMNN_USE_OPENCV=ON
    make -j4
    
  • 编译产物

    • MNNTrain 训练框架库

    • runTrainDemo.out 运行训练框架demo的入口程序

    • transformer.out 训练模型转换器

    • train.out 训练功能入口程序

    • rawDataTransform.out 将json文件转换为flatbuffers文件

    • dataTransformer.out 将图片转换为flatbuffers文件

测试工具

  • 相关编译选项

    • MNN_BUILD_TOOL 是否编译测试工具

  • 编译命令

    mkdir build && cd build
    cmake .. -DMNN_BUILD_TOOL=ON
    make -j4
    
  • 编译产物

    • GetMNNInfo 获取MNN模型信息

    • ModuleBasic.out 使用V3 API对模型执行基础推理测试

    • SequenceModuleTest.out 测试Sequence模型推理

    • MNNV2Basic.out 使用V2 API对模型执行基础推理测试

    • mobilenetTest.out 测试mobilenet模型推理

    • backendTest.out 测试模型在指定后端上执行的结果是否与CPU一致

    • modelCompare.out 原始模型与量化模型推理结果比较

    • testModel.out 给定输入输出测试模型推理正确性

    • testModel_expr.out 给定输入输出测试模型推理正确性

    • testModelWithDescribe.out 给定输入输出和shape描述测试模型推理正确性

    • getPerformance.out 获取当前设备的CPU性能

    • checkInvalidValue.out 检测输出目录里的数据

    • timeProfile.out 测试模型在指定后端上执行的时间,并获取每层的执行时间占比

    • testTrain.out 测试训练功能

    • aoa_nlu_encoder.out 测试NLU编码

    • aoa_nlu_decoder1.out 测试NLU解码1

    • aoa_nlu_decoder2.out 测试NLU解码2

    • checkDir.out 测试两个文件夹是否一致

    • checkFile.out 测试两个文件是否一致

    • winogradExample.out winograd示例

    • winogradGenerateGLSL.out winograd生成GLSL

    • winogradGenerateCL.out winograd生成CL

Benchmark工具

  • 相关编译选项

    • MNN_BUILD_BENCHMARK 是否编译Benchmark工具

  • 编译命令

    mkdir build && cd build
    cmake .. -DMNN_BUILD_BENCHMARK=ON
    make -j4
    
  • 编译产物

    • benchmark.out benchmark工具

    • benchmarkExprModels.out 表达式构图模型测试benchmark工具

模型量化工具

  • 相关编译选项

    • MNN_BUILD_QUANTOOLS 是否编译模型量化工具

  • 编译命令

    mkdir build && cd build
    cmake .. -DMNN_BUILD_QUANTOOLS=ON
    make -j4
    
  • 编译产物

    • quantized.out 模型量化工具

评估工具

  • 相关编译选项

    • MNN_EVALUATION 是否编译图片分类结果评估工具

  • 编译命令

    mkdir build && cd build
    cmake .. -DMNN_EVALUATION=ON
    make -j4
    
  • 编译产物

    • classficationTopkEval.out 图片分类结果评估工具

MNN OpenCV库

  • 相关编译选项

    • MNN_BUILD_OPENCV 是否编译OpenCV函数接口

    • MNN_IMGCODECS 是否编译OpenCV图像解码器

    • MNN_OPENCV_TEST 是否编译OpenCV单元测试

    • MNN_OPENCV_BENCH 是否编译OpenCV性能测试

  • 编译命令

    mkdir build && cd build
    cmake .. -DMNN_BUILD_OPENCV=ON -DMNN_IMGCODECS=ON -DMNN_OPENCV_TEST=ON -DMNN_OPENCV_BENCH=ON
    make -j4
    
  • 编译产物

    • libMNNOpenCV.so MNN OpenCV函数库

    • opencv_test MNN OpenCV单元测试

    • opencv_bench MNN OpenCV性能测试

示例工程

  • 相关编译选项

    • MNN_BUILD_DEMO 是否编译MNN Demo

  • 编译命令

    mkdir build && cd build
    cmake .. -DMNN_BUILD_DEMO=ON
    make -j4
    
  • 编译产物

    • pictureRecognition.out V2接口(Session)图片识别示例

    • pictureRecognition_module.out V3接口(Module)图片识别示例

    • pictureRecognition_batch.out 自定义batchsize图片识别示例

    • multithread_imgrecog.out 多线程图片识别示例

    • pictureRotate.out 图片旋转示例

    • multiPose.out 姿态检测示例

    • segment.out 图像实例分割示例

    • expressDemo.out 表达式接口推理示例

    • expressMakeModel.out 使用表达式构建模型示例

    • transformerDemo.out Transformer模型示例

    • rasterDemo.out Raster示例

    • nluDemo.out nlu模型示例

    • mergeInplaceForCPU 将模型中可以Inplace计算的算子改成Inplace计算,可以减少内存占用,但限定CPU后端运行

单元测试

  • 相关编译选项

    • MNN_BUILD_TEST 是否编译MNN单元测试

  • 编译命令

    mkdir build && cd build
    cmake .. -DMNN_BUILD_TEST=ON
    make -j4
    
  • 编译产物

    • run_test.out 单元测试程序

Pymnn构建

本地安装

cd /path/to/MNN/pymnn/pip_package
python build_deps.py
python setup.py install --version {MNN版本}

构建Python Wheel包

  • Linux

    # only CPU后端
    ./package_scripts/linux/build_whl.sh -v {MNN版本} -o MNN-CPU/py_whl
    # CPU+OpenCL后端
    ./package_scripts/linux/build_whl.sh -v {MNN版本} -o MNN-CPU-OPENCL/py_whl -b
    
  • Mac

    # only CPU后端
    ./package_scripts/mac/build_whl.sh -v {MNN版本} -o MNN-CPU/py_whl -p py27,py37,py38,py39
    # CPU+OpenCL后端
    ./package_scripts/mac/build_whl.sh -v {MNN版本} -o MNN-CPU/py_whl -p py27,py37,py38,py39 -b
    
  • Windows

    # CPU,64位编译
    powershell .\package_scripts\win\build_whl.ps1 -version {MNN版本} -path MNN-CPU/py_whl/x64 -pyenvs "py27,py37,py38,py39"
    # CPU,32位编译
    powershell .\package_scripts\win\build_whl.ps1 -version {MNN版本} -x86 -path MNN-CPU/py_whl/x86 -pyenvs "py27-win32,py37-win32,py38-win32,py39-win32"
    
    # CPU+OpenCL,64位编译
    .\package_scripts\win\build_whl.ps1 -version {MNN版本} -backends opencl -path MNN-CPU-OPENCL/py_whl/x64 -pyenvs "py27,py37,py38,py39"
    # CPU+OpenCL,32位编译
    .\package_scripts\win\build_whl.ps1 -version {MNN版本} -backends opencl -x86 -path MNN-CPU-OPENCL/py_whl/x86 -pyenvs "py27-win32,py37-win32,py38-win32,py39-win32"
    
    # CPU+Vulkan,64位编译
    .\package_scripts\win\build_whl.ps1 -version {MNN版本} -backends vulkan -path MNN-CPU-OPENCL/py_whl/x64 -pyenvs "py27,py37,py38,py39"
    # CPU+Vulkan,32位编译
    .\package_scripts\win\build_whl.ps1 -version {MNN版本} -backends vulkan -x86 -path MNN-CPU-OPENCL/py_whl/x86 -pyenvs "py27-win32,py37-win32,py38-win32,py39-win32"
    
    # CPU+OpenCL+Vulkan,64位编译
    .\package_scripts\win\build_whl.ps1 -version {MNN版本} -backends "opencl,vulkan" -path MNN-CPU-OPENCL/py_whl/x64 -pyenvs "py27,py37,py38,py39"
    # CPU+OpenCL+Vulkan,32位编译
    .\package_scripts\win\build_whl.ps1 -version {MNN版本} -backends "opencl,vulkan" -x86 -path MNN-CPU-OPENCL/py_whl/x86 -pyenvs "py27-win32,py37-win32,py38-win32,py39-win32"
    

Session API使用

创建会话

概述

使用MNN推理时,有两个层级的抽象,分别是解释器Interpreter会话SessionInterpreter是模型数据的持有者;Session通过Interpreter创建,是推理数据的持有者。多个推理可以共用同一个模型,即,多个Session可以共用一个Interpreter

在创建完Session,且不再创建Session或更新训练模型数据时,Interpreter可以通过releaseModel函数释放模型数据,以节省内存。

创建Interpreter

有两种创建Interpreter的方法:

  • 通过磁盘文件创建

/**
 * @brief create net from file.
 * @param file  given file.
 * @return created net if success, NULL otherwise.
 */
static Interpreter* createFromFile(const char* file);
  • 通过内存数据创建

/**
 * @brief create net from buffer.
 * @param buffer    given data buffer.
 * @param size      size of data buffer.
 * @return created net if success, NULL otherwise.
 */
static Interpreter* createFromBuffer(const void* buffer, size_t size);

函数返回的Interpreter实例是通过new创建的,务必在不再需要时,通过delete释放,以免造成内存泄露。

创建Session

一般通过Interpreter::createSession创建Session:

/**
 * @brief create session with schedule config. created session will be managed in net.
 * @param config session schedule config.
 * @return created session if success, NULL otherwise.
 */
Session* createSession(const ScheduleConfig& config);

函数返回的Session实例是由Interpreter管理,随着Interpreter销毁而释放,一般不需要关注。也可以在不再需要时,调用Interpreter::releaseSession释放,减少内存占用。

创建Session 一般而言需要较长耗时,而Session在多次推理过程中可以重复使用,建议只创建一次多次使用。

简易模式

一般情况下,不需要额外设置调度配置,函数会根据模型结构自动识别出调度路径、输入输出,例如:

ScheduleConfig conf;
Session* session = interpreter->createSession(conf);
调度配置

调度配置定义如下:

/** session schedule config */
struct ScheduleConfig {
    /** which tensor should be kept */
    std::vector<std::string> saveTensors;
    /** forward type */
    MNNForwardType type = MNN_FORWARD_CPU;
    /** CPU:number of threads in parallel , Or GPU: mode setting*/
    union {
        int numThread = 4;
        int mode;
    };

    /** subpath to run */
    struct Path {
        std::vector<std::string> inputs;
        std::vector<std::string> outputs;

        enum Mode {
            /**
             * Op Mode
             * - inputs means the source op, can NOT be empty.
             * - outputs means the sink op, can be empty.
             * The path will start from source op, then flow when encounter the sink op.
             * The sink op will not be compute in this path.
             */
            Op = 0,

            /**
             * Tensor Mode
             * - inputs means the inputs tensors, can NOT be empty.
             * - outputs means the outputs tensors, can NOT be empty.
             * It will find the pipeline that compute outputs from inputs.
             */
            Tensor = 1
        };

        /** running mode */
        Mode mode = Op;
    };
    Path path;

    /** backup backend used to create execution when desinated backend do NOT support any op */
    MNNForwardType backupType = MNN_FORWARD_CPU;

    /** extra backend config */
    BackendConfig* backendConfig = nullptr;
};

推理时,主选后端由type指定,默认为CPU。若模型中存在主选后端不支持的算子,这些算子会使用由backupType指定的备选后端运行。

推理路径包括由pathinputsoutputs途径的所有算子,在不指定时,会根据模型结构自动识别。为了节约内存,MNN会复用outputs之外的tensor内存。如果需要保留中间tensor的结果,可以使用saveTensors保留tensor结果,避免内存复用。

CPU推理时,并发数与线程数可以由numThread修改。numThread决定并发数的多少,但具体线程数和并发效率,不完全取决于numThread

  • iOS,线程数由系统GCD决定;

  • 启用MNN_USE_THREAD_POOL时,线程数取决于第一次配置的大于1的numThread

  • OpenMP,线程数全局设置,实际线程数取决于最后一次配置的numThread

GPU推理时,可以通过mode来设置GPU运行的一些参量选择(暂时只支持OpenCL)。GPU mode参数如下:

typedef enum {
    // choose one tuning mode Only
    MNN_GPU_TUNING_NONE    = 1 << 0,/* Forbidden tuning, performance not good */
    MNN_GPU_TUNING_HEAVY  = 1 << 1,/* heavily tuning, usually not suggested */
    MNN_GPU_TUNING_WIDE   = 1 << 2,/* widely tuning, performance good. Default */
    MNN_GPU_TUNING_NORMAL = 1 << 3,/* normal tuning, performance may be ok */
    MNN_GPU_TUNING_FAST   = 1 << 4,/* fast tuning, performance may not good */
    
    // choose one opencl memory mode Only
    /* User can try OpenCL_MEMORY_BUFFER and OpenCL_MEMORY_IMAGE both, then choose the better one according to performance*/
    MNN_GPU_MEMORY_BUFFER = 1 << 6,/* User assign mode */
    MNN_GPU_MEMORY_IMAGE  = 1 << 7,/* User assign mode */
} MNNGpuMode;

目前支持tuning力度以及GPU memory用户可自由设置。例如:

MNN::ScheduleConfig config;
config.mode = MNN_GPU_TUNING_NORMAL | MNN_GPU_MEMORY_IMAGE;

tuning力度选取越高,第一次初始化耗时越多,推理性能越佳。如果介意初始化时间过长,可以选取MNN_GPU_TUNING_FAST或者MNN_GPU_TUNING_NONE,也可以同时通过下面的cache机制,第二次之后就不会慢。GPU_Memory用户可以指定使用MNN_GPU_MEMORY_BUFFER或者MNN_GPU_MEMORY_IMAGE,用户可以选择性能更佳的那一种。如果不设定,框架会采取默认判断帮你选取(不保证一定性能最优)。

上述CPU的numThread和GPU的mode,采用union联合体方式,共用同一片内存。用户在设置的时候numThread和mode只需要设置一种即可,不要重复设置。

对于GPU初始化较慢的问题,提供了Cache机制。后续可以直接加载cache提升初始化速度。

  • 具体可以参考tools/cpp/MNNV2Basic.cpp里面setCacheFile设置cache方法进行使用。

  • 当模型推理输入尺寸有有限的多种时,每次resizeSession后调用updateCacheFile更新cache文件。

  • 当模型推理输入尺寸无限随机变化时,建议config.mode设为1,关闭MNN_GPU_TUNING。

此外,可以通过backendConfig设定后端的额外参数。具体见下。

后端配置

后端配置定义如下:

struct BackendConfig {
    enum MemoryMode {
        Memory_Normal = 0,
        Memory_High,
        Memory_Low
    };
    
    MemoryMode memory = Memory_Normal;
    
    enum PowerMode {
        Power_Normal = 0,
        Power_High,
        Power_Low
    };
    
    PowerMode power = Power_Normal;
    
    enum PrecisionMode {
        Precision_Normal = 0,
        Precision_High,
        Precision_Low,
        Precision_Low_BF16
    };
    
    PrecisionMode precision = Precision_Normal;
    
    /** user defined context */
    void* sharedContext = nullptr;
};

memorypowerprecision分别为内存、功耗和精度偏好。支持这些选项的后端会在执行时做出相应调整;若不支持,则忽略选项。

示例: 后端 OpenCL precision 为 Low 时,使用 fp16 存储与计算,计算结果与CPU计算结果有少量误差,实时性最好;precision 为 Normal 时,使用 fp16存储,计算时将fp16转为fp32计算,计算结果与CPU计算结果相近,实时性也较好;precision 为 High 时,使用 fp32 存储与计算,实时性下降,但与CPU计算结果保持一致。

后端 CPU precision 为 Low 时,根据设备情况开启 FP16 计算 precision 为 Low_BF16 时,根据设备情况开启 BF16 计算

sharedContext用于自定义后端,用户可以根据自身需要赋值。

创建多段路径Session

需要对推理路径做出更为复杂的配置时,可以通过调度配置组来实现:

/**
 * @brief create multi-path session with schedule configs. created session will be managed in net.
 * @param configs session schedule configs.
 * @return created session if success, NULL otherwise.
 */
Session* createMultiPathSession(const std::vector<ScheduleConfig>& configs);

每个调度配置可以独立配置路径、选项。

共享运行时资源

默认情况下,在createSession时对应create单独一个 Runtime。对于串行的一系列模型,可以先单独创建Runtime ,然后在各 Session 创建时传入,使各模型用共享同样的运行时资源(对CPU而言为线程池、内存池,对GPU而言Kernel池等)。

示例:

ScheduleConfig config;
config.numberThread = 4;
auto runtimeInfo = Interpreter::createRuntime({config});

/*创建第一个模型*/
std::shared_ptr<Interpreter> net1 = Interpreter::createFromFile("1.mnn");
auto session1 = net1->createSession(config, runtimeInfo);

/*创建第二个模型*/
std::shared_ptr<Interpreter> net2 = Interpreter::createFromFile("2.mnn");
auto session2 = net2->createSession(config, runtimeInfo);

/*创建第三个模型*/
std::shared_ptr<Interpreter> net3 = Interpreter::createFromFile("3.mnn");
auto session3 = net3->createSession(config, runtimeInfo);

// 这样 session1, session2, session3 共用同一个Runtime

/*使用*/
/* 填充输入1..... */
net1->runSession(session1);

/* 读取输出1 填充输入2..... */
net2->runSession(session2);

/* 读取输出2 填充输入3..... */
net3->runSession(session3);

输入数据

获取输入tensor

/**
 * @brief get input tensor for given name.
 * @param session   given session.
 * @param name      given name. if NULL, return first input.
 * @return tensor if found, NULL otherwise.
 */
Tensor* getSessionInput(const Session* session, const char* name);

/**
 * @brief get all input tensors.
 * @param session   given session.
 * @return all output tensors mapped with name.
 */
const std::map<std::string, Tensor*>& getSessionInputAll(const Session* session) const;

Interpreter上提供了两个用于获取输入Tensor的方法:getSessionInput用于获取单个输入tensor, getSessionInputAll用于获取输入tensor映射。

在只有一个输入tensor时,可以在调用getSessionInput时传入NULL以获取tensor。

拷贝数据

NCHW示例,适用 ONNX / Caffe / Torchscripts 转换而来的模型:

auto inputTensor = interpreter->getSessionInput(session, NULL);
auto nchwTensor = new Tensor(inputTensor, Tensor::CAFFE);
// nchwTensor-host<float>()[x] = ...
inputTensor->copyFromHostTensor(nchwTensor);
delete nchwTensor;

NHWC示例,适用于由 Tensorflow / Tflite 转换而来的模型:

auto inputTensor = interpreter->getSessionInput(session, NULL);
auto nhwcTensor = new Tensor(inputTensor, Tensor::TENSORFLOW);
// nhwcTensor-host<float>()[x] = ...
inputTensor->copyFromHostTensor(nhwcTensor);
delete nhwcTensor;

通过这类拷贝数据的方式,用户只需要关注自己创建的tensor的数据布局,copyFromHostTensor会负责处理数据布局上的转换(如需)和后端间的数据拷贝(如需)。

直接填充数据

auto inputTensor = interpreter->getSessionInput(session, NULL);
inputTensor->host<float>()[0] = 1.f;

Tensor上最简洁的输入方式是直接利用host填充数据,但这种使用方式仅限于CPU后端,其他后端需要通过deviceid来输入。另一方面,用户需要自行处理NC4HW4NHWC数据格式上的差异。

对于非CPU后端,或不熟悉数据布局的用户,宜使用拷贝数据接口。

图像处理

MNN中提供了CV模块,可以帮助用户简化图像的处理,还可以免于引入opencv、libyuv等图片处理库。

  1. 支持目标Tensor为float或 uint8_t 的数据格式

  2. 支持目标Tensor为NC4HW4或NHWC的维度格式

  3. CV模块支持直接输入Device Tensor,也即由Session中获取的Tensor。

图像处理配置
struct Config
{
    Filter filterType = NEAREST;
    ImageFormat sourceFormat = RGBA;
    ImageFormat destFormat = RGBA;
    
    //Only valid if the dest type is float
    float mean[4] = {0.0f,0.0f,0.0f, 0.0f};
    float normal[4] = {1.0f, 1.0f, 1.0f, 1.0f};
};

CV::ImageProcess::Config

  • 通过sourceFormatdestFormat指定输入和输出的格式,当前支持RGBARGBBGRGRAYBGRAYUV_NV21、YUV_NV12

  • 通过filterType指定插值的类型,当前支持NEARESTBILINEARBICUBIC三种插值方式

  • 通过meannormal指定均值归一化,但数据类型不是浮点类型时,设置会被忽略

图像变换矩阵

CV::Matrix移植自Android 系统使用的Skia引擎,用法可参考Skia的Matrix:https://skia.org/user/api/SkMatrix_Reference

需要注意的是,ImageProcess中设置的Matrix是从目标图像到源图像的变换矩阵。使用时,可以按源图像到目标图像的变换设定,最后取逆。例如:

// 源图像:1280x720
// 目标图像:逆时针旋转90度再缩小到原来的1/10,即变为72x128

Matrix matrix;
// 重设为单位矩阵
matrix.setIdentity();
// 缩小,变换到 [0,1] 区间:
matrix.postScale(1.0f / 1280, 1.0f / 720);
// 以中心点[0.5, 0.5]旋转90度
matrix.postRotate(90, 0.5f, 0.5f);
// 放大回 72x128
matrix.postScale(72.0f, 128.0f);
// 转变为 目标图像 -> 源图的变换矩阵
matrix.invert(&matrix);
图像处理实例

MNN中使用CV::ImageProcess进行图像处理。ImageProcess内部包含一系列缓存,为了避免内存的重复申请释放,建议将其作缓存,仅创建一次。我们使用ImageProcessconvert填充tensor数据。

/*
 * source: 源图像地址
 * iw: 源图像宽
 * ih:源图像高,
 * stride:源图像对齐后的一行byte数(若不需要对齐,设成 0(相当于 iw*bpp))
 * dest: 目标 tensor,可以为 uint8 或 float 类型
 */
ErrorCode convert(const uint8_t* source, int iw, int ih, int stride, Tensor* dest);
完整示例
auto input  = net->getSessionInput(session, NULL);
auto output = net->getSessionOutput(session, NULL);

auto dims  = input->shape();
int bpp    = dims[1]; 
int size_h = dims[2];
int size_w = dims[3];

auto inputPatch = argv[2];
FREE_IMAGE_FORMAT f = FreeImage_GetFileType(inputPatch);
FIBITMAP* bitmap = FreeImage_Load(f, inputPatch);
auto newBitmap = FreeImage_ConvertTo32Bits(bitmap);
auto width = FreeImage_GetWidth(newBitmap);
auto height = FreeImage_GetHeight(newBitmap);
FreeImage_Unload(bitmap);

Matrix trans;
//Dst -> [0, 1]
trans.postScale(1.0/size_w, 1.0/size_h);
//Flip Y  (因为 FreeImage 解出来的图像排列是Y方向相反的)
trans.postScale(1.0,-1.0, 0.0, 0.5);
//[0, 1] -> Src
trans.postScale(width, height);

ImageProcess::Config config;
config.filterType = NEAREST;
float mean[3] = {103.94f, 116.78f, 123.68f};
float normals[3] = {0.017f,0.017f,0.017f};
::memcpy(config.mean, mean, sizeof(mean));
::memcpy(config.normal, normals, sizeof(normals));
config.sourceFormat = RGBA;
config.destFormat = BGR;

std::shared_ptr<ImageProcess> pretreat(ImageProcess::create(config));
pretreat->setMatrix(trans);
pretreat->convert((uint8_t*)FreeImage_GetScanLine(newBitmap, 0), width, height, 0, input);
net->runSession(session);

可变维度

/**
 * @brief resize given tensor.
 * @param tensor    given tensor.
 * @param dims      new dims. at most 6 dims.
 */
void resizeTensor(Tensor* tensor, const std::vector<int>& dims);

/**
 * @brief resize given tensor by nchw.
 * @param batch  / N.
 * @param channel   / C.
 * @param height / H.
 * @param width / W
 */
void resizeTensor(Tensor* tensor, int batch, int channel, int height, int width);

/**
 * @brief call this function to get tensors ready. output tensor buffer (host or deviceId) should be retrieved
 *        after resize of any input tensor.
 * @param session given session.
 */
void resizeSession(Session* session);

在输入Tensor维度不确定或需要修改时,需要调用resizeTensor来更新维度信息。这种情况一般发生在未设置输入维度和输入维度信息可变的情况。更新完所有Tensor的维度信息之后,需要再调用resizeSession来进行预推理,进行内存分配及复用。示例如下:

auto inputTensor = interpreter->getSessionInput(session, NULL);
interpreter->resizeTensor(inputTensor, {newBatch, newChannel, newHeight, newWidth});
interpreter->resizeSession(session);
inputTensor->copyFromHostTensor(imageTensor);
interpreter->runSession(session);

运行会话

MNN中,Interpreter一共提供了三个接口用于运行Session,但一般来说,简易运行就足够满足绝对部分场景。

简易运行

/**
 * @brief run session.
 * @param session   given session.
 * @return result of running.
 */
ErrorCode runSession(Session* session) const;

传入事先创建好的Session即可。

函数耗时并不总是等于推理耗时 —— 在CPU下,函数耗时即推理耗时;在其他后端下,函数可能不会同步等待推理完成,例如GPU下,函数耗时为GPU指令提交耗时。

回调运行

typedef std::function<bool(const std::vector<Tensor*>&, 
                           const std::string& /*opName*/)> TensorCallBack;

/*
 * @brief run session.
 * @param session   given session.
 * @param before    callback before each op. return true to run the op; return false to skip the op.
 * @param after     callback after each op. return true to continue running; return false to interrupt the session.
 * @param sync      synchronously wait for finish of execution or not.
 * @return result of running.
 */
ErrorCode runSessionWithCallBack(const Session* session, 
                                 const TensorCallBack& before, 
                                 const TensorCallBack& end,
                                 bool sync = false) const;

相比于简易运行,回调运行额外提供了:

  • 每一个op执行前的回调,可以用于跳过Op执行;

  • 每一个op执行后的回调,可以用于中断整个推理;

  • 同步等待选项,默认关闭;开启时,所有后端均会等待推理完成,即函数耗时等于推理耗时;

计算量评估

class MNN_PUBLIC OperatorInfo {
    struct Info;

public:
    /** Operator's name*/
    const std::string& name() const;

    /** Operator's type*/
    const std::string& type() const;

    /** Operator's flops, in M*/
    float flops() const;

protected:
    OperatorInfo();
    ~OperatorInfo();
    Info* mContent;
};
typedef std::function<bool(const std::vector<Tensor*>&, const OperatorInfo*)> TensorCallBackWithInfo;

/*
 * @brief run session.
 * @param session   given session.
 * @param before    callback before each op. return true to run the op; return false to skip the op.
 * @param after     callback after each op. return true to continue running; return false to interrupt the session.
 * @param sync      synchronously wait for finish of execution or not.
 * @return result of running.
 */
ErrorCode runSessionWithCallBackInfo(const Session* session, 
                                     const TensorCallBackWithInfo& before,
                                     const TensorCallBackWithInfo& end, 
                                     bool sync = false) const;

一般而言,只有在评估计算量时才会用到的接口。相比于回调运行,在回调时,增加了Op类型和计算量信息。

获取输出

获取输出tensor

/**
 * @brief get output tensor for given name.
 * @param session   given session.
 * @param name      given name. if NULL, return first output.
 * @return tensor if found, NULL otherwise.
 */
Tensor* getSessionOutput(const Session* session, const char* name);

/**
 * @brief get all output tensors.
 * @param session   given session.
 * @return all output tensors mapped with name.
 */
const std::map<std::string, Tensor*>& getSessionOutputAll(const Session* session) const;

Interpreter上提供了两个用于获取输出Tensor的方法:getSessionOutput用于获取单个输出tensor, getSessionOutputAll用于获取输出tensor映射。

在只有一个输出tensor时,可以在调用getSessionOutput时传入NULL以获取tensor。

注意:当Session析构之后使用getSessionOutput获取的Tensor将不可用

拷贝数据

不熟悉MNN源码的用户,必须使用这种方式获取输出!!! NCHW (适用于 Caffe / TorchScript / Onnx 转换而来的模型)示例:

auto outputTensor = interpreter->getSessionOutput(session, NULL);
auto nchwTensor = new Tensor(outputTensor, Tensor::CAFFE);
outputTensor->copyToHostTensor(nchwTensor);
auto score = nchwTensor->host<float>()[0];
auto index = nchwTensor->host<float>()[1];
// ...
delete nchwTensor;

NHWC (适用于 Tensorflow / Tflite 转换而来的模型)示例:

auto outputTensor = interpreter->getSessionOutput(session, NULL);
auto nhwcTensor = new Tensor(outputTensor, Tensor::TENSORFLOW);
outputTensor->copyToHostTensor(nhwcTensor);
auto score = nhwcTensor->host<float>()[0];
auto index = nhwcTensor->host<float>()[1];
// ...
delete nhwcTensor;

通过这类拷贝数据的方式,用户只需要关注自己创建的tensor的数据布局,copyToHostTensor会负责处理数据布局上的转换(如需)和后端间的数据拷贝(如需)。

直接读取数据

由于绝大多数用户都不熟悉MNN底层数据布局,所以不要使用这种方式!!!

auto outputTensor = interpreter->getSessionOutput(session, NULL);
auto score = outputTensor->host<float>()[0];
auto index = outputTensor->host<float>()[1];
// ...

Tensor上最简洁的输出方式是直接读取host数据,但这种使用方式仅限于CPU后端,其他后端需要通过deviceid来读取数据。另一方面,用户需要自行处理NC4HW4NHWC数据格式上的差异。

对于非CPU后端,或不熟悉数据布局的用户,宜使用拷贝数据接口。

示例代码

完整的示例代码可以参考demo/exec/文件夹中的以下源码文件:

  • pictureRecognition.cpp 使用Session执行模型推理进行图片分类,使用ImageProcess进行前处理

  • multiPose.cpp 使用Session执行模型推理进行姿态检测,使用ImageProcess进行前处理

  • segment.cpp 使用Session执行模型推理进行图像分割,使用ImageProcess进行前处理,Expr进行后处理

  • pictureRotate.cpp 使用ImageProcess进行图像处理

Module API使用

概念说明

Module接口可以用于模型训练与模型推理

  • 模型训练时用户可以继承Module类增加自己的实现用来训练;

  • 模型推理与Session的区别是不需要用户显示resize,支持控制流,所以当模型中有ifwhile时必须使用Module推理

相关数据结构

  • Module Module接口的核心类,表示一个模型的虚类;实际加载模型时会创建其子类

  • Executor 包含若干个RuntimeManager,提供内存管理接口,每个Executor必须在单线程环境下运行。默认提供全局 Executor,需要并发执行时,可自行创建。

  • ExecutorScope 用于在子线程中绑定Executor,多线程并发必需

  • VARP 作为Module的输入输出,也是Expr API中的基础数据结构

工作流程

配置Executor(可选) -> 创建 RuntimeManager(可选) -> 创建Module -> 创建输入VARP -> 使用Module::forwad推理 -> 使用输出VARP -> 销毁Module

(可选)配置Executor

Executor给用户提供接口来配置推理后端、线程数等属性,以及做性能统计、算子执行的回调函数、内存回收等功能。 提供一个全局的Exector对象,用户不用创建或持有对象即可直接使用。

// 配置默认全局Exector
MNN::BackendConfig backend_config;    // default backend config 
// 设置使用4线程+CPU
MNN::Express::Executor::getGlobalExecutor()->setGlobalExecutorConfig(MNN_FORWARD_CPU, backend_config, 4);

(可选)创建 RuntimeManager

Executor 的配置会同时影响Module和表达式计算的后端配置。

*** 如下示例会触发表达式计算,若 Executor 设置为 OPENCL ,则该计算会用OpenCL后端实现

MNN::Express::VARP X;
MNN::Express::VARP Y = MNN::Express::_Sign(X);
float* yPtr = Y->readMap<float>();

若希望仅在该Module中采用某种后端配置(比如Module使用GPU但表达式计算使用CPU),可额外创建 RuntimeManager ,并在创建 Module 时传入

MNN::ScheduleConfig sConfig;
sConfig.type = MNN_FORWARD_OPENCL;

std::shared_ptr<MNN::Express::Executor::RuntimeManager> rtmgr(MNN::Express::Executor::RuntimeManager::createRuntimeManager(sConfig), MNN::Express::Executor::RuntimeManager::destroy);
rtmgr->setCache(".cachefile");

创建Module

Module可以通过指定模型,输入输出的名称,配置文件创建

// 从模型文件加载并创建新Module
const std::string model_file = "/tmp/mymodule.mnn"; // model file with path

// 输入名,可以为空,为空时 MNN 自动搜索模型中的输入,多输入情况下无法保证顺序,需要通过 getInfo 接口查看
const std::vector<std::string> input_names{"input_1", "input_2", "input_3"};
// 输出名,可以为空,为空时 MNN 自动搜索模型中的输出,多输出情况下无法保证顺序,需要通过 getInfo 接口查看
const std::vector<std::string> output_names{"output_1"};

Module::Config mdconfig; // default module config
std::unique_ptr<Module> module; // module 
// 若 rtMgr 为 nullptr ,Module 会使用Executor的后端配置
module.reset(Module::load(input_names, output_names, model_filename.c_str(), rtMgr, &mdconfig));

获取模型信息

调用getInfo函数可获取Module信息,可以参考代码:tools/cpp/GetMNNInfo.cpp工具

struct Info {
    // Input info load from model
    std::vector<Variable::Info> inputs;
    // The Module's defaultFormat, NCHW or NHWC
    Dimensionformat defaultFormat;
    // Runtime Info
    std::shared_ptr<MNN::Express::Executor::RuntimeManager> runTimeManager;
    // Input Names By Order
    std::vector<std::string> inputNames;
    // Output Names By Order
    std::vector<std::string> outputNames;
    // Version of MNN which build the model
    std::string version;
};
const Info* getInfo() const;

执行推理

调用onForward执行推理。

std::vector<MNN::Express::VARP> onForward(const std::vector<MNN::Express::VARP>& inputs);

示例代码:

int dim = 224;
std::vector<VARP> inputs(3);
// 对于 tensoflow 转换过来的模型用 NHWC ,由 onnx 转换过来的模型用 NCHW
inputs[0] = MNN::Express::_Input({1, dim}, NHWC, halide_type_of<int>());
inputs[1] = MNN::Express::_Input({1, dim}, NHWC, halide_type_of<int>());
inputs[2] = MNN::Express::_Input({1, dim}, NHWC, halide_type_of<int>());

// 设置输入数据
std::vector<int*> input_pointer = {inputs[0]->writeMap<int>(),
                                   inputs[1]->writeMap<int>(),
                                   inputs[2]->writeMap<int>()};
for (int i = 0; i < dim; ++i) {
    input_pointer[0] = i + 1;
    input_pointer[1] = i + 2;
    input_pointer[2] = i + 3;
}
// 执行推理
std::vector<MNN::Express::VARP> outputs  = module->onForward(inputs);
// 获取输出
auto output_ptr = outputs[0]->readMap<float>();

多实例推理

Module API 支持同个模型创建多个实例,分发到不同线程推理。具体步骤如下:

  • 【启动】主线程创建基准Module: 配置Executor(可选) -> 创建 RuntimeManager(可选) -> 创建Module

  • 【启动】创建子线程,在子线程中创建 Executor

  • 【启动】子线程绑定该线程的Executor , Clone Module

  • 【使用】子线程绑定该线程的executor,使用 Clone 出来的 Module进行推理:创建输入VARP -> 使用Module::forwad推理 -> 使用输出VARP

  • 【结束】子线程绑定该线程的executor,销毁 Module

  • 【结束】子线程销毁 Executor ,销毁子线程

  • 【结束】主线程销毁 Module

创建基准Module

第一个实例的创建过程不需要变更

每个实例新建Exector

NNForwardType type = MNN_FORWARD_CPU;
MNN::BackendConfig backend_config;    // default backend config 
std::shared_ptr<MNN::Express::Executor> executor(
    MNN::Express::Executor::newExecutor(type, backend_config, 1));

** 若一个算法流程中有多个模型运行,每份实例单独建一个 Executor 即可。

每个实例克隆基准Module

// 绑定这个实例的executor,这样不会与其他实例产生内存冲突
MNN::Express::ExecutorScope scope(executor);
std::unique_ptr<MNN::Express::Module> module_thread(MNN::Express::Module::clone(module.get()), MNN::Express::Module::destroy);

克隆出来的 Module 与基准 Module 共享不变的权重与常量数据,可以较大地降低新增实例若需的内存。

每个实例推理

// 每个实例推理之前用 ExecutorScope 绑定这个实例的 executor
MNN::Express::ExecutorScope scope(executor);
std::vector<VARP> inputs;
/* 构建输入......*/
// 执行推理
std::vector<MNN::Express::VARP> outputs = module_thread->onForward(inputs);
/* 使用输出......*/

销毁

//每个实例销毁Module之前,也需要用 ExecutorScope 绑定这个实例的 executor
MNN::Express::ExecutorScope scope(executor);
module_thread.reset();

调试

Module API 也支持使用回调函数进行调试,与runSessionWithCallBack相似。示例代码:

MNN::TensorCallBackWithInfo beforeCallBack = [&](const std::vector<MNN::Tensor*>& ntensors, const OperatorInfo* info) {

    // do any thing you want.
    auto opName = info->name();
    for (int i = 0; i < ntensors.size(); ++i) {
        auto ntensor    = ntensors[i];
        print("input op name:%s, shape:", opName.c_str());
        ntensor->printShape();
    }
    return true;
};
MNN::TensorCallBackWithInfo callBack = [&](const std::vector<MNN::Tensor*>& ntensors,  const OperatorInfo* info) {
    auto opName = info->name();
    for (int i = 0; i < ntensors.size(); ++i) {
        auto ntensor    = ntensors[i];
        print("output op name:%s, shape:", opName.c_str());
        ntensor->printShape();
    }
    return true;
};

// 设置回调函数,需要是创建该 Module 时的 executor ,非多实例情况下用全局 executor 即可:
Express::Executor::getGlobalExecutor()->setCallBack(std::move(beforeCallBack), std::move(callBack));

// forward would trigger callback
std::vector<VARP> outputs  = user_module->onForward(inputs);

示例代码

完整的示例代码可以参考demo/exec/文件夹中的以下源码文件:

  • pictureRecognition_module.cpp 使用Module执行图像分类,使用ImageProcess进行前处理,Expr进行后处理

  • pictureRecognition_batch.cpp 使用Module执行图像分类,使用ImageProcess进行前处理,Expr进行后处理

  • multithread_imgrecog.cpp 使用Module多线程并发执行图像分类,使用ImageProcess进行前处理,Expr进行后处理

  • transformerDemo.cpp 使用Module执行Transformer模型推理

Python API使用

安装

MNN Python API可以使用源码安装,也可以直接使用pip安装预编译whl包;pip安装用法如下:

# 外部版本安装
pip install MNN==$version
# 公司内部版本安装
pip install -i https://artifacts.antgroup-inc.cn/simple/ MNN-Internal==$version

概览

MNN在C++的基础上,增加了Python扩展。扩展单元包括两个部分:

  • MNN:负责推理,训练,图像处理和数值计算

  • MNNTools:对MNN的部分工具进行封装,包括:mnn,mnnconvert和mnnquant

MNN

MNN

MNNTools

MNNTools提供目前主要是2个工具,用法可以参考mnnconvertmnnquant

使用Python Module API

数据类型

Python中的Module API与C++中的函数名略有区别,用法相似。主要数据类型如下:

推理流程

基本推理流程如下:

  • 创建Module

  • 创建输入: 使用exprnumpy函数创建Var即可作为输入

  • 执行推理

  • 获取输出: 输出为Var类型,可以通过exprnumpy函数执行后处理

示例

import MNN.nn as nn
import MNN.cv as cv
import MNN.numpy as np
import MNN.expr as expr

# 配置执行后端,线程数,精度等信息;key-vlaue请查看API介绍
config = {}
config['precision'] = 'low' # 当硬件支持(armv8.2)时使用fp16推理
config['backend'] = 0       # CPU
config['numThread'] = 4     # 线程数

rt = nn.create_runtime_manager((config,))
# 加载模型创建_Module
net = nn.load_module_from_file('mobilenet_v1.mnn', ['data'], ['prob'], runtime_manager=rt)

# 读取图片
image = cv.imread('cat.jpg')
# 转换为float32, 形状为[224,224,3]
image = cv.resize(image, (224, 224), mean=[103.94, 116.78, 123.68], norm=[0.017, 0.017, 0.017])
# 增加batch HWC to NHWC
input_var = np.expand_dims(image, 0)
# NHWC to NC4HW4
input_var = expr.convert(input_var, expr.NC4HW4)

# 执行推理
output_var = net.forward(input_var)

# NC4HW4 to NHWC
output_var = expr.convert(output_var, expr.NHWC)
# 打印出分类结果, 282为猫
print("output belong to class: {}".format(np.argmax(output_var)))
# output belong to class: 282

其他示例可以参考示例;也可以参考示例工程

使用Python Session API [deprecated]

不建议使用该API执行推理,建议使用Module API

数据类型

Python中Session API的函数名与用法与C++基本一样。使用的主要数据类型如下:

推理流程

基本推理流程如下:

示例

import MNN
import MNN.cv as cv
import MNN.numpy as np
import MNN.expr as expr

# 创建interpreter
interpreter = MNN.Interpreter("mobilenet_v1.mnn")
# 创建session
config = {}
config['precision'] = 'low'
config['backend'] = 'CPU'
config['thread'] = 4
session = interpreter.createSession(config)
# 获取会话的输入输出
input_tensor = interpreter.getSessionInput(session)
output_tensor = interpreter.getSessionOutput(session)

# 读取图片
image = cv.imread('cat.jpg')

dst_height = dst_width = 224
# 使用ImageProcess处理第一张图片,将图片转换为转换为size=(224, 224), dtype=float32,并赋值给input_data1
image_processer = MNN.CVImageProcess({'sourceFormat': MNN.CV_ImageFormat_BGR,
                                      'destFormat': MNN.CV_ImageFormat_BGR,
                                      'mean': (103.94, 116.78, 123.68, 0.0),
                                      'filterType': MNN.CV_Filter_BILINEAL,
                                      'normal': (0.017, 0.017, 0.017, 0.0)})
image_data = image.ptr
src_height, src_width, channel = image.shape
input_data1 = MNN.Tensor((1, dst_height, dst_width, channel), MNN.Halide_Type_Float, MNN.Tensor_DimensionType_Tensorflow)
#设置图像变换矩阵
matrix = MNN.CVMatrix()
x_scale = src_width / dst_width
y_scale = src_height / dst_height
matrix.setScale(x_scale, y_scale)
image_processer.setMatrix(matrix)
image_processer.convert(image_data, src_width, src_height, 0, input_data1)

# 使用cv模块处理第二张图片,将图片转换为转换为size=(224, 224), dtype=float32,并赋值给input_data2
image = cv.imread('TestMe.jpg')
image = cv.resize(image, (224, 224), mean=[103.94, 116.78, 123.68], norm=[0.017, 0.017, 0.017])
input_data2 = np.expand_dims(image, 0) # [224, 224, 3] -> [1, 224, 224, 3]

# 合并2张图片到,并赋值给input_data
input_data1 = expr.const(input_data1.getHost(), input_data1.getShape(), expr.NHWC) # Tensor -> Var
input_data = np.concatenate([input_data1, input_data2])  # [2, 224, 224, 3]
input_data = MNN.Tensor(input_data) # Var -> Tensor

# 演示多张图片输入,所以将输入resize到[2, 3, 224, 224]
interpreter.resizeTensor(input_tensor, (2, 3, 224, 224))
# 重新计算形状分配内存
interpreter.resizeSession(session)

# 拷贝数据到输入Tensor
input_tensor.copyFrom(input_data)

# 执行会话推理
interpreter.runSession(session)

# 从输出Tensor拷贝出数据 
output_data = MNN.Tensor(output_tensor.getShape(), MNN.Halide_Type_Float, MNN.Tensor_DimensionType_Caffe)
output_tensor.copyToHostTensor(output_data)

# 打印出分类结果: 282为猫,385为象
output_var = expr.const(output_data.getHost(), [2, 1001])
print("output belong to class: {}".format(np.argmax(output_var, 1)))
# output belong to class: array([282, 385], dtype=int32)

其他示例可以参考示例;也可以参考示例工程

使用cv/numpy API

数据类型

Python的cvnumpy接口,其中cv是对C++中tools/cv实现的封装;numpy则是对expr接口的封装;这两个接口主要为了提高MNN的易用性,与opencvnumpy做到了再接口上的部分兼容,在用法和思路上基本一致。主要数据类型如下:

  • Var cv中的图像,numpy中的ndarray

主要用法

cvnumpy主要用作模型的前后处理部分,和一些数值计算任务。比如从图片直接读取数据后一般需要执行颜色空间变换,数据类型变换,缩放,裁剪等操作,这些可以用cv模块函数实现;模型输出的结果可能需要做一些额外的变换和计算,这些可以用numpy模块函数实现。

示例

使用cvnumpy中的函数做前后处理,执行模型推理的例子

import MNN
import MNN.cv as cv
import MNN.numpy as np

# 加载模型
net = MNN.nn.load_module_from_file('mobilenet_v1.mnn', ["data"], ["prob"])
# cv模块图片处理
image = cv.imread('cat.jpg')
image = cv.resize(image, (224, 224))
# 类似ndarray的数值运算
image = image - (103.94, 116.78, 123.68)
image = image * (0.017, 0.017, 0.017)
input_var = np.expand_dims(image, 0)
input_var = MNN.expr.convert(input_var, MNN.expr.NC4HW4)
output_var = net.forward(input_var)
output_var = MNN.expr.convert(output_var, MNN.expr.NHWC)
# 类似numpy操作的后处理
print("output belong to class: {}".format(np.argmax(output_var)))

其他示例可以参考示例;也可以参考示例工程

cv能力列表

cv模块提供了与OpenCV相似的接口函数,具备基础的图像处理能力,目前支持的cv函数60个。

图像编解码
函数名 功能
haveImageReader 是否可读(解码)
haveImageWriter 是否可写(编码)
imdecode 从内存解码为Mat
imencode 编码Mat到内存中
imread 读图片
imwrite 写图片
图像滤波
函数名 功能
blur 均值滤波,平滑模糊
boxFilter 盒子滤波,
dilate 膨胀
filter2D 2d卷积
GaussianBlur 高斯模糊
getDerivKernels 求导数,实际为Sobel/Scharr
getGaborKernel 获取Gabor核
getGaussianKernel 获得高斯核
getStructuringElement 获取结构化元素用于形态学操作
Laplacian 边缘检测滤波
pyrDown 高斯平滑+下采样
pyrUp 上采样+高斯平滑
Scharr 边缘检测滤波
sepFilter2D 2个一维kernel做滤波
Sobel 边缘检测滤波
spatialGradient 梯度,实际为Sobel
sqrBoxFilter 平方后滤波
图像形变
函数名 功能
getAffineTransform 仿射变换
getPerspectiveTransform 透视变换
getRectSubPix 截取矩形区域
getRotationMatrix2D 旋转矩阵
invertAffineTransform 仿射变换矩阵求逆
resize 图片放缩
warpAffine 仿射变换
warpPerspective 透视变换
图像转换
函数名 功能
blendLinear 线性混合2个图像
threshold 逐像素阈值化
绘画函数
函数名 功能
arrowedLine 画箭头
circle 画圆
drawContours 画轮廓
fillPoly 填充多边形
line 画线段
rectangle 画正方向
色彩空间转换
函数名 功能
cvtColor 颜色空间转换
cvtColorTwoPlane YUV420到RGB的转换
结构函数
函数名 功能
findContours 轮廓检测
contourArea 计算轮廓的面积
convexHull 计算点集的凸包
minAreaRect 最小外接矩形
boundingRect 计算点集的最小外接矩形
connectedComponentsWithStats 计算图像的连通域
boxPoints 计算矩形的四个顶点坐标
直方图
函数名 功能
calcHist 计算直方图
3D
函数名 功能
Rodrigues 旋转矩阵转换为旋转向量
solvePnP 计算2d到3d的映射
数组操作函数
函数名 功能
copyTo 带mask的拷贝
bitwise_and 带mask按位与
bitwise_or 带mask按位或
bitwise_xor 带mask按位异或
hconcat 水平方向拼接
vconcat 垂直方向拼接
mean 求均值
flip 翻转
rotate 旋转

numpy能力列表

numpy函数170个,函数列表如下:

数组创建
函数名 功能
empty 空数组
empty_like 空数组like
eye 对角线2d数组
identity 对角线2d数组
ones 全1数组
ones_like 全1数组like
zeros 全0数组
zeros_like 全0数组like
full 填充
full_like 填充like
array 创建数组
asarray 创建数组
asanyarray 创建数组
ascontiguousarray 创建数组
asmatrix 创建2d数组
copy 拷贝数组
arange 范围创建
linspace 区间创建
logspace log区间创建
geomspace log区间创建
meshgrid 坐标矩阵
mat 矩阵
数组操作
函数名 功能
copyto 拷贝至
shape 获取形状
reshape 改变形状
ravel 拉平
flat 拉平
flatten 拉平
moveaxis 移动维度
rollaxis 轮转维度
swapaxes 交换维度
T 转置
transpose 转置
atleast_1d 至少1维
atleast_2d 至少2维
atleast_3d 至少3维
broadcast_to 广播
broadcast_arrays 数组广播
expand_dims 增加维度
squeeze 压缩1维度
asfarray 转浮点
asscalar 转标量
concatenate 连接
stack 连接
vstack 垂直连接
hstack 水平连接
dstack 深度连接
column_stack 列连接
row_stack 行连接
split 切分
array_split 数组切分
dsplit 深度切分
hsplit 水平切分
vsplit 垂直切分
tile 重复堆叠
repeat 重复
reshape 变形
坐标操作
函数名 功能
nonzero 非0元素坐标
where 条件选取
unravel_index 反拉平坐标
线性代数
函数名 功能
dot 点乘
vdot 点乘
inner 内积
matmul 矩阵乘
逻辑函数
函数名 功能
all 全部非0
any 任意非0
logical_and
logical_or
logical_not
logical_xor 异或
array_equal 相等
array_equiv 相等
greater 大于
greater_equal 大于等于
less 小于
less_equal 小于等于
equal 等于
not_equal 不等
数学函数
API 功能
sin 正弦
cos 余弦
tan 正切
arcsin 反正弦
arccos 反余弦
arctan 反正切
hypot
arctan2
sinh
cosh
tanh
arcsinh
arccosh
arctanh
around
round_
rint
floor
ceil
trunc
prod
sum
nanprod
nansum
exp e指数
expm1 e指数-1
exp2 2指数
log 对数
log10 10对数
log2 2对数
log1p x+1对数
logaddexp exp对数
logaddexp2 2指数对数
sinc
signbit
copysign
frexp
ldexp
add
reciprocal 倒数
positive 取正
negative 取负
multiply
divide
power 指数
subtract
true_divide
floor_divide
float_power 指数
fmod
mod
modf
remainder
divmod 除,余
convolve 卷积
clip 缩小范围
sqrt 平方根
cbrt 立方根
square 平方
absolute 绝对值
fabs 绝对值
sign 符号
maximum 取大
minimum 取小
fmax 取大
fmin 取小
数组扩充
函数名 功能
pad 扩充
随机采样
函数名 功能
random 随机数
rand 随机数
randn 随机数
randint 随机定点数
排序,搜索,计数
函数名 功能
sortlexsortargsort 排序
argmax 最大值坐标
nanargmax 最大值坐标
argmin 最小值坐标
nanargmin 最小值坐标
argwhere 非0坐标
flatnonzero 非0元素
count_nonzero 非0总数
统计
函数名 功能
amin 最小值
amax 最大值
nanmin 最小值
nanmax 最大值
ptp 范围
average 均值
mean 均值
std 标准差
var 方差
nanmean 均值
nanstd 标准差
nanvar 方差

Expr API使用

概念说明

表达式

表达式是一个延迟计算引擎,它提供如下功能:

  1. 数值计算

  2. 模型搭建

基于数值计算的能力,Expr API 可用于模型推理,但效率相比session/module 较低,不建议采用这种方式做模型推理。

表达式计算原理如下: _images/expr.pngexpr.png

表达式可以设置为Defer(延迟计算)模式或Eager(立即计算)模式:Defer模式下,调用表达式相关API不直接计算,而是搭建模型,在需要获取输出值时才执行;Eager模式下,直接进行计算,对应地无法搭建模型。

C++环境默认为Defer模式,Python环境默认为Eager模式,可通过当前的执行器(Executor)切换计算模式。

数据类型

用户操作的数据类型为 VARP,可按Tensor去读取它的值,按保存时的方式不同,分成三类

  • Input: 由 _Input创建,或者加载模型而得,在保存时仅存储维度信息(shape),可以写入值

  • Const/Trainable: 由_Const_TrainableParam创建,或者加载模型而得,在保存时存储数值,不能写入,只能读取

  • Function: 非输入或者常量,一切由计算而得的变量,不能写入,在保存时存储与之相关的计算图 Function 变量可通过fix调用转换为相应类型,转换时将值计算出来,并去除前置节点依赖。

执行器

表达式在搭建模型或进行计算时,使用与Module API同样一个执行器(Executor) ,可配置表达式的执行模式、计算所用资源等。

表达式接口能力

模型存取与修改

  • 模型读取

    static std::vector<VARP> load(const char* fileName);
    static std::map<std::string, VARP> loadMap(const char* fileName);
    static std::vector<VARP> load(const uint8_t* buffer, size_t length);
    static std::map<std::string, VARP> loadMap(const uint8_t* buffer, size_t length);
    
  • 模型保存

    static void save(const std::vector<VARP>& vars, const char* fileName);
    
  • 节点替换

    static void replace(VARP dst, VARP src);
    

变量操作

  • 创建变量

    static VARP create(EXPRP expr, int index = 0);
    // include/MNN/expr/NeuralNetWorkOp.hpp 中的函数
    VARP _Input(INTS shape = {}, Dimensionformat data_format = NC4HW4, halide_type_t dtype = halide_type_of<float>()) ;
    ...
    VARP _Col2Im(VARP x, VARP outputShape, INTS kernelSize, INTS dilate, INTS pads, INTS stride);
    // include/MNN/expr/MathOp.hpp 中的函数
    VARP _Add(VARP x, VARP y);
    ...
    VARP _Histogram(VARP x, int bin, int min, int max, int channel = -1);
    
  • 获取变量信息

    struct Info {
        Dimensionformat order = NHWC;
        INTS dim;
        halide_type_t type;
        int size;
        void syncSize();
    };
    const Variable::Info* Variable::getInfo();
    
  • 读取变量数据

    template <typename T>
    const T* readMap();
    
  • 向变量写数据

    template <typename T>
    T* writeMap();
    
  • 转换变量类型

    bool fix(InputType type) const;
    

使用表Expr进行模型推理

可以通过模型加载函数将模型转换为表达式计算图,对输入的VARP写入数据后,对输出VARP执行读取操作,即可完成模型推理过程。 代码示例如下:

#include <MNN/expr/ExprCreator.hpp>
using namespace MNN::Express;
// 加载 model.mnn ,保存 prob 的计算部分
void splitDemp() {
    auto varMap = Variable::loadMap("model.mnn");
    std::vector<VARP> vars = std::vector<VARP> {varMap["prob"]};
    Variable::save(vars, "part.mnn");
}

// 保存变量数据
void saveOutput(float* data0, size_t n0, float* data1, size_t n1) {
    VARP input0 = _Const(data0, NHWC, {n0});
    VARP input1 = _Const(data1, NHWC, {n1});
    Variable::save({input0, input1}, "result.mnn");
}

// 加载输入输出分别为 input 和 output 的 model.mnn ,输入数据到 input ,计算 output
void loadAndCompute() {
    auto varMap = Variable::loadMap("model.mnn");
    float* inputPtr = varMap["input"]->writeMap<float>();
    size_t inputSize = varMap["input"]->getInfo()->size;
    for (int i=0; i<inputSize; ++i) {
        inputPtr[i] = (float)i/(float)1000;
    }
    auto outputPtr = varMap["output"]->readMap<float>();
    auto outputSize = varMap["output"]->getInfo()->size;
    for (int i=0; i<outputSize; ++i) {
        printf("%f, ", outputPtr[i]);
    }
}

使用Expr进行数值计算

可以通过NeuralNetWorkOp.hppMathOp.hpp中创建变量的函数组合构造计算图,完成数值计算任务。 代码示例如下:

#include <MNN/expr/ExprCreator.hpp>

using namespace MNN::Express;

void demo() {
    auto varp = _Input({1, 3, 224, 224}, NHWC);
    {
        // Init value init
        auto ptr = varp->writeMap<float>();
        auto size = varp->getInfo()->size;
        for (int i=0; i<size; ++i) {
            ptr[i] = (float)i / 100.0f;
        }
    }
    auto input = varp * _Scalar<float>(1.0f/255.0f);
    output = input * input + input;
    
    // fix input 之后,1.0f / 255.0f 的预处理不会保存到计算图里面
    input.fix(VARP::INPUT);
    // graph.mnn 描述 x * x + x 这个计算图
    Variable::save({output}, "graph.mnn");
    
    // fix output 之后,保存输出的数值而非计算图
    output.fix(VARP::CONSTANT);
    Variable::save({varp}, "output.mnn");
}

使用cv功能进行图像处理

MNN/tools/cv中提供了OpenCV-like的函数集合,这些函数操作的基本数据类型为VARP,使用方式与VARP数值计算相似,因此可以结合cv中的函数实现图像处理功能。 代码示例如下:

#include <MNN/expr/ExprCreator.hpp>
#include "tools/cv/include/cv/cv.hpp"

using namespace MNN;

void demo() {
    auto img = CV::imread("cat.jpg");
    auto rgb = CV::cvtColor(img, COLOR_BGR2RGB);
    auto input = CV::resize(rgb, {224, 224});
    input = Express::_Cast(input, halide_type_of<float>);
    input = input * _Scalar<float>(1.0f/255.0f);
    for (int i = 0; i < 10; i++) {
        printf("%f, ", input->readMap<float>()[i]);
    }
}

计算模式

表达式可以设置为Defer(延迟计算)模式或Eager(立即计算)模式:Defer模式下,调用表达式相关API不直接计算,而是搭建模型,在需要获取输出值时才执行;Eager模式下,直接进行计算,无法搭建模型。

C++环境默认为Defer模式,Python环境默认为Eager模式,可通过当前的执行器(Executor)切换计算模式。

参考如下代码切换Eager(立即计算)模式和Defer(延迟计算)模式:

C++ 代码:

void demo() {
    // Set Defer mode
    ExecutorScope::Current()->lazyEval = true;
    {
        // Defer Compute Begin
        VARP x = _Input();
        x->writeMap<float>[0] = 1.0f;
        VARP y = x + x;
        y = y * x;
        // Compute Only readMap
        const float* yPtr = y->readMap<float>();
        // Will save graph
        Variable::save([y], "graph.mnn");
        // Defer Compute End
    }

    // Set Eager mode
    ExecutorScope::Current()->lazyEval = false;
    {
        // Eager Compute Begin
        VARP x = _Input();
        x->writeMap<float>[0] = 1.0f;
        // Compute Directly
        VARP y = x + x;
        y = y * x;
        // Just Read value
        const float* yPtr = y->readMap<float>();
        // Will save constant value, can't save graph
        Variable::save([y], "graph.mnn");
        // Eager Compute End
    }
}

Python 代码:

import MNN
F = MNN.expr

# Set Defer mode
F.lazy_eval(True)

# Set Eager mode
F.lazy_eval(False)

示例代码

完整的示例代码可以参考demo/exec/文件夹中的以下源码文件:

  • expressDemo.cpp 使用Expr执行模型推理

  • expressMakeModel.cpp 使用Expr构建模型

  • segment.cpp 使用Session进行图像分割,使用Expr进行后处理

  • pictureRecognition_module.cpp 使用Module执行图像分类,使用Expr进行后处理

  • pictureRecognition_batch.cpp 使用Module执行图像分类,使用Expr进行后处理

使用表达式接口训练

从源码编译

获取可训练模型

首先我们需要获得一个可训练的模型结构,而在MNN中可以用两种方式达到这一目的。

一、将其他框架,如TensorFlow,Pytorch训练得到的模型转成MNN可训练模型,这一过程可使用 MNNConverter 工具实现。典型的应用场景为 1. 使用MNN Finetune,2. 使用MNN进行训练量化。在模型转换过程中建议使用 MNNConverter 的 –forTraining 选项,保留BatchNorm,Dropout等训练过程中会用到的算子。

二、使用MNN从零开始搭建一个模型,并使用MNN进行训练,这可以省去模型转换的步骤,并且也可以十分容易地转换为训练量化模型。在 MNN_ROOT/tools/train/source/models/ 目录中我们提供了Lenet,MobilenetV1,MobilenetV2等使用MNN训练框架搭建的模型。

将其他框架模型转换为MNN可训练模型

以MobilenetV2的训练量化为例。首先我们需要到下载TensorFlow官方提供的MobilenetV2模型,然后编译 MNNConverter,并执行以下命令进行转换:

./MNNConvert --modelFile mobilenet_v2_1.0_224_frozen.pb  --MNNModel mobilenet_v2_tfpb_train.mnn --framework TF --bizCode AliNNTest --forTraining

注意,上述命令中使用到的 mobilenet_v2_1.0_224_frozen.pb 模型中含有 BatchNorm 算子,没有进行融合,通过在转换时使用 –forTraining 选项,我们保留了BatchNorm算子到转换出来的 mobilenet_v2_tfpb_train.mnn 模型之中。

如果你的模型中没有BN,Dropout等在转MNN推理模型时会被融合掉的算子,那么直接使用MNN推理模型也可以进行训练,不必重新进行转换。

接下来我们仿照 MNN_ROOT/tools/train/source/demo/mobilenetV2Train.cpp 中的示例,读取转换得到的模型,将其转换为MNN可训练模型。关键代码示例如下

// mobilenetV2Train.cpp

// 读取转换得到的MNN模型
auto varMap = Variable::loadMap(argv[1]);
// 指定量化bit数
int bits = 8;
// 获取模型的输入和输出
auto inputOutputs = Variable::getInputAndOutput(varMap);
auto inputs       = Variable::mapToSequence(inputOutputs.first);
auto outputs      = Variable::mapToSequence(inputOutputs.second);

// 将转换得到的模型转换为可训练模型(将推理模型中的卷积,BatchNorm,Dropout抽取出来,转换成可训练模块)
std::shared_ptr<Module> model(PipelineModule::extract(inputs, outputs, true));
// 将可训练模型转换为训练量化模型,如果不需要进行训练量化,则可不做这一步
((PipelineModule*)model.get())->toTrainQuant(bits);
// 进入训练环节
MobilenetV2Utils::train(model, 1001, 1, trainImagesFolder, trainImagesTxt, testImagesFolder, testImagesTxt);

使用MNN从零开始搭建模型

以Lenet为例,我们来看一下,如何使用MNN从零搭建一个模型。MNN提供了丰富的算子可供使用,下面的例子就不详细展开。值得注意的是Pooling输出为NC4HW4格式,需要转换到 NCHW 格式才能进入全连接层进行计算。

class MNN_PUBLIC Lenet : public Module {
public:
    Lenet();

    virtual std::vector<Express::VARP> onForward(const std::vector<Express::VARP>& inputs) override;

    std::shared_ptr<Module> conv1;
    std::shared_ptr<Module> conv2;
    std::shared_ptr<Module> ip1;
    std::shared_ptr<Module> ip2;
    std::shared_ptr<Module> dropout;
};
// 初始化
Lenet::Lenet() {
    NN::ConvOption convOption;
    convOption.kernelSize = {5, 5};
    convOption.channel    = {1, 20};
    conv1.reset(NN::Conv(convOption));
    convOption.reset();
    convOption.kernelSize = {5, 5};
    convOption.channel    = {20, 50};
    conv2.reset(NN::Conv(convOption));
    ip1.reset(NN::Linear(800, 500));
    ip2.reset(NN::Linear(500, 10));
    dropout.reset(NN::Dropout(0.5));
    // 必须要进行register的参数才会进行更新
    registerModel({conv1, conv2, ip1, ip2, dropout});
}
// 前向计算
std::vector<Express::VARP> Lenet::onForward(const std::vector<Express::VARP>& inputs) {
    using namespace Express;
    VARP x = inputs[0];
    x      = conv1->forward(x);
    x      = _MaxPool(x, {2, 2}, {2, 2});
    x      = conv2->forward(x);
    x      = _MaxPool(x, {2, 2}, {2, 2});
    // Pooling输出为NC4HW4格式,需要转换到NCHW才能进入全连接层进行计算
    x      = _Convert(x, NCHW);
    x      = _Reshape(x, {0, -1});
    x      = ip1->forward(x);
    x      = _Relu(x);
    x      = dropout->forward(x);
    x      = ip2->forward(x);
    x      = _Softmax(x, 1);
    return {x};
}

实现数据集接口

这部分在MNN文档中加载训练数据 部分有详细描述。

训练并保存模型

以MNIST模型训练为例,代码在 MNN_ROOT/tools/train/source/demo/MnistUtils.cpp

//  MnistUtils.cpp

......

void MnistUtils::train(std::shared_ptr<Module> model, std::string root) {
    {
        // Load snapshot
        // 模型结构 + 模型参数
        auto para = Variable::load("mnist.snapshot.mnn");
        model->loadParameters(para);
    }
    // 配置训练框架参数
    auto exe = Executor::getGlobalExecutor();
    BackendConfig config;
    // 使用CPU,4线程
    exe->setGlobalExecutorConfig(MNN_FORWARD_CPU, config, 4);
    // SGD求解器
    std::shared_ptr<SGD> sgd(new SGD(model));
    // SGD求解器参数设置
    sgd->setMomentum(0.9f);
    sgd->setWeightDecay(0.0005f);

    // 创建数据集和DataLoader
    auto dataset = MnistDataset::create(root, MnistDataset::Mode::TRAIN);
    // the stack transform, stack [1, 28, 28] to [n, 1, 28, 28]
    const size_t batchSize  = 64;
    const size_t numWorkers = 0;
    bool shuffle            = true;

    auto dataLoader = std::shared_ptr<DataLoader>(dataset.createLoader(batchSize, true, shuffle, numWorkers));

    size_t iterations = dataLoader->iterNumber();

    auto testDataset            = MnistDataset::create(root, MnistDataset::Mode::TEST);
    const size_t testBatchSize  = 20;
    const size_t testNumWorkers = 0;
    shuffle                     = false;

    auto testDataLoader = std::shared_ptr<DataLoader>(testDataset.createLoader(testBatchSize, true, shuffle, testNumWorkers));

    size_t testIterations = testDataLoader->iterNumber();

    // 开始训练
    for (int epoch = 0; epoch < 50; ++epoch) {
        model->clearCache();
        exe->gc(Executor::FULL);
        exe->resetProfile();
        {
            AUTOTIME;
            dataLoader->reset();
            // 训练阶段需设置isTraining Flag为true
            model->setIsTraining(true);
            Timer _100Time;
            int lastIndex = 0;
            int moveBatchSize = 0;
            for (int i = 0; i < iterations; i++) {
                // AUTOTIME;
                // 获得一个batch的数据,包括数据及其label
                auto trainData  = dataLoader->next();
                auto example    = trainData[0];
                auto cast       = _Cast<float>(example.first[0]);
                example.first[0] = cast * _Const(1.0f / 255.0f);
                moveBatchSize += example.first[0]->getInfo()->dim[0];

                // Compute One-Hot
                auto newTarget = _OneHot(_Cast<int32_t>(example.second[0]), _Scalar<int>(10), _Scalar<float>(1.0f),
                                         _Scalar<float>(0.0f));
				// 前向计算
                auto predict = model->forward(example.first[0]);
                // 计算loss
                auto loss    = _CrossEntropy(predict, newTarget);
                // 调整学习率
                float rate   = LrScheduler::inv(0.01, epoch * iterations + i, 0.0001, 0.75);
                sgd->setLearningRate(rate);
                
                if (moveBatchSize % (10 * batchSize) == 0 || i == iterations - 1) {
                    std::cout << "epoch: " << (epoch);
                    std::cout << "  " << moveBatchSize << " / " << dataLoader->size();
                    std::cout << " loss: " << loss->readMap<float>()[0];
                    std::cout << " lr: " << rate;
                    std::cout << " time: " << (float)_100Time.durationInUs() / 1000.0f << " ms / " << (i - lastIndex) <<  " iter"  << std::endl;
                    std::cout.flush();
                    _100Time.reset();
                    lastIndex = i;
                }
                // 根据loss反向计算,并更新网络参数
                sgd->step(loss);
            }
        }
        // 保存模型参数,便于重新载入训练
        Variable::save(model->parameters(), "mnist.snapshot.mnn");
        {
            model->setIsTraining(false);
            auto forwardInput = _Input({1, 1, 28, 28}, NC4HW4);
            forwardInput->setName("data");
            auto predict = model->forward(forwardInput);
            predict->setName("prob");
            // 优化网络结构【可选】
            Transformer::turnModelToInfer()->onExecute({predict});
            // 保存模型和结构,可脱离Module定义使用
            Variable::save({predict}, "temp.mnist.mnn");
        }
		
        // 测试模型
        int correct = 0;
        testDataLoader->reset();
        // 测试时,需设置标志位
        model->setIsTraining(false);
        int moveBatchSize = 0;
        for (int i = 0; i < testIterations; i++) {
            auto data       = testDataLoader->next();
            auto example    = data[0];
            moveBatchSize += example.first[0]->getInfo()->dim[0];
            if ((i + 1) % 100 == 0) {
                std::cout << "test: " << moveBatchSize << " / " << testDataLoader->size() << std::endl;
            }
            auto cast       = _Cast<float>(example.first[0]);
            example.first[0] = cast * _Const(1.0f / 255.0f);
            auto predict    = model->forward(example.first[0]);
            predict         = _ArgMax(predict, 1);
            auto accu       = _Cast<int32_t>(_Equal(predict, _Cast<int32_t>(example.second[0]))).sum({});
            correct += accu->readMap<int32_t>()[0];
        }
        // 计算准确率
        auto accu = (float)correct / (float)testDataLoader->size();
        std::cout << "epoch: " << epoch << "  accuracy: " << accu << std::endl;
        exe->dumpProfile();
    }
}

保存和恢复模型

一、只保存模型参数,不保存模型结构,需要对应的模型结构去加载这些参数 保存:

Variable::save(model->parameters(), "mnist.snapshot.mnn");

恢复:

// 模型结构 + 模型参数
auto para = Variable::load("mnist.snapshot.mnn");
model->loadParameters(para);

二、同时保存模型结构和参数,便于推理 保存:

model->setIsTraining(false);
auto forwardInput = _Input({1, 1, 28, 28}, NC4HW4);
forwardInput->setName("data");
auto predict = model->forward(forwardInput);
predict->setName("prob");

// 保存输出节点,会连同结构参数一并存储下来
Variable::save({predict}, "temp.mnist.mnn");

恢复(进行推理):

auto varMap = Variable::loadMap("temp.mnist.mnn");

//输入节点名与保存时设定的名字一致,为 data,维度大小与保存时设定的大小一致,为 [1, 1, 28, 28]
float* inputPtr = varMap["data"]->writeMap<float>();
//填充 inputPtr

//输出节点名与保存时设定的名字一致,为 prob
float* outputPtr = varMap["prob"]->readMap<float>();
// 使用 outputPtr 的数据

加载训练数据

该模块用于读取保存在硬盘上的数据,将其包装并输出为MNN训练可用的数据类型。该模块源码位于MNN_root/tools/train/source/data/目录下。若要使用,请包含DataLoader.hpp头文件即可,该模块中其他组件会全部导入,用于构建DataLoader。

相关demo

1、MNN_root/tools/train/source/demo/dataLoaderDemo.cpp     使用MNIST数据集构建DataLoader,并进行输出显示。 2、MNN_root/tools/train/source/demo/dataLoaderTest.cpp     使用MNIST数据集构建DataLoader,并测试DataLoader中一些组件。 3、MNN_root/tools/train/source/demo/ImageDatasetDemo.cpp     读取硬盘上保存的图片数据,并显示出来。显示需要用到OpenCV,并在编译时打开MNN_USE_OPENCV宏。

自定义Dataset

可参考MNN_root/tools/train/source/datasets/中预置数据集的写法,继承Dataset类,实现两个抽象函数即可,例如:

//  MnistDataset.cpp

// 返回MNIST数据集中一张图片数据,及其对应的label
Example MnistDataset::get(size_t index) {
    auto data  = _Input({1, kImageRows, kImageColumns}, NCHW, halide_type_of<uint8_t>());
    auto label = _Input({}, NCHW, halide_type_of<uint8_t>());

    auto dataPtr = mImagePtr + index * kImageRows * kImageColumns;
    ::memcpy(data->writeMap<uint8_t>(), dataPtr, kImageRows * kImageColumns);

    auto labelPtr = mLabelsPtr + index;
    ::memcpy(label->writeMap<uint8_t>(), labelPtr, 1);

    auto returnIndex = _Const(index);
    // return the index for test
    return {{data, returnIndex}, {label}};
}
// 返回数据集大小,对于MNIST训练集是60000,测试集是10000
size_t MnistDataset::size() {
    return mImages->getInfo()->dim[0];
}

DataLoader使用示例

使用流程:自定义Dataset,构造DataLoader,读取数据,DataLoader->reset();

//
//  ImageDatasetDemo.cpp
//  MNN
//
//  Created by MNN on 2019/11/20.
//  Copyright © 2018, Alibaba Group Holding Limited
//

#include <iostream>
#include "DataLoader.hpp"
#include "DemoUnit.hpp"
#include "ImageDataset.hpp"
#include "RandomSampler.hpp"
#include "Sampler.hpp"
#include "Transform.hpp"
#include "TransformDataset.hpp"

#ifdef MNN_USE_OPENCV
#include <opencv2/opencv.hpp> // use opencv to show pictures
using namespace cv;
#endif

using namespace std;

/*
 * this is an demo for how to use the ImageDataset and DataLoader
 */

class ImageDatasetDemo : public DemoUnit {
public:
    // this function is an example to use the lambda transform
    // here we use lambda transform to normalize data from 0~255 to 0~1
    static Example func(Example example) {
        // // an easier way to do this
        auto cast       = _Cast(example.first[0], halide_type_of<float>());
        example.first[0] = _Multiply(cast, _Const(1.0f / 255.0f));
        return example;
    }

    virtual int run(int argc, const char* argv[]) override {
        if (argc != 3) {
            cout << "usage: ./runTrainDemo.out ImageDatasetDemo path/to/images/ path/to/image/txt\n" << endl;

            // ImageDataset的数据格式,采用的是ImageNet数据集的格式,你也可以写一个自己的数据集,自定义格式
            cout << "the ImageDataset read stored images as input data.\n"
                    "use 'pathToImages' and a txt file to construct a ImageDataset.\n"
                    "the txt file should use format as below:\n"
                    "     image1.jpg label1,label2,...\n"
                    "     image2.jpg label3,label4,...\n"
                    "     ...\n"
                    "the ImageDataset would read images from:\n"
                    "     pathToImages/image1.jpg\n"
                    "     pathToImages/image2.jpg\n"
                    "     ...\n"
                 << endl;

            return 0;
        }

        std::string pathToImages   = argv[1];
        std::string pathToImageTxt = argv[2];

        // ImageDataset可配置数据预处理
        auto converImagesToFormat  = ImageDataset::DestImageFormat::RGB;
        int resizeHeight           = 224;
        int resizeWidth            = 224;
        std::vector<float> scales = {1/255.0, 1/255.0, 1/255.0};
        auto config                = ImageDataset::ImageConfig(converImagesToFormat, resizeHeight, resizeWidth, scales);
        bool readAllImagesToMemory = false;

        // 构建ImageDataset
        auto dataset = std::make_shared<ImageDataset>(pathToImages, pathToImageTxt, config, readAllImagesToMemory);

        const int batchSize  = 1;
        const int numWorkers = 1;

        // 构建DataLoader,这里会将一个batch数据stack为一个VARP(Tensor)
        auto dataLoader = std::shared_ptr<DataLoader>(DataLoader::makeDataLoader(dataset, batchSize, true, false, numWorkers));

        const size_t iterations = dataset->size() / batchSize;

        for (int i = 0; i < iterations; i++) {
            // 读取数据
            auto trainData = dataLoader->next();

            auto data  = trainData[0].first[0]->readMap<float_t>();
            auto label = trainData[0].second[0]->readMap<int32_t>();

            cout << "index: " << i << " label: " << int(label[0]) << endl;

#ifdef MNN_USE_OPENCV
            // only show the first picture in the batch
            Mat image = Mat(resizeHeight, resizeWidth, CV_32FC(3), (void*)data);
            imshow("image", image);

            waitKey(-1);
#endif
        }
        
        // 每完整过一次数据集必须重置DataLoader
        // this will reset the sampler's internal state
        dataLoader->reset();
        return 0;
    }
};

DemoUnitSetRegister(ImageDatasetDemo, "ImageDatasetDemo");

相关类和概念

VARP

MNN动态图中的变量,类似于pytorch中的Tensor

Example

DataLoader输出数据的最小单位

/**
 First: data: a vector of input tensors (for single input dataset is only one)
 Second: target: a vector of output tensors (for single output dataset is only one)
 */
typedef std::pair<std::vector<VARP>, std::vector<VARP>> Example;

可以看到一个Example是一个数据对,其first部分是输入,second部分是target。由于网络有可能有多个输入和多个target,所以first和second都是vector结构。

RandomSampler : public Sampler

随机采样序列生成器,例如图片数据集中有1000张图片,则生成采样序列0~999,根据配置指定是否进行shuffle

public:
	// size: 采样序列长度
	// shuffle: 是否生成随机采样序列
    explicit RandomSampler(size_t size, bool shuffle = true);
	// 重置采样器内部状态
    void reset(size_t size) override;
	// 采样器长度
    size_t size() override;
	// 返回内部生成的采样序列
    const std::vector<size_t> indices();
	// 返回已经使用的采样序列数量
    size_t index();
	// 获取下一个长度为batchSize的采样序列
    std::vector<size_t> next(size_t batchSize) override;

private:
    std::vector<size_t> mIndices;
    size_t mIndex = 0;
    bool mShuffle;

Dataset

数据集抽象基类,用户自定义数据集需继承此基类,并实现抽象函数,可参考MNN_root/tools/train/source/datasets/中预置数据集的写法

// 返回数据集的大小,例如1000张图片的数据集,其大小为1000
virtual size_t size() = 0;
// 返回数据集中指定index的数据,如给定123,返回第123张图片数据
virtual Example get(size_t index) = 0;
// 返回数据集中指定index的一批数据,为一个batch
std::vector<Example> getBatch(std::vector<size_t> indices);

Transform

抽象基类,对一个batch中的每一个数据进行某个变换,可以是一些预处理等

BatchTransform

抽象基类,对一个batch的数据进行某个变换,可以是一些预处理等

StackTransform : public BatchTransform

将一个Dataset输出的vector表示的一个batch数据合成一个VARP,即 Stack( (c, h, w), (c, h, w), (c, h, w)… ) –> (n, c, h, w)

LambdaTransform : public Transform

对Dataset输出的每一个Example进行单独处理,例如中心化,归一化等

TransformDataset : public Dataset

对Dataset进行某种Transform,仍是一个Dataset,用于输出数据

DataLoaderConfig

对DataLoader进行配置,可配置项为:

batchSize: 指定batch大小 numWorkers: 多线程预读取的线程数

DataLoader

根据采样器生成的采样序列,到对应的Dataset中取得对应的数据并输出

// 构造函数
DataLoader(std::shared_ptr<BatchDataset> dataset, std::shared_ptr<Sampler> sampler,
               std::shared_ptr<DataLoaderConfig> config);
// 构造DataLoader,无Transform
static DataLoader* makeDataLoader(std::shared_ptr<BatchDataset> dataset,
                                  const int batchSize,
                                  const bool stack = true, // 是否将一个batch数据叠加为一个VARP(Tensor)
                                  const bool shuffle = true,
                                  const int numWorkers = 0);
// 构造DataLoader,有Transform,Transform可多个叠加
static DataLoader* makeDataLoader(std::shared_ptr<BatchDataset> dataset,
                                  std::vector<std::shared_ptr<BatchTransform>> transforms,
                                  const int batchSize,
                                  const bool shuffle = true,
                                  const int numWorkers = 0);
// 指定batch size后,迭代多少次用完全部数据,最后一个batch不足batchsize也会输出
size_t iterNumber() const;
// 数据集大小
size_t size() const;
// 输出一个batch的数据
std::vector<Example> next();
// 清空内部数据队列,重置内部采样器
void clean();
// clean(),并重新预读取,Dataset每次数据全部输出完毕,必须reset
void reset();

优化器使用

SGD with momentum

使用示例

// 新建SGD优化器
std::shared_ptr<SGD> solver(new SGD);
// 设置模型中需要优化的参数
solver->append(model->parameters());
// 设置momentum和weight decay
solver->setMomentum(0.9f);
solver->setWeightDecay(0.0005f);
// 设置正则化方法,默认L2
solver->setRegularizationMethod(RegularizationMethod::L2);
// 设置学习率
solver->setLearningRate(0.001);

// 根据loss计算梯度,并更新参数
solver->step(loss);

ADAM

使用示例

// 新建ADAM优化器
std::shared_ptr<SGD> solver(new ADAM);
// 设置模型中需要优化的参数
solver->append(model->parameters());
// 设置ADAM的两个momentum,设置weight decay
solver->setMomentum(0.9f);
solver->setMomentum2(0.99f);
solver->setWeightDecay(0.0005f);
// 设置正则化方法,默认L2
solver->setRegularizationMethod(RegularizationMethod::L2);
// 设置学习率
solver->setLearningRate(0.001);

// 根据loss计算梯度,并更新参数
solver->step(loss);

Loss

目前支持的Loss,也可自行设计

VARP _CrossEntropy(Express::VARP predicts, Express::VARP oneHotTargets);

VARP _KLDivergence(Express::VARP predicts, Express::VARP oneHotTargets);

VARP _MSE(Express::VARP predicts, Express::VARP oneHotTargets);

VARP _MAE(Express::VARP predicts, Express::VARP oneHotTargets);

VARP _Hinge(Express::VARP predicts, Express::VARP oneHotTargets);

VARP _DistillLoss(Express::VARP studentLogits, Express::VARP teacherLogits, Express::VARP oneHotTargets,
                                                                const float temperature, const float alpha);

训练量化

什么是训练量化

与离线量化不同,训练量化需要在训练中模拟量化操作的影响,并通过训练使得模型学习并适应量化操作所带来的误差,从而提高量化的精度。因此训练量化也称为Quantization-aware Training(QAT),意指训练中已经意识到此模型将会转换成量化模型。

如何在MNN中使用训练量化

已经通过其他训练框架如TensorFlow、Pytorch等训练得到一个float模型,此时可以通过先将此float模型通过MNNConverter转换为MNN统一的模型格式,然后使用MNN提供的离线量化工具直接量化得到一个全int8推理模型。如果此模型的精度不满足要求,则可以通过训练量化来提高量化模型的精度。

使用步骤:

  1. 首先通过其他训练框架训练得到原始float模型;

  2. 编译MNNConverter模型转换工具;

  3. 使用MNNConverter将float模型转成MNN统一格式模型,因为要进行再训练,建议保留BN,Dropout等训练过程中会使用到的算子,这可以通过MNNConverter的 –forTraining 选项实现;

  4. 参考MNN_ROOT/tools/train/source/demo/mobilenetV2Train.cpp 中的 MobilenetV2TrainQuant demo来实现训练量化的功能,下面以MobilenetV2的训练量化为例,来看一下如何读取并将模型转换成训练量化模型

  5. 观察准确率变化,代码保存下来的模型即为量化推理模型

//  mobilenetV2Train.cpp
// 读取转换得到的MNN float模型
auto varMap = Variable::loadMap(argv[1]);
if (varMap.empty()) {
    MNN_ERROR("Can not load model %s\n", argv[1]);
    return 0;
}
// 指定量化比特数
int bits = 8;
if (argc > 6) {
    std::istringstream is(argv[6]);
    is >> bits;
}
if (1 > bits || bits > 8) {
    MNN_ERROR("bits must be 2-8, use 8 default\n");
    bits = 8;
}
// 获得模型的输入和输出
auto inputOutputs = Variable::getInputAndOutput(varMap);
auto inputs       = Variable::mapToSequence(inputOutputs.first);
auto outputs      = Variable::mapToSequence(inputOutputs.second);

// 扫描整个模型,并将inference模型转换成可训练模型,此时得到的模型是可训练的float模型
std::shared_ptr<Module> model(PipelineModule::extract(inputs, outputs, true));
// 将上面得到的模型转换成训练量化模型,此处指定量化bit数
PipelineModule::turnQuantize(model.get(), bits);
// 进行训练,观察训练结果,保存得到的模型即是量化模型
MobilenetV2Utils::train(model, 1001, 1, trainImagesFolder, trainImagesTxt, testImagesFolder, testImagesTxt);

MNN训练量化原理

MNN训练量化的基本原理如下图所示 https://cdn.nlark.com/yuque/0/2020/png/405909/1582775538889-77cfe824-3f07-4456-a99e-b529ce888243.png#height=523&id=t2nNB&name=image.png&originHeight=1456&originWidth=1078&originalType=binary&size=590394&status=done&style=none&width=387image.png 以int8量化为例,首先要理解全int8推理的整个过程,全int8推理,即feature要量化为int8,weight和bias也要量化为int8,输出结果可以是float或者是int8,视该卷积模块的后面一个op的情况而定。而训练量化的本质就是在训练的过程中去模拟量化操作的影响,借由训练来使得模型学习并适应这种影响,以此来提高最后量化模型的准确率。 因此在两种 FakeQuant 模块中,我们的主要计算为 https://cdn.nlark.com/yuque/0/2020/png/405909/1582775538909-a701341d-ced6-48ad-9df3-d90b7d1cca36.png#height=538&id=thJFB&name=image.png&originHeight=1076&originWidth=632&originalType=binary&size=203698&status=done&style=none&width=316image.png 对于权值和特征的fake-quant基本都和上图一致,不一样的是对于特征由于其范围是随输入动态变化的,而最终int8模型中必须固定一个对于输入特征的scale值,所以,我们对每一此前向计算出来的scale进行了累积更新,例如使用滑动平均,或者直接取每一次的最大值。对于权值的scale,则没有进行平均,因为每一次更新之后的权值都是学习之后的较好的结果,没有状态保留。 此外,对于特征,我们提供了分通道(PerChannel)或者不分通道(PerTensor)的scale统计方法,可根据效果选择使用。对于权值,我们则使用分通道的量化方法,效果较好。

上述是在训练中的training阶段的计算过程,在test阶段,我们会将BatchNorm合进权值,使用训练过程得到的特征scale和此时权值的scale(每次重新计算得到)对特征和权值进行量化,并真实调用MNN中的 _FloatToInt8 和 _Int8ToFloat 来进行推理,以保证测试得到的结果和最后转换得到的全int8推理模型的结果一致。

最后保存模型的时候会自动保存test阶段的模型,并去掉一些冗余的算子,所以直接保存出来即是全int8推理模型。

训练量化结果

目前我们在Lenet,MobilenetV2,以及内部的一些人脸模型上进行了测试,均取得了不错的效果,下面给出MobilenetV2的一些详细数据

准确率 / 模型大小
原始float模型 72.324% / 13M
MNN训练量化int8模型 72.456% / 3.5M
TF训练量化int8模型 71.1% / 3.5M (原始 71.8% / 13M)

上述数据是使用batchsize为32,训练100次迭代得到的,即仅使用到了3200张图片进行训练量化,在ImageNet验证集5万张图片上进行测试得到。可以看到int8量化模型的准确率甚至比float还要高一点,而模型大小下降了73%,同时还可以得到推理速度上的增益。

【注】此处使用到的float模型为TensorFlow官方提供的模型,但官方给出的准确率数据是71.8%,我们测出来比他们要高一点,原因是因为我们使用的预处理代码上有细微差别所致。

使用训练量化的一些建议

  1. 模型转换时保留BatchNorm和Dropout等训练中会用到的算子,这些算子对训练量化也有帮助

  2. 要使用原始模型接近收敛阶段的训练参数,训练参数不对,将导致训练量化不稳定

  3. 学习率要调到比较小

  4. 我们仅对卷积层实现了训练量化,因此如果用MNN从零开始搭建模型,后期接训练量化,或者Finetune之后想继续训练量化,那么需要用卷积层来实现全连接层即可对全连接层也进行训练量化。示例代码如下

// 用卷积层实现输入1280,输出为4的全连接层
NN::ConvOption option;
option.channel = {1280, 4};
mLastConv      = std::shared_ptr<Module>(NN::Conv(option));

训练量化的配置选项

详见 MNN_ROOT/tools/train/source/module/PipelineModule.hpp

// 特征scale的计算方法
enum FeatureScaleStatMethod {
    PerTensor = 0, // 对特征不分通道进行量化
    PerChannel = 1 // 对特征分通道进行量化,deprecated
};
// 特征scale的更新方法
enum ScaleUpdateMethod {
    Maximum = 0, // 使用每一次计算得到的scale的最大值
    MovingAverage = 1 // 使用滑动平均来更新
};
// 指定训练量化的bit数,特征scale的计算方法,特征scale的更新方法,
void toTrainQuant(const int bits = 8, NN::FeatureScaleStatMethod featureScaleStatMethod = NN::PerTensor,
                      NN::ScaleUpdateMethod scaleUpdateMethod = NN::MovingAverage);

使用预训练模型Finetune

Finetune原理

深度模型可以看做一种特征提取器,例如卷积神经网络(CNN)可以看做一种视觉特征提取器。但是这种特征提取器需要进行广泛的训练才能避免过拟合数据集,取得较好的泛化性能。如果我们直接搭建一个模型然后在我们自己的小数据集上进行训练的话,很容易过拟合。这时候我们就可以用在一些相似任务的大型数据集上训练得到的模型,在我们自己的小数据集上进行Finetune,即可省去大量训练时间,且获得较好的性能表现。

使用场景

例如对于图像分类任务,我们可以使用在ImageNet数据集上训练过的模型如MobilenetV2等,取出其特征提取部分,而替换其最后的分类层(ImageNet有1000类,我们自己的数据集可能只有10类),然后仅对替换之后的分类层进行训练即可。这是因为MobilenetV2的特征提取部分已经得到了较充分的训练,这些特征提取器提取出来的特征对于其他图像是通用的。还有很多其他的任务,例如NLP中可以用在大型语料库上训练过的BERT模型在自己的语料库上进行Finetune。

MNN Finetune示例

下面以MobilenetV2在自己的4分类小数据集上Finetune为例,演示MNN中Finetune的用法。相关代码在 MNN_ROOT/tools/train/source/demo/mobilenetV2Train.cpp 和 MNN_ROOT/tools/train/source/demo/mobilenetV2Utils.cpp中,可以适当选择大一点的学习率如0.001,加快学习速度 注意,此demo中需要MobilenetV2的MNN模型

//  mobilenetV2Train.cpp

class MobilenetV2TransferModule : public Module {
public:
    MobilenetV2TransferModule(const char* fileName) {
        // 读取原始MobilenetV2模型
        auto varMap  = Variable::loadMap(fileName);
        // MobilenetV2的输入节点
        auto input   = Variable::getInputAndOutput(varMap).first.begin()->second;
        // MobilenetV2分类层之前的节点,AveragePooling的输出
        auto lastVar = varMap["MobilenetV2/Logits/AvgPool"];

        // 初始化一个4分类的全连接层,MNN中可以用卷积来表示全连接层
        NN::ConvOption option;
        option.channel = {1280, 4};
        mLastConv      = std::shared_ptr<Module>(NN::Conv(option));

        // 初始化内部特征提取器, 内部提取器设成不需要训练
        mFix.reset(PipelineModule::extract({input}, {lastVar}, false));

        // 注意这里只注册了我们新初始化的4分类全连接层,那么训练时将只更新此4分类全连接层
        registerModel({mLastConv});
    }
    virtual std::vector<VARP> onForward(const std::vector<VARP>& inputs) override {
        // 输入一张图片,获得MobilenetV2特征提取器的输出
        auto pool   = mFix->forward(inputs[0]);
        
        // 将上面提取的特征输入到新初始化的4分类层进行分类
        auto result = _Softmax(_Reshape(_Convert(mLastConv->forward(pool), NCHW), {0, -1}));
        
        return {result};
    }
    // MobilenetV2特征提取器,从输入一直到最后一个AveragePooling
    std::shared_ptr<Module> mFix;
    // 重新初始化的4分类全连接层
    std::shared_ptr<Module> mLastConv;
};

class MobilenetV2Transfer : public DemoUnit {
public:
    virtual int run(int argc, const char* argv[]) override {
        if (argc < 6) {
            std::cout << "usage: ./runTrainDemo.out MobilentV2Transfer /path/to/mobilenetV2Model path/to/train/images/ path/to/train/image/txt path/to/test/images/ path/to/test/image/txt"
                      << std::endl;
            return 0;
        }

        std::string trainImagesFolder = argv[2];
        std::string trainImagesTxt = argv[3];
        std::string testImagesFolder = argv[4];
        std::string testImagesTxt = argv[5];
        
		// 读取模型,并替换最后一层分类层
        std::shared_ptr<Module> model(new MobilenetV2TransferModule(argv[1]));
		// 进入训练环节
        MobilenetV2Utils::train(model, 4, 0, trainImagesFolder, trainImagesTxt, testImagesFolder, testImagesTxt);

        return 0;
    }
};

蒸馏训练

蒸馏训练原理

蒸馏(Distillation)总体思想是将一个模型所学到的知识蒸馏转移到另外一个模型上,就像教师教学生一样,因此前一个模型常被称为教师模型,后面一个模型常被称为学生模型。如果学生模型比教师模型小,那么蒸馏也成为一种模型压缩方法。Hinton在2015年提出了蒸馏这一思想,具体做法可以参考论文: Hinton, Geoffrey, Oriol Vinyals, and Jeff Dean. “Distilling the knowledge in a neural network.” arXiv preprint arXiv:1503.02531 (2015).

MNN蒸馏训练示例

以MobilenetV2的蒸馏训练量化为例,我们来看一下MNN中怎样做蒸馏训练,相关代码在 MNN_ROOT/tools/train/source/demo/distillTrainQuant.cpp中。 根据蒸馏算法,我们需要取出输入到模型Softmax节点的logits,然后加上温度参数,最后计算蒸馏loss进行训练 注意,此demo中需要MobilenetV2的MNN模型

// distillTrainQuant.cpp

......

// 读取教师MNN模型
auto varMap      = Variable::loadMap(argv[1]);
if (varMap.empty()) {
    MNN_ERROR("Can not load model %s\n", argv[1]);
    return 0;
}

......

// 获取教师模型的输入输出节点
auto inputOutputs = Variable::getInputAndOutput(varMap);
auto inputs       = Variable::mapToSequence(inputOutputs.first);
MNN_ASSERT(inputs.size() == 1);
// 教师模型的输入节点及其名称
auto input = inputs[0];
std::string inputName = input->name();
auto inputInfo = input->getInfo();
MNN_ASSERT(nullptr != inputInfo && inputInfo->order == NC4HW4);

// 教师模型的输出节点及其名称
auto outputs = Variable::mapToSequence(inputOutputs.second);
std::string originOutputName = outputs[0]->name();

// 教师模型输入到Softmax之前的节点,即logits
std::string nodeBeforeSoftmax = "MobilenetV2/Predictions/Reshape";
auto lastVar = varMap[nodeBeforeSoftmax];
std::map<std::string, VARP> outputVarPair;
outputVarPair[nodeBeforeSoftmax] = lastVar;

// 抽取出从输入一直到logits输出的模型部分
auto logitsOutput = Variable::mapToSequence(outputVarPair);
{
    auto exe = Executor::getGlobalExecutor();
    BackendConfig config;
    exe->setGlobalExecutorConfig(MNN_FORWARD_CPU, config, 4);
}

// 将原始模型输入到logits部分转换为float可训练模型
std::shared_ptr<Module> model(PipelineModule::extract(inputs, logitsOutput, true));

// 将上述模型转换为训练量化模型
PipelineModule::turnQuantize(model.get(), bits);

// 原始模型不会进行训练,只会进行前向推理
std::shared_ptr<Module> originModel(PipelineModule::extract(inputs, logitsOutput, false));
// 进入训练环节
_train(originModel, model, inputName, originOutputName);

OK,上面演示了如何获取logits输出,并将模型转换成训练量化模型,下面看一下训练工程中实现量化的关键部分代码

// 训练中的一次前向计算过程

// 将输入数据转换成MNN内部的NC4HW4格式
auto nc4hw4example = _Convert(example, NC4HW4);
// 教师模型前向,得到教师模型的logits输出
auto teacherLogits = origin->forward(nc4hw4example);
// 学生模型前向,得到学生模型的logits输出
auto studentLogits = optmized->forward(nc4hw4example);

// 计算label的One-Hot向量
auto labels = trainData[0].second[0];
const int addToLabel = 1;
auto newTarget = _OneHot(_Cast<int32_t>(_Squeeze(labels + _Scalar<int32_t>(addToLabel), {})),
                         _Scalar<int>(1001), _Scalar<float>(1.0f),
                         _Scalar<float>(0.0f));
// 使用教师模型和学生模型的logits,以及真实label,来计算蒸馏loss
// 温度T = 20,softTargets loss系数 0.9
VARP loss = _DistillLoss(studentLogits, teacherLogits, newTarget, 20, 0.9);

下面来看一下,蒸馏loss是如何计算的,代码在 MNN_ROOT/tools/train/source/optimizer/Loss.cpp中

// Loss.cpp

Express::VARP _DistillLoss(Express::VARP studentLogits, Express::VARP teacherLogits, Express::VARP oneHotTargets, const float temperature, const float alpha) {
    auto info = teacherLogits->getInfo();
    if (info->order == NC4HW4) {
        teacherLogits = _Convert(teacherLogits, NCHW);
        studentLogits = _Convert(studentLogits, NCHW);
    }
    MNN_ASSERT(studentLogits->getInfo()->dim.size() == 2);
    MNN_ASSERT(studentLogits->getInfo()->dim == teacherLogits->getInfo()->dim);
    MNN_ASSERT(studentLogits->getInfo()->dim == oneHotTargets->getInfo()->dim);
    MNN_ASSERT(alpha >= 0 && alpha <= 1);
    // 计算考虑温度之后的教师模型softTargets,学生模型的预测输出
    auto softTargets = _Softmax(teacherLogits * _Scalar(1 / temperature));
    auto studentPredict = _Softmax(studentLogits * _Scalar(1 / temperature));
    // 计算softTargets产生的loss
    auto loss1 = _Scalar(temperature * temperature) * _KLDivergence(studentPredict, softTargets);
    // 计算label产生的loss
    auto loss2 = _CrossEntropy(_Softmax(studentLogits), oneHotTargets);
    // 总的loss为两者加权之和
    auto loss = _Scalar(alpha) * loss1 + _Scalar(1 - alpha) * loss2;
    return loss;
}

模型转换工具

从源码编译

参数说明

Usage:
  MNNConvert [OPTION...]

  -h, --help                    Convert Other Model Format To MNN Model

  -v, --version                 显示当前转换器版本
  
  -f, --framework arg           需要进行转换的模型类型, ex: [TF,CAFFE,ONNX,TFLITE,MNN,TORCH, JSON]
  
      --modelFile arg           需要进行转换的模型文件名, ex: *.pb,*caffemodel

      --batch arg               如果模型时输入的batch是动态的,可以指定转换后的batch数

      --keepInputFormat         是否保持原始模型的输入格式,默认为:否;

      --optimizeLevel arg       图优化级别,默认为1:
                                    - 0 不执行图优化,仅针对原始模型是MNN的情况;
                                    - 1 保证优化后针对任何输入正确;
                                    - 2 保证优化后对于常见输入正确,部分输入可能出错;

      --optimizePrefer arg      图优化选项,默认为0:
                                    - 0:正常优化
                                    - 1:优化后模型尽可能小;
                                    - 2:优化后模型尽可能快;
      
      --prototxt arg            caffe模型结构描述文件, ex: *.prototxt
      
      --MNNModel arg            转换之后保存的MNN模型文件名, ex: *.mnn
      
      --fp16                    将conv/matmul/LSTM的float32参数保存为float16,
      													模型将减小一半,精度基本无损
      
      --benchmarkModel          不保存模型中conv/matmul/BN等层的参数,仅用于benchmark测试
      
      --bizCode arg             MNN模型Flag, ex: MNN
      
      --debug                   使用debug模型显示更多转换信息
      
      --forTraining             保存训练相关算子,如BN/Dropout,default: false
      
      --weightQuantBits arg     arg=2~8,此功能仅对conv/matmul/LSTM的float32权值进行量化,
      													仅优化模型大小,加载模型后会解码为float32,量化位宽可选2~8,
                                运行速度和float32模型一致。8bit时精度基本无损,模型大小减小4倍
                                default: 0,即不进行权值量化
      
      --compressionParamsFile arg
                                使用MNN模型压缩工具箱生成的模型压缩信息文件
                                
      --saveStaticModel         固定输入形状,保存静态模型, default: false

      --targetVersion arg       兼容旧的推理引擎版本,例如:1.2f

      --customOpLibs arg        用户自定义Op库,用于TorchScript模型中自定义算子的实现,如:libmy_add.so

      --info                    当-f MNN时,打印模型基本信息(输入名、输入形状、输出名、模型版本等)

      --authCode arg            认证信息,指定模型的认证信息,可用于鉴权等逻辑

      --inputConfigFile arg     保存静态模型所需要的配置文件, ex: ~/config.txt。文件格式为:
                                input_names = input0,input1
                                input_dims = 1x3x224x224,1x3x64x64

      --testdir arg             测试转换 MNN 之后,MNN推理结果是否与原始模型一致。
                                arg 为测试数据的文件夹,生成方式参考 "正确性校验" 一节

      --thredhold arg           当启用 --testdir 后,设置正确性校验的误差允可范围
                                若不设置,默认是 0.01

      --JsonFile arg            当-f MNN并指定JsonFile时,可以将MNN模型转换为Json文件

      --alignDenormalizedValue arg
                                可选值:{0, 1} 默认为1, `float(|x| < 1.18e-38)`会被视为0

      --detectSparseSpeedUp arg
                                可选值:{0, 1} 默认为1, 会检测权重是否使用稀疏化加速

      --saveExternalData        将权重,常量等数据存储在额外文件中,默认为`false`

说明1: 选项benchmarkModel将模型中例如卷积的weight,BN的mean、var等参数移除,减小转换后模型文件大小,在运行时随机初始化参数,以方便测试模型的性能。

说明2: 选项weightQuantBits,使用方式为 –weightQuantBits numBits,numBits可选2~8,此功能仅对conv/matmul/LSTM的float32权值进行量化,仅优化模型大小,加载模型后会解码为float32,量化位宽可选2~8,运行速度和float32模型一致。经内部测试8bit时精度基本无损,模型大小减小4倍。default: 0,即不进行权值量化。

说明3:如果使用Android JNI的Java接口开发,因为接口中不提供copyFromHost功能,所以需要在转换模型时使用keepInputFormat

其他模型转换到MNN

TensorFlow to MNN

./MNNConvert -f TF --modelFile XXX.pb --MNNModel XXX.mnn --bizCode biz

注意:*.pb必须是frozen model,不能使用saved_model

TensorFlow Lite to MNN

./MNNConvert -f TFLITE --modelFile XXX.tflite --MNNModel XXX.mnn --bizCode biz

Caffe to MNN

./MNNConvert -f CAFFE --modelFile XXX.caffemodel --prototxt XXX.prototxt --MNNModel XXX.mnn --bizCode biz

ONNX to MNN

./MNNConvert -f ONNX --modelFile XXX.onnx --MNNModel XXX.mnn --bizCode biz

TorchScript to MNN

./MNNConvert -f TORCH --modelFile XXX.pt --MNNModel XXX.mnn --bizCode biz

注意:TorchScript模型要求使用torch.jit导出的模型,不要直接使用Pytorch的权重文件作为模型转换;导出模型的代码如下:

import torch
# ...
#  model is exported model
model.eval()
# trace
model_trace = torch.jit.trace(model, torch.rand(1, 3, 1200, 1200))
model_trace.save('model_trace.pt')
# script
model_script = torch.jit.script(model)
model_script.save('model_script.pt')

MNN to Json

想了解MNN模型的具体结构,输入输出信息时,可以将模型转换为Json文件,并查找相关信息获取。

./MNNConvert -f MNN --modelFile XXX.mnn --JsonFile XXX.json

Json to MNN

可以通过将MNN模型转换为Json文件,对Json文件进行编辑修改,然后在转换为MNN模型,达到对模型修改微调的目的。

./MNNConvert -f JSON --modelFile XXX.json --MNNModel XXX.mnn

正确性校验

为了便于开发者排查问题,对于 PB / Tflite / Onnx ,MNN 提供了正确性校验工具(位于 tools/scripts 目录),以检查 MNN 推理结果是否与 原始模型一致。 相关脚本为:

  • testMNNFromTf.py :适用 pb

  • testMNNFromTflite.py :适用 tflite

  • testMNNFromOnnx.py :适用 onnx

  • testMNNFromTorch.py :适用 pt (torchscript)

注意:对于由Torchscript转换的模型,需要自行修改testMNNFromTorch.py中的的输入信息来测试

前置

  • 测试 pb / tflite :安装tensorflow(pip install tensorflow

  • 测试 onnx : 安装onnxruntime(pip install onnxruntime

  • 测试 torchscript:安装torch(pip install torch)

  • 【可选】MNN模型转换工具编译完成(编译完成产生MNNConvert可执行文件)

使用

  • 使用:在MNN的build目录下(包含MNNConvert)运行python3 testMNNFromTf.py SRC.pb(Onnx为python3 testMNNFromOnnx.py SRC.onnx,Tflite 类似),若最终结果为TEST_SUCCESS则表示 MNN 的模型转换与运行结果正确

  • 若路径下面没有编译好的 MNNConvert 可执行文件,脚本会使用 pymnn 去进行校验

  • 由于 MNN 图优化会去除 Identity ,有可能出现 find var error ,这个时候可以打开原始模型文件,找到 identity 之前的一层(假设为 LAYER_NAME )校验,示例:python3 ../tools/script/testMNNFromTF.py SRC.pb LAYER_NAME

  • 完整实例如下(以onnx为例):

    • 成功执行,当结果中显示TEST_SUCCESS时,就表示模型转换与推理没有错误

      cd build
      cmake -DMNN_BUILD_CONVERTER=ON .. && make -j4
      python ../tools/script/testMNNFromOnnx.py mobilenetv2-7.onnx # 模型转换后推理并与ONNXRuntime结果对比
      Dir exist
      onnx/test.onnx
      tensor(float)
      ['output']
      inputs:
      input
      onnx/
      outputs:
      onnx/output.txt (1, 1000)
      onnx/
      Test onnx
      Start to Convert Other Model Format To MNN Model...
      [21:09:40] /Users/wangzhaode/copy/AliNNPrivate/tools/converter/source/onnx/onnxConverter.cpp:40: ONNX Model ir version: 6
      Start to Optimize the MNN Net...
      108 op name is empty or dup, set to Const108
      109 op name is empty or dup, set to BinaryOp109
      110 op name is empty or dup, set to Unsqueeze110
      112 op name is empty or dup, set to Unsqueeze112
      97 op name is empty or dup, set to Unsqueeze97
      98 op name is empty or dup, set to Const98
      inputTensors : [ input, ]
      outputTensors: [ output, ]
      Converted Success!
      input
      output: output
      output: (1, 1000, )
      TEST_SUCCESS
      
  • 默认只支持限定数值范围的输入随机生成,如需修改,请自己修改脚本

出错及解决

  • 出现 Test Error 或者 MNN 的 crash 可直接反馈(提 github issue 或者钉钉群反馈)

  • 如需自查,testMNNFromOnnx.py 提供 debug 功能,可方便定位出错的 layer / op ,示例:

    • python3 testMNNFromOnnx.py SRC.onnx DEBUG

  • 示例,以ONNX为例:

    • 假设存在错误;此处为实验将MNN的Binary_ADD实现修改为错误实现;执行上述测试脚本,效果如下,显示TESTERROR表明可以转换但是推理结果有错误:

      python ../tools/script/testMNNFromOnnx.py mobilenetv2-7.onnx      
      Dir exist
      onnx/test.onnx
      tensor(float)
      ['output']
      inputs:
      input
      onnx/
      outputs:
      onnx/output.txt (1, 1000)
      onnx/
      Test onnx
      Start to Convert Other Model Format To MNN Model...
      [21:43:57] /Users/wangzhaode/copy/AliNNPrivate/tools/converter/source/onnx/onnxConverter.cpp:40: ONNX Model ir version: 6
      Start to Optimize the MNN Net...
      108 op name is empty or dup, set to Const108
      109 op name is empty or dup, set to BinaryOp109
      110 op name is empty or dup, set to Unsqueeze110
      112 op name is empty or dup, set to Unsqueeze112
      97 op name is empty or dup, set to Unsqueeze97
      98 op name is empty or dup, set to Const98
      inputTensors : [ input, ]
      outputTensors: [ output, ]
      Converted Success!
      input
      output: output
      output: (1, 1000, )
      TESTERROR output value error : absMaxV:5.814904 - DiffMax 32.684010
      Error for output output
      Save mnn result to  .error director
      
    • 对于推理出错的情况,可以使用可视化工具查看模型结果,测试每一层的输出,直至发现错误层:

      # test layer output 365: ERROR
      python ../tools/script/testMNNFromOnnx.py mobilenetv2-7.onnx 365
      ...
      365: (1, 32, 28, 28, )
      TESTERROR 365 value error : absMaxV:3.305553 - DiffMax 5.069034
      Error for output 365
      Save mnn result to  .error director
      # binary search test layers ...
      # test layer output 339: ERROR, 339's inputs is [489, 498]
      python ../tools/script/testMNNFromOnnx.py mobilenetv2-7.onnx 339 
      ...
      output: 339
      339: (1, 24, 56, 56, )
      TESTERROR 339 value error : absMaxV:3.704849 - DiffMax 5.504766
      Error for output 339
      Save mnn result to  .error director
      # test layer output 489: SUCCESS
      python ../tools/script/testMNNFromOnnx.py mobilenetv2-7.onnx 489
      ...
      output: 489
      489: (1, 24, 56, 56, )
      TEST_SUCCESS
      # test layer output 498: SUCCESS
      python ../tools/script/testMNNFromOnnx.py mobilenetv2-7.onnx 498
      ...
      output: 498
      498: (1, 24, 56, 56, )
      TEST_SUCCESS
      # so bug is layer 339
      
    • 对于ONNX的模型可以使用自动定位功能,在模型后输入DEBUG,便会执行基于支配树的二分查找,直至找到错误层:

      python ../tools/script/testMNNFromOnnx.py mobilenetv2-7.onnx DEBUG
      ...
      Test Node : Conv_14 True
      ### First Error Node is :  Add_15
      

算子支持列表

./MNNConvert -f CAFFE --OP
./MNNConvert -f TF --OP
./MNNConvert -f ONNX --OP
./MNNConvert -f TORCH --OP 

模型打印

将MNN模型文件dump成可读的类json格式文件,以方便对比原始模型参数,同时也可以对模型进行修改。 可以使用MNNConvertMNNDump2Json将模型转换成Json文件;在对模型进行修改后还可以使用MNNConvertMNNRevert2Buffer将Json文件转回MNN模型;执行方式如下:

./MNNDump2Json mobilenet_v1.mnn mobilenet_v1.json
# do some change in mobilenet_v1.json
./MNNRevert2Buffer mobilenet_v1.json mobilenet_v1_new.mnn
cat mobilenet_v1.json
{ "oplists":
[
{ "type": "Input", "name": "data", "outputIndexes":
[ 0 ]
, "main_type": "Input", "main":
{ "dims":
[ 1, 3, 224, 224 ]
, "dtype": "DT_FLOAT", "dformat": "NC4HW4" }
, "defaultDimentionFormat": "NHWC" }
,
{ "type": "Convolution", "name": "conv1", "inputIndexes":
[ 0 ]
, "outputIndexes":
[ 1 ]
, "main_type": "Convolution2D", "main":
{ "common":
{ "dilateX": 1, "dilateY": 1, "strideX": 2, "strideY": 2, "kernelX": 3, "kernelY": 3, "padX": 1, "padY": 1, "group": 1, "outputCount": 32, "relu": true, "padMode": "CAFFE", "relu6": false, "inputCount": 0 }
, weight: 
[ -0.0, -0.0, 0.0, -0.0, ... ]
, bias: 
[ -0.000004, 0.694553, 0.416608,  ... ]
 }
, "defaultDimentionFormat": "NHWC" }
, 
...
 ]
, "tensorName": 
[ "data", "conv1", "conv2_1/dw", "conv2_1/sep", "conv2_2/dw", "conv2_2/sep", "conv3_1/dw", "conv3_1/sep", "conv3_2/dw", "conv3_2/sep", "conv4_1/dw", "conv4_1/sep", "conv4_2/dw", "conv4_2/sep", "conv5_1/dw", "conv5_1/sep", "conv5_2/dw", "conv5_2/sep", "conv5_3/dw", "conv5_3/sep", "conv5_4/dw", "conv5_4/sep", "conv5_5/dw", "conv5_5/sep", "conv5_6/dw", "conv5_6/sep", "conv6/dw", "conv6/sep", "pool6", "fc7", "prob" ]
, "sourceType": "CAFFE", "bizCode": "AliNNTest", "tensorNumber": 0, "preferForwardType": "CPU" }

Python版

我们提供了预编译的MNNConvert Python工具:mnnconvert

测试工具

从源码编译使用cmake编译时,build目录下的产物也包含测试使用的工具集,下面逐项说明。

GetMNNInfo

功能

获取MNN模型文件的输入输出和版本信息:

  • 输入信息包括输入Tensor的名称数据排布形状数据类型

  • 输出信息包括输出Tensor的名称

  • 版本信息为转换该模型使用的MNNConvert的版本,当版本小于2.0.0时统一显示为Model Version: < 2.0.0

参数

Usage: ./GetMNNInfo model

  • model:str:模型文件路径

示例

$ ./GetMNNInfo mobilenet_v1.mnn 
Model default dimensionFormat is NCHW
Model Inputs:
[ data ]: dimensionFormat: NC4HW4, size: [ 1,3,224,224 ], type is float
Model Outputs:
[ prob ]
Model Version: < 2.0.0

MNNV2Basic.out

功能

测试性能、输出结果,可检查与Caffe/Tensorflow的预期结果是否匹配。 注意:对非CPU后端来说,只有总耗时是准确的,单个op耗时和op耗时占比都是不准确的

参数

./MNNV2Basic.out model [runLoops runMask forwardType numberThread precision_memory inputSize]

  • model:str 模型文件路径

  • runLoops:int 性能测试的循环次数,可选,默认为1

  • runMask:int 是否输出推理中间结果,0为不输出,1为只输出每个算子的输出结果({op_name}.txt),2为输出每个算子的输入(Input_{op_name}.txt)和输出({op_name}.txt)结果; 默认输出当前目录的output目录下(使用工具之前要自己建好output目录),可选,默认为0

  • forwardType:int 执行推理的计算设备,有效值为:0(CPU)、1(Metal)、2(CUDA)、3(OpenCL)、6(OpenGL),7(Vulkan) ,9 (TensorRT),可选,默认为0

  • numberThread:int 线程数仅对CPU有效,可选,默认为4

  • precision_memory:int 测试精度与内存模式,precision_memory % 16 为精度,有效输入为:0(Normal), 1(High), 2(Low), 3(Low_BF16),可选,默认为2 ; precision_memory / 16 为内存设置,默认为 0 (memory_normal) 。例如测试 memory 为 low (2) ,precision 为 1 (high) 时,设置 precision_memory = 9 (2 * 4 + 1)

  • inputSize:str 输入tensor的大小,输入格式为:1x3x224x224,可选,默认使用模型默认输入

默认输入与输出

只支持单一输入、单一输出。输入为运行目录下的input_0.txt;输出为推理完成后的第一个输出tensor,转换为文本后,输出到output.txt中。

示例

$ ./MNNV2Basic.out mobilenetv2-7.mnn 10 0 0 4 1x3x224x224
Use extra forward type: 0
1 3 224 224 
Open Model mobilenetv2-7.mnn
Load Cache file error.
===========> Resize Again...
test_main, 225, cost time: 8.656000 ms
Session Info: memory use 21.125130 MB, flops is 300.982788 M, backendType is 13
===========> Session Resize Done.
===========> Session Start running...
Input size:200704
    **Tensor shape**: 1, 3, 224, 224, 
output: output
precision:2, Run 10 time:
                            Convolution96_raster_0 	[Raster] run 10 average cost 0.003400 ms, 0.061 %, FlopsRate: 0.000 %
                                            452 	[BinaryOp] run 10 average cost 0.004700 ms, 0.084 %, FlopsRate: 0.002 %
                                            ...
                                            483 	[Convolution] run 10 average cost 0.452600 ms, 8.125 %, FlopsRate: 6.402 %
                                            486 	[ConvolutionDepthwise] run 10 average cost 0.461000 ms, 8.276 %, FlopsRate: 0.900 %
Avg= 5.570600 ms, OpSum = 7.059200 ms min= 3.863000 ms, max= 11.596001 ms

ModuleBasic.out

功能

类似MNNV2Basic.out,对于带控制流模型,或者多输入多输出的模型,建议采用这个工具

参数

./ModuleBasic.out model dir [runMask forwardType runLoops numberThread precision_memory cacheFile]

  • model:str 模型文件路径

  • dir:str 输入输出信息文件夹,可使用 testMNNFromTf.py / testMNNFromOnnx.py / testMNNFromTflite.py 等脚本生成,参考模型转换的正确性校验部分。

  • runMask:int 默认为 0 ,为一系列功能的开关,如需开启多个功能,可把对齐的 mask 值相加(不能叠加的情况另行说明),具体见下面的 runMask 参数解析

  • forwardType:int 执行推理的计算设备,有效值为:0(CPU)、1(Metal)、2(CUDA)、3(OpenCL)、6(OpenGL),7(Vulkan) ,9 (TensorRT),可选,默认为0

  • runLoops:int 性能测试的循环次数,可选,默认为0即不做性能测试

  • numberThread:int GPU的线程数,可选,默认为1

  • precision_memory:int 测试精度与内存模式,precision_memory % 16 为精度,有效输入为:0(Normal), 1(High), 2(Low), 3(Low_BF16),可选,默认为2 ; precision_memory / 16 为内存设置,默认为 0 (memory_normal) 。例如测试 memory 为 2(low) ,precision 为 1 (high) 时,设置 precision_memory = 9 (2 * 4 + 1)

默认输出

在当前目录 output 文件夹下,依次打印输出为 0.txt , 1.txt , 2.txt , etc

runMask 参数说明

  • 1 : 输出推理中间结果,每个算子的输入存到(Input_{op_name}.txt),输出存为({op_name}.txt), 默认输出当前目录的output目录下(使用工具之前要自己建好output目录),不支持与 2 / 4 叠加

  • 2 : 打印推理中间结果的统计值(最大值/最小值/平均值),只支持浮点类型的统计,不支持与 1 / 4 叠加

  • 4 : 统计推理过程中各算子耗时,不支持与 1 / 2 叠加,仅在 runLoops 大于 0 时生效

  • 8 : shapeMutable 设为 false (默认为 true)

  • 16 : 适用于使用 GPU 的情况,由 MNN 优先选择 CPU 运行,并将 GPU 的 tuning 信息存到 cache 文件,所有算子 tuning 完成则启用 GPU

  • 32 : rearrange 设为 true ,降低模型加载后的内存大小,但会增加模型加载的初始化时间

  • 64 : 创建模型后,clone 出一个新的模型运行,用于测试 clone 功能(主要用于多并发推理)的正确性

示例

$ python ../tools/script/fastTestOnnx.py mobilenetv2-7.onnx
$ ./ModuleBasic.out mobilenetv2-7.mnn onnx 0 0 10   
Test mobilenetv2-7.mnn from input info: onnx
input
output: output
Use extra forward type: 0
precision=0 in main, 291 
cacheFileName=s .tempcache in main, 292 
Load Cache file error.
before compare output: (1, 1000, )
Write output output to output/0.txt
memoryInMB=f 22.605423 in main, 428 
Avg= 9.946699 ms, min= 9.472000 ms, max= 10.227000 ms

SequenceModuleTest.out

功能

类似ModuleBasic.out,适用于多份输入输出数据的校验

参数

./SequenceModuleTest.out model [forwardType] [shapeMutable] dir1 dir2 ......

  • model:str 模型文件路径

  • forwardType:int 执行推理的计算设备,有效值为:0(CPU)、1(Metal)、2(CUDA)、3(OpenCL)、6(OpenGL),7(Vulkan) ,9 (TensorRT)

  • shapeMutable:int 输入形状是否可变

  • dir_n:str 输入输出信息文件夹,可使用 fastTestOnnx.py / fastTestTf.py / fastTestTflite.py 等脚本生成,参考模型转换的正确性校验部分

./SequenceModuleTest.out transformer.mnn 0 1 tr tr1 tr2 tr3 tr4 > error.txt

checkFile.out

功能

对比两个文本文件数据是否一致

参数

./checkFile.out file1 file2 [tolerance]

  • file_1:str 比较的第一个文件路径

  • file_2:str 比较的第二个文件路径

  • tolerance:float 误差的绝对阈值,误差大于阈值会输出到控制台,可选,默认为0.001

示例

$ echo '0.111\n0.222\n0.333\n0.444' > x.txt
$ echo '0.111\n0.2225\n0.335\n0.448' > y.txt
$ ./checkFile.out x.txt y.txt 0.001
Error for 2, v1=0.333000, v2=0.335000
Error for 3, v1=0.444000, v2=0.448000

checkDir.out

功能

对比两个文件夹下同名文件数据是否一致

参数

./checkDir.out dir1 dir2 [tolerance order]

  • dir1:str 比较的第一个文件夹路径

  • dir2:str 比较的第二个文件夹路径

  • tolerance:float 误差的绝对阈值,误差大于阈值会输出到控制台,可选,默认为0.001

  • order:str 比较文件顺序描述文件路径,该参数为一个文本文件,其中每行指定一个比较文件名,将会按照该顺序进行比较,可选,默认直接比较所有文件

示例

$ mkdir dir1 dir2
$ echo '0.111\n0.222\n0.333\n0.444' > dir1/a.txt
$ echo '0.111\n0.2225\n0.335\n0.448' > dir2/a.txt
$ ./checkDir.out dir1 dir2
Compare:
dir1
dir2
tolerance=0.001000
Error for 2, a.txt, 2, v1=0.333000, v2=0.335000

checkInvalidValue.out

功能

根据指定limit检测指定目录下的文件,是否包含非法数据,非法数据的定义为:

  • 数据值为nan

  • 数据的值小于10^limit

参数

./checkInvalidValue.out dir limit

  • dir:str 检测的目录路径

  • limit:int 检测值的最大阈值为10^limit,可选,默认为10

示例

$ mkdir dir1
$ echo '0.111\n0.222\n0.333\n0.444' > dir1/a.txt
$ ./checkInvalidValue.out dir1 1  
Compare:
dir1
limit=1
Correct : a.txt
$ ./checkInvalidValue.out dir1 -1
Compare:
dir1
limit=-1
Error for a.txt, 0, v1=0.111000

timeProfile.out

功能

模型总耗时,逐层耗时统计和模型运算量估计。注意:不要用这个工具测非CPU后端的性能,需要的话请用MNNV2Basic工具

参数

./timeProfile.out model [runLoops forwardType inputSize numberThread precision]

  • model:str 模型文件路径

  • runLoops:int 测试的循环次数,可选,默认为100

  • forwardType:int 执行推理的计算设备,有效值为:0(CPU)、1(Metal)、2(CUDA)、3(OpenCL)、6(OpenGL),7(Vulkan) ,9 (TensorRT),可选,默认为0;(当执行推理的计算设备不为 CPU 时,Op平均耗时和耗时占比可能不准)

  • inputSize:str 输入tensor的大小,输入格式为:1x3x224x224,可选,默认使用模型默认输入

  • numberThread:int 线程数仅对CPU有效,可选,默认为4

  • precision:int 精度仅对CPU有效,可选,默认为0

输出

  • 第一列为 Op类型

  • 第二列为 平均耗时

  • 第三列为 耗时占比

示例

$ ./timeProfile.out mobilenetv2-7.mnn 10 0 1x3x224x224 1
1 3 224 224 
Set ThreadNumber = 1
Open Model mobilenetv2-7.mnn
Sort by node name !
Node Name              	Op Type              	Avg(ms)  	%        	Flops Rate 	
339                    	BinaryOp             	0.039300 	0.364004 	0.023848   	
356                    	BinaryOp             	0.008500 	0.078729 	0.007949 
...  
Convolution96          	Convolution          	0.273800 	2.535987 	0.425273   	
Convolution96_raster_0 	Raster               	0.002100 	0.019451 	0.000406   	
output_raster_0        	Raster               	0.005200 	0.048163 	0.000317   	
Print <=20 slowest Op for Convolution, larger than 3.00
474 -  16.396393 GFlops, 6.12 rate
483 -  22.691771 GFlops, 7.86 rate
498 -  29.693188 GFlops, 3.38 rate
627 -  37.167416 GFlops, 5.00 rate
624 -  37.296322 GFlops, 3.74 rate

Sort by time cost !
Node Type            	Avg(ms)  	%         	Called times 	Flops Rate 	
Raster               	0.007300 	0.067614  	2.000000     	0.000722   	
Pooling              	0.020700 	0.191727  	1.000000     	0.000000   	
BinaryOp             	0.083600 	0.774319  	10.000000    	0.068562   	
ConvolutionDepthwise 	2.162301 	20.027637 	17.000000    	6.882916   	
Convolution          	8.522696 	78.938828 	36.000000    	93.047722  	
total time : 10.796584 ms, total mflops : 300.983246 
main, 138, cost time: 111.161003 ms

backendTest.out

功能

对比指定计算设备和CPU执行推理的结果,该工具默认读取当前目录下的input_0.txt作为输入

参数

./backendTest.out model [forwardType tolerance precision modeNum stopOp]

  • model:str 模型文件路径

  • forwardType:int 执行推理的计算设备,有效值为:0(CPU)、1(Metal)、2(CUDA)、3(OpenCL)、6(OpenGL),7(Vulkan) ,9 (TensorRT),可选,默认为0

  • tolerance:float 误差的绝对阈值,误差大于阈值会认为不一致,可选,默认为0.05

  • precision:int 测试精度,有效输入为:0(Normal), 1(High), 2(Low), 3(Low_BF16),可选,默认为0

  • modeNum:int 设置GPU的执行模式,可选,默认为1

  • stopOp:str 指定某一层的名称,当执行到该层时停止对比,可选,默认为空

示例

$ ./backendTest.out mobilenetv2-7.mnn 3 0.15 1
Test forward type: 3
Tolerance Rate: 0.150000
Open Model mobilenetv2-7.mnn
Input: 224,224,3,0
precision=1 in main, 268 
modeNum=1 in main, 273 
stopOp.c_str()=s  in main, 278 
Correct ! Run second pass
Correct !

在Android中使用

先编译相关的库和可执行文件,然后push到Android手机上,用adb执行命令,参考project/android/testCommon.sh

cd project/android
mkdir build_64
cd build_64 && ../build_64.sh
../updateTest.sh
../testCommon.sh ./backendTest.out temp.mnn 3 0.15 1

getPerformance

功能

获取当前设备的CPU性能,打印出每个CPU核心的频率;在Android设备上还会打印该设备CPU的浮点计算能力(GFLOPS)

各核心频率仅在Linux/Android环境中有效,计算能力仅在Android中有效

参数

./getPerformance.out 无参数

示例(Linux)

$ ./getPerformance.out 
Start PERFORMANCE !!! 
CPU PERFORMANCE -> loopCounts : 100000000 
core 0 : max : 3800000, min : 2200000 
core 1 : max : 3800000, min : 2200000 
...
core 23 : max : 3800000, min : 2200000 
 ======================== float ===============================
CPU float gflops : 0.000000

modelCompare.out

功能

原始模型与量化模型推理结果对比

参数

./modelCompare.out origin_model quant_model [tolerance]

  • origin_model:str 原始浮点模型路径

  • quant_model:str int8量化模型路径

  • tolerance:float 误差的绝对阈值,误差大于阈值会认为不一致,可选,默认为0.05

  • modeNum:int 设置GPU的执行模式,可选,默认为1

  • stopOp:str 指定某一层的名称,当执行到该层时停止对比,可选,默认为空

示例

$ ./modelCompare.out mobilnet.mnn mobilnet_quant.mnn 1  
Tolerance Rate: 1.000000
Open Model mobilnet.mnn, mobilnet_quant.mnn
Input: 224,224,3,1
precision=0 in main, 252 
modeNum=1 in main, 257 
stopOp.c_str()=s  in main, 262 
Correct ! Run second pass
Correct !

mobilenetTest.out

功能

执行mobilenet的推理测试,输入模型与图片,输出结果的top-10与执行时间

参数

./mobilenetTest.out model image [forwardType precision label]

  • model:str 模型文件路径

  • image:str 输入图片文件路径

  • forwardType:int 执行推理的计算设备,有效值为:0(CPU)、1(Metal)、2(CUDA)、3(OpenCL)、6(OpenGL),7(Vulkan) ,9 (TensorRT),可选,默认为0

  • precision:int 测试精度,有效输入为:0(Normal), 1(High), 2(Low), 3(Low_BF16),可选,默认为1

  • label:str 种类标签文件,imagenet的1000中分类的标签,可选,默认不使用标签,直接输出种类的index

示例

$ ./mobilenetTest.out mobilnet.mnn cat.jpg 
model:mobilnet.mnn, input image: cat.jpg, forwardType:0, precision:1
main, 90, cost time: 7.789001 ms
output size:1000
287, 0.218567
282, 0.141113
285, 0.125122
281, 0.117249
283, 0.039116
278, 0.038887
700, 0.034599
279, 0.014238
277, 0.012278
17, 0.011496

testModel.out

功能

输入模型,输入文件和期望输出文件;执行推理判断使用制定输入是否能够得到相同的输出

参数

./testModel.out model input output

  • model:str 模型文件路径

  • input:str 输入数据,文本文件格式,其中为浮点数据

  • output:str 期望输出数据,文本文件格式,其中为浮点数据

示例

$ ./testModel.out mobilenet.mnn input_0.txt output.txt 
Testing model mobilenet.mnn, input: input_0.txt, output: output.txt
First run pass
Test mobilenet.mnn Correct!

testModel_expr.out

功能

功能与testModel.out相同,使用Module接口执行,能够测试带控制流的模型,输出输出使用.mnn文件代替文本

参数

./testModel_expr.out model input output [forwardType tolerance precision]

  • model:str 模型文件路径

  • input:str 输入数据,.mnn格式文件,使用表达式接口存储的数据

  • output:str 期望输出数据,.mnn格式文件,使用表达式接口存储的数据

  • forwardType:int 执行推理的计算设备,有效值为:0(CPU)、1(Metal)、2(CUDA)、3(OpenCL)、6(OpenGL),7(Vulkan) ,9 (TensorRT),可选,默认为0

  • tolerance:float 误差的绝对阈值,误差大于阈值会认为不一致,可选,默认为0.1

  • precision:int 测试精度,有效输入为:0(Normal), 1(High), 2(Low), 3(Low_BF16),可选,默认为1

示例

$ ./testModel_expr.out mobilenet.mnn input_0.mnn output.mnn 
Testing model mobilenet.mnn, input: input.mnn, output: output.mnn
Correct!

testModelWithDescribe.out

功能

功能与testModel.out相同,输入输出通过配置文件来描述,支持多输入与多输出的对比,同时支持指定输入形状

参数

./testModelWithDescribe.out model confg

  • model:str 模型文件路径

  • confg:str 配置文件路径,配置文件如下:

    # 多个输入用,分开,不要用空格
    # 目前默认输入都是float
    # 文件名为输入tensor的名字后缀为txt,如0表示./0.txt
    input_size = 1
    input_names = 0
    input_dims = 1x3x416x416
    output_size = 4
    output_names = 1257,1859,2374,655
    

示例

./testModelWithDescribe.out mobilenet.mnn config.txt
model dir: mobilenet.mnn
Testing Model ====> mobilenet.mnn
First Time Pass
Correct!

testTrain.out

说明

根据指定配置文件对模型执行2次训练的反向传播过程并判断loss是否下降

参数

./testTrain.out config dir

  • config 训练测试的配置文件,其指定了训练测试的各种信息,其格式参考示例

  • dir 训练所需文件夹,文件夹内包含续联的模型与输入数据

示例

$ ./testTrain.out config.json ./mnist
From 42.261131 -> 11.569703, Test ./mnist/mnist.train.mnn Correct!

配置文件内容为:

{
    "Model": "mnist.train.mnn",
    "Loss": "Loss",
    "LR": "LearningRate",
    "LearningRate":0.005,
    "Input":"Input3",
    "Decay":0.3,
    "Target":"Reshape38_Compare",
    "Data":[
        "data.mnn"
    ]
}

winogradExample.out

说明

生成winograd变换矩阵程序

参数

./winogradExample.out unit kernelSize

  • unit:int 分块大小

  • kernelSize:int 卷积核大小

示例

$ ./winogradExample.out 3 3                        
A=
Matrix:
1.0000000	0.0000000	0.0000000	
1.0000000	0.5000000	0.2500000	
1.0000000	-0.5000000	0.2500000	
1.0000000	1.0000000	1.0000000	
0.0000000	0.0000000	1.0000000	
B=
Matrix:
1.0000000	0.0000000	0.0000000	0.0000000	0.0000000	
-1.0000000	2.0000000	-0.6666667	-0.3333333	0.2500000	
-4.0000000	2.0000000	2.0000000	0.0000000	-0.2500000	
4.0000000	-4.0000000	-1.3333334	1.3333334	-1.0000000	
0.0000000	0.0000000	0.0000000	0.0000000	1.0000000	
G=
Matrix:
1.0000000	0.0000000	0.0000000	
1.0000000	0.5000000	0.2500000	
1.0000000	-0.5000000	0.2500000	
1.0000000	1.0000000	1.0000000	
0.0000000	0.0000000	1.0000000

Benchmark工具

Linux / macOS / Ubuntu

从源码编译,然后执行如下命令:

./benchmark.out models_folder loop_count warm_up_count forwardtype numberThread precision weightSparsity weightSparseBlockNumber testQuantizdModel

参数如下:

  • models_folder: benchmark models文件夹,benchmark models

  • loop_count: 可选,默认是10

  • warm_up_count: 预热次数

  • forwardtype: 可选,默认是0,即CPU,forwardtype有0->CPU,1->Metal,3->OpenCL,6->OpenGL,7->Vulkan

  • numberThread: 可选,默认是4,为 CPU 线程数或者 GPU 的运行模式

  • precision: 可选,默认是2,有效输入为:0(Normal), 1(High), 2(Low_FP16), 3(Low_BF16)

  • weightSparsity: 可选,默认是 0.0 ,在 weightSparsity > 0.5 时且后端支持时,开启稀疏计算

  • weightSparseBlockNumber: 可选,默认是 1 ,仅当 weightSparsity > 0.5 时生效,为稀疏计算 block 大小,越大越有利于稀疏计算的加速,一般选择 1, 4, 8, 16

  • testQuantizedModel 可选,默认是0,即只测试浮点模型;取1时,会在测试浮点模型后进行量化模型的测试

Android

benchmark目录下直接执行脚本bench_android.sh,默认编译armv7,加参数-64编译armv8,参数-p将benchmarkModels push到机器上。 脚本执行完成在benchmark目录下得到测试结果benchmark.txt

iOS

  1. 先准备模型文件,进入tools/script目录下执行脚本get_model.sh

  2. 打开demo/iOS目录下的demo工程,点击benchmark;可通过底部工具栏切换模型、推理类型、线程数。

基于表达式构建模型的Benchmark

从源码编译,运行以下命令查看帮助:

 ./benchmarkExprModels.out help

示例:

 ./benchmarkExprModels.out MobileNetV1_100_1.0_224 10 0 4 
 ./benchmarkExprModels.out MobileNetV2_100 10 0 4 
 ./benchmarkExprModels.out ResNet_100_18 10 0 4 
 ./benchmarkExprModels.out GoogLeNet_100 10 0 4 
 ./benchmarkExprModels.out SqueezeNet_100 10 0 4 
 ./benchmarkExprModels.out ShuffleNet_100_4 10 0 4

相应模型的paper链接附在头文件里,如benchmark/exprModels/MobileNetExpr.hpp

单输入模型离线量化工具

./quantized.out origin.mnn quan.mnn imageInputConfig.json

通用(任意输入个数、维度、类型)模型离线量化请看说明

MNN现已推出基于TensorFlow/Pytorch的模型压缩工具mnncompress,请查看文档选择使用

参数

  • 第一个参数为原始模型文件路径,即待量化的浮点模

  • 第二个参数为目标模型文件路径,即量化后的模型

  • 第三个参数为预处理的配置项,参考imageInputConfig.json,该Json的配置信息如下表所示:

key value 说明
format "RGB", "BGR", "RGBA", "GRAY" 图片统一按RGBA读取,然后转换到format指定格式
mean/normal [float] dst = (src - mean) * normal
width/height int 模型输入的宽高
path str 存放校正特征量化系数的图片目录
used_image_num int 用于指定使用上述目录下多少张图片进行校正,默认使用path下全部图片
feature_quantize_method "KL", "ADMM", "EMA" 指定计算特征量化系数的方法,默认:"KL"
weight_quantize_method "MAX_ABS", "ADMM" 指定权值量化方法,默认:"MAX_ABS"
feature_clamp_value int 特征的量化范围,默认为127,即[-127, 127]对称量化,有时,量化之后溢出会很多,造成误差较大,可适当减小此范围,如减小至120,但范围减小太多会导致分辨率下降,使用时需测试
weight_clamp_value int 权值的量化范围,默认127,作用同feature_clamp_value,由于权值精度模型效果影响较大,建议调整feature_clamp_value即可
batch_size int EMA方法中指定batch size,和模型训练时差不多
quant_bits int 量化后的bit数,默认为8
skip_quant_op_names [str] 跳过不量化的op的卷积op名字,因为有些层,如第一层卷积层,对模型精度影响较大,可以选择跳过不量化,可用netron可视化模型,找到相关op名字
input_type str 输入数据的类型,默认为"image"
debug bool 是否输出debug信息,true或者false,输出的debug信息包含原始模型和量化模型各层输入输出的余弦距离和溢出率
feature_quantize_method 说明
KL 使用KL散度进行特征量化系数的校正,一般需要100 ~ 1000张图片(若发现精度损失严重,可以适当增减样本数量,特别是检测/对齐等回归任务模型,样本建议适当减少)
ADMM 使用ADMM(Alternating Direction Method of Multipliers)方法进行特征量化系数的校正,一般需要一个batch的数据
EMA 使用指数滑动平均来计算特征量化参数,这个方法会对特征进行非对称量化,精度可能比上面两种更好。这个方法也是MNNPythonOfflineQuant的底层方法,建议使用这个方法量化时,保留你pb或onnx模型中的BatchNorm,并使用 --forTraining 将你的模型转到MNN,然后基于此带BatchNorm的模型使用EMA方法量化。另外,使用这个方法时batch size应设置为和训练时差不多最好。
weight_quantize_method 说明
MAX_ABS 使用权值的绝对值的最大值进行对称量化
ADMM 使用ADMM方法进行权值量化

量化模型的使用

和浮点模型同样使用方法,输入输出仍然为浮点类型

参考资料

Extremely Low Bit Neural Network: Squeeze the Last Bit Out with ADMM

用法示例

cd /path/to/MNN/build
cmake -DMNN_BUILD_QUANTOOLS=ON && make -j4
./quantized.out mobilnet.mnn mobilnet_quant.mnn mobilnet_quant.json                                         
[11:53:29] /Users/wangzhaode/copy/AliNNPrivate/tools/quantization/quantized.cpp:23: >>> modelFile: mobilnet.mnn
[11:53:29] /Users/wangzhaode/copy/AliNNPrivate/tools/quantization/quantized.cpp:24: >>> preTreatConfig: mobilnet_quant.json
[11:53:29] /Users/wangzhaode/copy/AliNNPrivate/tools/quantization/quantized.cpp:25: >>> dstFile: mobilnet_quant.mnn
[11:53:29] /Users/wangzhaode/copy/AliNNPrivate/tools/quantization/quantized.cpp:53: Calibrate the feature and quantize model...
[11:53:29] /Users/wangzhaode/copy/AliNNPrivate/tools/quantization/calibration.cpp:156: Use feature quantization method: KL
[11:53:29] /Users/wangzhaode/copy/AliNNPrivate/tools/quantization/calibration.cpp:157: Use weight quantization method: MAX_ABS
[11:53:29] /Users/wangzhaode/copy/AliNNPrivate/tools/quantization/calibration.cpp:177: feature_clamp_value: 127
[11:53:29] /Users/wangzhaode/copy/AliNNPrivate/tools/quantization/calibration.cpp:178: weight_clamp_value: 127
[11:53:29] /Users/wangzhaode/copy/AliNNPrivate/tools/quantization/Helper.cpp:111: used image num: 2
[11:53:29] /Users/wangzhaode/copy/AliNNPrivate/tools/quantization/calibration.cpp:668: fake quant weights done.
ComputeFeatureRange: 100.00 %
CollectFeatureDistribution: 100.00 %
[11:53:31] /Users/wangzhaode/copy/AliNNPrivate/tools/quantization/quantized.cpp:58: Quantize model done!

配置文件mobilnet_quant.json内容如下:

{
    "format":"RGB",
    "mean":[
        103.94,
        116.78,
        123.68
    ],
    "normal":[
        0.017,
        0.017,
        0.017
    ],
    "width":224,
    "height":224,
    "path":"../resource/images/",
    "used_image_num":2,
    "feature_quantize_method":"KL",
    "weight_quantize_method":"MAX_ABS",
    "model":"mobilenet.mnn"
}

Python版

我们提供了预编译的Quant Python工具:mnnquant

模型压缩工具箱

介绍

是什么?

MNN模型压缩工具箱提供了包括低秩分解、剪枝、量化等模型压缩算法的实现,并且MNN进一步实现了其中一些需要软件特殊实现的算法(如稀疏计算和量化)的底层计算过程,因此,此工具箱需要配合MNN推理框架来使用。 具体来说,MNN压缩工具箱包含两个组成部分:

  1. MNN框架自身提供的压缩工具(输入MNN模型,输出MNN模型)

  2. mnncompress(基于主流训练框架TF/Pytorch的模型压缩工具)。

有什么?

目前提供的能力如下表所示:

原始模型格式 提供的工具 支持的压缩算法类别
MNN 量化工具(python,c++)
模型转换工具(python,c++)
离线量化
训练量化(不成熟)
权值量化(转换时直接完成)
FP16(转换时直接完成)
TensorFlow 1.X python压缩算法插件(mnncompress) 低秩分解
线性过参数化
模型剪枝
离线量化
训练量化
训练权值量化
Pytorch python压缩算法插件(mnncompress) 低秩分解
模型剪枝
离线量化
训练量化
训练权值量化

各类压缩算法的特点:

压缩算法类别 特点 支持的算法
低秩分解 将原始模型进行分解,降低模型计算量、存储量,分解之后的模型仍是一个规整的模型,不需要特殊的软件底层实现;需要进行finetune训练 Tucker分解,SVD分解
线性过参数化 用于模型从0开始训练的过程中,设计一个小模型,然后使用此算法对模型进行过参数化扩展为一个大模型,提高模型的表达能力,然后基于此大模型进行训练,训练完成之后可以将大模型中的层合并得到原始小模型一样的结构,而且精度和大模型一致 改进的 linear over parameterization
模型剪枝 将模型的权值矩阵进行稀疏,并进行finetune训练,利用稀疏编码(通道剪枝不需要)压缩模型大小,底层稀疏计算实现加速 1x1随机剪枝,1x4block剪枝,通道剪枝
离线量化 将float卷积转换为int8卷积计算,仅需少量校准图片,降低存储量到原始模型的四分之一,降低内存,加速计算(某些模型可能会比float模型慢,因为float的优化方法和int8不同) EMA,KL,ADMM
训练量化 将float卷积转换为int8卷积计算,需要进行训练,可提高量化模型精度,降低存储量到原始模型的四分之一,降低内存,加速计算(某些模型可能会比float模型慢,因为float的优化方法和int8不同) LSQ,OAQ,WAQ
直接权值量化 仅将模型中的权值进行量化,计算时还原为float进行计算,因此仅减少模型存储量,计算速度和float相同,可以在模型转换时一键完成,8bit量化情况下,精度基本不变,模型大小减小到原来的1/4 对称量化,非对称量化
训练权值量化 特点同直接权值量化,但通过mnncompress压缩算法插件实现,因而可以提供更低比特的权值量化,以减少更多的存储量,并提高权值量化之后模型的精度,例如4bit量化情况下,模型大小减小到原来的1/8 对称量化
FP16 将FP32计算转换为FP16计算,可在模型转换时一键完成,模型大小减小为原来的1/2,精度基本无损,并提高计算速度(需要硬件支持FP16计算) -

怎么用?

  1. 如果只想使用离线压缩方法,可以将模型转换为MNN模型之后使用对应的工具进行压缩。这类压缩算法不需要进行训练finetune,所以通常运行得很快。

  2. 如果离线压缩方法的精度不满足要求,且能够进行训练finetune的话,可以使用mnncompress中提供的压缩算法插件将原始模型进行压缩,得到压缩之后的模型和压缩信息描述文件,然后将这两个文件输入到MNN模型转换工具得到最终的MNN压缩模型。需要训练的压缩算法可以提供更好的精度,但需要一定的时间进行finetune训练,此finetune训练需要的时间一般比模型从0开始训练要少很多。

  3. 这些算法中有些是可以叠加使用的,以取得更好的压缩效果。推荐使用pipeline(其中方框中的算法均为可选,叠加压缩算法若精度不好,可选择使用): _images/mnncompress.jpg

MNN框架自身提供的压缩工具

使用方法

MNN框架压缩工具是基于离线量化工具和MNN转换工具来实现压缩功能的,这两个工具均提供c++版本和python版本,安装方式如下:

  • c++工具安装

    需要源码编译MNN转换工具 MNNConvert 和量化工具 quantized.out

    cd build
    cmake ..  -DMNN_BUILD_CONVERTER=ON -DMNN_BUILD_QUANTOOLS=ON
    make -j 8
    
  • python工具安装

    # 外部版本MNN,外网安装方式
    pip install MNN
    # 外部版本MNN,集团内安装方式
    pip install --index-url https://pypi.antfin-inc.com/simple/ -U MNN
    # 内部版本MNN
    pip install --index-url https://pypi.antfin-inc.com/simple/ -U MNN-Internal
    # 安装之后,命令行中将有如下工具:
    mnn:显示MNN命令行工具
    mnnconvert:转换器 MNNConvert 的预编译工具,功能同 MNNConvert
    mnnquant:量化工具 quantized.out 的预编译工具,功能同 quantized.out
    

MNN离线量化工具

原理

将float卷积转换为int8卷积进行计算(仅量化卷积,建议将FC转为1*1卷积实现),同时会通过MNN几何计算机制将量化信息在网络中进行传播,以支持尽可能多的算子的量化计算。模型大小减少为原始模型的1/4,并减少内存,提高推理速度(某些模型可能量化之后变慢,因为float的计算可以使用winograd、strassen等优化算法,而离线量化的int8计算并没有这些优化,如果要使用int8量化的特殊优化,如OAQ、WAQ等,需要使用mnncompress)。

单输入、图片输入模型的量化

这类模型可以使用 quantized.out(或mnnquant)进行量化,使用文档在:quantized.outmnnquant.md

通用模型的量化

通用模型量化工具可以支持任意输入和任意输入类型的模型的量化,基于MNN python包,使用文档在:MNNPythonOfflineQuant

注意:calibration_dataset.py__getitem__返回为一个输入sample,其形状不应该包含batch维度,在量化时我们会根据工具命令行中传入的batch参数,stack出一个batch的数据,但我们默认batch维度在第一维,所以,如果你的某个输入的batch维不在第一维,你需要在你对应的输入之前加一个transpose。

MNN权值量化工具

原理

仅将模型中卷积的float权值量化为int8存储,推理时反量化还原为float权值进行计算。因此,其推理速度和float模型一致,但是模型大小可以减小到原来的1/4,可以通过模型转换工具一键完成,比较方便。推荐float模型性能够用,仅需要减少模型大小的场景使用。

使用方法

使用MNNConvert(c++)或者mnnconvert(python包中自带)进行转换,转换命令行中加上下述选项即可:

--weightQuantBits 8 [--weightQuantAsymmetric](可选)

--weightQuantAsymmetric 选项是指使用非对称量化方法,精度要比默认的对称量化精度好一些。

MNN FP16压缩工具

原理

将模型中FP32权值转换为FP16存储,并在支持的设备上开启FP16推理,可以获得推理加速,并且速度减少到原来的1/2。可以在模型转换时一键完成,使用方便。

使用方法

使用MNNConvert(c++)或者mnnconvert(python包中自带)进行转换,转换命令行中加上下述选项即可:

--fp16

mnncompress

使用方法

安装

使用pip安装:pip install mnncompress

支持的算法
算法类别 算法名称 TensorFlow1.X Pytorch
线性过参数化 linear over parameterization 改进
低秩分解 Tucker分解,SVD分解
剪枝 TaylorFOChannelPruner 结构化通道剪枝
SIMDOCPruner 1*4半结构化剪枝
SNIPLevelPruner 1*1随机非结构化剪枝
训练量化(也可改做离线量化) LSQ(学习量化参数)
OAQ(overflow aware quantization)
WAQ(winograd aware quantization)
权值量化(也可不训练,查看直接权值量化精度) 对称量化,非对称量化
mnncompress技术路线

mnncompress的技术路线如下图所示: https://cdn.nlark.com/yuque/0/2022/png/405909/1655201133194-3996080e-ee69-4c7a-9671-1933848adcfd.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_62%2Ctext_QWxpYmFiYQ%3D%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10%2Fresize%2Cw_1324%2Climit_0image.png 通过mnncompress中的模型压缩插件对原始模型进行压缩,然后得到一个原始的float模型,以及该压缩算法所需要的额外的压缩参数(例如对于量化算法来说,需要各个卷积层的scale和zeropoint等信息)。最后将这个float模型和模型压缩参数文件输入到MNN转换器中,得到最终的MNN压缩模型。

关于模型压缩参数文件

在mnncompress的文档中通常命名为“compress_params.bin”,是一个protobuf序列化之后的二进制文件。每执行一个压缩算法都需要保存一个对应的模型压缩参数文件,如果有算法上的叠加(例如剪枝+量化的叠加),那么需要将算法对应的API接口中的append参数设置为True(例如剪枝+量化的叠加,则需要在量化算法保存压缩参数文件时,将对应的接口append参数设置为True,将量化算法所需要的参数append到剪枝算法的模型压缩文件上去),这样MNN模型转换器才会知道你的模型经过了哪些优化,才能进行正确的转换。

Benchmark

离线量化

以下模型均来自torchvision预训练模型,采用mnncompress中LSQ的offline模式进行量化,batch size为64,用10个batch,640张训练图片进行量化,表中标*的模型表示量化时跳过了第一层或者前两层不量化,以提升精度。测速均采用华为P20 Pro 单线程

模型 原始模型指标 压缩模型指标 ARMV7 (ms) ARMV8 (ms)
ResNet-18 69.758%,45M 69.740%,12M 196.6 --> 208.8 187.6 --> 167.0
ResNet-50 76.130%,98M 76.030%,25M 606.0 --> 470.5 550.8 --> 379.6
SqueezeNet 1.0 58.092%,4.8M 57.800%,1.3M 122.1 --> 104.1 120.8 --> 88.3
ShuffleNet V2 x1.0 69.362%,8.7M 68.616%,2.3M 33.3 --> 33.8 29.0 --> 26.0
MobileNet V2 71.878%,14M 71.150%,3.5M 69.1 --> 50.2 62.2 --> 42.1
*MobileNet V3 Large 74.042%,21M 73.030%,5.4M 68.6 --> 63.1 64.9 --> 50.1
MNASNet 1.0 73.456%,17M 72.692%,4.3M 70.6 --> 51.2 63.0 --> 42.9
*EfficientNet-B0 77.692%,21M 70.486%,5.3M 134.3 --> 113.0 128.7 --> 100.4
EfficientNet-B1 78.642%,30M 73.546%,7.8M 199.5 --> 166.9 185.2 --> 145.9
regnet_x_400mf 72.834%,21.3M 72.660%,8.0M 83.2 --> 67.6 75.1 --> 58.0
训练量化

训练量化用来提升量化的精度,其速度和离线量化版本模型一致。建议优先使用离线量化方法,精度不够的情况下再使用训练量化方法。以下数据中的标准模型来自torchvision预训练模型,有些模型如efficientnet的训练成本较大,故未给出训练量化版本。用户自己训练的模型知道训练参数,结果一般可以更好。

模型 原始模型指标 压缩模型指标 备注
ResNet-18 69.758%,45M 69.840%,12M 训练一个epoch即可
MobileNet V2 71.878%,14M 71.762%,3.5M github: pytorch/vision reference/classification,复现命令:torchrun --nproc_per_node=8 train.py --model mobilenet_v2 --data-path /mnt/data/ --epochs 100 --lr 0.01 --wd 0.00004 --lr-step-size 30 --lr-gamma 0.1 --pretrained --quant --sync-bn -b 128
*MobileNet V3 Large 74.042%,21M 73.924%,5.4M 跳过第一层,github: pytorch/vision reference/classification,复现命令:torchrun --nproc_per_node=8 train.py --opt rmsprop --auto-augment imagenet --random-erase 0.2 --model mobilenet_v3_large --data-path /mnt/data/ --epochs 100 --batch-size 128 --lr 0.01 --wd 0.00001 --lr-step-size 30 --lr-gamma 0.1 --pretrained --quant --sync-bn
剪枝
模型 原始模型指标 压缩模型指标 ARMV7 (ms) ARMV8 (ms)
MobileNet V2 71.878%,14M 71.272%,2.8M,50% SIMDOC稀疏 69.1 --> 64.9 62.2 --> 58.8
*MobileNet V3 Large 74.042%,21M 73.568%,4.2M 50% SIMDOC稀疏 68.6 --> 66.5 64.9 --> 62.3
低秩分解
模型 原始模型指标 压缩模型指标 ARMV7 (ms) ARMV8 (ms)
MobileNet V2 71.878%,14M 69.874%,11M 69.1 --> 60.6 62.2 --> 54.6
MobileNet V3 Large 74.042%,21M 72.748%,18M 68.6 --> 59.7 64.9 --> 55.7
用户案例
模型 压缩方案 原始模型指标 压缩模型指标
神经渲染relight模型 低秩分解 30.1,138M,238ms 29.98,58M,169ms / 29.68,17M,116ms
语音识别AOA V3 EMA训练量化 cer 18.3,50.6M cer 18.26,18.7M

Pytorch模型压缩工具

线性超参数化工具
  • 环境要求

    1. python3

    2. PyTorch >= 1.2.0

  • 总体使用流程

    1. 通过本工具得到训练好的float onnx模型,以及MNN模型压缩参数文件

    2. 通过MNN转换工具,输入这两个文件,得到最终的MNN稀疏模型

  • 支持的op,使用建议

    1. 目前支持普通卷积nn.Conv2d(group=1,非1*1)的过参数化

    2. 该算法应在模型从零开始训练时使用,因为其中的参数会重新进行初始化

    3. 该算法的特点是:设计小模型 –> 训练线性过参数化大模型 –> 保存时合并为原始小模型结构。因此你可以设计一个小模型,然后用此算法训练提高小模型精度,最后推理部署仍使用小模型结构

    4. 得到的小模型后续仍可叠加剪枝和量化等压缩算法

    5. 参考论文:ExpandNets: Linear Over-parameterization to Train Compact Convolutional Networks

  • 使用方法:(注意:model,expand_model,merged_model均为深拷贝,有不同的内存空间,它们之间不互相影响)

    from mnncompress.pytorch import LOP
    # 定义你的模型结构,并初始化
    model = Net()
    # 对模型进行线性过参数化
    lop = LOP(model)
    # 扩大8倍,指定模型压缩参数文件,更多参数查看api文档
    expand_model = lop.linear_expand_layers(8, "compress_params.bin")
    # 使用线性过参数化之后的模型进行训练
    train_and_evaluate(expand_model, data)
    # 保存模型之前,将过参数化的模型合并,然后保存合并之后的模型 merged_model
    merged_model = lop.linear_merge_layers()
    
  • 相关API

    • LOP

      LOP(model) # model为原始模型nn.Module实例
      

      方法linear_expand_layers

      linear_expand_layers(expand_rate, compress_params_file, add_batchnorm=True, add_bypass=True, append=False)
      '''
      将原始模型进行线性过参数化
      参数:
          expand_rate: int,线性过参数化的倍数,一般取2,4,8,16等
          compress_params_file: str,模型压缩参数文件名
          add_batchnorm: bool,是否在线性过参数化时使用BN
          add_bypass: bool,是否在线性过参数化时添加bypass(残差连接)
          append: bool,是否将线性过参数化算法的参数追加到compress_params_file中去,为False,则将覆盖compress_params_file
      返回值:
          线性过参数化之后的模型,深拷贝,和原始model不共享内存
      '''
      

      方法linear_merge_layers

      linear_merge_layers()
      '''
      将过参数化之后的模型合并为原始模型结构
      参数:
          无参数   
      返回值:
          合并之后的模型,深拷贝,和过参数化模型不共享内存
      '''
      
低秩分解工具
  • 环境要求

    1. python3

    2. PyTorch >= 1.2.0

  • 总体使用流程

    1. 通过本工具得到分解之后的float onnx模型,以及MNN模型压缩参数文件

    2. 通过MNN转换工具,输入这两个文件,得到最终的MNN压缩模型

  • 支持的op,使用建议

    1. 目前支持nn.Conv2d(group>1不支持)和nn.Linear的分解

    2. 建议从已经训练好的float模型开始finetune

    3. 分解之后的模型调整好学习率,准确率一般会迅速恢复

  • 使用方法

    from mnncompress.pytorch import low_rank_decompose
    
    # 加载已经训练好的float模型
    model = Net()
    model.load_state_dict(torch.load("ori_float_model.pt"))
    
    # 将原始模型分解,然后用分解之后的模型进行finetune训练即可
    model = low_rank_decompose(model, "compress_params.bin")
    
  • 相关API

    • low_rank_decompose

      low_rank_decompose(model, compress_params_file, skip_layers=[""], align_channels=8, 
                      in_place=False, tucker_minimal_ratio=0.25, 
                      reserved_singular_value_ratio=0.5, append=False)
      '''
      参数:
          model: nn.Module实例,训练好的float模型
          compress_params_file: str, MNN模型压缩参数文件名
          skip_layers: List[str], 跳过不分解层的名字,需要是nn.Conv2d或者nn.Linear类型,如["features.conv1",]
          align_channels: int, 分解之后进行通道对齐的倍数
          in_place: 分解时是否使用原模型内存空间,若为False,则分解之前会对原模型进行深拷贝
          tucker_minimal_ratio: float 0~1, 卷积层tucker分解保留的最低通道数比例
          reserved_singular_value_ratio: svd分解保留的特征值之和占总特征值之和的比例
          append: bool, 是否将低秩分解算法的参数追加到compress_params_file中去,为False,则将覆盖compress_params_file
      
      返回值:
          分解之后的模型,nn.Module实例        
      '''
      
自动剪枝工具
  • 环境要求

    1. python3

    2. PyTorch >= 1.8.0

  • 总体使用流程

    1. 通过本工具得到训练好的float onnx模型,以及MNN模型压缩参数文件

    2. 通过MNN转换工具,输入这两个文件,得到最终的MNN稀疏模型**(如果直接部署稀疏的float模型,而不叠加量化,那么转换时需要使用MNNConvert的 –weightQuantBits 8 参数进行转换,才会进行稀疏编码,否则模型大小将不变)**

  • 支持的op,使用建议

    1. 目前支持torch.nn.Conv2d,torch.nn.Linear

    2. 优化器超参使用模型收敛阶段的超参,学习率可以在收敛阶段学习率的基础上再调小

  • 支持的剪枝算法

    1. SNIPLevelPruner:最细粒度的随机剪枝算法,稀疏单位为单个权值。一般需要剪枝比例达到80%以上才有加速,此方法主要进行压缩。

    2. SIMDOCPruner:稀疏单位为1*4的剪枝算法,一般需要30%以上剪枝比例才能加速,精度比通道剪枝好。

    3. TaylorFOChannelPruner:通道剪枝算法,此算法是将conv2d的一整个filter剪掉,因此此filter对应的输出通道会失效,剪枝完模型是一个规整的模型,不需要后端进行特殊加速,但为了将剪掉的filter从模型中真正去掉,需要使用MNNConverter进行转换,转换过程中会分析剪枝通道之间的依赖关系,并完成最终的转换。

  • 使用方法

    1. 建议从训练好的float模型进行finetune

    2. 创建Pruner对象,对原始模型进行转换,然后用转换之后的模型进行训练

    3. 在train过程中,调用pruner的do_pruning方法进行剪枝

    4. 导出MNN模型压缩参数文件,示例代码如下(关注其中pruner的用法):

    from mnncompress.pytorch.SNIP_level_pruner import SNIPLevelPruner
    from mnncompress.pytorch.SIMD_OC_pruner import SIMDOCPruner
    Pruner = SIMDOCPruner
    # 你的模型代码
    class Net(nn.Module):
        pass
    model = Net()
    # 加载已经训练好的模型
    model.load_state_dict(torch.load("ori_model.pt"))
    model.to(device)
    # 将模型进行转换,并使用转换后的模型进行训练,测试
    # 更多配置请看API部分
    pruner = SIMDOCPruner(model, total_pruning_iterations=1, sparsity=0.6, debug_info=False)
    def train(model, data, optimizer, pruner)
        model.train()
        for d, t in data:
            optimizer.zero_grad()
            output = model(d)
            loss = F.nll_loss(output, t)
            loss.backward()
            optimizer.step() 
            # step之后调用pruner的剪枝方法
            pruner.do_pruning()
        # 获取当前剪枝比例
        print(pruner.current_prune_ratios())
    for epoch in range(1, epochs + 1):
        train(model, data, optimizer, pruner)
        test(model, data)
    # 保存模型
    model.eval()
    torch.save(model.state_dict(), "pruned_model.pt")
    x = torch.randn(input_shape).to(device)
    torch.onnx.export(model, x, "pruned_model.onnx")
    # 保存MNN模型压缩参数文件,应在剪枝完毕之后进行,建议在保存模型时调用
    pruner.save_compress_params("compress_params.bin", append=False)
    
    1. 模型稀疏之后,可以进一步使用PyTorch训练量化工具进行量化,得到稀疏量化模型。也可以直接使用此float稀疏的模型进行推理,需要在MNN模型转换时指定剪枝得到的MNN模型压缩参数文件,并进行权值量化(才会进行稀疏编码):

    mnnconvert --modelFile pruned_model.onnx  --MNNModel pruned_model.mnn --framework ONNX --bizCode MNNTest --compressionParamsFile compress_params.bin --weightQuantBits 8
    
  • 相关API

    • SNIPLevelPruner / SIMDOCPruner / TaylorFOChannelPruner

      Pruner(model, sparsity=0.5, total_pruning_iterations=1, config_file=None, debug_info= False, 
          prune_finetune_iterations=0, max_prune_ratio=0.99, 
          align_channels=4  # only for 'TaylorFOChannelPruner'
          ):
      '''
      参数:
      model:Module,pytorch模型
      sparsity:float,0~1,模型总体剪枝比例
      total_pruning_iterations:int,指定多少个iteration将指定比例的连接剪掉
      config_file:str,可先运行一次Pruner的do_pruning方法,得到搜索出来的剪枝比例yml文件之后,
                      然后微调此yml文件中的各层剪枝比例,将修改后的文件名通过此参数传入,可控制各层剪枝比例
      debug_info:是否输出debug信息
      prune_finetune_iterations: int, 指定剪枝进行一步,finetune多少步,如指定为99,则剪枝一次,
                                                                      finetune99次,在finetune的99次过程中剪枝mask不变
      max_prune_ratio: float, 指定各层最大剪枝比例
      align_channels: int, 仅通道剪枝算法TaylorFOChannelPruner有此参数,用于指定剪枝之后对齐的通道数
      
      方法和属性:
      do_pruning(result_file = "found_prune_ratios.yml"):进行一次剪枝,在训练optimizer.step之后调用,搜索到的各层剪枝比例将保存到此文件中
      current_prune_ratios():返回当前各层剪枝比例
      save_compress_params(filename, append=False): 保存MNN模型压缩参数到filename文件
                                                  append 表示是否将剪枝参数信息append到传入的文件中,如果为false,则将新建或覆盖该文件,
                                                  append=True 表示剪枝之前还做了其他模型压缩操作,filename文件中已存在相关压缩信息
      '''
      
权值量化工具
  • 环境要求

    1. python3

    2. PyTorch >= 1.8.0

  • 总体使用流程

    1. 通过本工具得到训练好的float onnx模型,以及MNN模型压缩参数文件

    2. 通过MNN转换工具,输入这两个文件,得到最终的MNN量化模型

  • 支持的op,使用建议

    1. 目前支持torch.nn.Conv2d,torch.nn.Linear的量化

    2. 仅对权值进行量化,一般8bit权值量化并不需要finetune,直接用MNN的转换工具的“–weightQuantBits”进行转换即可,但也可使用本工具进行测试精度,或者finetune到更低的bit数,如4bit,2bit,推理速度和float一致

    3. 可与剪枝工具叠加使用

  • 使用方法

    1. 建议从训练好的float模型进行finetune

    2. 创建WeightQuantizer对象,对原始模型进行转换,然后用转换之后的模型进行训练

    3. 保存onnx模型之前,去掉插入的节点

    4. 保存onnx模型之后,导出MNN模型压缩参数文件,示例代码如下(关注其中quantizer的用法):

    from mnncompress.pytorch import WeightQuantizer
    # 你的模型代码
    class Net(nn.Module):
        pass
    model = Net()
    # 加载已经训练好的模型,可以是剪枝之后的
    model.load_state_dict(torch.load("pruned_model.pt"))
    # 将模型进行转换,并使用转换后的模型进行训练,测试
    # 更多配置请看API部分
    quantizer = WeightQuantizer(model, bits=8)
    quant_model = quantizer.convert()
    quant_model.to(device)
    for epoch in range(1, epochs + 1):
        # 每次训练之前加上这一句,准备好量化训练图
        quantizer.resume_wq_graph()
        train(quant_model, data, optimizer)
        test(quant_model, data)
        if 触发模型保存条件:
            # 保存模型之前去掉插入的节点,恢复原模型结构
            quantizer.strip_wq_ops()
            # 保存模型,注意index,即模型和保存MNN模型压缩参数文件是一一对应的
            quant_model.eval()
            torch.save(quant_model.state_dict(), "quant_model_index.pt")
            x = torch.randn(input_shape).to(device)
            torch.onnx.export(quant_model, x, "quant_model_index.onnx")
            # 保存MNN模型压缩参数文件,如果进行量化的模型有剪枝,
            # 请将剪枝时生成的MNN模型压缩参数文件 "compress_params.bin" 文件在下方传入,并将 append 设置为True
            quantizer.save_compress_params("compress_params_index.bin", append=False)
    
    1. 训练完之后,使用MNN转换器进行模型转换,提供上面得到的MNN压缩参数文件,并使用--weightQuantBits指定量化比特数:

    mnnconvert --modelFile quant_model_index.onnx  --MNNModel weight_quant_model.mnn --framework ONNX --bizCode MNNTest --compressionParamsFile compress_params_index.bin --weightQuantBits 8
    
  • 相关API

    • WeightQuantizer

      WeightQuantizer(model, bits = 8, debug_info = False, mode = 'symmetric')
      '''
      参数:
      model:Module,pytorch模型
      bits:int,指定权值量化比特数
      debug_info:bool,是否输出debug信息
      mode:"symmetric"或"asymmetric",采用对称或者非对称量化,目前仅支持对称量化
      
      方法和属性:
      convert():返回用于训练量化的模型
      strip_wq_ops():去掉插入的权值量化op,恢复原始模型结构
      resume_wq_graph():strip_wq_ops保存模型之后,如果还要继续量化训练需加上这一句,以恢复量化训练图,由此支持边训练边保存
      save_compress_params(filename, append=False):
              用于保存MNN转换时需要用的模型压缩信息
              filename:str,MNN模型压缩参数将保存到这个文件名指定的文件中
              append:bool,是否将量化参数追加到filename文件中。如果进行量化的模型有剪枝,请将剪枝时通过save_compress_params生成的剪枝信息文件通过此参数传入,并将 append 设置为True
      '''
      
训练量化工具
  • 环境要求

    1. python3

    2. PyTorch >= 1.8.0

  • 总体使用流程

    1. 通过本工具得到训练好的float onnx模型,以及MNN模型压缩参数文件

    2. 通过MNN转换工具,输入这两个文件,得到最终的MNN量化模型

  • 支持的op,使用建议

    1. 建议优先试验此工具的离线量化功能,精度如果可以则不需要训练量化,速度快,节省时间

    2. 目前支持torch.nn.Conv2d,torch.nn.Linear的量化

    3. 优化器超参使用模型收敛阶段的超参,学习率可以在收敛阶段学习率的基础上再调小

    4. 模型的第一层或者前面几层对精度影响较大,可以尝试跳过不进行量化

    5. relu6建议使用nn.ReLU6,F.relu6使用opset 9导出到onnx会有问题

  • 支持的训练量化算法

    1. LSQQuantizer: 改进naive QAT算法,scale会在网络训练中学习更新

  • 使用方法

    1. 建议从训练好的float模型进行finetune

    2. 创建Quantizer对象,对原始模型进行转换,然后用转换之后的模型进行训练

    3. 保存onnx模型之前,去掉插入的节点

    4. 保存onnx模型之后,导出MNN模型压缩参数文件,示例代码如下(关注其中quantizer的用法):

    from mnncompress.pytorch import LSQQuantizer
    Quantizer = LSQQuantizer
    # 你的模型代码
    class Net(nn.Module):
        pass
    model = Net()
    # 加载已经训练好的模型,可以是剪枝之后的
    model.load_state_dict(torch.load("pruned_model.pt"))
    # 将模型进行转换,并使用转换后的模型进行训练,测试
    # retain_sparsity=True表示待量化的float模型是稀疏模型,希望叠加量化训练
    # 更多配置请看API部分
    # 注意在model还在cpu上,任何分布式还未生效前调用
    quantizer = Quantizer(model, retain_sparsity=False)
    quant_model = quantizer.convert()
    # 单机或分布式,根据你已有的代码来
    quant_model.to(device)
    for epoch in range(1, epochs + 1):
        # 每次训练之前加上这一句,准备好量化训练图
        quantizer.resume_qat_graph()
        train(quant_model, data, optimizer)
        test(quant_model, data)
        if 触发模型保存条件:
            # 保存模型之前去掉插入的节点,恢复原模型结构
            quantizer.strip_qat_ops()
            # 保存模型,注意index,即模型和保存MNN模型压缩参数文件是一一对应的
            quant_model.eval()
            torch.save(quant_model.state_dict(), "quant_model_index.pt")
            x = torch.randn(input_shape).to(device)
            torch.onnx.export(quant_model, x, "quant_model_index.onnx")
            # 保存MNN模型压缩参数文件,如果进行量化的模型有剪枝,
            # 请将剪枝时生成的MNN模型压缩参数文件 "compress_params.bin" 文件在下方传入,并将 append 设置为True
            quantizer.save_compress_params("quant_model_index.onnx", "compress_params_index.bin", append=False)
    
    1. 将onnx模型和生成的 “compress_params.bin” 文件输入到MNN转换工具中进行转换,得到最终的MNN量化模型:

    mnnconvert --modelFile quant_model.onnx  --MNNModel quant_model.mnn --framework ONNX --bizCode MNNTest --compressionParamsFile compress_params.bin
    
  • 作为离线量化工具使用

    • 去掉训练过程中的参数更新部分,并将LSQQuantizermode设置为offline(查看下方API),其他步骤和上述一致,此时该工具即成为离线量化工具,直接灌入训练数据即可完成离线量化:

      # 注释掉训练部分中的这一句即可
      # optimizer.step()
      

      可以先尝试这个,如果精度可接受,可以节省训练时间。

  • 相关API

    • LSQQuantizer

      LSQQuantizer(model, skip_quant_layers = [], bits = 8, debug_info = False, mode = 'online', retain_sparsity=False)
      '''
      参数:
      model:Module,pytorch模型
      skip_quant_layers:[str,],不进行量化的module的名字,可通过dict(model.named_modules()).keys()获得,如['model.conv1', 'model.conv2'],需要是nn.Conv2d或者nn.Linear
      bits:int,指定量化比特数
      debug_info:bool,是否输出debug信息
      mode:"online"或"offline",训练量化时选online,离线量化时选offline
      retain_sparsity: bool,为True表示待量化的float模型是稀疏模型,希望叠加量化训练
      
      方法和属性:
      convert():返回用于训练量化的模型
      strip_qat_ops():去掉插入的训练量化op,恢复原始模型结构
      resume_qat_graph():strip_qat_ops保存模型之后,如果还要继续量化训练需加上这一句,以恢复量化训练图,由此支持边训练边保存
      save_compress_params(onnx_inference_model_file, filename, append=False):
              用于保存MNN转换时需要用的模型压缩信息
              onnx_inference_model_file:str,去掉插入的训练量化节点之后保存的onnx推理模型名字
              filename:str,MNN模型压缩参数将保存到这个文件名指定的文件中
              append:bool,是否将量化参数追加到filename文件中。如果进行量化的模型有剪枝,请将剪枝时通过save_compress_params生成的剪枝信息文件通过此参数传入,并将 append 设置为True
      '''
      

Tensorflow 1.x模型压缩工具

低秩分解工具
  • 环境要求

    1. 支持TensorFlow1.X,在TensorFlow1.X最后一个版本1.15.3上测试通过;TensorFlow2.0暂未测试

  • 总体使用流程

    1. 通过本工具得到分解训练之后的float pb模型,以及MNN模型压缩参数文件

    2. 通过MNN转换工具,输入这两个文件,得到最终的压缩模型

  • 支持的op,使用建议

    1. 支持 Conv2D, MatMul两种op

    2. 建议从已经训练好的float模型开始finetune

    3. 优化器超参使用模型收敛阶段的超参

  • 使用方法

    from mnncompress.tensorflow import get_op_weight_values, low_rank_decompose
    # first, 构建前向网络模型
    build_model()
    # 新建一个临时session,此session会在分解之后关闭掉
    temp_sess = tf.Session()
    # 给原模型中的变量用checkpoint赋值
    saver = tf.train.Saver()
    saver.restore(sess, 'save/model.ckpt')
    # 获得图中的权值numpy数值
    get_op_weight_values(temp_sess, "model_weights.npy")
    temp_sess.close()
    # 将模型图进行分解
    low_rank_decompose(tf.get_default_graph(), "model_weights.npy", "compress_params.bin")
    # 此时图已经分解,构建optimizer,正常训练即可
    optimizer = ...
    # 恢复模型中未分解层的参数
    saver = tf.train.Saver()
    saver.restore(sess, 'save/model.ckpt')
    # 初始化分解之后的层的参数
    initializers = tf.global_variables_initializer()
    
  • 相关API

    • get_op_weight_values

      get_op_weight_values(sess, npy_file_name)
      '''
      参数:
          sess: 临时tf.Session,获取到权值numpy数值之后会被关掉
          npy_file_name: str, 模型中权值的数值会被保存到此npy文件中,用于low_rank_decompose分解
      
      返回值:
      
      '''
      
    • low_rank_decompose

      low_rank_decompose(graph, weight_npy_file, compress_params_file, skip_layers=[""], align_channels=8, 
                      tucker_minimal_ratio=0.25, 
                      reserved_singular_value_ratio=0.5, append=False)
      
      '''
      参数:
          graph: 模型前向图
          weight_npy_file: str, get_op_weight_values函数得到的模型权值npy文件名
          compress_params_file: str, MNN模型压缩参数文件名
          skip_layers: List[str], 跳过不分解层的名字,需要是nn.Conv2d或者nn.Linear类型,如["features.conv1",]
          align_channels: int, 分解之后进行通道对齐的通道数
          tucker_minimal_ratio: float 0~1, 卷积层tucker分解保留的最低通道数比例
          reserved_singular_value_ratio: svd分解保留的特征值之和占总特征值之和的比例
          append: bool, 是否将低秩分解算法的参数追加到compress_params_file中去,为False,则将覆盖compress_params_file
      
      返回值:
          无返回值,此时当前graph已经被分解
      '''
      
自动剪枝工具
  • 环境要求

    1. 支持TensorFlow1.X,在TensorFlow1.X最后一个版本1.15.3上测试通过;TensorFlow2.0暂未测试

  • 大致算法原理

  • 支持的op,使用建议

    1. 目前支持Conv2D,带可学习参数MatMul需要使用reshape+1*1 Conv2D卷积的组合来实现

  • 支持的剪枝算法

    1. SNIPLevelPruner:最细粒度的随机剪枝算法,稀疏单位为单个权值。一般需要剪枝比例达到80%以上才有加速,此方法主要进行压缩。

    2. SIMDOCPruner:稀疏单位为1*4的剪枝算法,一般需要30%以上剪枝比例才能加速,精度比通道剪枝好。

    3. TaylorFOChannelPruner:通道剪枝算法,以卷积filter为单位进行剪枝,剪枝完仍然是整齐的模型,无须框架底层特殊实现,但需要用MNN转换工具进行转换才能获得最终剪枝模型。

  • 使用方法

    1. 前提:一个预先训练好的模型checkpoint(也可以从头开始训练稀疏模型)

    2. 首先恢复出已经训练好的checkpoint,示例代码:

    import tensorflow as tf
    from mnncompress.tensorflow import SNIPLevelPruner, SIMDOCPruner, TaylorFOChannelPruner
    from mnncompress.tensorflow import SensitivityAnalyzer
    # 构图
    ...
    # 恢复checkpoint
    sess = tf.Session()
    sess.run(tf.global_variables_initializer())
    saver = tf.train.Saver()
    saver.restore(sess, 'original_model/model.ckpt')
    
    1. 获取网络中的(gradient,variable)pair

    # 模型中用到的优化器,例如,optimizer = tf.train.AdamOptimizer(1e-4)
    optimizer = ...
    # 获取(gradient,variable)pair
    gradients_vars = optimizer.compute_gradients(loss)
    # 模型更新op
    train_step = optimizer.apply_gradients(gradients_vars, global_step=tf.train.get_or_create_global_step())
    
    1. 计算模型中各网络连接的敏感度,并保存为npy文件。主要计算过程是喂几个batch数据进网络,计算得到各参数的梯度:

    from mnncompress.tensorflow import SensitivityAnalyzer
    analyzer = SensitivityAnalyzer(gradients_vars)
    for i in range(10):
        batch_x, batch_y = mnist.train.next_batch(50)
        batch_x = np.reshape(batch_x,(-1, 28, 28,1))
        feed_dict={x_PH: batch_x, labels: batch_y}
        # 计算各参数的梯度
        gv_numpy = sess.run(gradients_vars, feed_dict=feed_dict)
        analyzer.analyze(gv_numpy)
    analyzer.save_sensitivity_npy("model_sens.npy")
    
    1. 构建Pruner,示例代码:

    target_sparsity = 0.6
    total_prune_iterations = 1
    # 可选SNIPLevelPruner, SIMDOCPruner, TaylorFOChannelPruner等算法,更多配置参数参见下方api文档
    pruner = SNIPLevelPruner("model_sens.npy", sess.graph, target_sparsity, total_prune_iterations, debug_info=True)
    
    1. 正常训练,注意,训练时每次反向更新之后,执行一下剪枝步骤即可**(第6行)(测试时不需要)**,这样保存下来的模型就已经是稀疏的了,示例代码:

    for data in training_dataset:
        feed_dict = {x: batch_x, labels: batch_y}
        sess.run([train_step], feed_dict=feed_dict)
        # 反向更新之后,执行剪枝,测试时不需要
        pruner.do_pruning(sess)
    
    1. 测试过程中保存一下剪枝相关的参数文件:

    pruner.save_compress_params("compress_params.bin", sess)
    
    1. 训练完成保存frozen pb即可得到最终稀疏pb模型,示例代码:

    output_graph_def = tf.graph_util.convert_variables_to_constants(sess, sess.graph_def, output_node_names=output_names)
    f = open("frozen.pb", 'wb')
    f.write(output_graph_def.SerializeToString())
    
    1. 模型稀疏之后,可以进一步使用TF训练量化工具进行量化,得到稀疏量化模型。也可以直接使用此float稀疏的模型进行推理,需要在MNN模型转换时指定第6步中的bin文件,并进行权值量化(才会进行稀疏编码):

    mnnconvert --modelFile frozen.pb  --MNNModel quant_model.mnn --framework TF --bizCode AliNNTest --compressionParams compress_params.bin --weightQuantBits 8
    
  • 相关API

    • SNIPLevelPruner/SIMDOCPruner/TaylorFOChannelPruner

      Pruner(sensitivity_npy_file, graph=None, sparsity=0.5, total_pruning_iterations=1, config_file=None, debug_info= False, 
          prune_finetune_iterations=0, max_prune_ratio=0.99, 
          prune_ratio_result_file="算法名_found_prune_ratios.yml", 
          align_channels=4 # TaylorFOChannelPruner特有参数
          )
      '''
      参数:
      sensitivity_npy_file: 敏感度分析得到的npy文件
      graph: tensorflow计算图,如为None,则默认使用tf.get_default_graph()来获取计算图
      sparsity: float,0~1, 模型的总体剪枝比例
      total_pruning_iterations: int, 多少个iteration内将指定比例的参数稀疏掉
      config_file: str,可先运行一次Pruner的do_pruning方法,得到搜索出来的剪枝比例yml文件,
                      然后微调此yml文件中的各层剪枝比例,将修改后的文件名通过此参数传入,可控制各层剪枝比例
      debug_info: bool,是否输出debug信息
      prune_finetune_iterations: int,剪枝一次,finetune此参数指定步数,然后再剪枝一次。
                                      如果total_pruning_iterations设为10,prune_finetune_iterations设为99,则1000个iteration之后才执行完毕剪枝过程
      max_prune_ratio: 0~1,控制各层最大的剪枝比例
      prune_ratio_result_file: str,指定搜索得到的剪枝比例保存的文件名
      align_channels: int,指定通道剪枝之后对齐的通道数
      
      方法和属性:
      do_pruning(sess): 训练时每更新一次模型参数之后,执行剪枝步骤,测试时不需要
      save_compress_params(filename, sess, append=False): 保存剪枝参数信息到filename文件,sess为维护训练量化图的session, 
                                                          append 表示是否将剪枝参数信息append到传入的proto文件中,如果为false,则将新建或覆盖该文件,
                                                          append=True 表示剪枝之前还做了其他模型压缩操作,filename文件中已存在相关压缩信息
      '''
      
权值量化工具
  • 环境要求

    1. 支持TensorFlow1.X,在TensorFlow1.X最后一个版本1.15.3上测试通过;TensorFlow2.0暂未测试

  • 大致算法原理

    • 仅对权值进行int8量化,一般权值量化并不需要finetune,直接用MNN的转换工具的“–weightQuantBits”进行转换即可,但也可使用本工具进行测试精度,或者finetune到更低的bit数;可与剪枝工具叠加使用;

  • 支持的op,使用建议

    1. 权值量化8bit情况下一般不会损失精度,不需要训练,而模型参数压缩4倍左右,推理速度和float一致

    2. 目前支持Conv2D, DepthwiseConv2dNative,带参数MatMul

    3. 可以结合剪枝一起使用,以获得更大的压缩倍数

  • 使用方法

    1. 读取已经训练好的float模型的checkpoint,然后插入权值量化节点,注意添加第22行代码,示例代码:

    from mnncompress.tensorflow.weight_quantizer import WeightQuantizer, strip_wq_ops
    # 构建 前向 网络模型
    build_model_architecture()
    # 在定义反向计算之前构建weight quantizer,向图中插入相关节点
    graph = tf.get_default_graph()
    weight_quantizer = WeightQuantizer(graph, bits=4, debug_info=True)
    # 定义optimizer
    opt = ...
    # 恢复原模型中的变量
    saver = tf.train.Saver()
    sess = tf.Session()
    # 给原模型中的变量用checkpoint赋值
    saver.restore(sess, 'save/model.ckpt')
    # 训练之前初始化相关变量,放在restore之后
    weight_quantizer.init(sess)
    
    1. 正常训练,注意添加第5行代码,示例代码:

    for data in training_dataset:
        feed_dict = {x: batch_x, labels: batch_y}
        sess.run([train_step], feed_dict=feed_dict)
    
        weight_quantizer.update(sess)
    
    1. 训练完成,去掉插入的权值量化算子**(第2行代码)**,保存frozen pb,示例代码:

    # 去掉插入的权值量化算子
    strip_wq_ops()
    output_graph_def = tf.graph_util.convert_variables_to_constants(sess, sess.graph_def, output_node_names=output_names)
    f = open("frozen.pb", 'wb')
    f.write(output_graph_def.SerializeToString())
    
    1. 使用MNN转换工具的“–weightQuantBits numBits”选项将frozen pb转换成MNN模型,其中numBits为WeightQuantizer中的bit数,得到的MNN模型的精度和frozen.pb一致

    mnnconvert --modelFile frozen.pb  --MNNModel weight_quant_model.mnn --framework TF --bizCode MNNTest --compressionParams compress_params.bin --weightQuantBits 8
    
  • 相关API

    • WeightQuantizer

      WeightQuantizer(graph, bits=8, debug_info=False)
      '''
      参数:
      graph: tensorflow模型图
      bits: 权值量化的bit数
      debug_info: bool,是否输出debug信息
      
      方法和属性:
      init(sess): 训练之前初始化内部用到的相关变量
      update(sess): 更新内部状态
      save_compress_params(filename, append=False):
          用于保存MNN转换时需要用的模型压缩信息
          filename:str,MNN模型压缩参数将保存到这个文件名指定的文件中
          append:bool,是否将量化参数追加到filename文件中。如果进行量化的模型有剪枝,请将剪枝时通过save_compress_params生成的剪枝信息文件通过此参数传入,并将 append 设置为True
      '''
      
    • strip_wq_ops()

      '''
      去掉权值量化相关算子
      '''
      
训练量化工具
  • 环境要求

    1. 支持TensorFlow1.X,在TensorFlow1.X最后一个版本1.15.3上测试通过;TensorFlow2.0暂未测试

  • 总体使用流程

    1. 通过本工具得到训练好的float pb模型,以及量化参数文件

    2. 通过MNN转换工具,输入这两个文件,得到最终的量化模型

  • 支持的op,使用建议

    1. 建议从训练好的float模型进行finetune

    2. 目前支持 Conv2D, DepthwiseConv2dNative,带参数MatMul(EMA,LSQ量化算法支持)

    3. 使用OAQ量化算法需要量化矩阵乘 MatMul ,请将其利用 reshape 和 **Conv2D(1 * 1 卷积) **组合实现

    4. 优化器超参使用模型收敛阶段的超参,学习率可以适当调节一下

    5. 模型的第一层或者前面几层对精度影响较大,可以尝试跳过不进行量化

  • 支持的训练量化算法

    1. EMAQuantizer: naive QAT算法,scale采用滑动平均更新

    2. LSQQuantizer: 改进QAT算法,scale会在网络训练中学习更新

    3. OAQQuantizer: 改进LSQ算法,int8计算中累加到int16而不是通常的累加到int32,在低端手机上可以获得比普通量化30%~50%的加速,低端设备上加速更明显

  • 使用方法

    1. 搭建好网络之后,在session运行之前,构造 Quantizer 对象,指定相关配置项,构造此对象的同时会向前面搭建好的网络中插入fake quant节点,用于实现训练量化功能,示例代码:

    from mnncompress.tensorflow import EMAQuantizer, strip_MNN_QAT_ops, get_qat_state_placeholder, MNN_QAT_variables
    from mnncompress.tensorflow import LSQQuantizer, strip_MNN_QAT_ops, get_qat_state_placeholder, MNN_QAT_variables
    from mnncompress.tensorflow import OAQQuantizer, strip_MNN_QAT_ops, get_qat_state_placeholder, MNN_QAT_variables
    QATQuantizer = EMAQuantizer # 也可以改为使用LSQQuantizer或者OAQQuantizer
    # first, 构建前向网络模型
    build_model()
    # second, 插入fake quant节点,注意此句位置应在构建optimizer之前
    # 更多配置详见API部分
    quantizer = QATQuantizer(tf.get_default_graph())
    # 构建optimizer,反向网络
    opt = ...
    # 只恢复原模型中的变量
    saver = tf.train.Saver(var_list=[v for v in tf.all_variables() if v not in MNN_QAT_variables()])
    sess = tf.Session()
    # 初始化所有变量,包括MNN训练量化中用到的变量
    sess.run(tf.global_variables_initializer())
    # 给原模型中的变量用checkpoint赋值
    saver.restore(sess, 'save/model.ckpt')
    # 注意:该saver只维护了原来的模型的变量,要保存插入fake quant的模型,需要新建一个saver
    new_saver = tf.train.Saver()
    
    1. 正常训练,注意,训练和测试过程中需要给quantizer内部的 MNN_QAT_is_training 这个 placeholder 赋值,训练时赋值True,测试时赋值False;注意添加第1行、第7行代码:

    quantizer.init(sess)
    quant_state = quantizer.is_training # 或者 quant_state = get_qat_state_placeholder()
    for data in dataset:
        feed_dict = {x: batch_x, labels: batch_y, quant_state: True}
        sess.run(train_step, feed_dict=feed_dict)
        quantizer.update(sess)
    
    1. 测试过程中保存训练得到的量化参数文件,示例代码:

    # 如果先进行了剪枝训练,那么需要将剪枝中得到的 "compress_params.bin" 文件在下方传入,并设置 append=True
    quantizer.save_compress_params("compress_params.bin", sess, append=False)
    
    1. 训练完之后,我们得到了一些checkpoint文件和对应的量化参数文件,现在我们要来得到最终推理用的frozen pb了,示例代码:

    from mnncompress.tensorflow import EMAQuantizer, strip_MNN_QAT_ops
    # 保存frozen pb之前去掉(绕过)插入的量化节点,传入 strip_MNN_QAT_ops 的graph参数
    # 也可以是从checkpoint读取出来的graph
    strip_MNN_QAT_ops(sess.graph)
    output_graph_def = tf.graph_util.convert_variables_to_constants(sess, sess.graph_def, output_node_names=output_names)
    f = open("frozen.pb", 'wb')
    f.write(output_graph_def.SerializeToString())
    
    1. 使用MNN模型转换工具,输入frozen pb和量化参数文件,得到最终的量化模型,示例:

    mnnconvert --modelFile frozen.pb  --MNNModel quant_model.mnn --framework TF --bizCode AliNNTest --compressionParamsFile compress_params.bin
    
  • 测试训练量化结果需注意的地方

    1. 如果你是训练和测试分开两个文件测试时用python代码重新构图,在构图时给bn的is_training又直接赋值为python bool False,然后加载训练的checkpoint进行测试,那你重新构建的图里面是没有训练量化节点的,此时需要在你构图之后,创建session之前,恢复变量之前,你再新建一个和训练时候一样的Quantizer对象(复制粘贴),向测试图中插入训练量化节点即可,然后根据训练时候保存的checkpoint恢复训练得到的变量,就可以了,当然测试的时候也需要给 quant_state 赋值为False,即可。

    2. 另外注意,有的使用者在训练和测试的时候给模型加了不同的scope,这时训练的到的compress_params.bin就不能用了,因为tensor名字对不上,此时,只需要在测试之后转pb之前加入下面两句就可以了:

    quantizer.save_compress_params("compress_params.bin", sess)
    strip_MNN_QAT_ops(sess.graph)
    output_graph_def = tf.graph_util.convert_variables_to_constants(sess, sess.graph_def, output_node_names=output_names)
    f = open("frozen.pb", 'wb')
    f.write(output_graph_def.SerializeToString())
    
  • 相关API

    • EMAQuantizer/OAQQuantizer/LSQQuantizer

      Quantizer(graph=None, is_training = None, skip_quant_layers = [], skip_quant_flag = [], bits = 8, 
              min_clamp_value = 0, # 此参数仅OAQQuantizer有
              debug_info = False, retain_sparsity=False)
      '''
      参数:
      graph: 需要进行训练量化的模型图
      is_training: None or Python bool,为None时,Quantizer的is_training属性是一个placeholder,用于控制量化状态,需要在feed_dict中额外传值;为True或False时,则和用户代码中的训练或测试flag一样,is_training是一个constant,不需要在feed_dict中额外赋值
      skip_quant_layers: [str], 指定图中跳过量化的op的名字
      skip_quant_flag: [str],跳过op名字中含有指定字符串的op的量化,例如想要跳过模型中整个decoder1的量化,这些op的名字中都含有"decoder1",那么在此参数中指定["decoder1"]即可
      bits: int, 指定量化比特数,目前仅支持8bit量化
      min_clamp_value: int, OAQQuantizer特有参数,用于指定最小的量化范围,例如最低量化为6bit,则此数值设置为2^(6-1) - 1 = 31
      debug_info: bool,是否输出debug信息
      retain_sparsity: bool, 是否保持量化原始float模型的稀疏结构不变,剪枝之后叠加量化时,需要将此参数设为True
      '''
      # 为获得相关层的名字,我们提供了一个帮助函数:
      import mnncompress.tensorflow.helpers as helper
      helper.get_op_name_of_type(graph, ['Conv2D', 'DepthwiseConv2dNative', 'MatMul'])
      '''
      方法和属性:
      is_training: property,返回Quantizer内部的 MNN_QAT_is_training 这个 placeholder
      strip_qat_ops(): 保存froze pb前去掉(绕过)插入的训练量化节点
      save_compress_params(filename, sess, append=False): 保存量化参数信息到filename文件,sess为维护训练量化图的session, 
                                                          append 表示是否将量化参数信息append到传入的proto文件中,如果为false,则将新建或覆盖该文件,
                                                          append=True 表示量化之前还做了其他模型压缩操作,如剪枝,filename文件中已存在相关压缩信息
      init(sess): 初始化Quantizer内部信息
      update(sess): 更新内部需要的信息
      '''
      
    • strip_MNN_QAT_ops

      strip_MNN_QAT_ops(graph)
      '''
      此函数用于保存froze pb前去掉(绕过)插入的训练量化节点
      如果训练和测试分开来做,意味着测试时是读取训练得到的checkpoint然后进行测试的
      此时图中已经有了训练量化的fake quant节点,但无法使用Quantizer维护的原图信息进行
      保存froze pb前去掉(绕过)插入的训练量化节点 的功能
      此时可以使用该函数来达到去掉(绕过)fake quant节点的目的
      '''
      
    • get_qat_state_placeholder

      get_qat_state_placeholder(graph=None)
      # 用于在无quantizer对象的情况下获取MNN训练量化工具内部标志训练或者测试的placeholder
      
    • MNN_QAT_variables

      MNN_QAT_variables()
      # 用于获取MNN训练量化特有的量化variable集合
      
Hooks

TensorFlow推荐使用tf.train.MonitoredSessiontf.train.MonitoredTrainingSession进行训练,并使用tf.train.SessionRunHook进行训练流程控制。为方便此类用户使用,我们提供了相关压缩工具的hook,初始化hook之后,即可使用。 这些hook的构造方法和使用的压缩算法文档中一致,在此不再详述。 注意,有些hook需要在构造反向图之前创建,例如量化的hook。目前提供的hook有:

  1. SNIPLevelPrunerHook

  2. SIMDOCPrunerHook

  3. TaylorFOChannelPrunerHook

  4. LowRankDecompositionHook

  5. EMAQuantizerHook

  6. OAQQuantizerHook

  7. LSQQuantizerHook

可视化工具

可视化的效果: _images/visual.png可视化效果

在详细调研了市面上比较主流的可视化工具后,Netron是一款受众面较多、兼容多款模型的可视化模型,同时它还具有跨平台支持、Python模块支持的能力。因此,在研究了一番它的设计和架构并考虑后续MNN自身的演进,我们决定官方维护MNN模型的可视化能力并将其作为Pull Request合并,大家可以放心使用啦。

功能列表

- 支持加载`.mnn`模型 。
- 支持将可视化的图导出成图片保存。
- 支持拓扑结构的展示、`Operator`/`Input`/`Output`的内容展示。
- 支持结构化的`weight`,`scale`,`bias`等数据的展示,**并支持将此类数据持久化保存**。

使用方式(Release版本)

  • 下载地址

  • macOS: 下载 .dmg文件 或者 brew cask install netron

  • Linux: 下载 .AppImage或者.deb文件.

  • Windows: 下载.exe文件.

  • Pythonpip install netron

使用开发版本

  • 对仓库地址:https://github.com/lutzroeder/netron,进行clone。始终使用master分支。

  • cd [your_clone_path]/netron

  • 安装npm,确保npm版本大于6.0.0

  • npm install

使用JavaScript调试

npx electron ./(如果这步失败,单独npm install -g npx

使用Python调试

python3 setup.py build
export PYTHONPATH=build/lib:${PYTHONPATH}
python3 -c "import netron; netron.start(None)"

遗留问题

加载超大模型可能渲染失败(几千个节点)

Python工具

Pymnn是MNN的Python版本,其中将部分MNN工具封装成了MNNTools,MNNTools模块主要有以下工具:

  • mnn

  • mnnconvert

  • mnnquant

mnn

mnn工具的功能是列出目前MNNTools支持的所有工具,使用如下:

mnn
mnn toolsets has following command line tools
    $mnn
        list out mnn commands
    $mnnconvert
        convert other model to mnn model
    $mnnquant
        quantize  mnn model

mnnconvert

mnnconvert是对MNNConvert的Python封装,参数使用可以参考,示例如下:

mnnconvert -f ONNX --modelFile mobilenetv2-7.onnx --MNNModel mobilenetv2-7.mnn --bizCode mobilenet
Start to Convert Other Model Format To MNN Model...
[11:34:53] :40: ONNX Model ir version: 6
Start to Optimize the MNN Net...
107 op name is empty or dup, set to Const107
108 op name is empty or dup, set to BinaryOp108
109 op name is empty or dup, set to Unsqueeze109
111 op name is empty or dup, set to Unsqueeze111
inputTensors : [ input, ]
outputTensors: [ output, ]
Converted Success!

mnnquant

mnnquant是对quantized.out的Python封装,具体用法可以参考quantized.out,示例如下:

cp /path/to/MNN # using MNN/resource/images as input
mnnquant shuffle.mnn shuffle_quant.mnn shuffle_quant.json  
[11:48:17] :48: >>> modelFile: shuffle.mnn
[11:48:17] :49: >>> preTreatConfig: shuffle_quant.json
[11:48:17] :50: >>> dstFile: shuffle_quant.mnn
[11:48:17] :77: Calibrate the feature and quantize model...
[11:48:17] :156: Use feature quantization method: KL
[11:48:17] :157: Use weight quantization method: MAX_ABS
[11:48:17] :177: feature_clamp_value: 127
[11:48:17] :178: weight_clamp_value: 127
[11:48:17] :111: used image num: 2
[11:48:17] :668: fake quant weights done.
ComputeFeatureRange: 100.00 %
CollectFeatureDistribution: 100.00 %
[11:48:36] :82: Quantize model done!

配置文件shuffle_quant.json内容如下:

{
    "format":"RGB",
    "mean":[
        103.94,
        116.78,
        123.68
    ],
    "normal":[
        0.017,
        0.017,
        0.017
    ],
    "width":224,
    "height":224,
    "path":"./resource/images/",
    "used_image_num":2,
    "feature_quantize_method":"KL",
    "weight_quantize_method":"MAX_ABS",
    "model":"shuffle.mnn"
}

代码风格

格式化工具

我们使用clang-formatgit-clang-format统一C、C++、Objective-C代码风格。代码风格使用工程根目录下的.clang-format文件描述。

在新增文件时,使用clang-format调整代码风格:

clang-format -i /path/to/new_file

在修改文件时,使用git-clang-format调整新增部分的代码风格:

cd /path/to/MNN
git-clang-format

代码风格

对于C、C++、Objective-C代码,我们使用Google代码风格。但是对下列项目作出调整:

项目 修改
AccessModifierOffset 访问权限修正偏移 由-1改为-4
AlignConsecutiveAssignments 连续赋值对齐 由false改为true
ColumnLimit 列宽限制 由80改为120
IndentWidth 缩进宽度 由2改为4
ObjCBlockIndentWidth ObjC Block缩进宽度 由2改为4
ObjCSpaceAfterProperty ObjC属性后保留空格 由false改为true
SpacesBeforeTrailingComments 行尾注释前空格数 由2改为1

命名约定

一般规则

在C、C++和ObjC中使用驼峰命名法,如CityCatbigDoghouse

前缀

private、protected的成员变量,以m为前缀,如mCat;全局变量、类静态变量,以g为前缀,如gWorld;所有非static C函数、汇编函数都需要以MNN为前缀。

可见性

所有需要对外暴露的函数、类,都需要使用MNN_PUBLIC标记。

最佳实践

临近释放原则

为降低内存泄露风险,申请临时内存和释放内存宜在相邻代码块内实现,即,应使用智能指针或AutoStorage类。

防御式编程

对于外部入参,应明确判定入参有效性,如:

MNN_PUBLIC struct MNNNet* MNNCreateNet(const char* path) {
    if (NULL == path) {
        MNN_PRINT("input path is NULL, failed to create net!\n");
        return NULL;
    }
    // ...
}

对于内部入参,宜使用MNN_ASSERT避免问题代码的产生,如:

void copyFloats(const float* input, float* output, int size) {
    MNN_ASSERT(NULL != input);
    MNN_ASSERT(NULL != output);
    for (int i = 0; i < size; ++i) {
        output[i] = input[i];
    }
}

禁止在没有注释适当理由的情况下,忽略错误入参,如:

void setUnitDimensions(const int* dims, int size) {
    if (NULL == dims) return; // should not directly return without comments
    for (int i = 0; i < size; ++i) {
        dims[i] = 1;
    }
}

注释

对于所有非Op头文件:

  1. class需要注释说明类的用途;

  2. 所有_非override_的_public_方法需要通过注释说明方法、各参数的用途和返回值信息(若有);

  3. 所有public成员变量(一般为结构体成员)需说明其用途;

示例:

/**
 * @brief function description
 * @param param param description
 * @return return value description
 */
int example(int param) {
    // ...
}

特殊限制

C++

出于便于性能分析的理由,除引入的三方代码外,MNN代码需遵循:

  1. class禁止运算符重载

  2. class禁止实现拷贝构造函数、重载赋值运算符

  3. struct禁止自定义构造函数

出于控制库文件大小的理由,除引入的三方代码外,MNN代码需遵循:

  1. 不允许使用stream,如cout/cin、 ifstream/ofstream、 istringstream/ostringstream等

  2. 不允许使用C++异常机制,即try/catch/throw

汇编

出于跨平台编译的诉求,MNN代码需遵循:

  1. 所有汇编都需要有C语言等价实现,编译时,通过宏选择平台对应的汇编实现或C语言实现

  2. 入参、返回值类型必须是32位/64位兼容类型,即指针、size_t、ssize_t之一,禁用其他类型,避免编译环境不同导致调用规约偏差

  3. 严格按照ARM标准手册使用寄存器,如armv7a上q4 - q7使用后必须复原,armv8上v8 - v15 使用后必须复原

自定义后端

Backend是MNN对计算设备的抽象。MNN当前已经支持CPU、Vulkan、OpenCL、Metal等Backend,只在计算设备暂未支持时新增Backend,新增Op,请参阅新增Op文档

声明

所有新增Backend都需继承Backend类,并实现所有纯虚函数。

class XPUBackend final : public Backend {
  XPUBackend(MNNForwardType type, MemoryMode mode);
  virtual ~XPUBackend();
  virtual Execution* onCreate(const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs, const MNN::Op* op) override;
  virtual void onExecuteBegin() const override;
  virtual void onExecuteEnd() const override;
  virtual bool onAcquireBuffer(const Tensor* tensor, StorageType storageType) override;
  virtual bool onReleaseBuffer(const Tensor* tensor, StorageType storageType) override;
  virtual bool onClearBuffer() override;
  virtual void onCopyBuffer(const Tensor* srcTensor, const Tensor* dstTensor) const override;
}

构造与销毁

Backend构造时,可以额外指定内存环境,在内存受限环境中,应避免非必要的内存使用。可以在构造函数中,完成对计算设备访问的必要初始化,如GPU下预加载shader等。

/** backend memory mode */
enum MemoryMode {
    /** use memory without limit. */
    NORMAL = 0,
    /** use memory thriftily. */
    LIMIT = 1
};
/**
 * @brief initializer.
 * @param type  forward type.
 * @param mode  memory mode.
 */
Backend(MNNForwardType type, MemoryMode mode = NORMAL);

Execution创建

Backend需要通过onCreate为op创建出exection实例:

virtual Execution* onCreate(const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs, const MNN::Op* op) override;

可以在方法内根据op类型创建,但更建议提供注册接口:

class XPUBackend final : public Backend {
    // ...
    class Creator {
    public:
        /**
         * @brief create execution for given input, op on metal backend.
         * @param inputs    given input tensors.
         * @param op        given op.
         * @param backend   metal backend.
         * @return created execution if supported, NULL otherwise.
         */
        virtual Execution *onCreate(const std::vector<Tensor *> &inputs, const MNN::Op *op,
                                    Backend *backend) const = 0;
    };
    /**
     * @brief register creator for given op type.
     * @param type      given op type.
     * @param creator   registering creator.
     */
    static void addCreator(OpType type, Creator *creator);
    // ...
};
template <class T>
class XPUCreatorRegister {
public:
    /**
     * @brief initializer. register T creator for given op type.
     * @param type  given op type.
     */
    XPUCreatorRegister(OpType type) {
        T *test = new T;
        XPUBackend::addCreator(type, test);
    }
};

这样,Op Execution中,就可以通过注册追加Op类型:

class XPUPoolingCreator : public XPUBackend::Creator {
public:
    virtual Execution *onCreate(const std::vector<Tensor *> &inputs, const MNN::Op *op, Backend *backend) const {
        return new XPUPooling(backend, op->main_as_Pool());
    }
};
static XPUCreatorRegister<XPUPoolingCreator> __reg(OpType_Pooling);

内存管理

Backend通过onAcquireBuffer为tensor分配内存,通过onReleaseBuffer为tensor释放内存。内存有三种存储模式:STATIC内存不复用,一般用于op常量存储;DYNAMIC内存可复用,一般用于变量存储;DYNAMIC_SEPERATE内存在pipeline间可复用,一般用于pipeline常量存储。_onAcquireBuffer__onReleaseBuffer_中可以不实际分配/释放内存,只记录内存用量变更,在_onAllocateBuffer_调用时,再根据用量计算出优化方案,一次性完成分配/释放。

/** backend buffer storage type */
enum StorageType {
    /**
     use NOT reusable memory.
     - allocates memory when `onAcquireBuffer` is called.
     - releases memory when `onReleaseBuffer` is called or when the backend is deleted.
     - do NOTHING when `onClearBuffer` is called.
     */
    STATIC,
    /**
     use reusable memory.
     - allocates or reuses memory when `onAcquireBuffer` is called. prefers reusing.
     - collects memory for reuse when `onReleaseBuffer` is called
     - releases memory when `onClearBuffer` is called or when the backend is deleted.
     */
    DYNAMIC,
    /**
     use NOT reusable memory.
     - allocates memory when `onAcquireBuffer` is called.
     - do NOTHING when `onReleaseBuffer` is called.
     - releases memory when `onClearBuffer` is called or when the backend is deleted.
     */
    DYNAMIC_SEPERATE
};
/**
 * @brief allocate buffer of tensor for given storage type.
 * @param tensor        buffer provider.
 * @param storageType   buffer storage type.
 * @return success or not.
 */
virtual bool onAcquireBuffer(const Tensor* tensor, StorageType storageType) = 0;
/**
 * @brief release buffer of tensor for given storage type.
 * @param tensor        buffer provider.
 * @param storageType   buffer storage type.
 * @return success or not.
 */
virtual bool onReleaseBuffer(const Tensor* tensor, StorageType storageType) = 0;

在所有内存都分配完成后,backend会收到onAllocateBuffer回调:

/**
 * @brief callback after all buffers needed by backend ops were allocated.
 * @return success or not. (result not used currently)
 */
virtual bool onAllocateBuffer() {
    return true;
}

Backend在调用onClearBuffer时,需要释放所有DYNAMICDYNAMIC_SEPERATE存储模式的内存:

/**
 * @brief clear all dynamic buffers.
 * @return success or not.
 */
virtual bool onClearBuffer() = 0;

此外,backend还需要负责tensor数据的拷贝:

/**
 * @brief copy buffer from tensor to tensor.
 * @param srcTensor source buffer provider.
 * @param dstTensor dest buffer provider.
 */
virtual void onCopyBuffer(const Tensor* srcTensor, const Tensor* dstTensor) const = 0;

拷贝可能在backend内部,也可能在backend与CPU backend之间。 拷贝需要处理Tensor间的布局转换,相同布局时,可以直接拷贝数据;不同布局,如**NHWC****NC4HW4**,则一般需要做特殊转换。

Pipeline回调

Backend在pipeline执行的各个周期都会收到回调,onResizeBeginonResizeEnd在调整内存分配前后调用(op的onResize会在此间调用);onExecuteBeginonExecuteEnd在op执行前后调用(op的onExecute会在此间调用);onWaitFinish相对特殊,由用户主动调用,异步执行的pipeline需要同步等待完成。

/**
 * @brief callback before resize ops.
 */
virtual void onResizeBegin();
/**
 * @brief callback after resize ops.
 */
virtual ErrorCode onResizeEnd();
/**
 * @brief callback before executing ops.
 */
virtual void onExecuteBegin() const = 0;
/**
 * @brief callback after executing ops.
 */
virtual void onExecuteEnd() const = 0;

注册Backend

最后,定义Backend Creator,注册方法中调用MNNInsertExtraBackendCreator就可以完成Backend的注册,这里的注册方法需要在BackendRegister.cpp中声明并调用:

class XPUBackendCreator : public BackendCreator {
    virtual Backend *onCreate(const Backend::Info &info) const {
        return new MetalBackend;
    }
};
void registerCPUBackendCreator() {
    MNNInsertExtraBackendCreator(MNN_FORWARD_CPU, new CPUBackendCreator);
};

使用cmake编译时,完成代码修改后,也需要相应修改CMakeLists.txt。

自定义算子

概述

在添加自定义算子前,请参阅算子列表,避免不必要的重复。

MNN 算子转换与实现结构

MNN 的算子转换与实现如下图,

  • 模型转换包括以下步骤,二选一:

    • 训练框架导出的Op与MNN的Op一一对应:前端直接转换

    • 用组合器(参考 tools/converter/source/optimizer/onnxextra 等目录)由 MNN 算子组合。

  • MNN 算子实现包括如下步骤

    1. 添加Schema描述(必须)

    2. 添加维度计算(若算子输出维度和输入一致可跳过)

    3. 添加几何计算实现(可选,如果实现几何计算,无须后续在各后端添加算子实现)

    4. 添加各后端算子实现(可选,选择需要部分进行实现)

https://cdn.nlark.com/yuque/0/2021/png/405896/1618994794052-575a79b9-d291-4d1b-a630-79dd705bc977.png#clientId=u1c902b2d-d8e6-4&from=paste&height=701&id=ue223d8c2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1402&originWidth=3394&originalType=binary&ratio=1&size=256977&status=done&style=none&taskId=u4663d0eb-adcf-435b-b540-f61d2617cd4&width=1697image.png

添加算子的流程

https://cdn.nlark.com/yuque/0/2021/png/405896/1618995111237-321c5ca8-ed99-4cfc-9d91-04deaa2e29eb.png#clientId=u1c902b2d-d8e6-4&from=paste&height=597&id=u518a1fda&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1194&originWidth=2714&originalType=binary&ratio=1&size=222438&status=done&style=none&taskId=u9c8f2ef4-7bf3-4b18-9560-794c3344f01&width=1357image.png 简单来说,优先转换,然后组合,然后几何计算,最后各后端实现。

添加Schema描述

若添加的算子不在MNN的算子列表中,需要添加模型描述。修改完模型描述后,需要调用generate脚本重新生成模型描述头文件。

添加算子类型

schema/default/MNN.fbs文件的OpType列表里追加算子名称,如:

enum OpType : int {
    AbsVal,
    QuantizedAdd,
    ...
    MyCustomOp
}

添加算子参数描述

如果算子不包含参数,则可以略过这一步。

首先,在schema/default/MNN.fbs文件的OpParameter列表里追加算子参数名称,如:

union OpParameter {
    QuantizedAdd,
    ArgMax,
    AsString,
    ...
    MyCustomOpParam
}

而后,添加参数描述。如果算子来自Caffe,选择CaffeOps.fbs;如果算子来自TensorFlow,就使用TensorflowOp.fbs

table MyCustomOpParam {
    padX:int;
    padY:int;
    kernelX:int;
    kernelY:int;
    strideX:int;
    strideY:int;
    dataType:DataType=DT_FLOAT;
}

添加模型转换

用户可根据自己使用的框架,选择对应的模型转换模块去添加算子转换的支持。添加完模型转换后,需要重新cmake。

目前,MNN支持TensorFlow、TensorFlow Lite、Caffe、ONNX和TorchScript模型格式的转换。

TensorFlow模型转换

  1. 添加转换类 在tools/converter/source/tensorflow下添加MyCustomOpTf.cpp。可以直接声明转换类,也可以利用宏定义简化代码。

直接声明示例:

class MyCustomOpTf : public tfOpConverter {                                             
    public:                                                                       
        virtual void run(MNN::OpT *dstOp, TmpNode *srcNode, TmpGraph *tempGraph);
        MyCustomOpTf() {}                                                                   
        virtual ~MyCustomOpTf() {}                                                          
        virtual MNN::OpType opType();                                             
        virtual MNN::OpParameter type();                                          
}

等效宏定义示例:

DECLARE_OP_CONVERTER(MyCustomOpTf);

需要实现run、析构、opTypetype函数。其中,run函数用于解析模型的proto文件得到参数,然后赋值给flatbuffer自定义参数。参数srcNode保存有输入输出节点信息,可以根据输入输出节点在tempGraph中找到TmpNode。调用函数find_attr_value(const tensorflow::NodeDef& node, const char* key, tensorflow::AttrValue& value)获得对应参数的值。

注册转换类:

REGISTER_CONVERTER(MyCustomOpTf, MyCustomOp);
  1. 添加映射 在OpMapper.hpp中添加相应的TensorFlow Op名字到MNN Op名字的映射:

{"OpName1", MNN::OpType_MyCustomOp},
{"OpName2", MNN::OpType_MyCustomOp},
  1. 处理Op附带的Const 如果Const不作为此Op的参数,而是看成一个单独的Op,可以忽略此步骤;如果Op要把Const当成参数,要在文件TmpGraph.cpp里修改函数_genMinGraph(),把相应Const节点的isCovered属性设置为true。

TensorFlow Lite模型转换

  1. 添加转换类 在tools/converter/source/tflite下添加MyCustomOpTflite.cpp

宏定义示例:

DECLARE_OP_COVERTER(MyCustomOpTflite);

需要实现函数:

MyCustomOpTflite::opType(int quantizedModel);
MyCustomOpTflite::type(int quantizedModel);
MyCustomOpTflite::run(MNN::OpT *dstOp, 
                      const std::unique_ptr<tflite::OperatorT> &tfliteOp, 
                      const std::vector<std::unique_ptr<tflite::TensorT> > &tfliteTensors,
                      const std::vector<std::unique_ptr<tflite::BufferT> > &tfliteModelBuffer,
                      const std::vector<std::unique_ptr<tflite::OperatorCodeT> > &tfliteOpSet,
                      int quantizedModel)

其中,run函数相比TensorFlow的版本,多一个quantizedModel参数。若quantizedModel为true,则模型为量化模型,需转为相应的量化Op;若为false,转为浮点Op。在run函数中需要设置输入、输出tensor的index:

// set input output index
dstOp->inputIndexes.resize(1);
dstOp->outputIndexes.resize(1);
dstOp->inputIndexes[0]  = tfliteOp->inputs[0];
dstOp->outputIndexes[0] = tfliteOp->outputs[0];

注册转换类:

using namespace tflite;
REGISTER_CONVERTER(MyCustomOpTflite, BuiltinOperator_OPName);

Caffe模型转换

  1. 添加转换类 在/tools/converter/source/caffe下添加MyCustomOp.cpp。

类声明示例:

class MyCustomOp : public OpConverter {
public:
    virtual void run(MNN::OpT* dstOp, 
                     const caffe::LayerParameter& parameters, 
                     const caffe::LayerParameter& weight);
    MyCustomOp() {}
    virtual ~MyCustomOp() {}
    virtual MNN::OpType opType();
    virtual MNN::OpParameter type();
};

实现runopTypetype函数,在run函数中解析caffe参数得到具体参数。其中参数parameters保存有Op的参数信息,weight保存有卷积、BN等数据参数。

注册转换类:

static OpConverterRegister<MyCustomOp> a("MyCustomOp");

ONNX模型转换

  1. 添加转换类 在/tools/converter/source/onnx下添加MyCustomOpOnnx.cpp。

类声明示例:

DECLARE_OP_CONVERTER(MyCustomOpOnnx);

需要实现函数:

MNN::OpType MyCustomOpOnnx::opType();
MNN::OpParameter MyCustomOpOnnx::type();
void MyCustomOpOnnx::run(MNN::OpT* dstOp, 
                         const onnx::NodeProto* onnxNode, 
                         std::vector<const onnx::TensorProto*> initializers);

run函数中,onnxNode即onnx原始节点信息,权重等数据信息需从initializers取。

注册转换类:

REGISTER_CONVERTER(MyCustomOpOnnx, MyCustomOp);

添加维度计算

如果该Op的输出Tensor大小与第1个输入Tensor一致,并且不需要分析FLOPS,可以跳过这步。添加完形状计算代码后,需要在根目录下运行 python3 tools/scripts/register.py,并重新cmake。

添加计算类

/source/shape下添加ShapeMyCustomOp.cpp:

class MyCustomOpSizeComputer : public SizeComputer {
public:
    virtual bool onComputeSize(const MNN::Op* op, const std::vector<Tensor*>& inputs,
                               const std::vector<Tensor*>& outputs) const override {
        // set tensor->buffer.type
        //                   .dimensions
		//                   .dim[x].extent
        //       			 .dim[x].stride
        //       			 .dim[x].flag
        return true;
    }
    virtual float onComputeFlops(const MNN::Op* op, 
                                 const std::vector<Tensor*>& inputs,
                                 const std::vector<Tensor*>& outputs) const {
        return flops_for_calc_output_from_input;
    }
};

onComputeSize函数中,根据输入tensor的维度信息,计算输出tensor的维度信息,并设置输出tensor的数据类型。计算完成后返回true;若输入维度信息未知返回false。 在onComputeFlops函数中,根据输入、输出tensor的维度信息,返回总计算量。

注册计算类

REGISTER_SHAPE(MyCustomOpSizeComputer, OpType_MyCustomOp);

添加实现

添加完算子实现后,需要在根目录下运行 python3 tools/scripts/register.py,并重新cmake。

添加CPU实现

source/backend/CPU目录下添加CPUMyCustomOp.hppCPUMyCustomOp.cpp

1. 实现类声明

class CPUMyCustomOp : public Execution {
public:
    // 若执行onExecute需要使用缓存,在此函数中申请,若无可不声明
    virtual ErrorCode onResize(const std::vector<Tensor *> &inputs, 
                               const std::vector<Tensor *> &outputs) override;
    // 具体的Op执行函数
    virtual ErrorCode onExecute(const std::vector<Tensor *> &inputs, 
                                const std::vector<Tensor *> &outputs) override;
};

2. 实现onResizeonExecuteonResize中,调用backend()->onAcquireBuffer(&mCache, Backend::DYNAMIC)进行缓存的申请,调用backend()->onReleaseBuffer(&mCache, Backend::DYNAMIC)回收缓存。释放后的内存可以被复用。 在onExecute中,做必要的输入的检查,有利于提前发现问题。若执行完毕正确返回NO_ERROR。

3. 注册实现类

class CPUMyCustomOpCreator : public CPUBackend::Creator {
public:
    virtual Execution *onCreate(const std::vector<Tensor *> &inputs, 
                                const std::vector<Tensor *> &outputs, 
                                const MNN::Op *op,
                                Backend *backend) const override {
        return new CPUMyCustomOp(backend);
    }
};
REGISTER_CPU_OP_CREATOR(CPUMyCustomOpCreator, OpType_MyCustomOp);

添加Metal实现

  1. 添加Shader 在source/backend/Metal目录下添加MetalMyCustomOp.metal,并添加进Xcode工程。metal可以参考目录下已有实现。

2. 实现类声明 在source/backend/Metal目录下添加MetalMyCustomOp.hppMetalMyCustomOp.cpp,并添加进Xcode工程:

class MetalMyCustomOp : public Execution {
public:
    virtual ErrorCode onResize(const std::vector<Tensor *> &inputs, 
                               const std::vector<Tensor *> &outputs) override;
    virtual ErrorCode onExecute(const std::vector<Tensor *> &inputs, 
                                const std::vector<Tensor *> &outputs) override;
};

3. 实现onResizeonExecute 不同于CPU Tensor将数据存储在host指针中,Metal数据指针存放在deviceId中,deviceId上存储的是id<MTLBuffer>

auto buffer = (__bridge id<MTLBuffer>)(void *)tensor->deviceId();

Metal Op的特定参数等可以通过id<MTLBuffer>存储。buffer数据类型可以与tensor不同,buffer甚至可以混合多种数据类型,只需保证创建时指定了正确的长度即可。例如:

auto buffer = [context newDeviceBuffer:2 * sizeof(int) + 2 * sizeof(__fp16) access:CPUWriteOnly];
((__fp16 *)buffer.contents)[0] = mAlpha / mLocalSize;  // alpha
((__fp16 *)buffer.contents)[1] = mBeta;                // beta
((int *)buffer.contents)[1] = mLocalSize;              // local size
((int *)buffer.contents)[2] = inputs[0]->channel();    // channel

在创建buffer时,需要指定访问控制权限。目前共有三种权限:

  • CPUReadWrite,数据在CPU/GPU间共享存储,一般用于device buffer;

  • CPUWriteOnly,数据通过CPU写入后不再读取,一般用于参数buffer;

  • CPUTransparent,数据只在GPU中,一般用于heap buffer;

MNNMetalContext在创建buffer上,有两套相近的接口,区别只在数据的生命周期上:

  • device占用的内存在单次推理过程中都不会被复用;

  • 而heap占用的内存,在调用-[MNNMetalContext releaseHeapBuffer:]之后,可以被其他Op复用;

一般而言,heap只会与CPUTransparent一起使用。heap实际只在iOS 10+上有效,iOS 9-上会回退到device上。

使用Metal时,如非特殊情况,禁止自行创建device和library。加载library、编译function都是耗时行为,MNNMetalContext上做了必要的缓存优化。通过context执行Metal的示例如下:

auto context   = (__bridge MNNMetalContext *)backend->context();
auto kernel    = /* metal kernel name NSString */;
auto encoder   = [context encoder];
auto bandwidth = [context load:kernel encoder:encoder];
/* encoder set buffer(s)/sampler(s) */
[context dispatchEncoder:encoder 
			     threads:{x, y, z}
      maxThreadsPerGroup:maxThreadsPerThreadgroup]; // recommended way to dispatch
[encoder endEncoding];

4. 注册实现类

class MetalMyCustomOpCreator : public MetalBackend::Creator {
public:
    virtual Execution *onCreate(const std::vector<Tensor *> &inputs, 
                                const MNN::Op *op, Backend *backend) const {
        return new MetalMyCustomOp(backend);
    }
};
REGISTER_METAL_OP_CREATOR(MetalMyCustomOpCreator, OpType_MyCustomOp);

添加注册代码后,重新运行一下 CMake ,自动变更注册文件

添加Vulkan实现

  1. 添加Shader 在source/backend/vulkan/execution/glsl目录下添加具体的shader(*.comp)。若输入内存布局为NC4HW4,则按image实现,否则采用buffer实现。可以参考目录下已有实现。然后,执行makeshader.py脚本编译Shader。

2. 实现类声明 在目录source/backend/vulkan/execution/下添加VulkanMyCustomOp.hppVulkanMyCustomOp.cpp

class VulkanMyCustomOp : public VulkanBasicExecution {
public:
    VulkanMyCustomOp(const Op* op, Backend* bn);
    virtual ~VulkanMyCustomOp();
    ErrorCode onEncode(const std::vector<Tensor*>& inputs, 
                       const std::vector<Tensor*>& outputs,
                       const VulkanCommandPool::Buffer* cmdBuffer) override;
private:
    // GPU Shader所需的参数
    std::shared_ptr<VulkanBuffer> mConstBuffer;
    // Pipeline
    const VulkanPipeline* mPipeline;
    // Layout Descriptor Set
    std::shared_ptr<VulkanPipeline::DescriptorSet> mDescriptorSet;
};
  1. 实现 实现函数onEncode,首先需要做内存布局检查:若为NC4HW4,则Shader用image实现,否则用buffer。执行完毕返回NO_ERROR。

4. 注册实现类

class VulkanMyCustomOpCreator : public VulkanBackend::Creator {
public:
    virtual Execution* onCreate(const std::vector<Tensor*>& inputs, 
                                const MNN::Op* op,
                                Backend* backend) const override {
        return new VulkanMyCustomOp(op, backend);
    }
};
static bool gResistor = []() {
    VulkanBackend::addCreator(OpType_MyCustomOp, new VulkanMyCustomOpCreator);
    return true;
}();

添加OpenCL实现

  1. 添加Kernel 在source/backend/opencl/execution/cl目录添加具体的kernel(*.cl)。目前feature map均使用image2d实现。可以参考目录下已有实现。然后执行opencl_codegen.py来生成kernel映射。

2. 实现类声明 在目录source/backend/opencl/execution/下添加MyCustomOp.hMyCustomOp.cpp

template <typename T>
class MyCustomOp : public Execution {
public:
    virtual ErrorCode onResize(const std::vector<Tensor *> &inputs, 
                               const std::vector<Tensor *> &outputs) override;
    virtual ErrorCode onExecute(const std::vector<Tensor *> &inputs, 
                                const std::vector<Tensor *> &outputs) override;
};
  1. 实现 实现函数onResize(可选)、onExecute。执行完毕返回NO_ERROR。

4. 注册实现类

OpenCLCreatorRegister<TypedCreator<MyCustomOp<cl_data_t>>> __my_custom_op(OpType_MyCustomOp);

添加OpenGL实现

  1. 添加Shader 在source/backend/opengl/glsl下添加具体的shader(*.glsl),不用加文件头,feature map 均采用image3d表示。可以参考目录下已有实现。而后,在source/backend/opengl目录下执行makeshader.py

  2. 添加Executor 在source/backend/opengl/execution/目录下添加GLMyCustomOp.hGLMyCustomOp.cpp

class GLMyCustomOp : public Execution {
public:
    GLMyCustomOp(const std::vector<Tensor *> &inputs, const Op *op, Backend *bn);
    virtual ~GLMyCustomOp();
    virtual ErrorCode onExecute(const std::vector<Tensor *> &inputs, 
                                const std::vector<Tensor *> &outputs) override;
    virtual ErrorCode onResize(const std::vector<Tensor *> &inputs, 
                               const std::vector<Tensor *> &outputs) override;

private:
    std::shared_ptr<GLProgram> mProgram;
};
  1. 实现 实现函数onResize(可选)、onExecute。执行完毕返回NO_ERROR。

4. 注册实现类-

GLCreatorRegister<TypedCreator<GLMyCustomOp>> __my_custom_op(OpType_MyCustomOp);

常见问题与解答

热点问题

编译相关

环境需求

cmake 3.10+ gcc 4.9+ protobuf 3.0+

更新 gcc 之后请重新 cmake 一下

schema/generate.sh 相关问题

*** building flatc ***
CMake Error: Could not find CMAKE_ROOT !!!

这说明你的 CMake 没有正确安装,请尝试 sudo apt install extra-cmake-modulesexport CMAKE_ROOT=/path/to/where_cmake_installed

更改 schema 之后,需要重新运行 schema/generate.sh

找不到 Protobuf

触发问题操作

  • 编译MNN模型转换器 (-DMNN_BUILD_CONVERTER=ON)

  • tools/script/get_model.sh

报错信息类似:

Could NOT find Protobuf (missing: Protobuf_INCLUDE_DIR)
Unrecognized syntax identifier "proto3".  This parser only recognizes "proto2".

有两种解决方案

(建议)使用 MNN 自带的 Protobuf

cmake 加上选项 -DMNN_BUILD_PROTOBUFFER=ON ,使用 MNN 自带的 protobuf 编译 采用这种方案时,如果之前有编译残留,有可能出现原先生成的 protobuf 相关头文件与 MNN 自带的 protobuf 库不兼容的问题(编译出错),清空当前编译目录,重新编译即可。

(可选)安装 / 配置 Protobuf

参考 Protobuf’s Installation Instructions 安装. 如果您电脑上安装了多份 protobuffer ,他们之前可能产生冲突(protoc 与链接的 libprotobuf 不一致),可尝试如下方式解决:

which protoc
# comment the output path in .bashrc if it do NOT direct to the correct protoc.
source .bashrc
sudo ldconfig

# uninstall
sudo apt-get remove libprotobuf-dev
sudo apt-get remove protobuf-compiler
sudo apt-get remove python-protobuf
sudo rm -rf /usr/local/bin/protoc
sudo rm -rf /usr/bin/protoc
sudo rm -rf /usr/local/include/google
sudo rm -rf /usr/local/include/protobuf*
sudo rm -rf /usr/include/google
sudo rm -rf /usr/include/protobuf*
# install
sudo apt-get update
sudo ldconfig
sudo apt-get install libprotobuf* protobuf-compiler python-protobuf

MNN静态库的使用

MNN 一般以动态库形式使用,里面有大量自注册函数,如果需要以静态库形式使用 MNN ,需要根据您所用的编译器,增加完全链接的编译选项:

  • GCC: -Wl,–whole-archive MNN -Wl,–no-whole-archive

  • OSX(Xcode): -Wl,-force_load MNN

  • Window(Visio-Studio): /WHOLEARCHIVE:MNN

模型转换

Error for binary op: input0’s type != input1’s type

此处报错是 Binary 算子的形状计算出错。

  • MNN 在模型转换时会尝试进行图优化。

  • 图优化过程中部分Pass需要输入Tensor 的形状信息,触发形状计算。

  • 对于存在控制流的模型,子图的输入是未知类型,一般设成 float ,此时有可能出现 Binary 两侧 Tensor type 不一致的问题

  • 此报错不影响模型的实际使用

Reshape error

  • 在模型未指定输入大小时,MNN 模型转换时的图优化中计算 shape 可能打印该错误

  • 一般不影响模型转换结果,看最后是否 convert success 即可

不支持算子

opConverter ==> MNN Converter NOT_SUPPORTED_OP: [ ANY_OP_NAME ]

说明存在 MNN 不支持转换的算子,可以考虑如下解决方案:

  • 若原始模型为 tflite / caffe(含自定义算子) , 改成 MNN 支持较好的 Tensorflow pb 格式导出或转成 Onnx ,再转 MNN

  • 提 Issue 等待我们支持,并关注 MNN 的更新

  • 参考自定义算子

模型转换后与原框架结果不一致

先使用MNN中的模型一致性验证脚本进行测试,确定不是调用方法或其他错误,使用方法

Pymnn

import MNN 出现 import numpy error

临时解决方案:升级 numpy 版本到 1.20.0 或以上

运行问题

运行结果出错 / Tensor 的 elementSize 不为各维度乘积

MNN 内部对 CV 相关算子采用 NC4HW4 布局,计算 elementSize 时,channel 会上对齐到 4 返回,此内存布局允许实现的硬件自行确定内存排列方式,具体方式不对用户可见,但用户可以通过如下代码,输入或获取自己指定的NCHW / NHWC 内存布局的 Tensor / VARP。

Interpreter-Session API
auto srcTensor = net->getSessionInput(session, "data");
auto srcTensorHost = new Tensor(srcTensor, Tensor::TENSORFLOW);
// ... set srcTensorHost data
srcTensor->copyFromHostTensor(srcTensorHost);
delete srcTensorHost;
// ... set other inputs, if exist
net->runSession(session);

auto dstTensor = net->getSessionOutput(session, "prob"); 
auto dstTensorHost = new Tensor(dstTensor, Tensor::TENSORFLOW);
dstTensor->copyToHostTensor(dstTensorHost);
// ... use dstTensorHost data
delete dstTensorHost;
Module API
Module* net = Module::load("net.mnn", {"data"}, {"prob"});

VARP input = _Input({1, 224, 224, 3}, NHWC);
float* inputPtr = input->writeMap<float>();
// ... set srcTensor data
auto info = net->getInfo();
input = _Convert(input, info->inputs[0].order);
output = net->onForward({input});
output = _Convert(output, NHWC);

const float* outputPtr = output->readMap<float>();
// ... use outputPtr

compute shape error for XXX

  • 输入形状不正确

  • MNN 推理过程分形状计算-几何计算-内容计算三步,前两步在 resizeSession 中完成,在 createSession 时,会用初始设定的输入大小进行一次 resizeSession ,若初始 shape 设定不对,则会在某个算子报 shape 计算的 error ,重新设置输入 tensor 的大小并 resizeSession 即可

  • 在导出 Onnx 时,shape 没设成 dynamic ,导致部分参数写死,变动大小后无法 resize 网络

  • 如果确定输入形状正确,并且执行了resizeTensorresizeSession;可以打开source/shape/SizeComputer.cpp中的宏// #define MNN_DEBUG_TENSOR_SIZE定义,然后执行模型推理;打开宏之后可以看到每一层的形状信息,可以逐层进行Debug

Android 设备无法查看日志

Android 系统有两类打印日志的方式: printf 和 logcat. 默认 MNN 的编译脚本使用 printf,这样方便在命令行中调试。集成到 App 上时,用 cmake -DMNN_USE_LOGCAT=ON 将打印日志的方式改成 logcat 即可用 adb logcat 查看

如何增加 opencl so 地址?

MNN opencl 后端默认采用 dlopen 的方式动态打开设备的 opencl 驱动,相应位置若找不到您设备上的驱动,请修改 OpenCLWrapper.cpp

TensorArray Op 与 Switch / Merge 控制流支持

TensorArray 和控制流支持需要借助 MNN-Express , 请参考 demo/exec/transformerDemo.cpp 的接口使用

如何获取网络中间结果

默认情况下, MNN 只支持用户访问网络输入输出节点的数据,如果需要取中间结果,参考如下方式:

  • Interpreter - Session API

    1. 将需要的中间结果的 tensor 名字加到 config.saveTensors ,然后用这个 config 创建 session.

    2. 在 MNN 的运行过程中插入回调函数,即用 runSessionWithCallBack, 参考 tools/cpp/MNNV2Basic.cpp

  • Express - Module API

    • 加载网络时,把需要获取的中间结果加到 output name 中

GPU 后端无法使用

Linux系统上的简单解决方案: cmake .. -DMNN_USE_SYSTEM_LIB=true -DMNN_SEP_BUILD=false

Windows 系统上参考 MNN 静态库的使用,需要加静态库全链接选项

无法找到系统库

为了设备兼容性,MNN Vulkan / OpenCL 默认采用自己搜索路径 dlopen 的方式,不直接依赖系统驱动。您也可以设置 MNN_USE_SYSTEM_LIB = ON , 让 MNN 直接依赖系统驱动

找不到后端 (Can’t Find type=3 backend)

OpenCL / Vulkan 采用静态变量自注册的方式往 MNN 主库注册后端. 在 Linux 系统上默认采用懒加载,由于没有直接依赖 MNN_CL.so / MNN_Vulkan.so ,不会初始化静态变量,导致 opencl / vulkan 后端使用时找不到. 可以按如下方式之一解决:

  1. 设置 MNN_SEP_BUILD = OFF (cmake -DMNN_SEP_BUILD=OFF).  把 opencl / vulkan 后端统一编入 MNN 的 so.

  2. 自己在使用的代码中加上 dlopen(”libMNN_CL.so”) . 参考 https://github.com/alibaba/MNN/issues/105 .

部分模型用 MNNV2Basic 运行出现段错误

  • 模型不满足运行条件

    • MNNV2Basic 使用 Interpreter + Session 方式运行,此类运行方式要求模型满足一定条件,否则无法运行模型或产生特别的 crash ,条件如下:

      • 模型中所有Tensor的形状可以在输入Tensor形状确定后,预先计算而得

      • 模型中没有子图或其他控制流相关算子

    • 不满足运行条件的模型可以借助 MNN_Express 运行,参考示例代码:

      • demo/exec/transformerDemo.cpp

      • tools/cpp/ModuleBasic.cpp

  • MNN 内部算子实现逻辑错误,此概率较小,遇到可提 issue 反馈

使用 GPU 时的内存访问问题

  • 输入输出指针为空/段错误

    • 一般是直接访问了 tensor 的 host

    • 输入数据获取输出 里面的方式建host tensor 并 copy ,参考相关文档修改使用代码

  • 是否可基于 deviceId 直接传 GPU 地址?

    • 可以,需要理解 MNN GPU 内存布局并传上下文给 MNN ,但相关实现较复杂

    • 采用 MNN_Express 系列接口,可以支持模型之间的内存直接传递不做拷贝

性能相关

使用 GPU 时,调用 copyToHostTensor / copyFromHostTensor 非常慢

GPU 后端调用 copy 的时间包含两个部分

  • 异构数据拷贝

  • 等待数据相关的GPU计算完成

对 GPU 后端而言,在数据被要求对用户可见(比如复制 output tensor 数据出来)之前,是允许异步执行的。 在数据被用户要求可见之时,会等待相应的异步操作完成。 因此有可能 复制 output tensor 的过程包括了等待 GPU 算子异步执行完成,导致缓慢。

GPU 为什么比 CPU 跑得慢?

有如下原因:

  1. 相当一部分移动端设备 (如 pre-iPhone 8), GPU 算力不足,加以内存带宽的限制,本身不如 CPU.

    比如 Apple 由 Imagination 切换到自己的 GPU in iPhone 8, 导致 GPU 性能下降(不如 iphone 7) ,相对地, CPU 是提升的.

  2. 存在 GPU 不支持的算子,这些算子会切换到 CPU 执行,相应的输入输出需要 CPU - GPU 之间的内存拷贝,产生额外耗时

  3. 模型本身计算量小或者不易并行,发挥不了 GPU 并行计算的优势.

  4. GPU 被其他程序占用,或者系统限制了 GPU 频率

模型量化后为什么比浮点慢

这个需要从浮点和量化两个方面说明

  • 浮点性能是 MNN 优化的第一优先级

    • MNN 在浮点各架构(x86/x64/ARM)上均做了深入的优化,基本都达到了设备的理想性能。

    • 在浮点计算上,MNN 采用了 Winograd 卷积/ Strassen 矩阵乘等降低计算量的算法,对于对称卷积 (2x2 , 3x3, 4x4, 5x5, 6x6, 7x7),Winograd 算法有成倍数的性能优化效果。

  • 量化性能受多个因素影响

    • 不同架构上,量化计算性能与浮点计算性能相比有快有慢。

    • 模型量化后,由于部分算子不支持量化,出现回退到浮点计算的情况,交接处产生额外转换耗时。

    • 浮点计算的 Winorad 算法/Strassen 算法未应用于量化计算,相应的性能优化效果量化后不支持。

  • 架构说明:

    • x86 / x64 架构下,无 vnni 指令,量化计算需要先从 int8 转到 int16 ,乘加到 int32 ,本身计算效率不如浮点直接乘加到 fp32 上快。

    • x64 + vnni 指令,量化计算有 sdot 指令,明显快于 FP32 ,编译 MNN 时需要开启 MNN_AVX512 以支持这个指令,一般相比 AVX512 的浮点运算快 30%

    • ARM v7a / ARMv8 :量化计算采用 int8 乘加到 int16,再双加到 int32 的方式,计算效率略快于浮点(一般 30% 左右提升)。

    • ARMv8.2 + 量化计算有 sdot 指令,但同时 FP32 相对之前架构发射数也提升了一倍,编译 MNN 打开 MNN_ARM82 启用 sdot 指令则量化计算更快,否则 FP32 更快,理想情况比 FP32 快1倍以上,比 FP16 快 20%。

其他问题

MNN模型如何加密

加密与破解是攻防的较量,端侧加密很难做到绝对安全。 可以通过构造独有的模型格式来增加反向的难度,按照以下步骤操作可以得到独特的模型格式:

  1. 针对schema/default/*.fbs下的文件,对参数顺序,枚举类顺序进行重新排序;比如:可以重新调整MNN.fbsOpType的顺序;重新调整CaffeOp.fbsConvolution2DCommon成员变量的顺序;

  2. 执行schema/generate.sh重新生成flatbuffers头文件;

  3. 重新编译MNN库文件, Convert等所有工具;

  4. 使用新的工具重新转换模型;

  5. 在端侧使用新模型和新的MNN库文件进行部署;

Interpreter

class Interpreter

枚举类

SessionMode

enum SessionMode {
    Session_Debug = 0,
    Session_Release = 1,
    Session_Input_Inside = 2,
    Session_Input_User = 3,
    Session_Output_Inside = 4,
    Session_Output_User = 5,
    Session_Resize_Direct = 6,
    Session_Resize_Defer = 7,
    Session_Backend_Fix = 8,
    Session_Backend_Auto = 9,
};
value name 说明
0 Session_Debug 可以执行callback函数,并获取Op信息(默认)
1 Session_Release 不可执行callback函数
2 Session_Input_Inside 输入由session申请(默认)
3 Session_Input_User 输入由用户申请
4 Session_Output_Inside 输出依赖于session不可单独使用
5 Session_Output_User 输出不依赖于session可单独使用
6 Session_Resize_Direct 在创建Session时执行resize(默认)
7 Session_Resize_Defer 在创建Session时不执行resize
8 Session_Backend_Fix 使用用户指定的后端,后端不支持时回退CPU
9 Session_Backend_Auto 根据算子类型自动选择后端

ErrorCode

enum ErrorCode {
    NO_ERROR           = 0,
    OUT_OF_MEMORY      = 1,
    NOT_SUPPORT        = 2,
    COMPUTE_SIZE_ERROR = 3,
    NO_EXECUTION       = 4,
    INVALID_VALUE      = 5,
    INPUT_DATA_ERROR = 10,
    CALL_BACK_STOP   = 11,
    TENSOR_NOT_SUPPORT = 20,
    TENSOR_NEED_DIVIDE = 21,
};
value name 说明
0 NO_ERROR 没有错误,执行成功
1 OUT_OF_MEMORY 内存不足,无法申请内存
2 NOT_SUPPORT 有不支持的OP
3 COMPUTE_SIZE_ERROR 形状计算出错
4 NO_EXECUTION 创建执行时出错
10 INPUT_DATA_ERROR 输入数据出错
11 CALL_BACK_STOP 用户callback函数退出
20 TENSOR_NOT_SUPPORT resize出错
21 TENSOR_NEED_DIVIDE resize出错

SessionInfoCode

enum SessionInfoCode {
    MEMORY = 0,
    FLOPS = 1,
    BACKENDS = 2,
    RESIZE_STATUS = 3,
    ALL
};
value name 说明
0 MEMORY 会话的内存占用大小,MB计算,浮点类型数据
1 FLOPS 会话的计算量,flops,浮点数据类型
2 BACKENDS 会话的后端数目,个数是config数量加1
3 RESIZE_STATUS resize的状态,数据是int类型,0表示就绪,1表示需要分配内存,2表示需要resize
ALL 以上所有信息

HintMode

enum HintMode {
    MAX_TUNING_NUMBER = 0,
};
value name 说明
0 MAX_TUNING_NUMBER GPU下tuning的最大OP数

成员函数


Interpreter

该构造函数禁止使用,创建对象请使用createFromFile


~Interpreter

析构函数


createFromFile

static Interpreter* createFromFile(const char* file);

从文件加载模型,加载.mnn模型文件创建一个MNN解释器,返回一个解释器对象

参数:

  • file MNN模型所放置的完整文件路径

返回:创建成功则返回创建的解释器对象指针,失败就返回nullptr


createFromBuffer

static Interpreter* createFromBuffer(const void* buffer, size_t size)

从内存加载模型,根据指定内存地址与大小,从内存中创建一个计时器对象

参数:

  • buffer 模型文件内存中的数据指针

  • size 模型文件字节数大小

返回:创建成功则返回创建的解释器对象指针,失败就返回nullptr


destroy

static void destroy(Interpreter* net);

释放指定的解释器对象

参数:

  • net 需要释放的解释器对象

返回:void


setSessionMode

void setSessionMode(SessionMode mode);

设置模型推理时回话的执行模式,参考SessionMode

该函数需在createSession前调用

参数:

  • mode 回话的执行模式

返回:void


setCacheFile

void setCacheFile(const char* cacheFile, size_t keySize = 128);

设置缓存文件,缓存文件在GPU模式下用来存储Kernel与Op信息;执行该函数,在runSession前会从缓存文件中加载信息;在runSession后会将相关信息写入缓存文件中

该函数需在createSession前调用

参数:

  • cacheFile 缓存文件名

  • keySize 保留参数,现在未使用

返回:void


setExternalFile

void setExternalFile(const char* file, size_t flag = 128);

设置额外文件,额外文件是指存储了模型中权重,常量等数据的文件,在创建Session时会从该文件中加载权重等数据。

该函数需在createSession前调用

参数:

  • file 额外文件名

  • flag 保留参数,现在未使用

返回:void


updateCacheFile

ErrorCode updateCacheFile(Session *session, int flag = 0);

更新缓存文件,在最近的一次resizeSession中如果修改了缓存信息,就写入到缓存文件中;如果没有修改缓存信息,就什么都不做

该函数需在resizeSession前调用

参数:

  • session 要更新的缓存会话

  • flag 保留参数,现在未使用

返回:更新缓存的错误码


setSessionHint

void setSessionHint(HintMode mode, int value);

设置会话的额外执行信息,使用HintMode来描述额外信息的类型

该函数需在createSession前调用

参数:

  • mode 额外信息的类型

  • value 额外信息的数值

返回:void


createRuntime

static RuntimeInfo createRuntime(const std::vector<ScheduleConfig>& configs);

根据配置创建一个Runtime,默认情况下,在createSession时对应create单独一个Runtime。对于串行的一系列模型,可以先单独创建Runtime,然后在各Session创建时传入,使各模型用共享同样的运行时资源(对CPU而言为线程池、内存池,对GPU而言Kernel池等);RuntimeInfo的定义如下:

typedef std::pair<std::map<MNNForwardType, std::shared_ptr<Runtime>>, std::shared_ptr<Runtime>> RuntimeInfo;

参数:

  • configs 调度信息

返回:创建的运行时信息,可以用于创建Session


createSession

Session* createSession(const ScheduleConfig& config);
Session* createSession(const ScheduleConfig& config, const RuntimeInfo& runtime);

根据配置或根据用户指定的运行时信息创建会话Session

  • 当仅传入config时会根据config信息创建Runtime

  • 当传入runtime时则使用用户传入的Runtime

参数:

  • config 调度信息

  • runtime 执行信息

返回:创建的会话Session


createMultiPathSession

Session* createMultiPathSession(const std::vector<ScheduleConfig>& configs);
Session* createMultiPathSession(const std::vector<ScheduleConfig>& configs, const RuntimeInfo& runtime);

根据多个配置创建多段计算路径的会话

  • 当仅传入config时会根据config信息创建Runtime

  • 当传入runtime时则使用用户传入的Runtime

参数:

  • configs 多个调度信息

返回:创建的会话Session


releaseSession

bool releaseSession(Session* session);

释放指定的Session

参数:

  • session 待释放的Session

返回:是否成功释放该Session


resizeSession

void resizeSession(Session* session);
void resizeSession(Session* session, int needRelloc);

为session分配内存,进行推理准备工作;该函数一般配合resizeTensor一起调用,修改Tensor输入形状后对应整个推理过程中的内存分配也需要修改

参数:

  • session 改变输入形状后需要重新分配内存的Session对象

  • needRelloc 是否重新分配内存,如果为0则只进行形状计算不进行内存分配

返回:void


releaseModel

void releaseModel();

当不再需要执行createSessionresizeSession的时候,可以调用此函数释放解释器中持有的模型资源,会释放模型文件大小的内存

参数:

  • void

返回:void


getModelBuffer

std::pair<const void*, size_t> getModelBuffer() const;

获取模型模型的内存数据指针和内存大小,方便用户存储模型

参数:

  • void

返回:内存数据指针和内存大小


getModelVersion

const char* getModelVersion() const;

获取模型的版本信息,以字符串的形式返回

参数:

  • void

返回:模型的版本信息,类似:"2.0.0"


updateSessionToModel

ErrorCode updateSessionToModel(Session* session);

SessionTensor的数据更新Model中的常量数据

参数:

  • session 需要更新的会话

返回:更新数据的错误码


runSession

ErrorCode runSession(Session* session) const;

运行session执行模型推理,返回对应的error code,需要根据错误码来判断后续是否成功执行模型推理

参数:

  • session 执行推理的Session对象

返回:执行推理的错误码


runSessionWithCallBack

ErrorCode runSessionWithCallBack(const Session* session, const TensorCallBack& before, const TensorCallBack& end, bool sync = false) const;

该函数本质上与runSession一致,但是提供了用户hook函数的接口,在运行session做网络推理,每层推理前前后会执行的beforeend并根据返回值来决定是否继续执行

参数:

  • session 执行推理的Session对象

  • before 每层推理前执行的回调函数,类型为

    std::function<bool(const std::vector<Tensor*>&, const std::string& /*opName*/)>
    
  • end 每层推理后执行的回调函数,类型同上

  • sync 是否同步等待执行完成

返回:执行推理的错误码


runSessionWithCallBackInfo

ErrorCode runSessionWithCallBackInfo(const Session* session, const TensorCallBackWithInfo& before,
                                     const TensorCallBackWithInfo& end, bool sync = false) const;

该函数与runSessionWithCallBack相似,但是回调函数中增加了Op的类型和计算量信息,可以用来评估模型的计算量

参数:

  • session 执行推理的Session对象

  • before 每层推理前执行的回调函数,类型为

    std::function<bool(const std::vector<Tensor*>&, const OperatorInfo*)>
    
  • end 每层推理后执行的回调函数,类型同上

  • sync 是否同步等待执行完成

返回:执行推理的错误码


getSessionInput

Tensor* getSessionInput(const Session* session, const char* name);

根据name返回模型指定会话的输入tensor;如果没有指定tensor名称,则返回第一个输入tensor

参数:

  • session 持有推理会话数据的Session对象

  • name Tensor的名称

返回:输入Tensor对象


getSessionOutput

Tensor* getSessionOutput(const Session* session, const char* name);

根据name返回模型指定会话的输出tensor;如果没有指定tensor名称,则返回第一个输出tensor

参数:

  • session 持有推理会话数据的Session对象

  • name Tensor的名称

返回:输出Tensor对象


getSessionInfo

bool getSessionInfo(const Session* session, SessionInfoCode code, void* ptr);

根据指定类型获取Session的信息

参数:

  • session 要获取信息的Session对象

  • code 获取的信息类型,使用SessionInfoCode

  • ptr 将信息存储在该指针中

返回:是否支持获取code类型的信息


getSessionOutputAll

const std::map<std::string, Tensor*>& getSessionOutputAll(const Session* session) const;

返回模型指定会话的所有的输出tensor

参数:

  • session 持有推理会话数据的Session对象

返回:所有的输出Tensor的名称和指针


getSessionInputAll

const std::map<std::string, Tensor*>& getSessionInputAll(const Session* session) const;

返回模型指定会话的所有的输入tensor

参数:

  • session 持有推理会话数据的Session对象

返回:所有的输入Tensor的名称和指针


resizeTensor

void resizeTensor(Tensor* tensor, const std::vector<int>& dims);

改变tensor形状,并重新分配内存

参数:

  • tensor 需要改变形状的Tensor对象,一般为输入tensor

  • dims 新的形状

  • batch,channel,height,width 新的形状各维度

返回:void


getBackend

const Backend* getBackend(const Session* session, const Tensor* tensor) const;

获取指定tensor创建时使用的后端Backend;可以在代码中使用该函数来判断当前Session的推理实际使用什么后端。

参数:

  • session tensor相关的会话

  • tensor 被创建的tensor

返回:创建指定tensor的后端,可能为nullptr


bizCode

const char* bizCode() const;

获取创建该解释器的模型中的bizCode

参数:

  • void

返回:模型中的bizCode


uuid

const char* uuid() const;

获取创建该解释器的模型中的uuid

参数:

  • void

返回:模型中的uuid

Tensor

class Tensor

枚举类

DimensionType

用于创建张量的维度类型
enum DimensionType {
    TENSORFLOW,
    CAFFE,
    CAFFE_C4
};
value name 说明
0 TENSORFLOW tensorflow网络类型,数据格式为NHWC
1 CAFFE caffe网络类型,数据格式为NCHW
2 CAFFE_C4 caffe网络类型,数据格式为NC4HW4

数据处理类型

HandleDataType

enum HandleDataType {
    HANDLE_NONE        = 0,
    HANDLE_STRING      = 1
};
value name 说明
0 HANDLE_NONE 默认处理类型
1 HANDLE_STRING 字符串处理类型

MapType

张量映射类型:读或写
enum MapType {
    MAP_TENSOR_WRITE   = 0,
    MAP_TENSOR_READ    = 1
};
value name 说明
0 MAP_TENSOR_WRITE 默认写类型
1 MAP_TENSOR_READ 读类型

成员函数


Tensor

构造函数

Tensor(int dimSize = 4, DimensionType type = CAFFE);

创建一个具有维度大小和类型的张量,而不需要为数据获取内存

参数:

  • dimSize 尺寸大小,默认为4

  • type 张量的维度类型,默认为CAFFE

返回:具有维度大小和类型的张量


Tensor

构造函数

Tensor(const Tensor* tensor, DimensionType type = CAFFE, bool allocMemory = true);

创建一个与给定张量形状相同的张量

参数:

  • tensor 形状提供者

  • type 张量的维度类型,默认为CAFFE

  • allocMemory 是否为数据获取内存

返回:给定张量形状相同的张量


~Tensor

析构函数


createDevice

static Tensor* createDevice(const std::vector<int>& shape, halide_type_t type, DimensionType dimType = TENSORFLOW);

创建具有形状、数据类型和维度类型的张量,存储数据的内存不会被获取,调用后端的onAcquireBuffer来准备内存

参数:

  • shape 张量的形状

  • type 数据类型

  • dimType 张量的维度类型,默认为TENSORFLOW

返回:具有形状、数据类型和维度类型的张量


createDevice

static Tensor* createDevice(const std::vector<int>& shape, DimensionType dimType = TENSORFLOW) {
    return createDevice(shape, halide_type_of<T>(), dimType);
};

创建具有形状和尺寸类型的张量,数据类型用“T”表示,存储数据的内存不会被获取,调用后端的onAcquireBuffer来准备内存

参数:

  • shape 张量的形状

  • dimType 张量的维度类型,默认为TENSORFLOW

返回:具有形状、数据类型和维度类型的张量


create

static Tensor* create(const std::vector<int>& shape, halide_type_t type, void* data = NULL,
                      DimensionType dimType = TENSORFLOW);

创建具有形状、数据类型、数据和维度类型的张量

参数:

  • shape 张量的形状

  • type 数据类型

  • data 数据保存

  • dimType 张量的维度类型,默认为TENSORFLOW

返回:具有形状、数据类型、数据和维度类型的张量


create

static Tensor* create(const std::vector<int>& shape, void* data = NULL, DimensionType dimType = TENSORFLOW) {
    return create(shape, halide_type_of<T>(), data, dimType);
};

创建具有形状、数据和维度类型的张量,数据类型用‘T’表示

参数:

  • shape 张量的形状

  • data 数据保存

  • dimType 张量的维度类型,默认为TENSORFLOW

返回:具有形状、数据类型、数据和维度类型的张量


clone

static Tensor* clone(const Tensor* src, bool deepCopy = false);

拷贝张量

参数:

  • src 张量

  • deepCopy 是否创建新的内容和复制,目前只支持deepCopy = false

返回:拷贝的张量


destroy

static void destroy(Tensor* tensor);

释放张量

参数:

  • tensor 需要释放的张量对象

返回:void


copyFromHostTensor

bool copyFromHostTensor(const Tensor* hostTensor);

对于DEVICE张量,从给定的HOST张量拷贝数据

参数:

  • hostTensor HOST张量,数据提供者

返回:DEVICE张量为真,HOST张量为假


copyToHostTensor

bool copyToHostTensor(Tensor* hostTensor) const;

对于DEVICE张量,将数据复制到给定的HOST张量

参数:

  • hostTensor HOST张量,数据消费者

返回:DEVICE张量为真,HOST张量为假


createHostTensorFromDevice

static Tensor* createHostTensorFromDevice(const Tensor* deviceTensor, bool copyData = true);

从DEVICE张量创建HOST张量,可以复制数据也可以不复制数据

参数:

  • deviceTensor DEVICE张量

  • copyData 是否复制数据,默认为true

返回:HOST张量


getDimensionType

DimensionType getDimensionType() const;

获取维度类型

参数:无

返回:维度类型


getHandleDataType

HandleDataType getHandleDataType() const;

处理数据类型,当数据类型代码为halide_type_handle时使用

参数:无

返回:处理数据类型


setType

void setType(int type);

设置数据类型

参数:

  • type 定义在“Type_generated.h”中的数据类型

返回:void


getType

inline halide_type_t getType() const {
    return mBuffer.type;
};

获取数据类型

参数:无

返回:数据类型


host

template <typename T>
T* host() const {
    return (T*)mBuffer.host;
};

访问Host内存,数据类型用“T”表示

参数:无

返回:“T”类型的数据点


deviceId

uint64_t deviceId() const {
    return mBuffer.device;
};

访问设备内存

参数:无

返回:设备数据ID,ID的含义因后端而异


dimensions

int dimensions() const {
    return mBuffer.dimensions;
};

张量维度

参数:无

返回:维度


shape

std::vector<int> shape() const;

得到所有维度的范围

参数:无

返回:维度的程度


size

int size() const;

考虑到重新排序标志,计算存储数据所需的字节数

参数:无

返回:存储数据所需的字节数


elementSize

inline int elementSize() const {
    return size() / mBuffer.type.bytes();
};

考虑到重新排序标志,计算存储数据所需的元素数量

参数:无

返回:存储数据所需的元素数量


width

inline int width() const {
    if (getDimensionType() == TENSORFLOW) {
        return mBuffer.dim[2].extent;
    }
    return mBuffer.dim[3].extent;
};

张量宽度

参数:无

返回:张量宽度


height

inline int height() const {
    if (getDimensionType() == TENSORFLOW) {
        return mBuffer.dim[1].extent;
    }
    return mBuffer.dim[2].extent;
};

张量高度

参数:无

返回:张量高度


channel

inline int channel() const {
    if (getDimensionType() == TENSORFLOW) {
        return mBuffer.dim[3].extent;
    }
    return mBuffer.dim[1].extent;
};

张量通道

参数:无

返回:张量通道


batch

inline int batch() const {
    return mBuffer.dim[0].extent;
};

张量批量

参数:无

返回:张量批量


stride

inline int stride(int index) const {
    return mBuffer.dim[index].stride;
};

返回张量的步幅

参数:

  • index 指定维度

返回:张量的步幅


length

inline int length(int index) const {
    return mBuffer.dim[index].extent;
};

返回张量的长度

参数:

  • index 指定维度

返回:张量的长度


setStride

inline void setStride(int index, int stride) {
    mBuffer.dim[index].stride = stride;
};

设置张量的步幅

参数:

  • index 指定维度

  • stride 步幅

返回:void


setLength

inline void setLength(int index, int length) {
    mBuffer.dim[index].extent = length;
};

设置张量的长度

参数:

  • index 指定维度

  • stride 长度

返回:void


print

void print() const;

打印张量数据,仅供调试使用

参数:无

返回:void


printShape

void printShape() const;

打印张量的形状

参数:无

返回:void


map

void* map(MapType mtype, DimensionType dtype);

GPU张量,以获得主机ptr

参数:

  • mtype 张量映射类型:读或写

  • dtype 张量类型

返回:主机ptr


unmap

void unmap(MapType mtype, DimensionType dtype, void* mapPtr);

GPU张量

参数:

  • mtype 张量映射类型:读或写

  • dtype 张量类型

  • mapPtr 主机ptr

返回:void


wait

int wait(MapType mtype, bool finish);

等待直到张量准备好读/写

参数:

  • mtype 等待读取或写入

  • finish 等待命令刷新或完成

返回:读/写

ImageProcess

class ImageProcess

枚举类

ImageFormat

enum ImageFormat {
    RGBA     = 0,
    RGB      = 1,
    BGR      = 2,
    GRAY     = 3,
    BGRA     = 4,
    YCrCb    = 5,
    YUV      = 6,
    HSV      = 7,
    XYZ      = 8,
    BGR555   = 9,
    BGR565   = 10,
    YUV_NV21 = 11,
    YUV_NV12 = 12,
    YUV_I420 = 13,
    HSV_FULL = 14,
};
value name 说明
0 RGBA
1 RGB
2 BGR
3 GRAY
4 BGRA
5 YCrCb
6 YUV
7 HSV
8 XYZ
9 BGR555
10 BGR565
11 YUV_NV21
12 YUV_NV12
13 YUV_I420
14 HSV_FULL

Filter

enum Filter {
    NEAREST       = 0,
    BILINEAR      = 1,
    BICUBIC       = 2,
};
value name 说明
0 NEAREST 最近点
1 BILINEAR 双线性的
2 BICUBIC 双三次的

Wrap

enum Wrap {
    CLAMP_TO_EDGE  = 0,
    ZERO           = 1,
    REPEAT         = 2
};
value name 说明
0 CLAMP_TO_EDGE 固定边缘的
1 ZERO
1 REPEAT 重复

成员函数


create

static ImageProcess* create(const Config& config, const Tensor* dstTensor = nullptr);

为给定张量创建给定配置的图像处理

参数:

  • config 给定配置

  • dstTensor 给定张量,默认为nullptr

返回:图像处理器


create

static ImageProcess* create(const ImageFormat sourceFormat = RGBA, const ImageFormat destFormat = RGBA,
                            const float* means = nullptr, const int meanCount = 0, const float* normals = nullptr,
                            const int normalCount = 0, const Tensor* dstTensor = nullptr);

为给定张量创建给定配置的图像处理

参数:

  • sourceFormat 源图像格式,默认为RGBA

  • destFormat 目的图像格式,默认为RGBA

  • means 给定方法,默认为nullptr

  • meanCount 给定方法数量,默认为0

  • normals 给定常量,默认为nullptr

  • normalCount 给定常量数量,默认为0

  • dstTensor 给定张量,默认为nullptr

返回:图像处理器


~ImageProcess

析构函数


destroy

static void destroy(ImageProcess* imageProcess);

释放图像资源

参数:

  • imageProcess 被释放的图像进程

返回:void


matrix

inline const Matrix& matrix() const {
        return mTransform;
    };

得到仿射变换矩阵

参数:无

返回:仿射变换矩阵


setMatrix

void setMatrix(const Matrix& matrix);

设置仿射变换矩阵

参数:

  • matrix 源仿射变换矩阵

返回:void


convert

ErrorCode convert(const uint8_t* source, int iw, int ih, int stride, Tensor* dest);

将源数据转换为给定的张量

参数:

  • source 源资源数据

  • iw 源资源数据的宽度

  • ih 源资源数据的高度

  • stride 每行的元素数,100宽RGB包含至少300个元素

  • dest 目的张量

返回:结果code


convert

ErrorCode convert(const uint8_t* source, int iw, int ih, int stride, void* dest, int ow, int oh, int outputBpp = 0,
                  int outputStride = 0, halide_type_t type = halide_type_of<float>());

将源数据转换为给定的张量

参数:

  • source 源资源数据

  • iw 源资源数据的宽度

  • ih 源资源数据的高度

  • stride 每行的元素数,100宽RGB包含至少300个元素

  • dest 目的张量

  • ow 输出宽度

  • oh 输出高度

  • outputBpp 如果是0,设置为保存和config.destFormat,默认为0

  • outputStride 如果为0,设置为ow * outputBpp,默认为0

  • type 支持halide_type_of<uint8_t>halide_type_of<float>,默认为halide_type_of<float>

返回:结果code


createImageTensor

template <typename T>
static Tensor* createImageTensor(int w, int h, int bpp, void* p = nullptr) {
    return createImageTensor(halide_type_of<T>(), w, h, bpp, p);
}
static Tensor* createImageTensor(halide_type_t type, int w, int h, int bpp, void* p = nullptr);

用给定的数据创建张量

参数:

  • type 只支持halide_type_of和halide_type_of

  • w 图像宽度

  • h 图像高度

  • bpp 每像素字节

  • p 像素数据指针,默认为nullptr

返回:创建的张量


setPadding

void setPadding(uint8_t value) {
    mPaddingValue = value;
};

当wrap=ZERO时设置填充值

参数:

  • value 填充值

返回:void


setDraw

void setDraw();

设置绘制模式

参数:无

返回:void


draw

void draw(uint8_t* img, int w, int h, int c, const int* regions, int num, const uint8_t* color);

绘制img区域的颜色

参数:

  • img 要绘制的图像

  • w 图像的宽度

  • h 图像的高度

  • c 图像的通道

  • regions 要绘制的区域,大小为[num * 3]包含num x {y, xl, xr}

  • num 区域数量

  • color 要绘制的颜色

返回:void

Matrix

class Matrix

枚举类

TypeMask

enum TypeMask {
    kIdentity_Mask    = 0,
    kTranslate_Mask   = 0x01,
    kScale_Mask       = 0x02,
    kAffine_Mask      = 0x04,
    kPerspective_Mask = 0x08
};
value name 说明
0 kIdentity_Mask 单位矩阵
1 kTranslate_Mask 转换矩阵
2 kScale_Mask 缩放矩阵
3 kAffine_Mask 倾斜或旋转矩阵
4 kPerspective_Mask 透视矩阵

ScaleToFit

enum ScaleToFit {
    kFill_ScaleToFit,
    kStart_ScaleToFit,
    kCenter_ScaleToFit,
    kEnd_ScaleToFit
};
value name 说明
0 kFill_ScaleToFit 缩放x和y来填充目标矩形
1 kStart_ScaleToFit 在左和上缩放和对齐
2 kCenter_ScaleToFit 中心缩放和对齐
3 kEnd_ScaleToFit 在右边和底部缩放和对齐

成员函数


MakeScale

static Matrix MakeScale(float sx, float sy) {
    Matrix m;
    m.setScale(sx, sy);
    return m;
};

设置矩阵缩放(sx, sy),返回矩阵:| sx 0 0 | | 0 sy 0 | | 0 0 1 |

参数:

  • sx 水平比例因子

  • sy 垂直比例因子

返回:缩放矩阵


MakeScale

static Matrix MakeScale(float scale) {
    Matrix m;
    m.setScale(scale, scale);
    return m;
};

设置矩阵缩放(scale, scale),返回矩阵:| scale 0 0 | | 0 scale 0 | | 0 0 1 |

参数:

  • scale 水平比例因子

返回:缩放矩阵


MakeTrans

static Matrix MakeTrans(float dx, float dy) {
    Matrix m;
    m.setTranslate(dx, dy);
    return m;
};

设置矩阵平移到(dx, dy),返回矩阵: | 1 0 dx | | 0 1 dy | | 0 0 1 |

参数:

  • dx 水平平移

  • dy 垂直平移

返回:平移矩阵


MakeAll

static Matrix MakeAll(float scaleX, float skewX, float transX, float skewY, float scaleY, float transY, float pers0,
                      float pers1, float pers2) {
    Matrix m;
    m.setAll(scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2);
    return m;
};

设置矩阵: | scaleX skewX transX | | skewY scaleY transY | | pers0 pers1 pers2 |

参数:

  • scaleX 水平比例因子

  • skewX 水平倾斜因子

  • transX 水平平移

  • skewY 垂直倾斜因子

  • scaleY 垂直比例因子

  • transY 垂直平移

  • pers0 输入x轴透视因子

  • pers1 输入y轴透视因子

  • pers2 透视比例因子

返回:矩阵


getType

TypeMask getType() const {
    if (fTypeMask & kUnknown_Mask) {
            fTypeMask = this->computeTypeMask();
    }
    return (TypeMask)(fTypeMask & 0xF);
};

返回一个位字段,描述矩阵可能进行的转换执行,位域是保守计算的。例如,当设置kPerspective_Mask时,all其他位被设置

参数:无

返回:kIdentity_Mask或kTranslate_Mask、kScale_Mask、kIdentity_Mask的组合kAffine_Mask, kPerspective_Mask


isIdentity

bool isIdentity() const {
    return this->getType() == 0;
};

如果矩阵是一致的则返回true,单位矩阵:| 1 0 0 | | 0 1 0 | | 0 0 1 |

参数:无

返回:如果矩阵是一致的则返回true


isScaleTranslate

bool isScaleTranslate() const {
    return !(this->getType() & ~(kScale_Mask | kTranslate_Mask));
};

矩阵可以是identity,只包含缩放元素,只包含平移元素,或同时包含二者。矩阵形式: | scale-x 0 translate-x | | 0 scale-y translate-y | | 0 0 1 |

参数:无

返回:如果矩阵是一致的,或者缩放,平移,或者两者兼而有之,则返回true


isTranslate

bool isTranslate() const {
    return !(this->getType() & ~(kTranslate_Mask));
};

矩阵形式: | 1 0 translate-x | | 0 1 translate-y | | 0 0 1 |

参数:无

返回:如果矩阵是一致的或者平移的,则返回true


rectStaysRect

bool rectStaysRect() const {
    if (fTypeMask & kUnknown_Mask) {
        fTypeMask = this->computeTypeMask();
    }
    return (fTypeMask & kRectStaysRect_Mask) != 0;
};

如果矩阵将一个矩形映射到另一个,则返回true,如果为true,矩阵是一致的,或缩放,或旋转90度的倍数,或者轴上的镜像。在所有情况下,矩阵也可以有平移。矩阵形式可以是: | scale-x 0 translate-x | | 0 scale-y translate-y | | 0 0 1 |

    or

        |    0     rotate-x translate-x |
        | rotate-y    0     translate-y |
        |    0        0          1      | 

对于非零的缩放-x,缩放-y,旋转-x和旋转-y,也称为preservesAxisAlignment(),使用提供更好内联文档的方法

参数:无

返回:如果矩阵将一个矩形映射到另一个,则返回true


preservesAxisAlignment

bool preservesAxisAlignment() const {
    return this->rectStaysRect();
};

矩阵将Rect映射到另一个Rect。如果为真,矩阵为恒等,或缩放,或旋转90度,或在轴上反射。在所有情况下,矩阵也可以有翻译。矩阵形式可以是: | scale-x 0 translate-x | | 0 scale-y translate-y | | 0 0 1 |

    or

        |    0     rotate-x translate-x |
        | rotate-y    0     translate-y |
        |    0        0          1      |

对于非零的缩放-x,缩放-y,旋转-x和旋转-y,也称为rectStaysRect(),使用提供更好内联文档的方法。

参数:无

返回:如果矩阵将一个矩形映射到另一个,则返回true


operator

float operator[](int index) const {
    MNN_ASSERT((unsigned)index < 9);
    return fMat[index];
};

返回一个矩阵值,如果索引超出范围并且定义了SK_DEBUG,则抛出

参数:

  • index kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, kMPersp0, kMPersp1, kMPersp2其中之一

返回:索引对应的值


get

float get(int index) const {
    MNN_ASSERT((unsigned)index < 9);
    return fMat[index];
};

返回一个矩阵值,如果索引超出范围并且定义了SK_DEBUG,则抛出

参数:

  • index kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, kMPersp0, kMPersp1, kMPersp2其中之一

返回:索引对应的值


getScaleX

float getScaleX() const {
    return fMat[kMScaleX];
};

返回比例因子 * x轴输入,影响x轴输出。通过mapPoints()方法,缩放点沿着x轴

参数:无

返回:水平缩放因子


getScaleY

float getScaleY() const {
    return fMat[kMScaleY];
};

返回比例因子 * y轴输入,影响y轴输出。通过mapPoints()方法,缩放点沿着y轴

参数:无

返回:垂直缩放因子


getSkewY

float getSkewY() const {
    return fMat[kMSkewY];
};

返回比例因子 * y轴输入,影响y轴输出。通过mapPoints()方法,沿着y轴倾斜角度。倾斜两个轴可以旋转角度

参数:无

返回:垂直倾斜因子


getSkewX

float getSkewX() const {
    return fMat[kMSkewX];
};

返回比例因子 * x轴输入,影响x轴输出。通过mapPoints()方法,沿着x轴倾斜角度。倾斜两个轴可以旋转角度

参数:无

返回:水平倾斜因子


getTranslateX

float getTranslateX() const {
    return fMat[kMTransX];
};

返回用于x轴输出的平移。通过mapPoints()方法,沿着x轴移动

参数:无

返回:水平移动因子


getTranslateY

float getTranslateY() const {
    return fMat[kMTransY];
};

返回用于y轴输出的平移。通过mapPoints()方法,沿着y轴移动

参数:无

返回:垂直移动因子


getPerspX

float getPerspX() const {
    return fMat[kMPersp0];
};

返回x轴缩放输入相对于y轴缩放输入的缩放因子

参数:无

返回:x轴输入的角度因子


getPerspY

float getPerspY() const {
    return fMat[kMPersp1];
};

返回y轴缩放输入相对于x轴缩放输入的缩放因子

参数:无

返回:y轴输入的角度因子


operator

float& operator[](int index) {
    MNN_ASSERT((unsigned)index < 9);
    this->setTypeMask(kUnknown_Mask);
    return fMat[index];
};

返回可写的矩阵值,如果索引超出范围并且定义了SK_DEBUG,则抛出。清除内部缓存,预计调用者将更改矩阵值。下一次读取矩阵状态可能会重新计算缓存,随后对矩阵值的写入必须在dirtyMatrixTypeCache()之后。

参数:

  • index kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, kMPersp0, kMPersp1, kMPersp2其中之一

返回:索引对应的可写值


set

void set(int index, float value) {
    MNN_ASSERT((unsigned)index < 9);
    fMat[index] = value;
    this->setTypeMask(kUnknown_Mask);
};

返回矩阵值,如果索引超出范围并且定义了SK_DEBUG,则抛出。比运营商安全,始终维护内部缓存

参数:

  • index kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, kMPersp0, kMPersp1, kMPersp2其中之一

  • value 存储在矩阵中的标量

返回:void


setScaleX

void setScaleX(float v) {
    this->set(kMScaleX, v);
};

设置水平比例因子

参数:

  • v 存储的水平比例因子

返回:void


setScaleY

void setScaleY(float v) {
    this->set(kMScaleY, v);
};

设置垂直比例因子

参数:

  • v 存储的垂直比例因子

返回:void


setSkewY

void setSkewY(float v) {
    this->set(kMSkewY, v);
};

设置垂直倾斜因子

参数:

  • v 存储的垂直倾斜因子

返回:void


setSkewX

void setSkewX(float v) {
    this->set(kMSkewX, v);
};

设置水平倾斜因子

参数:

  • v 存储的水平倾斜因子

返回:void


setTranslateX

void setTranslateX(float v) {
    this->set(kMTransX, v);
};

设置水平平移因子

参数:

  • v 存储的水平平移因子

返回:void


setTranslateY

void setTranslateY(float v) {
    this->set(kMTransY, v);
};

设置垂直平移因子

参数:

  • v 存储的垂直平移因子

返回:void


setPerspX

void setPerspX(float v) {
    this->set(kMPersp0, v);
};

设置输入x轴透视因子,它会导致mapXY()改变输入x轴值与输入y轴值成反比

参数:

  • v 存储的x轴透视因子

返回:void


setPerspY

void setPerspY(float v) {
    this->set(kMPersp1, v);
};

设置输入y轴透视因子,它会导致mapXY()以输入y轴值与输入x轴值成反比的方式改变输入y轴值

参数:

  • v 存储的y轴透视因子

返回:void


setAll

void setAll(float scaleX, float skewX, float transX, float skewY, float scaleY, float transY, float persp0,
            float persp1, float persp2) {
    fMat[kMScaleX] = scaleX;
    fMat[kMSkewX]  = skewX;
    fMat[kMTransX] = transX;
    fMat[kMSkewY]  = skewY;
    fMat[kMScaleY] = scaleY;
    fMat[kMTransY] = transY;
    fMat[kMPersp0] = persp0;
    fMat[kMPersp1] = persp1;
    fMat[kMPersp2] = persp2;
    this->setTypeMask(kUnknown_Mask);
};

根据参数设置所有值,设置矩阵: | scaleX skewX transX | | skewY scaleY transY | | pers0 pers1 pers2 |

参数:

  • scaleX 存储的水平比例因子

  • skewX 存储的水平倾斜因子

  • transX 存储的水平平移因子

  • skewY 存储的垂直倾斜因子

  • scaleY 存储的垂直比例因子

  • transY 存储的垂直平移因子

  • pers0 存储的输入x轴透视因子

  • pers1 存储的输入y轴透视因子

  • pers2 存储的透视比例因子

返回:矩阵


get9

void get9(float buffer[9]) const {
    memcpy(buffer, fMat, 9 * sizeof(float));
};

将矩阵中包含的9个标量值按成员值升序复制到缓冲区:kMScaleX、kMSkewX、kMTransX、kMSkewY、kMScaleY、kMTransY、kMPersp0、kMPersp1、kMPersp2

参数:

  • buffer[9] 存储九个标量值

返回:void


set9

void set9(const float buffer[9]);

设置矩阵缓冲区中的9个标量值,成员值按升序排列: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, kMPersp0, kMPersp1, kMPersp2 设置矩阵: | buffer[0] buffer[1] buffer[2] | | buffer[3] buffer[4] buffer[5] | | buffer[6] buffer[7] buffer[8] | 将来,set9后跟get9可能不会返回相同的值。由于矩阵映射非齐次坐标,缩放所有9个值产生了等效变换,可能会提高精度

参数:

  • buffer[9] 九个标量值

返回:void


reset

void reset();

设置矩阵单位,这对映射的点没有影响。设置矩阵:| 1 0 0 | | 0 1 0 | | 0 0 1 | 也称为setIdentity(),使用提供更好内联的那个文档

参数:无

返回:void


setIdentity

void setIdentity() {
    this->reset();
};

设置矩阵单位,这对映射的点没有影响。设置矩阵:| 1 0 0 | | 0 1 0 | | 0 0 1 | 也称为reset(),使用提供更好内联的那个文档

参数:无

返回:void


setTranslate

void setTranslate(float dx, float dy);

设置矩阵平移到(dx, dy)

参数:

  • dx 水平平移

  • dy 垂直平移

返回:void


setScale

void setScale(float sx, float sy, float px, float py);

设置矩阵缩放sx和sy,大约一个枢轴点(px, py),当映射到矩阵时,枢轴点是不变的

参数:

  • sx 水平缩放因子

  • sy 垂直缩放因子

  • px x轴

  • py y轴

返回:void


setScale

void setScale(float sx, float sy);

设置矩阵在(0,0)的枢轴点处按sx和sy缩放

参数:

  • sx 水平缩放因子

  • sy 垂直缩放因子

返回:void


setRotate

void setRotate(float degrees, float px, float py);

设置矩阵以轴点(px, py)旋转角度,当映射到矩阵时,枢轴点是不变的,正度顺时针旋转

参数:

  • degrees 水平坐标轴与垂直坐标轴的夹角

  • sx 水平缩放因子

  • sy 垂直缩放因子

返回:void


setSinCos

void setSinCos(float sinValue, float cosValue, float px, float py);

设置矩阵旋转sinValue和cosValue,旋转一个轴心点(px, py)。当映射到矩阵时,轴点是不变的,向量(sinValue, cosValue)描述相对于(0,1)的旋转角度,向量长度指定缩放

参数:

  • sinValue 旋转向量x轴部分

  • cosValue 旋转向量y轴部分

  • sx 水平缩放因子

  • sy 垂直缩放因子

返回:void


setSinCos

void setSinCos(float sinValue, float cosValue);

设置矩阵的sinValue和cosValue旋转,大约在(0,0)的轴点。向量(sinValue, cosValue)描述相对于(0,1)的旋转角度,向量长度指定缩放

参数:

  • sinValue 旋转向量x轴部分

  • cosValue 旋转向量y轴部分

返回:void


setSkew

void setSkew(float kx, float ky, float px, float py);

设置矩阵在kx和ky上的倾斜,关于一个轴点(px, py),当映射到矩阵时,轴点是不变的

参数:

  • kx 水平倾斜因子

  • ky 垂直倾斜因子

  • px x轴

  • py y轴

返回:void


setConcat

void setConcat(const Matrix& a, const Matrix& b);

将矩阵设为矩阵a乘以矩阵b,a或b都可以是这个 假定: | A B C | | J K L | a = | D E F |, b = | M N O | | G H I | | P Q R | 设置矩阵: | A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR | a * b = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR | | G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR |

参数:

  • a 乘法表达式的左边矩阵

  • b 乘法表达式的右边矩阵

返回:void


preTranslate

void preTranslate(float dx, float dy);

设置矩阵到矩阵乘以由平移(dx, dy)构造的矩阵,这可以被认为是在应用矩阵之前移动要映射的点 假定: | A B C | | 1 0 dx | Matrix = | D E F |, T(dx, dy) = | 0 1 dy | | G H I | | 0 0 1 | 设置矩阵: | A B C | | 1 0 dx | | A B Adx+Bdy+C | Matrix * T(dx, dy) = | D E F | | 0 1 dy | = | D E Ddx+Edy+F | | G H I | | 0 0 1 | | G H Gdx+Hdy+I |

参数:

  • dx 应用矩阵前在x轴平移

  • dy 应用矩阵前在y轴平移

返回:void


preScale

void preScale(float sx, float sy, float px, float py);

在应用矩阵之前缩放一个轴点 假定: | A B C | | 1 0 dx | Matrix = | D E F |, T(dx, dy) = | 0 1 dy | | G H I | | 0 0 1 | 目标:

        dx = px - sx * px
        dy = py - sy * py

设置矩阵: | A B C | | 1 0 dx | | A B Adx+Bdy+C | Matrix * T(dx, dy) = | D E F | | 0 1 dy | = | D E Ddx+Edy+F | | G H I | | 0 0 1 | | G H Gdx+Hdy+I |

参数:

  • sx 水平比例因子

  • sy 垂直比例因子

  • px x轴

  • py y轴

返回:void


preScale

void preScale(float sx, float sy);

在应用矩阵之前缩放原点 假定: | A B C | | sx 0 0 | Matrix = | D E F |, S(sx, sy) = | 0 sy 0 | | G H I | | 0 0 1 | 目标: c = cos(degrees) s = sin(degrees) dx = s * py + (1 - c) * px dy = -s * px + (1 - c) * py

设置矩阵: | A B C | | c -s dx | | Ac+Bs -As+Bc Adx+Bdy+C | Matrix * R(degrees, px, py) = | D E F | | s c dy | = | Dc+Es -Ds+Ec Ddx+Edy+F | | G H I | | 0 0 1 | | Gc+Hs -Gs+Hc Gdx+Hdy+I | 参数:

  • sx 水平比例因子

  • sy 垂直比例因子

返回:void


preRotate

void preRotate(float degrees, float px, float py);

在应用矩阵之前绕一个轴点旋转,顺时针旋转为正 假定: | A B C | | c -s dx | Matrix = | D E F |, R(degrees, px, py) = | s c dy | | G H I | | 0 0 1 | 目标: c = cos(degrees) s = sin(degrees) dx = s * py + (1 - c) * px dy = -s * px + (1 - c) * py 设置矩阵:

                                      | A B C | | c -s dx |   | Ac+Bs -As+Bc A*dx+B*dy+C |
        Matrix * R(degrees, px, py) = | D E F | | s  c dy | = | Dc+Es -Ds+Ec D*dx+E*dy+F |
                                      | G H I | | 0  0  1 |   | Gc+Hs -Gs+Hc G*dx+H*dy+I |

参数:

  • degrees 坐标轴与垂直坐标轴的夹角

  • px x轴

  • py y轴

返回:void


preRotate

void preRotate(float degrees);

应用矩阵之前绕原点旋转,顺时针旋转为正 假定: | A B C | | c -s dx | Matrix = | D E F |, R(degrees, px, py) = | s c dy | | G H I | | 0 0 1 | 目标: c = cos(degrees) s = sin(degrees) 设置矩阵: | A B C | | c -s 0 | | Ac+Bs -As+Bc C | Matrix * R(degrees, px, py) = | D E F | | s c 0 | = | Dc+Es -Ds+Ec F | | G H I | | 0 0 1 | | Gc+Hs -Gs+Hc I |

参数:

  • degrees 坐标轴与垂直坐标轴的夹角

返回:void


preSkew

void preSkew(float kx, float ky, float px, float py);

应用矩阵之前绕一个轴点倾斜 假定: | A B C | | 1 kx dx | Matrix = | D E F |, K(kx, ky, px, py) = | ky 1 dy | | G H I | | 0 0 1 | 目标: dx = -kx * py dy = -ky * px 设置矩阵: | A B C | | 1 kx dx | | A+Bky Akx+B Adx+Bdy+C | Matrix * K(kx, ky, px, py) = | D E F | | ky 1 dy | = | D+Eky Dkx+E Ddx+Edy+F | | G H I | | 0 0 1 | | G+Hky Gkx+H Gdx+Hdy+I |

参数:

  • kx 水平倾斜因子

  • ky 垂直倾斜因子

  • px x轴

  • py y轴

返回:void


preSkew

void preSkew(float kx, float ky);

应用矩阵之前绕原点倾斜 假定: | A B C | | 1 kx 0 | Matrix = | D E F |, K(kx, ky) = | ky 1 0 | | G H I | | 0 0 1 | 设置矩阵: | A B C | | 1 kx 0 | | A+Bky Akx+B C | Matrix * K(kx, ky) = | D E F | | ky 1 0 | = | D+Eky Dkx+E F | | G H I | | 0 0 1 | | G+Hky Gkx+H I |

参数:

  • kx 水平倾斜因子

  • ky 垂直倾斜因子

返回:void


preConcat

void preConcat(const Matrix& other);

在应用矩阵之前的映射 假定: | A B C | | J K L | Matrix = | D E F |, other = | M N O | | G H I | | P Q R | 设置矩阵: | A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR | Matrix * other = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR | | G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR |

参数:

  • other 乘法表达式的右边矩阵

返回:void


postTranslate

void postTranslate(float dx, float dy);

应用矩阵后移动被映射的点 假定: | J K L | | 1 0 dx | Matrix = | M N O |, T(dx, dy) = | 0 1 dy | | P Q R | | 0 0 1 | 设置矩阵: | 1 0 dx | | J K L | | J+dxP K+dxQ L+dxR | T(dx, dy) * Matrix = | 0 1 dy | | M N O | = | M+dyP N+dyQ O+dyR | | 0 0 1 | | P Q R | | P Q R |

参数:

  • dx 应用矩阵后的x轴平移

  • dy 应用矩阵后的y轴平移

返回:void


postScale

void postScale(float sx, float sy, float px, float py);

应用矩阵后缩放一个轴点 假定: | J K L | | sx 0 dx | Matrix = | M N O |, S(sx, sy, px, py) = | 0 sy dy | | P Q R | | 0 0 1 | 目标: dx = px - sx * px dy = py - sy * py 设置矩阵: | sx 0 dx | | J K L | | sxJ+dxP sxK+dxQ sxL+dx+R | S(sx, sy, px, py) * Matrix = | 0 sy dy | | M N O | = | syM+dyP syN+dyQ syO+dy*R | | 0 0 1 | | P Q R | | P Q R | 参数:

  • sx 水平比例因子

  • sy 垂直比例因子

  • px x轴

  • py y轴

返回:void


postScale

void postScale(float sx, float sy);

应用矩阵之后关于原点的缩放 假定: | J K L | | sx 0 0 | Matrix = | M N O |, S(sx, sy) = | 0 sy 0 | | P Q R | | 0 0 1 | 设置矩阵: | sx 0 0 | | J K L | | sxJ sxK sxL | S(sx, sy) * Matrix = | 0 sy 0 | | M N O | = | syM syN syO | | 0 0 1 | | P Q R | | P Q R | 参数:

  • sx 水平比例因子

  • sy 垂直比例因子

返回:void


postIDiv

bool postIDiv(int divx, int divy);

应用矩阵之后按照(1/divx, 1/divy)比例缩放一个枢轴点 假定: | J K L | | sx 0 0 | Matrix = | M N O |, I(divx, divy) = | 0 sy 0 | | P Q R | | 0 0 1 | 目标: sx = 1 / divx sy = 1 / divy 设置矩阵: | sx 0 0 | | J K L | | sxJ sxK sxL | I(divx, divy) * Matrix = | 0 sy 0 | | M N O | = | syM syN syO | | 0 0 1 | | P Q R | | P Q R | 参数:

  • divx x逆比例的整数除数

  • divy y逆比例的整数除数

返回:缩放成功返回true


postRotate

void postRotate(float degrees, float px, float py);

应用矩阵后绕一个枢轴点旋转 假定: | J K L | | c -s dx | Matrix = | M N O |, R(degrees, px, py) = | s c dy | | P Q R | | 0 0 1 | 目标: c = cos(degrees) s = sin(degrees) dx = s * py + (1 - c) * px dy = -s * px + (1 - c) * py 设置矩阵: |c -s dx| |J K L| |cJ-sM+dxP cK-sN+dxQ cL-sO+dx+R| R(degrees, px, py) * Matrix = |s c dy| |M N O| = |sJ+cM+dyP sK+cN+dyQ sL+cO+dy*R| |0 0 1| |P Q R| | P Q R| 参数:

  • degrees 坐标轴与垂直坐标轴的夹角

  • px x轴

  • py y轴

返回:void


postRotate

void postRotate(float degrees);

应用矩阵后绕原点旋转 假定: | J K L | | c -s 0 | Matrix = | M N O |, R(degrees, px, py) = | s c 0 | | P Q R | | 0 0 1 | 目标: c = cos(degrees) s = sin(degrees) 设置矩阵: | c -s dx | | J K L | | cJ-sM cK-sN cL-sO | R(degrees, px, py) * Matrix = | s c dy | | M N O | = | sJ+cM sK+cN sL+cO | | 0 0 1 | | P Q R | | P Q R | 参数:

  • degrees 坐标轴与垂直坐标轴的夹角

返回:void


postSkew

void postSkew(float kx, float ky, float px, float py);

应用矩阵后绕一个枢轴点倾斜 假定: | J K L | | 1 kx dx | Matrix = | M N O |, K(kx, ky, px, py) = | ky 1 dy | | P Q R | | 0 0 1 | 目标: dx = -kx * py dy = -ky * px 设置矩阵: | 1 kx dx| |J K L| |J+kxM+dxP K+kxN+dxQ L+kxO+dx+R| K(kx, ky, px, py) * Matrix = |ky 1 dy| |M N O| = |kyJ+M+dyP kyK+N+dyQ kyL+O+dy*R| | 0 0 1| |P Q R| | P Q R| 参数:

  • kx 水平倾斜因子

  • ky 垂直倾斜因子

  • px x轴

  • py y轴

返回:void


postSkew

void postSkew(float kx, float ky);

应用矩阵后绕一个枢轴点倾斜 假定: | J K L | | 1 kx 0 | Matrix = | M N O |, K(kx, ky) = | ky 1 0 | | P Q R | | 0 0 1 | 设置矩阵: | 1 kx 0 | | J K L | | J+kxM K+kxN L+kxO | K(kx, ky) * Matrix = | ky 1 0 | | M N O | = | kyJ+M kyK+N kyL+O | | 0 0 1 | | P Q R | | P Q R | 参数:

  • kx 水平倾斜因子

  • ky 垂直倾斜因子

返回:void


postConcat

void postConcat(const Matrix& other);

设置矩阵到矩阵其他乘以矩阵,这可以被认为是映射后,其他应用矩阵 假定: | J K L | | A B C | Matrix = | M N O |, other = | D E F | | P Q R | | G H I | 设置矩阵: | A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR | other * Matrix = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR | | G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR | 参数:

  • other 乘法表达式的左边矩阵

返回:void


setRectToRect

bool setRectToRect(const Rect& src, const Rect& dst, ScaleToFit stf);

设置矩阵缩放并将src Rect转换为dst,recf选择映射是否完全填充dst或保留长宽比,以及如何在dst内对齐src。如果src为空则返回false,并设置矩阵为identity。如果dst为空则返回true 设置矩阵:| 0 0 0 | | 0 0 0 | | 0 0 1 | 参数:

  • src 要映射的rect

  • dst 要映射到的rect

  • stf kFill_ScaleToFit, kStart_ScaleToFit,kCenter_ScaleToFit, kEnd_ScaleToFit其中之一

返回:如果矩阵可以表示Rect映射,则为true


MakeRectToRect

static Matrix MakeRectToRect(const Rect& src, const Rect& dst, ScaleToFit stf) {
    Matrix m;
    m.setRectToRect(src, dst, stf);
    return m;
};

返回矩阵设置为缩放并将src Rect转换为dst,recf选择映射是否完全填充dst或保留长宽比,以及如何在dst内对齐src。如果src为空,则返回标识矩阵。如果dst为空,返回设置矩阵:| 0 0 0 | | 0 0 0 | | 0 0 1 | 参数:

  • src 要映射的rect

  • dst 要映射到的rect

  • stf kFill_ScaleToFit, kStart_ScaleToFit,kCenter_ScaleToFit, kEnd_ScaleToFit其中之一

返回:将src映射到dst的矩阵


setPolyToPoly

bool setPolyToPoly(const Point src[], const Point dst[], int count);

设置“矩阵”将src映射到dst,Count必须为0或更大,4或更小。 如果count为零,设置Matrix为identity并返回true。 如果count为1,设置Matrix转换并返回true。 如果count是两个或更多,设置矩阵映射点,如果可能;返回false 如果矩阵不能被构造。如果计数是4,矩阵可能包括透视。

参数:

  • src[] 要映射的rect

  • dst[] 要映射到的rect

  • count 在scr和dst中点的数量

返回:如果矩阵构造成功,返回true


invert

bool invert(Matrix* inverse) const {
    if (this->isIdentity()) {
        if (inverse) {
            inverse->reset();
        }
        return true;
    }
    return this->invertNonIdentity(inverse);
};

矩阵反转,几何上,如果矩阵从源映射到目标,则逆矩阵从目标映射到源。如果矩阵不能被反转,逆矩阵不变

参数:

  • inverse 要被反转的矩阵,可能是nullptr

返回:矩阵反转成功,返回true


SetAffineIdentity

static void SetAffineIdentity(float affine[6]);

在主序列中用标识值填充仿射 设置仿射: | 1 0 0 | | 0 1 0 | OpenGL和XPS在主序列中仿射3x2矩阵

参数:

  • affine 3x2仿射矩阵

返回:void


asAffine

bool asAffine(float affine[6]) const;

在主序列中填充仿射 设置仿射: | scale-x skew-x translate-x | | skew-y scale-y translate-y | 如果矩阵包含透视图,则返回false并保持仿射不变

参数:

  • affine 3x2仿射矩阵,可能是nullptr

返回:如果矩阵不包含透视图,则返回true


setAffine

void setAffine(const float affine[6]);

将矩阵设置为仿射值,按主序列传递,给定仿射,列|行 例如: | scale-x skew-x translate-x | | skew-y scale-y translate-y |

矩阵是集合,行|列 例如: | scale-x skew-x translate-x | | skew-y scale-y translate-y | | 0 0 1 |

参数:

  • affine 3 x2仿射矩阵

返回:void


mapPoints

void mapPoints(Point dst[], const Point src[], int count) const {
    MNN_ASSERT((dst && src && count > 0) || 0 == count);
    MNN_ASSERT(src == dst || &dst[count] <= &src[0] || &src[count] <= &dst[0]);
    this->getMapPtsProc()(*this, dst, src, count);
};

映射指定长度计数的点数组,通过将每个点乘以矩阵来映射点 假定: | A B C | | x | Matrix = | D E F |, pt = | y | | G H I | | 1 | 目标: for (i = 0; i < count; ++i) { x = pts[i].fX y = pts[i].fY } 每一个点的计算结果为: |A B C| |x| Ax+By+C Dx+Ey+F Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ——- , ——- |G H I| |1| Gx+Hy+I Gx+Hy+I SRC和DST可能指向相同的存储空间

参数:

  • dst 映射点存储空间

  • src 变换点

  • count 变换点的个数

返回:void


mapPoints

void mapPoints(Point pts[], int count) const {
    this->mapPoints(pts, pts, count);
};

映射指定长度计数的点数组,通过将每个点乘以矩阵来映射点 假定: | A B C | | x | Matrix = | D E F |, pt = | y | | G H I | | 1 | 目标: for (i = 0; i < count; ++i) { x = pts[i].fX y = pts[i].fY } 每一个点的计算结果为: |A B C| |x| Ax+By+C Dx+Ey+F Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ——- , ——- |G H I| |1| Gx+Hy+I Gx+Hy+I SRC和DST可能指向相同的存储空间

参数:

  • pts 映射点存储空间

  • count 变换点的个数

返回:void


mapXY

void mapXY(float x, float y, Point* result) const {
    this->getMapXYProc()(*this, x, y, result);
};

点(x, y)的映射结果,点通过乘以矩阵来映射 假定: | A B C | | x | Matrix = | D E F |, pt = | y | | G H I | | 1 | 计算结果为: |A B C| |x| Ax+By+C Dx+Ey+F Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ——- , ——- |G H I| |1| Gx+Hy+I Gx+Hy+I

参数:

  • x 要映射的点的x轴值

  • y 要映射的点的y轴值

  • result 映射点的存储

返回:void


mapXY

Point mapXY(float x, float y) const {
    Point result;
    this->getMapXYProc()(*this, x, y, &result);
    return result;
};

点(x, y)的映射结果,点通过乘以矩阵来映射 假定: | A B C | | x | Matrix = | D E F |, pt = | y | | G H I | | 1 | 计算结果为: |A B C| |x| Ax+By+C Dx+Ey+F Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ——- , ——- |G H I| |1| Gx+Hy+I Gx+Hy+I

参数:

  • x 要映射的点的x轴值

  • y 要映射的点的y轴值

返回:映射点


mapRect

bool mapRect(Rect* dst, const Rect& src) const;

将dst设置为矩阵映射的src角的边界,如果映射的角是dst角则返回true。返回值与调用rectStaysRect()方法相同

参数:

  • dst 存储的映射点的边界

  • src 要绘制的rect

返回:如果DST等价于映射的SRC,则为True


mapRect

bool mapRect(Rect* rect) const {
    return this->mapRect(rect, *rect);
};

将rect设置为矩阵映射的矩形角的边界,如果映射的角是计算出来的矩形角,则返回true,返回值与调用rectStaysRect()相同

参数:

  • rect 要映射的rect,并存储映射角的边界

返回:如果结果等价于映射的SRC,则为True


mapRect

Rect mapRect(const Rect& src) const {
    Rect dst;
    (void)this->mapRect(&dst, src);
    return dst;
};

返回由矩阵映射的src角的边界

参数:

  • src 要绘制的矩形

返回:映射的边界


mapRectScaleTranslate

void mapRectScaleTranslate(Rect* dst, const Rect& src) const;

将dst设置为矩阵映射的src角的边界,如果矩阵包含了缩放或转换以外的元素:如果SK_DEBUG被定义了则生效,否则结果为undefined

参数:

  • dst 存储映射点的边界

  • src 要绘制的Rect

返回:void


cheapEqualTo

bool cheapEqualTo(const Matrix& m) const {
    return 0 == memcmp(fMat, m.fMat, sizeof(fMat));
};

如果矩阵等于m,则返回true;当zero值的符号不同时返回false;当一个矩阵为正zero另一个矩阵为负zero时,即使两个矩阵都包含NaN,也返回true。NaN从不等于任何值,包括它自己。为了提高性能,如果NaN值的位模式相等,则将其视为相等的位模式。

参数:

  • m 被比较的矩阵

返回:如果m和矩阵由相同的位模式表示,则为true


operator==

friend MNN_PUBLIC bool operator==(const Matrix& a, const Matrix& b);

比较a和b,如果a和b在数值上相等,返回true。即使zero值的符号不同,也返回true。如果其中一个矩阵包含NaN,则返回false,即使另一个矩阵也包含NaN

参数:

  • a 被比较的矩阵a

  • b 被比较的矩阵b

返回:当矩阵a和矩阵b在数值上相等时为true


operator!=

friend MNN_PUBLIC bool operator!=(const Matrix& a, const Matrix& b) {
    return !(a == b);
};

比较a和b,如果a和b在数值上不相等,则返回true。即使zero值的符号不同,也返回false。如果其中一个矩阵包含NaN,则返回true,即使另一个矩阵也包含NaN

参数:

  • a 被比较的矩阵a

  • b 被比较的矩阵b

返回:如果矩阵a和矩阵b在数值上不相等,则为true


dump

void dump() const;

将矩阵的文本表示形式写入标准输出,浮点值的写入精度有限,可能无法重建原始矩阵的输出

参数:无

返回:void


getMinScale

float getMinScale() const;

通过分解缩放和倾斜元素,返回矩阵的最小缩放因子。如果比例因子溢出或矩阵包含透视图,则返回-1

参数:无

返回:最小缩放因子


getMaxScale

float getMaxScale() const;

通过分解缩放和倾斜元素,返回矩阵的最大缩放因子。如果比例因子溢出或矩阵包含透视图,则返回-1

参数:无

返回:最大缩放因子


getMinMaxScales

bool getMinMaxScales(float scaleFactors[2]) const;

将scaleFactors[0]设置为最小缩放因子,将scaleFactors[1]设置为最大缩放因子。缩放因子是通过分解矩阵缩放和倾斜元素来计算的。如果找到scaleFactors则返回true,否则,返回false,并将scaleFactors设置为未定义的值

参数:

  • scaleFactors 最小和最大的缩放因子

返回:如果缩放因子计算正确,则返回true


I

static const Matrix& I();

返回对单位矩阵常量的引用,返回矩阵被设置为: | 1 0 0 | | 0 1 0 | | 0 0 1 |

参数:无

返回:单位矩阵常量


InvalidMatrix

static const Matrix& InvalidMatrix();

返回指向一个值无效的常量矩阵的引用,返回矩阵被设置为: | SK_ScalarMax SK_ScalarMax SK_ScalarMax | | SK_ScalarMax SK_ScalarMax SK_ScalarMax | | SK_ScalarMax SK_ScalarMax SK_ScalarMax |

参数:无

返回:无效的常量矩阵


Concat

static Matrix Concat(const Matrix& a, const Matrix& b) {
    Matrix result;
    result.setConcat(a, b);
    return result;
};

返回矩阵a乘以矩阵b 假定: | A B C | | J K L | a = | D E F |, b = | M N O | | G H I | | P Q R | 设置矩阵为: | A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR | a * b = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR | | G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR |

参数:

  • a 乘法表达式的左边矩阵

  • b 乘法表达式的右边矩阵

返回:无效的常量矩阵


dirtyMatrixTypeCache

void dirtyMatrixTypeCache() {
    this->setTypeMask(kUnknown_Mask);
};

将内部缓存设置为未知状态,用于在对操作符[](int index)返回的矩阵元素引用进行重复修改后强制更新

参数:无

返回:void


setScaleTranslate

void setScaleTranslate(float sx, float sy, float tx, float ty) {
    fMat[kMScaleX] = sx;
    fMat[kMSkewX]  = 0;
    fMat[kMTransX] = tx;

    fMat[kMSkewY]  = 0;
    fMat[kMScaleY] = sy;
    fMat[kMTransY] = ty;

    fMat[kMPersp0] = 0;
    fMat[kMPersp1] = 0;
    fMat[kMPersp2] = 1;

    unsigned mask = 0;
    if (sx != 1 || sy != 1) {
        mask |= kScale_Mask;
    }
    if (tx || ty) {
        mask |= kTranslate_Mask;
    }
    this->setTypeMask(mask | kRectStaysRect_Mask);
};

使用缩放和转换元素初始化矩阵 | sx 0 tx | | 0 sy ty | | 0 0 1 |

参数:

  • sx 水平缩放因子

  • sy 垂直缩放因子

  • tx 水平平移因子

  • ty 垂直平移因子

返回:void

VARP

class VARP

枚举类

Dimensionformat

enum Dimensionformat {
    NHWC,
    NC4HW4,
    NCHW
};
value name 说明
0 NHWC
1 NC4HW4
2 NCHW

InputType

enum InputType {
    INPUT = 0,
    CONSTANT = 1,
    TRAINABLE = 2,
};
value name 说明
0 INPUT 默认输入变量
1 CONSTANT 常量
2 TRAINABLE 可训练变量

成员函数


VARP

VARP(std::shared_ptr<Variable> c) {
    mContent = std::move(c);
};

调用移动构造函数,将c的对象移动至mContent对象

参数:

  • c Variable类型变量

返回:void


VARP

VARP(Variable* c) {
    mContent.reset(c);
};

重置mContent对象中的Variable对象

参数:

  • c Variable类型变量

返回:void


get

Variable* get() const  {
    return mContent.get();
};

获取mContent的Variable对象

参数:无

返回:Variable对象


VARP

VARP(const VARP& var) {
    mContent = var.mContent;
};

把var的content对象赋值给mContent变量

参数:

  • var 输入变量

返回:void


VARP

VARP(VARP&& var) {
    mContent = std::move(var.mContent);
};

调用移动构造函数,将var.mContent的对象移动至mContent对象

参数:

  • var 输入变量

返回:void


operator+

VARP operator+(VARP var) const;

VARP类型对象加法计算

参数:

  • var 输入变量

返回:VARP对象


operator-

VARP operator-(VARP var) const;

VARP类型对象减法计算

参数:

  • var 输入变量

返回:VARP对象


operator*

VARP operator*(VARP var) const;

VARP类型对象乘法计算

参数:

  • var 输入变量

返回:VARP对象


operator/

VARP operator/(VARP var) const;

VARP类型对象除法计算

参数:

  • var 输入变量

返回:VARP对象


mean

VARP mean(INTS dims) const;

计算均值

参数:

  • dims 一个向量

返回:VARP对象


sum

VARP sum(INTS dims) const;

计算和

参数:

  • dims 一个向量

返回:VARP对象


operator==

bool operator==(const VARP& var) const {
    return var.mContent == mContent;
};

重载相等运算符,判断var.mContent和mContent是否相等

参数:

  • var 输入变量

返回:true/false


operator<

bool operator<(const VARP& var) const {
    return mContent < var.mContent;
};

重载小于运算符,判断mContent是否小于var.mContent

参数:

  • var 输入变量

返回:true/false


operator<=

bool operator<=(const VARP& var) const {
    return mContent <= var.mContent;
};

重载小于等于运算符,判断mContent是否小于等于var.mContent

参数:

  • var 输入变量

返回:true/false


operator=

VARP& operator=(const VARP& var) {
    mContent = var.mContent;
    return *this;
};

拷贝var.mContent对象到mContent

参数:

  • var 输入变量

返回:当前对象的拷贝


operator=

VARP& operator=(Variable* var) {
    mContent.reset(var);
    return *this;
};

重置mContent对象中的Variable对象,并拷贝

参数:

  • var 输入变量

返回:当前对象的拷贝


operator->

Variable* operator->() const  {
    return mContent.get();
};

获取mContent对象的值

参数:无

返回:Variable对象


fix

bool fix(InputType type) const;

朝零方向取整

参数:

  • type 输入数据类型

返回:true/false


operator==

inline bool operator==(Variable* src, VARP dst) {
    return src == dst.get();
};

重载相等运算符,判断src和dst.get()是否相等

参数:

  • src Variable类型输入变量

  • dst VARP类型输入变量

返回:true/false


operator!=

inline bool operator!=(Variable* src, VARP dst) {
    return src != dst.get();
};

重载不相等运算符,判断src和dst.get()是否不相等

参数:

  • src Variable类型输入变量

  • dst VARP类型输入变量

返回:true/false

Variable

class Variable

成员函数


name

const std::string& name() const;

获取Variable对象的名称

参数:无

返回:名称


setName

void setName(const std::string& name);

设置名称

参数:

  • name 名称

返回:void


expr

std::pair<EXPRP, int> expr() const {
    return std::make_pair(mFrom, mFromIndex);
};

创建一个EXPRP对象,需要mFrom作为参数,位置为mFromIndex

参数:无

返回:EXPRP对象


getInfo

const Info* getInfo();

获取Variable对象的相关信息

参数:无

返回:如果计算信息错误,返回nullptr


resize

bool resize(INTS dims);

调整Variable对象的大小

参数:

  • dims 一个向量

返回:true/false


readMap

template <typename T>
const T* readMap() {
    return (const T*)readInternal();
};

读取内部信息

参数:无

返回:信息


writeMap

template <typename T>
T* writeMap() {
    return (T*)writeInternal();
};

写入内部信息

参数:无

返回:信息


input

bool input(VARP src);

输入信息

参数:

  • src 输入变量

返回:true/false


replace

static void replace(VARP dst, VARP src);

替换信息

参数:

  • dst 输入变量

  • src 输入变量

返回:void


create

static VARP create(EXPRP expr, int index = 0);

在index位置创建expr对象

参数:

  • expr 输入变量

  • index 位置下标,默认为0

返回:VARP对象


load

static std::vector<VARP> load(const char* fileName);

通过文件名加载对象

参数:

  • fileName 文件名

返回:VARP对象矩阵


loadMap

static std::map<std::string, VARP> loadMap(const char* fileName);

通过文件名读取模型对象

参数:

  • fileName 文件名

返回:模型对象


load

static std::vector<VARP> load(const uint8_t* buffer, size_t length);

加载存储的,长度为length的模型对象

参数:

  • buffer 存储数据

  • length 数据长度

返回:模型对象


loadMap

static std::map<std::string, VARP> loadMap(const uint8_t* buffer, size_t length);

读取存储的,长度为length的模型对象

参数:

  • buffer 存储数据

  • length 数据长度

返回:模型对象


getInputAndOutput

static std::pair<std::map<std::string, VARP>, std::map<std::string, VARP>> getInputAndOutput(const std::map<std::string, VARP>& allVariable);

获取模型输入输出节点

参数:

  • allVariable 输入变量

返回:模型输入输出节点


mapToSequence

static std::vector<VARP> mapToSequence(const std::map<std::string, VARP>& source);

模型的输出节点及其名称

参数:

  • source 输入变量

返回:输出节点及其名称


getExecuteOrder

static std::vector<EXPRP> getExecuteOrder(const std::vector<VARP>& output);

获取操作指令

参数:

  • output 输入变量

返回:指令集


save

static void save(const std::vector<VARP>& vars, const char* fileName);

保存vars到指定位置

参数:

  • vars 输入变量

  • fileName 文件名

返回:void


save

static std::vector<int8_t> save(const std::vector<VARP>& vars);

保存vars到默认位置

参数:

  • vars 输入变量

返回:void


save

static void save(const std::vector<VARP>& vars, NetT* dest);

保存vars到dest位置

参数:

  • vars 输入变量

  • dest 目标地址

返回:void


prepareCompute

static void prepareCompute(const std::vector<VARP>& vars, bool forceCPU = false);

将几个变量打包在一个管道中进行计算

参数:

  • vars 输入变量

  • forceCPU 是否强制使用CPU,默认为false

返回:void


compute

static void compute(const std::vector<VARP>& vars, bool forceCPU = false);

计算变量

参数:

  • vars 输入变量

  • forceCPU 是否强制使用CPU,默认为false

返回:void


linkNumber

size_t linkNumber() const;

获取输出信息的size

参数:无

返回:size


toExprs

const std::vector<WeakEXPRP>& toExprs() const;

返回模型对象信息

参数:无

返回:模型对象信息


setExpr

void setExpr(EXPRP expr, int index) {
    mFrom = expr;
    mFromIndex = index;
};

在index位置设置EXPRP对象

参数:无

返回:EXPRP对象

Expr

class Expr

枚举类


MemoryType

enum MemoryType {
    COPY,
    MOVE,
    REF
};
value name 说明
0 COPY 拷贝
1 MOVE 移动
2 REF 引用

成员函数


create

static EXPRP create(Tensor* tensor, bool own = false);

创建一个包含tensor的EXPRP变量

参数:

  • tensor 输入变量

  • own 默认为false

返回:EXPRP变量


create

static EXPRP create(Variable::Info&& info, const void* ptr, VARP::InputType type, MemoryType copy = COPY);

创建一个包含info对象的EXPRP变量

参数:

  • info Variable类型输入变量

  • ptr 目标对象地址

  • type 输入数据类型

  • copy 内存拷贝

返回:EXPRP变量


create

static EXPRP create(const OpT* op, std::vector<VARP> inputs, int outputSize = 1);

创建EXPRP变量

参数:

  • op 输入变量

  • inputs 输入变量

  • outputSize 输出信息大小,默认为1

返回:EXPRP变量


create

static EXPRP create(std::shared_ptr<BufferStorage> extra, std::vector<VARP>&& inputs, int outputSize = 1);

创建EXPRP变量

参数:

  • extra 输入变量

  • inputs 输入变量

  • outputSize 输出信息大小,默认为1

返回:EXPRP变量


create

static EXPRP create(std::unique_ptr<OpT>&& op, std::vector<VARP> inputs, int outputSize = 1) {
    return create(op.get(), inputs, outputSize);
};

创建EXPRP变量

参数:

  • op 输入变量

  • inputs 输入变量

  • outputSize 输出信息大小,默认为1

返回:EXPRP变量


setName

void setName(const std::string& name);

设置名称

参数:

  • name 名称

返回:void


get

const Op* get() const {
    return mOp;
};

获取对象信息

参数:无

返回:对象信息


inputs

const std::vector<VARP>& inputs() const {
    return mInputs;
};

获取输入节点信息

参数:无

返回:输入节点信息


outputSize

int outputSize() const {
    return (int)mOutputNames.size();
};

返回输出节点信息大小

参数:无

返回:size


replace

static void replace(EXPRP oldExpr, EXPRP newExpr);

用newExpr替换oldExpr

参数:

  • oldExpr 输入变量,源对象

  • newExpr 输入变量,目标对象

返回:void


requireInfo

bool requireInfo();

获取需要的信息

参数:无

返回:信息


visitOutputs

void visitOutputs(const std::function<bool(EXPRP, int)>& visit);

访问输出节点的信息

参数:

  • visit 访问方法

返回:void


visit

static void visit(EXPRP expr, const std::function<bool(EXPRP)>& before, const std::function<bool(EXPRP)>& after);

访问某一个范围的信息

参数:

  • expr 输入变量

  • before before指针

  • after after指针

返回:void


outputs

const std::vector<WeakEXPRP>& outputs() const {
    return mTo;
};

返回输出节点信息

参数:无

返回:信息


~Expr()

析构函数


visited

bool visited() const {
    return mVisited;
};

是否已经访问过

参数:无

返回:true/false


setVisited

void setVisited(bool visited) {
    mVisited = visited;
};

设置已经被访问

参数:

  • visited 是否访问

返回:void


name

const std::string& name() const {
    return mName;
};

获取名称

参数:无

返回:名称


outputName

const std::string& outputName(int index) {
    return mOutputNames[index];
};

输出指定index的名称

参数:

  • index 下标

返回:名称


inputType

VARP::InputType inputType() const {return mType;};

返回当前输入类型

参数:无

返回:输入类型


outputInfo

Variable::Info* outputInfo(int index) const;

返回指定下标的输出信息

参数:

  • index 下标值

返回:输出信息


extra

std::shared_ptr<BufferStorage> extra() const {
    return mStorage;
};

返回附加信息

参数:无

返回:附加信息


inside

std::shared_ptr<Inside> inside() const {
    return mInside;
};

返回内部信息

参数:无

返回:内部信息


valid

bool valid() const {
    return mValid;
};

是否有效

参数:无

返回:true/false

Module

class Module

成员函数


Tensor

构造函数

Module() == default;

创建一个空Module

参数:无

返回:Module对象


~Module

析构函数

virtual ~Module() == default;

onForward

virtual std::vector<Express::VARP> onForward(const std::vector<Express::VARP>& inputs) = 0;

模块前向传播,返回多个结果变量

参数:

  • inputs 前向传播输入变量

返回:前向传播输出变量


forward

Express::VARP forward(Express::VARP input);

模块前向传播,返回一个结果变量

参数:

  • input 前向传播输入变量

返回:前向传播输出变量


parameters

std::vector<Express::VARP> parameters() const;

获取Module的参数

参数:无

返回:Module的参数


loadParameters

bool loadParameters(const std::vector<Express::VARP>& parameters);

加载现有的参数

参数:

  • parameters 参数值

返回:是否成功加载参数


setIsTraining

void setIsTraining(const bool isTraining);

设置Module的训练状态

参数:

  • isTraining 是否为训练模式

返回:void


getIsTraining

bool getIsTraining();

获取_Module的是否为训练模式

参数:无

返回:Module是否为训练模式,是则返回true,不是返回false


clearCache

void clearCache();

清除Module的缓存,并递归清除子模块的缓存

参数:无

返回:void


name

const std::string& name() const {
    return mName;
};

获取Module的名称

参数:无

返回:Module的名称


setName

void setName(std::string name) {
    mName = std::move(name);
};

设置Module的名称

参数:

  • name 模块的名称

返回:void


type

const std::string type() const {
    return mType;
};

获取Module的类型

参数:无

返回:Module的类型


setType

void setType(std::string type) {
    mType = std::move(type);
};

设置Module的类型

参数:

  • type 模块的类型

返回:void


addParameter

int addParameter(Express::VARP parameter);

添加参数

参数:

  • parameter 参数值

返回:添加前的参数数量


setParameter

void setParameter(Express::VARP parameter, int index);

设置参数

参数:

  • type 参数值

  • index 参数的位置索引

返回:void


createEmpty

static Module* createEmpty(const std::vector<Express::VARP>& parameters);

根据参数创建一个空的Module对象

参数:

  • parameters 参数值

返回:创建的空的Module对象


load

static Module* load(const std::vector<std::string>& inputs, const std::vector<std::string>& outputs, const uint8_t* buffer, size_t length, const Config* config = nullptr);

加载module对象

参数:

  • inputs module输入信息

  • outputs module输出信息

  • buffer 缓冲信息

  • length 信息长度

  • config 其他配置项

返回:module对象


load

static Module* load(const std::vector<std::string>& inputs, const std::vector<std::string>& outputs, const char* fileName, const Config* config = nullptr);

加载module对象

参数:

  • inputs module输入信息

  • outputs module输出信息

  • fileName 文件名

  • config 其他配置项

返回:module对象


load

static Module* load(const std::vector<std::string>& inputs, const std::vector<std::string>& outputs, const char* fileName, const std::shared_ptr<MNN::Express::Executor::RuntimeManager> rtMgr, const Config* config = nullptr);

加载module对象

参数:

  • inputs module输入信息

  • outputs module输出信息

  • fileName 文件名

  • rtMgr 运行时资源

  • config 其他配置项

返回:module对象


load

static Module* load(const std::vector<std::string>& inputs, const std::vector<std::string>& outputs, const uint8_t* buffer, size_t length, const std::shared_ptr<MNN::Express::Executor::RuntimeManager> rtMgr, const Config* config = nullptr);

加载module对象

参数:

  • inputs module输入信息

  • outputs module输出信息

  • buffer 缓冲信息

  • length 信息长度

  • rtMgr 运行时资源

  • config 其他配置项

返回:module对象


load

static Module* extract(std::vector<Express::VARP> inputs, std::vector<Express::VARP> outputs, bool fortrain, const std::map<std::string, SubGraph>& subGraph = {});

加载module对象

参数:

  • inputs module输入信息

  • outputs module输出信息

  • fortrain

  • subGraph 子图

返回:module对象


clone

static Module* clone(const Module* module, const bool shareParams = false);

克隆Module对象

参数:

  • module module对象实例

  • shareParams 是否共享参数,默认为false

返回:Module对象实例


getInfo

const Info* getInfo() const;

获取Module的信息

参数:无

返回:Module的信息


CloneContext

CloneContext() = default;

克隆Module的内容

参数:无

返回:Module的内容


CloneContext

explicit CloneContext(const bool shareParams)
        : mShareParams(shareParams) {};

克隆Module的内容

参数:

  • shareParams 是否共享参数

返回:Module的内容


~CloneContext

析构函数

virtual ~CloneContext() = default;

shareParams

const bool shareParams() const { return mShareParams; };

是否共享参数

参数:无

返回:共享返回true,反之则为false


getOrClone

EXPRP getOrClone(const EXPRP expr);

获取克隆的EXPRP对象

参数:

  • expr EXPRP对象值

返回:EXPRP对象


getOrClone

VARP getOrClone(const VARP var);

获取克隆的VARP对象

参数:

  • expr VARP对象值

返回:VARP对象


clone

virtual Module* clone(CloneContext* ctx) const {
    return nullptr;
};

克隆Module对象

参数:

  • ctx 克隆的上下文

返回:Module对象


registerModel

void registerModel(const std::vector<std::shared_ptr<Module>>& children);

注册子模块

参数:

  • children 子模块列表

返回:void


destroy

static void destroy(Module* m);

销毁Module对象

参数:

  • m Module对象

返回:void

Optimizer

class Optimizer

枚举类

Device

enum Device {
    CPU = 0,
    GPU = 1,
    OTHER = 2,
    AUTO = 3
};
value name 说明
0 CPU 中央处理器
1 GPU 图像处理器
2 OTHER 其他
3 AUTO 自定义

成员函数


Optimizer

构造函数

Optimizer() = default;

创建一个空Optimizer

参数:无

返回:Optimizer对象


~Optimizer

析构函数

virtual ~Optimizer() = default;

创建一个空Optimizer

参数:无

返回:Optimizer对象


create

static std::shared_ptr<Optimizer> create(Config config);

创建一个Optimizer对象

参数:

  • config 配置信息,包括线程、Device和MNNForwardType等信息

返回:Optimizer对象


onGetParameters

virtual std::shared_ptr<Parameters> onGetParameters(const std::vector<VARP>& outputs) {
    return nullptr;
};

获取Optimizer对象的参数

参数:

  • outputs Optimizer输出信息

返回:Optimizer对象的参数


onMeasure

virtual Cost onMeasure(const std::vector<VARP>& outputs, std::shared_ptr<Parameters> parameters = nullptr) = 0;

返回Cost对象信息,包括compute(计算)和memory(内存)信息,parameters必须与onGetParameters相同

参数:

  • outputs Optimizer输出信息

  • parameters 与onGetParameters相同

返回:Cost对象信息


onExecute

virtual bool onExecute(const std::vector<VARP>& outputs, std::shared_ptr<Parameters> parameters = nullptr) = 0;

修改输出信息,parameters必须与onGetParameters相同

参数:

  • outputs Optimizer输出信息

  • parameters 与onGetParameters相同

返回:是否修改输出成功

Parameters

class Parameters

成员函数


Parameters

Parameters(int n);

创建一个Parameters对象

参数:

  • n 成员个数

返回:Parameters对象


~Parameters

析构函数


get

float* get() const {
    return mValue;
};

获取Parameters对象成员数量

参数:无

返回:Parameters对象成员数量


size

int size() const {
    return mSize;
};

获取Parameters对象大小

参数:无

返回:Parameters对象大小

MathOp

class MathOp

成员函数


_Add

MNN_PUBLIC VARP _Add(VARP x, VARP y);

返回x + y的值

参数:

  • x 一个变量,Halide_Type_Int or Halide_Type_Float, Halide_Type_Int64, Halide_Type_Uint8类型之一

  • y 一个变量,Halide_Type_Int or Halide_Type_Float, Halide_Type_Int64, Halide_Type_Uint8类型之一

返回:x相同的类型变量


_Subtract

MNN_PUBLIC VARP _Subtract(VARP x, VARP y);

计算x-y的值

参数:

  • x 一个变量,Halide_Type_Int or Halide_Type_Float, Halide_Type_Int64, Halide_Type_Uint8类型之一

  • y 一个变量,Halide_Type_Int or Halide_Type_Float, Halide_Type_Int64, Halide_Type_Uint8类型之一

返回:x相同的类型变量


_Multiply

MNN_PUBLIC VARP _Multiply(VARP x, VARP y);

计算x*y的值

参数:

  • x 一个变量,Halide_Type_Int or Halide_Type_Float, Halide_Type_Int64, Halide_Type_Uint8类型之一

  • y 一个变量,Halide_Type_Int or Halide_Type_Float, Halide_Type_Int64, Halide_Type_Uint8类型之一

返回:x相同的类型变量


_Divide

MNN_PUBLIC VARP _Divide(VARP x, VARP y);

计算x/y的值

参数:

  • x 一个变量,Halide_Type_Int or Halide_Type_Float, Halide_Type_Int64, Halide_Type_Uint8类型之一

  • y 一个变量,Halide_Type_Int or Halide_Type_Float, Halide_Type_Int64, Halide_Type_Uint8类型之一

返回:x相同的类型变量


_Pow

MNN_PUBLIC VARP _Pow(VARP x, VARP y);

计算x的y的幂次方的值

参数:

  • x 一个变量,Halide_Type_Int or Halide_Type_Float, Halide_Type_Int64类型之一

  • y 一个变量,Halide_Type_Int or Halide_Type_Float, Halide_Type_Int64类型之一

返回:x相同的类型变量


_Minimum

MNN_PUBLIC VARP _Minimum(VARP x, VARP y);

返回x和y的最小值

参数:

  • x 一个变量,Halide_Type_Int or Halide_Type_Float, Halide_Type_Int64类型之一

  • y 一个变量,Halide_Type_Int or Halide_Type_Float, Halide_Type_Int64类型之一

返回:x相同的类型变量


_Maximum

MNN_PUBLIC VARP _Maximum(VARP x, VARP y);

返回x和y的最大值

参数:

  • x 一个变量,Halide_Type_Int or Halide_Type_Float, Halide_Type_Int64类型之一

  • y 一个变量,Halide_Type_Int or Halide_Type_Float, Halide_Type_Int64类型之一

返回:x相同的类型变量


_BiasAdd

MNN_PUBLIC VARP _BiasAdd(VARP value, VARP bias);

增加value的偏差。这(主要)是加法的一个特殊情况,其中偏差限制在1-D。支持广播,因此value可以有任意数量的维度。与加法不同,在量子化的情况下,偏差的类型允许与值不同

参数:

  • value 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • bias 一个一维变量,其大小与值的通道维度相匹配,必须与值的类型相同,除非值是量化类型,在这种情况下可以使用不同的量化类型

返回:value相同的类型变量


_Greater

MNN_PUBLIC VARP _Greater(VARP x, VARP y);

比较x和y的大小,如果x > y为true,否者为false

参数:

  • x 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • y 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

返回:true或者false


_GreaterEqual

MNN_PUBLIC VARP _GreaterEqual(VARP x, VARP y);

比较x和y的大小,如果x >= y为true,否者为false

参数:

  • x 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • y 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

返回:true或者false


_Less

MNN_PUBLIC VARP _Less(VARP x, VARP y);

比较x和y的大小,如果x < y为true,否者为false

参数:

  • x 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • y 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

返回:true或者false


_FloorDiv

MNN_PUBLIC VARP _FloorDiv(VARP x, VARP y);

返回x // y的值

参数:

  • x 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • y 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

返回:x相同的类型变量


_SquaredDifference

MNN_PUBLIC VARP _SquaredDifference(VARP x, VARP y);

返回(x - y)(x - y)的值

参数:

  • x 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • y 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

返回:x相同的类型变量


_Equal

MNN_PUBLIC VARP _Equal(VARP x, VARP y);

判断x和y是否相等,如果x = y为true,否者为false

参数:

  • x 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • y 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

返回:true或者false


_LessEqual

MNN_PUBLIC VARP _LessEqual(VARP x, VARP y);

判断x和y的大小,如果x <= y则返回true,否则返回false

参数:

  • x 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • y 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

返回:true或者false


_FloorMod

MNN_PUBLIC VARP _FloorMod(VARP x, VARP y);

返回x % y的值

参数:

  • x 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • y 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

返回:x相同的类型变量


_Atan2

MNN_PUBLIC VARP _Atan2(VARP x, VARP y);

计算y / x的元素反正切的值

参数:

  • x 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • y 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

返回:x相同的类型变量


_LogicalOr

MNN_PUBLIC VARP _LogicalOr(VARP x, VARP y);

返回x和y的逻辑或的值

参数:

  • x Halide_Type_Int类型的变量

  • y Halide_Type_Int类型的变量

返回:true/false


_NotEqual

MNN_PUBLIC VARP _NotEqual(VARP x, VARP y);

判断x和y是否相等,如果x != y则返回true,否者返回false

参数:

  • x Halide_Type_Int类型的变量

  • y Halide_Type_Int类型的变量

返回:true/false


_BitwiseAnd

MNN_PUBLIC VARP _BitwiseAnd(VARP x, VARP y);

返回x和y的按位逻辑与的值(x & y)

参数:

  • x Halide_Type_Int类型的变量

  • y Halide_Type_Int类型的变量

返回:x相同的类型变量


_BitwiseOr

MNN_PUBLIC VARP _BitwiseOr(VARP x, VARP y);

返回x和y的按位逻辑或的值(x | y)

参数:

  • x Halide_Type_Int类型的变量

  • y Halide_Type_Int类型的变量

返回:x相同的类型变量


_BitwiseXor

MNN_PUBLIC VARP _BitwiseXor(VARP x, VARP y);

返回x和y的按位异或的值(x ^ y)

参数:

  • x Halide_Type_Int类型的变量

  • y Halide_Type_Int类型的变量

返回:x相同的类型变量


_Sign

MNN_PUBLIC VARP _Sign(VARP a);

去掉x元素的符号 sign(x) = 0 if x=0 sign(x) =-1 if x<0 sign(x) = 1 if x>0

参数:

  • a 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:-1、0或者1


_Abs

MNN_PUBLIC VARP _Abs(VARP x);

计算变量的绝对值,给定一个整型或浮点型变量,该操作将返回一个相同类型的变量,其中每个元素都包含输入中对应元素的绝对值 x = MNN.const((-1.0, -2.0, 3.0), (3,)) x = MNN.abs(x) # (1.0, 2.0, 3.0)

参数:

  • x Halide_Type_Int或Halide_Type_Float类型的变量

返回:一个大小相同的变量,类型与x的绝对值相同


_Negative

MNN_PUBLIC VARP _Negative(VARP x);

计算元素数值负值 x = MNN.const((-1.0, -2.0, 3.0), (3,)) x = MNN.negative(x) #(1.0, 2.0, -3.0)

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:和x相同类型的变量


_Floor

MNN_PUBLIC VARP _Floor(VARP x);

返回不大于x的最大整数

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:和x相同类型的变量


_Round

MNN_PUBLIC VARP _Round(VARP x);

返回元素四舍五入的整数

参数:

  • x Halide_Type_Float类型的变量

返回:Halide_Type_Float类型的变量


_Ceil

MNN_PUBLIC VARP _Ceil(VARP x);

返回不小于x的最小整数

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:和x相同类型的变量


_Square

MNN_PUBLIC VARP _Square(VARP x);

计算x元素的平方值

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:x相同类型的变量


_Sqrt

MNN_PUBLIC VARP _Sqrt(VARP x);

计算x的平方根

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:x相同类型的变量


_Rsqrt

MNN_PUBLIC VARP _Rsqrt(VARP x);

计算x根号的倒数

参数:一个变量,Halide_Type_Int或Halide_Type_Float类型之一

  • x

返回:x相同类型的变量


_Exp

MNN_PUBLIC VARP _Exp(VARP x);

计算x的指数

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:x相同类型的变量


_Log

MNN_PUBLIC VARP _Log(VARP x);

计算x的对数

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:x相同类型的变量


_Sin

MNN_PUBLIC VARP _Sin(VARP x);

计算x的正弦值

参数:

  • x Halide_Type_Float类型的变量

返回:x相同类型的变量


_Sinh

MNN_PUBLIC VARP _Sinh(VARP x);

计算x的双曲正弦值

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:x相同类型的变量


_Cos

MNN_PUBLIC VARP _Cos(VARP x);

计算x的余弦值

参数:

  • x Halide_Type_Float类型的变量

返回:x相同类型的变量


_Cosh

MNN_PUBLIC VARP _Cosh(VARP x);

计算x的双曲余弦的值

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:x相同类型的变量


_Tan

MNN_PUBLIC VARP _Tan(VARP x);

计算x的正切值

参数:

  • x Halide_Type_Float类型的变量

返回:x相同类型的变量


_Asin

MNN_PUBLIC VARP _Asin(VARP x);

计算x的反正弦值

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:x相同类型的变量


_Asinh

MNN_PUBLIC VARP _Asinh(VARP x);

计算x的反双曲正弦的值

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:x相同类型的变量


_Acos

MNN_PUBLIC VARP _Acos(VARP x);

计算x的反余弦值

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:x相同类型的变量


_Acosh

MNN_PUBLIC VARP _Acosh(VARP x);

计算x的反双曲余弦的值

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:x相同类型的变量


_Atan

MNN_PUBLIC VARP _Atan(VARP x);

计算x的反正切函数

参数:

  • x Halide_Type_Float类型的变量

返回:x相同类型的变量


_Atanh

MNN_PUBLIC VARP _Atanh(VARP x);

计算x的双曲反正切的值

参数:

  • x Halide_Type_Float类型的变量

返回:x相同类型的变量


_Reciprocal

MNN_PUBLIC VARP _Reciprocal(VARP x);

计算x的倒数

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:x相同类型的变量


_Log1p

MNN_PUBLIC VARP _Log1p(VARP x);

计算(1 + x)的自然对数

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:x相同类型的变量


_Gelu

MNN_PUBLIC VARP _Gelu(VARP x);

计算x的高斯误差线性单元激活函数

参数:

  • x Halide_Type_Float类型的变量

返回:x相同类型的变量


_Tanh

MNN_PUBLIC VARP _Tanh(VARP x);

计算x的双曲正切函数

参数:

  • x Halide_Type_Float类型的变量

返回:x相同类型的变量


_Sigmoid

MNN_PUBLIC VARP _Sigmoid(VARP x);

计算x的神经元的非线性作用函数

参数:

  • x Halide_Type_Float类型的变量

返回:x相同类型的变量


_Erf

MNN_PUBLIC VARP _Erf(VARP x);

计算x的高斯误差值

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:x相同类型的变量


_Erfc

MNN_PUBLIC VARP _Erfc(VARP x);

计算x的互补误差函数的值

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:x相同类型的变量


_Erfinv

MNN_PUBLIC VARP _Erfinv(VARP x);

x的逆函数的值

参数:

  • x 一个变量,Halide_Type_Int或Halide_Type_Float类型之一

返回:x相同类型的变量


_Expm1

MNN_PUBLIC VARP _Expm1(VARP x);

计算((x指数)- 1)的值

参数:

  • x Halide_Type_Float类型的变量

返回:x相同类型的变量


_Hardswish

MNN_PUBLIC VARP _Hardswish(VARP x);

元素x的Hardswish神经网络激活函数

参数:

  • x Halide_Type_Float类型的变量

返回:x相同类型的变量


_ReduceSum

MNN_PUBLIC VARP _ReduceSum(VARP input_variable, INTS axis = {}, bool keepDims = false);

计算变量各维度上元素的和,沿轴中给定的维度减少input_variable。除非keepdim为真,否则变量在轴上的每一项的排名都会减少1。如果keepdim为true,则缩减后的维度保留长度为1;如果axis为空,则减少所有维度,并返回具有单个元素的变量

参数:

  • input_variable 要减少的变量,应该是数值类型

  • axis 要减少的尺寸。如果为空(默认值),则减少所有维度。必须在范围内[-rank(input_variable), rank(input_variable))

  • keepDims 如果为true,则保留长度为1的缩减维度

返回:简化后的变量,与input_variable具有相同的类型


_ReduceMean

MNN_PUBLIC VARP _ReduceMean(VARP input_variable, INTS axis = {}, bool keepDims = false);

计算变量各维度元素的平均值,沿轴中给定的维度减少input_variable,除非keepdim为真,否则变量在轴上的每一项的排名都会减少1。如果keepdim为true,则缩减后的维度保留长度为1;如果axis为空,则减少所有维度,并返回具有单个元素的变量

参数:

  • input_variable 要减少的变量,应该是数值类型

  • axis 要减少的尺寸。如果为空(默认值),则减少所有维度。必须在范围内[-rank(input_variable), rank(input_variable))

  • keepDims 如果为true,则保留长度为1的缩减维度

返回:简化后的变量,与input_variable具有相同的类型


_ReduceMax

MNN_PUBLIC VARP _ReduceMax(VARP input_variable, INTS axis = {}, bool keepDims = false);

计算变量跨维元素的最大值,沿轴中给定的维度减少input_variable,除非keepdim为真,否则变量在轴上的每一项的排名都会减少1。如果keepdim为true,则缩减后的维度保留长度为1;如果axis为空,则减少所有维度,并返回具有单个元素的变量

参数:

  • input_variable 要减少的变量,应该是数值类型

  • axis 要减少的尺寸。如果为空(默认值),则减少所有维度。必须在范围内[-rank(input_variable), rank(input_variable))

  • keepDims 如果为true,则保留长度为1的缩减维度

返回:简化后的变量,与input_variable具有相同的类型


_ReduceMin

MNN_PUBLIC VARP _ReduceMin(VARP input_variable, INTS axis = {}, bool keepDims = false);

计算变量跨维元素的最小值,沿轴中给定的维度减少input_variable,除非keepdim为真,否则变量在轴上的每一项的排名都会减少1。如果keepdim为true,则缩减后的维度保留长度为1;如果axis为空,则减少所有维度,并返回具有单个元素的变量

参数:

  • input_variable 要减少的变量,应该是数值类型

  • axis 要减少的尺寸。如果为空(默认值),则减少所有维度。必须在范围内[-rank(input_variable), rank(input_variable))

  • keepDims 如果为true,则保留长度为1的缩减维度

返回:简化后的变量,与input_variable具有相同的类型


_ReduceProd

MNN_PUBLIC VARP _ReduceProd(VARP input_variable, INTS axis = {}, bool keepDims = false);

计算变量各维度上元素的乘积,沿轴中给定的维度减少input_variable,除非keepdim为真,否则变量在轴上的每一项的排名都会减少1。如果keepdim为true,则缩减后的维度保留长度为1;如果axis为空,则减少所有维度,并返回具有单个元素的变量

参数:

  • input_variable 要减少的变量,应该是数值类型

  • axis 要减少的尺寸。如果为空(默认值),则减少所有维度。必须在范围内[-rank(input_variable), rank(input_variable))

  • keepDims 如果为true,则保留长度为1的缩减维度

返回:简化后的变量,与input_variable具有相同的类型


_ReduceAny

MNN_PUBLIC VARP _ReduceAny(VARP input_variable, INTS axis = {}, bool keepDims = false);

跨变量的维度计算元素的“逻辑或”的值,沿轴中给定的维度减少input_variable,除非keepdim为真,否则变量在轴上的每一项的排名都会减少1。如果keepdim为true,则缩减后的维度保留长度为1;如果axis为空,则减少所有维度,并返回具有单个元素的变量

参数:

  • input_variable 要减少的变量,应该是数值类型

  • axis 要减少的尺寸。如果为空(默认值),则减少所有维度。必须在范围内[-rank(input_variable), rank(input_variable))

  • keepDims 如果为true,则保留长度为1的缩减维度

返回:简化后的变量,与input_variable具有相同的类型


_ReduceAll

MNN_PUBLIC VARP _ReduceAll(VARP input_variable, INTS axis = {}, bool keepDims = false);

跨变量的维度计算元素的“逻辑和”的值,沿轴中给定的维度减少input_variable,除非keepdim为真,否则变量在轴上的每一项的排名都会减少1。如果keepdim为true,则缩减后的维度保留长度为1;如果axis为空,则减少所有维度,并返回具有单个元素的变量

参数:

  • input_variable 要减少的变量,应该是数值类型

  • axis 要减少的尺寸。如果为空(默认值),则减少所有维度。必须在范围内[-rank(input_variable), rank(input_variable))

  • keepDims 如果为true,则保留长度为1的缩减维度

返回:简化后的变量,与input_variable具有相同的类型


_ReduceSumMutable

MNN_PUBLIC VARP _ReduceSumMutable(VARP input_variable, INTS axis = {}, bool keepDims = false);

计算变量各维度上元素的和,沿轴中给定的维度减少input_variable。除非keepdim为真,否则变量在轴上的每一项的排名都会减少1。如果keepdim为true,则缩减后的维度保留长度为1;如果axis为空,则减少所有维度,并返回具有单个元素的变量,是可变的

参数:

  • input_variable 要减少的变量,应该是数值类型

  • axis 要减少的尺寸。如果为空(默认值),则减少所有维度。必须在范围内[-rank(input_variable), rank(input_variable))

  • keepDims 如果为true,则保留长度为1的缩减维度

返回:简化后的变量,与input_variable具有相同的类型


_ReduceMeanMutable

MNN_PUBLIC VARP _ReduceMeanMutable(VARP input_variable, INTS axis = {}, bool keepDims = false);

计算变量各维度元素的平均值,沿轴中给定的维度减少input_variable,除非keepdim为真,否则变量在轴上的每一项的排名都会减少1。如果keepdim为true,则缩减后的维度保留长度为1;如果axis为空,则减少所有维度,并返回具有单个元素的变量

参数:

  • input_variable 要减少的变量,应该是数值类型

  • axis 要减少的尺寸。如果为空(默认值),则减少所有维度。必须在范围内[-rank(input_variable), rank(input_variable))

  • keepDims 如果为true,则保留长度为1的缩减维度

返回:简化后的变量,与input_variable具有相同的类型


_ReduceMaxMutable

MNN_PUBLIC VARP _ReduceMaxMutable(VARP input_variable, INTS axis = {}, bool keepDims = false);

计算变量跨维元素的最大值,沿轴中给定的维度减少input_variable,除非keepdim为真,否则变量在轴上的每一项的排名都会减少1。如果keepdim为true,则缩减后的维度保留长度为1;如果axis为空,则减少所有维度,并返回具有单个元素的变量

参数:

  • input_variable 要减少的变量,应该是数值类型

  • axis 要减少的尺寸。如果为空(默认值),则减少所有维度。必须在范围内[-rank(input_variable), rank(input_variable))

  • keepDims 如果为true,则保留长度为1的缩减维度

返回:简化后的变量,与input_variable具有相同的类型


_ReduceMinMutable

MNN_PUBLIC VARP _ReduceMinMutable(VARP input_variable, INTS axis = {}, bool keepDims = false);

计算变量跨维元素的最大值,沿轴中给定的维度减少input_variable,除非keepdim为真,否则变量在轴上的每一项的排名都会减少1。如果keepdim为true,则缩减后的维度保留长度为1;如果axis为空,则减少所有维度,并返回具有单个元素的变量

参数:

  • input_variable 要减少的变量,应该是数值类型

  • axis 要减少的尺寸。如果为空(默认值),则减少所有维度。必须在范围内[-rank(input_variable), rank(input_variable))

  • keepDims 如果为true,则保留长度为1的缩减维度

返回:简化后的变量,与input_variable具有相同的类型


_ReduceProdMutable

MNN_PUBLIC VARP _ReduceProdMutable(VARP input_variable, INTS axis = {}, bool keepDims = false);

计算变量各维度上元素的乘积,沿轴中给定的维度减少input_variable,除非keepdim为真,否则变量在轴上的每一项的排名都会减少1。如果keepdim为true,则缩减后的维度保留长度为1;如果axis为空,则减少所有维度,并返回具有单个元素的变量

参数:

  • input_variable 要减少的变量,应该是数值类型

  • axis 要减少的尺寸。如果为空(默认值),则减少所有维度。必须在范围内[-rank(input_variable), rank(input_variable))

  • keepDims 如果为true,则保留长度为1的缩减维度

返回:简化后的变量,与input_variable具有相同的类型


_ReduceAnyMutable

MNN_PUBLIC VARP _ReduceAnyMutable(VARP input_variable, INTS axis = {}, bool keepDims = false);

跨变量的维度计算元素的“逻辑或”的值,沿轴中给定的维度减少input_variable,除非keepdim为真,否则变量在轴上的每一项的排名都会减少1。如果keepdim为true,则缩减后的维度保留长度为1;如果axis为空,则减少所有维度,并返回具有单个元素的变量

参数:

  • input_variable 要减少的变量,应该是数值类型

  • axis 要减少的尺寸。如果为空(默认值),则减少所有维度。必须在范围内[-rank(input_variable), rank(input_variable))

  • keepDims 如果为true,则保留长度为1的缩减维度

返回:简化后的变量,与input_variable具有相同的类型


_ReduceAllMutable

MNN_PUBLIC VARP _ReduceAllMutable(VARP input_variable, INTS axis = {}, bool keepDims = false);

跨变量的维度计算元素的“逻辑和”的值,沿轴中给定的维度减少input_variable,除非keepdim为真,否则变量在轴上的每一项的排名都会减少1。如果keepdim为true,则缩减后的维度保留长度为1;如果axis为空,则减少所有维度,并返回具有单个元素的变量

参数:

  • input_variable 要减少的变量,应该是数值类型

  • axis 要减少的尺寸。如果为空(默认值),则减少所有维度。必须在范围内[-rank(input_variable), rank(input_variable))

  • keepDims 如果为true,则保留长度为1的缩减维度

返回:简化后的变量,与input_variable具有相同的类型


_Prod

MNN_PUBLIC VARP _Prod(VARP a, VARP b, std::vector<float> coeff);

计算元素积

参数:

  • a Halide_Type_Float类型的变量

  • b Halide_Type_Float类型的变量

  • coeff blob-wise系数

返回:元素积变量


_Sum

MNN_PUBLIC VARP _Sum(VARP a, VARP b, std::vector<float> coeff);

计算元素和

参数:

  • a Halide_Type_Float类型的变量

  • b Halide_Type_Float类型的变量

  • coeff blob-wise系数

返回:元素和


_Max

MNN_PUBLIC VARP _Max(VARP a, VARP b, std::vector<float> coeff);

计算元素最大值

参数:

  • a Halide_Type_Float类型的变量

  • b Halide_Type_Float类型的变量

  • coeff blob-wise系数

返回:最大值


_Sub

MNN_PUBLIC VARP _Sub(VARP a, VARP b, std::vector<float> coeff);

计算元素下标

参数:

  • a Halide_Type_Float类型的变量

  • b Halide_Type_Float类型的变量

  • coeff blob-wise系数

返回:下标元素


_EltwiseProdInt8

MNN_PUBLIC VARP _EltwiseProdInt8(VARP x, VARP y, 
                    std::vector<int8_t> x_weight, std::vector<int32_t> x_bias, std::vector<float> x_scale, std::vector<float> x_tensorScale,
                    std::vector<int8_t> y_weight, std::vector<int32_t> y_bias, std::vector<float> y_scale, std::vector<float> y_tensorScale,
                    std::vector<int8_t> output_weight, std::vector<int32_t> output_bias, std::vector<float> output_scale, std::vector<float> output_tensorScale);

在Eltwise层对x和y进行累计乘积

参数:

  • x 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • y 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • x_weight 变量x的权值

  • x_bias 变量x的偏差

  • x_scale 变量x的比例因子

  • x_tensorScale 变量x的张量比例因子

  • y_weight 变量y的权值

  • y_bias 变量y的偏差

  • y_scale 变量y的比例因子

  • y_tensorScale 变量y的张量比例因子

  • output_weight 输出数据的权值

  • output_bias 输出数据的偏差

  • output_scale 输出数据的比例因子

  • output_tensorScale 输出数据的张量比例因子

返回:VARP类型变量


_EltwiseSumInt8

MNN_PUBLIC VARP _EltwiseSumInt8(VARP x, VARP y, 
                    std::vector<int8_t> x_weight, std::vector<int32_t> x_bias, std::vector<float> x_scale, std::vector<float> x_tensorScale,
                    std::vector<int8_t> y_weight, std::vector<int32_t> y_bias, std::vector<float> y_scale, std::vector<float> y_tensorScale,
                    std::vector<int8_t> output_weight, std::vector<int32_t> output_bias, std::vector<float> output_scale, std::vector<float> output_tensorScale);

在Eltwise层对x和y进行累计求和

参数:

  • x 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • y 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • x_weight 变量x的权值

  • x_bias 变量x的偏差

  • x_scale 变量x的比例因子

  • x_tensorScale 变量x的张量比例因子

  • y_weight 变量y的权值

  • y_bias 变量y的偏差

  • y_scale 变量y的比例因子

  • y_tensorScale 变量y的张量比例因子

  • output_weight 输出数据的权值

  • output_bias 输出数据的偏差

  • output_scale 输出数据的比例因子

  • output_tensorScale 输出数据的张量比例因子

返回:VARP类型变量


_EltwiseSubInt8

MNN_PUBLIC VARP _EltwiseSubInt8(VARP x, VARP y, 
                    std::vector<int8_t> x_weight, std::vector<int32_t> x_bias, std::vector<float> x_scale, std::vector<float> x_tensorScale,
                    std::vector<int8_t> y_weight, std::vector<int32_t> y_bias, std::vector<float> y_scale, std::vector<float> y_tensorScale,
                    std::vector<int8_t> output_weight, std::vector<int32_t> output_bias, std::vector<float> output_scale, std::vector<float> output_tensorScale);

在Eltwise层对x和y进行累计求差值

参数:

  • x 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • y 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • x_weight 变量x的权值

  • x_bias 变量x的偏差

  • x_scale 变量x的比例因子

  • x_tensorScale 变量x的张量比例因子

  • y_weight 变量y的权值

  • y_bias 变量y的偏差

  • y_scale 变量y的比例因子

  • y_tensorScale 变量y的张量比例因子

  • output_weight 输出数据的权值

  • output_bias 输出数据的偏差

  • output_scale 输出数据的比例因子

  • output_tensorScale 输出数据的张量比例因子

返回:VARP类型变量


_EltwiseMaxInt8

MNN_PUBLIC VARP _EltwiseMaxInt8(VARP x, VARP y, 
                    std::vector<int8_t> x_weight, std::vector<int32_t> x_bias, std::vector<float> x_scale, std::vector<float> x_tensorScale,
                    std::vector<int8_t> y_weight, std::vector<int32_t> y_bias, std::vector<float> y_scale, std::vector<float> y_tensorScale,
                    std::vector<int8_t> output_weight, std::vector<int32_t> output_bias, std::vector<float> output_scale, std::vector<float> output_tensorScale);

在Eltwise层对x和y进行累计求最大值

参数:

  • x 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • y 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • x_weight 变量x的权值

  • x_bias 变量x的偏差

  • x_scale 变量x的比例因子

  • x_tensorScale 变量x的张量比例因子

  • y_weight 变量y的权值

  • y_bias 变量y的偏差

  • y_scale 变量y的比例因子

  • y_tensorScale 变量y的张量比例因子

  • output_weight 输出数据的权值

  • output_bias 输出数据的偏差

  • output_scale 输出数据的比例因子

  • output_tensorScale 输出数据的张量比例因子

返回:VARP类型变量


_Mod

MNN_PUBLIC VARP _Mod(VARP x, VARP y);

求余函数,即x和y作除法运算后的余数

参数:

  • x 一个变量,Halide_Type_Int, Halide_Type_Float类型之一

  • y 一个变量,Halide_Type_Int, Halide_Type_Float类型之一

返回:和x类型相同的变量


_Cast

VARP _Cast(VARP x) {
    return _Cast(x, halide_type_of<T>());
};

将变量强制转换为新类型

参数:

  • x 一个变量,Halide_Type_Int, Halide_Type_Float, Halide_Type_Int64, Halide_Type_Uint8类型之一

返回:与x形状相同,与dtype类型相同的变量


_Cast

MNN_PUBLIC VARP _Cast(VARP x, halide_type_t dtype);

将变量强制转换为新类型

参数:

  • x 一个变量,Halide_Type_Int, Halide_Type_Float, Halide_Type_Int64, Halide_Type_Uint8类型之一

  • dtype 目标类型,支持的dtypes列表与x相同

返回:与x形状相同,与dtype类型相同的变量


_MatMul

MNN_PUBLIC VARP _MatMul(VARP a, VARP b, bool tranposeA = false, bool tranposeB = false);

矩阵a * 矩阵b,输入必须是二维矩阵和“a”的内部维数(如果转置se_a为真,则转置后),必须匹配”b”的外部尺寸(如果transposed_b为true则被转置)

参数:

  • a 一个表示矩阵A的变量

  • b 一个表示矩阵B的变量

  • tranposeA 如果为true,则a在乘法之前被转置,默认为false

  • tranposeB 如果为true,则b在乘法之前被转置,默认为false

返回:矩阵a * 矩阵b


_Normalize

MNN_PUBLIC VARP _Normalize(VARP x, int32_t acrossSpatial, int32_t channelShared, float eps, std::vector<float> scale);

返回x数据转换成指定的标准化的格式

参数:

  • x 输入变量

  • acrossSpatial 输入变量

  • channelShared 输入变量

  • eps 输入变量,data_format

  • scale 缩放因子

返回:x数据转换成指定的标准化的格式


_ArgMax

MNN_PUBLIC VARP _ArgMax(VARP input, int axis = 0);

返回张量坐标轴上最大值的索引

参数:

  • input 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • axis 坐标轴

返回:索引值


_ArgMin

MNN_PUBLIC VARP _ArgMin(VARP input, int axis = 0);

返回张量坐标轴上最小值的索引

参数:

  • input 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • axis 坐标轴

返回:索引值


_BatchMatMul

MNN_PUBLIC VARP _BatchMatMul(VARP x, VARP y, bool adj_x = false, bool adj_y = false);

批量相乘两个变量的切片,乘以变量x和y的所有切片(每个切片可以看作是一个批处理的一个元素),并将单个结果安排在同一个批处理大小的单个输出变量中。每一个单独的切片都可以有选择地被伴随(伴随一个矩阵意味着转置和共轭它)将adj_x或adj_y标志设置为True,默认为False。输入变量x和y是二维或更高的形状[…], r_x, c_x]和[…]、r_y提出)。输出变量为二维或更高的形状[…], r_o, c_o],其中: R_o = c_x if adj_x else r_x C_o = r_y if adj_y else c_y 计算公式为: 输出[…,:,:] =矩阵(x[…,:,:]) *矩阵(y[…]、::])

参数:

  • x 二维或更高形状[…, r_x, c_x]

  • y 二维或更高形状[…, r_x, c_x]

  • adj_x 如果为True,则连接x的切片,默认为False

  • adj_y 如果为True,则连接y的切片,默认为False

返回:3-D或更高形状[…], r_o, c_o]


_UnravelIndex

MNN_PUBLIC VARP _UnravelIndex(VARP indices, VARP dims);

返回indices中的元素在维度为dims的数组中的索引值,默认按元组的形式返回

参数:

  • indices 指定的张量保存指向输出张量的索引

  • dims 操作的维度

返回:索引


_ScatterNd

MNN_PUBLIC VARP _ScatterNd(VARP indices, VARP updates, VARP shape);

根据声明的索引,通过对声明的形状张量的零张量内的单个切片或值进行分散更新,来形成不同的张量

参数:

  • indices 指定的张量保存指向输出张量的索引

  • updates 它是声明的张量,用于保存索引的值

  • shape 它是输出张量的规定形状

返回:张量


_ScatterNd

MNN_PUBLIC VARP _ScatterNd(VARP indices, VARP updates, VARP shape, VARP input);

根据声明的索引,通过对声明的形状张量的零张量内的单个切片或值进行分散更新,来形成不同的张量

参数:

  • indices 指定的张量保存指向输出张量的索引

  • updates 它是声明的张量,用于保存索引的值

  • shape 它是输出张量的规定形状

  • input 输入的张量数据

返回:张量


_ScatterNd

MNN_PUBLIC VARP _ScatterNd(VARP indices, VARP updates, VARP shape, int reduction);

根据声明的索引,通过对声明的形状张量的零张量内的单个切片或值进行分散更新,来形成不同的张量

参数:

  • indices 指定的张量保存指向输出张量的索引

  • updates 它是声明的张量,用于保存索引的值

  • shape 它是输出张量的规定形状

  • reduction 减少数值,默认为-1

返回:张量


_ScatterNd

MNN_PUBLIC VARP _ScatterNd(VARP indices, VARP updates, VARP shape, VARP input, int reduction);

根据声明的索引,通过对声明的形状张量的零张量内的单个切片或值进行分散更新,来形成不同的张量

参数:

  • indices 指定的张量保存指向输出张量的索引

  • updates 它是声明的张量,用于保存索引的值

  • shape 它是输出张量的规定形状

  • input 输入的张量数据

  • reduction 减少数值,默认为-1

返回:张量


_ScatterElements

MNN_PUBLIC VARP _ScatterElements(VARP data, VARP indices, VARP updates, int reduction = -1);

根据updates和indices来更新data的值,并把结果返回

参数:

  • data 一个张量

  • indices 一个张量

  • updates 一个张量

  • reduction 减少数值,默认为-1

返回:更新后的data


_ScatterElements

MNN_PUBLIC VARP _ScatterElements(VARP data, VARP indices, VARP updates, VARP axis, int reduction = -1);

根据updates和indices来更新data的值,并把结果返回

参数:

  • data 一个张量

  • indices 一个张量

  • updates 一个张量

  • axis 数轴,表示在行还是列进行操作

  • reduction 减少数值,默认为-1

返回:更新后的data


_OneHot

MNN_PUBLIC VARP _OneHot(VARP indices, VARP depth, VARP onValue, VARP offValue, int axis = -1);

独热编码,一般是在有监督学习中对数据集进行标注时候使用的,指的是在分类问题中,将存在数据类别的那一类用X表示,不存在的用Y表示,这里的X常常是1,Y常常是0

参数:

  • indices 输入的张量

  • depth 一个标量,用于定位维度的深度

  • onValue 定义在indices[j] = i 时填充输出值的标量

  • offValue 定义在indices[j] != i 时填充输出值的标量

  • axis 要填充的轴,默认为-1

返回:编码数据


_BroadcastTo

MNN_PUBLIC VARP _BroadcastTo(VARP a, VARP shape);

利用广播将原始矩阵成倍增加

参数:

  • a 广播的张量

  • shape 期望输出的形状

返回:矩阵


_LinSpace

MNN_PUBLIC VARP _LinSpace(VARP start, VARP stop, VARP num);

创建一个等差数列

参数:

  • start 数据的起始点,即区间的最小值

  • stop 数据的结束点,即区间的最大值

  • num 数据量,可以理解成分割了多少份

返回:等差数列


_RandomUnifom

MNN_PUBLIC VARP _RandomUnifom(VARP shape, halide_type_t dtype, float low = 0.0f, float high = 1.0f, int seed0 = 0, int seed1 = 0);

获取随机数

参数:

  • shape 输入数据的形状

  • dtype 目标类型

  • low 随机数的最小区间值

  • high 随机数的最大区间值

  • seed0 随机因子

  • seed1 随机因子

返回:dtype类型的随机数


_CumSum

MNN_PUBLIC VARP _CumSum(VARP x, int axis, bool exclusive = false, bool reverse = false);

计算元素x在axis坐标轴的累加值

参数:

  • x 输入参数

  • axis 坐标轴

  • exclusive 默认为false

  • reverse 是否逆向,默认为false

返回:和x相同类型的变量


_CumProd

MNN_PUBLIC VARP _CumProd(VARP x, int axis);

计算x在axis坐标轴的累计乘积

参数:

  • x 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

  • axis 坐标轴

返回:和x相同类型的变量


_Svd

MNN_PUBLIC VARPS _Svd(VARP x);

奇异值分解,降维算法中的特征分解

参数:

  • x 一个变量,Halide_Type_Float, Halide_Type_Int类型之一

返回:和x相同类型的变量


_Histogram

MNN_PUBLIC VARP _Histogram(VARP x, int bin, int min, int max, int channel = -1);

直方图函数

参数:

  • x 待统计的数据

  • bin 指定统计的区间个数

  • min 统计范围最小值

  • max 统计范围最大值

  • channel通道,默认为-1

返回:和x相同类型的变量

NeuralNetWorkOp

class NeuralNetWorkOp

成员函数


_Input

MNN_PUBLIC VARP _Input(INTS shape = {}, Dimensionformat data_format = NC4HW4, halide_type_t dtype = halide_type_of<float>());

创建一个输入变量

参数:

  • shape 一个矢量,变量的形状

  • data_format 一个枚举值,允许为NCHW/NHWC/NC4HW4

  • dtype 目的变量的元素类型

返回:变量


_Clone

MNN_PUBLIC VARP _Clone(VARP source, bool deepCopy = false);

克隆变量

参数:

  • source 被克隆的变量

  • deepCopy 是否深度拷贝,默认为false

返回:与source类型相同的变量


_Scalar

MNN_PUBLIC VARP _Scalar(const void* ptr, halide_type_t type);

用于创建张量均值的标量类型

参数:

  • ptr 一个指针,标量的值

  • type 目标变量的数据类型

返回:标量类型


_Const

MNN_PUBLIC VARP _Const(float value, INTS shape = {}, Dimensionformat format = NHWC);

创建一个不可变变量

参数:

  • value 显示的值

  • shape 一个矢量,变量的形状

  • format 一个枚举值,允许是NCHW/NHWC/NC4HW4

返回:一个不可变变量


_Const

MNN_PUBLIC VARP _Const(const void* ptr, INTS shape = {}, Dimensionformat format = NHWC,
                       halide_type_t type = halide_type_of<float>());

创建一个不可变变量

参数:

  • ptr 一个指针,显示的值

  • shape 一个矢量,变量的形状

  • format 一个枚举值,允许是NCHW/NHWC/NC4HW4

  • type 目标变量的数据类型

返回:一个不可变变量


_TrainableParam

MNN_PUBLIC VARP _TrainableParam(float value, INTS dims, Dimensionformat format);

可训练的参数

参数:

  • value 参数的值

  • dims 一个向量。置换后的索引轴顺序

  • format 目标格式

返回:参数变量


_TrainableParam

MNN_PUBLIC VARP _TrainableParam(const void* ptr, INTS dims, Dimensionformat format,
                                halide_type_t type = halide_type_of<float>());

可训练的参数

参数:

  • ptr 一个指针,参数的值

  • dims 一个向量,置换后的索引轴顺序

  • format 目标格式

  • type 目标变量的数据类型

返回:参数变量


_InnerProduct

MNN_PUBLIC VARP _InnerProduct(std::vector<float>&& weight, std::vector<float>&& bias, VARP x, INTS outputShape);

全连接层,运算实质上是若干的输入向量与权值矩阵中的权值向量做內积的过程

参数:

  • weight 权值矩阵

  • bias 偏置项行向量

  • x 输入变量

  • outputShape 输出形状

返回:变量


_Conv

MNN_PUBLIC VARP _Conv(VARP weight, VARP bias, VARP x, PaddingMode pad = VALID, INTS stride = {1, 1},
                      INTS dilate = {1, 1}, int group = 1, INTS pads = {0, 0});

对由多个输入平面组成的输入信号进行卷积

参数:

  • weight 卷积产生的通道数

  • bias 偏置项行向量,在输出中添加一个可学习的偏差

  • x 输入变量

  • pad 填充模式,默认为VALID

  • stride 卷积步长,默认为{1, 1}

  • dilate 扩张操作:控制kernel点(卷积核点)的间距,默认值为{1, 1}

  • group 控制分组卷积,默认不分组,为1组

  • pads 填充操作,默认为{0, 0}

返回:卷积


_Conv

MNN_PUBLIC VARP _Conv(float weight, float bias, VARP x, INTS channel, INTS kernelSize, PaddingMode pad = VALID,
                      INTS stride = {1, 1}, INTS dilate = {1, 1}, int group = 1);

对由多个输入平面组成的输入信号进行卷积

参数:

  • weight 卷积产生的通道数

  • bias 偏置项行向量,在输出中添加一个可学习的偏差

  • x 输入变量

  • channel 渠道

  • kernelSize 卷积核大小

  • pad 填充模式,默认为VALID

  • stride 卷积步长,默认为{1, 1}

  • dilate 扩张操作:控制kernel点(卷积核点)的间距,默认值为{1, 1}

  • group 控制分组卷积,默认不分组,为1组

返回:卷积


_Conv

MNN_PUBLIC VARP _Conv(std::vector<int8_t>&& weight, std::vector<float>&& bias, VARP x, INTS channel, INTS kernelSize,
                      PaddingMode pad = VALID, INTS stride = {1, 1}, INTS dilate = {1, 1}, int group = 1, INTS pads = {0, 0}, bool relu = false, bool relu6 = false, int nbits = 8);

对由多个输入平面组成的输入信号进行卷积

参数:

  • weight 卷积产生的通道数

  • bias 偏置项行向量,在输出中添加一个可学习的偏差

  • x 输入变量

  • channel 渠道

  • kernelSize 卷积核大小

  • pad 填充模式,默认为VALID

  • stride 卷积步长,默认为{1, 1}

  • dilate 扩张操作:控制kernel点(卷积核点)的间距,默认值为{1, 1}

  • group 控制分组卷积,默认不分组,为1组

  • pads 填充操作,默认为{0, 0}

  • relu 是否修正线性单元,默认为fasle

  • relu6 修正线性单元6,默认为fasle

  • nbits 默认为8

返回:卷积


_Conv

MNN_PUBLIC VARP _Conv(std::vector<float>&& weight, std::vector<float>&& bias, VARP x, INTS channel, INTS kernelSize,
                      PaddingMode pad = VALID, INTS stride = {1, 1}, INTS dilate = {1, 1}, int group = 1, INTS pads = {0, 0}, bool relu = false, bool relu6 = false);

对由多个输入平面组成的输入信号进行卷积

参数:

  • weight 卷积产生的通道数

  • bias 偏置项行向量,在输出中添加一个可学习的偏差

  • x 输入变量

  • channel 渠道

  • kernelSize 卷积核大小

  • pad 填充模式,默认为VALID

  • stride 步长,默认为{1, 1}

  • dilate 扩张操作:控制kernel点(卷积核点)的间距,默认值为{1, 1}

  • group 控制分组卷积,默认不分组,为1组

  • pads 填充操作,默认为{0, 0}

  • relu 是否修正线性单元,默认为fasle

  • relu6 修正线性单元6,默认为fasle

返回:卷积


_Deconv

MNN_PUBLIC VARP _Deconv(VARP weight, VARP bias, VARP x, PaddingMode pad = VALID, INTS stride = {1, 1},
                                INTS dilate = {1, 1}, int group = 1, INTS pads = {0, 0});

卷积的反向操作

参数:

  • weight 反卷积产生的通道数

  • bias 偏置项行向量,在输出中添加一个可学习的偏差

  • x 输入变量

  • pad 填充模式,默认为VALID

  • stride 步长,默认为{1, 1}

  • dilate 扩张操作:控制kernel点(反卷积核点)的间距,默认值为{1, 1}

  • group 控制分组反卷积,默认不分组,为1组

  • pads 填充操作,默认为{0, 0}

返回:转置卷积(反卷积)


_Deconv

MNN_PUBLIC VARP _Deconv(std::vector<float>&& weight, std::vector<float>&& bias, VARP x, INTS channel, INTS kernelSize,
PaddingMode pad, INTS stride = {1, 1}, INTS dilate = {1, 1}, int group = 1, INTS pads = {0, 0}, bool relu = false, bool relu6 = false);

卷积的反向操作

参数:

  • weight 反卷积产生的通道数

  • bias 偏置项行向量,在输出中添加一个可学习的偏差

  • x 输入变量

  • channel 渠道

  • kernelSize 反积核大小

  • pad 填充模式,默认为VALID

  • stride 步长,默认为{1, 1}

  • dilate 扩张操作:控制kernel点(反卷积核点)的间距,默认值为{1, 1}

  • group 控制分组反卷积,默认不分组,为1组

  • pads 填充操作,默认为{0, 0}

  • relu 是否修正线性单元,默认为fasle

  • relu6 是否修正线性单元6,默认为fasle

返回:转置卷积(反卷积)


_MaxPool

MNN_PUBLIC VARP _MaxPool(VARP x, INTS kernel, INTS stride = {1, 1}, PaddingMode pad = VALID, INTS pads= {0, 0});

最大值池化操作

参数:

  • x 池化的输入

  • kernel 内核

  • stride 窗口在每一个维度上滑动的步长

  • pad 填充模式,默认为VALID

  • pads 填充操作,默认为{0, 0}

返回:最大池化值


_AvePool

MNN_PUBLIC VARP _AvePool(VARP x, INTS kernel, INTS stride = {1, 1}, PaddingMode pad = VALID, INTS pads= {0, 0});

平均池化操作

参数:

  • x 池化的输入

  • kernel 内核

  • stride 窗口在每一个维度上滑动的步长

  • pad 填充模式,默认为VALID

  • pads 填充操作,默认为{0, 0}

返回:平均池化值


_Reshape

MNN_PUBLIC VARP _Reshape(VARP x, INTS shape, Dimensionformat original_format = NCHW);

重塑一个变量

参数:

  • x 被重塑的变量

  • shape 一个矢量,目标的形状变量

  • original_format 一个枚举值,只允许NCHW/NHWC,不允许NC4HW4,因为它提供额外的信息(x来自NCHW或NHWC)当x是NC4HW4时

返回:与’ x ‘类型相同的变量


_Reshape

MNN_PUBLIC VARP _Reshape(VARP x, VARP shape);;

重塑一个变量

参数:

  • x 被重塑的变量

  • shape 一个矢量,目标的形状变量

返回:与’ x ‘类型相同的变量


_Scale

MNN_PUBLIC VARP _Scale(VARP x, int channels, std::vector<float>&& scales, std::vector<float>&& bias);

返回x * scale + bias的值

参数:

  • x 输入变量

  • channels 渠道

  • scales 输入变量

  • bias 输入变量

返回:x * scale + bias的值


_Relu

MNN_PUBLIC VARP _Relu(VARP x, float slope = 0.0f);

给定一个输入值x,如果x > 0,它计算输出为x,如果x <= 0,则返回斜率 * x

参数:

  • x 一个输入变量

  • slope 一个浮点数,一个正的浮点值,它通过乘以“斜率”而不是设置为0.0f来漏掉负的部分,默认为0.0f

返回:与’ x ‘类型相同的变量


_Relu6

MNN_PUBLIC VARP _Relu6(VARP x, float minValue = 0.0f, float maxValue = 6.0f);

给定一个输入值x,它计算修正线性6: min(max(x, 0), 6)

参数:

  • x 一个输入变量

  • minValue 最小值

  • maxValue 最大值

返回:与’ x ‘类型相同的变量


_PRelu

MNN_PUBLIC VARP _PRelu(VARP x, std::vector<float> &&slopes);

给定一个输入值x,如果x > 0,它计算输出为x,如果x <= 0,则返回斜率 * x

参数:

  • x 一个变量,必须是4-D的NC4HW4格式

  • slopes 一个向量,保存大小为x

返回:与’ x ‘类型相同的变量


_Softmax

MNN_PUBLIC VARP _Softmax(VARP logits, int axis = -1);

归一化指数函数,作用是将多分类的结果以概率的形式展现出来

参数:

  • logits 一个非空的变量,必须Halide_Type_Float

  • axis 默认值是-1,表示最后一个维度

返回:与’ x ‘类型相同的变量


_Softplus

MNN_PUBLIC VARP _Softplus(VARP features);

激活函数,可以看作是ReLU函数的平滑:log(exp(features) + 1)

参数:

  • features 一个变量,必须Halide_Type_Float

返回:与’features’类型相同的变量


_Softsign

MNN_PUBLIC VARP _Softsign(VARP features);

激活函数,是Tanh函数的另一个替代选择:features / (abs(features) + 1)

参数:

  • features 一个变量,必须Halide_Type_Float

返回:与’features’类型相同的变量


_Split

MNN_PUBLIC std::vector<VARP> _Split(VARP value, INTS size_splits, int axis = 0);

将变量值拆分为子变量列表

参数:

  • value 要拆分的变量

  • size_splits 一个矢量,一个一维整数,包含每个输出变量沿轴的大小

  • axis 一个int型,沿其进行分割的维度,必须在范围[-rank(value), rank(value))内,默认值为0

返回:变量列表


_Slice

MNN_PUBLIC VARP _Slice(VARP x, VARP starts, VARP sizes);

返回从张量中提取想要的切片,此操作从由starts指定位置开始的张量x中提取一个尺寸sizes的切片,切片sizes被表示为张量形状,提供公式x[starts[0]:starts[0]+sizes[0], ..., starts[-1]:starts[-1]+sizes[-1]]

参数:

  • x 输入变量

  • starts 切片提取起始位置

  • sizes 切片提取的尺寸

返回:切片数据


_StridedSlice

MNN_PUBLIC VARP _StridedSlice(VARP input, VARP begin, VARP end, VARP strided,
                              int32_t beginMask, int32_t endMask, int32_t ellipsisMask,
                              int32_t newAxisMask, int32_t shrinkAxisMask);

从给定的 x 张量中提取一个尺寸 (end-begin)/stride 的片段,从 begin 片段指定的位置开始,以步长 stride 添加索引,直到所有维度都不小于 end,这里的 stride 可以是负值,表示反向切片,公式:x[begin[0]:strides[0]:end[0], ..., begin[-1]:strides[-1]:end[-1]]

参数:

  • input 输入变量

  • begin 开始切片处

  • end 终止切片处

  • strided 步长

  • beginMask 输入变量,默认为0

  • endMask 输入变量,默认为0

  • ellipsisMask 输入变量,默认为0

  • newAxisMask 输入变量,默认为0

  • shrinkAxisMask 输入变量,默认为0

返回:提取的片段


_StridedSliceWrite

MNN_PUBLIC VARP _StridedSliceWrite(VARP input, VARP begin, VARP end, VARP strided, VARP write,
                                   int32_t beginMask, int32_t endMask, int32_t ellipsisMask,
                                   int32_t newAxisMask, int32_t shrinkAxisMask);

从给定的 x 张量中提取一个尺寸 (end-begin)/stride 的片段,从 begin 片段指定的位置开始,以步长 stride 添加索引,直到所有维度都不小于 end,这里的 stride 可以是负值,表示反向切片,可以写入。公式:x[begin[0]:strides[0]:end[0], ..., begin[-1]:strides[-1]:end[-1]]

参数:

  • input 输入变量

  • begin 开始切片处

  • end 终止切片处

  • strided 步长

  • write 输入变量

  • beginMask 输入变量,默认为0

  • endMask 输入变量,默认为0

  • ellipsisMask 输入变量,默认为0

  • newAxisMask 输入变量,默认为0

  • shrinkAxisMask 输入变量,默认为0

返回:提取的片段


_Concat

MNN_PUBLIC VARP _Concat(VARPS values, int axis);

沿某个维度连接变量

参数:

  • values 变量列表单个变量

  • axis 一个int,要连接的维度,必须在范围[-rank(values),rank(values))内。与在Python中一样,axis的索引是基于0的,区间[0,rank(values))为轴第1维,负轴表示轴+秩(值)-第维

返回:由输入变量连接而产生的变量


_Convert

MNN_PUBLIC VARP _Convert(VARP input, Dimensionformat format);

将变量转换为另一种格式(可能添加在’ input ‘之后)

参数:

  • input 输入变量

  • format 目标格式

返回:一个变量,如果’input’已经是’format’,那么直接返回’input’,否则在’input’后面加上’format’变量


_Transpose

MNN_PUBLIC VARP _Transpose(VARP x, INTS perm);

转置x

参数:

  • x 输入变量

  • perm 一个向量,表示x的维数的排列

返回:转置变量


_Transpose

MNN_PUBLIC VARP _Transpose(VARP x, VARP perm);

转置x

参数:

  • x 输入变量

  • perm 一个向量,表示x的维数的排列

返回:转置变量


_ChannelShuffle

MNN_PUBLIC VARP _ChannelShuffle(VARP x, int group);

做以下操作: x = _Convert(x, NHWC); x = _Reshape(x, {0, 0, 0, group, -1}, NHWC); x = _Transpose(x, {0, 1, 2, 4, 3}); x = _Reshape(x, {0, 0, 0, -1}, NHWC); channel_shuffle_res = _Convert(x, NC4HW4);

参数:

  • x 输入变量

  • group 控制分组

返回:一个变量,如果’input’已经是’format’,那么直接返回’input’,否则在’input’后面加上’format’变量


_ChangeInputFormat

MNN_PUBLIC VARP _ChangeInputFormat(VARP input, Dimensionformat format);

将变量转换为另一种格式(可能添加在’ input ‘之前)

参数:

  • input 输入变量

  • format 目标格式

返回:目标变量,如果’input’已经是’format’,那么直接返回’input’,否则在’input’之前加上一个’format’变量。


_Conv2DBackPropFilter

MNN_PUBLIC VARP _Conv2DBackPropFilter(VARP input, VARP inputGrad, INTS kernelSize, PaddingMode pad = VALID, INTS stride = {1, 1}, INTS dilate = {1, 1}, int group = 1, INTS pads = {0, 0});

计算卷积相对于滤波器的梯度

参数:

  • input 4-D形状’[batch, in_height, in_width, in_channels]’

  • inputGrad 输入梯度

  • kernelSize 卷积核大小

  • pad 填充模式,默认为VALID

  • stride 滑动窗口的步长对每个维度的输入进行卷积,必须与格式指定的尺寸顺序相同

  • dilate 扩张操作:控制kernel点(卷积核点)的间距,默认值为{1, 1}

  • group 控制分组卷积,默认不分组,为1组

  • pads 填充操作,默认为{0, 0}

返回:梯度


_PoolGrad

MNN_PUBLIC VARP _PoolGrad(VARP originInput, VARP originOutput, VARP inputGrad, INTS kernel, INTS stride, PoolingMode type, PaddingMode pad = VALID, INTS pads= {0, 0});

池化操作

参数:

  • originInput 起始输入

  • originOutput 起始输出

  • inputGrad 输入梯度

  • kernel 内核

  • stride 窗口在每一个维度上滑动的步长

  • type 池化类型

  • pad 填充模式,默认为VALID

  • pads 填充操作,默认为{0, 0}

返回:池化的值


_ReverseSequence

MNN_PUBLIC VARP _ReverseSequence(VARP x, VARP y, int batchDim, int seqDim);

沿着batch_dim维度对x进行切片并反转维度seq_dim上的y[i]元素

参数:

  • x 输入变量

  • y 输入变量

  • batchDim 切片的维度

  • seqDim 反转的维度

返回:反转序列的值


_Crop

MNN_PUBLIC VARP _Crop(VARP images, VARP size, int axis, INTS offset);

裁剪图片

参数:

  • images NC4HW4格式的4-D变量

  • size 一个变量,它以’size’的形状作为输出裁剪变量的形状,而省略了’size’的值/格式

  • axis 指示要裁剪的维度的整数,必须> = 2。所有在“axis”之前但不包括“axis”的维度都被保留,而包括和尾随“axis”的维度则被裁剪

  • offset 表示偏移量的int型矢量,Length (’offset’)必须为>=1且<=2。如果length(’offset’)为1,那么所有维度都将被这个量所抵消。否则,偏移量的数量必须等于每个维度中裁剪轴的数量

返回:NC4HW4格式的裁剪4-D变量


_Resize

MNN_PUBLIC VARP _Resize(VARP images, float xScale, float yScale);

调整图像

参数:

  • images NC4HW4格式的4-D变量

  • xScale 在x轴的缩放比例

  • yScale 在y轴的缩放比例

返回:NC4HW4格式的调整大小的4-D变量


_Pad

MNN_PUBLIC VARP _Pad(VARP x, VARP paddings, PadValueMode mode = CONSTANT);

对张量进行填充

参数:

  • x 输入变量

  • paddings Halide_Type_Int类型的变量,形状为[n, 2],其中n为变量的秩

  • mode 一个枚举值,PadValueMode_CONSTANT、PadValueMode_SYMMETRIC或PadValueMode_REFLECT之一

返回:和x有相同的类型的变量


_ExpandDims

MNN_PUBLIC VARP _ExpandDims(VARP input, int axis);

返回在索引轴插入额外维度的变量

参数:

  • input 输入变量

  • axis 一个int,指定在其上展开输入形状的维度索引,给定一个D维的输入,轴必须在范围-(D+1), D

返回:具有与输入相同数据的变量,在轴指定的索引处插入额外的维度


_ExpandDims

MNN_PUBLIC VARP _ExpandDims(VARP input, VARP axis);

返回在索引轴插入额外维度的变量

参数:

  • input 输入变量

  • axis 一个int,指定在其上展开输入形状的维度索引,给定一个D维的输入,轴必须在范围-(D+1), D

返回:具有与输入相同数据的变量,在轴指定的索引处插入额外的维度


_Shape

MNN_PUBLIC VARP _Shape(VARP input, bool nchw = false);

返回变量的形状

参数:

  • input 输入变量

  • nchw 默认为false

返回:Halide_Type_Int类型的变量


_Stack

MNN_PUBLIC VARP _Stack(VARPS values, int axis=0);

将一列rank-R变量堆叠成一个rank-(R+1)变量,将’values’中的变量列表打包到一个比values中的每个变量都高1的变量中,通过沿轴尺寸排列它们,给定一个长度为N的形状变量列表(a, B, C)。 如果axis == 0,那么输出变量将具有形状(N, A, B, C)。 如果axis == 1,那么输出变量将具有形状(A, N, B, C)。

参数:

  • values 具有相同形状和类型的变量对象列表

  • axis 一个int,沿轴堆叠。默认为第一个维度。负值环绕,所以有效范围是[-(R+1), R+1)

返回:一个与values相同类型的堆叠变量


_CropAndResize

MNN_PUBLIC VARP _CropAndResize(VARP image, VARP boxes, VARP box_ind, VARP crop_size, 
                                InterpolationMethod method, float extrapolation_value = 0.0);

从输入图像变量中提取作物,并使用双线性采样或最近邻采样(可能会改变长宽比)调整它们的大小,到由crop_size指定的通用输出大小。返回一个带有农作物的变量,该变量来自于框中边界框位置定义的输入图像位置。裁剪的盒子都被调整大小(双线性或最近邻插值)为固定大小= [crop_height, crop_width]。结果是一个4-D张量num_boxes, crop_height, crop_width, depth

参数:

  • image 一个形状[batch, image_height, image_width, depth]的4-D变量(假设NHWC格式)。image_height和image_width都必须为正

  • boxes 形状为[num_boxes, 4]的二维变量。变量的第i行指定了box_ind[i]图像中盒子的坐标,并以规范化坐标[y1, x1, y2, x2]指定将y的一个归一化坐标值映射到y * (image_height - 1)处的图像坐标,因此,在图像高度坐标中,归一化图像高度的[0,1]区间映射为[0,image_height - 1]。我们允许y1 > y2,在这种情况下,采样的裁剪是原始图像的上下翻转版本。宽度维度的处理方式类似。允许在[0,1]范围之外的规范化坐标,在这种情况下,我们使用extrapolation_value来外推输入图像值。

  • box_ind 形状为[num_boxes]的1-D变量,其int值范围为[0,batch),box_ind[i]的值指定第i个框所指向的图像。

  • crop_size 一个包含2个元素的1-D变量,size = [crop_height, crop_width]。所有裁剪的图像补丁都调整到这个大小。不保留图像内容的长宽比。作物高度和作物宽度都必须是正的。

  • method 一个枚举值, CropAndResizeMethod_NEAREST或CropAndResizeMethod_BILINEAR,默认为CropAndResizeMethod_BILINEAR,extrapolation_value:适用时用于外推的值。

  • extrapolation_value 推断值,默认为0.0

返回:形状[num_boxes, crop_height, crop_width, depth]的4-D变量(假设NHWC格式)


_Fill

MNN_PUBLIC VARP _Fill(VARP dims, VARP value);

创建一个填充标量值的变量

参数:

  • dims 一个变量,必须是1-D Halide_Type_Int。表示输出变量的形状

  • value 一个变量,0-D(标量),值填充返回的变量

返回:一个变量,类型与值相同


_Tile

MNN_PUBLIC VARP _Tile(VARP input, VARP multiples);

通过平铺给定变量来构造一个变量

参数:

  • Fill 一个变量,一维或更高

  • multiples 一个变量,必须是1-D Halide_Type_Int,长度必须与输入的维度数相同

返回:一个变量,与输入的类型相同


_Gather

MNN_PUBLIC VARP _Gather(VARP params, VARP indices);

根据索引从参数中收集切片

参数:

  • params 收集值的变量

  • indices 指标变量,必须是范围[0,ndim (params)-1]的Halide_Type_Int

返回:从索引给出的索引中收集的参数值


_GatherV2

MNN_PUBLIC VARP _GatherV2(VARP params, VARP indices, VARP axis = nullptr);

根据索引从参数轴收集切片

参数:

  • params 收集值的变量

  • indices 指标变量,必须是范围[0,ndim (params)-1]的Halide_Type_Int

  • axis 一个int,参数中用于收集下标的轴,支持负索引,如果设为0,它就和_Gather一样。目前只支持0

返回:从索引给出的索引收集的参数值


_Squeeze

MNN_PUBLIC VARP _Squeeze(VARP input, INTS axis = {});

从变量的形状中移除大小为1的维度

参数:

  • input 一个变量,挤压输入

  • axis 一个向量,默认为{}。如果指定,只挤压所列的尺寸。维度索引从0开始。必须在范围内[-rank(input), rank(input))

返回:一个变量,与输入的类型相同。包含与输入相同的数据,但删除了一个或多个大小为1的维度


_Unsqueeze

MNN_PUBLIC VARP _Unsqueeze(VARP input, INTS axis = {});

插入到指定位置的尺寸为1的新数据

参数:

  • input 输入变量

  • axis 默认为{},用来指定要增加的为1的维度

返回:变换后的数据


_BatchToSpaceND

MNN_PUBLIC VARP _BatchToSpaceND(VARP input, VARP block_shape, VARP crops);

BatchToSpace用于N-D变量,该操作将“batch”维度0重塑为形状block_shape + [batch]的M + 1个维度,将这些块插入由空间维度定义的网格中[1,…]M],获得与输入相同秩的结果。这个中间结果的空间维度然后根据作物选择性地裁剪产生的输出。这与SpaceToBatch正好相反。请参阅下面的精确描述。

参数:

  • input 必须为4-D格式,且为NC4HW4格式。N-D的形状input_shape = [batch] + spatial_shape + remaining_shape,其中spatial_shape有M个维度。

  • block_shape 形状为[M]的1- d,所有值必须为>= 1

  • crops 形状为[M, 2]的二维,所有值必须为>= 0。Crops [i] = [crop_start, crop_end]指定从输入维度i + 1开始的作物数量,对应空间维度i,要求crop_start[i] + crop_end[i] <= block_shape[i] * input_shape[i + 1]。 该操作相当于以下步骤: shape的整形输入:[block_shape[0],…], block_shape[M-1], batch / prod(block_shape),input_shape[1],……, input_shape [n]]置换变形的尺寸产生置换的形状: [batch / prod(block_shape),input_shape[1], block_shape[0],…], input_shape [M], block_shape [M - 1], input_shape [M + 1],…, input_shape [n]] 重塑排列以产生形状的重塑: [batch / prod(block_shape),input_shape[1] * block_shape[0],…], input_shape [M] * block_shape [M - 1], input_shape [M + 1],…, input_shape [n]] 裁剪开始和结束的维度[1,…, M]的reshaped_per组合根据作物产生形状的输出: [batch / prod(block_shape),input_shape[1] * block_shape[0] -庄稼[0,0]-庄稼[0,1],…], input_shape [M] * block_shape [M - 1] -作物[M - 1,0]作物(M - 1, - 1), input_shape [M + 1],…, input_shape [n]] 例子: 对于以下形状[4,1,1,3],block_shape = [2,2], and crops =[[0,0],[0,0]]的输入: [[[[1, 2, 3]]],[[[4、5、6]]],[[[7 8 9]]],[[[10 11 12]]]] 输出变量的形状为[1,2,2,3],值为: X = [[[[1,2,3], [4,5,6], [[7, 8, 9], [10, 11, 12]]]]

返回:输出变量


_GatherND

MNN_PUBLIC VARP _GatherND(VARP params, VARP indices);

将参数中的切片收集到一个由索引指定形状的变量中

参数:

  • input 一个变量,用于收集值的变量

  • indices 一个变量,Halide_Type_Int类型

返回:一个变量,与参数有相同的类型


_GatherElements

MNN_PUBLIC VARP _GatherElements(VARP params, VARP indices);

返回将参数中的切片收集到一个由索引指定形状的变量

参数:

  • params 输出参数

  • indices 一个变量,Halide_Type_Int类型

返回:一个变量,与参数有相同的类型


_GatherElements

MNN_PUBLIC VARP _GatherElements(VARP params, VARP indices, VARP axis);

返回将参数中的切片收集到一个由索引指定形状的变量

参数:

  • params 输出参数

  • indices 一个变量,Halide_Type_Int类型

  • axis 操作的维度

返回:一个变量,与参数有相同的类型


_Selu

MNN_PUBLIC VARP _Selu(VARP features, float scale, float alpha);

计算缩放指数线性:scale * alpha * (exp(特征)- 1),如果< 0,scale * 特征

参数:

  • features Halide_Type_Float类型的变量

  • scale 比例因子(正浮点数)

  • alpha 透明度因子(正浮动)

返回:一个变量,具有与功能相同的类型


_Size

MNN_PUBLIC VARP _Size(VARP input);

计算变量的大小

参数:

  • input Halide_Type_Float或Halide_Type_Int类型的变量

返回:一个变量,形状是(),类型是Halide_Type_Int


_Elu

MNN_PUBLIC VARP _Elu(VARP features, float alpha=1.0);

计算指数线性:alpha * (exp(特征)- 1),如果< 0,则特征为其他,Halide_Type_Float类型的变量

参数:

  • features 一个变量,必须Halide_Type_Float

  • alpha Alpha因子(正浮动)

返回:一个变量,具有与功能相同的类型


_Threshold

MNN_PUBLIC VARP _Threshold(VARP features, float threshold);

给定一个输入值x,如果x > threshold,它计算输出为1.0,如果x <= threshold,则为0.0

参数:

  • features 一个变量,必须Halide_Type_Float

  • threshold 阈值

返回:一个变量,具有与功能相同的类型


_MatrixBandPart

MNN_PUBLIC VARP _MatrixBandPart(VARP input, VARP num_lower, VARP num_upper);

复制一个变量,设置每个最内层矩阵中中心带以外的所有内容

参数:

  • input 秩为k的变量

  • num_lower 要保留的子对角线数。如果是负的,保持整个下三角形

  • num_upper 要保持的超对角线的数量。如果是负的,保持整个上三角形

返回:将k变量的形状与输入相同,提取的带状张量


_Moments

MNN_PUBLIC std::vector<VARP> _Moments(VARP x, INTS axis, VARP shift, bool keepDims);

计算x的均值和方差

参数:

  • x 一个变量,必须是4-D的NC4HW4格式

  • axis 整数的数组。计算平均值和方差的轴。忽略此实现:必须为{2,3}

  • shift 在当前实现中未使用

  • keepDims 产生与输入相同维度的力矩。忽略此实现:必须为true

返回:均值和方差


_SetDiff1D

MNN_PUBLIC VARP _SetDiff1D(VARP x, VARP y);

计算两个数字或字符串列表之间的差值,给定一个列表x和一个列表y,该操作返回一个列表,该列表表示在x中但不在y中所有的值。返回的列表的排序顺序与x中数字出现的顺序相同(保留重复的数)。此操作还返回一个列表idx,该列表表示x中每个out元素的位置。

参数:

  • x Halide_Type_Int类型的1-D变量

  • y Halide_Type_Int类型的1-D变量,值删除

返回:Halide_Type_Int类型的1-D变量。值在x中存在,但在y中不存在


_SpaceToDepth

MNN_PUBLIC VARP _SpaceToDepth(VARP input, int block_size);

重新排列空间数据块,进入深度。更具体地说,它输出输入变量的副本,其中高度和宽度维度的值被移动到深度维度。block_size表示输入块的大小。大小为block_size x block_size的非重叠块在每个位置重新排列为深度。输出变量的深度是block_size * block_size * input_depth。每个输入块中的Y、X坐标成为输出通道索引的高阶分量。输入变量的高度和宽度必须能被block_size整除

参数:

  • input 输入变量

  • block_size 一个整数>= 2。空间块的大小

返回:一个变量。与输入的类型相同


_SpaceToBatchND

MNN_PUBLIC VARP _SpaceToBatchND(VARP input, VARP block_shape, VARP paddings);

他的操作划分了“空间”维度[1,…, M]输入到形状块block_shape的网格中,并将这些块与“批处理”维度交织使得在输出中,空间维度[1,…], M]对应网格内的位置,批处理尺寸结合了空间块内的位置和原始批处理位置。在划分为块之前,输入的空间维度可以根据填充值选择零填充。请参阅下面的精确描述

参数:

  • input 一个变量。必须为4-D格式,且为NC4HW4格式。N-D的形状input_shape = [batch] + spatial_shape + remaining_shape,其中spatial_shape有M个维度。block_shape:一个变量。必须是以下类型之一:int32, int64。形状为[M]的1- d,所有值必须为>= 1。

  • block_size 一个整数>= 2。空间块的大小

  • paddings 一个变量。必须是以下类型之一:int32, int64。形状为[M, 2]的二维,所有值必须为>= 0。padding [i] = [pad_start, pad_end]指定输入维度i + 1的填充,对应空间维度i。要求block_shape[i]除input_shape[i + 1] + pad_start + pad_end。

返回:一个变量。与输入的类型相同


_ZerosLike

MNN_PUBLIC VARP _ZerosLike(VARP input);

创建一个所有元素都设为零的变量

参数:

  • input 输入变量

返回:一个所有元素都设为零的变量


_Unstack

MNN_PUBLIC std::vector<VARP> _Unstack(VARP value, int axis=0);

将秩为r的张量的给定维度解包为秩-(R-1)变量。 例如,给定一个形状变量(a, B, C, D): 如果axis == 0,那么输出中的第i个变量是切片值[i,:,:,:],输出中的每个变量将具有形状(B, C, D)(注意,与拆分不同,沿着拆分的维度消失了)。 如果axis == 1,那么输出中的第i个变量是切片值[:,i,:,:],输出中的每个变量都有形状(A, C, D)。

参数:

  • value 一个秩为R>0的变量

  • axis 一个int。沿轴线解叠。默认为第一个维度。负值环绕,所以有效范围是[-R, R)

返回:从值中分离出来的变量对象列表


_Rank

MNN_PUBLIC VARP _Rank(VARP input);

返回变量的秩,返回一个0-D的int32变量,表示输入的秩。 注意:变量的秩与矩阵的秩是不同的。 它是唯一选择变量的每个元素所需的索引数。它也被称为“顺序”、“程度”或“ndim”。

参数:

  • input 输入变量

返回:Halide_Type_Int类型的0-D变量


_Range

MNN_PUBLIC VARP _Range(VARP start, VARP limit, VARP delta);

创建一个数字序列

参数:

  • start 0-D变量(标量)

  • limit 0-D变量(标量)

  • delta 0-D变量(标量)

返回:数字序列


_DepthToSpace

MNN_PUBLIC VARP _DepthToSpace(VARP input, int block_size);

将深度数据重新排列为空间数据块。这是SpaceToDepth的反向转换。更具体地说,它输出输入变量的副本,其中深度维度的值在空间块中移动到高度和宽度维度

参数:

  • input 输入变量

  • block_size 一个整数>= 2。空间块的大小,与Space2Depth相同

返回:一个变量。与输入的类型相同


_PriorBox

MNN_PUBLIC VARP _PriorBox(VARP feature, VARP image, 
                          std::vector<float> min_size, std::vector<float> max_size, std::vector<float>aspect_ratio, 
                          bool flip, bool clip, std::vector<float>variance,
                          unsigned int img_h, unsigned int img_w, float step_h, float step_w, float offset = 0.5);

SSD网络的priorbox层,人脸检测网络

参数:

  • feature 一个变量。包含特性图,也就是caffprior中的底部[0]

  • image 一个变量。包含图像,也就是caffe中的底部[1]

  • min_size 最小区域大小(像素)

  • max_size 最大区域大小(像素)

  • aspect_ratio 各种纵横比。重复比率被忽略。如果没有提供,则使用默认1.0

  • flip 如果为true,则翻转每个纵横比。例如,如果有高宽比“r”,也会生成高宽比“1.0/r”,违约事实

  • clip 如果为true,则剪辑之前,使其在[0,1]内。默认为false。

  • variance 在bboxes之前调整方差

  • img_h 图像的高度。如果为0,则使用图像中的信息

  • img_w 图像的宽度。如果为0,则使用图像中的信息

  • step_h 步高

  • step_w 步宽

  • offset 每个单元格的左上角的偏移

返回:一个变量


_Permute

MNN_PUBLIC VARP _Permute(VARP input, INTS dims);

SSD网络的交换层,用于置换索引轴顺序的

参数:

  • input 一个变量。包含特性图,也就是caffe中的底部[0]

  • dims 一个向量。置换后的索引轴顺序

返回:一个变量


_DetectionOutput

MNN_PUBLIC VARP _DetectionOutput(VARP location, VARP confidence, VARP priorbox, 
                        unsigned int num_classes, bool share_location, int background_label_id, 
                        float nms_threshhold, int nms_topk, int code_type, 
                        bool variance_encoded_in_target,
                        int keep_top_k, float confidence_threshold, float visualize_threshold);

SSD网络的detectionoutput层,用于整合预选框、预选框偏移以及得分三项结果,最终输出满足条件的目标检测框、目标的label和得分

参数:

  • location 位置

  • confidence 得分

  • priorbox SSD网络的priorbox层,人脸检测网络

  • num_classes 预测种类

  • share_location 指示不同类之间是否共享位置,默认为true

  • background_label_id 默认为0

  • nms_threshhold nms的阈值

  • nms_topk nms的topk

  • code_type 表示bbox编码模式,默认= CORNER

  • variance_encoded_in_target variance是否被编码,默认为false

  • keep_top_k 每张图片在nms处理后保留框的数量,默认值-1(保留所有boxes)

  • confidence_threshold 得分阈值

  • visualize_threshold 阈值用于将检测结果可视化

返回:目标检测框、目标的label和得分


_DetectionPostProcess

MNN_PUBLIC  std::vector<VARP> _DetectionPostProcess(VARP encode_boxes, VARP class_predictions, VARP anchors, 
                        int num_classes, int max_detections, 
                        int max_class_per_detection, int detections_per_class, 
                        float nms_threshold, float iou_threshold, 
                        bool use_regular_nms, std::vector<float> centersize_encoding);

SSD网络的detectionpostprocess层,对于预测阶段,模型输出的结果比较多,需要筛选最终结果

参数:

  • encode_boxes 盒子编码

  • class_predictions 类预测

  • anchors 一个变量

  • num_classes 预测种类

  • max_detections 最大检测次数

  • max_class_per_detection 每次检测的最大类

  • detections_per_class 表示每个类的检测

  • nms_threshold nms的阈值

  • iou_threshold iou的阈值

  • use_regular_nms 是否使用常规的NMS方法,目前只支持false

  • centersize_encoding 一个浮点向量,表示中心大小编码

返回:4个变量,detection_boxes, detection_class, detection_scores, num_detections


_Interp

MNN_PUBLIC VARP _Interp(VARPS xs, float widthScale, float heightScale, int outputWidth, int outputHeight, int resizeType, bool alignCorners);

一维线性插值,返回离散数据的一维分段线性插值结果

参数:

  • xs 待插入数据的横坐标

  • widthScale 宽度比

  • heightScale 高度比

  • outputWidth 输出宽度

  • outputHeight 输出高度

  • resizeType 调整类型

  • alignCorners 是否边缘对齐

返回:离散数据的一维分段线性插值结果


_ZeroGrad

MNN_PUBLIC VARP _ZeroGrad(VARP x);

参数梯度置0.

参数:

  • x 输入变量

返回:梯度为0的变量


_Conv

MNN_PUBLIC VARP _Conv(std::vector<int8_t>&& weight, std::vector<int>&& bias, std::vector<float>&& scale, VARP x, INTS channel, INTS kernelSize, PaddingMode pad, INTS stride, INTS dilate, int group, INTS pads, bool relu, int nbits = 8);

对由多个输入平面组成的输入信号进行卷积

参数:

  • weight 卷积产生的通道数

  • bias 偏置项行向量,在输出中添加一个可学习的偏差

  • scale 缩放因子

  • x 输入变量

  • channel 渠道

  • kernelSize 卷积核大小

  • pad 填充模式,默认为VALID

  • stride 卷积步长,默认为{1, 1}

  • dilate 扩张操作:控制kernel点(卷积核点)的间距,默认值为{1, 1}

  • group 控制分组卷积,默认不分组,为1组

  • pads 填充操作,默认为{0, 0}

  • relu 是否修正线性单元,默认为fasle

  • nbits 默认为8

返回:卷积


_Conv

MNN_PUBLIC VARP _Conv(std::vector<int8_t>&& weight, std::vector<int>&& bias, std::vector<float>&& scale,
                      VARP x, INTS channel, INTS kernelSize,
                      PaddingMode pad, INTS stride, INTS dilate, int group, INTS pads, bool relu,
                      int8_t inputZeroPoint, int8_t outputZeroPoint,
                      int8_t minValue, int8_t maxValue, bool accumulateToInt16);

对由多个输入平面组成的输入信号进行卷积

参数:

  • weight 卷积产生的通道数

  • bias 偏置项行向量,在输出中添加一个可学习的偏差

  • scale 缩放因子

  • x 输入变量

  • channel 渠道

  • kernelSize 卷积核大小

  • pad 填充模式,默认为VALID

  • stride 卷积步长,默认为{1, 1}

  • dilate 扩张操作:控制kernel点(卷积核点)的间距,默认值为{1, 1}

  • group 控制分组卷积,默认不分组,为1组

  • pads 填充操作,默认为{0, 0}

  • relu 是否修正线性单元,默认为fasle

  • inputZeroPoint 输入变量

  • outputZeroPoint 输入变量

  • minValue 最小值

  • maxValue 最大值

  • accumulateToInt16 输入变量

返回:卷积


_Conv

MNN_PUBLIC VARP _Conv(std::vector<int8_t>&& weight, std::vector<float>&& bias, std::vector<float>&& weightScale,
                      VARP x, INTS channel, INTS kernelSize,
                      PaddingMode pad, INTS stride, INTS dilate, int group, INTS pads, bool relu,
                      float scaleIn, float scaleOut,
                      int8_t inputZeroPoint, int8_t outputZeroPoint,
                      int8_t minValue, int8_t maxValue, float weightClampValue, bool accumulateToInt16);

对由多个输入平面组成的输入信号进行卷积

参数:

  • weight 卷积产生的通道数

  • bias 偏置项行向量,在输出中添加一个可学习的偏差

  • weightScale 卷积产生的通道数的缩放因子

  • x 输入变量

  • channel 渠道

  • kernelSize 卷积核大小

  • pad 填充模式,默认为VALID

  • stride 卷积步长,默认为{1, 1}

  • dilate 扩张操作:控制kernel点(卷积核点)的间距,默认值为{1, 1}

  • group 控制分组卷积,默认不分组,为1组

  • pads 填充操作,默认为{0, 0}

  • relu 是否修正线性单元,默认为fasle

  • scaleIn 向内扩展值

  • scaleOut 向外扩展值

  • inputZeroPoint 输入变量

  • outputZeroPoint 输入变量

  • minValue 最小值

  • maxValue 最大值

  • weightClampValue 输入变量

  • accumulateToInt16 输入变量

返回:卷积


_CosineSimilarity

MNN_PUBLIC VARP _CosineSimilarity(VARP input0, VARP input1, VARP inputDim);

余弦相似度,又称为余弦相似性,是通过测量两个向量的夹角的余弦值来度量它们之间的相似性

参数:

  • input0 输入变量

  • input1 输入变量

  • inputDim表示对应行或者列的向量之间进行cos相似度计算

返回:和input0类型一致的数据


_GridSample

MNN_PUBLIC VARP _GridSample(VARP input, VARP grid, InterpolationMethod mode=BILINEAR, GridSamplePaddingMode paddingMode=GRID_SAMPLE_PADDING_ZEROS, bool alignCorners=false);

提供一个input的Tensor以及一个对应的flow-field网格(比如光流,体素流等),然后根据grid中每个位置提供的坐标信息(这里指input中pixel的坐标),将input中对应位置的像素值填充到grid指定的位置,得到最终的输出。

参数:

  • input 输入数据

  • grid flow-field网格

  • mode 定义了在input中指定位置的pixel value中进行插值的方法,默认为BILINEAR

  • paddingMode 对于越界的位置在网格中采用填充方式,默认为GRID_SAMPLE_PADDING_ZEROS

  • alignCorners 默认为false

返回:网格数据


_FloatToInt8

MNN_PUBLIC VARP _FloatToInt8(VARP x, VARP scale, char minValue, char maxValue);

float类型转换为Int8类型

参数:

  • x 输入变量

  • scale 比例因子

  • minValue 最小值

  • maxValue 最大值

返回:Int8类型数据


_FloatToInt8

MNN_PUBLIC VARP _FloatToInt8(VARP x, VARP scale, int8_t minValue, int8_t maxValue, int8_t zeroPoint);

float类型转换为Int8类型

参数:

  • x 输入变量

  • scale 比例因子

  • minValue 最小值

  • maxValue 最大值

  • zeroPoint 原点

返回:Int8类型数据


_Int8ToFloat

MNN_PUBLIC VARP _Int8ToFloat(VARP x, VARP scale);

Int8转换为float类型

参数:

  • x 输入变量

  • scale 比例因子

返回:float类型数据


_Int8ToFloat

MNN_PUBLIC VARP _Int8ToFloat(VARP x, VARP scale, int8_t zeroPoint);

Int8转换为float类型

参数:

  • x 输入变量

  • scale 比例因子

  • zeroPoint 原点

返回:float类型数据


_Select

MNN_PUBLIC VARP _Select(VARP select, VARP input0, VARP input1);

返回根据’cond’从’x’或’y’中选择的元素

参数:

  • select 输入变量

  • input0 输入变量

  • input1 输入变量

返回:根据条件选中的元素


_TopKV2

MNN_PUBLIC std::vector<VARP> _TopKV2(VARP input0, VARP input1);

查找排序后的数据的值和索引。

参数:

  • input0 输入变量

  • input1 输入变量

返回:值和索引


_ImageProcess

MNN_PUBLIC VARP _ImageProcess(VARP input, CV::ImageProcess::Config config, CV::Matrix matrix, int oh, int ow, int oc, int dtype, uint8_t padVal = 0);

图像处理

参数:

  • input 输入变量

  • config 配置信息

  • matrix 输出矩阵

  • oh 图像高

  • ow 图像宽

  • oc 卷积计算类型

  • dtype 返回数据类型

  • padVal 默认为0

返回:图像


_Where

MNN_PUBLIC VARP _Where(VARP x);

返回满足条件x > 0的索引

参数:

  • x 输入变量

返回:索引


_Sort

MNN_PUBLIC VARP _Sort(VARP x, int axis = -1, bool arg = false, bool descend = false);

排序

参数:

  • x 输入变量

  • axis 输入变量,int类型,操作的坐标轴,默认为-1

  • arg 是否返回排序元素的index, 默认为false

  • descend true代表倒序,false代表正序,默认为false

返回:排序结果


_Raster

MNN_PUBLIC VARP _Raster(const std::vector<VARP>& vars, const std::vector<int>& regions, const std::vector<int>& shape);

光栅化

参数:

  • vars 输入变量

  • regions 区域

  • shape 输出形状

返回:光栅化的值


_Nms

MNN_PUBLIC VARP _Nms(VARP boxes, VARP scores, int maxDetections, float iouThreshold = -1, float scoreThreshold = -1);

非极大值抑制算法,搜索局部极大值,抑制非极大值元素

参数:

  • boxes 形状必须为[num, 4]

  • scores float类型的大小为[num_boxes]代表上面boxes的每一行,对应的每一个box的一个score

  • maxDetections 一个整数张量,代表最多可以利用NMS选中多少个边框

  • iouThreshold IOU阙值展示的是否与选中的那个边框具有较大的重叠度,默认为-1

  • scoreThreshold 默认为-1,来决定什么时候删除这个边框

返回:搜索局部极大值,抑制非极大值元素


_Im2Col

MNN_PUBLIC VARP _Im2Col(VARP x, INTS kernelSize, INTS dilate, INTS pads, INTS stride);

我们沿着原始矩阵逐行计算,将得到的新的子矩阵展开成列,放置在列块矩阵中

参数:

  • x 输入变量

  • kernelSize 内核大小

  • dilate 扩张操作:控制kernel点的间距

  • pads 填充操作

  • stride 步长

返回:列块矩阵


_Col2Im

MNN_PUBLIC VARP _Col2Im(VARP x, VARP outputShape, INTS kernelSize, INTS dilate, INTS pads, INTS stride);

我们沿着列块矩阵逐行计算,将得到的行展成子矩阵,然后将子矩阵放置在最终结果对应的位置(每次当前值进行相加),同时记录每个位置的值放置的次数。最后,将当前位置的值除以放置的次数,即可得到结果(原始矩阵)

参数:

  • x 输入变量

  • outputShape 输出形状

  • kernelSize 内核大小

  • dilate 扩张操作:控制kernel点的间距

  • pads 填充操作

  • stride 步长

返回:原始矩阵

MNN

module MNN

MNN是Pymnn中最基础的Module,其中包含了V2 API所需要数据结构与一些枚举类型;同时包含了一些基础函数。 其他子模块则也需要通过MNN模块引入,引入方式为import MNN.{module} as {module}


MNN submodules


MNN Types


version()

获取当前MNN的版本号

参数:

  • None

返回:版本号

返回类型:str


get_model_uuid(model_path)

获取指定模型的uuid信息

注意:该API仅在打开PYMNN_TRAIN_API时有效,移动端默认关闭

参数:

  • model_path:str 模型路径

返回:uuid信息

返回类型:str

expr

module expr

expr是MNN的表达式模块,包含了一系列的表达式函数能够构造MNN中所有的算子,并提供了类型Var用来描述数据与表达式。

通过表达式函数和Var变量的组合,可以实现以下功能:

  • 模型推理

  • 数值计算

  • 模型构造


expr Types

  • Var

  • var_likeVar或者可以转换为Var的数据,如:listtuplescalar


const(value_list, shape, data_format, dtype)

根据输入数据创建一个Const类型的Var;该函数是创建的Var的最基本函数, 能够将listtuplebytesndarrayPyCapsuleint指针等格式的数据转换成Var

注意:value_list仅在PYMNN_NUMPY_USABLE打开的情况下支持ndarray,移动端默认关闭

参数:

  • value_list:ndarray/list/tuple/bytes/PyCapsule/int_addr 输入数据

  • shape:[int] 构造Var的形状

  • data_format:data_format 数据排布格式,参考data_format

  • dtype:dtype 数据类型,参考dtype

返回:创建的Var

返回类型:Var

示例:

>>> import numpy as np
>>> expr.const(np.arange(4.0).astype(np.float32), [1, 4], expr.NCHW, expr.float) # ndarray
array([[0., 1., 2., 3.]], dtype=float32)
>>> expr.const([2, 3, 4], [3], expr.NCHW, expr.int) # list/tuple
array([2, 3, 4], dtype=int32)
>>> expr.const(bytes('abc', encoding='utf8'), [3], expr.NCHW, expr.uint8) # bytes
array([97, 98, 99], dtype=uint8)
>>> expr.const(MNN.Tensor([2, 3]).getData(), [2], expr.NCHW, expr.int) # PyCapsule
array([2, 3], dtype=int32)
>>> expr.const(np.arange(4.0).astype(np.float32).__array_interface__['data'][0], [4], expr.NCHW, expr.float) # int_addr 该方法要求ndarray内存必须连续
array([0., 1., 2., 3.], dtype=float32)

set_thread_number(numberThread)

设置表达式求值的线程数

参数:

  • numberThread:int 线程数,当numberThread < 1时,设置numberThread=1,当numberThread > 8时,设置numberThread=8

返回:None

返回类型:None

示例:

>>> expr.set_thread_number(4)

load_as_list(fileName)

从文件中加载模型,并将模型转换为计算图,以list的形式返回计算图的所有Var节点

参数:

  • fileName:str 模型文件路径

返回:加载的模型计算图

返回类型:[Var]

示例:

>>> len(expr.load_as_list('mobilenet_v1.mnn'))
31

save(vars, fileName, |forInference)

list形式的计算图存储为模型文件,此函数也用于将Var保存到磁盘中

参数:

  • vars:list 计算图的list形式

  • fileName:str 模型文件路径

  • forInference:bool 是否仅保存为推理使用,默认为True

返回:存储计算图到模型文件中

返回类型:None

示例:

>>> x = expr.const([1., 2., 3., 4.], [2, 2])
>>> expr.save([x], 'x.mnn')
>>> expr.load_as_list('x.mnn')
[array([[1., 2.],
        [3., 4.]], dtype=float32)]

gc(full)

手动回收内存,当在循环中调用MNN表达式求值时,常量部分数据不会在每次循环结束释放,当执行次数增加时会有内存增长现象,可以在每次循环结束时调用该函数回收常量内存

参数:

  • full:bool 是否全部回收,目前回收方式TrueFalse没有区别

返回:None

返回类型:None

示例:

>>> expr.gc(1)

lazy_eval(lazy)

是否开启惰性求值,在Python中默认关闭;主要区别如下:

模式 开启惰性求值 关闭惰性求值
构建区别 创建的Var时实际表达式类型 创建的Var都是Const类型
计算方式 Var执行read操作时触发计算 构建表达式时会立即执行计算
应用场景 使用表达式构建模型,训练,需要对表达式进行修改 数值计算,关闭后会加速和降低内存占用

参数:

  • lazy:bool 是否开启惰性求值

返回:None

返回类型:None

示例:

>>> expr.placeholder().op_type
'Const'
>>> expr.lazy_eval(True)
>>> expr.placeholder().op_type
'Input'

sign(x)

返回输入值的符号,正数返回1,负数返回-1

参数:

  • x:Var_like 输入变量

返回:x的符号,1-1

返回类型:Var

示例:

>>> expr.sign([-5., 4.5])
    array([-1., 1.])

abs(x)

返回输入值的绝对值,正数返回原值,负数返回相反数

参数:

  • x:Var_like 输入变量

返回:x的绝对值

返回类型:Var

示例:

>>> expr.abs([-5., 4.5])
    array([5., 4.5])

negative(x)

返回输入值的相反数

参数:

  • x:Var_like 输入变量

返回:x的相反数

返回类型:Var

示例:

>>> expr.abs([-5., 4.5])
    array([5., -4.5])

floor(x)

返回不大于输入值的最大整数

参数:

  • x:Var_like 输入变量

返回:不大于x的最大整数

返回类型:Var

示例:

>>> expr.floor([-5.1, 4.5])
array([-6.,  4.])

round(x)

返回输入值的四舍五入的值

参数:

  • x:Var_like 输入变量

返回:x的四舍五入的值

返回类型:Var

示例:

>>> expr.round([-5.1, 4.5])
array([-5.,  5.])

ceil(x)

返回输入值的整数部分的值

参数:

  • x:Var_like 输入变量

返回:x的整数部分的值

返回类型:Var

示例:

>>> expr.ceil([-4.9, 4.5])
array([-4.,  5.])

square(x)

返回输入值的平方值

参数:

  • x:Var_like 输入变量

返回:x的平方值

返回类型:Var

示例:

 >>> expr.square([-5., 4.5])
array([25., 20.25])

sqrt(x)

返回输入值的平方根的值,输入值为非负实数

参数:

  • x:Var_like 输入变量

返回:x的平方根的值

返回类型:Var

示例:

>>> expr.sqrt([9., 4.5])
array([3., 2.1213202])

rsqrt(x)

返回输入值的平方根的倒数的值,输入值为非负实数

参数:

  • x:Var_like 输入变量

返回:x的平方根的倒数的值

返回类型:Var

示例:

>>> expr.rsqrt([9., 4.5])
array([0.33333334, 0.47140455])

exp(x)

返回自然常数e的输入值(x)次方的值

参数:

  • x:Var_like 输入变量

返回:自然常数e的x次方的值

返回类型:Var

示例:

>>> expr.exp([9., 4.5])
array([8102.449, 90.01698])

log(x)

返回输入值以自然常数e为底的对数值

参数:

  • x:Var_like 输入变量

返回:x的以自然常数e为底的对数值

返回类型:Var

示例:

>>> expr.log([9., 4.5])
array([2.1972246, 1.5040774])

sin(x)

返回输入值的正弦值

参数:

  • x:Var_like 输入变量

返回:x的正弦值

返回类型:Var

示例:

>>> expr.sin([9., 4.5])
array([0.4121185, -0.9775301])

sinh(x)

返回输入值的双曲正弦值 相当于 1/2 * (np.exp(x) - np.exp(-x)) 或者 -1j * np.sin(1j*x)

参数:

  • x:Var_like 输入变量

返回:x的双曲正弦值

返回类型:Var

示例:

>>> expr.sinh([9., 4.5])
array([4051.542, 45.00301])

cos(x)

返回输入值的余弦值

参数:

  • x:Var_like 输入变量

返回:x的余弦值

返回类型:Var

示例:

>>> expr.cos([9., 4.5])
array([-0.91113025, -0.2107958])

cosh(x)

返回输入值的双曲余弦值 相当于 1/2 * (np.exp(x) + np.exp(-x)) 或者 np.cos(1j*x)

参数:

  • x:Var_like 输入变量

返回:x的双曲余弦值

返回类型:Var

示例:

>>> expr.cosh([9., 4.5])
array([4051.542, 45.014122])

tan(x)

返回输入值的正切值

参数:

  • x:Var_like 输入变量

返回:x的正切值

返回类型:Var

示例:

>>> expr.tan([9., 4.5])
array([-0.45231566, 4.637332])

tanh(x)

返回输入值的双曲正切值 相当于 np.sinh(x)/np.cosh(x) 或者 -1j * np.tan(1j*x)

参数:

  • x:Var_like 输入变量

返回:x的双曲正切值

返回类型:Var

示例:

>>> expr.tanh([9., 4.5])
array([1., 0.9997533])

asin(x)

返回输入值的反正弦值,别名arcsin

参数:

  • x:Var_like 输入变量

返回:x的反正弦值

返回类型:Var

示例:

>>> expr.asin([9., 0.5])
array([nan, 0.5235988])

asinh(x)

返回输入值的双曲反正弦值,别名arcsinh

参数:

  • x:Var_like 输入变量

返回:x的双曲反正弦值

返回类型:Var

示例:

>>> expr.asinh([9., 0.5])
array([2.893444, 0.4812118])

acos(x)

返回输入值的反余弦值,别名arccos

参数:

  • x:Var_like 输入变量

返回:x的反余弦值

返回类型:Var

示例:

>>> expr.asin([9., 0.5])
array([nan, 1.0471975])

acosh(x)

返回输入值的双曲反余弦值,别名arccosh

参数:

  • x:Var_like 输入变量

返回:x的双曲反余弦值

返回类型:Var

示例:

>>> expr.acosh([9., 0.5])
array([2.887271, nan])

atan(x)

返回输入值的反正切值,别名arctan

参数:

  • x:Var_like 输入变量

返回:x的反正切值

返回类型:Var

示例:

>>> expr.atan([9., 0.5])
array([1.4601392, 0.4636476])

atanh(x)

返回输入值的双曲反正切值,别名arctanh

参数:

  • x:Var_like 输入变量

返回:x的双曲反正切值

返回类型:Var

示例:

>>> expr.atanh([9., 0.5])
array([1.4601392, 0.4636476])

reciprocal(x)

返回输入值的倒数,输入值不能为0

参数:

  • x:Var_like 输入变量

返回:x的倒数

返回类型:Var

示例:

>>> expr.reciprocal([9., 0.5])
array([0.11111111, 2.])

log1p(x)

返回log(1 + x)的值

参数:

  • x:Var_like 输入变量

返回:log(1 + x)的值

返回类型:Var

示例:

>>> expr.log1p([9., 0.5])
array([2.3025851, 0.4054651])

gelu(x)

返回 0.5x(1+tanh(sqrt(pi/2)(0.044715x^3))) 的值

参数:

  • x:Var_like 输入变量

返回:0.5x(1+tanh(sqrt(pi/2)(0.044715x^3))) 的值

返回类型:Var

示例:

>>> expr.gelu([9., 0.5])
array([9., 0.345714])

sigmoid(x)

返回 1/(1+exp(-1) 的值

参数:

  • x:Var_like 输入变量

返回:1/(1+exp(-1)的值

返回类型:Var

示例:

>>> expr.sigmoid([9., 0.5])
array([0.9998766, 0.62246716])

erf(x)

计算高斯误差函数值的数据输入

参数:

  • x:Var_like 输入变量

返回:高斯误差函数值的数据

返回类型:Var

示例:

>>> expr.erf([[1., 25., 16., 0.]])
array([0.8427007., 1., 1., 0.])

erfc(x)

返回 x 的互补误差

参数:

  • x:Var_like 输入变量

返回: x的互补误差

返回类型:Var

示例:

>>> expr.erfc([0.67., 1.34., -6.])
array([0.34337229769969496., 0.05808628474163466., 2.0.])

erfinv(x)

返回x的逆误差

参数:

  • x:Var_like 输入变量

返回:x的逆误差

返回类型:Var

示例:

>>> expr.erfinv([0.1., 0.2])
array([0.08885599., 0.17914345.])

expm1(x)

返回 exp(x) - 1 的值

参数:

  • x:Var_like 输入变量

返回:exp(x) - 1的值

返回类型:Var

示例:

>>> expr.expm1([9., 0.5])
array([8.1014492e+03, 6.4869785e-01])

add(x, y)

返回两个输入数的和

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:x+y的值

返回类型:Var

示例:

>>> expr.add([9., 0.5], [1.2, -3.0])
array([10.2, -2.5])

subtract(x, y)

返回两个输入数的差值

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:x - y 的值

返回类型:Var

示例:

>>> expr.subtract([9., 0.5], [1.2, -3.0])
array([7.8, 3.5])

multiply(x, y)

返回两个输入数的乘积

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:x * y 的值

返回类型:Var

示例:

>>> expr.multiply([9., 0.5], [1.2, -3.0])
array([10.8, -1.5])

divide(x, y)

返回两个输入数相除的值,y值不能为0

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:x / y 的值

返回类型:Var

示例:

>>> expr.divide([9., 0.5], [1.2, -3.0])
array([7.4999995, -0.16666667])

floordiv(x, y)

返回小于或等于代数商的最大(最接近正无穷大)int值

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:x // y 的值

返回类型:Var

示例:

>>> expr.floordiv([9., 0.5], [1.2, -3.0])
array([7., -1.])

mod(x, y)

返回两个输入数求余的值,y不能为0,取余运算在计算商值向0方向舍弃小数位

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:x % y 的值

返回类型:Var

示例:

>>> expr.mod([9., 0.5], [1.2, -3.0])
array([0.59999967, 0.5])

floormod(x, y)

返回两个输入数取模的值,y不能为0,取模运算在计算商值向负无穷方向舍弃小数位

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:(1)与y符号相同 (2)x > y:结果的绝对值与 % 运算相同; (3)x < y:①符号相同 结果的绝对值为 y – x ;②符号不同 结果的绝对值与 % 运算相同

返回类型:Var

示例:

>>> expr.floormod([9., 0.5], [1.2, -3.0])
array([0.5999994, -2.5])

pow(x, y)

返回x的y次方的值

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:x的y次方的值

返回类型:Var

示例:

>>> expr.pow([9., 0.5], [1.2, -3.0])
array([13.966612, 8.])

minimum(x, y)

返回输入数中的最小值

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:x和y中的最小值

返回类型:Var

示例:

>>> expr.minimum([9., 0.5], [1.2, -3.0])
array([1.2, -3.])

maximum(x, y)

返回输入数中的最大值

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:x和y中的最大值

返回类型:Var

示例:

>>> expr.maximum([9., 0.5], [1.2, -3.0])
array([9., 0.5])

equal(x, y)

判断输入数是否相等,相等返回1,不等返回0

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:1或0

返回类型:Var

示例:

>>> expr.equal([-9., 0.5], [1.2, 0.5])
array([0, 1])

not_equal(x, y)

判断输入数是否相等,相等返回0,不等返回1

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:1或0

返回类型:Var

示例:

>>> expr.not_equal([-9., 0.5], [1.2, 0.5])
array([1, 0])

greater(x, y)

判断输入数x和y的大小,如果x > y返回1,否者返回0

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:1或0

返回类型:Var

示例:

>>> expr.greater([-9., 0.5], [1.2, -3.0])
array([0, 1])

greater_equal(x, y)

判断输入数x和y的大小,如果x >= y返回1,否者返回0

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:1或0

返回类型:Var

示例:

>>> expr.greater_equal([-9., 0.5], [1.2, -3.0])
array([0, 1])

less(x, y)

判断输入数x和y的大小,如果x < y返回1,否者返回0

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:1或0

返回类型:Var

示例:

>>> expr.less([-9., 0.5], [1.2, -3.0])
array([1, 0])

less_equal(x, y)

判断输入数x和y的大小,如果x <= y返回1,否者返回0

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:1或0

返回类型:Var

示例:

>>> expr.less_equal([-9., 0.5], [1.2, -3.0])
array([1, 0])

squared_difference(x, y)

返回输入数x和输入数y的差的平方值

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:(x - y)^2

返回类型:Var

示例:

>>> expr.squared_difference([-9., 0.5], [1.2, -3.0])
array([104.03999, 12.25])

atan2(x, y)

返回 x / y 的反正切值,别名arctan2

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

返回:arctan(x / y)的值

返回类型:Var

示例:

>>> expr.atan2([-9., 0.5], [1.2, -3.0])
array([-1.4382448, -0.16514869])

logical_or(x, y)

返回输入数的“或”逻辑的值,x和y形状相同,返回1,否则返回0

参数:

  • x:Var_like 输入变量,仅支持int32

  • y:Var_like 输入变量,仅支持int32

返回:1或0

返回类型:Var

示例:

>>> expr.logical_or([2, 1], [4, 2])
array([1, 1])

bias_add(value, bias)

这(主要)是加法的一个特殊情况,其中偏差限制在1-D,value可以有任意数量的维度,与加法不同,在量子化的情况下,偏差的类型允许与值不同。

参数:

  • value 输入变量,dtype.float或者dtype.int类型

  • bias 输入变量,一个一维变量,其大小与值的通道维度相匹配,必须与值的类型相同,除非值是量化类型,在这种情况下可以使用不同的量化类型。

返回:与value类型相同的变量

返回类型:Var

示例:

 >>> expr.bias_add(np.eye(3,3), np.ones(3))
array([[2., 1., 1.],
       [1., 2., 1.],
       [1., 1., 2.]], dtype=float32)

bitwise_and(x, y)

返回输入数的“与”运算的值(x & y,x, y必须为dtype.int32

参数:

  • x:Var_like 输入变量,仅支持int32

  • y:Var_like 输入变量,仅支持int32

返回:1或0

返回类型:Var

示例:

>>> expr.bitwise_and([1, 2], [3, 4])
array([1, 0])

bitwise_or(x, y)

返回输入数的“或”运算的值(x | y),x, y必须为dtype.int32

参数:

  • x:Var_like 输入变量,仅支持int32

  • y:Var_like 输入变量,仅支持int32

返回:x | y

返回类型:Var

示例:

>>> expr.bitwise_or([1, 2], [3, 4])
array([3, 6])

bitwise_xor(x, y)

返回输入数的“异或”运算的值(x ^ y),x, y必须为dtype.int32

参数:

  • x:Var_like 输入变量,仅支持int32

  • y:Var_like 输入变量,仅支持int32

返回:x ^ y

返回类型:Var

示例:

>>> expr.bitwise_xor([1, 2], [3, 4])
array([2, 6])

reduce_sum(x, axis=[], keepdims=False)

计算张量x沿着指定的数轴(x的某一维度)上的和

参数:

  • x:Var_like 输入变量

  • axis : axis_like 仅支持int32,默认为[],指定按哪个维度进行加和,默认将所有元素进行加和

  • keepdims : bool 默认为false,表示不维持原来张量的维度,反之维持原张量维度

返回:x沿着指定的数轴(x的某一维度)上的和

返回类型:Var

示例:

>>> expr.reduce_sum([[1.,2.],[3.,4.]])
array(10.)
>>> expr.reduce_sum([[1.,2.],[3.,4.]], 0)
array([4., 6.])

reduce_mean(x, axis=[], keepdims=False)

计算张量x沿着指定的数轴(x的某一维度)上的平均值

参数:

  • x:Var_like 输入变量

  • axis : axis_like 仅支持int32,默认为[],指定按哪个维度进行加和,默认将所有元素进行加和

  • keepdims : bool 默认为false,表示不维持原来张量的维度,反之维持原张量维度

返回:x沿着指定的数轴(x的某一维度)上的平均值

返回类型:Var

示例:

>>> expr.reduce_mean([[1.,2.],[3.,4.]])
array(2.5.)
>>> expr.reduce_mean([[1.,2.],[3.,4.]], 0)
array([2., 3.])

reduce_max(x, axis=[], keepdims=False)

计算x沿着指定的数轴(x的某一维度)上的最大值

参数:

  • x:Var_like 输入变量

  • axis : axis_like 仅支持int32,默认为[],指定按哪个维度进行加和,默认将所有元素进行加和

  • keepdims : bool 默认为false,表示不维持原来张量的维度,反之维持原张量维度

返回:x沿着指定的数轴(x的某一维度)上的最大值

返回类型:Var

示例:

>>> expr.reduce_max([[1.,2.],[3.,4.]])
array(4.)
>>> expr.reduce_max([[1.,2.],[3.,4.]], 0)
array([3., 4.])

reduce_min(x, axis=[], keepdims=False)

计算x沿着指定的数轴(x的某一维度)上的最小值

参数:

  • x:Var_like 输入变量

  • axis : axis_like 仅支持int32,默认为[],指定按哪个维度进行加和,默认将所有元素进行加和

  • keepdims : bool 默认为false,表示不维持原来张量的维度,反之维持原张量维度

返回:x沿着指定的数轴(x的某一维度)上的最小值

返回类型:Var

示例:

>>> expr.reduce_min([[1.,2.],[3.,4.]])
array(1.)
>>> expr.reduce_min([[1.,2.],[3.,4.]], 0)
array([1., 2.])

reduce_prod(x, axis=[], keepdims=False)

计算x沿着指定的数轴(x的某一维度)上的乘积

参数:

  • x:Var_like 输入变量

  • axis : axis_like 仅支持int32,默认为[],指定按哪个维度进行加和,默认将所有元素进行加和

  • keepdims : bool 默认为false,表示不维持原来张量的维度,反之维持原张量维度

返回:x沿着指定的数轴(x的某一维度)上的乘积

返回类型:Var

示例:

>>> expr.reduce_prod([[1.,2.],[3.,4.]])
array(24.)
>>> expr.reduce_prod([[1.,2.],[3.,4.]], 0)
array([3., 8.])

reduce_any(x, axis=[], keepdims=False)

计算x沿着指定的数轴(x的某一维度)上的“逻辑或”的值

参数:

  • x:Var_like 输入变量

  • axis : axis_like 仅支持int32,默认为[],指定按哪个维度进行加和,默认将所有元素进行加和

  • keepdims : bool 默认为false,表示不维持原来张量的维度,反之维持原张量维度

返回:x沿着指定的数轴(x的某一维度)上的“逻辑或”的值

返回类型:Var

示例:

>>> expr.reduce_any([[0,1],[0,3]])
array(1)
>>> expr.reduce_any([[0,1],[0,3]], 1)
array([0, 1])

reduce_all(x, axis=[], keepdims=False)

计算x沿着指定的数轴(x的某一维度)上的“逻辑和”的值

参数:

  • x:Var_like 输入变量

  • axis : axis_like 仅支持int32,默认为[],指定按哪个维度进行加和,默认将所有元素进行加和

  • keepdims : bool 默认为false,表示不维持原来张量的维度,反之维持原张量维度

返回:x沿着指定的数轴(x的某一维度)上的“逻辑和”的值

返回类型:Var

示例:

>>> expr.reduce_all([[0,1],[0,3]])
array(0)
>>> expr.reduce_all([[0,1],[0,3]], 0)
array([0, 1])

eltwise_prod(x, y, coeff)

逐元素对输入的变量执行乘法运算

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

  • coeff:[float] 系数,目前仅支持[1.,0.][]/[0.]

返回:x*y, 当coeff=[1.,0.]时返回x

返回类型:Var

示例:

>>> expr.eltwise_prod([1., 2., 3.], [2., 2., 2.], [])
array([2., 4., 6.], dtype=float32)
>>> expr.eltwise_prod([1., 2., 3.], [2., 2., 2.], [1., 0.])
array([1., 2., 3.], dtype=float32)

eltwise_sum(x, y, coeff)

逐元素对输入的变量执行加法运算

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

  • coeff:[float] 系数,目前仅支持[1.,0.][]/[0.]

返回:x+y, 当coeff=[1.,0.]时返回x

返回类型:Var

示例:

>>> expr.eltwise_sum([1., 2., 3.], [2., 2., 2.], [])
array([3., 4., 5.], dtype=float32)
>>> expr.eltwise_sum([1., 2., 3.], [2., 2., 2.], [1., 0.])
array([1., 2., 3.], dtype=float32)

eltwise_sub(x, y, coeff)

逐元素对输入的变量执行减法运算

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

  • coeff:[float] 系数,目前仅支持[1.,0.][]/[0.]

返回:x-y, 当coeff=[1.,0.]时返回x

返回类型:Var

示例:

>>> expr.eltwise_sub([1., 2., 3.], [2., 2., 2.], [])
array([-1.,  0.,  1.], dtype=float32)
>>> expr.eltwise_sub([1., 2., 3.], [2., 2., 2.], [1., 0.])
array([1., 2., 3.], dtype=float32)

eltwise_max(x, y, coeff)

逐元素对输入的变量执行比较运算,取最大值

参数:

  • x:Var_like 输入变量

  • y:Var_like 输入变量

  • coeff:[float] 系数,目前仅支持[1.,0.][]/[0.]

返回:max(x,y), 当coeff=[1.,0.]时返回x

返回类型:Var

示例:

>>> expr.eltwise_max([1., 2., 3.], [2., 2., 2.], [])
array([2., 2., 3.], dtype=float32)
>>> expr.eltwise_max([1., 2., 3.], [2., 2., 2.], [1., 0.])
array([1., 2., 3.], dtype=float32)

cast(x, dtype=_F.float)

返回输入数的dtype

参数:

  • x:Var_like 输入变量

  • dtype 默认是float类型

返回:x的dtype

返回类型:Var

示例:

>>> expr.cast([[0,1],[0,3]], float)
array([[0., 1.],
       [0., 3.]], dtype=float32)

matmul(a, b, transposeA=False, transposeB=False)

返回两个数组的矩阵乘积

参数:

  • a:Var_like 输入变量

  • b:Var_like 输入变量

  • transposeA 布尔值,用于判定a是否为转置矩阵,默认为false

  • transposeB 布尔值,用于判定b是否为转置矩阵,默认为false

返回:a @ b 的值,dtype是float32

返回类型:Var

示例:

>>> expr.matmul([[1,2],[3,4]], [[1,1],[2,2]])
array([[0., 1.],
       [0., 3.]], dtype=float32)

normalize(x, acrossSpatial, channelShared, eps, scale)

返回x数据转换成指定的标准化的格式

参数:

  • a:Var_like 输入变量

  • acrossSpatial 输入变量,int类型

  • channelShared 输入变量,int类型

  • eps 输入变量,float类型,data_format

  • scale 输入变量,[float]

返回:x数据转换成指定的标准化的格式

返回类型:Var

示例:

>>> x = expr.const([-1.0, -2.0, 3.0, 4.0], [1, 2, 2, 1], expr.NCHW)
>>> expr.normalize(x, 0, 0, 0.0, [0.5, 0.5])
array([[[[-0.2236068],
         [-0.4472136]],
        [[ 0.3      ],
         [ 0.4      ]]]], dtype=float32)

argmax(x, axis=0)

返回数据最大值的索引,根据axis判断返回的是行还是列的最大值索引

参数:

  • x:Var_like 输入变量

  • axis 输入变量,int类型,默认为0,当值为0时则按照每一列操作,为1时按照每一行操作

返回:x最大值的索引

返回类型:Var

示例:

>>> expr.argmax([[1,2],[3,4]])
array([1, 1], dtype=int32)

argmin(x, axis=0)

返回数据最小值的索引,根据axis判断返回的是行还是列的最小值索引

参数:

  • x:Var_like 输入变量

  • axis 输入变量,int类型,默认为0,0代表列,1代表行

返回:x最小值的索引

返回类型:Var

示例:

>>> expr.argmax([[1,2],[3,4]])
array([1, 1], dtype=int32)

cumsum(x, axis=0)

返回给定axis上的累计和,如果axis == 0,行一不变,行二累加,以此类推;axis == 1,列一不变,列二累加,以此类推

参数:

  • x:Var_like 输入变量

  • axis 输入变量,int类型,默认为0,0代表列,1代表行

返回:x在给定axis上的累计和

返回类型:Var

示例:

>>> expr.cumsum([[1,2],[3,4]])
array([[1, 2],
       [4, 6]], dtype=int32)

cumprod(x, axis=0)

返回给定axis上的累计乘积,如果axis == 0,行一不变,行二累计相乘,以此类推;axis == 1,列一不变,列二累计相乘,以此类推

参数:

  • x:Var_like 输入变量

  • axis 输入变量,int类型,默认为0,0代表列,1代表行

返回:x在给定axis上的累计乘积

返回类型:Var

示例:

>>> expr.cumprod([[1.,2.],[3.,4.]])
array([[1., 2.],
       [3., 8.]], dtype=float32)

svd(x)

返回’x’的svd矩阵,’x’是一个2D矩阵。 返回’w’,’u’,’vt’为 a = u @ w @ vt。

参数:

  • x:Var_like 输入变量

返回:w : 形状是 (N). u : 形状是 (M, N). vt : 形状是 (N, N).

返回类型:Var

示例:

>>> expr.cumprod([[1.,2.],[3.,4.]])
[array([5.464986  , 0.36596605], dtype=float32), array([[ 0.40455356,  0.91451436],
       [ 0.91451424, -0.40455365]], dtype=float32), array([[ 0.5760485 ,  0.81741554],
       [-0.81741554,  0.5760485 ]], dtype=float32)]

unravel_index(indices, shape)

求出数组某元素(或某组元素)拉成一维后的索引值在原本维度(或指定新维度)中对应的索引

参数:

  • indices:Var_like 输入变量,int32构成的数组, 其中元素是索引值

  • shape:Var_like 输入变量,一般是原本数组的维度,也可以给定的新维度

返回:索引

返回类型:Var

示例:

>>> expr.unravel_index([22, 41, 37], (7,6))
array([[3, 6, 6],
       [4, 5, 1]], dtype=int32)

scatter_nd(indices, updates, shape)

根据indices将updates散布到新的(初始为零)张量

参数:

  • indices:Var_like 输入变量,指数张量

  • updates:Var_like 输入变量,分散到输出的更新

  • shape:Var_like 输入变量,得到的张量的形状

返回:(初始为零)张量

返回类型:Var

示例:

>>> indices = expr.const([4, 3, 1, 7], [4, 1], expr.NHWC, expr.int)
>>> expr.scatter_nd(indices, [9.0, 10.0, 11.0, 12.0], [8])
array([ 0., 11.,  0., 10.,  9.,  0.,  0., 12.], dtype=float32)

one_hot(indices, depth, onValue=1.0, offValue=0.0, axis=-1)

转换成one_hot类型的张量输出

参数:

  • indices: Var_like 输入变量,指示on_value的位置,不指示的地方都为off_value。indices可以是向量、矩阵。

  • depth: int 输入变量,输出张量的尺寸,indices中元素默认不超过(depth-1),如果超过,输出为[0,0,···,0]

  • onValue: float 输入变量,定义在indices[j] = i 时填充输出值的标量,默认为1

  • offValue: float 输入变量,定义在indices[j] != i 时填充输出值的标量,默认为0

  • axis: int 输入变量,要填充的轴,默认为-1,即一个新的最内层轴

返回:one_hot类型的张量输出

返回类型:Var

示例:

>>> expr.one_hot([0, 1, 2], 3)
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]], dtype=float32)

broadcast_to(input, shape)

利用广播将原始矩阵成倍增加

参数:

  • input: Var_like 输入变量,一个张量,广播的张量

  • shape: int 输入变量,一个一维的int32张量,期望输出的形状

返回:成倍增加的原始矩阵

返回类型:Var

示例:

>>> expr.broadcast_to([1,2], 4)
array([1, 2, 1571999095, -536868871], dtype=int32)

placeholder(shape=[], format=_F.NCHW, dtype=_F.float)

构建一个Var类型的占位符

参数:

  • shape: [int] 输入变量,默认是[]

  • format: data_format 输入变量,默认是NCHW

  • dtype: dtype 输入变量,默认是float

返回:Var类型的占位符,使用之前需先write

返回类型:Var

示例:

>>> expr.placeholder()
array(1., dtype=float32)
>>> expr.placeholder([2,2])
array([[1.67e-43, 1.60e-43],
       [1.36e-43, 1.54e-43]], dtype=float32)

clone(x, deepCopy=False)

克隆一个Var

参数:

  • x: Var_like 输入变量

  • deepCopy: bool 输入变量,true代表深拷贝,false代表浅拷贝,默认为false

返回:通过x拷贝一个Var

返回类型:Var

示例:

>>> x = expr.const([1.,2.], [2])
>>> expr.clone(x, True)
array([1., 2.], dtype=float32)

conv2d(input, weight, bias, stride=(1,1), padding=(0,0), dilate=(1,1), group=1, padding_mode=_F.VALID)

对由多个输入平面组成的输入信号进行二维卷积

参数:

  • input: var_like 输入变量,data_format为NC4HW4,输入图像通道数

  • weight: var_like 输入变量,卷积产生的通道数

  • bias: var_like 输入变量,在输出中添加一个可学习的偏差

  • stride: tuple of int 输入变量,卷积步长,默认为(1,1)

  • padding: tuple of int 输入变量,填充操作,控制padding_mode的数目

  • dilate: tuple of int 输入变量,扩张操作:控制kernel点(卷积核点)的间距,默认值为(1,1)

  • group: int 输入变量,控制分组卷积,默认不分组,为1组

  • padding_mode:  Padding_Mode 输入变量,填充模式,默认是VALID

返回:二维卷积

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 18., 1.), [1, 2, 3, 3])
>>> x = expr.convert(x, expr.NC4HW4)
>>> w = expr.reshape(expr.range(0., 16., 1.), [2, 2, 2, 2])
>>> b = expr.const([1.0, 1.0], [2])
>>> expr.convert(expr.conv2d(x, w, b), expr.NCHW)
array([[[[ 269.,  297.],
         [ 353.,  381.]],
        [[ 685.,  777.],
         [ 961., 1053.]]]], dtype=float32)

conv2d_transpose(input, weight, bias, stride=(1,1), padding=(0,0), dilate=(1,1), group=1, padding_mode=_F.VALID)

转置卷积,是卷积的反向操作

参数:

  • input: var_like 输入变量,需要做反卷积的输入图像

  • weight: var_like 输入变量,反卷积产生的通道数

  • bias: var_like 输入变量,在输出中添加一个可学习的偏差

  • stride: tuple of int 输入变量,反卷积步长,默认为(1,1)

  • padding: tuple of int 输入变量,填充操作,控制padding_mode的数目

  • dilate: tuple of int 输入变量,扩张操作:控制kernel点(反卷积核点)的间距,默认值为(1,1)

  • group: int 输入变量,控制分组反卷积,默认不分组,为1组

  • padding_mode:  Padding_Mode 输入变量,填充模式,默认是VALID

返回:转置卷积(反卷积)

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 18., 1.), [1, 2, 3, 3])
>>> x = expr.convert(x, expr.NC4HW4)
>>> w = expr.reshape(expr.range(0., 16., 1.), [2, 2, 2, 2])
>>> b = expr.const([1.0, 1.0], [2])
>>> expr.convert(expr.conv2d_transpose(x, w, b), expr.NCHW)
array([[[[ 73., 162., 180., 102.],
         [187., 417., 461., 259.],
         [247., 549., 593., 331.],
         [163., 358., 384., 212.]],
        [[109., 242., 276., 154.],
         [283., 625., 701., 387.],
         [391., 853., 929., 507.],
         [247., 534., 576., 312.]]]], dtype=float32)

max_pool(x, kernel, stride, padding_mode=_F.VALID, pads=(0,0))

最大值池化操作

参数:

  • x: var_like 输入变量,池化的输入

  • kernel: var_like 输入变量

  • stride: tuple of int 输入变量,窗口在每一个维度上滑动的步长

  • padding_mode:  Padding_Mode 输入变量,填充模式,默认是VALID

  • pads: tuple of int 输入变量

返回:最大池化

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 18., 1.), [1, 2, 3, 3])
>>> expr.max_pool(x, [2,2], [1,1])
array([[[[ 4.,  5.],
         [ 7.,  8.]],
        [[13., 14.],
         [16., 17.]]]], dtype=float32)

avg_pool(x, kernel, stride, padding_mode=_F.VALID, pads=(0,0))

平均池化操作

参数:

  • x: var_like 输入变量,池化的输入

  • kernel: var_like 输入变量

  • stride: tuple of int 输入变量,窗口在每一个维度上滑动的步长

  • padding_mode:  Padding_Mode 输入变量,填充模式,默认是VALID

  • pads: tuple of int 输入变量

返回:平均池化

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 18., 1.), [1, 2, 3, 3])
    >>> expr.avg_pool(x, [2,2], [1,1])
array([[[[ 2.,  3.],
         [ 5.,  6.]],
        [[11., 12.],
         [14., 15.]]]], dtype=float32)

reshape(x, shape, original_format=_F.NCHW)

改变输入数的形状

参数:

  • x: var_like 输入变量,数组或者矩阵

  • shape: axis_like 输入变量,返回数据的形状

  • original_format : data_format 输入变量,默认是NCHW

返回:输入数对应的shape

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 18., 1.), [1, 2, 3, 3])
>>> reshape(x, [3, 6])
array([[ 0.,  1.,  2.,  3.,  4.,  5.],
       [ 6.,  7.,  8.,  9., 10., 11.],
       [12., 13., 14., 15., 16., 17.]], dtype=float32)

scale(x, channels, scale, bias)

返回x * scale + bias的值

参数:

  • x: var_like 输入变量

  • channels : int 输入变量

  • scale : [float] 输入变量

  • bias : [float] 输入变量

返回:x * scale + bias的值

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 18., 1.), [1, 2, 3, 3])
>>> x = expr.convert(x, expr.NC4HW4)
>>> y = expr.scale(x, 2, [2.0, 1.0], [3.0, 4.0])
>>> expr.convert(y, expr.NCHW)
array([[[[ 3.,  5.,  7.],
           [ 9., 11., 13.],
           [15., 17., 19.]],
          [[13., 14., 15.],
           [16., 17., 18.],
           [19., 20., 21.]]]], dtype=float32)

relu(x, slope=0.0)

修正线性单元,修正公式为slope*x if x < 0 else x

参数:

  • x: var_like 输入变量

  • slope : float 输入变量,修正梯度,默认是0.0

返回:修正后的值

返回类型:Var

示例:

>>> expr.relu([-1.0, 0.0, 2.0])
    var[0., 0., 2.], dtype=float32)

relu6(x, min=0.0, max=6.0)

修正线性单元,修正公式为max(min(x, max), min)

参数:

  • x: var_like 输入变量

  • min : float 输入变量,修正最小范围,默认是0.0

  • max : float 输入变量,修正最大范围,默认是6.0

返回:修正后的值

返回类型:Var

示例:

>>> expr.relu6([-1.0, 7.0, 2.0])
    var[0., 6., 2.], dtype=float32)

prelu(x, slopes)

查找指定张量输入的泄漏校正线性,修正公式为slope*x if x < 0 else x

参数:

  • x: var_like 输入变量,data_format 是 NC4HW4.

  • slopes : [float] 输入变量,修正梯度

返回:修正后的值

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(-4., 4., 1.), [1, 2, 2, 2])
>>> x = expr.convert(x, expr.NC4HW4)
>>> expr.convert(expr.prelu(x, [0.5, 0.6]), expr.NCHW)
array([[[[-2. , -1.5],
         [-1. , -0.5]],
        [[ 0. ,  1. ],
         [ 2. ,  3. ]]]], dtype=float32)

softmax(x, axis=-1)

数学公式:exp(x)/sum(exp(x), axis)

参数:

  • x: var_like 输入变量

  • axis : int 输入变量,默认为-1

返回:修正后的值

返回类型:Var

示例:

>>> expr.softmax([[1., 2.], [3., 4.]], 0)
array([[0.1191897 , 0.1191897 ],
       [0.88081026, 0.88081026]], dtype=float32)

softplus(x)

通过输入数据 x(张量)并生成一个输出数据(张量)转换公式:log(exp(x) + 1)

参数:

  • x: var_like 输入变量

返回:输出数据(张量)

返回类型:Var

示例:

>>> expr.softplus([[1., 2.], [3., 4.]])
array([[1.313261 , 2.1268892],
       [3.048587 , 4.01813  ]], dtype=float32)

softsign(x)

返回通过转换公式:x / (abs(x) + 1)转换后的数据

参数:

  • x: var_like 输入变量

返回:转换公式x / (abs(x) + 1)转换后的数据

返回类型:Var

示例:

>>> expr.softsign([[1., 2.], [3., 4.]])
array([[0.5      , 0.6666667],
       [0.75     , 0.8      ]], dtype=float32)

slice(x, starts, sizes)

返回从张量中提取想要的切片,此操作从由starts指定位置开始的张量x中提取一个尺寸sizes的切片,切片sizes被表示为张量形状,提供公式x[starts[0]:starts[0]+sizes[0], ..., starts[-1]:starts[-1]+sizes[-1]]

参数:

  • x: var_like 输入变量

  • starts : var_like 输入变量,切片提取起始位置

  • sizes : var_like 输入变量,切片提取的尺寸

返回:切片数据

返回类型:Var

示例:

>>> expr.slice([[1., 2., 3.], [4., 5., 6.]], [0, 1], [1, 2])
array([[2., 3.]], dtype=float32)

split(x, size_splits, axis)

返回切割张量的子张量数据

参数:

  • x: var_like 输入变量

  • size_splits : var_like 输入变量,int类型,切成的份数

  • axis : int 输入变量,进行切割的维度

返回:切割张量的子张量数据

返回类型:Var

示例:

>>> expr.split([[1., 2., 3.], [4., 5., 6.]], [1, 1], 0)
    [array([[1., 2., 3.]], dtype=float32), array([[4., 5., 6.]], dtype=float32)]

strided_slice(x, begin, end, strides, begin_mask=0, end_mask=0, ellipsis_mask=0, new_axis_mask=0, shrink_axis_mask=0)

从给定的 x 张量中提取一个尺寸 (end-begin)/stride 的片段,从 begin 片段指定的位置开始,以步长 stride 添加索引,直到所有维度都不小于 end,这里的 stride 可以是负值,表示反向切片,公式:x[begin[0]:strides[0]:end[0], ..., begin[-1]:strides[-1]:end[-1]]

参数:

  • x: var_like 输入变量

  • begin : var_like 输入变量,int类型,开始切片处

  • end : var_like 输入变量,int类型,终止切片处

  • strides : var_like 输入变量,int类型,步长

  • begin_mask : int 输入变量,默认为0

  • end_mask : int 输入变量,默认为0

  • ellipsis_mask : int 输入变量,默认为0

  • new_axis_mask : int 输入变量,默认为0

  • shrink_axis_mask : int 输入变量,默认为0

返回:提取的片段

返回类型:Var

示例:

>>> expr.strided_slice([[1., 2., 3.], [4., 5., 6.]], [0, 0], [1, 2], [1, 2])
array([[1.]], dtype=float32)

concat(values, axis)

根据axis设置的维度拼接输入变量values中的数据

参数:

  • values : [var_like] 输入变量

  • axis : int 输入变量,操作的维度

返回:拼接后的数据

返回类型:Var

示例:

>>> expr.concat([[1., 2., 3.], [4., 5., 6.]], 0)
array([1., 2., 3., 4., 5., 6.], dtype=float32)

where(x)

返回满足条件x > 0的索引

参数:

  • x : [var_like] 输入变量

返回:索引,int类型

返回类型:Var

示例:

>>> expr.where([0., 1., -2., 3.3])
array([[1],[3]], dtype=int32)

convert(x, format)

将输入变量 x 的 data_format 转换为 format

参数:

  • x : [var_like] 输入变量

  • format : data_format 输入变量,[NCHW, NHWC, NC4HW4]

返回:转换后的format

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 8., 1.), [1, 2, 2, 2]) # data_format is NCHW
>>> expr.convert(x, expr.NHWC)
array([[[[0., 4.], [1., 5.]],
       [[2., 6.], [3., 7.]]]], dtype=float32)

transpose(x, perm)

返回一个转置的数据,转置的维度为perm

参数:

  • x : [var_like] 输入变量

  • perm : [int] or var_like 新的维度序列

返回:通过perm转置的数据

返回类型:Var

示例:

>>> expr.transpose([[1.,2.,3.],[4.,5.,6.]], [1,0])
array([[1., 4.],
       [2., 5.],
       [3., 6.]], dtype=float32)

channel_shuffle(x, axis)

做以下操作: x = _Convert(x, NHWC); x = _Reshape(x, {0, 0, 0, group, -1}, NHWC); x = _Transpose(x, {0, 1, 2, 4, 3}); x = _Reshape(x, {0, 0, 0, -1}, NHWC); channel_shuffle_res = _Convert(x, NC4HW4);

参数:

  • x : [var_like] 输入变量

  • axis : int 输入变量

返回:

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 8., 1.), [1, 4, 1, 2])
>>> expr.convert(expr.channel_shuffle(x, 2), expr.NCHW)
array([[[[0., 1.]],
        [[4., 5.]],
        [[2., 3.]],
        [[6., 7.]]]], dtype=float32)

reverse_sequence(x, y, batch_dim, seq_dim)

沿着batch_dim维度对x进行切片并反转维度seq_dim上的y[i]元素

参数:

  • x : var_like 输入变量

  • y : var_like 输入变量

  • batch_dim : int 输入变量,切片的维度

  • seq_dim : int 输入变量,反转的维度

返回:反转序列的值

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 8., 1.), [2, 4])
>>> expr.reverse_sequence(x, [1, 1], 0, 1)
array([[0., 1., 2., 3.],
       [4., 5., 6., 7.]], dtype=float32)

crop(x, size, axis, offset)

裁剪输入数据x到size对应的尺寸,通过axis维度,起始点offset

参数:

  • x : var_like 输入变量,data_format 为 NC4HW4.

  • size : var_like 返回裁剪的尺寸

  • axis : int 输入变量,裁剪的维度

  • offset : [int] 输入变量,裁剪的起始点

返回:裁剪后的数据

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 16., 1.), [1, 1, 4, 4])
>>> x = expr.convert(x, expr.NC4HW4)
>>> size = expr.const([0.0, 0.0, 0.0, 0.0], [1, 1, 2, 2])
>>> expr.convert(expr.crop(x, size, 2, [1, 1]), expr.NCHW)
array([[[[ 5.,  6.],
         [ 9., 10.]]]], dtype=float32)

resize(x, x_scale, y_scale)

调整输入值x的(高度、宽度)的大小,公式为:(y_scale * height, x_scale * width).

参数:

  • x : var_like 输入变量

  • x_scale : float 输入变量,图像高度缩放比例

  • y_scale : float 输入变量,图像宽度缩放比例

返回:(y_scale * height, x_scale * width)的值

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 4., 1.), [1, 1, 2, 2])
>>> x = expr.convert(x, expr.NC4HW4)
>>> expr.convert(expr.resize(x, size, 2, 2), expr.NCHW)
array([[[[0. , 0.5, 1. , 1. ],
         [1. , 1.5, 2. , 2. ],
         [2. , 2.5, 3. , 3. ],
         [2. , 2.5, 3. , 3. ]]]], dtype=float32)

crop_and_resize(x, boxes, box_ind, crop_size, method=_F.BILINEAR, extrapolation_value=0.)

从输入图像数据中提取固定大小的切片,并指定插值算法 (method) resize成指定的输出大小(crop_size)

参数:

  • x : var_like 输入变量,data_format 为 NHWC.

  • boxes : var_like 输入变量,int类型,需要划分的区域

  • box_ind : var_like 输入变量,是boxes和x之间的索引

  • crop_size : var_like 输入变量,表示RoiAlign之后的大小

  • method : Interp_Method 输入变量,[NEAREST, BILINEAR],默认是BILINEAR,差值算法

  • extrapolation_value : float 输入变量,默认为0,外插值,boxes>1时裁剪超出图像范围,自动补齐的填充值

返回:crop_size数据

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 16., 1.), [1, 1, 4, 4])
>>> x = expr.convert(x, expr.NHWC)
>>> boxes = expr.const([0.2, 0.3, 0.4, 0.5], [1, 4])
>>> expr.convert(expr.crop_and_resize(x, boxes, [0], [2, 2]), expr.NHWC)
array([[[[3.3000002],
           [3.9      ]],
           [[5.7000003],
           [6.3      ]]]], dtype=float32)

pad(x, paddings, mode=_F.CONSTANT)

对输入数x某个维度进行填充或复制

参数:

  • x : var_like 输入变量

  • paddings : var_like 输入变量,形状是[x.ndim, 2],int类型,填充的维度

  • mode : PadValue_Mode 输入变量,[CONSTANT(填充常数), REFLECT(镜像复制), SYMMETRIC(对称复制)], 默认为CONSTANT

返回:填充或复制后的数据

返回类型:Var

示例:

>>> expr.pad([[1.,2.],[3.,4.]], [[0, 1], [1,2]])
array([[0., 1., 2., 0., 0.],
       [0., 3., 4., 0., 0.],
       [0., 0., 0., 0., 0.]], dtype=float32)

randomuniform(shape, dtype=_F.float, low=0.0, high=1.0, seed0=0, seed1=1)

用于从均匀分布中输出随机值

参数:

  • shape : axis_like 输入变量,数据形状

  • dtype : dtype 输出的类型:[float, double, int, int64, uint8],默认为float

  • low : float 输入变量,随机值范围下限,默认为0

  • high : float 输入变量,随机值范围上限,默认为1

  • seed0 : int 输入变量,随机因子,默认为0

  • seed1 : int 输入变量,随机因子,默认为0

返回:随机值

返回类型:Var

示例:

>>> expr.pad([[1.,2.],[3.,4.]], [[0, 1], [1,2]])
array([[0., 1., 2., 0., 0.],
       [0., 3., 4., 0., 0.],
       [0., 0., 0., 0., 0.]], dtype=float32)

expand_dims(x, axis)

给输入数据在axis轴上新增一个维度

参数:

  • x : var_like 输入变量

  • axis : int or var_like 输入变量,int类型,指定扩大输入数据形状的维度索引值

返回:新增维度后的数据

返回类型:Var

示例:

>>> expr.expand_dims([1.,2.], 0)
array([[1., 2.]], dtype=float32)

rank(x)

返回输入数据的维度

参数:

  • x : var_like 输入变量

返回:维度信息

返回类型:Var

示例:

>>> expr.rank([[1.,2.],[3.,4.]])
array(2, dtype=int32)

size(x)

返回输入数据的大小

参数:

  • x : var_like 输入变量

返回:大小size

返回类型:Var

示例:

>>> expr.size([[1.,2.],[3.,4.]])
array(4, dtype=int32)

shape(x)

返回输入数据的形状

参数:

  • x : var_like 输入变量

返回:形状

返回类型:Var

示例:

>>> expr.shape([[1.,2.],[3.,4.]])
array([2, 2], dtype=int32)

stack(values, axis=0)

将输入数据x和要沿着axis维度进行拼接

参数:

  • x : var_like 输入变量

  • axis : int 输入变量,默认为0,指明对矩阵的哪个维度进行拼接

返回:拼接的数据

返回类型:Var

示例:

>>> x = expr.const([1., 2.], [2])
>>> y = expr.const([3., 4.], [2])
>>> expr.stack([x, y])
array([[1., 2.],
       [3., 4.]], dtype=float32)

unstack(x, axis=0)

将输入数据x沿着axis维度堆叠为r-1级数据

参数:

  • x : var_like 输入变量

  • axis : int 输入变量,默认为0,指明对矩阵的哪个维度进行分解

返回:分解的数据

返回类型:Var

示例:

>>> expr.unstack([[1., 2.], [3., 4.]])
    [array([1., 2.], dtype=float32), array([3., 4.], dtype=float32)]

fill(shape, value)

创建一个填充有标量值的数据

参数:

  • shape : var_like 输入变量,int类型,定义输出张量形状的整数数组

  • value : var_like 输入变量,一个标量值,用于填充输出数据。

返回:填充有标量值的指定形状的数据

返回类型:Var

示例:

>>> expr.fill([2,3], 1.0)
array([[1., 1., 1.],
       [1., 1., 1.]], dtype=float32)

tile(x, multiples)

返回输入数x在同一维度上重复multiples次的数据

参数:

  • x : var_like 输入变量

  • multiples : var_like 输入变量,int类型,同一维度上重复的次数

返回:x重复multiples次的数据

返回类型:Var

示例:

>>> expr.tile([2,3], [3])
array([2, 3, 2, 3, 2, 3], dtype=int32)

gather(x, index)

根据下标返回对应的值

参数:

  • x : var_like 输入变量

  • index : var_like 输入变量,int类型,下标索引

返回:x[index]的值

返回类型:Var

示例:

>>> expr.gather([[1.,2.],[3.,4.]], [1])
array([[3., 4.]], dtype=float32)

gather_nd(x, indices)

根据indeces描述的索引,在x中提取元素,拼接成一个新的数据

参数:

  • x : var_like 输入变量

  • indices : var_like 输入变量,int类型,索引

返回:x[indices[0], ..., indices[-1]]

返回类型:Var

示例:

>>> expr.gather_nd([[1.,2.],[3.,4.]], [1, 0])
array(3., dtype=float32)

select(cond, x, y)

返回根据’cond’从’x’或’y’中选择的元素

参数:

  • cond : var_like 输入变量

  • x : var_like 输入变量

  • y : var_like 输入变量

返回:根据条件选中的元素

返回类型:Var

示例:

>>> expr.select([1., 0., 2.], [-1., -2., -3.], [1., 2., 3.])
array([-1.,  2., -3.], dtype=float32)

squeeze(x, axes=[])

删除到指定位置的尺寸为1的新数据

参数:

  • x : var_like 输入变量

  • axes : axis_like 输入变量,默认为[],用来指定要删掉的为1的维度

返回:变换后的数据

返回类型:Var

示例:

>>> expr.squeeze([[[1.0, 2.0]]], [0, 1])
array([1., 2.], dtype=float32)

unsqueeze(x, axes=[])

插入到指定位置的尺寸为1的新数据

参数:

  • x : var_like 输入变量

  • axes : axis_like 输入变量,默认为[],用来指定要增加的为1的维度

返回:变换后的数据

返回类型:Var

示例:

>>> expr.unsqueeze([1.0, 2.0], [0, 1])
array([[[1., 2.]]], dtype=float32)

depth_to_space(x, axis)

将数据从深度重新排列为空间数据块

参数:

  • x : var_like 输入变量,data_format为NHWC.

  • axis : int 输入变量,操作的维度

返回:空间数据块

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 12., 1.), [1, 4, 1, 3])
>>> expr.depth_to_space(x, 2)
array([[[[ 0.,  3.,  1.,  4.,  2.,  5.],
         [ 6.,  9.,  7., 10.,  8., 11.]]]], dtype=float32)

space_to_depth(x, axis)

重新排列空间数据块,进入深度。

参数:

  • x : var_like 输入变量,data_format为NHWC.

  • axis : int 输入变量,操作的维度

返回:空间数据块,进入深度

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 16., 1.), [1, 1, 4, 4])
>>> x = expr.convert(x, expr.NHWC)
>>> expr.space_to_depth(x, 2)
array([[[[ 0.,  1.,  4.,  5.],
         [ 2.,  3.,  6.,  7.]],
        [[ 8.,  9., 12., 13.],
         [10., 11., 14., 15.]]]], dtype=float32)

batch_to_space_nd(x, block_shape, crops)

将给定的 “batch” 从零维重构为具有“block-Shape + [batch]”形状的 “M+1” 维,其中 block-Shape 是参数,batch 是指定的张量。这里根据给定的裁剪数组对 in-between 结果进行裁剪。

参数:

  • x : var_like 输入变量,data_format为NC4HW4.

  • block_shape : var_like 输入变量,int类型,一维数组的形状必须为 [M],因为所有值都必须大于或等于 1

  • crops : var_like 输入变量,int类型,[M, 2] 的二维数组形状,其​​中所有值必须大于或等于0,这里的crop[i] = [cropStart,cropEnd] 定义了从输入维度 i + 1 裁剪的部分

返回:指定批次的重塑版本的数据

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 12., 1.), [4, 1, 1, 3])
>>> x = expr.convert(x, expr.NC4HW4)
>>> crops = expr.const([0, 0, 0, 0], [2, 2], expr.NCHW, expr.int)
>>> expr.convert(expr.batch_to_space_nd(x, [2, 2], crops), expr.NHWC)
array([[[[ 0.],
         [ 3.],
         [ 1.],
         [ 4.],
         [ 2.],
         [ 5.]],
        [[ 6.],
         [ 9.],
         [ 7.],
         [10.],
         [ 8.],
         [11.]]]], dtype=float32)

space_to_batch_nd(x, block_shape, paddings)

将指定输入空间的空间维度拆分为块的矩阵,其形状为 blockShape,其中 blockshape 是参数。这里根据指定的填充数组填充空间维度

参数:

  • x : var_like 输入变量,data_format为NC4HW4.

  • block_shape : var_like 输入变量,int类型,它是一维数组,必须具有 [M] 的形状,因为所有值必须大于或等于 1

  • paddings : var_like 输入变量,int类型,一个形状为 [M, 2] 的二维数组,所有值必须大于或等于0。这里 padding 等于 [padStart, padEnd]

返回:指定输入空间的拆分版本的数据

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 12., 1.), [3, 1, 2, 2])
>>> x = expr.convert(x, expr.NC4HW4)
>>> paddings = expr.const([0, 0, 0, 0], [2, 2], expr.NCHW, expr.int)
>>> expr.convert(expr.space_to_batch_nd(x, [2, 2], paddings), expr.NHWC)
array([[[[ 0.]]],
       [[[ 4.]]],
       [[[ 8.]]],
       [[[ 1.]]],
       [[[ 5.]]],
       [[[ 9.]]],
       [[[ 2.]]],
       [[[ 6.]]],
       [[[10.]]],
       [[[ 3.]]],
       [[[ 7.]]],
       [[[11.]]]], dtype=float32)

elu(x, alpha)

激励函数,主要是为计算图归一化返回结果而引进的非线性部分

参数:

  • x : var_like 输入变量

  • alpha : float 输入变量,预定义常量

返回:

返回类型:Var

示例:

>>> expr.elu([-1.0, 2.0], 1.673263)
array([-1.0577048,  2.], dtype=float32)

selu(x, scale, alpha)

比例指数线性单元激活函数

参数:

  • x : var_like 输入变量

  • scale : float 输入变量,预定义常量

  • alpha : float 输入变量,预定义常量

返回:缩放的指数单位激活: scale * elu(x, alpha).

返回类型:Var

示例:

>>> expr.selu([-1.0, 2.0], 1.0507, 1.673263)
array([-1.1113304, 2.1014], dtype=float32)

matrix_band_part(x, num_lower, num_upper)

计算矩阵维度

参数:

  • x : var_like 输入变量

  • num_lower : var_like 输入变量,int类型,要保持的对角线的数量,如果为负,则保留整个下三角

  • num_upper : var_like 输入变量,int类型,要保留的superdiagonals数,如果为负,则保持整个上三角.

返回:返回一个与 x 具有相同的类型且有相同形状的秩为k的数据,提取的带状数据

返回类型:Var

示例:

>>> expr.matrix_band_part([[-2., 1.], [-1., 2.]], 1, -1)
array([[-2.,  1.],
       [-1.,  2.]], dtype=float32)

moments(x, axes=[2, 3], shift=None, keep_dims=True)

用于在指定维度计算均值和方差

参数:

  • x : var_like 输入变量,data_format为NC4HW4

  • axes : axis_like 输入变量,int类型,指定计算均值和方差的轴,默认是[2,3]

  • shift : var_like 输入变量,int类型,未在当前实现中使用,默认是None

  • keep_dims : bool 输入变量,保持维度,表示产生是否与输入数据具有相同的维度,默认是true

返回:均值和方差

返回类型:Var

示例:

>>> x = expr.reshape(expr.range(0., 4., 1.), [1, 1, 2, 2])
>>> x = expr.convert(x, expr.NC4HW4)
>>> expr.moments(x)
[array([[[[1.5]]]], dtype=float32), array([[[[1.25]]]], dtype=float32)]

setdiff1d(x, y)

返回在x中出现,但是在y中没有出现的的元素

参数:

  • x : var_like 输入变量

  • y : var_like 输入变量

返回:在x中出现,但是在y中没有出现的的元素

返回类型:Var

示例:

>>> expr.setdiff1d([1, 2, 3], [2, 3, 4])
array([1], dtype=int32)

zeros_like(x)

把所有元素都置为零

参数:

  • x : var_like 输入变量

返回:所有元素都为零的张量

返回类型:Var

示例:

>>> expr.zeros_like([[1, 2], [3, 4]])
array([[0, 0],
       [0, 0]], dtype=int32)

range(start, limit, delta)

创建数字序列变量

参数:

  • start : var_like 输入变量,int类型,表示范围的起始编号

  • limit : var_like 输入变量,int类型,最大编号限制,并且不包括在内

  • delta : var_like 输入变量,int类型,增量

返回:数字序列变量

返回类型:Var

示例:

>>> expr.range(1.0, 7.0, 2.0)
array([1., 3., 5.], dtype=float32)

sort(x, axis=-1, arg=False, descend=False)

排序

参数:

  • x : var_like 输入变量

  • axis : int 输入变量,int类型

  • arg : bool 输入变量,是否返回排序元素的index, 默认为false

  • descend : bool 输入变量,true代表倒序,false代表正序,默认为false

返回:排序结果

返回类型:Var

示例:

>>> expr.sort([[5, 0], [1, 3]])
array([[1, 0],
       [5, 3]], dtype=int32)

nms(boxes, scores, max_detections, iou_threshold=-1.0, score_threshold=-1.0)

非极大值抑制算法,搜索局部极大值,抑制非极大值元素

参数:

  • boxes : var_like 输入变量,形状必须为[num, 4]

  • scores : var_like 输入变量,float类型的大小为[num_boxes]代表上面boxes的每一行,对应的每一个box的一个score

  • max_detections : int 输入变量,一个整数张量,代表最多可以利用NMS选中多少个边框

  • iou_threshold : float 输入变量,IOU阙值展示的是否与选中的那个边框具有较大的重叠度,默认为0

  • score_threshold : float 输入变量,默认为float_min,来决定什么时候删除这个边框

返回:搜索局部极大值,抑制非极大值元素

返回类型:Var

示例:

>>> expr.nms([[1, 1, 4, 4], [0, 0, 3, 3], [5, 5, 7, 7]], [0.9, 0.5, 0.1], 3, 0.1)
array([0, 2], dtype=int32)
array([5., 4.5])

raster(vars, region, shape)

使用Raster创建一个映射关系,Raster是表示内存映射的元算子; region使用[int]来描述;其表示了从var到结果的内存映射关系,对应的C++数据结构如下:

struct View {
    int32_t offset = 0;
    int32_t stride[3] = {1, 1, 1};
};

struct Region {
    View src;
    View dst;
    int32_t size[3] = {1, 1, 1};
};

在Python中使用11个int来表示Region,顺序为:src_offset, src_stride[3], dst_offset, dst_stride[3], size[3];多个region则继续增加int数目,总数目应该为11的倍数;并且region的数目应该与vars的数目相等

参数:

  • var : [Var] 输入变量,内存映射的数据来源

  • region : [int] 表示内存映射关系

  • shape : [int] 输出变量的形状

返回:内存映射后的变量

返回类型:Var

示例:

>>> var = expr.const([1., 2., 3. ,4.], [2, 2])
>>> expr.raster([var], [0, 4, 1, 2, 0, 4, 2, 1, 1, 2, 2], [2, 2]) # do transpose
array([[1., 3.],
       [2., 4.]], dtype=float32)

quant(var, scale, min=-128, max=127, zero=0)

量化,根据scale把float类型的输入量化为int8类型的输出,量化公式为:y = clamp(x / scale + zero, min, max)

参数:

  • var : Var 输入变量,dtype为float, data_format为NC4HW4

  • scale : Var 量化的scale值,dtype为float

  • min : int 输出变量的最小值,默认为-128

  • max : int 输出变量的最大值,默认为127

  • zero : int 零点值,默认为0

返回:量化后的int8类型变量

返回类型:Var

示例:

>>> x = expr.const([1., 2., 3. ,4.], [4])
>>> x = expr.convert(x, expr.NC4HW4)
>>> expr.quant(x, 0.2, -128, 127)
array([-128, -128, -127, -127], dtype=int8)

dequant(var, scale, zero=0)

反量化,根据scale把int8类型的输入反量化为float类型的输出,量化公式为:y = (x - zero) * scale

参数:

  • var : Var 输入变量,dtype为int8

  • scale : Var 反量化的scale值,dtype为float

  • zero : int 反量化的zero值,默认为0

返回:反量化后的float类型变量

返回类型:Var

示例:

>>> x = expr.const([-128, -128, -127, -127], [4], NCHW, expr.int8)
>>> x = expr.convert(x, expr.NC4HW4)
>>> expr.dequant(x, 0.2, 0)
array([0. , 0. , 0.2, 0.2], dtype=float32)

histogram(input, binNum, minVal, maxVal)

计算输入变量在指定范围内的直方图分布

参数:

  • input : var_like 输入变量

  • binNum : int 直方图桶的个数

  • minVal : int 直方图计算的最小值

  • maxVal : int 直方图计算的最大值

返回:直方图统计结果

返回类型:Var

示例:

>>> expr.histogram(expr.range(0., 10.0, 1.0), 5, 1, 9)
array([2., 2., 1., 2., 2.], dtype=float32)

detection_post_process(encode_boxes, class_predictions, anchors, num_classes, max_detections, max_class_per_detection, detections_per_class, nms_threshold, iou_threshold, use_regular_nms, centersize_encoding)

SSD检测模型后处理函数

参数:

  • encode_boxes : Var 检测框坐标

  • class_predictions : Var 分类结果概率

  • anchors : Var 锚点

  • num_classes : int 分类个数

  • max_detections : int 最大检测值

  • max_class_per_detection : int 每个检测的最大种类

  • detections_per_class : int 每个类别的检测结果

  • nms_threshold : float nms阈值

  • iou_threshold : float iou阈值

  • use_regular_nms : bool 是否使用常规nms,目前仅支持False

  • centersize_encoding : [float] 中心尺寸编码

返回:后处理结果

返回类型:Var


roi_pooling(input, roi, pooledHeight, pooledWidth, spatialScale, outputGrad, backwardDiff)

roi_pooling

参数:

  • input : Var 输入变量,dtype为int8

  • roi : Var 反量化的scale值,dtype为float

  • pooledHeight : int 反量化的zero值,默认为0

  • pooledWidth : int 反量化的zero值,默认为0

返回:roipooling结果

返回类型:Var

示例:

TODO

roi_align(input, roi, pooledHeight, pooledWidth, spatialScale, samplingRatio, aligned, poolType, outputGrad, backwardDiff)

roialign

参数:

  • input : Var 输入变量,dtype为int8

  • roi : Var 反量化的scale值,dtype为float

  • pooledHeight : int pooling的

  • pooledHeight : int 反量化的zero值,默认为0

返回:roialign结果

返回类型:Var

示例:

TODO

以下函数为框架开发者使用函数,普通用户不建议使用!


load_as_dict(fileName) [deprecated]

从文件中加载模型,并将模型转换为计算图,以dict的形式返回计算图的所有节点名称和Var

不建议使用该接口

参数:

  • fileName:str 模型文件路径

返回:加载的模型计算图,其keystrvalueVar

返回类型:dict

示例:

>>> vars = expr.load_as_dict('mobilenet_v1.mnn')
>>> vars.keys()
dict_keys(['conv1', 'conv2_1/dw', 'conv2_1/sep', 'conv2_2/dw', 'conv2_2/sep', 'conv3_1/dw', 'conv3_1/sep', 'conv3_2/dw', 'conv3_2/sep', 'conv4_1/dw', 'conv4_1/sep', 'conv4_2/dw', 'conv4_2/sep', 'conv5_1/dw', 'conv5_1/sep', 'conv5_2/dw', 'conv5_2/sep', 'conv5_3/dw', 'conv5_3/sep', 'conv5_4/dw', 'conv5_4/sep', 'conv5_5/dw', 'conv5_5/sep', 'conv5_6/dw', 'conv5_6/sep', 'conv6/dw', 'conv6/sep', 'data', 'fc7', 'pool6', 'prob'])

get_inputs_and_outputs(allVariable) [deprecated]

获取dict形式计算图的输入输出节点,可以在使用V3接口时获取输入输出的信息

参数:

  • allVariable:dict 计算图的dict形式,其keystrvalueVar

返回:计算图的输入输出,其中输入输出都为dict形式,其keystrvalueVar

返回类型:(dict, dict)

示例:

>>> vars = expr.load_as_dict('mobilenet_v1.mnn')
>>> inputs, outputs = expr.get_inputs_and_outputs(vars)
>>> inputs.keys()
dict_keys(['data'])
>>> outputs.keys()
dict_keys(['prob'])

numpy

module numpy

numpy模块提供了基础的数值计算函数,在expr模块基础上提供了兼容了numpy的API

numpy兼容参数是指为了兼容numpy的api,但是在MNN.numpy中无效的参数

用法注意:

  • 此numpy模块并非完整支持numpy的所有API,当遇到函数或用法不支持时可以尝试用其他函数拼凑;

  • 此numpy模块数值计算基于MNN.expr封装,计算过程会有创建Op的开销,对于较小规模的Var计算性能相比numpy会有比较明显的劣势,因此尽量避免使用此模块处理小规模数据;

  • Var的存取可以利用expr.saveexpr.load_as_list实现;


numpy Types


numpy.dtype

描述Var的数据类型,是expr.dtype的封装

  • 类型:Enum

  • 枚举值:

    • uint8

    • int32

    • int64

    • float32

    • float64


concatenate(args, axis=0, out=None, dtype=None, casting="same_kind")

作用等同与 numpy 中的 np.concatenate 函数,沿指定轴连接输入的Var

参数:

  • args:Var序列 被连接的变量

  • axis:int 指定连接的轴

  • out:numpy兼容参数 默认为None

  • dtype:numpy兼容参数 默认为None

  • casting:numpy兼容参数 默认为None

返回:创建的Var

返回类型:Var

示例

>>> a = np.array([[1, 2], [3, 4]])
>>> b = np.array([[5, 6]])
>>> np.concatenate((a, b), axis=0)
array([[1, 2],
       [3, 4],
       [5, 6]], dtype=int32)

pad(array, pad_width, mode='constant')

作用等同与 numpy 中的 np.pad 函数,对输入变量的边沿按照指定规则进行扩充。

参数:

  • array:Var 将要扩充的变量

  • pad_width:Var,扩充的数目,一共为维度的2倍 扩充的数目,一共为维度的2倍,分别对应一个维度前后

  • mode:{'constant', 'reflect', 'symmetric'} 填充的方式

返回:扩充后得到的变量

返回类型:Var

示例

a = [1, 2, 3, 4, 5]
>>> np.pad(a, (2, 3), 'constant')
array([0, 0, 1, 2, 3, 4, 5, 0, 0, 0], dtype=int32)
>>> np.pad(a, (2, 3), 'reflect')
array([3, 2, 1, 2, 3, 4, 5, 4, 3, 2], dtype=int32)
>>> np.pad(a, (2, 3), 'symmetric')
array([2, 1, 1, 2, 3, 4, 5, 5, 4, 3], dtype=int32)

sign(x)

作用等同与 numpy 中的 np.sign 函数,按位获取符号,正数返回1负数返回-1。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.sign([1.,-2.])
array([ 1., -1.], dtype=float32)

asscalar(a)

作用等同与 numpy 中的 np.asscalar 函数,将数据转换为scalar。

参数:

  • a:Var 输出变量的数据来源

返回:创建的Var

返回类型:Var

示例

>>> np.asscalar(np.array([24]))
24

shape(a)

作用等同与 numpy 中的 np.shape 函数,获取输入Var的形状,类型为tuple

参数:

  • a:Var 将会输出该变量的形状

返回:Var的形状

返回类型:tuple

示例

>>> a = np.ones([[2],[2]])
>>> np.shape(a)
(2, 2)

mat(a, dtype=None)

作用等同与 numpy 中的 np.mat 函数,根据指定数据,将输入数据转换为ndim=2的Var。

参数:

  • a:Var|list|tuple 输出变量的数据来源

  • dtype:dtype 重新指定输出的类型; 默认为a的数据类型

返回:创建的Var

返回类型:Var

示例

>>> np.mat([1, 2, 3, 4])
array([[1, 2, 3]])

divmod(x1, x2)

作用等同与 numpy 中的 np.divmod 函数,返回2个输入的(floordiv, mod)值。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:tuple of Var

示例

>>> np.divmod(2, 3)
(array(0, dtype=int32), array(2, dtype=int32))

zeros(shape, dtype=None, order='C')

作用等同与 numpy 中的 np.zeros 函数,创建一个指定形状,类型的Var,其全部的值都为0,是full的特例。

参数:

  • shape:[int] 指定输出的形状

  • dtype:dtype 指定输出的形状类型; 默认为np.float32

  • order:numpy兼容参数 默认为None

返回:创建的Var

返回类型:Var

示例

>>> np.ones((2, 2))
array([[0., 0.],
       [0., 0.]])

matrix(a, dtype=None)

作用等同与 numpy 中的 np.matrix 根据指定数据,将输入数据转换为ndim=2的Var。

参数:

  • a:Var|list|tuple 输出变量的数据来源

  • dtype:dtype 重新指定输出的类型; 默认为a的数据类型

返回:创建的Var

返回类型:Var

示例

>>> np.matrix([1, 2, 3, 4])
array([[1, 2, 3]])

round_(x)

作用等同与 numpy 中的 np.round_ 函数,按位进行四舍五入。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.round_([1.2, 3.5, 4.7])
array([1., 4., 5.], dtype=float32)

ones(shape, dtype=None, order='C')

作用等同与 numpy 中的 np.ones 函数,创建一个指定形状,类型的Var,其全部的值都为1,是full的特例。

参数:

  • shape:[int] 指定输出的形状

  • dtype:dtype 指定输出的形状类型; 默认为np.float32

  • order:numpy兼容参数 默认为None

返回:创建的Var

返回类型:Var

示例

>>> np.ones((2, 2))
array([[1., 1.],
     [1., 1.]])

expand_dims(x, axis)

作用等同与 numpy 中的 np.expand_dims 函数,扩展输入的形状,在指定位置插入一个新维度,是expr.unsqueeze的封装。

参数:

  • x:Var 需要扩展维度的变量

  • axis:int 插入新维度的位置

返回:创建的Var

返回类型:Var

示例

>>> a = np.array([1, 2])
>>> b = np.expand_dims(a, axis=0)
>>> a.shape
[2]
>>> b.shape
[1,2]

rollaxis(a, axis, start=0)

作用等同与 numpy 中的 np.rollaxis 函数,将Var的指定维度滚动到目标位置。

参数:

  • a:Var 将会滚动该变量的维度

  • axis:int 指定要滚动的维度

  • start:int 滚动维度的目标位置

返回:滚动维度后的MNN.Var

返回类型:Var

示例

>>> a = np.ones((3,4,5,6))
>>> np.rollaxis(a, 3, 1).shape
(3, 6, 4, 5)

asarray(a, dtype=None, order=None)

作用等同与 numpy 中的 np.asarray 根据指定数据,将输入数据转换为一个Var。

参数:

  • a:ndarray 输出变量的数据来源

  • dtype:dtype 重新指定输出的类型; 默认为a的数据类型

  • order:numpy兼容参数 默认为None

返回:创建的Var

返回类型:Var

示例

>>> np.asarray([1, 2, 3])
array([1, 2, 3])

repeat(a, repeats, axis=None)

作用等同与 numpy 中的 np.repeat 函数,将输入变量的元素重复指定次数。

参数:

  • A:Var 被复制的Var

  • repeats:int 复制的次数

  • axis:numpy兼容参数 默认为None

返回:复制得到的Var

返回类型:Var

示例

>>> a = np.array([0, 1, 2])
>>> np.repeat(a, 2)
array([0, 0, 1, 1, 2, 2], dtype=int32)

zeros_like(a, dtype=None, order='K', subok=True, shape=None)

作用等同与 numpy 中的 np.zeros_like 函数,创建一个与指定Var的形状类型相同的Var,其全部的值为0, 为full_like的特例。

参数:

  • a:Var 输出Var的形状和数据类型与该Var相同

  • dtype:dtype 指定输出的形状类型; 默认为np.float32

  • order:numpy兼容参数 默认为None

  • subok:numpy兼容参数 默认为None

  • shape:[int] 重新指定输出的形状

返回:创建的Var

返回类型:Var

示例

>>> a = np.empty([2,2])
>>> np.ones_like(a)
array([[0., 0.],
     [0., 0.]])

clip(x, a_min, a_max)

作用等同与 numpy 中的 np.clip 函数, 对元素按照最小最大的范围进行约束。

参数:

  • x:Var 参与计算的变量

  • a_min:scalar 约束的最小值

  • a_max:scalar 约束的最大值

返回:计算得到的变量

返回类型:tuple of Var

示例

>>> np.clip(np.array([-3., -1., 3.]), -2., 2.)
array([-2., -1.,  2.], dtype=float32)

sin(x)

作用等同与 numpy 中的 np.sin 函数,按元素计算正弦值。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.sin([1.,2.])
array([0.84147096, 0.9092974 ], dtype=float32)

cos(x)

作用等同与 numpy 中的 np.cos 函数,按元素计算余弦值。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.cos([1.,2.])
array([ 0.5403023 , -0.41614684], dtype=float32)

count_nonzero(a, axis=None, keepdims=False)

作用等同与 numpy 中的 np.count_nonzero 函数,计算非0元素的数目。

参数:

  • a:Var 将要计数的变量

  • axis:int 计数的轴

  • keepdims:bool 是否保留计数维度

返回:计数得到的变量

返回类型:int of Var

示例

>>> np.count_nonzero(np.eye(4))
4

cbrt(x)

作用等同与 numpy 中的 np.cbrt 函数,按元素计算立方根。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.cbrt([1.,2.])
array([1., 1.2599211], dtype=float32)

ptp(a, axis=None)

作用等同与 numpy 中的 np.ptp 函数,返回沿axis轴的数值范围(最大值减去最小值)。

参数:

  • a:Var 将要统计的变量

  • axis:int 统计的轴

返回:计数得到的变量

返回类型:int of Var

示例

>>> a = np.arange(4).reshape((2,2))
>>> np.ptp(a) 
3
>>> np.ptp(a, axis=0) 
array([2, 2], dtype=int32)

where(indices, shape, order='C')

作用等同与 numpy 中的 np.unravel_index 函数,将平面索引转换为坐标索引; 比如在形状为[3,4]的坐标中,平面索引6代表拉平后的第六个元素,对应的坐标索引为第2行第3列,所以坐标索引为(2,3)

参数:

  • indices:Var 平面索引值

  • shape:Var 坐标索引对应的形状

  • order:numpy兼容参数 默认为None

返回:计算得到的坐标索引各轴的坐标值

返回类型:Var数组

示例

>>> np.unravel_index([22, 41, 37], (7,6))
[array([3, 6, 6], dtype=int32), array([4, 5, 1], dtype=int32)]

empty_like(prototype, dtype=None, order='K', subok=True, shape=None)

作用等同与 numpy 中的 np.empty_like 函数,创建一个与指定Var相同的未初始化Var变量,是expr.placeholder的封装。

参数:

  • prototype:Var 输出Var的形状和数据类型与该Var相同

  • dtype:dtype 重新指定输出的数据类型

  • order:numpy兼容参数 默认为None

  • subok:numpy兼容参数 默认为None

  • shape:[int] 重新指定输出的形状

返回:创建的空Var

返回类型:Var

示例

a = ([1,2,3], [4,5,6])
>>> x = np.empty_like(a)

arccos(x)

作用等同与 numpy 中的 np.arccos 函数,按元素计算反余弦值。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.arccos([0.5, 1.0])
array([1.0471975, 0.       ], dtype=float32)

fabs(x)

作用等同与 numpy 中的 np.fabs 函数,求输入的绝对值, 是expr.fabs的封装。

参数:

  • x:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.fabs([-1., 2.])
array([1., 2.], dtype=float32)

equal(a1, a2)

作用等同与 numpy 中的 np.equal 函数,判断一个变量是否等于另一个变量。

参数:

  • a1:Var 输入的变量

  • a2:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.equal([2, 2], [1, 2])
array([0, 1], dtype=int32)

float_power(x1, x2)

作用等同与 numpy 中的 np.float_power 函数,对输入的2个变量求指数, 是expr.power的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.float_power(2., 3.)
array(8., dtype=float32)

arctanh(x)

作用等同与 numpy 中的 np.arctanh 函数,按元素计算双曲反正切值。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.arctanh([0.5, 0.8])
array([0.54930615, 1.0986123 ], dtype=float32)

arcsin(x)

作用等同与 numpy 中的 np.arcsin 函数,按元素计算反正弦值。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.arcsin([0.5, 1.0])
array([0.5235988, 1.5707963], dtype=float32)

abs(x)

作用等同与 numpy 中的 np.abs 函数,求输入的绝对值, 是expr.abs的封装。

参数:

  • x:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.abs([-1, 2])
array([1, 2], dtype=int32)

var(a, axis=None, dtype=float32, out=None, keepdims=False)

作用等同与 numpy 中的 np.var 函数,返回沿axis轴的方差。

参数:

  • a:Var 将要统计的变量

  • axis:int 统计的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留计数维度

返回:计数得到的变量

返回类型:float of Var

示例

>>> a = np.arange(4).reshape((2,2))
>>> np.var(a)
1.25
>>> np.var(a, axis=0) 
array([1., 1.], dtype=float32)

multiply(x1, x2)

作用等同与 numpy 中的 np.multiply 函数,对输入的2个变量相乘, 是expr.multiply的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.multiply(13, 17)
array(221, dtype=int32)

arcsinh(x)

作用等同与 numpy 中的 np.arcsinh 函数,按元素计算双曲反正弦值。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.arcsinh([0.5, 1.0])
array([0.4812118, 0.8813736], dtype=float32)

array(object, dtype=None, copy=True, order='K', subok=False, ndmin=0, like=None)

作用等同与 numpy 中的 np.array 根据指定数据,类型创建一个Var。

参数:

  • object:ndarray 输出变量的数据来源

  • dtype:dtype 指定输出的类型; 默认为object的类型

  • order:numpy兼容参数 默认为None

  • subok:numpy兼容参数 默认为None

返回:创建的Var

返回类型:Var

示例

>>> np.array([1, 2, 3])
array([1, 2, 3])

vdot(a, b)

作用等同与 numpy 中的 np.vdot 函数,对输入的2个变量做点乘。 根据ab的维度进行如下计算:

  • 如果都是0维,等价于乘法

  • 如果都是1维,等价于秋内积,即按位乘之后求和

  • 如果都是2维,等价于矩阵乘法

  • 如果a是N维,b是1维,则对最后一维度求内积

  • 如果a是N维,b是M维(M>=2),则对a最后一维和b的倒数第二维度求内积

参数:

  • a:Var 参与计算的变量

  • b:Var 参与计算的变量

  • out:numpy兼容参数 默认为None

返回:计算得到的变量

返回类型:Var

示例

>>> np.vdot(3, 4)
12
>>> np.vdot([[1, 0], [0, 1]], [[4, 1], [2, 2]])
array([[4, 1],
     [2, 2]], dtype=int32)

asfarray(a, dtype=np.float32)

作用等同与 numpy 中的 np.asfarray 根据指定数据,将输入数据转换为一个类型为float的Var。

参数:

  • a:ndarray 输出变量的数据来源

  • dtype:dtype 重新指定输出的类型; 默认为np.float32

返回:创建的Var

返回类型:Var

示例

>>> np.asfarray([1, 2, 3])
array([1., 2., 3.])

remainder(x1, x2)

作用等同与 numpy 中的 np.remainder 函数,对输入的2个变量求模, 是expr.mod的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.remainder(13, 5)
array(3, dtype=int32)

amax(a, axis=None, out=None, keepdims=False)

作用等同与 numpy 中的 np.amax 函数,返回沿axis轴的最大值。

参数:

  • a:Var 将要统计的变量

  • axis:int 统计的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留计数维度

返回:计数得到的变量

返回类型:int of Var

示例

>>> a = np.arange(4).reshape((2,2))
>>> np.amax(a) 
3
>>> np.amin(a, axis=0) 
array([2, 3], dtype=int32)

power(x1, x2)

作用等同与 numpy 中的 np.power 函数,对输入的2个变量求指数, 是expr.power的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.power(2., 3.)
array(8., dtype=float32)

nanmax(a, axis=None, out=None, keepdims=False)

作用等同与 numpy 中的 np.nanmax 函数,返回沿axis轴的最大值。

参数:

  • a:Var 将要统计的变量

  • axis:int 统计的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留计数维度

返回:计数得到的变量

返回类型:int of Var

示例

>>> a = np.arange(4).reshape((2,2))
>>> np.nanmax(a) 
3
>>> np.nanmax(a, axis=0) 
array([2, 3], dtype=int32)

empty(shape, dtype=float32, order='C')

作用等同与 numpy 中的 np.empty 函数,创建一个指定形状未初始化的Var变量,是expr.placeholder的封装。

参数:

  • shape:tuple 指定输出的形状

  • dtype:dtype 指定输出的形状类型; 默认为np.float32

  • order:numpy兼容参数 默认为None

返回:创建的空Var

返回类型:Var

示例

>>> x = np.empty([2, 2])
x.write([1.,2.,3.,4.])

arange([start, ]stop, [step, ], dtype=None)

作用等同与 numpy 中的 np.arange 根据指定数据,类型创建一个指定范围和步长的Var。

参数:

  • start:int|float 输出变量的范围起点

  • stop:int|float 输出变量的范围终点

  • step:int|float 输出变量的范围步长

  • dtype:dtype 指定输出的类型; 默认为stop的类型

返回:创建的Var

返回类型:Var

示例

>>> np.arange(0, 5, 1)
array([0, 1, 2, 3, 4])

log2(x)

作用等同与 numpy 中的 np.log2 函数,按元素计算log2(x)

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.log2([1.,2.])
array([0., 1.], dtype=float32)

where(condition, x=None, y=None)

作用等同与 numpy 中的 np.where 函数,根据给定条件返回两个输入的元素。

参数:

  • condition:Var 条件变量

  • x:Var 参与取值的变量

  • y:Var 参与取值的变量

返回:计算得到的变量

返回类型:Var

示例

>>> a = np.arange(10)
>>> np.where(a < 5, a, a*10)
array([ 0,  1,  2,  3,  4, 50, 60, 70, 80, 90], dtype=int32)

all(a, axis=None, out=None, keepdims=None)

作用等同与 numpy 中的 np.all 函数,判断是否所有元素都为真(不为0)。

参数:

  • a:Var 输入的变量

  • axis:list 判断的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留指定轴的维度

返回:计算得到的变量

返回类型:Varorscalar

示例

>>> np.all([1, 0, 1])
False
>>> np.all([1, -1, 1])
True

round(x)

作用等同与 numpy 中的 np.round 函数,按位进行四舍五入。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.round([1.2, 3.5, 4.7])
array([1., 4., 5.], dtype=float32)

divide(x1, x2)

作用等同与 numpy 中的 np.divide 函数,对输入的2个变量相除, 是expr.divide的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.divide(13., 17.)
array(0.7647059, dtype=float32)

average(a, axis=None, weights=None, returned=False)

作用等同与 numpy 中的 np.average 函数,返回沿axis轴的平均值。

参数:

  • a:Var 将要统计的变量

  • axis:int 统计的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留计数维度

返回:计数得到的变量

返回类型:float of Var

示例

>>> a = np.arange(4).reshape((2,2))
>>> np.average(a)
1.5
>>> np.average(a, axis=0) 
array([1., 2.], dtype=float32)

trunc(x)

作用等同与 numpy 中的 np.trunc 函数,按位进行四舍五入。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.trunc([1.2, 3.5, 4.7])
array([1., 4., 5.], dtype=float32)

nanprod(a, axis=None, out=None, keepdims=False)

作用等同与 numpy 中的 np.nanprod 函数,沿着指定维度,对数据求乘积。

参数:

  • x:Var 输入的变量

  • axis:list 计算的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留指定轴的维度

返回:计算得到的变量

返回类型:Varorscalar

示例

>>> np.nanprod([1,2,3,4])
24

std(a, axis=None, dtype=float32, out=None, keepdims=False)

作用等同与 numpy 中的 np.std 函数,返回沿axis轴的标准差。

参数:

  • a:Var 将要统计的变量

  • axis:int 统计的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留计数维度

返回:计数得到的变量

返回类型:float of Var

示例

>>> a = np.arange(4).reshape((2,2))
>>> np.std(a)
1.1180340051651
>>> np.std(a, axis=0) 
array([0.99999994, 0.99999994], dtype=float32)

bitwise_xor(x1, x2)

作用等同与 numpy 中的 np.bitwise_xor 函数,对输入的2个变量按位与, 是expr.bitwise_xor的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.bitwise_xor(13, 17)
array(28, dtype=int32)

dot(a, b, out=None)

作用等同与 numpy 中的 np.dot 函数,对输入的2个变量做点乘。 根据ab的维度进行如下计算:

  • 如果都是0维,等价于乘法

  • 如果都是1维,等价于秋内积,即按位乘之后求和

  • 如果都是2维,等价于矩阵乘法

  • 如果a是N维,b是1维,则对最后一维度求内积

  • 如果a是N维,b是M维(M>=2),则对a最后一维和b的倒数第二维度求内积

参数:

  • a:Var 参与计算的变量

  • b:Var 参与计算的变量

  • out:numpy兼容参数 默认为None

返回:计算得到的变量

返回类型:Var

示例

>>> np.dot(3, 4)
12
>>> np.dot([[1, 0], [0, 1]], [[4, 1], [2, 2]])
array([[4, 1],
     [2, 2]], dtype=int32)

moveaxis(a, source, destination)

作用等同与 numpy 中的 np.moveaxis 函数,移动Var的指定维度到指定位置。

参数:

  • a:Var 将会移动该变量的维度

  • source:int 需要移动的维度

  • destination:int 移动的目标位置

返回:形状为newshape的MNN.Var

返回类型:Var

示例

>>> a = np.ones((3, 4, 5))
>>> np.moveaxis(a, 0, -1).shape
(4, 5, 3)

flatnonzero(a)

作用等同与 numpy 中的 np.flatnonzero 函数,平铺之后输出非零元素的索引。

参数:

  • a:Var 将要搜索的变量

返回:搜索得到的变量

返回类型:tuple of Var

示例

>>> x = np.arange(6).reshape(2,3)
>>> np.flatnonzero(x>1)
array([2, 3, 4, 5], dtype=int32)

copysign(x1, x2)

作用等同与 numpy 中的 np.copysign 函数,将第二个输入的符号作用到第一个输入上并返回。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.copysign(13, -1)
array(-13, dtype=int32)

atleast_3d(*arys)

作用等同与 numpy 中的 np.atleast_2d 函数,将输入Var的维度变为至少为3,如果输入为标量或1/2维变量则输出为3维变量,否则保持维度不变。

参数:

  • *arys:Var 多个需要转换的变量

返回:转置后的 MNN.Var

返回类型:Var

示例

>>> np.atleast_1d(3, [2,3])
[array([[[3]]], dtype=int32), array([[[2],[3]]], dtype=int32)]

sinc(x)

作用等同与 numpy 中的 np.sinc 函数,对于输入x进行如下计算sin(x*PI)/(x*PI)

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.sinc([1.,2.])
array([-2.7827534e-08,  2.7827534e-08], dtype=float32)

modf(x)

作用等同与 numpy 中的 np.modf 函数, 按元素返回输入的整数部分和小数部分。

参数:

  • x:Var 参与计算的变量

返回:计算得到的变量

返回类型:tuple of Var

示例

>>> np.modf([2.1, -1.2])
(array([ 0.0999999 , -0.20000005], dtype=float32), array([ 2., -2.], dtype=float32))

nanmean(a, axis=None, dtype=float32, out=None, keepdims=False)

作用等同与 numpy 中的 np.nanmean 函数,返回沿axis轴的平均值。

参数:

  • a:Var 将要统计的变量

  • axis:int 统计的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留计数维度

返回:计数得到的变量

返回类型:float of Var

示例

>>> a = np.arange(4).reshape((2,2))
>>> np.nanmean(a)
1.5
>>> np.nanmean(a, axis=0) 
array([1., 2.], dtype=float32)

cosh(x)

作用等同与 numpy 中的 np.cosh 函数,按元素计算双曲余弦值。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.cosh([1.,2.])
array([1.5430807, 3.7621956], dtype=float32)

log1p(x)

作用等同与 numpy 中的 np.log1p 函数,按元素计算log(x+1)

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.log1p([1.,2.])
array([0.6931472, 1.0986123], dtype=float32)

maximum(x1, x2)

作用等同与 numpy 中的 np.maximum 函数,输入2个变量中的最大值, 是expr.maximum的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.maximum(13, 17)
array(17, dtype=int32)

ceil(x)

作用等同与 numpy 中的 np.ceil 函数,按位对小数部分进位。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.ceil([1.2, 3.5, 4.7])
array([2., 4., 5.], dtype=float32)

log10(x)

作用等同与 numpy 中的 np.log10 函数,按元素计算log10(x)

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.log10([1.,2.])
array([0., 0.30102998], dtype=float32)

inner(a, b)

作用等同与 numpy 中的 np.inner 函数,对输入的2个变量做内积,即按位乘后求和。

参数:

  • a:Var 参与计算的变量

  • b:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.inner([1,2,3], [0,1,0])
2

array_split(ary, indices_or_sections, axis=0)

作用等同与 numpy 中的 np.array_split 函数,将一个Var拆分成为多个子变量。 该实现与split等价;与numpy的区别是当分块数目无法整除时不会保留余数部分。

参数:

  • ary:Var 被拆分的Var

  • indices_or_sections:list 拆分的块数或者块大小

  • axis:int 拆分的维度

返回:拆分得到的Var

返回类型:Var 数组

示例

>>> x = np.arange(9.0)
>>> np.array_split(x, 3)
[array([0.,  1.,  2.]), array([3.,  4.,  5.]), array([6.,  7.,  8.])]

greater(a1, a2)

作用等同与 numpy 中的 np.greater 函数,判断一个变量是否大于另一个变量。

参数:

  • a1:Var 输入的变量

  • a2:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.greater([2, 1], [1, 2])
array([1, 0], dtype=int32)

tile(A, reps)

作用等同与 numpy 中的 np.tile 函数,将输入变量复制指定次数构造一个新变量。

参数:

  • A:Var 被复制的Var

  • reps:int 复制的次数

返回:复制得到的Var

返回类型:Var

示例

>>> a = np.array([0, 1, 2])
>>> np.tile(a, 2)
array([0, 1, 2, 0, 1, 2], dtype=int32)

argwhere(a)

作用等同与 numpy 中的 np.argwhere 函数,输出非零元素的索引。

参数:

  • a:Var 将要搜索的变量

返回:搜索得到的变量

返回类型:Var

示例

>>> x = np.arange(6).reshape(2,3)
>>> np.argwhere(x>1)
array([[0, 2],
     [1, 0],
     [1, 1],
     [1, 2]], dtype=int32)

ones_like(a, dtype=None, order='K', subok=True, shape=None)

作用等同与 numpy 中的 np.ones_like 函数,创建一个与指定Var的形状类型相同的Var,其全部的值为1, 为full_like的特例。

参数:

  • a:Var 输出Var的形状和数据类型与该Var相同

  • dtype:dtype 指定输出的形状类型; 默认为np.float32

  • order:numpy兼容参数 默认为None

  • subok:numpy兼容参数 默认为None

  • shape:[int] 重新指定输出的形状

返回:创建的Var

返回类型:Var

示例

>>> a = np.empty([2,2])
>>> np.ones_like(a)
array([[1., 1.],
     [1., 1.]])

bitwise_and(x1, x2)

作用等同与 numpy 中的 np.bitwise_and 函数,对输入的2个变量按位与, 是expr.bitwise_and的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.bitwise_and(13, 17)
array(1, dtype=int32)

lexsort(a, axis=-1)

作用等同与 numpy 中的 np.lexsort 函数,对输入变量排序。

参数:

  • a:Var 将要排序的变量

  • axis:int 排序的维度

返回:排序后得到的变量

返回类型:Var

示例

>>> a = np.array([[1,4],[3,1]])
>>> np.lexsort(a)  
array([[1, 4],
     [1, 3]], dtype=int32)

linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)

作用等同与 numpy 中的 np.linspace 根据指定数据,类型创建一个指定范围(等差)和数目的Var。

参数:

  • start:int|float 输出变量的范围起点

  • stop:int|float 输出变量的范围终点

  • num:int 输出数据总数

  • endpoint:bool 目前仅支持endpoint=False

  • retstep:,返回值是否包含步长;如果包含则返回(Var 返回值是否包含步长;如果包含则返回(Var, step)

  • dtype:dtype 指定输出的类型; 默认为stop的类型

  • axis:numpy兼容参数 默认为None

返回:创建的Var

返回类型:Var

示例

>>> np.linspace(0, 10, 2, False, True)
(array([0, 5], dtype=int32), 5.0)

nansum(a, axis=None, out=None, keepdims=False)

作用等同与 numpy 中的 np.nansum 函数,沿着指定维度,对数据求和。

参数:

  • x:Var 输入的变量

  • axis:list 计算的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留指定轴的维度

返回:计算得到的变量

返回类型:Varorscalar

示例

>>> np.nansum([1,2,3,4])
10

expm1(x)

作用等同与 numpy 中的 np.expm1 函数,按元素计算exp(x)-1

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.expm1([1.,2.])
array([1.7182794, 6.388731 ], dtype=float32)

atleast_2d(*arys)

作用等同与 numpy 中的 np.atleast_2d 函数,将输入Var的维度变为至少为2,如果输入为标量或1维变量则输出为2维变量,否则保持维度不变。

参数:

  • *arys:Var 多个需要转换的变量

返回:转置后的 MNN.Var

返回类型:Var

示例

>>> np.atleast_1d(3, [2,3])
[array([[3]], dtype=int32), array([[2, 3]], dtype=int32)]

broadcast_to(array, shape)

作用等同与 numpy 中的 np.broadcast_to 函数,将输入广播到指定形状。

参数:

  • array:Var 需要广播的变量

  • shape:[int] 广播的形状

返回:创建的Var

返回类型:Var

示例

>>> a = np.array([1, 2, 3])
>>> np.broadcast_to(a, (3,3))
array([[1, 2, 3],
     [1, 2, 3],
     [1, 2, 3]], dtype=int32)

fmin(x1, x2)

作用等同与 numpy 中的 np.fmin 函数,输入2个变量中的最小值, 是expr.minimum的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.fmin(13., 17.)
array(13., dtype=float32)

min(a, axis=None, out=None, keepdims=False)

作用等同与 numpy 中的 np.min 函数,返回沿axis轴的最小值。

参数:

  • a:Var 将要统计的变量

  • axis:int 统计的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留计数维度

返回:计数得到的变量

返回类型:int of Var

示例

>>> a = np.arange(4).reshape((2,2))
>>> np.min(a) 
0
>>> np.min(a, axis=0) 
array([0, 1], dtype=int32)

geomspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)

作用等同与 numpy 中的 np.geomspace 根据指定数据,类型创建一个指定范围(指数等差)和数目的Var。

参数:

  • start:int|float 输出变量的范围起点

  • stop:int|float 输出变量的范围终点

  • num:int 输出数据总数

  • endpoint:bool 是否包含终点元素,目前仅支持endpoint=False

  • retstep:(Var, step) 返回值是否包含步长;如果包含则返回(Var, step)

  • dtype:dtype 指定输出的类型; 默认为stop的类型

  • axis:numpy兼容参数 默认为None

返回:创建的Var

返回类型:Var

示例

>>> np.geomspace(1., 10., 4, False)
array([1.       , 1.7782794, 3.1622777, 5.6234136], dtype=float32)

add(x1, x2)

作用等同与 numpy 中的 np.add 函数,对输入的2个变量相加, 是expr.add的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.add(13, 17)
array(30, dtype=int32)

nonzero(a)

作用等同与 numpy 中的 np.nonzero 函数,输出非零元素的索引。

参数:

  • a:Var 将要搜索的变量

返回:搜索得到的变量

返回类型:tuple of Var

示例

>>> x = np.arange(6).reshape(2,3)
>>> np.nonzero(x>1)
(array([0, 1, 1, 1], dtype=int32), array([2, 0, 1, 2], dtype=int32))

full(shape, fill_value, dtype=None, order='C')

作用等同与 numpy 中的 np.full 函数,创建一个指定形状,类型的Var,其全部的值为指定数据。

参数:

  • shape:[int] 指定输出的形状

  • fill_value:scalar 指定输出的填充值

  • dtype:dtype 指定输出的形状类型; 默认为np.float32

  • order:numpy兼容参数 默认为None

返回:创建的Var

返回类型:Var

示例

>>> np.full((2, 2), 10)
array([[10, 10],
     [10, 10]])

copy(a, order='K', subok=False)

作用等同与 numpy 中的 np.copy 函数,创建一个输入Var的拷贝。

参数:

  • a:Var 输出Var的与该Var相同

  • order:numpy兼容参数 默认为None

  • subok:numpy兼容参数 默认为None

返回:创建的Var

返回类型:Var

示例

>>> a = np.ones([2,2])
>>> np.copy(a)
array([[1., 1.],
     [1., 1.]])

subtract(x1, x2)

作用等同与 numpy 中的 np.subtract 函数,对输入的2个变量相减, 是expr.subtract的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.subtract(2., 3.)
array(-1., dtype=float32)

atleast_1d(*arys)

作用等同与 numpy 中的 np.atleast_1d 函数,将输入Var的维度变为至少为1,如果输入为标量则输出为1维变量,否则保持维度不变。

参数:

  • *arys:Var 多个需要转换的变量

返回:转置后的 MNN.Var

返回类型:Var

示例

>>> np.atleast_1d(3, [2,3])
[array([3], dtype=int32), array([2, 3], dtype=int32)]

nanstd(a, axis=None, dtype=float32, out=None, keepdims=False)

作用等同与 numpy 中的 np.nanstd 函数,返回沿axis轴的标准差。

参数:

  • a:Var 将要统计的变量

  • axis:int 统计的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留计数维度

返回:计数得到的变量

返回类型:float of Var

示例

>>> a = np.arange(4).reshape((2,2))
>>> np.nanstd(a)
1.1180340051651
>>> np.nanstd(a, axis=0) 
array([0.99999994, 0.99999994], dtype=float32)

hsplit(ary, indices_or_sections)

作用等同与 numpy 中的 np.vsplit 函数,沿着axis=0, 将一个Var拆分成为多个子变量。

参数:

  • ary:Var 被拆分的Var

  • indices_or_sections:list 拆分的块数或者块大小

返回:拆分得到的Var

返回类型:Var 数组

示例

>>> x = np.arange(16.0).reshape(2, 2, 4)
>>> np.vsplit(x, 2)
[array([[[0., 1., 2., 3.],
       [4., 5., 6., 7.]]], dtype=float32),
 array([[[ 8.,  9., 10., 11.],
       [12., 13., 14., 15.]]], dtype=float32)]

column_stack(tup)

作用等同与 numpy 中的 np.column_stack 函数,把1维Var作为列stack成2位变量。

参数:

  • tup:Var序列 被连接的变量

返回:创建的Var

返回类型:Var

示例

>>> a = np.array((1,2,3))
>>> b = np.array((2,3,4))
>>> np.column_stack((a,b))
array([[1, 2],
     [2, 3],
     [3, 4]])

dstack(tup)

作用等同与 numpy 中的 np.dstack 函数,深度顺序连接Var序列。

参数:

  • tup:Var序列 被连接的变量

返回:创建的Var

返回类型:Var

示例

>>> a = np.array([[1], [2], [3]])
>>> b = np.array([[4], [5], [6]])
>>> np.dstack((a, b))
array([[[1, 2]],
      [[2, 3]],
      [[3, 4]]])

reciprocal(x)

作用等同与 numpy 中的 np.reciprocal 函数,按位取倒数。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.reciprocal([1.,2.])
array([1. , 0.5], dtype=float32)

positive(x)

作用等同与 numpy 中的 np.positive 函数,按位取正,相当于拷贝。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.positive([1.,-2.])
array([1., -2.], dtype=float32)

array_equal(a1, a2)

作用等同与 numpy 中的 np.array_equiv 函数,判断2个变量是否完全相等。

参数:

  • a1:Var 输入的变量

  • a2:Var 输入的变量

返回:计算得到的变量

返回类型:bool

示例

>>> np.array_equiv([1, 2], [1, 2])
True

square(x)

作用等同与 numpy 中的 np.square 函数,求输入的平方, 是expr.square的封装。

参数:

  • x:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.square(13)
array(160, dtype=int32)

sinh(x)

作用等同与 numpy 中的 np.sinh 函数,按元素计算双曲正弦值。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.sinh([1.,2.])
array([1.1752012, 3.6268604], dtype=float32)

greater_equal(a1, a2)

作用等同与 numpy 中的 np.greater_equal 函数,判断一个变量是否大于等于另一个变量。

参数:

  • a1:Var 输入的变量

  • a2:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.greater_equal([2, 2], [1, 2])
array([1, 1], dtype=int32)

tanh(x)

作用等同与 numpy 中的 np.tanh 函数,按元素计算双曲正切值。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.tanh([1.,2.])
array([0.7616205, 0.9640294], dtype=float32)

floor_divide(x1, x2)

作用等同与 numpy 中的 np.floor_divide 函数,对输入的2个变量相除并舍去小数部分, 是expr.floordiv的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.floor_divide(13., 17.)
array(0., dtype=float32)

matmul(a, b)

作用等同与 numpy 中的 np.matmul 函数,对输入的2个变量做矩阵乘。

参数:

  • a:Var 参与计算的变量

  • b:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.matmul([[1, 0], [0, 1]], [[4, 1], [2, 2]])
array([[4, 1],
     [2, 2]], dtype=int32)

split(ary, indices_or_sections, axis=0)

作用等同与 numpy 中的 np.split 函数,将一个Var拆分成为多个子变量。

参数:

  • ary:Var 被拆分的Var

  • indices_or_sections:list 拆分的块数或者块大小

  • axis:int 拆分的维度

返回:拆分得到的Var

返回类型:Var 数组

示例

>>> x = np.arange(9.0)
>>> np.split(x, 3)
[array([0.,  1.,  2.]), array([3.,  4.,  5.]), array([6.,  7.,  8.])]

argsort(a, axis=-1, kind=None, order=None)

作用等同与 numpy 中的 np.argsort 函数,对输入变量排序,输出排序后的下标索引。

参数:

  • a:Var 将要排序的变量

  • axis:int 排序的维度

  • kind:numpy兼容参数 默认为None

  • order:numpy兼容参数 默认为None

返回:排序后得到的变量

返回类型:Var

示例

>>> a = np.array([[1,4],[3,1]])
>>> np.argsort(a)  
array([[0, 1],
     [1, 0]], dtype=int32)

not_equal(a1, a2)

作用等同与 numpy 中的 np.not_equal 函数,判断一个变量是否不等于另一个变量。

参数:

  • a1:Var 输入的变量

  • a2:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.not_equal([2, 2], [1, 2])
array([1, 0], dtype=int32)

broadcast_arrays(*args)

作用等同与 numpy 中的 np.broadcast_arrays 函数,将输入的变量相互广播。

参数:

  • *args:Var 需要广播的变量

返回:创建的Var

返回类型:Var

示例

>>> x = np.array([[1,2,3]])
>>> y = np.array([[4],[5]])
>>> np.broadcast_arrays(x, y)
[array([[1, 2, 3],
        [1, 2, 3]], dtype=int32),
 array([[4, 4, 4],
        [5, 5, 5]], dtype=int32)]

squeeze(a, axis=None)

作用等同与 numpy 中的 np.squeeze 函数,在指定位置移除一个维度,是expr.squeeze的封装。

参数:

  • x:Var 需要移除维度的变量

  • axis:int 移除维度的位置

返回:创建的Var

返回类型:Var

示例

>>> a = np.array([[[0], [1], [2]]])
>>> b = np.squeeze(a, axis=0)
>>> a.shape
[1,3,1]
>>> b.shape
[3,1]

argmax(a, axis=None, out=None)

作用等同与 numpy 中的 np.argmax 函数,输出沿axis最大值的下标。

参数:

  • a:Var 将要搜索的变量

  • axis:int 搜索的维度

  • out:numpy兼容参数 默认为None

返回:搜索得到的变量

返回类型:int or Var

示例

>>> a = np.array([[1,4],[3,1]])
>>> np.argmax(a)  
1
>>> np.argmax(a, 0)  
array([1, 0], dtype=int32)

minimum(x1, x2)

作用等同与 numpy 中的 np.minimum 函数,输入2个变量中的最小值, 是expr.minimum的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.minimum(13, 17)
array(13, dtype=int32)

signbit(x)

作用等同与 numpy 中的 np.signbit 函数,输入是否有负符号(小于0)。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.signbit([1.,-2.])
array([0, 1], dtype=int32)

around(x)

作用等同与 numpy 中的 np.around 函数,按位进行四舍五入。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.around([1.2, 3.5, 4.7])
array([1., 4., 5.], dtype=float32)

meshgrid(*xi, copy=True, sparse=False, indexing='xy')

作用等同与 numpy 中的 np.meshgrid 根据坐标值返回坐标矩阵。

参数:

  • *xi:Var 一维坐标值

  • copy:bool 是否拷贝所有的值到输出

  • sparse:bool 是否返回全部坐标矩阵

  • indexing:{‘xy’, ‘ij’} ‘ij’}`,’xy’会翻转坐标轴

返回:创建的Var

返回类型:Var

示例

nx, ny = (3, 2)
>>> x = np.linspace(0., 1., nx, False)
>>> y = np.linspace(0., 1., ny, False)
>>> xv, yv = np.meshgrid(x, y)
xv
array([[0.        , 0.33333334, 0.6666667 ],
     [0.        , 0.33333334, 0.6666667 ]], dtype=float32)
yv
array([[0. , 0. , 0. ],
     [0.5, 0.5, 0.5]], dtype=float32)

nanmin(a, axis=None, out=None, keepdims=False)

作用等同与 numpy 中的 np.nanmin 函数,返回沿axis轴的最小值。

参数:

  • a:Var 将要统计的变量

  • axis:int 统计的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留计数维度

返回:计数得到的变量

返回类型:int of Var

示例

>>> a = np.arange(4).reshape((2,2))
>>> np.nanmin(a) 
0
>>> np.nanmin(a, axis=0) 
array([0, 1], dtype=int32)

amin(a, axis=None, out=None, keepdims=False)

作用等同与 numpy 中的 np.amin 函数,返回沿axis轴的最小值。

参数:

  • a:Var 将要统计的变量

  • axis:int 统计的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留计数维度

返回:计数得到的变量

返回类型:int of Var

示例

>>> a = np.arange(4).reshape((2,2))
>>> np.amin(a) 
0
>>> np.amin(a, axis=0) 
array([0, 1], dtype=int32)

sqrt(x)

作用等同与 numpy 中的 np.sin 函数,按元素计算平方根。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.sqrt([1.,2.])
array([0.99999994, 1.4142134 ], dtype=float32)

sum(a, axis=None, out=None, keepdims=False)

作用等同与 numpy 中的 np.sum 函数,沿着指定维度,对数据求和。

参数:

  • x:Var 输入的变量

  • axis:[int] 计算的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留指定轴的维度

返回:计算得到的变量

返回类型:Varorscalar

示例

>>> np.sum([1,2,3,4])
10

nanargmax(a, axis=None, out=None)

作用等同与 numpy 中的 np.nanargmax 函数,输出沿axis最大值的下标。

参数:

  • a:Var 将要搜索的变量

  • axis:int 搜索的维度

  • out:numpy兼容参数 默认为None

返回:搜索得到的变量

返回类型:int or Var

示例

>>> a = np.array([[1,4],[3,1]])
>>> np.nanargmax(a)  
1
>>> np.nanargmax(a, 0)  
array([1, 0], dtype=int32)

log(x)

作用等同与 numpy 中的 np.log 函数,按元素计算log(x)

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.log([1.,2.])
array([0. , 0.6931472], dtype=float32)

floor(x)

作用等同与 numpy 中的 np.floor 函数,按位舍去小数部分。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.floor([1.2, 3.5, 4.7])
array([1., 3., 4.], dtype=float32)

hypot(x1, x2)

作用等同与 numpy 中的 np.hypot 函数,输入2个变量x1x2执行计算sqrt(x1*x1+x2*x2)

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.hypot(13., 17.)
array(21.400934, dtype=float32)

vstack(tup)

作用等同与 numpy 中的 np.vstack 函数,垂直顺序连接Var序列。

参数:

  • tup:Var序列 被连接的变量

返回:创建的Var

返回类型:Var

示例

>>> a = np.array([[1], [2], [3]])
>>> b = np.array([[4], [5], [6]])
>>> np.vstack((a, b))
array([[1],
       [2],
       [3],
       [4],
       [5],
       [6]])

bitwise_or(x1, x2)

作用等同与 numpy 中的 np.bitwise_or 函数,对输入的2个变量按位或, 是expr.bitwise_or的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.bitwise_or(13, 17)
array(29, dtype=int32)

dsplit(ary, indices_or_sections)

作用等同与 numpy 中的 np.dsplit 函数,沿着axis=2, 将一个Var拆分成为多个子变量。

参数:

  • ary:Var 被拆分的Var

  • indices_or_sections:list 拆分的块数或者块大小

返回:拆分得到的Var

返回类型:Var 数组

示例

>>> x = np.arange(16.0).reshape(2, 2, 4)
>>> np.dsplit(x, 2)
[array([[[ 0.,  1.],
         [ 4.,  5.]],
        [[ 8.,  9.],
         [12., 13.]]], dtype=float32),
 array([[[ 2.,  3.],
         [ 6.,  7.]],
        [[10., 11.],
         [14., 15.]]], dtype=float32)]

full_like(a, fill_value, dtype=None, order='K', subok=True, shape=None)

作用等同与 numpy 中的 np.full_like 函数,创建一个与指定Var的形状类型相同的Var,其全部的值为指定数据。

参数:

  • a:Var 输出Var的形状和数据类型与该Var相同

  • fill_value:scalar 指定输出的填充值

  • dtype:dtype 指定输出的形状类型; 默认为np.float32

  • order:numpy兼容参数 默认为None

  • subok:numpy兼容参数 默认为None

  • shape:[int] 重新指定输出的形状

返回:创建的Var

返回类型:Var

示例

>>> a = np.empty([2,2])
>>> np.full_like(a, 10)
array([[10, 10],
       [10, 10]])

identity(n, dtype=float32)

作用等同与 numpy 中的 np.identity 函数,创建一个二维Var, 对角线的值为1,是eye的特殊情况。

参数:

  • n:int 指定输出的行和列

  • dtype:dtype 指定输出的形状类型; 默认为np.float32

返回:创建的Var

返回类型:Var

示例

>>> np.identity(3)
array([[1.,  0.,  0.],
     [0.,  1.,  0.],
     [0.,  0.,  1.]])

eye(N, M=None, k=0, dtype=float32, order='C')

作用等同与 numpy 中的 np.eye 函数,创建一个二维Var, 对角线的值为1。

参数:

  • N:int 指定输出的行

  • M:int 指定输出的列

  • k:int 指定输出对角线位置

  • dtype:dtype 指定输出的形状类型; 默认为np.float32

  • order:numpy兼容参数 默认为None

返回:创建的Var

返回类型:Var

示例

>>> np.eye(3, k=1)
array([[0.,  1.,  0.],
     [0.,  0.,  1.],
     [0.,  0.,  0.]])

absolute(x)

作用等同与 numpy 中的 np.absolute 函数,求输入的绝对值, 是expr.abs的封装。

参数:

  • x:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.absolute([-1, 2])
array([1, 2], dtype=int32)

less(a1, a2)

作用等同与 numpy 中的 np.less 函数,判断一个变量是否小于另一个变量。

参数:

  • a1:Var 输入的变量

  • a2:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.less([2, 2], [1, 2])
array([0, 0], dtype=int32)

repmat(a, m, n)

作用等同与 numpy 中的 np.repmat 根据指定数据,将0维数据复制成为2维(MxN)的Var。

参数:

  • a:ndarray 被复制的数据

  • m:int 复制的行数

  • n:int 复制的列数

返回:创建的Var

返回类型:Var

示例

>>> a = np.arange(3)
>>> np.repmat(a, 2, 3)
array([[0, 1, 2, 0, 1, 2, 0, 1, 2],
     [0, 1, 2, 0, 1, 2, 0, 1, 2]], dtype=int32)

arctan(x)

作用等同与 numpy 中的 np.arctan 函数,按元素计算反正切值。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.arctan([0.5, 1.0])
array([0.4636476, 0.7853982], dtype=float32)

stack(arrays, axis=0, out=None)

作用等同与 numpy 中的 np.stack 函数,沿指定轴连接输入的Var连接轴会创建一个新的维度。

参数:

  • args:Var序列 被连接的变量

  • axis:int 指定连接的轴

  • out:numpy兼容参数 默认为None

返回:创建的Var

返回类型:Var

示例

>>> a = np.array([1, 2, 3])
>>> b = np.array([4, 5, 6])
>>> np.stack((a, b))
array([[1, 2, 3],
     [4, 5, 6]])

rint(x)

作用等同与 numpy 中的 np.rint 函数,按位进行四舍五入。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.rint([1.2, 3.5, 4.7])
array([1., 4., 5.], dtype=float32)

sort(a, axis=-1, kind=None, order=None)

作用等同与 numpy 中的 np.sort 函数,对输入变量排序。

参数:

  • a:Var 将要排序的变量

  • axis:int 排序的维度

  • kind:numpy兼容参数 默认为None

  • order:numpy兼容参数 默认为None

返回:排序后得到的变量

返回类型:Var

示例

>>> a = np.array([[1,4],[3,1]])
>>> np.sort(a)  
array([[1, 4],
       [1, 3]], dtype=int32)

arctan2(x1, x2)

作用等同与 numpy 中的 np.arctan2 函数,按元素计算输入比值的反正切值。

参数:

  • x1:Var 输入的变量

  • x2:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.arctan2(2., 3.)
array(0.5880026, dtype=float32)

logaddexp(x1, x2)

作用等同与 numpy 中的 np.logaddexp 函数,输入2个变量x1x2执行计算log(exp(x1) + exp(x2))

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.logaddexp(2, 3)
array(3.3132498, dtype=float32)

swapaxes(a, axis1, axis2)

作用等同与 numpy 中的 np.swapaxes 函数,交换Var的两个指定维度。

参数:

  • a:Var 将会交换该变量的维度

  • axis1:int 交换的维度1

  • axis2:int 交换的维度2

返回:交换维度后的 MNN.Var

返回类型:Var

示例

>>> a = np.ones((3, 4, 5))
>>> np.swapaxes(a, 0, 2).shape
[5, 4, 3]

less_equal(a1, a2)

作用等同与 numpy 中的 np.less_equal 函数,判断一个变量是否小于等于另一个变量。

参数:

  • a1:Var 输入的变量

  • a2:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.less_equal([2, 2], [1, 2])
array([0, 1], dtype=int32)

msort(a)

作用等同与 numpy 中的 np.msort 函数,沿axis=0对输入变量排序。

参数:

  • a:Var 将要排序的变量

返回:排序后得到的变量

返回类型:Var

示例

>>> a = np.array([[1,4],[3,1]])
>>> np.msort(a)  
array([[1, 1],
       [3, 4]], dtype=int32)

logspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)

作用等同与 numpy 中的 np.logspace 根据指定数据,类型创建一个指定范围(对数等差)和数目的Var。

参数:

  • start:int|float 输出变量的范围起点

  • stop:int|float 输出变量的范围终点

  • num:int 输出数据总数

  • endpoint:bool 是否包含终点元素,目前仅支持endpoint=False

  • retstep:(Var, step) 返回值是否包含步长;如果包含则返回(Var, step)

  • dtype:dtype 指定输出的类型; 默认为stop的类型

  • axis:numpy兼容参数 默认为None

返回:创建的Var

返回类型:Var

示例

>>> np.logspace(0., 10., 5, False)
array([1.e+00, 1.e+02, 1.e+04, 1.e+06, 1.e+08], dtype=float32)

array_equal(a1, a2, equal_nan=False)

作用等同与 numpy 中的 np.array_equal 函数,判断2个变量是否完全相等。

参数:

  • a1:Var 输入的变量

  • a2:Var 输入的变量

  • equal_nan:numpy兼容参数 默认为None

返回:计算得到的变量

返回类型:bool

示例

>>> np.array_equal([1, 2], [1, 2])
True

nanvar(a, axis=None, dtype=float32, out=None, keepdims=False)

作用等同与 numpy 中的 np.nanvar 函数,返回沿axis轴的方差。

参数:

  • a:Var 将要统计的变量

  • axis:int 统计的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留计数维度

返回:计数得到的变量

返回类型:float of Var

示例

>>> a = np.arange(4).reshape((2,2))
>>> np.nanvar(a)
1.25
>>> np.nanvar(a, axis=0) 
array([1., 1.], dtype=float32)

row_stack(tup)

作用等同与 numpy 中的 np.row_stack 函数,垂直顺序连接Var序列,与vstack相同。

参数:

  • tup:Var序列 被连接的变量

返回:创建的Var

返回类型:Var

示例

>>> a = np.array([[1], [2], [3]])
>>> b = np.array([[4], [5], [6]])
>>> np.row_stack((a, b))
array([[1],
       [2],
       [3],
       [4],
       [5],
       [6]])

negative(x)

作用等同与 numpy 中的 np.negative 函数,按位取负。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.negative([1.,-2.])
array([-1., 2.], dtype=float32)

tan(x)

作用等同与 numpy 中的 np.tan 函数,按元素计算正切值。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.tan([1.,2.])
array([ 1.5574077, -2.1850398], dtype=float32)

any(a, axis=None, out=None, keepdims=None)

作用等同与 numpy 中的 np.any 函数,判断是否所有元素存在真(不为0)。

参数:

  • a:Var 输入的变量

  • axis:list 判断的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留指定轴的维度

返回:计算得到的变量

返回类型:Varorscalar

示例

>>> np.any([1, 0, 1])
True
>>> np.all([0, 0, 0])
False

arccosh(x)

作用等同与 numpy 中的 np.arccosh 函数,按元素计算双曲反余弦值。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.arccosh([2.0, 3.0])
array([1.316958 , 1.7627472], dtype=float32)

exp(x)

作用等同与 numpy 中的 np.exp 函数,按元素计算exp(x)

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.exp([1.,2.])
array([2.7182794, 7.388731 ], dtype=float32)

ldexp(x1, x2)

作用等同与 numpy 中的 np.ldexp 函数,输入2个变量x1x2执行计算x1 * exp2(x2)

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.ldexp(2., 3.)
array(16., dtype=float32)

prod(a, axis=None, out=None, keepdims=False)

作用等同与 numpy 中的 np.prod 函数,沿着指定维度,对数据求乘积。

参数:

  • x:Var 输入的变量

  • axis:list 计算的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留指定轴的维度

返回:计算得到的变量

返回类型:Varorscalar

示例

>>> np.prod([1,2,3,4])
24

ravel(a, order='C')

作用等同与 numpy 中的 np.ravel 函数,改变输入Var的形状为一维。

参数:

  • a:Var 将会改变该变量的形状

  • order:numpy兼容参数 默认为None

返回:形状为1维的MNN.Var

返回类型:Var

示例

>>> a = np.ones([[2],[2]])
>>> np.ravel(a)
array([1, 1, 1, 1], dtype=int32)

true_divide(x1, x2)

作用等同与 numpy 中的 np.true_divide 函数,对输入的2个变量相除, 是expr.divide的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.true_divide(13., 17.)
array(0.7647059, dtype=float32)

exp2(x)

作用等同与 numpy 中的 np.exp2 函数,按元素计算2**x

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.exp2([1.,2.])
array([2., 4.], dtype=float32)

fmax(x1, x2)

作用等同与 numpy 中的 np.fmax 函数,输入2个变量中的最大值, 是expr.maximum的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.fmax(13., 17.)
array(17., dtype=float32)

fmod(x1, x2)

作用等同与 numpy 中的 np.fmod 函数,对输入的2个变量求模, 是expr.mod的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.fmod(13., 5.)
array(3., dtype=float32)

mean(a, axis=None, dtype=float32, out=None, keepdims=False)

作用等同与 numpy 中的 np.mean 函数,返回沿axis轴的平均值。

参数:

  • a:Var 将要统计的变量

  • axis:int 统计的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留计数维度

返回:计数得到的变量

返回类型:float of Var

示例

>>> a = np.arange(4).reshape((2,2))
>>> np.mean(a)
1.5
>>> np.mean(a, axis=0) 
array([1., 2.], dtype=float32)

max(a, axis=None, out=None, keepdims=False)

作用等同与 numpy 中的 np.max 函数,返回沿axis轴的最大值。

参数:

  • a:Var 将要统计的变量

  • axis:int 统计的轴

  • out:numpy兼容参数 默认为None

  • keepdims:bool 是否保留计数维度

返回:计数得到的变量

返回类型:int of Var

示例

>>> a = np.arange(4).reshape((2,2))
>>> np.max(a) 
3
>>> np.max(a, axis=0) 
array([2, 3], dtype=int32)

mod(x1, x2)

作用等同与 numpy 中的 np.mod 函数,对输入的2个变量求模, 是expr.mod的封装。

参数:

  • x1:Var 参与计算的变量

  • x2:Var 参与计算的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.mod(13, 5)
array(3, dtype=int32)

asmatrix(a, dtype=None)

作用等同与 numpy 中的 np.asmatrix 根据指定数据,将输入数据转换为ndim=2的Var。

参数:

  • a:ndarray 输出变量的数据来源

  • dtype:dtype 重新指定输出的类型; 默认为a的数据类型

返回:创建的Var

返回类型:Var

示例

>>> np.asmatrix([1, 2, 3, 4])
array([[1, 2, 3]])

fix(x)

作用等同与 numpy 中的 np.fix 函数,按位进行四舍五入。

参数:

  • x:Var 输入的变量

返回:计算得到的变量

返回类型:Var

示例

>>> np.fix([1.2, 3.5, 4.7])
array([1., 4., 5.], dtype=float32)

hstack(tup)

作用等同与 numpy 中的 np.hstack 函数,水平顺序连接Var序列。

参数:

  • tup:Var序列 被连接的变量

返回:创建的Var

返回类型:Var

示例

>>> a = np.array([[1], [2], [3]])
>>> b = np.array([[4], [5], [6]])
>>> np.hstack((a, b))
array([[1, 4],
     [2, 5],
     [3, 6]])

transpose(a, axes=None)

作用等同与 numpy 中的 np.transpose 函数,按照指定维度转置Var

参数:

  • a:Var 将转置该变量的维度

  • axes:tuple|list 转置的维度顺序

返回:转置后的 MNN.Var

返回类型:Var

示例

>>> a = np.ones((3, 4, 5))
>>> np.transpose(a).shape
[5, 4, 3]

cv

module cv

cv模块提供了基础的图像处理函数,并在接口上兼容了opencv-python的API。

使用注意:

  • 图像描述使用Var变量,其属性为:

    • data_formatNHWC

    • shape[h, w, c]

    • dtypeuint8

  • CV模块中的枚举类型直接用int实现,所以请使用cv.COLOR_BGR2BGRA,不要用cv.ColorConversionCodes.COLOR_BGR2BGRA*

  • [h, w, c]的图形转换为模型输入的[n, c, h, w]不要使用transpose;请使用expr.convert,示例如下:

    import MNN.cv as cv
    import MNN.numpy as np
    import MNN.expr as expr
    # data_format: NHWC, shape: [360, 480, 3], dtype: uint8
    img = imread('cat.jpg')
    # data_format: NHWC, shape: [360, 480, 3], dtype: float32
    imgf = img.astype(np.float32)
    # data_format: NHWC, shape: [1, 360, 480, 3], dtype: float32
    imgf_batch = np.expand_dims(imgf, 0)
    # data_format: NCHW, shape: [1, 360, 480, 3], dtype: float32
    input_var = expr.convert(imgf_batch, expr.NCHW)
    

cv Types


cv.COLOR_*

描述图像颜色空间转换函数cvtColor的转换方式

  • 类型:int

  • 枚举值:

    • COLOR_BGR2BGRA

    • COLOR_RGB2RGBA

    • COLOR_BGRA2BGR

    • COLOR_RGBA2RGB

    • COLOR_BGR2RGBA

    • COLOR_RGB2BGRA

    • COLOR_RGBA2BGR

    • COLOR_BGRA2RGB

    • COLOR_BGR2RGB

    • COLOR_RGB2BGR

    • COLOR_BGRA2RGBA

    • COLOR_RGBA2BGRA

    • COLOR_BGR2GRAY

    • COLOR_RGB2GRAY

    • COLOR_GRAY2BGR

    • COLOR_GRAY2RGB

    • COLOR_GRAY2BGRA

    • COLOR_GRAY2RGBA

    • COLOR_BGRA2GRAY

    • COLOR_RGBA2GRAY

    • COLOR_BGR2BGR565

    • COLOR_RGB2BGR565

    • COLOR_BGR5652BGR

    • COLOR_BGR5652RGB

    • COLOR_BGRA2BGR565

    • COLOR_RGBA2BGR565

    • COLOR_BGR5652BGRA

    • COLOR_BGR5652RGBA

    • COLOR_GRAY2BGR565

    • COLOR_BGR5652GRAY

    • COLOR_BGR2BGR555

    • COLOR_RGB2BGR555

    • COLOR_BGR5552BGR

    • COLOR_BGR5552RGB

    • COLOR_BGRA2BGR555

    • COLOR_RGBA2BGR555

    • COLOR_BGR5552BGRA

    • COLOR_BGR5552RGBA

    • COLOR_GRAY2BGR555

    • COLOR_BGR5552GRAY

    • COLOR_BGR2XYZ

    • COLOR_RGB2XYZ

    • COLOR_XYZ2BGR

    • COLOR_XYZ2RGB

    • COLOR_BGR2YCrCb

    • COLOR_RGB2YCrCb

    • COLOR_YCrCb2BGR

    • COLOR_YCrCb2RGB

    • COLOR_BGR2HSV

    • COLOR_RGB2HSV

    • COLOR_BGR2Lab

    • COLOR_RGB2Lab

    • COLOR_BGR2Luv

    • COLOR_RGB2Luv

    • COLOR_BGR2HLS

    • COLOR_RGB2HLS

    • COLOR_HSV2BGR

    • COLOR_HSV2RGB

    • COLOR_Lab2BGR

    • COLOR_Lab2RGB

    • COLOR_Luv2BGR

    • COLOR_Luv2RGB

    • COLOR_HLS2BGR

    • COLOR_HLS2RGB

    • COLOR_BGR2HSV_FULL

    • COLOR_RGB2HSV_FULL

    • COLOR_BGR2HLS_FULL

    • COLOR_RGB2HLS_FULL

    • COLOR_HSV2BGR_FULL

    • COLOR_HSV2RGB_FULL

    • COLOR_HLS2BGR_FULL

    • COLOR_HLS2RGB_FULL

    • COLOR_LBGR2Lab

    • COLOR_LRGB2Lab

    • COLOR_LBGR2Luv

    • COLOR_LRGB2Luv

    • COLOR_Lab2LBGR

    • COLOR_Lab2LRGB

    • COLOR_Luv2LBGR

    • COLOR_Luv2LRGB

    • COLOR_BGR2YUV

    • COLOR_RGB2YUV

    • COLOR_YUV2BGR

    • COLOR_YUV2RGB

    • COLOR_YUV2RGB_NV12

    • COLOR_YUV2BGR_NV12

    • COLOR_YUV2RGB_NV21

    • COLOR_YUV2BGR_NV21

    • COLOR_YUV420sp2RGB

    • COLOR_YUV420sp2BGR

    • COLOR_YUV2RGBA_NV12

    • COLOR_YUV2BGRA_NV12

    • COLOR_YUV2RGBA_NV21

    • COLOR_YUV2BGRA_NV21

    • COLOR_YUV420sp2RGBA

    • COLOR_YUV420sp2BGRA

    • COLOR_YUV2RGB_YV12

    • COLOR_YUV2BGR_YV12

    • COLOR_YUV2RGB_IYUV

    • COLOR_YUV2BGR_IYUV

    • COLOR_YUV2RGB_I420

    • COLOR_YUV2BGR_I420

    • COLOR_YUV420p2RGB

    • COLOR_YUV420p2BGR

    • COLOR_YUV2RGBA_YV12

    • COLOR_YUV2BGRA_YV12

    • COLOR_YUV2RGBA_IYUV

    • COLOR_YUV2BGRA_IYUV

    • COLOR_YUV2RGBA_I420

    • COLOR_YUV2BGRA_I420

    • COLOR_YUV420p2RGBA

    • COLOR_YUV420p2BGRA

    • COLOR_YUV2GRAY_420

    • COLOR_YUV2GRAY_NV21

    • COLOR_YUV2GRAY_NV12

    • COLOR_YUV2GRAY_YV12

    • COLOR_YUV2GRAY_IYUV

    • COLOR_YUV2GRAY_I420

    • COLOR_YUV420sp2GRAY

    • COLOR_YUV420p2GRAY

    • COLOR_YUV2RGB_UYVY

    • COLOR_YUV2BGR_UYVY

    • COLOR_YUV2RGB_Y422

    • COLOR_YUV2BGR_Y422

    • COLOR_YUV2RGB_UYNV

    • COLOR_YUV2BGR_UYNV

    • COLOR_YUV2RGBA_UYVY

    • COLOR_YUV2BGRA_UYVY

    • COLOR_YUV2RGBA_Y422

    • COLOR_YUV2BGRA_Y422

    • COLOR_YUV2RGBA_UYNV

    • COLOR_YUV2BGRA_UYNV

    • COLOR_YUV2RGB_YUY2

    • COLOR_YUV2BGR_YUY2

    • COLOR_YUV2RGB_YVYU

    • COLOR_YUV2BGR_YVYU

    • COLOR_YUV2RGB_YUYV

    • COLOR_YUV2BGR_YUYV

    • COLOR_YUV2RGB_YUNV

    • COLOR_YUV2BGR_YUNV

    • COLOR_YUV2RGBA_YUY2

    • COLOR_YUV2BGRA_YUY2

    • COLOR_YUV2RGBA_YVYU

    • COLOR_YUV2BGRA_YVYU

    • COLOR_YUV2RGBA_YUYV

    • COLOR_YUV2BGRA_YUYV

    • COLOR_YUV2RGBA_YUNV

    • COLOR_YUV2BGRA_YUNV

    • COLOR_YUV2GRAY_UYVY

    • COLOR_YUV2GRAY_YUY2

    • COLOR_YUV2GRAY_Y422

    • COLOR_YUV2GRAY_UYNV

    • COLOR_YUV2GRAY_YVYU

    • COLOR_YUV2GRAY_YUYV

    • COLOR_YUV2GRAY_YUNV

    • COLOR_RGBA2mRGBA

    • COLOR_mRGBA2RGBA

    • COLOR_RGB2YUV_I420

    • COLOR_BGR2YUV_I420

    • COLOR_RGB2YUV_IYUV

    • COLOR_BGR2YUV_IYUV

    • COLOR_RGBA2YUV_I420

    • COLOR_BGRA2YUV_I420

    • COLOR_RGBA2YUV_IYUV

    • COLOR_BGRA2YUV_IYUV

    • COLOR_RGB2YUV_YV12

    • COLOR_BGR2YUV_YV12

    • COLOR_RGBA2YUV_YV12

    • COLOR_BGRA2YUV_YV12


cv.INTER_*

描述图像形变函数resize,warpAffine,warpPerspective的插值方式

  • 类型:int

  • 枚举值:

    • INTER_NEAREST

    • INTER_LINEAR

    • INTER_CUBIC

    • INTER_AREA

    • INTER_LANCZOS4

    • INTER_LINEAR_EXACT

    • INTER_NEAREST_EXACT

    • WARP_FILL_OUTLIERS

    • WARP_INVERSE_MAP


cv.BORDER_*

描述图像形变函数warpAffine,warpPerspective的边界填充方式

  • 类型:int

  • 枚举值:

    • BORDER_CONSTANT

    • BORDER_REFLECT_101

    • BORDER_REFLECT

    • BORDER_REFLECT101

    • BORDER_DEFAULT


cv.THRESH_*

描述阈值函数threshold的阈值方式

  • 类型:int

  • 枚举值:

    • THRESH_BINARY

    • THRESH_BINARY_INV

    • THRESH_TRUNC

    • THRESH_TOZERO

    • THRESH_TOZERO_INV

    • THRESH_MASK

    • THRESH_OTSU

    • THRESH_TRIANGLE


cv.RETR_*

描述轮廓检测函数findContours的轮廓检索方式

  • 类型:int

  • 枚举值:

    • RETR_EXTERNAL

    • RETR_LIST

    • RETR_CCOMP

    • RETR_TREE

    • RETR_FLOODFILL


cv.CHAIN_*

描述轮廓检测函数findContours的轮廓逼近算法

  • 类型:int

  • 枚举值:

    • CHAIN_APPROX_NONE

    • CHAIN_APPROX_SIMPLE

    • CHAIN_APPROX_TC89_L1

    • CHAIN_APPROX_TC89_KCOS


cv.LINE_*

用在画图相关函数,如:line, fillPoly等,描述画线的类型

  • 类型:int

    • FILLED

    • LINE_4

    • LINE_8

    • LINE_AA


cv.IMREAD_*

用在图片读取函数imread的参数flag中,分别表示读取:uint8灰度图,uint8的bgr图,float32的bgr图

  • 类型:int

    • IMREAD_GRAYSCALE

    • IMREAD_COLOR

    • IMREAD_ANYDEPTH


cv.ROTATE_*

描述图像旋转函数rotate的旋转方式

  • 类型:int

    • ROTATE_90_CLOCKWISE

    • ROTATE_180

    • ROTATE_90_COUNTERCLOCKWISE


cv.SOLVEPNP_*

描述3d重建函数solvePnP的求解方法

  • 类型:int

    • SOLVEPNP_ITERATIVE

    • SOLVEPNP_SQPNP


cv.DECOMP_*

描述线性方程组求解函数solve的求解方法

  • 类型:int

    • DECOMP_LU

    • DECOMP_SVD

    • DECOMP_EIG

    • DECOMP_CHOLESKY

    • DECOMP_QR

    • DECOMP_NORMAL


cv.NORM_*

描述线归一化函数normalize的归一化方法

  • 类型:int

    • NORM_INF

    • NORM_L1

    • NORM_L2

    • NORM_MINMAX


cv.ADAPTIVE_THRESH_*

描述自适应阈值函数adaptiveThreshold的自适应方法

  • 类型:int

    • ADAPTIVE_THRESH_MEAN_C

    • ADAPTIVE_THRESH_GAUSSIAN_C


copyTo(src, |mask, dst)

将src复制并返回,如果mask不为空,则只拷贝mask为1的像素;如果dst不为空,则在mask为0时拷贝dst中对应的像素,参考:copyTo

注意:目前src仅支持int32类型数据,用户使用前后需要自行转换类型

参数:

  • src:Var 源图像

  • mask:Var 掩码图像,可选

  • dst:Var mask为0时选择的图像,可选

返回:复制的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> h, w, _ = img.shape
>>> zero = np.zeros((h//3, w), dtype=np.int32)
>>> one = np.ones((h//3, w), dtype=np.int32)
>>> mask = np.concatenate((one, zero, one), axis=0)
>>> img = img.astype(np.int32)
>>> copyTo = cv.copyTo(img, mask).astype(np.uint8)
>>> cv.imwrite('copyTo.jpg', copyTo)
True

_images/copyTo.jpgcopyTo.jpg


bitwise_and(src1, src2, |dst, mask)

对src1和src2执行按位与操作,并对结果按照执行copyTo返回,参考:bitwise_and

参数:

  • src1:Var 源图像

  • src2:Var 源图像

  • mask:Var 掩码图像,可选

  • dst:Var mask为0时选择的图像,可选

返回:按位与的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> cv.bitwise_and(img, img)
array([[[ 49,  57,  26],
        ...
        [158, 175, 184]]], dtype=uint8)

bitwise_or(src1, src2, |dst, mask)

对src1和src2执行按位或操作,并对结果按照执行copyTo返回,参考:bitwise_or

参数:

  • src1:Var 源图像

  • src2:Var 源图像

  • mask:Var 掩码图像,可选

  • dst:Var mask为0时选择的图像,可选

返回:按位或的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> cv.bitwise_or(img, img)
array([[[ 49,  57,  26],
        ...
        [158, 175, 184]]], dtype=uint8)

bitwise_xor(src1, src2, |dst, mask)

对src1和src2执行按位异或操作,并对结果按照执行copyTo返回,参考:bitwise_xor

参数:

  • src1:Var 源图像

  • src2:Var 源图像

  • mask:Var 掩码图像,可选

  • dst:Var mask为0时选择的图像,可选

返回:按位异或的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> cv.bitwise_xor(img, img)
array([[[0, 0, 0],
        ...
        [0, 0, 0]]], dtype=uint8)

hconcat(src)

在水平方向上将src中的图像连接起来,并返回,相当于做axis=1的concat,参考:hconcat

参数:

  • src:Var 源图像

返回:水平连接的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> cv.hconcat(img)
>>> cv.hconcat(img)
array([[ 49,  57,  26, ...,  25,  62,  46],
       ...,
       [ 45,  94,  56, ..., 158, 175, 184]], dtype=uint8)

vconcat(src)

在垂直方向上将src中的图像连接起来,并返回,相当于做axis=0的concat,参考:vconcat

参数:

  • src:Var 源图像

返回:垂直连接的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> cv.vconcat(img)
array([[ 49,  57,  26],
       ...,
       [158, 175, 184]], dtype=uint8)

mean(src, mask)

逐channel计算src的元素均值,如果mask不为空,则只返回mask为1的结果,参考:mean

参数:

  • src:Var 源图像

返回:每个channel的均值

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> cv.mean(img)
array([ 85.656685, 135.9716, 125.76543,   0.], dtype=float32)

flip(src, flipCode)

对src进行水平,垂直,或水平+垂直翻转,并返回,参考:flip

flipCode 说明
filpCode = 0 垂直翻转
flipCode > 0 水平翻转
flipCode < 0 水平+垂直翻转

参数:

  • src:Var 源图像

返回:翻转的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> flip = cv.flip(img, -1)
>>> cv.imwrite('flip.jpg', flip)
True

_images/flip.jpgflip.jpg


rotate(src, rotateMode)

以90度的倍数旋转src,并返回,参考:rotate

rotateCode 说明
ROTATE_90_CLOCKWISE 顺时针旋转90度
ROTATE_180 顺时针旋转180度
ROTATE_90_COUNTERCLOCKWISE 顺时针旋转270度

参数:

  • src:Var 源图像

返回:翻转的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> rotate = cv.rotate(img, cv.ROTATE_90_CLOCKWISE)
>>> cv.imwrite('rotate.jpg', rotate)
True

_images/rotate.jpgrotate.jpg


solve(src1, src2, |method)

求解线性方程组,目前仅实现了LU方法;参考:solve

参数:

  • src1:Var 线性方程组左侧矩阵

  • src2:Var 线性方程组右侧矩阵

  • method:int 求解方法,可选;默认为cv.DECOMP_LU (目前仅实现了LU方法)

返回:能否求解,求解获得的矩阵

返回类型:Tuple(bool, Var)

示例:

>>> a = np.array([2., 3., 4., 0., 1., 5., 0., 0., 3.]).reshape(3, 3)
>>> b = np.array([1., 2., 3.]).reshape(3, 1)
>>> cv.solve(a, b)
(True, array([[ 3.],
              [-3.],
              [ 1.]], dtype=float32))

normalize(src, dst, alpha, beta, norm_type, |dtype, mask)

对输入进行归一化;参考:normalize

参数:

  • src:Var 输入矩阵

  • dst:Var Python中不需要使用该参数,直接赋为None即可

  • alpha:float 归一化的下限

  • beta:float 归一化的上限

  • norm_type:int 归一化类型,如:cv.NORM_MINMAX

  • dtype:dtype 输入类型,不需要赋值

  • mask 兼容性参数,目前还不支持mask

返回:归一化结果

返回类型:Var

示例:

>>> x = np.arange(12).reshape(2, 2, 3).astype(np.uint8)
>>> cv.normalize(x, None, -50, 270, cv.NORM_MINMAX)
array([[[  0,   0,   8],
        [ 37,  66,  95]],

       [[125, 154, 183],
        [212, 241, 255]]], dtype=uint8)

merge(mv)

将多张图片沿channel合并;参考:merge

参数:

  • mv:[Var] 输入矩阵数组

返回:合并结果矩阵

返回类型:Var

示例:

>>> x = np.arange(9).reshape(3, 3)
>>> cv.merge([x, x])
array([[[0, 0],
        [1, 1],
        [2, 2]],

       [[3, 3],
        [4, 4],
        [5, 5]],

       [[6, 6],
        [7, 7],
        [8, 8]]], dtype=int32)

split(m)

将图片沿channel方向拆分;参考:split

参数:

  • m:Var 待拆分图片

返回:拆分出的图片

返回类型:[Var]

示例:

>>> x = np.arange(12).reshape(2, 2, 3)
>>> cv.split(x)
[array([[0, 3],[6, 9]], dtype=int32),
 array([[1, 4],[7, 10]], dtype=int32),
 array([[2, 5],[8, 11]], dtype=int32)]

addWeighted(src1, alpha, src2, beta, gamma)

对输入的两个矩阵执行权重相加:dst = src1 * alpha + src2 * beta + gamma;参考:addWeighted

参数:

  • src1:Var 第一个输入矩阵

  • alpha:float 第一个输入矩阵的权重

  • src2:Var 第二个输入矩阵

  • beta:float 第二个输入矩阵的权重

  • gamma:float 额外增加的常量

返回:加权得到的和

返回类型:Var

示例:

>>> x = np.arange(3.)
>>> cv.addWeighted(x, 0.2, x, 0.5, 1)
array([1. , 1.7, 2.4], dtype=float32)

haveImageReader(filename)

用于判断是否支持特定图像格式的解码,目前支持的图像格式:jpg, jpeg, png, bmp,参考:haveImageReader

移动端默认不包含该函数

参数:

  • filename:str 图像文件路径

返回:是否有读取图像的接口

返回类型:bool

示例:

>>> cv.haveImageReader('cat.jpg')
True

haveImageWriter(filename)

用于判断是否支持特定图像格式的编码,目前支持的图像格式:jpg, jpeg, png, bmp,参考:haveImageWriter

移动端默认不包含该函数

参数:

  • filename:str 图像文件路径

返回:是否有写图像的接口

返回类型:bool

示例:

>>> cv.haveImageWriter('cat.jpg')
True

imdecode(buf, |flag)

将内存数据解码为图像,并返回,参考:imdecode

移动端默认不包含该函数

参数:

  • buf:ndarray|sequence 图像数据序列,可以是ndarray, list, tuple, bytes等

  • flag:int 解码方式,可选,默认为cv2.IMREAD_COLOR

返回:解码后的图像

返回类型:Var

示例:

>>> cv.imdecode(bytearray(open('cat.jpg', 'rb').read()), cv.IMREAD_COLOR)
array([[[ 49,  57,  26],
        [ 50,  58,  27],
        [ 47,  55,  25],
        ...,
        [188, 205, 214],
        [158, 175, 184],
        [158, 175, 184]]], dtype=uint8)

imencode(ext, img, |params)

将图像编码为图像数据,并返回,参考:imencode

移动端默认不包含该函数

参数:

  • ext:str 图像文件扩展名,如jpg, png等

  • img:Var 图像

  • params:[int] 编码参数,可选,默认为[cv2.IMWRITE_JPEG_QUALITY, 95]

返回:编码后的图像数据序列,first是bool代表是否编码成功,second是listofuint8代表编码后的图像数据序列

返回类型:pair

示例:

>>> success, buf = cv.imencode('jpg', cv.imread('cat.jpg'))
>>> success
True
>>> buf[:10]
[255, 216, 255, 224, 0, 16, 74, 70, 73, 70]

imread(filename, |flag)

读取图像,并返回,参考:imread

移动端默认不包含该函数

参数:

  • filename:str 图像文件路径

  • flag:int 读取方式,可选,默认为cv2.IMREAD_COLOR

返回:读取的图像

返回类型:Var

示例:

>>> cv.imread('cat.jpg')
array([[[ 49,  57,  26],
        [ 50,  58,  27],
        [ 47,  55,  25],
        ...,
        [188, 205, 214],
        [158, 175, 184],
        [158, 175, 184]]], dtype=uint8)

_images/cat.jpgcat.jpg


imwrite(filename, img, |params)

将图像写入文件,参考:imwrite

移动端默认不包含该函数

参数:

  • filename:str 图像文件写的路径

  • img:Var 图像对象

  • params:[int] 编码参数,可选,默认为[cv2.IMWRITE_JPEG_QUALITY, 95]

返回:是否写入成功

返回类型:bool

示例:

>>> img = cv.imread('cat.jpg')
>>> cv.imwrite('write.jpg', img)
True

Rodrigues(src)

将旋转矩阵转换为旋转向量,在solvePnP的返回值前会被使用,参考:Rodrigues

该函数只支持旋转矩阵到旋转向量,反之不支持

参数:

  • src:Var 旋转矩阵

返回:旋转向量

返回类型:Var

示例:

>>> cv.Rodrigues(np.array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]))
array([[0.],
       [0.],
       [0.]], dtype=float32)

solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs, |flags)

根据输入的 3d坐标集合和2d坐标集合,相机内参和平移矩阵,计算3d坐标到2d坐标的映射关系,并返回旋转矩阵和平移矩阵,参考:solvePnP

目前仅支持SOLVEPNP_SQPNP

参数:

  • objectPoints:Var 3d坐标集合, shape为(n,3),n为点的个数

  • imagePoints:Var 2d坐标集合, shape为(n,2),n为点的个数

  • cameraMatrix:Var 相机内参矩阵, shape为(3,3)

  • distCoeffs:Var 相机畸变系数, shape为(1,5)或(5,), 不使用可以传入[]

  • flags:int 标志位,可选,做兼容性处理,目前仅支持SOLVEPNP_SQPNP

返回:返回值tuple中有3个值,第一个值为bool是否找到变换关系,第二个值为Var是旋转向量,第三个值为Var是平移矩阵

返回类型:tuple

示例:

>>> model_points = np.array([0.0, 0.0, 0.0, 0.0, -330.0, -65.0, -225.0, 170.0, -135.0, 225.0, 170.0, -135.0, -150.0, -150.0, -125.0, 150.0, -150.0, -125.0]).reshape(6, 3)
>>> image_points = np.array([359., 391., 399., 561., 337., 297., 513., 301., 345., 465., 453., 469.]).reshape(6, 2)
>>> camera_matrix = np.array([1200., 0., 600., 0., 1200., 337.5, 0., 0., 1.]).reshape(3, 3)
>>> dist_coeffs = np.array([0.0, 0.0, 0.0, 0.0]).reshape(4, 1)
>>> cv.solvePnP(model_points, image_points, camera_matrix, dist_coeffs, flags=cv.SOLVEPNP_SQPNP)
(True, array([[ 3.000745  ],
       [ 0.03165916],
       [-0.9225616 ]], dtype=float32), array([[-435.97495],
       [  95.3929 ],
       [2201.46   ]], dtype=float32))

cvtColor(src, code, |dstCn)

将图像转换为另一种颜色空间,参考: cvtColor

如果src为YUV图像请使用cvtColorTwoPlane

参数:

  • src:Var 输入图像

  • code:int 转换方式,使用cv.COLOR_*

  • dstCn:int 转换后图像的通道数,可选,默认为原图像的通道数

返回:转换后的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')         # bgr
>>> cv.cvtColor(img, cv.COLOR_BGR2GRAY) # gray
>>> gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
>>> cv.imwrite('cvtColor.jpg', gray) 
True

_images/cvtColor.jpgcvtColor.jpg


cvtColorTwoPlane(src1, src2, code)

将图像转换为另一种颜色空间,源图像存储在两个平面中,一般用于YUV_NV21和YUV_NV12到其他颜色空间的转换,参考: cvtColorTwoPlane

参数:

  • src1:Var 图像的第一个平面

  • src2:Var 图像的第二个平面

  • code:int 转换方式,使用cv.COLOR_*

返回:转换后的图像

返回类型:Var

示例:

>>> h = w = 224
>>> y = np.random.randint(0, 255, h * w).reshape(h, w).astype(np.uint8)
>>> uv = np.random.randint(0, 255, h * w / 2).astype(np.uint8)
>>> rgb = cv.cvtColorTwoPlane(y, uv, cv.COLOR_YUV2RGB_NV21)
>>> cv.imwrite('cvtColorTwoPlane.jpg', rgb)
True

_images/cvtColorTwoPlane.jpgcvtColorTwoPlane.jpg


bilateralFilter(src, d, sigmaColor, sigmaSpace, |borderType)

双边滤波,直接实现未优化,速度较慢;参考: bilateralFilter

参数:

  • src:Var 输入图像

  • d:int 滤波时考虑周围像素的直径,如果为负数则通过sigmaSpace计算

  • sigmaColor:float 颜色空间sigma值

  • sigmaSpace:float 坐标空间sigma值

  • borderType:int 边界模式,可选值;默认为REFLECT

返回:滤波后的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> img = cv.bilateralFilter(img, 20, 80.0, 35.0)
>>> cv.imwrite('bilateralFilter.jpg', img)
True

_images/bilateralFilter.jpgbilateralFilter.jpg


blur(src, ksize, |borderType)

使用归一化框滤镜模糊图像,参考: blur

参数:

  • src:Var 输入图像

  • ksize:[int] kernel大小

  • borderType:int 边界类型,可选,默认为cv.BORDER_DEFAULT

返回:模糊后的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> img = cv.blur(img, [3, 3])
>>> cv.imwrite('blur.jpg', img)
True

_images/blur.jpgblur.jpg


boxFilter(src, ddepth, ksize, |normalize, borderType)

使用方框滤镜模糊图像,参考: boxFilter

参数:

  • src:Var 输入图像

  • ddepth:int 图像的深度

  • ksize:[int] kernel大小

  • borderType:int 边界类型,可选,默认为cv.BORDER_DEFAULT

返回:模糊后的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> img = cv.boxFilter(img, -1, [7, 7])
>>> cv.imwrite('boxFilter.jpg', img)
True

_images/boxFilter.jpgboxFilter.jpg


dilate(src, kernel, |iterations, borderType)

通过使用特定的结构元素对图像进行扩张,参考: dilate

参数:

  • src:Var 输入图像

  • kernel:Var 结构元素

  • iterations:int 迭代次数,可选,默认为1

  • borderType:int 边界类型,可选,默认为cv.BORDER_DEFAULT

返回:扩张后的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> img = cv.dilate(img, cv.getStructuringElement(0, (3, 3)))
>>> cv.imwrite('dilate.jpg', img)
True

_images/dilate.jpgdilate.jpg


erode(src, kernel, |iterations, borderType)

通过使用特定的结构元素对图像进行腐蚀,参考: erode

参数:

  • src:Var 输入图像

  • kernel:Var 结构元素

  • iterations:int 迭代次数,可选,默认为1

  • borderType:int 边界类型,可选,默认为cv.BORDER_DEFAULT

返回:腐蚀后的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> img = cv.erode(img, cv.getStructuringElement(0, (3, 3)))
>>> cv.imwrite('erode.jpg', img)
True

_images/erode.jpgerode.jpg


filter2D(src, ddepth, kernel, |delta, borderType)

度图像执行二维卷积,参考: filter2D

参数:

  • src:Var 输入图像

  • ddepth:int 图像的深度

  • kernel:Var 卷积核

  • delta:float 加到卷积结果的偏移量,可选,默认为0

  • borderType:int 边界类型,可选,默认为cv.BORDER_DEFAULT

返回:卷积结果

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> img = cv.filter2D(img, -1, cv.getStructuringElement(0, (3, 3)))
>>> cv.imwrite('filter2D.jpg', img)
True

_images/filter2D.jpgfilter2D.jpg


GaussianBlur(src, ksize, sigmaX, |sigmaY, borderType)

使用高斯滤镜模糊图像,参考: GaussianBlur

参数:

  • src:Var 输入图像

  • ksize:[int] kernel大小

  • sigmaX:float X 方向的高斯核标准差

  • sigmaY:float Y方向的高斯核标准差;如果 sigmaY 为零,则设置为等于 sigmaX,可选,默认为0

  • borderType:int 边界类型,可选,默认为cv.BORDER_DEFAULT

返回:模糊后的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> img = cv.GaussianBlur(img, [5, 5], 3)
>>> cv.imwrite('GaussianBlur.jpg', img)
True

_images/GaussianBlur.jpgGaussianBlur.jpg


getDerivKernels(dx, dy, ksize, |normalize)

返回用于计算空间图像导数的滤波器系数,参考: getDerivKernels

参数:

  • dx:int 关于 x 的导数

  • dy:int 关于 y 的导数

  • ksize:int 返回的kernel大小,可以是1,3,5,7

  • normalize:bool 是否将系数归一化,可选,默认为false

返回:滤波器系数

返回类型:Var

示例:

>>> cv.getDerivKernels(1, 1, 3)
(array([[-1.,  0.,  1.]], dtype=float32), array([[-1.,  0.,  1.]], dtype=float32))

getGaborKernel(ksize, sigma, theta, lambd, gamma, |psi)

返回Gabor滤波器系数,参考: getGaborKernel

参数:

  • ksize:[int] 返回的kernel大小

  • sigma:float Gabor的标准差

  • theta:float Gabor函数的平行条纹的法线方向

  • lambd:float 正弦因子的波长

  • gamma:float 空间纵横比

  • psi:float 相位偏移,可选,默认为PI/2

返回:滤波器系数

返回类型:Var

示例:

>>> cv.getGaborKernel([3, 3], 10, 5, 5, 5)
array([[ 6.1722213e-01,  9.2025989e-01,  9.3729156e-01],
       [-3.1094351e-01, -4.3711388e-08,  3.1094342e-01],
       [-9.3729156e-01, -9.2025995e-01, -6.1722219e-01]], dtype=float32)

getGaussianKernel(ksize, sigma)

返回高斯滤波器系数,参考: getGaussianKernel

参数:

  • ksize:int 返回的kernel大小,必须是奇数

  • sigma:float 高斯标准差sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8

返回:滤波器系数

返回类型:Var

示例:

>>> cv.getGaussianKernel(3, 5)
array([[0.33110374, 0.3377925 , 0.33110374]], dtype=float32)

getStructuringElement(shape, ksize)

返回指定大小和形状的结构元素,用于形态学操作,参考: getStructuringElement

参数:

  • shape:int 元素形状

    • 0: 矩形

    • 1: 十字形

    • 2:椭圆形

  • ksize:[int] 结构元素的大小

返回:结构元素

返回类型:Var

示例:

>>> cv.getStructuringElement(0, (3, 3))
array([[1, 1, 1],
       [1, 1, 1],
       [1, 1, 1]], dtype=uint8)

Laplacian(src, ddepth, |ksize, scale, delta, borderType)

计算图像的拉普拉斯算子,参考: Laplacian

参数:

  • src:Var 输入图像

  • ddepth:int 图像的深度

  • ksize:int 卷积核大小,可选,默认为1

  • scale:float 缩放因子,可选,默认为1

  • delta:float 加到结果的偏移量,可选,默认为0

  • borderType:int 边界类型,可选,默认为cv.BORDER_DEFAULT

返回:拉普拉斯算子计算结果

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> img = cv.Laplacian(img, -1, 3)
>>> cv.imwrite('Laplacian.jpg', img)
True

_images/Laplacian.jpgLaplacian.jpg


pyrDown(src, |dstsize, borderType)

模糊图像并对其进行下采样,参考: pyrDown

参数:

  • src:Var 输入图像

  • dstsize:[int] 输出图像的大小

  • borderType:int 边界类型,可选,默认为cv.BORDER_DEFAULT

返回:下采样后的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> img = cv.pyrDown(img)
>>> cv.imwrite('pyrDown.jpg', img)
True

_images/pyrDown.jpgpyrDown.jpg


pyrUp(src, |dstsize, borderType)

对图像进行上采样,然后对其进行模糊处理,参考: pyrUp

参数:

  • src:Var 输入图像

  • dstsize:[int] 输出图像的大小

  • borderType:int 边界类型,可选,默认为cv.BORDER_DEFAULT

返回:下采样后的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> img = cv.pyrUp(img)
>>> cv.imwrite('pyrUp.jpg', img)
True

_images/pyrUp.jpgpyrUp.jpg


Scharr(src, ddepth, dx, dy, |scale, delta, borderType)

使用Scharr算子计算图像导数,参考: Scharr

参数:

  • src:Var 输入图像

  • ddepth:int 图像的深度

  • dx:int 导数x的阶数

  • dy:int 导数y的阶数

  • scale:float 缩放因子,可选,默认为1

  • delta:float 加到结果的偏移量,可选,默认为0

  • borderType:int 边界类型,可选,默认为cv.BORDER_DEFAULT

返回:Scharr算子计算结果

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> cv.Scharr(img, -1, 1, 1)
array([[[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        ...,
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]]], dtype=uint8)

sepFilter2D(src, ddepth, kx, ky, |delta, borderType)

对图像应用可分离的线性过滤器,参考: sepFilter2D

参数:

  • src:Var 输入图像

  • ddepth:int 图像的深度

  • kx:int x方向的kernel

  • ky:int y方向的kernel

  • delta:float 加到结果的偏移量,可选,默认为0

  • borderType:int 边界类型,可选,默认为cv.BORDER_DEFAULT

返回:sepFilter2D计算结果

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> kernelX = np.array([[0., -1., 0.]])
>>> kernelY = np.array([[-1., 0., -1.]])
>>> cv.sepFilter2D(img, -1, kernelX, kernelY, 1)
array([[[1, 1, 1],
        [1, 1, 1],
        [1, 1, 1],
        ...,
        [1, 1, 1],
        [1, 1, 1],
        [1, 1, 1]]], dtype=uint8)

Sobel(src, ddepth, dx, dy, |ksize, scale, delta, borderType)

使用Sobel算子计算图像导数,参考: Sobel

参数:

  • src:Var 输入图像

  • ddepth:int 图像的深度

  • dx:int 导数x的阶数

  • dy:int 导数y的阶数

  • ksize:int kernel的大小,可选,默认为3

  • scale:float 缩放因子,可选,默认为1

  • delta:float 加到结果的偏移量,可选,默认为0

  • borderType:int 边界类型,可选,默认为cv.BORDER_DEFAULT

返回:Sobel算子计算结果

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> cv.Sobel(img, -1, 1, 0)
array([[[0, 0, 0],
        [0, 0, 0],
        [0, 0, 2],
        ...,
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]]], dtype=uint8)

spatialGradient(src, |ksize, borderType)

使用Sobel算子分别计算x和y方向的一阶图像导数,参考: spatialGradient

参数:

  • src:Var 输入图像

  • ksize:int Sobel kernel的大小,可选,默认为3

  • borderType:int 边界类型,可选,默认为cv.BORDER_DEFAULT

返回:spatialGradient计算结果

返回类型:Var


sqrBoxFilter(src, ddepth, ksize, |normalize, borderType)

计算与过滤器重叠的像素值的归一化平方和,参考: sqrBoxFilter

参数:

  • src:Var 输入图像

  • ddepth:int 图像的深度

  • ksize:[int] kernel的大小

  • normalize:bool 是否归一化,可选,默认为true

  • borderType:int 边界类型,可选,默认为cv.BORDER_DEFAULT

返回:sqrBoxFilter计算结果

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> img = cv.sqrBoxFilter(img, -1, (3,3))
>>> img = img.astype(np.uint8)
>>> cv.imwrite('sqrBoxFilter.jpg', img)
True

_images/sqrBoxFilter.jpgsqrBoxFilter.jpg


getAffineTransform(src, dst)

计算两组三个顶点之间仿射变换矩阵,参考: getAffineTransform

参数:

  • src:[float] 输入的一组顶点,类型为list。里面为6个 float元素,分别代表三个顶点的 x, y

  • dst:[float] 计算仿射变换的另一组顶点,类型为list。里面为6个float元素,分别代表三个顶点的 x, y

返回:变换矩阵

返回类型:CVMatrix 参考:CVMatrix

示例:

>>> src = [50.0, 50.0, 200.0, 50.0, 50.0, 200.0]
>>> dst = [10.0, 100.0, 200.0, 20.0, 100.0, 250.0]
>>> cv.getAffineTransform(src, dst)
    [[1.266667  0.600000    -83.333336]
    [-0.533333 1.000000    76.666664]
    [76.666664 0.000000    0.000000]]

getPerspectiveTransform(src, dst)

计算两组三个顶点之间透视变换矩阵,参考: getPerspectiveTransform

参数:

  • src:[float] 输入的一组顶点,类型为list。里面为6个 float元素,分别代表三个顶点的 x, y

  • dst:[float] 计算仿射变换的另一组顶点,类型为list。里面为6个float元素,分别代表三个顶点的 x, y

返回:变换矩阵

返回类型:CVMatrix 参考:CVMatrix

示例:

>>> src =  [100.0, 50.0, 100.0, 390.0, 600.0, 50.0, 600.0, 390.0]
>>> dst = [200.0, 100.0, 200.0, 330.0, 500.0, 50.0, 600.0, 390.0]
>>> cv2.getPerspectiveTransform(src, dst)
    [[0.307692  -0.104072   174.434372]
    [-0.129231 0.504751    87.685509]
    [87.685509 -0.000585   -0.000520]]

getRectSubPix(image, patchSize, center)

获取图像的矩形子块,参考: getRectSubPix

参数:

  • image:Var 输入的图像

  • patchSize:[int] 裁剪的patch大小(width, height)

  • center:[int] 被裁减出的矩形的中心点(x, y)

返回:裁剪出的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> h, w, c = img.shape
>>> center = (w / 2.0, h / 2.0)
>>> img = cv2.getRectSubPix(img, [90, 90], center)
>>> cv.imwrite('getRectSubPix.jpg', img)
True

_images/getRectSubPix.jpggetRectSubPix.jpg


getRotationMatrix2D(center, angle, scale)

作用等同与 OpenCVGeometric Image Transformations 模块的getRotationMatrix2D 函数,用于计算 2D 旋转的仿射变换矩阵。

参数:

  • center:[float] 图像中的旋转中心点(x, y)

  • angle:float 旋转角度(degrees)

  • scale:float 同向放缩因子

返回:仿射变换矩阵

返回类型:类型为 CVMatrix

示例:

>>> cv.getRotationMatrix2D((500.0 / 2.0, 333.0 / 2.0), 90, 1.0)
[[-0.000000	1.000000	83.500015]
 [-1.000000	-0.000000	416.500000]
 [416.500000	0.000000	0.000000]]

invertAffineTransform(m)

作用等同与 OpenCVGeometric Image Transformations 模块的invertAffineTransform 函数,计算仿射变换矩阵的逆矩阵。

参数:

  • m:CVMatrix 输入的仿射矩阵

返回:仿射变换矩阵的逆矩阵

返回类型:类型为 CVMatrix

示例:

>>> m = MNN.CVMatrix()
>>> m.setScale(5.0, 5.0)
>>> cv.invertAffineTransform(m)
[[0.200000      0.000000        -0.000000]
 [0.000000      0.200000        -0.000000]
 [-0.000000     0.000000        0.000000]]

convertMaps(map1, map2, dstmap1type, |interpolation)

映射map转换,为了兼容OpenCV中的convertMaps 函数;但实际不进行任何操作,仍返回map1, map2

参数:

  • map1:Var 原始映射关系

  • map2:Var 原始映射关系

  • dstmap1type:int 兼容性参数,不支持

  • interpolation:int 兼容性参数,不支持

返回:(map1, map2)

返回类型:类型为 Tuple


remap(src, map1, map2, interpolation, |borderMode, borderValue)

作用等同与 OpenCVGeometric Image Transformations 模块的remap 函数,用于图像重映射。

不支持borderMode与borderValue

参数:

  • src:Var 输入的图像

  • map1:Var x坐标映射

  • map2:Var y坐标映射

  • interpolation:int 插值方式,仅支持cv.INTER_NEARESTcv.INTER_LINEAR

  • borderMode:int 兼容性参数,不支持

  • borderValue:int 兼容性参数,不支持

返回:重映射后的图像

返回类型:类型为 Var

示例:

>>> img = cv.imread('cat.jpg')
>>> row, col, ch = img.shape
>>> mapx = np.ones(img.shape[:2], np.float32)
>>> mapy = np.ones(img.shape[:2], np.float32)
>>> for i in range(row):
>>>     for j in range(col):
>>>         mapx[i, j] = float(j)
>>>         mapy[i, j] = float(row-i)
>>> img = cv.remap(img, mapx, mapy, cv.INTER_LINEAR)
>>> cv.imwrite('remap.jpg', img)
True

_images/remap.jpgremap.jpg


resize(src, dsize, |fx, fy, interpolation, code, mean, norm)

作用等同与 OpenCVGeometric Image Transformations 模块的resize 函数,用于放缩图像。

该函数在兼容OpenCV函数的基础上,额外增加了3个参数可选参数:code, mean, norm可以额外完成cvtColor和typeas的功能

参数:

  • src:Var 输入的图像

  • dsize:tuple 放缩后的大小

  • fx:float 水平方向的放缩因子,如果为0,则自动计算,默认为0

  • fy:float 竖直方便的放缩因子,如果为0,则自动计算,默认为0

  • interpolation:int 放缩的插值方法,默认为cv.INTER_LINEAR

  • code:int 可以在缩放时转换颜色空间,默认为-1不执行转换

  • mean:[float] 转换为float的归一化的均值,默认为空不转换为float

  • norm:[float] 转换为float的归一化的标准差,默认为空不转换为float

返回:放缩后的图像

返回类型:类型为 Var

示例:

>>> img = cv.imread('cat.jpg')
>>> img = cv.resize(img, [100, 100])
>>> cv.imwrite('resize.jpg', img)
True

_images/resize.jpgresize.jpg


warpAffine(src, M, dsize, |flag, borderMode, borderValue, code, mean, norm)

作用等同与 OpenCVGeometric Image Transformations 模块的warpAffine 函数,对一个图像应用仿射变换。

该函数在兼容OpenCV函数的基础上,额外增加了3个参数可选参数:code, mean, norm可以额外完成cvtColor和typeas的功能

参数:

  • src:Var 输入的图像

  • dsize:tuple 放缩后的大小

  • interpolation:int 放缩的插值方法,默认为cv.INTER_LINEAR

  • borderMode:int 边界模式,默认为cv.BORDER_CONSTANT

  • borderValue:int 当边界模式为 cv.BORDER_CONSTANT 时设定的值,默认为0

  • code:int 可以在缩放时转换颜色空间,默认为-1不执行转换

  • mean:[float] 转换为float的归一化的均值,默认为空不转换为float

  • norm:[float] 转换为float的归一化的标准差,默认为空不转换为float

返回:仿射变换的图像

返回类型:Var

示例:

>>> src = [50.0, 50.0, 200.0, 50.0, 50.0, 200.0, 125.0, 222.0]
>>> dst = [10.0, 100.0, 200.0, 20.0, 100.0, 250.0, 200.0, 300.0]
>>> transform = cv.getAffineTransform(src, dst)
>>> img = cv.imread('cat.jpg')
>>> img = cv.warpAffine(img, transform, [300, 330])
>>> cv.imwrite('warpAffine.jpg', img)
True

_images/warpAffine.jpgwarpAffine.jpg


warpPerspective(src, M, dsize, flag, borderMode, borderValue)

作用等同与 OpenCVGeometric Image Transformations 模块的warpPerspective 函数,对一个图像应用透视变换。

参数:

  • src:Var 输入的图像

  • dsize:tuple 放缩后的大小

  • interpolation:int 放缩的插值方法,默认为cv.INTER_LINEAR

  • borderMode:int 边界模式,默认为cv.BORDER_CONSTANT

  • borderValue:int 当边界模式为 cv.BORDER_CONSTANT 时设定的值,默认为0

返回:透视变换的图像

返回类型:Var

示例:

>>> src = [50.0, 50.0, 200.0, 50.0, 50.0, 200.0, 125.0, 222.0]
>>> dst = [10.0, 100.0, 200.0, 20.0, 100.0, 250.0, 200.0, 300.0]
>>> transform = cv.getPerspectiveTransform(src, dst)
>>> img = cv.imread('cat.jpg')
>>> img = cv.warpPerspective(img, transform, [500, 333])
>>> cv.imwrite('warpPerspective.jpg', img)
True

_images/warpPerspective.jpgwarpPerspective.jpg


adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)

作用等同与 OpenCVMiscellaneous Image Transformations 模块的adaptiveThreshold 函数,对图像逐像素进行自适应阈值变化,可以将使用此函数将图像变成二值图像。

参数:

  • src:Var 输入的图像

  • maxValue:float 阈值的最大值

  • adaptiveMethod:int 自适应方法,如:cv.ADAPTIVE_THRESH_MEAN_C

  • thresholdType:int 阈值变化的类型,如:cv.THRESH_BINARY

  • blockSize:int 计算阈值时取邻域的大小,如:3,5,7等

  • C:float

返回:阈值变化后的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
>>> img = cv.adaptiveThreshold(img, 50, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 5, 2)
>>> cv.imwrite('adaptiveThreshold.jpg', img)
True

_images/adaptiveThreshold.jpgadaptiveThreshold.jpg


blendLinear(src1, src2, weight1, weight2)

作用等同与 OpenCVMiscellaneous Image Transformations 模块的blendLinear 函数,对两幅图像进行线性混合。

参数:

  • src1:Var 输入的图像

  • src2:Var 输入的图像

  • weight1:Var src1 计算的叠加权重

  • weight2:Var src2 计算的叠加权重

返回:混合后的图像。

返回类型:Var

示例:

>>> src1 = np.array([[2.0, 3.0], [1.0, 1.0]])
>>> src2 = np.array([[0.0, 1.0], [1.0, 1.0]])
>>> weight1 = np.array([[1.0, 2.0], [1.5, 1.5]])
>>> weight2 = np.array([[0.1, 0.5], [0.2, 0.3]])
>>> cv.blendLinear(src1, src2, weight1, weight2)
array([[1.8181652 , 2.5999894 ],
       [0.9999941 , 0.99999446]], dtype=float32)

threshold(src, thresh, maxval, type)

作用等同与 OpenCVMiscellaneous Image Transformations 模块的threshold 函数,对图像逐像素进行阈值变化,可以将使用此函数将图像变成二值图像,比如在寻找轮廓时(findContours)可以使用该函数。

参数:

  • src:Var 输入的图像

  • thresh:float 阈值

  • maxval:float 阈值的最大值

  • type:int 阈值变化的类型,默认为cv.THRESH_BINARY

参数 说明
THRESH_BINARY 小于阈值的像素置为0,大于阈值的像素置为maxval
THRESH_BINARY_INV 小于阈值的像素置为maxval,大于阈值的像素置为0
THRESH_TRUNC 小于阈值的像素置为0,大于阈值的像素保持不变
THRESH_TOZERO 小于阈值的像素置为0,大于阈值的像素不变
THRESH_TOZERO_INV 小于阈值的像素不变,大于阈值的像素置为0

返回:阈值变化后的图像

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> img = img.astype(np.float32)
>>> img = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
>>> img = img.astype(np.uint8)
>>> cv.imwrite('threshold.jpg', img)
True

_images/threshold.jpgthreshold.jpg


findContours(image, mode, method, offset)

作用等同与 OpenCVStructural Analysis and Shape Descriptors模块的findContours 函数,对二值图像进行轮廓查找,查找得到的结果可以用作contourAreafillPolydrawContours的参数使用。

注意:该实现未计算hierarchy信息

参数:

  • image:Var 输入的图像

  • mode:int 轮廓查找的模式,默认为cv.RETR_EXTERNAL

  • method:int 轮廓查找的方法,默认为cv.CHAIN_APPROX_SIMPLE

  • offset:tuple 轮廓查找的偏移量,默认为(0, 0)

返回:tuple的第一个元素为找到的轮廓像素,类型为list of Var,第二个值为兼容opencv的值,本函数未实现。

返回类型:tuple

示例:

>>> img = cv.imread('cat.jpg')
>>> gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
>>> binary = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
>>> cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
([array([[[143, 294]],
         ...,
         [[144, 295]]], dtype=int32),
         ...
  array([[[304,   1]],
         ...,
         [[309,   1]]], dtype=int32)], 'no hierarchy')

contourArea(points, oriented)

作用等同与 OpenCVStructural Analysis and Shape Descriptors模块的contourArea 函数,计算轮廓的面积。

参数:

  • points:Var 轮廓像素

  • oriented:bool 是否计算有向面积,默认为False

返回:轮廓的面积

返回类型:float

示例:

>>> img = cv.imread('cat.jpg')
>>> gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
>>> binary = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
>>> contours, _ = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
>>> cv.contourArea(contours[0], False)
15.5

convexHull(points, clockwise, returnPoints)

作用等同与 OpenCVStructural Analysis and Shape Descriptors模块的convexHull 函数,计算点集的凸包。

参数:

  • points:Var 轮廓像素

  • clockwise:bool 是否按顺时针方向计算凸包,默认为False

  • returnPoints:bool 是否返回凸包的点集,默认为True

返回:凸包的点集

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
>>> binary = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
>>> contours, _ = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
>>> cv.convexHull(contours[0])
array([[[147, 295]],
       [[147, 298]],
       [[146, 299]],
       [[143, 298]],
       [[142, 297]],
       [[142, 296]],
       [[143, 294]]], dtype=int32)

minAreaRect(points)

作用等同与 OpenCVStructural Analysis and Shape Descriptors模块的minAreaRect 函数,计算点集的最小外接矩形。

参数:

  • points:Var 轮廓像素

返回:最小外接矩形的中心点坐标,长宽,旋转角度

返回类型:tuple

示例:

>>> img = cv.imread('cat.jpg')
>>> gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
>>> binary = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
>>> contours, _ = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
>>> cv.minAreaRect(contours[0])
((144.61766052246094, 296.5294494628906), (5.3357834815979, 4.123105525970459), 14.03624439239502)

boundingRect(points)

作用等同与 OpenCVStructural Analysis and Shape Descriptors模块的boundingRect 函数,计算点集的最小外接矩形。

参数:

  • points:Var 轮廓像素

返回:最小外接矩形的中心点坐标,长宽

返回类型:tuple

示例:

>>> img = cv.imread('cat.jpg')
>>> gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
>>> binary = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
>>> contours, _ = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
>>> cv.boundingRect(contours[0])
[142, 294, 6, 6]

connectedComponentsWithStats(image, connectivity)

作用等同与 OpenCVConnected Components模块的connectedComponentsWithStats 函数,计算图像的连通域。

参数:

  • image:Var 图像

  • connectivity:int 连通域的连通性,默认为8

返回:连通域的数量,连通域的标签,每个标签的统计输出,每个标签的质心输出

返回类型:tuple

示例:

>>> img = cv.imread('cat.jpg')
>>> cv.connectedComponentsWithStats(img)
(2, array([[[[1], ..., [1]], ..., [[1], ..., [1]]]], dtype=int32),
    array([[213, 60, 262, 52, 3], [0, 0, 480, 360, 172797]], dtype=int32),
    array([[386., 77.333336], [239.49745, 179.50177]], dtype=float32))

boxPoints(box)

作用等同与 OpenCVStructural Analysis and Shape Descriptors模块的boxPoints 函数,计算矩形的四个顶点坐标。

参数:

  • box:tuple 矩形的中心点坐标,长宽,旋转角度,参考 minAreaRect 函数的返回值

返回:四个顶点坐标

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
>>> binary = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
>>> contours, _ = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
>>> cv.boxPoints(cv.minAreaRect(contours[0]))
array([[141.52942, 297.8824 ],
       [142.52942, 293.8824 ],
       [147.7059 , 295.1765 ],
       [146.7059 , 299.1765 ]], dtype=float32)

line(img, pt1, pt2, color, thickness, lineType, shift)

作用等同与 OpenCVDrawing Functions 模块的line 函数,绘制从第一个点指向第二个点的直线。

该函数为 in-replace,直接作用于原图

参数:

  • img:Var 代表需要绘制线条的图像

  • pt1:tuple 线条绘制的起点(x, y)

  • pt2:tuple 线条绘制的终点(x, y)

  • color:tuple 线条绘制的颜色(b, g, r, a)

  • thickness:int 线的粗细,默认为1

  • lineType:int 线条绘制的方式,默认为cv.LINE_8

  • shift:int 坐标小数点向前移动的位数(缩小10倍数),默认为0

返回:None

返回类型:None

示例:

>>> img = cv.imread('cat.jpg')
>>> cv.line(img, (10, 10), (100, 100), (255, 0, 0, 0), 5)
>>> cv.imwrite('line.jpg', img)
True

_images/line.jpgline.jpg


arrowedLine(img, pt1, pt2, color, thickness, lineType, shift, tipLength)

作用等同与 OpenCVDrawing Functions 模块的arrowedLine 函数,绘制从第一个点指向第二个点的箭头段。

该函数为 in-replace,直接作用于原图

参数:

  • img:Var 代表需要绘制箭头的图像

  • pt1:tuple 箭头绘制的起点(x, y)

  • pt2:tuple 箭头绘制的终点(x, y)

  • color:tuple 箭头绘制的颜色(b, g, r, a)

  • thickness:int 箭头的粗细,默认为1

  • lineType:int 箭头绘制的方式,默认为cv.LINE_8

  • shift:int 坐标小数点向前移动的位数(缩小10倍数),默认为0

  • tipLength:float 箭头部分与直线长度的百分比,默认为0.1

返回:None

返回类型:None

示例:

>>> img = cv.imread('cat.jpg')
>>> cv.arrowedLine(img, (10, 10), (100, 100), (255, 0, 0, 0), 5)
>>> cv.imwrite('arrowedLine.jpg', img)
True

_images/arrowedLine.jpgarrowedLine.jpg


circle(img, center, radius, color, thickness, lineType, shift)

作用等同与 OpenCVDrawing Functions 模块的circle 函数,绘制一个圆。

该函数为 in-replace,直接作用于原图

参数:

  • img:Var 代表需要绘制圆的图像

  • center:tuple 圆的中心点(x, y)

  • radius:int 圆的半径大小

  • color:tuple 圆绘制的颜色(b, g, r, a)

  • thickness:int 圆的粗细,默认为1

  • lineType:int 圆绘制的方式,默认为cv.LINE_8

  • shift:int 坐标小数点向前移动的位数(缩小10倍数),默认为0

返回:None

返回类型:None

示例:

>>> img = cv.imread('cat.jpg')
>>> cv.circle(img, (100, 100), 5, (255, 0, 0, 0), 5)
>>> cv.circle(img, (100, 100), 50, (0, 0, 255, 0), 5)
>>> cv.imwrite('circle.jpg', img)
True

_images/circle.jpgcircle.jpg


rectangle(src, pt1, pt2, color, thickness, lineType, shift)

作用等同与 OpenCVDrawing Functions 模块的rectangle 函数,绘制一个矩形。

该函数为 in-replace,直接作用于原图

参数:

  • img:Var 代表需要绘制圆的图像

  • pt1:tuple 矩形的一个顶(x, y)

  • pt2:tuple 矩形的另一个顶(x, y)

  • color:tuple 矩形绘制的颜色(b, g, r, a)

  • thickness:int 矩形的粗细,默认为1

  • lineType:int 矩形绘制的方式,默认为cv.LINE_8

  • shift:int 坐标小数点向前移动的位数(缩小10倍数),默认为0

返回:None

返回类型:None

示例:

>>> img = cv.imread('cat.jpg')
>>> cv.rectangle(img, (50, 50), (150, 150), (255, 0, 0, 0), 5)
>>> cv.rectangle(img, (100, 100), (200, 200), (0, 0, 255, 0), 5)
>>> cv.imwrite('rectangle.jpg', img)
True

_images/rectangle.jpgrectangle.jpg


drawContours(img, contours, contourIdx, color, thickness, lineType)

作用等同与 OpenCVDrawing Functions 模块的drawContours 函数,绘制轮廓边缘或对其填充。

该函数为 in-replace,直接作用于原图

参数:

  • img:Var 代表需要绘制圆的图像

  • contours:[[int]] 其中每一个元素都是一个list,代表一组轮廓点。一组轮廓点中的元素分别代表一个点的 x 或者 y,必须配对

  • contourIdx:int 代表要绘制第几个轮廓组。如果传入负数,绘制所有轮廓组

  • color:tuple 矩形绘制的颜色(b, g, r, a)

  • thickness:int 矩形的粗细,默认为1

  • lineType:int 矩形绘制的方式,默认为cv.LINE_8

返回:None

返回类型:None

示例:

>>> img = cv.imread('cat.jpg')
>>> gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
>>> gray = gray.astype(np.float32)
>>> binary = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
>>> binary = binary.astype(np.uint8)
>>> contours, _ = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
>>> cv.drawContours(img, contours, -1, [0, 0, 255])
>>> cv.imwrite('drawContours.jpg', img)
True

_images/drawContours.jpgdrawContours.png


fillPoly(img, contours, color, lineType, shift, offset)

作用等同与 OpenCVDrawing Functions 模块的fillPoly 函数,绘制填充多边形。

该函数为 in-replace,直接作用于原图

参数:

  • img:Var 代表需要绘制圆的图像

  • contours:[[int]] 其中每一个元素都是一个list,代表一组轮廓点。一组轮廓点中的元素分别代表一个点的 x 或者 y,必须配对

  • color:tuple 矩形绘制的颜色(b, g, r, a)

  • lineType:int 矩形绘制的方式,默认为cv.LINE_8

  • shift:int 坐标小数点向前移动的位数(缩小10倍数),默认为0

  • offset:tuple 所有点相对轮廓的偏移量,默认为(0, 0)

返回:None

返回类型:None

示例:

>>> img = cv.imread('cat.jpg')
>>> gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
>>> gray = gray.astype(np.float32)
>>> binary = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
>>> binary = binary.astype(np.uint8)
>>> contours, _ = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
>>> cv.fillPoly(img, contours, [0, 0, 255])
>>> cv.imwrite('fillPoly.jpg', img)
True

_images/fillPoly.jpgfillPoly.png


calcHist(imgs, channels, mask, histSize, ranges, accumulate)

作用等同与 OpenCVHistograms 模块的calcHist 函数,计算图像的直方图。

参数:

  • imgs:[Var] 需要计算的图像

  • channels:[int] 需要计算的通道

  • mask:Var 需要计算的图像的掩码,本函数实现不支持mask

  • histSize:[int] 直方图的大小,如:[256]

  • ranges:[float] 直方图的范围,如:[0., 256.]

  • accumulate:bool 是否累加,默认为False 本函数实现不支持累加

返回:计算得到的直方图

返回类型:Var

示例:

>>> img = cv.imread('cat.jpg')
>>> cv.calcHist([img], [0], None, [256], [0., 256.])
array([ 9.,    5.,   13.,   25., ..., 41.,   74.,   41.,  173.], dtype=float32)

nn

module nn

MNN是Pymnn中最基础的Module,其中包含了V2 API所需要数据结构与一些枚举类型;同时包含了一些基础函数。 其他子模块则也需要通过MNN模块引入,引入方式为import MNN.{module} as {module}


nn submodules


nn Types


load_module(inputs, outputs, for_training)

模块加载类, 将计算图的部分加载为一个_Module

参数:

  • inputs:[Var] 计算图的开始部分

  • outputs:[Var] 计算图的结束部分

  • for_training:bool 是否加载为训练模式

返回:模型

返回类型:_Module

示例

>>> var_dict = expr.load_as_dict('mobilenet_v1.mnn')
>>> input_var = var_dict['data']
>>> output_var = var_dict['prob']
>>> nn.load_module([input_var], [output_var], False)
<_Module object at 0x7f97c42068f0>

load_module_from_file(file_name, input_names, output_names, |dynamic, shape_mutable, rearrange, backend, memory_mode, power_mode, precision_mode)

加载模型,从模型文件加载为_Module

参数:

  • file_name:str 模型文件名

  • input_names:list[str] 输入变量名列表

  • output_names:list[str] 输出变量名列表

  • dynamic:bool 是否动态图,默认为False

  • shape_mutable:bool 是否在内部控制流中形状变化,默认为False

  • rearrange:bool 是否重新排列输入变量,默认为False

  • backend:expr.Backend 后端,默认为expr.Backend.CPU

  • memory_mode:expr.MemoryMode 内存模式,默认为expr.MemoryMode.Normal

  • power_mode:expr.PowerMode 功耗模式,默认为expr.PowerMode.Normal

  • precision_mode:expr.PrecisionMode 精度模式,默认为expr.PrecisionMode.Normal

  • thread_num:int 使用线程数,默认为4

返回:创建的模型

返回类型:_Module

示例

>>> conv = nn.conv(3, 16, [3, 3])
>>> input_var = np.random.random((1, 3, 64, 64))
>>> conv(input_var)
array([[[[-5.25599599e-01,  3.63697767e-01,  5.57627320e-01, ...,
          -3.90964895e-01, -3.85326982e-01,  5.49694777e-01],
          ...,
          [-8.73677015e-01,  2.95535415e-01,  3.95657867e-02, ...,
           5.87978542e-01, -1.16958594e+00,  1.74816132e-01]]]], dtype=float32)

create_runtime_manager(config)

根据config信息创建RuntimeManager

参数:

返回:创建的RuntimeManager

返回类型:RuntimeManager


conv(in_channel, out_channel, kernel_size, stride, padding, dilation, depthwise, bias, padding_mode)

创建卷积模块实例

参数:

  • model_path:str 模型路径

  • in_channel:int 输入通道数

  • out_channel:int 输出通道数

  • kernel_size:int 卷积核大小

  • stride:list[int] 卷积步长,默认为[1, 1]

  • padding:list[int] 填充大小,默认为[0, 0]

  • dilation:list[int] 卷积核膨胀,默认为[1, 1]

  • depthwise:bool 是否深度卷积,默认为False

  • bias:bool 是否使用偏置,默认为True

  • padding_mode:expr.Padding_Mode 填充模式,默认为expr.Padding_Mode.VALID

返回:卷积模块

返回类型:_Module

示例

>>> conv = nn.conv(3, 16, [3, 3])
>>> input_var = np.random.random((1, 3, 64, 64))
>>> conv(input_var)
array([[[[-5.25599599e-01,  3.63697767e-01,  5.57627320e-01, ...,
          -3.90964895e-01, -3.85326982e-01,  5.49694777e-01],
          ...,
          [-8.73677015e-01,  2.95535415e-01,  3.95657867e-02, ...,
           5.87978542e-01, -1.16958594e+00,  1.74816132e-01]]]], dtype=float32)

linear(input_length, output_length, bias)

创建innerproduct实例

参数:

  • input_length:int 输入长度

  • output_length:int 输出长度

  • bias:bool 是否使用偏置,默认为True

返回:线性模块

返回类型:_Module

示例

>>> linear = nn.linear(32, 64)
>>> input_var = np.random.random([32])
>>> linear(input_var)

batch_norm(channels, dims, momentum, epsilon)

创建batchnorm实例

参数:

  • channels:int 通道数

  • dims:int 维度,默认为4

  • momentum:float 动量,默认为0.99

  • epsilon:float 极小值,默认为1e-05

返回:batch_norm模块

返回类型:_Module

示例

>>> bn = nn.batch_norm(3)
>>> input_var = np.random.random([1, 3, 2, 2])
>>> bn(input_var)
array([[[[-1.5445713 ,  1.0175514 ],
         [-0.20512265,  0.73214275]],
        [[-0.9263869 , -0.59447914],
         [-0.14278792,  1.663654  ]],
        [[-0.61769044,  0.15747389],
         [-1.0898823 ,  1.5500988 ]]]], dtype=float32)

dropout(drop_ratio)

创建dropout实例

参数:

  • drop_ratio:float dropout比例

返回:dropout模块

返回类型:_Module

示例

>>> dropout = nn.dropout(0.5)
>>> input_var = np.random.random([8])
>>> dropout(input_var)
array([0.0000000e+00, 1.9943696e+00, 1.4406490e+00, 0.0000000e+00,
       2.2876216e-04, 0.0000000e+00, 6.0466516e-01, 1.9980811e+00], dtype=float32)

optim

module optim

optim时优化器模块,提供了一个优化器基类Optimizer,并提供了SGDADAM优化器实现;主要用于训练阶段迭代优化


optim Types


optim.Regularization_Method

优化器的正则化方法,提供了L1和L2正则化方法

  • 类型:Enum

  • 枚举值:

    • L1

    • L2

    • L1L2


SGD(module, lr, momentum, weight_decay, regularization_method)

创建一个SGD优化器

参数:

  • module:_Module 模型实例

  • lr:float 学习率

  • momentum:float 动量,默认为0.9

  • weight_decay:float 权重衰减,默认为0.0

  • regularization_method:RegularizationMethod 正则化方法,默认为L2正则化

返回:SGD优化器实例

返回类型:Optimizer

示例:

model = Net()
sgd = optim.SGD(model, 0.001, 0.9, 0.0005, optim.Regularization_Method.L2)
# feed some date to the model, then get the loss
loss = ...
sgd.step(loss) # backward and update parameters in the model

ADAM(module, lr, momentum, momentum2, weight_decay, eps, regularization_method)

创建一个ADAM优化器

参数:

  • module:_Module 模型实例

  • lr:float 学习率

  • momentum:float 动量,默认为0.9

  • momentum2:float 动量2,默认为0.999

  • weight_decay:float 权重衰减,默认为0.0

  • eps:float 正则化阈值,默认为1e-8

  • regularization_method:RegularizationMethod 正则化方法,默认为L2正则化

返回:ADAM优化器实例

返回类型:Optimizer

示例:

model = Net()
sgd = optim.ADAM(model, 0.001)
# feed some date to the model, then get the loss
loss = ...
sgd.step(loss) # backward and update parameters in the model

data

module data

data时数据模块,提供了数据集(Dataset)和数据加载器(DataLoader),主要用于训练时的数据读取和预处理;


data Types

loss

module loss

loss模块是模型训练使用的模块,提供了多个损失函数


cross_entropy(predicts, onehot_targets)

求交叉熵损失

参数:

  • predicts:Var 输出层的预测值,dtype=floatshape=(batch_size, num_classes)

  • onehot_targets:Var onehot编码的标签,dtype=floatshape=(batch_size, num_classes)

返回:交叉熵损失

返回类型:Var

示例

>>> predict = np.random.random([2,3])
>>> onehot = np.array([[1., 0., 0.], [0., 1., 0.]])
>>> nn.loss.cross_entropy(predict, onehot)
array(4.9752955, dtype=float32)

kl(predicts, onehot_targets)

求KL损失

参数:

  • predicts:Var 输出层的预测值,dtype=floatshape=(batch_size, num_classes)

  • onehot_targets:Var onehot编码的标签,dtype=floatshape=(batch_size, num_classes)

返回:KL损失

返回类型:Var

示例

>>> predict = np.random.random([2,3])
>>> onehot = np.array([[1., 0., 0.], [0., 1., 0.]])
>>> nn.loss.kl(predict, onehot)
array(inf, dtype=float32)

mse(predicts, onehot_targets)

求MSE损失

参数:

  • predicts:Var 输出层的预测值,dtype=floatshape=(batch_size, num_classes)

  • onehot_targets:Var onehot编码的标签,dtype=floatshape=(batch_size, num_classes)

返回:MSE损失

返回类型:Var

示例

>>> predict = np.random.random([2,3])
>>> onehot = np.array([[1., 0., 0.], [0., 1., 0.]])
>>> nn.loss.mse(predict, onehot)
array(1.8694793, dtype=float32)

mae(predicts, onehot_targets)

求MAE损失

参数:

  • predicts:Var 输出层的预测值,dtype=floatshape=(batch_size, num_classes)

  • onehot_targets:Var onehot编码的标签,dtype=floatshape=(batch_size, num_classes)

返回:MAE损失

返回类型:Var

示例

>>> predict = np.random.random([2,3])
>>> onehot = np.array([[1., 0., 0.], [0., 1., 0.]])
>>> nn.loss.mae(predict, onehot)
array(2.1805272, dtype=float32)

hinge(predicts, onehot_targets)

求Hinge损失

参数:

  • predicts:Var 输出层的预测值,dtype=floatshape=(batch_size, num_classes)

  • onehot_targets:Var onehot编码的标签,dtype=floatshape=(batch_size, num_classes)

返回:Hinge损失

返回类型:Var

示例

>>> predict = np.random.random([2,3])
>>> onehot = np.array([[1., 0., 0.], [0., 1., 0.]])
>>> nn.loss.hinge(predict, onehot)
array(2.791432, dtype=float32)

compress

module compress

compress模块用来做Quantization-Aware-Training(QAT)训练量化,提供了训练量化的接口


compress.Feature_Scale_Method

对特征的量化方式,可以针对整个特征进行量化,也可以针对每个channel进行量化

  • 类型:Enum

  • 枚举值:

    • PER_TENSOR

    • PER_CHANNEL


compress.Scale_Update_Method

scale的更新方式

  • 类型:Enum

  • 枚举值:

    • MAXIMUM

    • MOVING_AVERAGE


train_quant(module, |quant_bits, feature_scale_method, scale_update_method)

训练量化

参数:

  • module 待训练模型

  • quant_bits 量化位数,默认为 8

  • feature_scale_method 特征的量化方式,默认为 PER_TENSOR

  • scale_update_method scale的更新方式,默认为 MOVING_AVERAGE

返回:是否成功

返回类型:bool

示例

# args are self-explained
nn.compress.train_quant(module, quant_bits = 8, feature_scale_method = Feature_Scale_Method.PER_TENSOR, scale_update_method = Scale_Update_Method.MOVING_AVERAGE)

linalg

module linalg

linalg模块提供了numpy.linalg模块的部分函数,提供了对矩阵的求解和线性代数运算的函数


norm(a, ord=None, axis=None, keepdims=False)

作用等同与 numpy 中的 np.linalg.norm 函数,计算矩阵或向量的范数。

参数:

  • a:Var 将要计算的变量

  • ord:str 范数的顺序,见下表

  • axis:int 计算的轴

  • keepdims:bool 是否保留计数维度

ord 矩阵范数 数组范数
None Frobenius norm 2-norm
fro Frobenius norm -
nuc nuclear norm -
inf max(sum(abs(x), axis=1)) max(abs(x))
-inf min(sum(abs(x), axis=1)) min(abs(x))
0 - sum(x != 0)
1 max(sum(abs(x), axis=0)) as below
-1 min(sum(abs(x), axis=0)) as below
2 2-norm as below
-2 smallest singular value as below
other - sum(abs(x)ord)(1./ord)

返回:计数得到的变量

返回类型:Var

示例

>>> a = np.arange(9) - 4
>>> np.linalg.norm(a)
array(7.745967, dtype=float32)

svd(a, |full_matrices, compute_uv, hermitian)

作用等同与 numpy 中的 np.linalg.svd 函数,奇异值分解

full_matrices, compute_uv, hermitian参数无效只能按照默认值执行

参数:

  • a:Var 输入矩阵

  • full_matrices:bool 是否计算全部的奇异值,numpy兼容参数

  • compute_uv:bool 是否计算奇异值,numpy兼容参数

  • hermitian:bool 是否计算对称矩阵的奇异值,numpy兼容参数

返回:以(u, w, vt)的顺序返回奇异值分解结果

返回类型:tuple of Var

示例

>>> x = np.arange(9.).reshape(3, 3)
>>> np.linalg.svd(x)
(array([[ 0.13511899, -0.90281564,  0.40824836],
       [ 0.49633518, -0.2949318 , -0.81649655],
       [ 0.8575514 ,  0.31295216,  0.40824828]], dtype=float32), array([1.4226706e+01, 1.2652264e+00, 7.3003456e-08], dtype=float32), array([[ 0.46632814,  0.57099086,  0.67565346],
       [ 0.7847747 ,  0.08545685, -0.6138614 ],
       [-0.40824845,  0.8164966 , -0.4082482 ]], dtype=float32))

random

module random

random模块提供了numpy.random模块的部分函数,提供了生成随机数的功能。


rand(*args)

作用等同与 numpy 中的 np.random.rand 函数,生成指定形状的随机数。

参数:

  • args:[int] 输出随机数的形状

返回:得到的随机变量

返回类型:Var

示例

>>> np.random.rand([2, 3])
array([[4.1702199e-01, 9.9718481e-01, 7.2032452e-01],
       [9.3255734e-01, 1.1438108e-04, 1.2812445e-01]], dtype=float32)

randn(*args)

作用等同与 numpy 中的 np.random.randn 函数,生成指定形状的随机数。

参数:

  • args:[int] 输出随机数的形状

返回:得到的随机变量

返回类型:Var

示例

>>> np.random.randn([2, 3])
array([[4.1702199e-01, 9.9718481e-01, 7.2032452e-01],
       [9.3255734e-01, 1.1438108e-04, 1.2812445e-01]], dtype=float32)

randint(low, high=None, size=None, dtype=_F.int)

作用等同与 numpy 中的 np.random.randint 函数,生成指定形状,范围的随机数。

参数:

  • low:scalar 输出随机数的最小值

  • high:scalar 输出随机数的最大值

  • size:[int] 输出随机数的形状

  • dtype:dtype,输出随机数的类型 输出随机数的类型,默认为int

返回:得到的随机变量

返回类型:Var

示例

>>> np.random.randint(0, 4, size=10, dtype=np.int32)
array([1, 3, 2, 3, 0, 0, 1, 3, 0, 0], dtype=int32)

random(shape)

作用等同与 numpy 中的 np.random.random 函数,生成指定形状的随机数。

参数:

  • shape:[int] 输出随机数的形状

返回:得到的随机变量

返回类型:Var

示例

>>> np.random.random([2, 3])
array([[4.1702199e-01, 9.9718481e-01, 7.2032452e-01],
       [9.3255734e-01, 1.1438108e-04, 1.2812445e-01]], dtype=float32)

MNN.Interpreter [deprecated]

class Interpreter

Interpreter是MNN V2接口中模型数据的持有者。使用MNN推理时,有两个层级的抽象,分别是解释器Interpreter和会话Session

不建议使用该接口,请使用nn代替


Interpreter(model_path)

加载.mnn模型文件创建一个MNN解释器,返回一个解释器对象

参数:

  • model_path:str MNN模型所放置的完整文件路径,其中MNN模型可由Tensorflow、Caffe、PyTorch和 ONNX等模型进行转换得到

返回:Interpreter对象

返回类型:Interpreter


createRuntime(config)

根据配置创建一个Runtime,并获取config中指定的参数是否生效;默认情况下,在createSession时对应create单独一个Runtime。对于串行的一系列模型,可以先单独创建Runtime,然后在各Session创建时传入,使各模型用共享同样的运行时资源(对CPU而言为线程池、内存池,对GPU而言Kernel池等),参考RuntimeManager

参数:

  • config:dict 创建Runtime的配置, 其key, value和含义如下变所示

key value 说明
backend str or int 可选值:"CPU"或0(默认), "OPENCL"或3,"OPENGL"或6, "VULKAN"或7, "METAL"或1, "TRT"或9, "CUDA"或2, "HIAI"或8
precision str 可选值:"normal"(默认), "low","high","lowBF"
numThread int or long value为推理线程数,只在 CPU 后端下起作用
saveTensors tuple of str value为想要保留成为输出层的tensorName
inputPaths tuple of str 推理路径的起点,输入tensorName
outputPaths tuple of str 推理路径的终点,输出tensorName

返回:一个pair

  • first:Runtime对象的PyCapsule,可以用来创建Session

  • second:为tuple of bool;代表config中对应的配置是否生效

返回类型:pair


createSession(config, |runtime)

根据配置创建Session,返回一个Session对象。

参数:

  • config:dict 创建推理会话的配置,含义同createRuntime方法

  • runtime:PyCapsule 指定的runtime信息,如果不指定,则使用config中的配置创建runtime

返回:持有推理会话数据的Session对象

返回类型:Session


setCacheFile(cache_path)

设置缓存文件路径,在GPU情况下可以把kernel和Op-info缓存到该文件中

参数:

  • cache_path:str 缓存文件的路径

返回:None

返回类型:None


setExternalFile(path)

设置额外数据文件路径,使用该文件中的数据作为权重或常量

参数:

  • path:str 额外数据文件的路径

返回:None

返回类型:None


updateCacheFile(session, flag)

在执行推理之后,更新GPU的kernel信息到缓存文件;应该在每次推理结束后指定该函数

参数:

  • session:Session 需要缓存的会话

  • flag 保留参数,目前未使用;输入0即可

返回:error code 参考runSession方法

返回类型:int


setSessionMode(mode)

设置会话的执行模式

参数:

  • mode:int 执行Session的模式,含义如下表所示

value name 说明
0 Session_Debug 可以执行callback函数,并获取Op信息(默认)
1 Session_Release 不可执行callback函数
2 Session_Input_Inside 输入由session申请(默认)
3 Session_Input_User 输入由用户申请
4 Session_Output_Inside 输出依赖于session不可单独使用
5 Session_Output_User 输出不依赖于session可单独使用
6 Session_Resize_Direct 在创建Session时执行resize(默认)
7 Session_Resize_Defer 在创建Session时不执行resize
8 Session_Backend_Fix 使用用户指定的后端,后端不支持时回退CPU
9 Session_Backend_Auto 根据算子类型自动选择后端

返回:None

返回类型:None


setSessionHint(mode, value)

设置执行时的额外信息

参数:

  • mode:int hint类型

  • value:int hint值

mode name 说明
0 MAX_TUNING_NUMBER GPU下tuning的最大OP数

返回:None

返回类型:None


getSessionInput(session, |tensorName)

根据tensorName,返回模型指定会话的输入tensor;如果没有指定tensor名称,则返回第一个输入tensor

参数:

  • session:Session 持有推理会话数据的Session对象

  • tensorName:str Tensor的名称

返回:输入Tensor对象

返回类型:Tensor


getSessionInputAll(session)

返回模型指定会话的所有的输入tensor

参数:

  • session:Session 持有推理会话数据的Session对象

返回:所有的输入Tensor对象,类型为字典,其中key为tensorName,类型为str;value为一个输入tensor,类型为Tensor

返回类型:dict


getSessionOutput(session, |tensorName)

根据tensorName,返回模型指定会话的输出tensor;如果没有指定tensor名称,则返回第一个输出tensor

参数:

  • session:Session 持有推理会话数据的Session对象

  • tensorName:str Tensor的名称

返回:输出Tensor对象

返回类型:Tensor


getSessionOutputAll(session)

返回模型指定会话的所有的输出tensor

参数:

  • session:Session 持有推理会话数据的Session对象

返回:所有的输出Tensor对象,类型为字典,其中key为tensorName,类型为str;value为一个输入tensor,类型为Tensor

返回类型:dict


resizeTensor(tensor, shape)

改变tensor形状,并重新分配内存

参数:

  • tensor:Tensor 需要改变形状的Tensor对象,一般为输入tensor

  • shape:tuple 新的形状,元素类型为int

返回:None

返回类型:None


resizeSession(session)

为session分配内存,进行推理准备工作;该API一般配合resizeTensor一起调用,修改Tensor输入形状后对应整个推理过程中的内存分配也需要修改。

参数:

  • session:Session 改变输入形状后需要重新分配内存的Session对象

返回:成功返回True,否则抛出相应的异常

返回类型:bool


runSession(session)

运行session执行模型推理,返回对应的error code,需要根据错误码来判断后续是否成功执行模型推理

参数:

  • session:Session 执行推理的Session对象

返回:错误码,具体含义如下表

value name 说明
0 NO_ERROR 没有错误,执行成功
1 OUT_OF_MEMORY 内存不足,无法申请内存
2 NOT_SUPPORT 有不支持的OP
3 COMPUTE_SIZE_ERROR 形状计算出错
4 NO_EXECUTION 创建执行时出错
10 INPUT_DATA_ERROR 输入数据出错
11 CALL_BACK_STOP 用户callback函数退出
20 TENSOR_NOT_SUPPORT resize出错
21 TENSOR_NEED_DIVIDE resize出错

返回类型:int


runSessionWithCallBack(session, begin_callback, end_callback)

该API本质上与runSession一致,但是提供了用户hook函数的接口,在运行session做网络推理,每层推理前前后会执行的begin_callback和end_callback 并根据返回值来决定是否继续执行

参数:

  • session:Session 执行推理的Session对象

  • begin_callback:function|lambda 每层推理前执行的回调函数,函数原型为:

    def begin_callback(tensors, name):
        # do something
        return True
    

    参数:

    • tensors:[Tensor] 该层的输入tensor

    • name:str 该层的名称 返回:True继续执行推理,False停止执行推理 返回类型:bool

  • end_callback:function|lambda 每层推理后执行的回调函数,函数原型同上

返回:同runSession

返回类型:int


runSessionWithCallBackInfo(session, begin_callback, end_callback)

该API与runSessionWithCallBack相似,但是回调函数中增加了Op的类型和计算量信息,可以用来评估模型的计算量

参数:

  • session:Session 执行推理的Session对象

  • begin_callback:function|lambda 每层推理前执行的回调函数,函数原型为:

    def begin_callback(tensors, opinfo):
        # do something
        return True
    

    参数:

    • tensors:[Tensor] 该层的输入tensor

    • opinfo:OpInfo 该层Op的相关信息,参考OpInfo 返回:True继续执行推理,False停止执行推理 返回类型:bool

  • end_callback:function|lambda 每层推理后执行的回调函数,函数原型同上

返回:同runSession

返回类型:int


cache()

将该Interpreter存储到当前线程的缓存中,以便多次使

参数:

  • None

返回:None

返回类型:None


removeCache()

将该Session从当前线程的缓存中移除

参数:

  • None

返回:None

返回类型:None


Example

import MNN
import MNN.cv as cv
import MNN.numpy as np

# 创建interpreter
interpreter = MNN.Interpreter("mobilenet_v1.mnn")
# 创建session
session = interpreter.createSession()
# 获取会话的输入输出张量
input_tensor = interpreter.getSessionInput(session)
output_tensor = interpreter.getSessionOutput(session)
# 将输入resize到[1, 3, 224, 224]
interpreter.resizeTensor(input_tensor, (1, 3, 224, 224))

# 读取图片,转换为size=(224, 224), dtype=float32,并赋值给input_tensor
image = cv.imread('cat.jpg')
image = cv.resize(image, (224, 224), mean=[103.94, 116.78, 123.68], norm=[0.017, 0.017, 0.017])
# HWC to NHWC
image = np.expand_dims(image, 0)
tmp_input = MNN.Tensor(image)
input_tensor.copyFrom(tmp_input)

# 执行会话推理
interpreter.runSession(session)

# 将输出结果拷贝到tmp_output中
tmp_output = MNN.Tensor((1, 1001), MNN.Halide_Type_Float, MNN.Tensor_DimensionType_Caffe)
output_tensor.copyToHostTensor(tmp_output)

# 打印出分类结果, 282为猫
print("output belong to class: {}".format(np.argmax(tmp_output.getData())))

MNN.Session [deprecated]

class Session

Session是MNN V2接口中推理数据的持有者。Session通过Interpreter创建;多个推理可以共用同一个模型,即,多个Session可以共用一个Interpreter。

不建议使用该接口,请使用nn代替


Session()

创建一个空Tensor

在实际使用中创建空Session没有意义,请使用Interpreter.createSession来创建Session

参数:

  • None

返回:Session对象

返回类型:Session


cache()

将该Session存储到当前线程的缓存中,以便多次使

参数:

  • None

返回:None

返回类型:None


removeCache()

将该Session从当前线程的缓存中移除

参数:

  • None

返回:None

返回类型:None


Example

完整用法请参考Interpreter的用法示例

import MNN

# 创建interpreter
interpreter = MNN.Interpreter("mobilenet_v1.mnn")
# 创建session
session = interpreter.createSession({
    'numThread': 2,
    'saveTensors': ('fc7',),
    'inputPaths': ('data',),
    'outputPaths': ('prob',)
})
session.cache()
# session1-3均使用session的cache
session1 = interpreter.createSession()
session2 = interpreter.createSession()
session3 = interpreter.createSession()
session.removeCache()
# session4不使用session的cache
session4 = interpreter.createSession()

MNN.OpInfo

class OpInfo

OpInfo是用来描述推理过程中一个执行层的具体信息的数据类型,其中包含了该层的Op类型,名称,计算量(flops)

用法参考runSessionWithCallBackInfo


OpInfo()

创建一个空OpInfo

在实际使用中创建空OpInfo没有意义,仅在runSessionWithCallBackInfo函数中用作返回信息

参数:

  • None

返回:OpInfo对象

返回类型:OpInfo


getName()

获取该层的名称

参数:

  • None

返回:该层的名称

返回类型:str


getType()

获取该层的算子类型

参数:

  • None

返回:该层的算子类型

返回类型:str


getFlops()

获取该层的计算量

参数:

  • None

返回:该层的计算量

返回类型:float


Example

import MNN

interpreter = MNN.Interpreter("mobilenet_v1.mnn")
session = interpreter.createSession()
# set input
# ...
def begin_callback(tensors, opinfo):
    print('layer name = ', opinfo.getName())
    print('layer op = ', opinfo.getType())
    # print('layer flops = ', opinfo.getFlops())
    for tensor in tensors:  
        print(tensor.getShape())
    return True
def end_callback(tensors, opinfo):
    print('layer name = ', opinfo.getName())
    print('layer op = ', opinfo.getType())
    # print('layer flops = ', opinfo.getFlops())
    for tensor in tensors:  
        print(tensor.getShape())
    return True
interpreter.runSessionWithCallBackInfo(session, begin_callback, end_callback)

MNN.Tensor [deprecated]

class Tensor

Tensor是MNN V2接口中的基础数据结构,是最基本的数据封装类型。Tensor存储了数据以及数据类型,形状等诸多信息,用户可以通过Tensor本身的函数获取这些信息。

不建议使用该接口,请使用Var代替


MNN.Halide_Type_*

描述Tensor的数据类型

  • 类型:PyCapsule

  • 枚举值:

    • Halide_Type_Double

    • Halide_Type_Float

    • Halide_Type_Int

    • Halide_Type_Int64

    • Halide_Type_String

    • Halide_Type_Uint8


MNN.Tensor_DimensionType_*

描述Tensor的数据排布格式

  • 类型:int

  • 枚举值:

    • Tensor_DimensionType_Caffe

    • Tensor_DimensionType_Caffe_C4

    • Tensor_DimensionType_Tensorflow


Tensor()

创建一个空Tensor

参数:

  • None

返回:Tensor对象

返回类型:Tensor

Tensor(var)

创建一个Tensor,并使用var的数据

参数:

  • var:Var 类型为Var的变量

Tensor(tensor_or_var, dimension)

创建一个Tensor,并使用tensor_or_var的数据, 并将数据排布格式设置为dimension

参数:

  • tensor_or_var:Tensor/Var 类型为Tensor或者Var的变量

  • dimension:MNN.Tensor_DimensionType_* 数据排布格式

Tensor(shape, dtype, dimension)

创建一个指定形状,数据类型和数据排布的Tensor, 数据未经初始化

参数:

  • shape:tuple Tensor形状

  • dtype:MNN.Halide_Type_* Tensor数据类型

  • dimension:MNN.Tensor_DimensionType_* 数据排布格式

Tensor(shape, dtype, value_list, dimension)

创建一个指定形状,数据类型, 数据和数据排布的Tensor, 数据拷贝自value_list, 能够将listtuplebytesndarrayPyCapsuleint指针等格式的数据转换成Tensor

注意:value_list仅在PYMNN_NUMPY_USABLE打开的情况下支持ndarray,移动端默认关闭

此函数在PYMNN_NUMPY_USABLE=OFF时不接受ndarray作为数据输入

参数:

  • shape:tuple Tensor形状

  • dtype:MNN.Halide_Type_* Tensor数据类型

  • value_list:ndarray/tuple/list/bytes/PyCapsule/int_addr 数据

  • dimension:MNN.Tensor_DimensionType_* 数据排布格式


getShape()

获取Tensor的形状。

参数:

  • None

返回:Tensor的数据形状

返回类型:Tuple


getDataType()

获取Tensor的数据类型。

参数:

  • None

返回:Tensor的数据类型

返回类型:MNN.Halide_Type_*


getDimensionType()

获取Tensor的持有的数据排布格式。

参数:

  • None

返回:Tensor持有的数据排布格式

返回类型:MNN.Tensor_DimensionType_*


getData()

获取Tensor的数据。

参数:

  • None

返回:Tensor的数据

返回类型:Tuple


getHost()

获取Tensor的数据指针。

参数:

  • None

返回:Tensor内部数据的数据指针

返回类型:PyCapsule, 可以参考PyCapsule介绍


copyFrom(from)

从from中拷贝数据到当前Tensor,可用此函数将数据拷贝到输入Tensor中。

参数:

  • from:Tensor - 拷贝的源Tensor

返回:是否拷贝成功

返回类型:bool


copyToHostTensor(to)

从当前Tensor拷贝数据到to,可用此函数将输出Tensor中的数据拷出。

参数:

  • to:Tensor - 拷贝的目标Tensor

返回:是否拷贝成功

返回类型:bool


getNumpyData()

获取Tensor的数据,返回numpy数据。 该API仅在PYMNN_NUMPY_USABLE=ON时生效,移动端默认关闭

参数:

  • None

返回:Tensor数据的numpy形式

返回类型:numpy.ndarray


Example

import numpy as _np
import MNN
import MNN.numpy as np
data = _np.array([1., 2., 3.], dtype=_np.float32)
# 创建Tensor
# 通过给定的tuple创建Tensor, 参数分别为:形状,数据类型,数据,数据排布格式
t1 = MNN.Tensor((1, 3), MNN.Halide_Type_Float, (1., 2., 3.), MNN.Tensor_DimensionType_Caffe)
# 通过Var创建Tensor
t2 = MNN.Tensor(np.array([1., 2., 3.])) # 与t1等价
# 通过ndarray创建Tensor
t3 = MNN.Tensor([1, 3], MNN.Halide_Type_Float, data, MNN.Tensor_DimensionType_Caffe)
# 通过bytes创建Tensor
t4 = MNN.Tensor([1, 3], MNN.Halide_Type_Float, data.tobytes(), MNN.Tensor_DimensionType_Caffe)
# 通过int类型的内存指针创建Tensor,使用该方法比直接用ndarray速度快,但是要求ndarray的内存必须连续
t5 = MNN.Tensor([1, 3], MNN.Halide_Type_Float, data.__array_interface__['data'][0], MNN.Tensor_DimensionType_Caffe)

print(t1.getShape()) # (1, 3)
print(t1.getDataType()) # <capsule object NULL at 0x7fe01e74ff30>
print(t1.getDimensionType()) # 1
print(t1.getData()) # (1.0, 2.0, 3.0)
print(t1.getHost()) # <capsule object NULL at 0x7fe01e5645a0>
print(t1.getNumpyData()) # [[1. 2. 3.]]

MNN.CVImageProcess [deprecated]

class CVImageProcess

CVImageProcess用于图像处理,该图像处理类提供了一下图像处理能力:

  • 图像格式转换,类似于cv2.cvtColor,通过设置sourceFormatdestFormat来实现

  • 图像数据类型转换,将uint8类型的图像转换为float32类型的数据

  • 图像的仿射变换,类似于cv2.resizecv2.warpAffine,通过设置CVMatrix 来实现

  • 对图像进行归一化,通过设置meannormal来实现; x = (x - mean) / normal

不建议使用该接口,请使用cv代替

MNN.CV_ImageFormat_*

描述图像格式的数据类型,支持RBG,RGBA,BGR,BGRA,GRAY,YUV_NV21类型

  • 类型:int

  • 枚举值:

    • CV_ImageFormat_BGR

    • CV_ImageFormat_BGRA

    • CV_ImageFormat_RGB

    • CV_ImageFormat_RGBA

    • CV_ImageFormat_GRAY

    • CV_ImageFormat_YUV_NV21


MNN.CV_Filter_*

描述图片变换时的插值类型,支持最近邻,双线性,双三次插值

  • 类型:int

  • 枚举值:

    • CV_Filter_NEAREST

    • CV_Filter_BILINEAL

    • CV_Filter_BICUBIC


MNN.CV_Wrap_*

描述图片变换时的填充方式,支持填0,重复,和最近值填充

  • 类型:int

  • 枚举值:

    • CV_Wrap_ZERO

    • CV_Wrap_REPEAT

    • CV_Wrap_CLAMP_TO_EDGE


CVImageProcess(config)

根据config创建一个图像处理类

参数:

  • config:dict 一个字典,其中的key和value的含义如表格所示

key value 说明
filterType MNN.CV_Filter_* 用于进行图像缩放的滤波类型,默认为:CV_Filter_NEAREST
sourceFormat MNN.CV_ImageFormat_* 用于对转换数据的数据格式进行定义,默认为:CV_ImageFormat_BGRA
destFormat MNN.CV_ImageFormat_* 用于对转换数据的数据格式进行定义,默认为:CV_ImageFormat_BGRA
wrap MNN.CV_Wrap_* 用于对转换后的图像进行填充,默认为:CV_Wrap_ZERO
mean tuple 用于对输入图像进行减均值处理,默认为:(0, 0, 0, 0)
normal tuple 用于对输入图像进行归一化处理,默认为:(1, 1, 1, 1)

返回:CVImageProcess对象

返回类型:CVImageProcess


setMatrix(matrix)

设置仿射变换矩阵

参数:

  • matrix:CVMatrix 图片仿射变换的变换矩阵, 参考CVMatrix

返回:None

返回类型:None


setPadding(value)

当填充类型为CV_Wrap_ZERP时,设置填充值,如果不设置则填充0

参数:

  • value:int 填充值,默认填充0

返回:None

返回类型:None


convert(src, iw, ih, stride, dst)

执行图像处理流程,将src中的数据按照config和matrix的要求进行转换,并将结果存入dst中

参数:

  • src:int|PyCapsule|tuple|ndarray 输入的图像数据,可以是指针(int, PyCapsule),也可以是数据(Tuple, ndarray)

  • iw:int 输入图像的宽度

  • ih:int 输入图像的高度

  • stride:int 输入图像的步长,指每行的字节数,输入0stride=iw * ichannel; 注意在处理YUV图像的时候必须传入stride

  • dst:Tensor 输出的图像Tensor

返回:None

返回类型:None


createImageTensor(dtype, width, height, channel, data)

创建一个存储图像的Tensor

该解口功能不完善,不建议使用

参数:

  • dtype:MNN.Halide_Type_* Tensor的数据类型

  • width:int 图像的宽度

  • height:int 图像的高度

  • channel:int 图像的通道数

  • data:NoneType 未使用参数

返回:存储图像的Tensor对象

返回类型:Tensor


Example

更多用法请参考CVMatrix中的Example

import MNN
import MNN.cv as cv

image = cv.imread('cat.jpg')
image_data = image.ptr
src_height, src_width, channel = image.shape
dst_height = dst_width = 224

# 对读入图像执行一下变换:
# 1. 图像格式转换:RGB -> BGR
# 2. 图像大小缩放:h,w -> 224,224
# 3. 图像类型变换:uint8 -> float32
# 4. 归一化处理:[0,255] -> [0,1]
dst_tensor = MNN.Tensor((1, dst_height, dst_width, channel), MNN.Halide_Type_Float, MNN.Tensor_DimensionType_Tensorflow)
image_processer = MNN.CVImageProcess({'sourceFormat': MNN.CV_ImageFormat_BGR,
                                      'destFormat': MNN.CV_ImageFormat_RGB,
                                      'mean': (127.5, 127.5, 127.5, 0),
                                      'filterType': MNN.CV_Filter_BILINEAL,
                                      'normal': (0.00784, 0.00784,0.00784, 1)})
#设置图像变换矩阵
matrix = MNN.CVMatrix()
x_scale = src_width / dst_width
y_scale = src_height / dst_height
matrix.setScale(x_scale, y_scale)
image_processer.setMatrix(matrix)
image_processer.convert(image_data, src_width, src_height, 0, dst_tensor)

MNN.CVMatrix [deprecated]

class CVMatrix

CVMatrix主要用在CVImageProcess中使用; CVMatrix是MNN图像处理中使用的类,用来描述仿射变化的矩阵值,其内部存储了一个3x3的float矩阵,并提供了基本的矩阵操作。 CVMatrix移植自Android系统使用的Skia引擎,主要用于设置从目标图像到源图像的变换矩阵, SKia文档

使用中一定要注意, CVMatrix表示的是「目标图像」到「源图像」的变换矩阵,平时我们在编写代码过程中,直觉上写出的是将源图像转换到目标图像,因此需要注意设置中需要以逆序来进行


CVMatrix()

创建一个默认的CVMatrix,其值如下:

| 1 0 0 |
| 0 1 0 |
| 0 0 1 |

参数:

  • None

返回:CVMatrix对象

返回类型:CVMatrix


setScale(sx, sy, |px, py)

Sets Matrix to scale by sx and sy, about a pivot point at (px, py).

设置矩阵比例缩放,比例缩放的值为sx和sy,缩放的中心点为(px, py)。缩放中心点不受矩阵的影响。

参数:

  • sx:int/float 水平缩放值

  • sy:int/float 垂直缩放值

  • px:int/float 中心点坐标,默认为0

  • py:int/float 中心点坐标,默认为0

返回:None

返回类型:None


preScale(sx, sy, |px, py)

Sets Matrix to Matrix multiplied by Matrix constructed from scaling by (sx, sy) about pivot point (px, py).

设置矩阵为矩阵乘以仿射变换矩阵,其中仿射变换矩阵为比例缩放矩阵,比例缩放的值为sx和sy,缩放的中心点为(px, py)。

Given:

          | A B C |                       | sx  0 dx |
Matrix =  | D E F |,  S(sx, sy, px, py) = |  0 sy dy |
          | G H I |                       |  0  0  1 |

where

dx = px - sx * px
dy = py - sy * py

sets Matrix to:

                              | A B C | | sx  0 dx |   | A*sx B*sy A*dx+B*dy+C |
Matrix * S(sx, sy, px, py) =  | D E F | |  0 sy dy | = | D*sx E*sy D*dx+E*dy+F |
                              | G H I | |  0  0  1 |   | G*sx H*sy G*dx+H*dy+I |

参数:

  • sx:int/float 水平缩放值

  • sy:int/float 垂直缩放值

  • px:int/float 中心点坐标,默认为0

  • py:int/float 中心点坐标,默认为0

返回:None

返回类型:None


postScale(sx, sy, |px, py)

Sets Matrix to Matrix constructed from scaling by (sx, sy) about pivot point (px, py), multiplied by Matrix.

设置矩阵为比例缩放矩阵,比例缩放的值为sx和sy,缩放的中心点为(px, py),然后乘以矩阵。

Given:

          | J K L |                       | sx  0 dx |
Matrix =  | M N O |,  S(sx, sy, px, py) = |  0 sy dy |
          | P Q R |                       |  0  0  1 |

where

dx = px - sx * px
dy = py - sy * py

sets Matrix to:

                              | sx  0 dx | | J K L |   | sx*J+dx*P sx*K+dx*Q sx*L+dx+R |
S(sx, sy, px, py) * Matrix =  |  0 sy dy | | M N O | = | sy*M+dy*P sy*N+dy*Q sy*O+dy*R |
                              |  0  0  1 | | P Q R |   |         P         Q         R |

参数:

  • sx:int/float 水平缩放值

  • sy:int/float 垂直缩放值

  • px:int/float 中心点坐标,默认为0

  • py:int/float 中心点坐标,默认为0

返回:None

返回类型:None


setRotate(degrees, |px, py)

Sets Matrix to rotate by degrees about a pivot point at (px, py). The pivot point is unchanged when mapped with Matrix. Positive degrees rotates clockwise.

设置矩阵为旋转矩阵,旋转的角度为degrees,旋转的中心点为(px, py)。缩放中心点不受矩阵的影响。角度为正时为顺时针旋转。

参数:

  • degrees:int/float 旋转角度

  • px:int/float 中心点坐标,默认为0

  • py:int/float 中心点坐标,默认为0

返回:None

返回类型:None


preRotate(degrees, |px, py)

Sets Matrix to Matrix multiplied by Matrix constructed from rotating by degrees about pivot point (px, py).

设置矩阵为矩阵乘以旋转矩阵,旋转的角度为degrees,旋转的中心点为(px, py)。

Given:

          | A B C |                        | c -s dx |
Matrix =  | D E F |,  R(degrees, px, py) = | s  c dy |
          | G H I |                        | 0  0  1 |

where

c  = cos(degrees)
s  = sin(degrees)
dx =  s * py + (1 - c) * px
dy = -s * px + (1 - c) * py

sets Matrix to:

                              | A B C | | c -s dx |   | Ac+Bs -As+Bc A*dx+B*dy+C |
Matrix * R(degrees, px, py) = | D E F | | s  c dy | = | Dc+Es -Ds+Ec D*dx+E*dy+F |
                              | G H I | | 0  0  1 |   | Gc+Hs -Gs+Hc G*dx+H*dy+I |

参数:

  • degrees:int/float 旋转角度

  • px:int/float 中心点坐标,默认为0

  • py:int/float 中心点坐标,默认为0

返回:None

返回类型:None


postRotate(degrees, |px, py)

Sets Matrix to Matrix constructed from rotating by degrees about pivot point (0, 0), multiplied by Matrix.

设置矩阵为旋转矩阵,旋转的角度为degrees,旋转的中心点为(0, 0),然后乘以矩阵。

Given:

          | J K L |                        | c -s dx |
Matrix =  | M N O |,  R(degrees, px, py) = | s  c dy |
          | P Q R |                        | 0  0  1 |

where

c  = cos(degrees)
s  = sin(degrees)
dx =  s * py + (1 - c) * px
dy = -s * px + (1 - c) * py

sets Matrix to:

                              |c -s dx| |J K L|   |cJ-sM+dx*P cK-sN+dx*Q cL-sO+dx+R|
R(degrees, px, py) * Matrix = |s  c dy| |M N O| = |sJ+cM+dy*P sK+cN+dy*Q sL+cO+dy*R|
                              |0  0  1| |P Q R|   |         P          Q          R|

参数:

  • degrees:int/float 旋转角度

  • px:int/float 中心点坐标,默认为0

  • py:int/float 中心点坐标,默认为0

返回:None

返回类型:None


setTranslate(dx, dy)

Sets Matrix to translate by (dx, dy).

设置矩阵为平移矩阵,平移的值为(dx, dy)。

参数:

  • dx:int/float 水平平移值

  • dy:int/float 垂直平移值

返回:None

返回类型:None


preTranslate(dx, dy)

Sets Matrix to Matrix multiplied by Matrix constructed from translation (dx, dy).

设置矩阵为矩阵乘以平移矩阵,平移的值为(dx, dy)。

Given:

          | A B C |               | 1 0 dx |
Matrix =  | D E F |,  T(dx, dy) = | 0 1 dy |
          | G H I |               | 0 0  1 |

sets Matrix to:

                      | A B C | | 1 0 dx |   | A B A*dx+B*dy+C |
Matrix * T(dx, dy) =  | D E F | | 0 1 dy | = | D E D*dx+E*dy+F |
                      | G H I | | 0 0  1 |   | G H G*dx+H*dy+I |

参数:

  • dx:int/float 水平平移值

  • dy:int/float 垂直平移值

返回:None

返回类型:None


postTranslate(dx, dy)

Sets Matrix to Matrix constructed from translation (dx, dy) multiplied by Matrix. This can be thought of as moving the point to be mapped after applying Matrix.

设置矩阵为平移矩阵,平移的值为(dx, dy),然后乘以矩阵。

Given:

          | J K L |               | 1 0 dx |
Matrix =  | M N O |,  T(dx, dy) = | 0 1 dy |
          | P Q R |               | 0 0  1 |

sets Matrix to:

                      | 1 0 dx | | J K L |   | J+dx*P K+dx*Q L+dx*R |
T(dx, dy) * Matrix =  | 0 1 dy | | M N O | = | M+dy*P N+dy*Q O+dy*R |
                      | 0 0  1 | | P Q R |   |      P      Q      R |

参数:

  • dx:int/float 水平平移值

  • dy:int/float 垂直平移值

返回:None

返回类型:None


setPolyToPoly(src, dst)

Sets Matrix to map src to dst. count must be zero or greater, and four or less. If count is zero, sets Matrix to identity and returns true. If count is one, sets Matrix to translate and returns true. If count is two or more, sets Matrix to map Point if possible; returns false if Matrix cannot be constructed. If count is four, Matrix may include perspective.

设置矩阵,将src的坐标列表映射到dst的坐标列表。坐标的数目需大于等于0,小于等于4。

  • 如果数目为0,将矩阵设置为单位矩阵

  • 如果数目为1,将矩阵设置为平移矩阵

  • 如果数目大于等于2,将矩阵设置为映射坐标(有可能没有正确的映射)

  • 如果数目为4,矩阵可能包含透视值

参数:

  • src:[float] 源点坐标列表

  • dst:[float] 目标点坐标列表

返回:None

返回类型:None


invert()

Sets inverse to reciprocal matrix, returning true if Matrix can be inverted. Geometrically, if Matrix maps from source to destination, inverse Matrix maps from destination to source. If Matrix can not be inverted, inverse is unchanged.

如果该CVMatrix可以求逆则对该CVMatrix的矩阵求逆,否则不变;几何意义上,如果该CVMatrix映射源到目标,则求逆后映射目标到源。

参数:

  • None

返回:None

返回类型:None


write(value)

Sets all values from input value. Sets matrix to: | sx skewX transX | | skewY sy transY | | persp0 persp1 persp2 |

将value值写入到CVMatrix中,value的值为float数组, 写入数据数量为 min(len(value), 9)

参数:

  • value:[float] 写入值

返回:None

返回类型:None


read()

Copies nine scalar values contained by Matrix into list, in member value ascending order: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, kMPersp0, kMPersp1, kMPersp2.

返回矩阵的9个值,以list的形式返回, 顺序为:kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, kMPersp0, kMPersp1, kMPersp2

参数:

  • None

返回:CVMatrix中的9个浮点值

返回类型:list


Example

import MNN
import MNN.cv as cv
import MNN.expr as expr
# CVMatrix创建
matrix = MNN.CVMatrix() # [[1., 0., 0.], [0., 1., 0.], [0., 0., 0.]]

# CVMatrix设置
matrix.setScale(2, 3, 4, 5) # [[2., 0., -4.], [0., 3., -10.], [-10., 0., 0.]]
matrix.preScale(-1, -3) # [[-2., 0., -4.], [0., -9., -10.], [-10., 0., 0.]]
matrix.setRotate(5) # [[0.996195, -0.087156, 0.0], [0.087156, 0.996195, 0.0], [0.0, 0.0, 1.0]]
matrix.setTranslate(5, 6) #[[1.0, 0.0, 5.0], [0.0, 1.0, 6.0], [0.0, 0.0, 1.0]]
matrix.setPolyToPoly([0, 0, 1, 1], [0, 1, 1, 0]) # [[0.0, 1.0, 0.0], [-1.0, 0.0, 1.0], [0.0, 0.0, 1.0]]
matrix.invert() # [[0.0, -1.0, 1.0], [1.0, 0.0, -0.0], [0.0, 0.0, 1.0]]

# 常见图像处理用法
image = cv.imread('cat.jpg')
image_data = image.ptr
src_height, src_width, channel = image.shape
dst_height = dst_width = 224
dst_tensor = MNN.Tensor((1, dst_height, dst_width, channel), MNN.Halide_Type_Float, MNN.Tensor_DimensionType_Tensorflow)
# 1. 图像缩放
image_processer = MNN.CVImageProcess({'sourceFormat': MNN.CV_ImageFormat_BGR,
                                      'wrap': MNN.CV_Wrap_REPEAT,
                                      'destFormat': MNN.CV_ImageFormat_BGR})
height_scale = float(src_height / dst_height)
width_scale = float(src_width / dst_width)
matrix.setScale(width_scale, height_scale)
image_processer.setMatrix(matrix)
image_processer.convert(image_data, src_width, src_height, 0, dst_tensor)
scale_img = expr.const(dst_tensor.getHost(), [dst_height, dst_width, channel], expr.NHWC).astype(expr.uint8)
cv.imwrite('CVMatrix_scale.jpg', scale_img)
# 2. 图像填充
scale = max(height_scale, width_scale)
matrix.setScale(scale, scale)
resize_height = int(src_height / scale)
resize_width = int(src_width / scale)
if (dst_height - resize_height) > (dst_width - resize_width): # 从目标图片到源图片, 因此偏移应该用负值
    matrix.postTranslate(0, -(dst_height - resize_height) // 2 * scale)
else:
    matrix.postTranslate(-(dst_width - resize_width) // 2 * scale, 0)
image_processer.setMatrix(matrix)
image_processer.convert(image_data, src_width, src_height, 0, dst_tensor)
pad_img = expr.const(dst_tensor.getHost(), [dst_height, dst_width, channel], expr.NHWC).astype(expr.uint8)
cv.imwrite('CVMatrix_pad.jpg', pad_img)
# 3. 图像裁剪
image_processer = MNN.CVImageProcess({'sourceFormat': MNN.CV_ImageFormat_BGR,
                                      'wrap': MNN.CV_Wrap_ZERO,
                                      'destFormat': MNN.CV_ImageFormat_BGR})
matrix.setScale(width_scale, height_scale)
offset_y = (resize_height - dst_height) / 2 * height_scale
offset_x = (resize_width - dst_width) / 2 * width_scale
matrix.postTranslate(offset_x, offset_y)
image_processer.setMatrix(matrix)
image_processer.convert(image_data, src_width, src_height, 0, dst_tensor)
crop_img = expr.const(dst_tensor.getHost(), [dst_height, dst_width, channel], expr.NHWC).astype(expr.uint8)
cv.imwrite('CVMatrix_crop.jpg', crop_img)
# 4. 图像旋转
matrix.setScale(1 / src_width, 1 / src_height)
matrix.postRotate(30, 0.5, 0.5)
matrix.postScale(dst_width, dst_height)
matrix.invert() # 由于设置的是源图片到目标图片的变换矩阵, 因此取逆
image_processer.setMatrix(matrix)
image_processer.convert(image_data, src_width, src_height, 0, dst_tensor)
rotate_img = expr.const(dst_tensor.getHost(), [dst_height, dst_width, channel], expr.NHWC).astype(expr.uint8)
cv.imwrite('CVMatrix_rotate.jpg', rotate_img)
CVMatrix_scale.jpg CVMatrix_pad.jpg
CVMatrix_scale.jpg CVMatrix_pad.jpg
CVMatrix_crop.jpg CVMatrix_rotate.jpg
CVMatrix_crop.jpg CVMatrix_rotate.jpg

expr.Var

class Var

Var是MNN V3及表达式接口中的基础数据结构,是对数据和表达式的封装类型。Var存储了计算表达式,数据以及数据类型,形状等诸多信息,用户可以通过Var本身的函数获取这些信息。

在numpy和cv接口中,Var的作用等价于numpy.ndarray


expr.dtype

描述Var的数据类型

  • 类型:Enum

  • 枚举值:

    • double

    • float

    • int

    • int64

    • uint8


expr.data_format

描述Var的数据排布格式

  • 类型:Enum

  • 枚举值:

    • NCHW

    • NC4HW4

    • NHWC


Var()

使用expr.constMNN.numpy.array创建Var

不要使用Var()来创建Var的变量,应该使用expr/numpy中提供的函数来创建Var


valid

该Var是否有效,如果无效,则不能使用, 作为表达式输入异常或无效的表达式的输出,Var都是无效的

返回:Var的数据形状

返回类型:[int]


shape

获取Var的形状

返回:Var的数据形状

返回类型:[int]


data_format

获取Var的数据排布格式

属性类型:只读

类型:expr.data_format


dtype

获取Var的数据类型

属性类型:只读

类型:expr.dtype


size

获取Var的元素个数

属性类型:只读

类型:int


name

获取Var的名称

属性类型:只读

类型:str


ndim

获取Var的维度数量

属性类型:只读

类型:int


ptr

获取Var的数据指针

属性类型:只读

类型:PyCapsule


op_type

获取Var对应表达式的Op类型

属性类型:只读

类型:str


fix_as_placeholder()

将Var作为Placeholder,执行该函数后可以对该变量执行写操作

注意:表达式中使用,numpy数值计算时勿使用

参数:

  • None

返回:None

返回类型:None


fix_as_const()

将Var作为Const,执行该操作会立即计算出该变量的值并设置为常量

注意:表达式中使用,numpy数值计算时勿使用

参数:

  • None

返回:None

返回类型:None


fix_as_trainable()

将Var作为Trainable,执行该操作会将该变量设置为可训练变量

注意:表达式中使用,numpy数值计算时勿使用

参数:

  • None

返回:None

返回类型:None


close()

将Var的输入设置为空

注意:表达式中使用,numpy数值计算时勿使用

参数:

  • None

返回:None

返回类型:None


copy_from(src)

将src设置为Var的输入

注意:表达式中使用,numpy数值计算时勿使用

参数:

  • src:Var 源变量

返回:None

返回类型:None


set_inputs(inputs)

将inputs设置为Var的输入

注意:表达式中使用,numpy数值计算时勿使用

参数:

  • inputs:[Var] 源变量

返回:None

返回类型:None


replace(src)

使用src替换掉该Var

注意:表达式中使用,numpy数值计算时勿使用

参数:

  • src:Var 替换变量

返回:None

返回类型:None


reorder(format)

将Var的数据排布格式设置为format

注意:表达式中使用,numpy数值计算时勿使用

参数:

  • format:expr.data_format 数据排布格式

返回:None

返回类型:None


resize(shape)

将Var的形状设置为shape

注意:表达式中使用,numpy数值计算时勿使用

参数:

  • shape:[int] 形状

返回:None

返回类型:None


read()

读取Var的数据,返回numpy.ndarray数据

注意:该API仅在PYMNN_NUMPY_USABLE打开的情况下有效,移动端默认关闭

参数:

  • None

返回:Var数据的numpy形式

返回类型:numpy.ndarray


read_as_tuple()

读取Var的数据,返回tuple数据

参数:

  • None

返回:Var数据的tuple形式

返回类型:tuple


write(data)

将data中的数据写入Var,data可以是numpy.ndarray或tuple类型

注意:该函数在使用numpy时经常被用作ndarray->Var的转换函数,在使用MNN.numpy时不需要使用该函数

参数:

  • data:tuple|ndarray 待写入数据

返回:None

返回类型:None


all(axis)

Var中指定轴的数据是否全不为0,相当于: for x in data: res &= x

要求Var的数据类型为int32

参数:

  • axis:[int] 指定的轴,默认为[-1]

返回:True全不为0,False包含0

返回类型:bool


any(axis)

Var中指定轴的数据是否至少有一个数据不为0,相当于: for x in data: res |= x

要求Var的数据类型为int32

参数:

  • axis:[int] 指定的轴,默认为[-1]

返回:True至少有一个数据不为0,False全部为0

返回类型:bool


argmax(axis)

返回Var中指定轴的最大值的索引

参数:

  • axis:[int] 指定的轴,默认为[-1]

返回:最大值的索引

返回类型:int


argmin(axis)

返回Var中指定轴的最小值的索引

参数:

  • axis:[int] 指定的轴,默认为[-1]

返回:最小值的索引

返回类型:int


sort(axis)

将Var按照axis的方向排序

参数:

  • axis:int 排序的轴,默认为[-1]

返回:排序后的Var

返回类型:Var


argsort(axis)

返回排序后各元素对应的位置

参数:

  • axis:[int] 排序的轴,默认为[-1]

返回:元素的对应顺序

返回类型:Var


astype(type)

将Var的数据类型设置为type

参数:

  • type:expr.dtype 数据类型

返回:数据类型为type的变量

返回类型:Var


copy()

返回一个拷贝的Var

参数:

  • None

返回:一个拷贝的Var

返回类型:Var


dot(b)

返回Var与b的点积

参数:

  • b:Var 另一个变量

返回:Var与b的点积

返回类型:Var


fill(value)

将Var的数据全部设置为value

参数:

  • value:scalar 填充值

返回:全部设置为value的变量

返回类型:Var


flatten()

将Var的数据展平,相当于 reshape(-1)

参数:

  • None

返回:展平的变量

返回类型:Var


max(axis)

返回Var指定轴的最大值

参数:

  • axis:[int] 指定的轴,默认为[-1]

返回:最大值

返回类型:float


mean(axis)

返回Var指定轴的均值

参数:

  • axis:[int] 指定的轴,默认为[-1]

返回:均值

返回类型:float


min()

返回Var中最小值

参数:

  • None

返回:最小值

返回类型:float


nonzero()

返回Var中不为0的元素的坐标

参数:

  • None

返回:不为0的元素的坐标

返回类型:(Var,)


prod()

返回Var的乘积

参数:

  • None

返回:乘积

返回类型:float


ptp()

返回Var的最大值与最小值的差

参数:

  • None

返回:最大值与最小值的差

返回类型:float


ravel()

返回Var的数据展平,相当于 reshape(-1)

参数:

  • None

返回:展平的变量

返回类型:Var


repeat(num)

将Var重复num次

参数:

  • num:int 重复次数

返回:重复num次的变量

返回类型:Var


reshape(shape)

将Var的数据reshape为shape

参数:

  • shape:[int] 新的形状

返回:形状为shape的变量

返回类型:Var


squeeze(axis)

将Var中指定轴且维度为1的维度移除

参数:

  • axis:[int] 要移除的轴, 默认为[-1]

返回:移除维度为1的变量

返回类型:Var


round()

将Var的数据四舍五入

参数:

  • None

返回:四舍五入的变量

返回类型:Var


sum(axis)

返回Var指定轴的和

参数:

  • axis:[int] 指定的轴, 默认为[-1]

返回:和

返回类型:Var


var(axis)

返回Var指定轴的方差

参数:

  • axis:[int] 指定的轴, 默认为[-1]

返回:标准差

返回类型:Var


std(axis)

返回Var指定轴的标准差

参数:

  • axis:[int] 指定的轴, 默认为[-1]

返回:标准差

返回类型:Var


swapaxes(axis1, axis2)

将Var的指定轴交换

参数:

  • axis1:int 指定的交换轴

  • axis2:int 指定的交换轴

返回:交换后的变量

返回类型:Var


transpose(axes)

将Var转置

参数:

  • axes:[int] 转置的轴顺序,默认为None, 将轴逆序排列

返回:转置后的变量

返回类型:Var


item(idx)

返回Var的第idx个元素,类似:var[idx]

参数:

  • idx:int 元素的索引

返回:元素的值

返回类型:Var


number_overload

  • +

  • -

  • *

  • /

  • %

  • **

  • abs()

compare_overload

  • ==

  • !=

  • <

  • <=

  • >

  • >=

sequence_overload

  • __iter__, __iternext__ 可以通过for item in var遍历Var

  • __len__ 返回最高维度的长度

  • __subscript__

    • int 如:x[0]

    • [int] 如:x[[0,2]]

    • Var 数据类型为int或bool,如: x[x > 0]

    • PySlice 如:x[:, 2], x[0:10:-1]

  • __ass_subscript____subscript__ 相同,是赋值操作

str_overload

  • __repr__, __str__ 在支持numpy的环境中打印格式与numpy一致,否则以tuple形式打印

not_impl_overload

  • float(var)

  • max(var1, var2)

  • if var:

  • 注意:以上均为未定义行为,请勿使用!!!


Example

import MNN.expr as expr
import MNN.numpy as np

# 创建Var变量
# 通过expr.const创建变量,参数分别为:数据,形状,数据排布,数据类型
x = expr.const([1., 2., 3., 4.], [2, 2], F.NCHW, F.float)  # array([[1., 2.], [3., 4.]], dtype=float32)
# 通过numpy.array创建变量,直接传入数据即可
y = np.array([2, 0])  # array([2, 0], dtype=int32)
# 通过numpy.ones创建变量,指定参数为形状
z = np.ones([4]) # array([1, 1, 1, 1], dtype=int32)

# Var的属性
x.shape # [2, 2]
x.size # 4
x.ndim # 2
x.dtype # dtype.float
x.data_format # data_format.NCHW
x.op_type # 'Const'

# 形状变化
x1 = x.reshape(1, 4) # array([[1., 2., 3., 4.]], dtype=float32)
x2 = x.transpose()  # array([[1., 3.], [2., 4.]], dtype=float32)
x3 = x.flatten() # array([1., 2., 3., 4.], dtype=float32)

# 运算符重载
x4 = x3 + z # array([3., 5., 7., 9.], dtype=float32)
x5 = x3 > z # array([0, 1, 1, 1], dtype=int32)

# 数学函数
x.max() # 4.0
x.std() # 1.1180340051651
x.sum() # 10.0

# 元素操作,可以通过索引、切片、迭代等方式进行操作
x[0] # array([1., 2.], dtype=float32)
x[0, 1] # 2.0
x[:, 0] # array([1., 3.], dtype=float32)
x.item(1) # 2.0
len(x) # 2
for item in x:
    print(item) # array([1., 2.], dtype=float32); array([3., 4.], dtype=float32)

# 数据类型转换:到ndarray, tuple, scalar
np_x = x.read() # array([[1., 2.], [3., 4.]], dtype=float32)
type(np_x) # <class 'numpy.ndarray'>
tuple_x = x.read_as_tuple() # (1.0, 2.0, 3.0, 4.0)
type(tuple_x) # <class 'tuple'>
scalar_x = x[0, 1] # 1.0
type(scalar_x) # <class 'float'>

nn._Module

class _Module

_Module时V3 API中用来描述模型的基类,维护了具体的计算图,可以执行推理和训练等操作;具体的模型类都继承自_Module; 可以通过nn.load_module_from_file加载模型来推理或训练现有模型;也可以通过继承_Module来实现自定义模型并训练推理


_Module()

创建一个空_Module

在实际使用中创建空_Module没有意义,请使用nn.load_module_from_file或继承实现自己的_Module

参数:

  • None

返回:_Module对象

返回类型:_Module


parameters

获取_Module的参数

属性类型:只读

类型:[Var]


name

获取_Module的名称

属性类型:只读

类型:str


is_training

获取_Module的是否为训练模式

属性类型:只读

类型:bool


forward(input)

模块前向传播,返回一个结果变量

参数:

  • input:Var|[Var] 前向传播输入变量

返回:前向传播输出变量

返回类型:Var


__call__(input)

forward相同


onForward(input)

模块前向传播,返回多个结果变量

参数:

  • input:Var|[Var] 前向传播输入变量

返回:前向传播输出变量

返回类型:[Var]


set_name(name)

设置_Module的名称

参数:

  • name:str 模块的名称

返回:None

返回类型:None


train(isTrain)

设置_Module的训练状态

参数:

  • isTrain:bool 是否为训练模式

返回:None

返回类型:None


load_parameters(parameters)

加载现有的参数

参数:

  • parameters:[Var] 参数值

返回:是否成功加载参数

返回类型:bool


clear_cache()

清除_Module的缓存,并递归清除子模块的缓存

参数:

  • None

返回:None

返回类型:None


_register_submodules(children)

注册子模块

参数:

  • children:[_Module] 子模块列表

返回:None

返回类型:None


_add_parameter(parameter)

添加参数

参数:

  • parameter:Var 参数值

返回:添加前的参数数量

返回类型:int


Example

import MNN
import MNN.nn as nn
import MNN.expr as expr

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.conv(1, 20, [5, 5])
        self.conv2 = nn.conv(20, 50, [5, 5])
        
        self.fc1 = nn.linear(800, 500)
        self.fc2 = nn.linear(500, 10)
    def forward(self, x):
        x = expr.relu(self.conv1(x))
        x = expr.max_pool(x, [2, 2], [2, 2])
        x = expr.relu(self.conv2(x))
        x = expr.max_pool(x, [2, 2], [2, 2])
        # some ops like conv, pool , resize using special data format `NC4HW4`
        # so we need to change their data format when fed into reshape
        # we can get the data format of a variable by its `data_format` attribute
        x = expr.convert(x, expr.NCHW)
        x = expr.reshape(x, [0, -1])
        x = expr.relu(self.fc1(x))
        x = self.fc2(x)
        x = expr.softmax(x, 1)
        return x
model = Net()

nn.RuntimeManager

class RuntimeManager

RuntimeManager持有运行时资源,在CPU时持有线程池,内存池等资源;在GPU时持有Kernal池等资源; 模型的执行需要使用RuntimeManager的资源,在同一个线程内RuntimeManager可以被共享使用,注意:不可跨线程使用


RuntimeManager()

创建一个空Tensor

在实际使用中创建空RuntimeManager没有意义,请使用nn.create_runtime_manager来创建RuntimeManager

参数:

  • None

返回:RuntimeManager对象

返回类型:RuntimeManager


set_cache(cache_path)

设置缓存文件路径,在GPU情况下可以把kernel和Op-info缓存到该文件中

参考:Interpreter.setCacheFile

参数:

  • cache_path:str

返回:None

返回类型:None


set_external(path)

设置额外数据文件路径,使用该文件中的数据作为权重或常量

参考:Interpreter.setExternalFile

参数:

  • path:str

返回:None

返回类型:None


update_cache()

在执行推理之后,更新GPU的kernel信息到缓存文件;应该在每次推理结束后指定该函数

参考:Interpreter.updateCacheFile

参数:

  • None

返回:None

返回类型:None


set_mode(mode)

设置会话的执行模式

参考:Interpreter.setSessionMode

参数:

  • mode:int 执行Session的模式,请参考mode

返回:None

返回类型:None


set_hint(mode, value)

设置执行时的额外信息

参考:Interpreter.setSessionMode

参数:

  • mode:int hint类型

  • value:int hint值

返回:None

返回类型:None


Example

import MNN.nn as nn
import MNN.cv as cv
import MNN.numpy as np
import MNN.expr as expr

config = {}
config['precision'] = 'low'
# 使用GPU后端
config['backend'] = 3
config['numThread'] = 4

# 创建RuntimeManager
rt = nn.create_runtime_manager((config,))
rt.set_cache(".cachefile")
# mode = auto_backend
rt.set_mode(9)
# tune_num = 20 
rt.set_hint(0, 20)
# 加载模型并使用RuntimeManager
net = nn.load_module_from_file('mobilenet_v1.mnn', ['data'], ['prob'], runtime_manager=rt)
# cv读取bgr图片
image = cv.imread('cat.jpg')
# 转换为float32, 形状为[224,224,3]        
image = cv.resize(image, (224, 224), mean=[103.94, 116.78, 123.68], norm=[0.017, 0.017, 0.017])
# 增加batch HWC to NHWC
input_var = np.expand_dims(image, 0)
# NHWC to NC4HW4
input_var = expr.convert(input_var, expr.NC4HW4)
# 执行推理
output_var = net.forward(input_var)
# NC4HW4 to NHWC 
output_var = expr.convert(output_var, expr.NHWC)
# 打印出分类结果, 282为猫
print("output belong to class: {}".format(np.argmax(output_var)))

optim.Optimizer

class Optimizer

Optimizer是一个优化器基类OptimizerSGDADAM都是该类的具体实现


Optimizer()

创建一个空Optimizer

在实际使用中创建空Optimizer没有意义,请使用optim.SGDoptim.ADAM来创建优化器实例

参数:

  • None

返回:Optimizer对象

属性类型:读写

返回类型:Optimizer


learning_rate

获取和设置优化器的学习率

属性类型:读写

类型:float


momentum

获取和设置优化器的动量

属性类型:读写

类型:float


momentum2

获取并设置优化器的第二个动量,只有ADAM优化器才有该属性

属性类型:读写

类型:float


weight_decay

获取并设置优化器的权重衰减因子

属性类型:读写

类型:float


eps

获取并设置优化器的eps系数,只有ADAM优化器才有该属性

属性类型:读写

类型:float


regularization_method

获取并设置优化器的正则化方法

属性类型:读写

类型:RegularizationMethod


step(loss)

反向传播以获得参数的梯度,并使用其相应的梯度更新参数

参数:

  • loss:Var 当前的损失函数值

返回:是否更新成功

返回类型:bool


示例

请参考optim

data.Dataset

class Dataset

Dataset是一个虚基类, 用户实现自己的Dataset需要继承基类并重写__getitem____len__方法

注意到__getitem__方法需要返回两个变量,一个为输入,另一个为目标

示例:

try:
    import mnist
except ImportError:
    print("please 'pip install mnist' before run this demo")
import MNN
import MNN.expr as expr
class MnistDataset(MNN.data.Dataset):
    def __init__(self, training_dataset=True):
        super(MnistDataset, self).__init__()
        self.is_training_dataset = training_dataset
        if self.is_training_dataset:
            self.data = mnist.train_images() / 255.0
            self.labels = mnist.train_labels()
        else:
            self.data = mnist.test_images() / 255.0
            self.labels = mnist.test_labels()
    def __getitem__(self, index):
        dv = expr.const(self.data[index].flatten().tolist(), [1, 28, 28], expr.NCHW)
        dl = expr.const([self.labels[index]], [], expr.NCHW, expr.uint8)
        # first for inputs, and may have many inputs, so it's a list
        # second for targets, also, there may be more than one targets
        return [dv], [dl]
    def __len__(self):
        # size of the dataset
        if self.is_training_dataset:
            return 60000
        else:
            return 10000

data.DataLoader

class DataSet

DataLoader数据加载器,支持数据批处理和随机采样


DataLoader(dataset, batch_size, shuffle, num_workers)

创建一个DataLoader

参数:

  • dataset:DataSet 数据集实例

  • batch_size:int 批处理大小

  • shuffle:bool 打乱数据集标记,默认为True

  • num_workers:int 线程数,默认为0

返回:数据加载器

返回类型:DataLoader


iter_number

返回总迭代次数,当剩余的数据在一个批次大小中没有满仍然会被加载

属性类型:只读

类型:int


size

获取数据集大小

属性类型:只读

类型:int


reset()

重置数据加载器,数据加载器每次用完后都需要重置

返回:None

返回类型:None


next()

在数据集中获取批量数据

返回:([Var], [Var]) 两组数据,第一组为输入数据,第二组为结果数据

返回类型:tuple

示例:

train_dataset = MnistDataset(True)
test_dataset = MnistDataset(False)
train_dataloader = data.DataLoader(train_dataset, batch_size = 64, shuffle = True)
test_dataloader = data.DataLoader(test_dataset, batch_size = 100, shuffle = False)
...
# use in training
def train_func(net, train_dataloader, opt):
    """train function"""
    net.train(True)
    # need to reset when the data loader exhausted
    train_dataloader.reset()
    t0 = time.time()
    for i in range(train_dataloader.iter_number):
        example = train_dataloader.next()
        input_data = example[0]
        output_target = example[1]
        data = input_data[0]  # which input, model may have more than one inputs
        label = output_target[0]  # also, model may have more than one outputs
        predict = net.forward(data)
        target = expr.one_hot(expr.cast(label, expr.int), 10, 1, 0)
        loss = nn.loss.cross_entropy(predict, target)
        opt.step(loss)
        if i % 100 == 0:
            print("train loss: ", loss.read())

Indices and tables