目录
使用MNNConvert命令将.onnx模型转为.mnn模型(linux上进行)
将pytorch训练好的.pth模型转为.onnx模型
import torch
import torch.onnx
import models
# 读取模型
model = models.crnn(inputdim=64, outputdim=1, pretrained_file= "trainedModels/bs1_epoch1_validloss_0.27896.pth")
# 将模型转为验证模式(这步好像不用也行)
model.eval()
# 随便创建一个临时数据,数据的大小要与网络输入一致。内容无所谓,因为后面只需要它的shape
temp_data = torch.randn(1,10,64) # 1是batch_size
# 定义输入输出的名字,随便定,不过之后的操作好像会用到
input_names = ["input"]
output_names = ["output"]
# 转换模型
torch.onnx.export(model, temp_data, "trainedModels/onnx/test.onnx", verbose=True, training = True, input_names=input_names, output_names=output_names)
# blabla
print("ok")
使用MNNConvert命令将.onnx模型转为.mnn模型(linux上进行)
第一种方法
使用编译好的“MNNConvert”命令
./MNNConvert -f ONNX --modelFile test.onnx --MNNModel test.mnn --bizCode MNN
第二种方法
先安装python上的MNN库
python3 -m MNN.tools.mnnconvert
然后使用这个命令
python3 -m MNN.tools.mnnconvert -f ONNX --modelFile test.onnx --MNNModel test.mnn --bizCode MNN
报错解决
这里容易出现各种各样的错误,比如我原本的模型结构是这样的
转换模型的时候报了这个错误
最开始以为是mnn不支持LeakyReLU或者LPPooling,将二者都换掉以后还是不行,排查了很久最后发现是forward函数中的clamp导致的,果断把这个函数注释掉(对网络影响不大)。
然后重新训练、将模型转为onnx,报错变成了这个
这个报错就很明显了,意思是mnn不支持GRU,无奈只能把GRU换成LSTM。重新训练、将模型转为onnx,就没问题了。
随后我又尝试将LeakyReLU或者LPPooling换回来,发现LeakyReLU可以,但是LPPooling报了这个错
这个错误我没有仔细分析,猜测可能是mnn不支持LeakyReLU的意思。
最后经过若干次改动,模型结构变成了这样
大概过程就是
1、训练模型、将模型转为onnx。
2、有无报错。若有,转到第3步。若无,转到第5步。
3、结合报错和自己的猜测修改网络结构,将导致问题的算子直接替换掉(当然如果c的功底足够强的话也可以去改mnn的源代码)。
4、回到第1步。
5、成功。
训练量化
参考这两个代码,实际上过程跟pytorch训练模型差不多。
创建DataLoader
除了父类不同外,创建DataLoader的方法跟pytorch几乎一样,就是注意__getitem__函数的返回值需要转为c++支持的expr格式。
class XxxDataset(MNN.data.Dataset):
def __init__(self, xxx):
super(ImagenetDataset, self).__init__()
pass
def __getitem__(self, index):
pass
#最后需要把data和label转换为那个什么expr的格式
#否则后面加载数据的时候会报错“Unable to cast Python instance to C++ type”
data = F.const(data, data.shape, F.data_format.NCHW)
label = F.const(label, label.shape, F.data_format.NCHW)
return data, label
def __len__(self):
pass
train_dataset = ImagenetDataset(xxx)
test_dataset = ImagenetDataset(xxx)
train_dataloader = MNN.data.DataLoader(train_dataset, batch_size=1, num_workers=4, shuffle=True)
test_dataloader = MNN.data.DataLoader(test_dataset, batch_size=1, num_workers=4, shuffle=False)
加载模型
import MNN
nn = MNN.nn
F = MNN.expr
m = F.load_as_dict(model_file_path)
#这里的input_name、output_name跟前面将pth转为onnx时设置的input_name、output_name一样
inputs_outputs = F.get_inputs_and_outputs(m)
for key in inputs_outputs[0].keys():
print('input names:\t', key)
for key in inputs_outputs[1].keys():
print('output names:\t', key)
inputs = [m['input']]
outputs = [m['output']]
net = nn.load_module(inputs, outputs, for_training=True)
将模型设置为训练量化模式
#默认int8
nn.compress.train_quant(net, quant_bits=8)
定义优化器
opt = MNN.optim.SGD(net, 1e-5, 0.9, 0.00004)
训练
net.train(True)
train_dataloader.reset()
for i in range(train_dataloader.iter_number):
data, label = train_dataloader.next()
predict = net.forward(data)
loss = nn.loss.cross_entropy(predict, label)
opt.step(loss)
测试
net.train(False)
test_dataloader.reset()
for i in range(test_dataloader.iter_number):
data, label= test_dataloader.next()
predict = net(data) # 用 net.forwar() 应该也可以,没试过
loss = nn.loss.cross_entropy(predict, label)
predict = np.array(predict.read())
label = np.array(label.read())
correct = (np.sum(label == predict))
保存模型
predict = net.forward(F.placeholder([1, 10, 64], F.NCHW))
F.save([predict], file_name)
量化精度
浮点精度 > 训练量化精度 > 离线量化精度
训练量化的过程中精度会逐渐降低并介于浮点精度与离线量化精度之间。