介绍
本文介绍了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次。
<表格>
tr>
header>
<身体>
tr>
tr>
tr>
tbody>
table>
尽管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 |
它将停止,并显示诸如
之类的错误。我这次放弃了。 .. ..