docs/start/python.md
新手? 如果你是第一次使用 MNN,建议先阅读 Python 快速开始(5分钟) 了解基本流程,然后回到本页查看进阶内容。
import torch
class MyModel(torch.nn.Module):
def __init__(self):
super(MyModel, self).__init__()
self.conv1 = torch.nn.Conv2d(1, 128, 5)
def forward(self, x):
return torch.relu(self.conv1(x))
input_tensor = torch.rand((1, 1, 128, 128), dtype=torch.float32)
model = MyModel()
torch.onnx.export(
model, # model to export
(input_tensor,), # inputs of the model,
"my_model.onnx", # filename of the ONNX model
input_names=["input"], # Rename inputs for the ONNX model
dynamo=False # True or False to select the exporter to use
)
mnnconvert -f ONNX --modelFile user.onnx --MNNModel user.mnn
mnnconvert -f TF --modelFile user.pb --MNNModel user.mnn
mnnconvert -f TFLITE --modelFile user.tflite --MNNModel user.mnn
pip install MNN 失败(可能是当前系统和python版本不支持),可以按如下方式步骤编译安装pymnncd pymnn/pip_package
python3 build_deps.py llm
python3 setup.py install
import MNN
import MNN.numpy as np
x = np.array([[1.0, 2.0], [3.0, 4.0]])
print(x)
y = x * 2
print(y)
import MNN.cv as cv2
image = cv2.imread("cat.jpg")
image = cv2.resize(image, (224, 224))
image = image[..., ::-1]
print(image.mean([0, 1]))
打印输出:
The device supports: i8sdot:1, fp16:1, i8mm: 0, sve2: 0
array([[1., 2.],
[3., 4.]], dtype=float32)
array([[2., 4.],
[6., 8.]], dtype=float32)
array([125.76422 , 135.97044 , 85.656685], dtype=float32)
MNN.cv 和 MNN.numpy 产出的数据结构即为 MNN 推理所需要的张量 MNN.expr.VARP
# x0, x1 是输入,y0, y1 是输出
net = MNN.nn.load_module_from_file("user.mnn", ["x0", "x1"], ["y0", "y1"])
y = net.forward([x0, x1])
y0 = y[0]
y1 = y[1]
示例代码:
import MNN
import MNN.numpy as np
x = np.array([[1.0, 2.0], [3.0, 4.0]])
print(x)
print(x.shape)
y = x * 2
print(y.shape)
print(y.read_as_tuple())
输出结果
The device supports: i8sdot:1, fp16:1, i8mm: 0, sve2: 0
array([[1., 2.],
[3., 4.]], dtype=float32)
[2, 2]
[[1. 2.]
[3. 4.]]
array([[2., 4.],
[6., 8.]], dtype=float32)
(2.0, 4.0, 6.0, 8.0)
from __future__ import print_function
import MNN.numpy as np
import MNN
import MNN.cv as cv2
import sys
def inference(net, imgpath):
""" inference mobilenet_v1 using a specific picture """
# 预处理
image = cv2.imread(imgpath)
#cv2 read as bgr format
image = image[..., ::-1]
#change to rgb format
image = cv2.resize(image, (224, 224))
#resize to mobile_net tensor size
image = image - (103.94, 116.78, 123.68)
image = image * (0.017, 0.017, 0.017)
#change numpy data type as np.float32 to match tensor's format
image = image.astype(np.float32)
#Make var to save numpy; [h, w, c] -> [n, h, w, c]
input_var = np.expand_dims(image, [0])
#cv2 read shape is NHWC, Module's need is NC4HW4, convert it
input_var = MNN.expr.convert(input_var, MNN.expr.NC4HW4)
#inference
output_var = net.forward([input_var])
# 后处理及使用
predict = np.argmax(output_var[0])
print("expect 983")
print("output belong to class: {}".format(predict))
# 模型加载
net = MNN.nn.load_module_from_file(sys.argv[1], ["image"], ["prob"])
inference(net, sys.argv[2])
import torch
class IfModel(torch.nn.Module):
def forward(self, x, m):
if torch.greater(m, torch.zeros((), dtype=torch.int32)):
return x * x
return x + x;
model = torch.jit.script(IfModel())
inputs = torch.randn(16)
mask = torch.ones((), dtype=torch.int32)
out = model(inputs, mask)
torch.onnx.export(model, (inputs, mask), 'if.onnx')
import torch
class IfModel(torch.nn.Module):
def forward(self, x, m):
if torch.greater(m, torch.zeros((), dtype=torch.int32)):
return x * x
return x + x;
model = torch.jit.script(IfModel())
inputs = torch.randn((1, 3, 16, 16))
mask = torch.ones((), dtype=torch.int32)
out = model(inputs, mask)
dynamicaxes = {}
dynamicaxes['inputs'] = {
0:"batch",
2:"height",
3:"width"
}
torch.onnx.export(model, (inputs, mask),
'if.onnx',
input_names=['inputs','mask'],
output_names=['outputs'],
dynamic_axes = dynamicaxes)
可以用如下命令查看模型转换中的参数
mnnconvert -h
mnnconvert -f TF --OP
mnnconvert -f ONNX --OP
mnnconvert -f TFLITE --OP
校验,参考文档:https://mnn-docs.readthedocs.io/en/latest/tools/convert.html#id3
如下命令可以查看 mnn 基础信息
mnnconvert -f MNN --modelFile user.mnn --info
打印结果示例:
The device supports: i8sdot:1, fp16:1, i8mm: 0, sve2: 0
Model default dimensionFormat is NCHW
Model Inputs:
[ patches ]: dimensionFormat: NCHW, size: [ 1,1176 ], type is float
[ position_ids ]: dimensionFormat: NCHW, size: [ 2,-1 ], type is int32
[ attention_mask ]: dimensionFormat: NCHW, size: [ 1,-1,-1 ], type is float
Model Outputs:
[ image_embeds ]
Model Version: 3.0.4
mnnconvert -f MNN --modelFile user.mnn --JsonFile user.json
mnnconvert -f JSON --modelFile user.json --MNNModel user.mnn
模型压缩详细可参考 https://mnn-docs.readthedocs.io/en/latest/tools/compress.html ,最常用的是权重量化功能
mnnconvert -f ONNX --modelFile user.onnx --MNNModel user.mnn --weightQuantBits=8
mnnconvert -f ONNX --modelFile user.onnx --MNNModel user.mnn --weightQuantBits=8 --weightQuantBlock=128
mnnconvert -f ONNX --modelFile user.onnx --MNNModel user.mnn --weightQuantBits=8 --weightQuantBlock=128 --saveExternalData
此时产出两个文件:user.mnn 和 user.mnn.weight
手机上有 CPU / GPU / NPU 三类计算单元,根据自身需求决定使用硬件的方案。
| CPU | GPU | NPU | |
|---|---|---|---|
| 可用模型 | 全部模型 | 几乎全部模型 | CV模型 |
| 可变形状/控制流 | 支持 | 支持但有性能损失 | 不支持 |
| 加载时间 | 短 | 中 / 长 | 中 |
| 算力 | 中 | 中 | 高 |
| 功耗 | 高 | 中 | 低 |
通过设置 forwardtype ,可通过 MNN 启用相应的计算单元,具体如下
| 枚举值 | 枚举名 | 计算单元 | 适用设备 |
|---|---|---|---|
| 0 | <font style="color:rgba(0, 0, 0, 0.85);background-color:#ffffff;">MNN_FORWARD_CPU</font> | CPU | 通用 |
| 1 | <font style="color:rgba(0, 0, 0, 0.85);background-color:#ffffff;">MNN_FORWARD_METAL</font> | GPU | PC-Mac / iPhone |
| 2 | <font style="color:rgba(0, 0, 0, 0.85);background-color:#ffffff;">MNN_FORWARD_CUDA</font> | GPU | PC-NV / 服务器-NV |
| 3 | <font style="color:rgba(0, 0, 0, 0.85);background-color:#ffffff;">MNN_FORWARD_OPENCL</font> | GPU | PC / Android |
| 5 | <font style="color:rgba(0, 0, 0, 0.85);background-color:#ffffff;">MNN_FORWARD_NN</font> | NPU / GPU | PC-Mac / iPhone |
| 7 | <font style="color:rgba(0, 0, 0, 0.85);background-color:#ffffff;">MNN_FORWARD_VULKAN</font> | GPU | PC / Android |
| 9 | <font style="color:rgba(0, 0, 0, 0.85);background-color:#ffffff;">MNN_FORWARD_USER_1</font> | NPU | Android-华为手机 |
在加载 MNN 时,创建 RuntimeManager 并配置,可以让 MNN 使用对应的计算单元,比如以下代码基于 MNN 的 OpenCL 后端启用 GPU 计算单元
config = {}
config['precision'] = 2
config['backend'] = 3
config['numThread'] = 4
rt = MNN.nn.create_runtime_manager((config,))
rt.set_cache(".cachefile")
net = MNN.nn.load_module_from_file(sys.argv[1], ["input"], ["MobilenetV1/Predictions/Reshape_1"], runtime_manager=rt)
config['backend'] = 0
通过 numThread 可以设置线程数
config['backend'] = 0
config['numThread'] = 4
memory 设成 low ,且模型为权重量化出来的模型,bits 数为4或8时,可以启用动态量化:
config['backend'] = 0
config['memory'] = 2
rt = MNN.nn.create_runtime_manager((config,))
net = MNN.nn.load_module_from_file(sys.argv[1], ["input"], ["MobilenetV1/Predictions/Reshape_1"], runtime_manager=rt)
通过 precision 可以设置模型是否使用 fp16 推理
config['precision'] = 2
参考数据:
| 耗时 / ms | 内存 / mb | ||
|---|---|---|---|
| FP32 | precision = 1, memory = 1 | 8.106100 | 19.242306 |
| 基于FP32的动态量化 | precision = 1, memory = 2 | 4.739200 | 9.624172 |
| FP16 | precision = 2, memory = 1 | 4.225200 | 9.762356 |
| 基于FP16的动态量化 | precision = 2, memory = 2 | 3.663600 | 6.616970 |
由于不同平台上使用 GPU 的驱动不一样,对应地MNN实现了多个后端,需要按各自平台,选择使用 GPU 的方案
| type | 驱动 | 适用系统 | 适用设备 |
|---|---|---|---|
| 1 | Metal | iOS / MacOS | 苹果设备 |
| 2 | Cuda | Linux / Windows | Nvdia 系列GPU |
| 3 | OpenCL | Android / MacOs / Windows / Linux | 安装了OpenCL驱动的设备 |
| 7 | Vulkan | Android / MacOs / Windows / Linux | 安装了Vulkan驱动的设备 |
GPU 的 numThread 是一个 mask ,表示多种功能叠加:
比如设成 580 (512 + 64 + 4)表示:开启 batch record ,使用 buffer 数据类型,进行 Autotuning
net = MNN.nn.load_module_from_file(sys.argv[1], ["input"], ["MobilenetV1/Predictions/Reshape_1"], runtime_manager=rt, shape_mutable=False)
config = {}
config['precision'] = 2
config['backend'] = 3
config['numThread'] = 4
rt = MNN.nn.create_runtime_manager((config,))
rt.set_cache("user.cache")
示例:
config = {}
config['precision'] = 'low'
config['backend'] = 3
config['numThread'] = 4
rt = MNN.nn.create_runtime_manager((config,))
rt.set_cache("user.cache")
# set_mode(type) //type 9 for "auto_backend"
rt.set_mode(9)
# set_hint(type, value) //type 0 for "tune_num"
rt.set_hint(0, 20)
net = MNN.nn.load_module_from_file("user.mnn",
["image"], ["prop"],
runtime_manager=rt,
shape_mutable=False
)
shape_mutable 必须设为 Falseconfig = {}
config['precision'] = 0
config['backend'] = 5
config['numThread'] = 4
rt = MNN.nn.create_runtime_manager((config,))
net = MNN.nn.load_module_from_file("user.mnn",
["image"], ["prob"],
runtime_manager=rt,
shape_mutable=False)
NHWC布局NCHW布局--info可以查看模型所需的输入内存布局)不匹配,可以用set_order函数强制修改布局:import MNN.numpy as np
dims = [2, 21, 3]
var = np.random.random(dims)
var.set_order(MNN.expr.NHWC)