欢迎使用MNN文档
遇到问题请先查看文档和FAQ,如果没有答案请在Github提issue或在钉钉群提问。
MNN介绍
MNN
MNN是一个轻量级的深度神经网络引擎,支持深度学习的推理与训练。适用于服务器/个人电脑/手机/嵌入式各类设备。目前,MNN已经在阿里巴巴的手机淘宝、手机天猫、优酷等30多个App中使用,覆盖直播、短视频、搜索推荐、商品图像搜索、互动营销、权益发放、安全风控等场景。
架构图
在阿里巴巴中,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 训练模块,支持各平台训练
社区交流与反馈
钉钉群组:
钉钉群1:23329087
钉钉群2:23350225
钉钉群3:点击申请加入
历史论文
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
指令支持,支持了smmla
和bfmmla
指令MNN新增汇编预处理脚本,能够将汇编指令转换为
.inst
指令,降低新指令对编译器的依赖新增A16和M2 CPU family支持
新增Interp3D支持
MNN新增
NNAPI
后端,能够利用Android设备上的NPU/APU/DSP进行计算;支持float32
与float16
数据类型的模型推理。目前支持的算子如下:[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%左右。smmla1
smmla2
bfmmla
实现的GemmBF16
实测性能在规模为[1024, 1024, 1024]
时,性能相比fp16fmla
提升为91.53%(图中1024,1024,1024
项),接近理论性能;模型性能相比原来的bf16提升一倍以上。bfmmla1
bfmmla2
在执行Mobilenetv1时,NNAPI使用accelerator设备进行推理,在中端和高端设备上相比CPU单线程均有性能优势;在高端设备上相比CPU 4线程仍有性能优势;在其他类模型对比时,除卷积外其他算子较少的模型NNAPI均有优势,包含其他算子的模型会出现性能不如MNN-CPU的情况;在使用
float16
推理时,NNAPI平均性能相比MNN-CPU慢。nnapi
nnapi
CUDA性能优化,Depthwise卷积、Raster快速计算、Im2Col等优化,MobileNet/Squeezenet等模型性能提升
cuda
新增BinaryRelu-Fuse和对应的各后端实现,resnet模型性能提升
bianryrelu
其他
进行了部分代码重构(包括但不限于)
对于包含多个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算法优化
cuda
MNN-CoreML后端支持输出zero-copy
coreml
模型压缩
支持 Winograd Int8对kernel_size > 1
的量化卷积进行优化 ,离线量化工具(C++: quantized.out,python: mnnquant)json配置文件中增加"winogradOpt": true
,并将特征量化方法设置为"feature_quantize_method":"EMA"
即可使用
winograd
其他
进行了部分代码重构(包括但不限于)
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重写;
AVX512后端单线程性能对比.png
AVX512后端8线程性能对比.png
CUDA性能对比.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%的优化,性能如下:
bf16.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%的提升,且与业界主流引擎对比处于领先地位。
arm.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% 的加速。
x86.png
OpenCL后端
随着移动App中的部署的各类深度学习模型数量增多,MNN团队发现,CPU占用率居高不下,会影响App稳定性和用户体验。基于此判断,我们重点优化OpenCL后端的性能(与主流引擎相比已处于领先地位),并且与内部业务方共同设计面向GPU模型,达到性能、精度双高的模型。性能数据如下图:
opencl.png
模型压缩
ARM 浮点稀疏算子实现
随着CPU性能优化的边际收益的降低,为了获得更高的性能,需要从模型结构本身着手,设计、裁剪出合适目标硬件和推理引擎的模型结构,以获得最佳的精度和性能。基于此,MNN添加了随机稀疏和半结构化稀疏算子的ARM浮点实现 (原理见 ” Fast Conv Nets ” ),如下图所示:
sparse.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进行了支持。
性能优化
模型压缩
新添模型压缩的仅权值量化(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
目录下)使用表达式接口动态构图
使用表达式接口训练,训练耗时如下:
train.png
使用表达式执行训练量化(QAT)
性能优化
ARM 后端
ARMv82使用
asimdhp
和asimddp
扩展提速接近100%arm82.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,大致可以分为三个阶段:
concept.png
训练
在训练框架上,根据训练数据训练出模型的阶段。虽然当前MNN也提供了训练模型的能力,但主要用于端侧训练或模型调优。在数据量较大时,依然建议使用成熟的训练框架,如TensorFlow、PyTorch等。除了自行训练外,也可以直接利用开源的预训练模型。
转换
将其他训练框架模型转换为MNN模型的阶段。MNN当前支持Tensorflow(Lite)、Caffe、ONNX和TorchScript的模型转换。模型转换工具可以参考编译文档和使用说明。支持转换的算子,可以参考算子列表文档;在遇到不支持的算子时,可以尝试自定义算子,或在Github上给我们提交issue。此外,模型打印工具可以用于输出模型结构,辅助调试。除模型转换外,MNN也提供了模型量化工具,可以对浮点模型进行量化压缩。
推理
在端侧加载MNN模型进行推理的阶段。端侧运行库的编译请参考各平台的编译文档:iOS、Android、Linux/macOS/Ubuntu、Windows。我们提供了API接口文档,也详细说明了会话创建、数据输入、执行推理、数据输出相关的接口和参数。demo/exec
下提供了使用示例,如图像识别 demo/exec/pictureRecognition.cpp
,图像实例分割(人像分割)demo/exec/segment.cpp
,更多demo。此外,测试工具和benchmark工具也可以用于问题定位。
示例工程
C++ Demo
姿态检测
代码位置:demo/exec/multiPose.cpp
下载原始的Tensorflow模型 pose model
使用 模型转换工具 转换为 MNN 模型
执行姿态检测
./multiPose.out model.mnn input.png pose.png
效果示例:
input.png
pose.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
效果示例:
input.png
result.png
图像识别
代码位置:demo/exec/pictureRecognition.cpp
下载 mobilenet 模型并转换为 MNN 格式 第一个参数为 MNN 模型地址 第二个参数为图像地址 追加参数则为下一张图像地址
示例:
./pictureRecognition.out moiblenet.mnn Test.jpg
效果示例:
TestMe.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执行测试。
效果示例:
android_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
,既可编译运行。
效果示例:
ios_demo.png
Github Demo
欢迎开发者提供示例,可以在issue中提交自己的示例项目,审核通过后可以再此处展示
以下示例为Github开发者贡献,具体用法需参考相关代码;
编译宏介绍
MNN使用CMake构建项目,CMake中的宏定义列表如下:
宏名 | 宏说明 |
---|---|
MNN_USE_SYSTEM_LIB | 在使用opencl 和vulkan 时,使用系统库(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 | 构建删减版训练模块,不构建Dataset 与model ,该宏仅在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.so
,express/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设备
具体步骤
准备工作 (可选,修改 MNN Schema 后需要)
cd /path/to/MNN ./schema/generate.sh ./tools/script/get_model.sh # 可选,模型仅demo工程需要
本地编译
mkdir build && cd build && cmake .. && make -j8
Windows
环境要求
Microsoft Visual Studio >= 2017
cmake >= 3.13
powershell
Ninja
相关编译选项
同
Linux/MacOS
具体步骤
opencl/vulkan
*(可选)*下载GPU Caps Viewer,你可以通过这个工具来查看本机设备的详细信息(opencl、opengl、vulkan等)
sdk和驱动准备
opencl sdk,将opencl sdk目录的路径加到AMDAPPSDKROOT环境变量
vulkan sdk,将vulkan skd路径加入VULKAN_SDK环境变量,以备cmake查找
编译
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推理,减少内存占用,提升性能
具体步骤
在NDK download下载安装NDK,建议使用最新稳定版本;
在 .bashrc 或者 .bash_profile 中设置NDK环境变量,例如:export ANDROID_NDK=/Users/username/path/to/android-ndk-r14b
执行编译
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进行交叉编译。
获取交叉编译工具链
以Linaro工具链为例。首先从Linaro网页中按照宿主机以及交叉编译目标设备来选择合适的工具链。这里我们以
arm-linux-gnueabi
为例,点击网页上的链接,进入arm-linux-gnueabi页面。 按照宿主机类型(这里以X64 Linux为例)选择下载链接, 文件名形如 gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabi.tar.xz 下载后解压到任意目录。
配置交叉编译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++编译器的路径
以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
模型转换为JsonMNNRevert2Buffer
Json转换为模型OnnxClip
Onnx模型裁剪工具
训练框架
相关编译选项
MNN_BUILD_TRAIN
是否编译训练框架MNN_BUILD_TRAIN_MINI
对于移动端/嵌入式设备,建议设置MNN_BUILD_TRAIN_MINI=ON
,不编译内置的Dataset
,Models
,这部分在移动端/嵌入式设备上一般有其他解决方案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解码1aoa_nlu_decoder2.out
测试NLU解码2checkDir.out
测试两个文件夹是否一致checkFile.out
测试两个文件是否一致winogradExample.out
winograd示例winogradGenerateGLSL.out
winograd生成GLSLwinogradGenerateCL.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
和会话Session
。Interpreter
是模型数据的持有者;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
指定的备选后端运行。
推理路径包括由path
的inputs
到outputs
途径的所有算子,在不指定时,会根据模型结构自动识别。为了节约内存,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;
};
memory
、power
、precision
分别为内存、功耗和精度偏好。支持这些选项的后端会在执行时做出相应调整;若不支持,则忽略选项。
示例: 后端 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
来输入。另一方面,用户需要自行处理NC4HW4
、NHWC
数据格式上的差异。
对于非CPU后端,或不熟悉数据布局的用户,宜使用拷贝数据接口。
图像处理
MNN中提供了CV模块,可以帮助用户简化图像的处理,还可以免于引入opencv、libyuv等图片处理库。
支持目标Tensor为float或 uint8_t 的数据格式
支持目标Tensor为NC4HW4或NHWC的维度格式
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
中
通过
sourceFormat
和destFormat
指定输入和输出的格式,当前支持RGBA
、RGB
、BGR
、GRAY
、BGRA
、YUV_NV21、YUV_NV12
通过
filterType
指定插值的类型,当前支持NEAREST
、BILINEAR
和BICUBIC
三种插值方式通过
mean
和normal
指定均值归一化,但数据类型不是浮点类型时,设置会被忽略
图像变换矩阵
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
内部包含一系列缓存,为了避免内存的重复申请释放,建议将其作缓存,仅创建一次。我们使用ImageProcess
的convert
填充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
来读取数据。另一方面,用户需要自行处理NC4HW4
、NHWC
数据格式上的差异。
对于非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,支持控制流,所以当模型中有if
或while
时必须使用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
MNNTools
MNNTools提供目前主要是2个工具,用法可以参考mnnconvert和mnnquant
使用Python Module API
数据类型
Python中的Module API
与C++中的函数名略有区别,用法相似。主要数据类型如下:
推理流程
基本推理流程如下:
示例
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++基本一样。使用的主要数据类型如下:
Interpreter 解释器,持有模型资源
Session 会话,持有推理资源
Tensor 用来描述输入输出数据
CVImageProcess 图像处理模块
CVMatrix 用来描述图像的仿射变换
推理流程
基本推理流程如下:
示例
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的cv
和numpy
接口,其中cv
是对C++中tools/cv
实现的封装;numpy
则是对expr
接口的封装;这两个接口主要为了提高MNN的易用性,与opencv
与numpy
做到了再接口上的部分兼容,在用法和思路上基本一致。主要数据类型如下:
Var
cv
中的图像,numpy
中的ndarray
主要用法
cv
和numpy
主要用作模型的前后处理部分,和一些数值计算任务。比如从图片直接读取数据后一般需要执行颜色空间变换,数据类型变换,缩放,裁剪等操作,这些可以用cv
模块函数实现;模型输出的结果可能需要做一些额外的变换和计算,这些可以用numpy
模块函数实现。
示例
使用cv
与numpy
中的函数做前后处理,执行模型推理的例子
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 | 随机定点数 |
排序,搜索,计数
函数名 | 功能 |
---|---|
sort,lexsort,argsort | 排序 |
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使用
概念说明
表达式
表达式是一个延迟计算引擎,它提供如下功能:
数值计算
模型搭建
基于数值计算的能力,Expr API 可用于模型推理,但效率相比session/module 较低,不建议采用这种方式做模型推理。
表达式计算原理如下:
expr.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.hpp
和MathOp.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
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推理模型。如果此模型的精度不满足要求,则可以通过训练量化来提高量化模型的精度。
使用步骤:
首先通过其他训练框架训练得到原始float模型;
编译MNNConverter模型转换工具;
使用MNNConverter将float模型转成MNN统一格式模型,因为要进行再训练,建议保留BN,Dropout等训练过程中会使用到的算子,这可以通过MNNConverter的 –forTraining 选项实现;
参考MNN_ROOT/tools/train/source/demo/mobilenetV2Train.cpp 中的 MobilenetV2TrainQuant demo来实现训练量化的功能,下面以MobilenetV2的训练量化为例,来看一下如何读取并将模型转换成训练量化模型
观察准确率变化,代码保存下来的模型即为量化推理模型
// 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训练量化的基本原理如下图所示
image.png
以int8量化为例,首先要理解全int8推理的整个过程,全int8推理,即feature要量化为int8,weight和bias也要量化为int8,输出结果可以是float或者是int8,视该卷积模块的后面一个op的情况而定。而训练量化的本质就是在训练的过程中去模拟量化操作的影响,借由训练来使得模型学习并适应这种影响,以此来提高最后量化模型的准确率。
因此在两种 FakeQuant 模块中,我们的主要计算为
image.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%,我们测出来比他们要高一点,原因是因为我们使用的预处理代码上有细微差别所致。
使用训练量化的一些建议
模型转换时保留BatchNorm和Dropout等训练中会用到的算子,这些算子对训练量化也有帮助
要使用原始模型接近收敛阶段的训练参数,训练参数不对,将导致训练量化不稳定
学习率要调到比较小
我们仅对卷积层实现了训练量化,因此如果用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格式文件,以方便对比原始模型参数,同时也可以对模型进行修改。
可以使用MNNConvert
或MNNDump2Json
将模型转换成Json文件;在对模型进行修改后还可以使用MNNConvert
或MNNRevert2Buffer
将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
先准备模型文件,进入tools/script目录下执行脚本
get_model.sh
;打开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压缩工具箱包含两个组成部分:
MNN框架自身提供的压缩工具(输入MNN模型,输出MNN模型)
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计算) | - |
怎么用?
如果只想使用离线压缩方法,可以将模型转换为MNN模型之后使用对应的工具进行压缩。这类压缩算法不需要进行训练finetune,所以通常运行得很快。
如果离线压缩方法的精度不满足要求,且能够进行训练finetune的话,可以使用mnncompress中提供的压缩算法插件将原始模型进行压缩,得到压缩之后的模型和压缩信息描述文件,然后将这两个文件输入到MNN模型转换工具得到最终的MNN压缩模型。需要训练的压缩算法可以提供更好的精度,但需要一定的时间进行finetune训练,此finetune训练需要的时间一般比模型从0开始训练要少很多。
这些算法中有些是可以叠加使用的,以取得更好的压缩效果。推荐使用pipeline(其中方框中的算法均为可选,叠加压缩算法若精度不好,可选择使用):
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.out,mnnquant.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的技术路线如下图所示:
image.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模型压缩工具
线性超参数化工具
环境要求
python3
PyTorch >= 1.2.0
总体使用流程
通过本工具得到训练好的float onnx模型,以及MNN模型压缩参数文件
通过MNN转换工具,输入这两个文件,得到最终的MNN稀疏模型
支持的op,使用建议
目前支持普通卷积nn.Conv2d(group=1,非1*1)的过参数化
该算法应在模型从零开始训练时使用,因为其中的参数会重新进行初始化
该算法的特点是:设计小模型 –> 训练线性过参数化大模型 –> 保存时合并为原始小模型结构。因此你可以设计一个小模型,然后用此算法训练提高小模型精度,最后推理部署仍使用小模型结构
得到的小模型后续仍可叠加剪枝和量化等压缩算法
参考论文: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() ''' 将过参数化之后的模型合并为原始模型结构 参数: 无参数 返回值: 合并之后的模型,深拷贝,和过参数化模型不共享内存 '''
低秩分解工具
环境要求
python3
PyTorch >= 1.2.0
总体使用流程
通过本工具得到分解之后的float onnx模型,以及MNN模型压缩参数文件
通过MNN转换工具,输入这两个文件,得到最终的MNN压缩模型
支持的op,使用建议
目前支持nn.Conv2d(group>1不支持)和nn.Linear的分解
建议从已经训练好的float模型开始finetune
分解之后的模型调整好学习率,准确率一般会迅速恢复
使用方法
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实例 '''
自动剪枝工具
环境要求
python3
PyTorch >= 1.8.0
总体使用流程
通过本工具得到训练好的float onnx模型,以及MNN模型压缩参数文件
通过MNN转换工具,输入这两个文件,得到最终的MNN稀疏模型**(如果直接部署稀疏的float模型,而不叠加量化,那么转换时需要使用MNNConvert的 –weightQuantBits 8 参数进行转换,才会进行稀疏编码,否则模型大小将不变)**
支持的op,使用建议
目前支持torch.nn.Conv2d,torch.nn.Linear
优化器超参使用模型收敛阶段的超参,学习率可以在收敛阶段学习率的基础上再调小
支持的剪枝算法
SNIPLevelPruner:最细粒度的随机剪枝算法,稀疏单位为单个权值。一般需要剪枝比例达到80%以上才有加速,此方法主要进行压缩。
SIMDOCPruner:稀疏单位为1*4的剪枝算法,一般需要30%以上剪枝比例才能加速,精度比通道剪枝好。
TaylorFOChannelPruner:通道剪枝算法,此算法是将conv2d的一整个filter剪掉,因此此filter对应的输出通道会失效,剪枝完模型是一个规整的模型,不需要后端进行特殊加速,但为了将剪掉的filter从模型中真正去掉,需要使用MNNConverter进行转换,转换过程中会分析剪枝通道之间的依赖关系,并完成最终的转换。
使用方法
建议从训练好的float模型进行finetune
创建Pruner对象,对原始模型进行转换,然后用转换之后的模型进行训练
在train过程中,调用pruner的do_pruning方法进行剪枝
导出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)
模型稀疏之后,可以进一步使用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文件中已存在相关压缩信息 '''
权值量化工具
环境要求
python3
PyTorch >= 1.8.0
总体使用流程
通过本工具得到训练好的float onnx模型,以及MNN模型压缩参数文件
通过MNN转换工具,输入这两个文件,得到最终的MNN量化模型
支持的op,使用建议
目前支持torch.nn.Conv2d,torch.nn.Linear的量化
仅对权值进行量化,一般8bit权值量化并不需要finetune,直接用MNN的转换工具的“–weightQuantBits”进行转换即可,但也可使用本工具进行测试精度,或者finetune到更低的bit数,如4bit,2bit,推理速度和float一致
可与剪枝工具叠加使用
使用方法
建议从训练好的float模型进行finetune
创建WeightQuantizer对象,对原始模型进行转换,然后用转换之后的模型进行训练
保存onnx模型之前,去掉插入的节点
保存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)
训练完之后,使用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 '''
训练量化工具
环境要求
python3
PyTorch >= 1.8.0
总体使用流程
通过本工具得到训练好的float onnx模型,以及MNN模型压缩参数文件
通过MNN转换工具,输入这两个文件,得到最终的MNN量化模型
支持的op,使用建议
建议优先试验此工具的离线量化功能,精度如果可以则不需要训练量化,速度快,节省时间
目前支持torch.nn.Conv2d,torch.nn.Linear的量化
优化器超参使用模型收敛阶段的超参,学习率可以在收敛阶段学习率的基础上再调小
模型的第一层或者前面几层对精度影响较大,可以尝试跳过不进行量化
relu6建议使用nn.ReLU6,F.relu6使用opset 9导出到onnx会有问题
支持的训练量化算法
LSQQuantizer: 改进naive QAT算法,scale会在网络训练中学习更新
使用方法
建议从训练好的float模型进行finetune
创建Quantizer对象,对原始模型进行转换,然后用转换之后的模型进行训练
保存onnx模型之前,去掉插入的节点
保存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)
将onnx模型和生成的 “compress_params.bin” 文件输入到MNN转换工具中进行转换,得到最终的MNN量化模型:
mnnconvert --modelFile quant_model.onnx --MNNModel quant_model.mnn --framework ONNX --bizCode MNNTest --compressionParamsFile compress_params.bin
作为离线量化工具使用
去掉训练过程中的参数更新部分,并将
LSQQuantizer
的mode
设置为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模型压缩工具
低秩分解工具
环境要求
支持TensorFlow1.X,在TensorFlow1.X最后一个版本1.15.3上测试通过;TensorFlow2.0暂未测试
总体使用流程
通过本工具得到分解训练之后的float pb模型,以及MNN模型压缩参数文件
通过MNN转换工具,输入这两个文件,得到最终的压缩模型
支持的op,使用建议
支持 Conv2D, MatMul两种op
建议从已经训练好的float模型开始finetune
优化器超参使用模型收敛阶段的超参
使用方法
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已经被分解 '''
自动剪枝工具
环境要求
支持TensorFlow1.X,在TensorFlow1.X最后一个版本1.15.3上测试通过;TensorFlow2.0暂未测试
大致算法原理
使用权值mask梯度信息,权值mask梯度的绝对值为权值的重要性表示,全局对所有权值的重要性大小进行排序,将指定比例的重要性较小的权值剪掉,参考论文 SNIP: Single-shot Network Pruning based on Connection Sensitivity
支持的op,使用建议
目前支持Conv2D,带可学习参数MatMul需要使用reshape+1*1 Conv2D卷积的组合来实现
支持的剪枝算法
SNIPLevelPruner:最细粒度的随机剪枝算法,稀疏单位为单个权值。一般需要剪枝比例达到80%以上才有加速,此方法主要进行压缩。
SIMDOCPruner:稀疏单位为1*4的剪枝算法,一般需要30%以上剪枝比例才能加速,精度比通道剪枝好。
TaylorFOChannelPruner:通道剪枝算法,以卷积filter为单位进行剪枝,剪枝完仍然是整齐的模型,无须框架底层特殊实现,但需要用MNN转换工具进行转换才能获得最终剪枝模型。
使用方法
前提:一个预先训练好的模型checkpoint(也可以从头开始训练稀疏模型)
首先恢复出已经训练好的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')
获取网络中的(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())
计算模型中各网络连接的敏感度,并保存为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")
构建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)
正常训练,注意,训练时每次反向更新之后,执行一下剪枝步骤即可**(第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)
测试过程中保存一下剪枝相关的参数文件:
pruner.save_compress_params("compress_params.bin", sess)
训练完成保存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())
模型稀疏之后,可以进一步使用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文件中已存在相关压缩信息 '''
权值量化工具
环境要求
支持TensorFlow1.X,在TensorFlow1.X最后一个版本1.15.3上测试通过;TensorFlow2.0暂未测试
大致算法原理
仅对权值进行int8量化,一般权值量化并不需要finetune,直接用MNN的转换工具的“–weightQuantBits”进行转换即可,但也可使用本工具进行测试精度,或者finetune到更低的bit数;可与剪枝工具叠加使用;
支持的op,使用建议
权值量化8bit情况下一般不会损失精度,不需要训练,而模型参数压缩4倍左右,推理速度和float一致
目前支持Conv2D, DepthwiseConv2dNative,带参数MatMul
可以结合剪枝一起使用,以获得更大的压缩倍数
使用方法
读取已经训练好的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)
正常训练,注意添加第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)
训练完成,去掉插入的权值量化算子**(第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())
使用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()
''' 去掉权值量化相关算子 '''
训练量化工具
环境要求
支持TensorFlow1.X,在TensorFlow1.X最后一个版本1.15.3上测试通过;TensorFlow2.0暂未测试
总体使用流程
通过本工具得到训练好的float pb模型,以及量化参数文件
通过MNN转换工具,输入这两个文件,得到最终的量化模型
支持的op,使用建议
建议从训练好的float模型进行finetune
目前支持 Conv2D, DepthwiseConv2dNative,带参数MatMul(EMA,LSQ量化算法支持)
使用OAQ量化算法需要量化矩阵乘 MatMul ,请将其利用 reshape 和 **Conv2D(1 * 1 卷积) **组合实现
优化器超参使用模型收敛阶段的超参,学习率可以适当调节一下
模型的第一层或者前面几层对精度影响较大,可以尝试跳过不进行量化
支持的训练量化算法
EMAQuantizer: naive QAT算法,scale采用滑动平均更新
LSQQuantizer: 改进QAT算法,scale会在网络训练中学习更新
OAQQuantizer: 改进LSQ算法,int8计算中累加到int16而不是通常的累加到int32,在低端手机上可以获得比普通量化30%~50%的加速,低端设备上加速更明显
使用方法
搭建好网络之后,在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()
正常训练,注意,训练和测试过程中需要给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)
测试过程中保存训练得到的量化参数文件,示例代码:
# 如果先进行了剪枝训练,那么需要将剪枝中得到的 "compress_params.bin" 文件在下方传入,并设置 append=True quantizer.save_compress_params("compress_params.bin", sess, append=False)
训练完之后,我们得到了一些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())
使用MNN模型转换工具,输入frozen pb和量化参数文件,得到最终的量化模型,示例:
mnnconvert --modelFile frozen.pb --MNNModel quant_model.mnn --framework TF --bizCode AliNNTest --compressionParamsFile compress_params.bin
测试训练量化结果需注意的地方
如果你是训练和测试分开两个文件,测试时用python代码重新构图,在构图时给bn的is_training又直接赋值为python bool False,然后加载训练的checkpoint进行测试,那你重新构建的图里面是没有训练量化节点的,此时需要在你构图之后,创建session之前,恢复变量之前,你再新建一个和训练时候一样的Quantizer对象(复制粘贴),向测试图中插入训练量化节点即可,然后根据训练时候保存的checkpoint恢复训练得到的变量,就可以了,当然测试的时候也需要给 quant_state 赋值为False,即可。
另外注意,有的使用者在训练和测试的时候给模型加了不同的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.MonitoredSession,tf.train.MonitoredTrainingSession进行训练,并使用tf.train.SessionRunHook进行训练流程控制。为方便此类用户使用,我们提供了相关压缩工具的hook,初始化hook之后,即可使用。 这些hook的构造方法和使用的压缩算法文档中一致,在此不再详述。 注意,有些hook需要在构造反向图之前创建,例如量化的hook。目前提供的hook有:
SNIPLevelPrunerHook
SIMDOCPrunerHook
TaylorFOChannelPrunerHook
LowRankDecompositionHook
EMAQuantizerHook
OAQQuantizerHook
LSQQuantizerHook
可视化工具
可视化的效果:
可视化效果
在详细调研了市面上比较主流的可视化工具后,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
文件.Python
:pip 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-format
和git-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中使用驼峰命名法,如CityCat
和bigDoghouse
。
前缀
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头文件:
class需要注释说明类的用途;
所有_非override_的_public_方法需要通过注释说明方法、各参数的用途和返回值信息(若有);
所有public成员变量(一般为结构体成员)需说明其用途;
示例:
/**
* @brief function description
* @param param param description
* @return return value description
*/
int example(int param) {
// ...
}
特殊限制
C++
出于便于性能分析的理由,除引入的三方代码外,MNN代码需遵循:
class禁止运算符重载
class禁止实现拷贝构造函数、重载赋值运算符
struct禁止自定义构造函数
出于控制库文件大小的理由,除引入的三方代码外,MNN代码需遵循:
不允许使用stream,如cout/cin、 ifstream/ofstream、 istringstream/ostringstream等
不允许使用C++异常机制,即try/catch/throw
汇编
出于跨平台编译的诉求,MNN代码需遵循:
所有汇编都需要有C语言等价实现,编译时,通过宏选择平台对应的汇编实现或C语言实现
入参、返回值类型必须是32位/64位兼容类型,即指针、size_t、ssize_t之一,禁用其他类型,避免编译环境不同导致调用规约偏差
严格按照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
时,需要释放所有DYNAMIC
和DYNAMIC_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执行的各个周期都会收到回调,onResizeBegin
和onResizeEnd
在调整内存分配前后调用(op的onResize
会在此间调用);onExecuteBegin
和onExecuteEnd
在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 算子实现包括如下步骤
添加Schema描述(必须)
添加维度计算(若算子输出维度和输入一致可跳过)
添加几何计算实现(可选,如果实现几何计算,无须后续在各后端添加算子实现)
添加各后端算子实现(可选,选择需要部分进行实现)
image.png
添加算子的流程
image.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模型转换
添加转换类 在
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
、析构、opType
和type
函数。其中,run
函数用于解析模型的proto文件得到参数,然后赋值给flatbuffer自定义参数。参数srcNode
保存有输入输出节点信息,可以根据输入输出节点在tempGraph
中找到TmpNode
。调用函数find_attr_value(const tensorflow::NodeDef& node, const char* key, tensorflow::AttrValue& value)
获得对应参数的值。
注册转换类:
REGISTER_CONVERTER(MyCustomOpTf, MyCustomOp);
添加映射 在
OpMapper.hpp
中添加相应的TensorFlow Op名字到MNN Op名字的映射:
{"OpName1", MNN::OpType_MyCustomOp},
{"OpName2", MNN::OpType_MyCustomOp},
处理Op附带的Const 如果Const不作为此Op的参数,而是看成一个单独的Op,可以忽略此步骤;如果Op要把Const当成参数,要在文件
TmpGraph.cpp
里修改函数_genMinGraph()
,把相应Const节点的isCovered
属性设置为true。
TensorFlow Lite模型转换
添加转换类 在
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模型转换
添加转换类 在
/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();
};
实现run
、opType
、type
函数,在run
函数中解析caffe参数得到具体参数。其中参数parameters保存有Op的参数信息,weight保存有卷积、BN等数据参数。
注册转换类:
static OpConverterRegister<MyCustomOp> a("MyCustomOp");
ONNX模型转换
添加转换类 在
/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.hpp
、CPUMyCustomOp.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. 实现onResize
和onExecute
在onResize
中,调用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实现
添加Shader 在
source/backend/Metal
目录下添加MetalMyCustomOp.metal
,并添加进Xcode工程。metal可以参考目录下已有实现。
2. 实现类声明
在source/backend/Metal
目录下添加MetalMyCustomOp.hpp
和MetalMyCustomOp.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. 实现onResize
和onExecute
不同于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实现
添加Shader 在
source/backend/vulkan/execution/glsl
目录下添加具体的shader(*.comp)。若输入内存布局为NC4HW4
,则按image
实现,否则采用buffer实现。可以参考目录下已有实现。然后,执行makeshader.py
脚本编译Shader。
2. 实现类声明
在目录source/backend/vulkan/execution/
下添加VulkanMyCustomOp.hpp
和VulkanMyCustomOp.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;
};
实现 实现函数
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实现
添加Kernel 在
source/backend/opencl/execution/cl
目录添加具体的kernel(*.cl)。目前feature map均使用image2d
实现。可以参考目录下已有实现。然后执行opencl_codegen.py
来生成kernel映射。
2. 实现类声明
在目录source/backend/opencl/execution/
下添加MyCustomOp.h
和MyCustomOp.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;
};
实现 实现函数
onResize
(可选)、onExecute
。执行完毕返回NO_ERROR。
4. 注册实现类
OpenCLCreatorRegister<TypedCreator<MyCustomOp<cl_data_t>>> __my_custom_op(OpType_MyCustomOp);
添加OpenGL实现
添加Shader 在
source/backend/opengl/glsl
下添加具体的shader(*.glsl),不用加文件头,feature map 均采用image3d
表示。可以参考目录下已有实现。而后,在source/backend/opengl
目录下执行makeshader.py
。添加Executor 在
source/backend/opengl/execution/
目录下添加GLMyCustomOp.h
和GLMyCustomOp.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;
};
实现 实现函数
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-modules
或
export 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 网络
如果确定输入形状正确,并且执行了
resizeTensor
和resizeSession
;可以打开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
将需要的中间结果的 tensor 名字加到 config.saveTensors ,然后用这个 config 创建 session.
在 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 后端使用时找不到. 可以按如下方式之一解决:
设置 MNN_SEP_BUILD = OFF (cmake -DMNN_SEP_BUILD=OFF). 把 opencl / vulkan 后端统一编入 MNN 的 so.
自己在使用的代码中加上 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 时的内存访问问题
性能相关
使用 GPU 时,调用 copyToHostTensor / copyFromHostTensor 非常慢
GPU 后端调用 copy 的时间包含两个部分
异构数据拷贝
等待数据相关的GPU计算完成
对 GPU 后端而言,在数据被要求对用户可见(比如复制 output tensor 数据出来)之前,是允许异步执行的。 在数据被用户要求可见之时,会等待相应的异步操作完成。 因此有可能 复制 output tensor 的过程包括了等待 GPU 算子异步执行完成,导致缓慢。
GPU 为什么比 CPU 跑得慢?
有如下原因:
相当一部分移动端设备 (如 pre-iPhone 8), GPU 算力不足,加以内存带宽的限制,本身不如 CPU.
比如 Apple 由 Imagination 切换到自己的 GPU in iPhone 8, 导致 GPU 性能下降(不如 iphone 7) ,相对地, CPU 是提升的.
存在 GPU 不支持的算子,这些算子会切换到 CPU 执行,相应的输入输出需要 CPU - GPU 之间的内存拷贝,产生额外耗时
模型本身计算量小或者不易并行,发挥不了 GPU 并行计算的优势.
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模型如何加密
加密与破解是攻防的较量,端侧加密很难做到绝对安全。 可以通过构造独有的模型格式来增加反向的难度,按照以下步骤操作可以得到独特的模型格式:
针对
schema/default/*.fbs
下的文件,对参数顺序,枚举类顺序进行重新排序;比如:可以重新调整MNN.fbs
中OpType
的顺序;重新调整CaffeOp.fbs
中Convolution2DCommon
成员变量的顺序;执行
schema/generate.sh
重新生成flatbuffers
头文件;重新编译
MNN
库文件,Convert
等所有工具;使用新的工具重新转换模型;
在端侧使用新模型和新的
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();
当不再需要执行createSession
和resizeSession
的时候,可以调用此函数释放解释器中持有的模型资源,会释放模型文件大小的内存
参数:
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);
将Session
中Tensor
的数据更新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做网络推理,每层推理前前后会执行的before
和end
并根据返回值来决定是否继续执行
参数:
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
获取的信息类型,使用SessionInfoCodeptr
将信息存储在该指针中
返回:是否支持获取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对象,一般为输入tensordims
新的形状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
尺寸大小,默认为4type
张量的维度类型,默认为CAFFE
返回:具有维度大小和类型的张量
Tensor
构造函数
Tensor(const Tensor* tensor, DimensionType type = CAFFE, bool allocMemory = true);
创建一个与给定张量形状相同的张量
参数:
tensor
形状提供者type
张量的维度类型,默认为CAFFEallocMemory
是否为数据获取内存
返回:给定张量形状相同的张量
~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
源图像格式,默认为RGBAdestFormat
目的图像格式,默认为RGBAmeans
给定方法,默认为nullptrmeanCount
给定方法数量,默认为0normals
给定常量,默认为nullptrnormalCount
给定常量数量,默认为0dstTensor
给定张量,默认为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,默认为0outputStride
如果为0,设置为ow * outputBpp,默认为0type
支持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_ofw
图像宽度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
要映射的rectdst
要映射到的rectstf
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
要映射的rectdst
要映射到的rectstf
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[]
要映射的rectdst[]
要映射到的rectcount
在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
被比较的矩阵ab
被比较的矩阵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
被比较的矩阵ab
被比较的矩阵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;
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在乘法之前被转置,默认为falsetranposeB
如果为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_formatscale
缩放因子
返回: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的切片,默认为Falseadj_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
默认为falsereverse
是否逆向,默认为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/NC4HW4dtype
目的变量的元素类型
返回:变量
_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/NC4HW4type
目标变量的数据类型
返回:一个不可变变量
_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
填充模式,默认为VALIDstride
卷积步长,默认为{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
填充模式,默认为VALIDstride
卷积步长,默认为{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
填充模式,默认为VALIDstride
卷积步长,默认为{1, 1}dilate
扩张操作:控制kernel点(卷积核点)的间距,默认值为{1, 1}group
控制分组卷积,默认不分组,为1组pads
填充操作,默认为{0, 0}relu
是否修正线性单元,默认为faslerelu6
修正线性单元6,默认为faslenbits
默认为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
填充模式,默认为VALIDstride
步长,默认为{1, 1}dilate
扩张操作:控制kernel点(卷积核点)的间距,默认值为{1, 1}group
控制分组卷积,默认不分组,为1组pads
填充操作,默认为{0, 0}relu
是否修正线性单元,默认为faslerelu6
修正线性单元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
填充模式,默认为VALIDstride
步长,默认为{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
填充模式,默认为VALIDstride
步长,默认为{1, 1}dilate
扩张操作:控制kernel点(反卷积核点)的间距,默认值为{1, 1}group
控制分组反卷积,默认不分组,为1组pads
填充操作,默认为{0, 0}relu
是否修正线性单元,默认为faslerelu6
是否修正线性单元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
填充模式,默认为VALIDpads
填充操作,默认为{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
填充模式,默认为VALIDpads
填充操作,默认为{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_Floataxis
默认值是-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
输入变量,默认为0endMask
输入变量,默认为0ellipsisMask
输入变量,默认为0newAxisMask
输入变量,默认为0shrinkAxisMask
输入变量,默认为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
输入变量,默认为0endMask
输入变量,默认为0ellipsisMask
输入变量,默认为0newAxisMask
输入变量,默认为0shrinkAxisMask
输入变量,默认为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
填充模式,默认为VALIDstride
滑动窗口的步长对每个维度的输入进行卷积,必须与格式指定的尺寸顺序相同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
填充模式,默认为VALIDpads
填充操作,默认为{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_Intaxis
一个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,所有值必须为>= 1crops
形状为[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_Floatalpha
Alpha因子(正浮动)
返回:一个变量,具有与功能相同的类型
_Threshold
MNN_PUBLIC VARP _Threshold(VARP features, float threshold);
给定一个输入值x,如果x > threshold,它计算输出为1.0,如果x <= threshold,则为0.0
参数:
features
一个变量,必须Halide_Type_Floatthreshold
阈值
返回:一个变量,具有与功能相同的类型
_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.0flip
如果为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
指示不同类之间是否共享位置,默认为truebackground_label_id
默认为0nms_threshhold
nms的阈值nms_topk
nms的topkcode_type
表示bbox编码模式,默认= CORNERvariance_encoded_in_target
variance是否被编码,默认为falsekeep_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方法,目前只支持falsecentersize_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
填充模式,默认为VALIDstride
卷积步长,默认为{1, 1}dilate
扩张操作:控制kernel点(卷积核点)的间距,默认值为{1, 1}group
控制分组卷积,默认不分组,为1组pads
填充操作,默认为{0, 0}relu
是否修正线性单元,默认为faslenbits
默认为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
填充模式,默认为VALIDstride
卷积步长,默认为{1, 1}dilate
扩张操作:控制kernel点(卷积核点)的间距,默认值为{1, 1}group
控制分组卷积,默认不分组,为1组pads
填充操作,默认为{0, 0}relu
是否修正线性单元,默认为fasleinputZeroPoint
输入变量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
填充模式,默认为VALIDstride
卷积步长,默认为{1, 1}dilate
扩张操作:控制kernel点(卷积核点)的间距,默认值为{1, 1}group
控制分组卷积,默认不分组,为1组pads
填充操作,默认为{0, 0}relu
是否修正线性单元,默认为faslescaleIn
向内扩展值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中进行插值的方法,默认为BILINEARpaddingMode
对于越界的位置在网格中采用填充方式,默认为GRID_SAMPLE_PADDING_ZEROSalignCorners
默认为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类型,操作的坐标轴,默认为-1arg
是否返回排序元素的index, 默认为falsedescend
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的一个scoremaxDetections
一个整数张量,代表最多可以利用NMS选中多少个边框iouThreshold
IOU阙值展示的是否与选中的那个边框具有较大的重叠度,默认为-1scoreThreshold
默认为-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_like
指Var
或者可以转换为Var
的数据,如:list
,tuple
,scalar
等
const(value_list, shape, data_format, dtype)
根据输入数据创建一个Const
类型的Var
;该函数是创建的Var
的最基本函数,
能够将list
,tuple
,bytes
,ndarray
,PyCapsule
,int指针
等格式的数据转换成Var
注意:value_list
仅在PYMNN_NUMPY_USABLE打开的情况下支持ndarray
,移动端默认关闭
参数:
value_list:ndarray/list/tuple/bytes/PyCapsule/int_addr
输入数据shape:[int]
构造Var
的形状data_format:data_format
数据排布格式,参考data_formatdtype: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
是否全部回收,目前回收方式True
和False
没有区别
返回: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
输入变量,仅支持int32y: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
输入变量,仅支持int32y: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
输入变量,仅支持int32y: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
输入变量,仅支持int32y: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是否为转置矩阵,默认为falsetransposeB
布尔值,用于判定b是否为转置矩阵,默认为false
返回:a @ b 的值,dtype是float32
返回类型:Var
示例:
>>> expr.matmul([[1,2],[3,4]], [[1,1],[2,2]])
array([[0., 1.],
[0., 3.]], 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 时填充输出值的标量,默认为1offValue: float
输入变量,定义在indices[j] != i 时填充输出值的标量,默认为0axis: 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
输入变量,默认是NCHWdtype: 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
输入变量,填充模式,默认是VALIDpads: 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
输入变量,填充模式,默认是VALIDpads: 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.0max : 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
输入变量,默认为0end_mask : int
输入变量,默认为0ellipsis_mask : int
输入变量,默认为0new_axis_mask : int
输入变量,默认为0shrink_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],默认为floatlow : float
输入变量,随机值范围下限,默认为0high : float
输入变量,随机值范围上限,默认为1seed0 : int
输入变量,随机因子,默认为0seed1 : 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],因为所有值都必须大于或等于 1crops : 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] 的形状,因为所有值必须大于或等于 1paddings : 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为NC4HW4axes : axis_like
输入变量,int类型,指定计算均值和方差的轴,默认是[2,3]shift : var_like
输入变量,int类型,未在当前实现中使用,默认是Nonekeep_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, 默认为falsedescend : 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的一个scoremax_detections : int
输入变量,一个整数张量,代表最多可以利用NMS选中多少个边框iou_threshold : float
输入变量,IOU阙值展示的是否与选中的那个边框具有较大的重叠度,默认为0score_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
输出变量的最小值,默认为-128max : int
输出变量的最大值,默认为127zero : 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为int8scale : Var
反量化的scale值,dtype为floatzero : 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为int8roi : Var
反量化的scale值,dtype为floatpooledHeight : int
反量化的zero值,默认为0pooledWidth : int
反量化的zero值,默认为0
返回:roipooling结果
返回类型:Var
示例:
TODO
roi_align(input, roi, pooledHeight, pooledWidth, spatialScale, samplingRatio, aligned, poolType, outputGrad, backwardDiff)
roialign
参数:
input : Var
输入变量,dtype为int8roi : Var
反量化的scale值,dtype为floatpooledHeight : int
pooling的pooledHeight : int
反量化的zero值,默认为0
返回:roialign结果
返回类型:Var
示例:
TODO
以下函数为框架开发者使用函数,普通用户不建议使用!
load_as_dict(fileName)
[deprecated]
从文件中加载模型,并将模型转换为计算图,以dict
的形式返回计算图的所有节点名称和Var
不建议使用该接口
参数:
fileName:str
模型文件路径
返回:加载的模型计算图,其key
为str
,value
为Var
返回类型: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
形式,其key
为str
,value
为Var
返回:计算图的输入输出,其中输入输出都为dict
形式,其key
为str
,value
为Var
返回类型:(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.save
和expr.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兼容参数
默认为Nonedtype:numpy兼容参数
默认为Nonecasting: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
被复制的Varrepeats: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兼容参数
默认为Nonesubok:numpy兼容参数
默认为Noneshape:[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兼容参数
默认为Nonesubok:numpy兼容参数
默认为Noneshape:[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兼容参数
默认为Nonekeepdims: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兼容参数
默认为Nonesubok:numpy兼容参数
默认为None
返回:创建的Var
返回类型:Var
示例
>>> np.array([1, 2, 3])
array([1, 2, 3])
vdot(a, b)
作用等同与 numpy
中的 np.vdot
函数,对输入的2个变量做点乘。
根据a
和b
的维度进行如下计算:
如果都是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兼容参数
默认为Nonekeepdims: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兼容参数
默认为Nonekeepdims: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兼容参数
默认为Nonekeepdims:bool
是否保留指定轴的维度
返回:计算得到的变量
返回类型:Var
orscalar
示例
>>> 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兼容参数
默认为Nonekeepdims: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兼容参数
默认为Nonekeepdims:bool
是否保留指定轴的维度
返回:计算得到的变量
返回类型:Var
orscalar
示例
>>> 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兼容参数
默认为Nonekeepdims: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个变量做点乘。
根据a
和b
的维度进行如下计算:
如果都是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兼容参数
默认为Nonekeepdims: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
被拆分的Varindices_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
被复制的Varreps: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兼容参数
默认为Nonesubok:numpy兼容参数
默认为Noneshape:[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=Falseretstep:,返回值是否包含步长;如果包含则返回(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兼容参数
默认为Nonekeepdims:bool
是否保留指定轴的维度
返回:计算得到的变量
返回类型:Var
orscalar
示例
>>> 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兼容参数
默认为Nonekeepdims: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=Falseretstep:(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兼容参数
默认为Nonesubok: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兼容参数
默认为Nonekeepdims: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
被拆分的Varindices_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
被拆分的Varindices_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兼容参数
默认为Noneorder: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兼容参数
默认为Nonekeepdims: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兼容参数
默认为Nonekeepdims: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兼容参数
默认为Nonekeepdims:bool
是否保留指定轴的维度
返回:计算得到的变量
返回类型:Var
orscalar
示例
>>> 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个变量x1
和x2
执行计算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
被拆分的Varindices_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兼容参数
默认为Nonesubok:numpy兼容参数
默认为Noneshape:[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兼容参数
默认为Noneorder: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个变量x1
和x2
执行计算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
交换的维度1axis2: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=Falseretstep:(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兼容参数
默认为Nonekeepdims: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兼容参数
默认为Nonekeepdims:bool
是否保留指定轴的维度
返回:计算得到的变量
返回类型:Var
orscalar
示例
>>> 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个变量x1
和x2
执行计算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兼容参数
默认为Nonekeepdims:bool
是否保留指定轴的维度
返回:计算得到的变量
返回类型:Var
orscalar
示例
>>> 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兼容参数
默认为Nonekeepdims: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兼容参数
默认为Nonekeepdims: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_format
为NHWC
shape
是[h, w, c]
dtype
是uint8
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
copyTo.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
flip.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
rotate.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是list
ofuint8
代表编码后的图像数据序列
返回类型: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)
cat.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
cvtColor.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
cvtColorTwoPlane.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
bilateralFilter.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
blur.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
boxFilter.jpg
dilate(src, kernel, |iterations, borderType)
通过使用特定的结构元素对图像进行扩张,参考: dilate
参数:
src:Var
输入图像kernel:Var
结构元素iterations:int
迭代次数,可选,默认为1borderType: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
dilate.jpg
erode(src, kernel, |iterations, borderType)
通过使用特定的结构元素对图像进行腐蚀,参考: erode
参数:
src:Var
输入图像kernel:Var
结构元素iterations:int
迭代次数,可选,默认为1borderType: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
erode.jpg
filter2D(src, ddepth, kernel, |delta, borderType)
度图像执行二维卷积,参考: filter2D
参数:
src:Var
输入图像ddepth:int
图像的深度kernel:Var
卷积核delta:float
加到卷积结果的偏移量,可选,默认为0borderType: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
filter2D.jpg
GaussianBlur(src, ksize, sigmaX, |sigmaY, borderType)
使用高斯滤镜模糊图像,参考: GaussianBlur
参数:
src:Var
输入图像ksize:[int]
kernel大小sigmaX:float
X 方向的高斯核标准差sigmaY:float
Y方向的高斯核标准差;如果 sigmaY 为零,则设置为等于 sigmaX,可选,默认为0borderType:int
边界类型,可选,默认为cv.BORDER_DEFAULT
返回:模糊后的图像
返回类型:Var
示例:
>>> img = cv.imread('cat.jpg')
>>> img = cv.GaussianBlur(img, [5, 5], 3)
>>> cv.imwrite('GaussianBlur.jpg', img)
True
GaussianBlur.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
卷积核大小,可选,默认为1scale:float
缩放因子,可选,默认为1delta:float
加到结果的偏移量,可选,默认为0borderType:int
边界类型,可选,默认为cv.BORDER_DEFAULT
返回:拉普拉斯算子计算结果
返回类型:Var
示例:
>>> img = cv.imread('cat.jpg')
>>> img = cv.Laplacian(img, -1, 3)
>>> cv.imwrite('Laplacian.jpg', img)
True
Laplacian.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
pyrDown.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
pyrUp.jpg
Scharr(src, ddepth, dx, dy, |scale, delta, borderType)
使用Scharr算子计算图像导数,参考: Scharr
参数:
src:Var
输入图像ddepth:int
图像的深度dx:int
导数x的阶数dy:int
导数y的阶数scale:float
缩放因子,可选,默认为1delta:float
加到结果的偏移量,可选,默认为0borderType: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方向的kernelky:int
y方向的kerneldelta:float
加到结果的偏移量,可选,默认为0borderType: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的大小,可选,默认为3scale:float
缩放因子,可选,默认为1delta:float
加到结果的偏移量,可选,默认为0borderType: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的大小,可选,默认为3borderType:int
边界类型,可选,默认为cv.BORDER_DEFAULT
返回:spatialGradient计算结果
返回类型:Var
sqrBoxFilter(src, ddepth, ksize, |normalize, borderType)
计算与过滤器重叠的像素值的归一化平方和,参考: sqrBoxFilter
参数:
src:Var
输入图像ddepth:int
图像的深度ksize:[int]
kernel的大小normalize:bool
是否归一化,可选,默认为trueborderType: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
sqrBoxFilter.jpg
getAffineTransform(src, dst)
计算两组三个顶点之间仿射变换矩阵,参考: getAffineTransform
参数:
src:[float]
输入的一组顶点,类型为list。里面为6个 float元素,分别代表三个顶点的 x, ydst:[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, ydst:[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
getRectSubPix.jpg
getRotationMatrix2D(center, angle, scale)
作用等同与 OpenCV
中 Geometric 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)
作用等同与 OpenCV
中 Geometric 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)
作用等同与 OpenCV
中 Geometric Image Transformations
模块的remap 函数,用于图像重映射。
不支持borderMode与borderValue
参数:
src:Var
输入的图像map1:Var
x坐标映射map2:Var
y坐标映射interpolation:int
插值方式,仅支持cv.INTER_NEAREST
和cv.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
remap.jpg
resize(src, dsize, |fx, fy, interpolation, code, mean, norm)
作用等同与 OpenCV
中 Geometric Image Transformations
模块的resize 函数,用于放缩图像。
该函数在兼容OpenCV函数的基础上,额外增加了3个参数可选参数:code, mean, norm可以额外完成cvtColor和typeas的功能
参数:
src:Var
输入的图像dsize:tuple
放缩后的大小fx:float
水平方向的放缩因子,如果为0,则自动计算,默认为0fy:float
竖直方便的放缩因子,如果为0,则自动计算,默认为0interpolation:int
放缩的插值方法,默认为cv.INTER_LINEAR
code:int
可以在缩放时转换颜色空间,默认为-1
不执行转换mean:[float]
转换为float的归一化的均值,默认为空不转换为floatnorm:[float]
转换为float的归一化的标准差,默认为空不转换为float
返回:放缩后的图像
返回类型:类型为 Var
示例:
>>> img = cv.imread('cat.jpg')
>>> img = cv.resize(img, [100, 100])
>>> cv.imwrite('resize.jpg', img)
True
resize.jpg
warpAffine(src, M, dsize, |flag, borderMode, borderValue, code, mean, norm)
作用等同与 OpenCV
中 Geometric 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的归一化的均值,默认为空不转换为floatnorm:[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
warpAffine.jpg
warpPerspective(src, M, dsize, flag, borderMode, borderValue)
作用等同与 OpenCV
中 Geometric 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
warpPerspective.jpg
adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)
作用等同与 OpenCV
中 Miscellaneous 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
adaptiveThreshold.jpg
blendLinear(src1, src2, weight1, weight2)
作用等同与 OpenCV
中 Miscellaneous 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)
作用等同与 OpenCV
中 Miscellaneous 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
threshold.jpg
findContours(image, mode, method, offset)
作用等同与 OpenCV
中 Structural Analysis and Shape Descriptors
模块的findContours 函数,对二值图像进行轮廓查找,查找得到的结果可以用作contourArea
,fillPoly
和drawContours
的参数使用。
注意:该实现未计算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)
作用等同与 OpenCV
中 Structural 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)
作用等同与 OpenCV
中 Structural 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)
作用等同与 OpenCV
中 Structural 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)
作用等同与 OpenCV
中 Structural 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)
作用等同与 OpenCV
中 Connected 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)
作用等同与 OpenCV
中 Structural 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)
作用等同与 OpenCV
中 Drawing 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
line.jpg
arrowedLine(img, pt1, pt2, color, thickness, lineType, shift, tipLength)
作用等同与 OpenCV
中 Drawing 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
arrowedLine.jpg
circle(img, center, radius, color, thickness, lineType, shift)
作用等同与 OpenCV
中 Drawing 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
circle.jpg
rectangle(src, pt1, pt2, color, thickness, lineType, shift)
作用等同与 OpenCV
中 Drawing 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
rectangle.jpg
drawContours(img, contours, contourIdx, color, thickness, lineType)
作用等同与 OpenCV
中 Drawing 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
drawContours.png
fillPoly(img, contours, color, lineType, shift, offset)
作用等同与 OpenCV
中 Drawing 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
fillPoly.png
calcHist(imgs, channels, mask, histSize, ranges, accumulate)
作用等同与 OpenCV
中 Histograms
模块的calcHist 函数,计算图像的直方图。
参数:
imgs:[Var]
需要计算的图像channels:[int]
需要计算的通道mask:Var
需要计算的图像的掩码,本函数实现不支持maskhistSize:[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
是否动态图,默认为Falseshape_mutable:bool
是否在内部控制流中形状变化,默认为Falserearrange:bool
是否重新排列输入变量,默认为Falsebackend: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
参数:
config:str
配置信息,参考createRuntime
返回:创建的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
是否深度卷积,默认为Falsebias:bool
是否使用偏置,默认为Truepadding_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
维度,默认为4momentum:float
动量,默认为0.99epsilon: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
,并提供了SGD
和ADAM
优化器实现;主要用于训练阶段迭代优化
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.9weight_decay:float
权重衰减,默认为0.0regularization_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.9momentum2:float
动量2,默认为0.999weight_decay:float
权重衰减,默认为0.0eps:float
正则化阈值,默认为1e-8regularization_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=float
,shape=(batch_size, num_classes)
onehot_targets:Var
onehot编码的标签,dtype=float
,shape=(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=float
,shape=(batch_size, num_classes)
onehot_targets:Var
onehot编码的标签,dtype=float
,shape=(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=float
,shape=(batch_size, num_classes)
onehot_targets:Var
onehot编码的标签,dtype=float
,shape=(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=float
,shape=(batch_size, num_classes)
onehot_targets:Var
onehot编码的标签,dtype=float
,shape=(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=float
,shape=(batch_size, num_classes)
onehot_targets:Var
onehot编码的标签,dtype=float
,shape=(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
,可以用来创建Sessionsecond:为
tuple
ofbool
;代表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对象,一般为输入tensorshape: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]
该层的输入tensorname: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]
该层的输入tensoropinfo: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
,
能够将list
,tuple
,bytes
,ndarray
,PyCapsule
,int指针
等格式的数据转换成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
,通过设置sourceFormat
和destFormat
来实现图像数据类型转换,将
uint8
类型的图像转换为float32
类型的数据图像的仿射变换,类似于
cv2.resize
和cv2.warpAffine
,通过设置CVMatrix 来实现对图像进行归一化,通过设置
mean
和normal
来实现;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
输入图像的步长,指每行的字节数,输入0
则stride=iw * ichannel
; 注意在处理YUV图像的时候必须传入stridedst: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_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.const
或MNN.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缓存到该文件中
参数:
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)
设置会话的执行模式
参数:
mode:int
执行Session的模式,请参考mode
返回:None
返回类型:None
set_hint(mode, value)
设置执行时的额外信息
参数:
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是一个优化器基类Optimizer
,SGD
和ADAM
都是该类的具体实现
Optimizer()
创建一个空Optimizer
在实际使用中创建空Optimizer没有意义,请使用optim.SGD
或optim.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
打乱数据集标记,默认为Truenum_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())