PyTorch→ONNX→神经网络


介绍

本文介绍了NNVM,但本文中提到了

  • OpenCL构建无法通过
  • 从PyTorch导出的ONNX无法通过

的问题已得到开发和解决,因此我将对此进行介绍。

这次,我将通过ONNX在经过训练的PyTorch模型上构建LLVM / OpenCL,并将执行速度与PyTorch进行比较。 OpenCL的目标是MacBook Pro的Intel HD Graphics 4000。

检查训练好的模型

使用经过训练的火炬视觉模型。提供VGG,Alexnet,Resnet,Squeezenet,Inception和Densenet。您可以轻松获得经过训练的模型,如下所示。

1
torch_model = models.vgg16(pretrained=True)

首先,推断PyTorch并检查操作。通过将OpenCV读取的图像转换为float来创建与模型输入匹配的支持功能。

1
2
3
4
5
6
7
8
def img2dat(img):
    inshape=(1,3,224,224)
    rgb = cv2.cvtColor(img,cv2.COLOR_BGR2RGB).astype(np.float32)/255.0
    rgb = normalize(rgb)
    rgb = cv2.resize(rgb,(224,224))
    dat = rgb.transpose(2,0,1)
    dat = dat.reshape(inshape)
    return dat

根据

文档,归一化是学习期间输入的先决条件,因此请编写归一化功能。 PyTorch也具有实用程序功能,但是当我将它们制作为NNVM时,我想稍后将其设为无PyTorch。

1
2
3
4
5
6
def normalize(img):
    mean=[0.485, 0.456, 0.406]
    std=[0.229, 0.224, 0.225]
    for c in range(3):
        img[:,:,c] =  (img[:,:,c]-mean[c])/std[c]
    return img

准备好上述内容后,使用torch_from_numpy()将其转换为张量,使其可变,然后将其放入模型中。

1
2
3
4
img = cv2.imread("image.jpg")
dat=torch.from_numpy(img2dat(img))
x = Variable(dat)
out=torch_model(x)

使用imagenet1000_clsid_to_human.txt将

类号转换为字符串。在下面创建一个名为class_dict的字典。

1
2
3
classfile="imagenet1000_clsid_to_human.txt"
f=open(classfile).read()
exec("class_dict="+f)

输出是按批处理方向排列的Imagenet 1000类概率向量的张量。您可以使用np.array()作为数据访问张量。由于推理时的批次数为1,因此可以使用out.data [0]获得推理结果的概率向量。以argmax来获取最可能的类编号。较早使用class_dict将其转换为字符串。好的,文本很长,但是代码是下面的行。

1
result=class_dict[np.argmax(out.data[0])]

转换为ONNX

您可以如下导出。您需要输入一个虚拟输入。

1
2
3
torch_model.train(False)
x = Variable(torch.randn(1, 3, 224, 224), requires_grad=True)
torch_out = torch.onnx._export(torch_model,x,target_path,export_params=True)

从ONNX构建

您可以执行以下操作。 shape_dict指定端口名称和输入张量的大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
onnx_graph = onnx.load(onnx_file_name)
sym, params = nnvm.frontend.from_onnx(onnx_graph)

target="llvm"
shape_dict = {'input_0': (3,224,224)}

deploy_graph, lib, params = nnvm.compiler.build(sym, target, shape_dict, params=params)

lib.export_library("deploy.dylib")
with open("deploy.json", "w") as fo:
    fo.write(deploy_graph.json())
with open("deploy.params", "wb") as fo:
    fo.write(nnvm.compiler.save_param_dict(params))

构建结果将输出到以下3个文件。

  • dylib:动态链接库
  • json:图结构
  • 参数:参数

火炬视觉→ONNX→NNVM
我尝试了各种构建,但此刻

  • 亚历克斯网
  • VGG

通过。其他模型存在以下问题。

  • ResNet:最初的模型不能很好地识别。
  • 据说没有SqueezeNet max_pool2d并且ONNX转换不起作用。
  • Inception_v3:输出大小太小,无法进行ONNX转换。
  • densitynet:因为需要CUDA,所以ONNX转换在CPU上不起作用。

使用预建模型进行推断

您可以推断以下代码。

1
2
3
4
5
6
7
8
9
10
loaded_lib = tvm.module.load("deploy.dylib")
loaded_json = open("deploy.json").read()
loaded_params = bytearray(open("deploy.params", "rb").read())

dat=img2dat(img)

module.set_input('input_0', tvm.nd.array(dat))
module.run()
out=module.get_output(0, out=tvm.nd.empty(outshape))
out=out.asnumpy()

工作总结

上面的代码在GitHub上进行了总结。

使用以下命令,您可以导出vgg16 onnx,使用llvm进行构建,并使用构建结果进行推断。

1
2
3
python export.py vgg16
python compile.py llvm vgg16
python replay.py orange.jpg llvm vgg16

速度比较

VGG16比较了普通的PyTorch,LLVM,OpenCL。平均每个20次。

<表格>

框架

执行时间[秒]


<身体>

PyTorch

0.71

LLVM / NNVM

17.41

OpenCL / NNVM

2.92


尽管LLVM和OpenCL之间的比较存在很大差异,但结果是普通的PyTorch最快。如此处所述,目前看来ARM已优先考虑优化算术调度,而x86尚未完成,所以我认为我还不能完全展示自己的能力。我认为创建这样一个框架比这更大。

关于部署到Raspberry Pi

好吧,从这样的流程中,我当然想在Raspberry Pi上运行它,但是不幸的是,本文来不及时。本文档介绍了如何部署到Raspberry Pi。似乎可以交叉构建,通过RPC传输库和参数并执行。

但是,我已经在我的RaspberryPi Zero W上尝试过,但是还没有奏效。

1
target="llvm -target=armv6-none-linux-gnueabihf -mcpu=arm1176jzf-s -mattr=+neon"

我正在尝试

,但是

1
check failed: code == RPCCode::kReturn code=4

它将停止,并显示诸如

之类的错误。我这次放弃了。 .. ..