本文介绍 2022_08_01_03_WNNX从0实现AI推理引擎之终结篇前序
2022_08_01_03_WNNX从0实现AI推理引擎之终结篇前序
本文由在当地较为英俊的男子金天大神原创,版权所有,欢迎转载,本文首发地址 jinfagang.github.io 。但请保留这段版权信息,多谢合作,有任何疑问欢迎通过微信联系我交流:
jintianiloveu
我这个从0写一个AI推理引擎系列,前面写了个六个章节,然后停更了很长一段时间。你们可能觉得我已经放弃,确实这个坑是在是太深了.....
但是我并没有放弃,自己挖的坑,跪着也得给他填完。先来复盘一下我们要做什么事情,可能很多人已经忘记了,如果你不知道这个系列说的是什么事,可以翻我之前的 一 - 六 系列回顾一下。
简单来说,这个想法起源于一个痛点:onnx粒度太细,胶水算子太多,而大多数框架又都是基于onnx在做优化,当然这不是最主要的痛点,主要的痛点是很多框架为了兼容,会同时支持多个不同训练框架的标准,也就会带来麻烦,换句话说,没有任何一个框架足够充分的贴近训练框架的算子定义。于是我们就自己做了一套定义。在此基础上,我们甚至完善了一套可视化框架。
到目前为止,已经可以可视化诸如基于shufflenetv2的YOLO之类的复杂模型:
上面是shufflenet的block结构,也可以可视化诸如SPP,以及DetectHead之类的结构:
然后我们大概实现了一些简单模型的推理,例如VGG等等。
这是前情提要。
但是问题,也随之而来。
建造的巨大漏洞
当我一步一步完善推理的框架,使得它可以支持更多backbone的时候,对于普通的结构,一切都比较容易,但当我开始推理shufflenetv2的时候,噩梦开始了。。
如果你没有写过推理框架,或者说,你没有从头设计过一套推理框架,你可能无法体会这个问题。问题本源是来自于shufflenet里面的ChannelShuffle操作。
这个操作本身不会带来问题,而它在shuffle之后会把结果进行Split,这个Split算子,是我的初代版本的噩梦起源。
为什么无法处理
为了解决这个问题,我们首先要知道为什么Split用第一版的逻辑没有办法推理。
原因很简单:我对图的输入输出处理过于天真了,我以为通过float*可以给每一层输入它需要的数据。直到我遇到了Split,我意识到,这玩意儿你根本无法通过一个指针知道它输出什么,以及他的下一个算子需要什么。
图调度是推理框架最难的地方。
所以,停更的这几个礼拜,我只做了一件事:那就是对初代版本进行 100%的推翻,并进行重构,这足足花了将近一个礼拜的时间(业余)。
于是我checkout了一个 2.0的分支,并疯狂的 commit。截止到目前,完全重构之后的版本,终于可以推理shufflenetv2。
这听起来似乎很容易,因为外面无数框架可以做到这件事情,但当你真正从0去做的时候,你会发现复杂度不止一点点。
最后我们来对比一下,在wnn推理框架里面,一个层的定义,与其他框架的差别到底在哪里:
我们以permute这个层为例,ncnn里面的实现大概有739行代码:
而在wnn里面:
大概只有199行代码。
总结起来,我们具有的优势在于:
- 极度精简,不仅仅是实现简单,整个部署的依赖也非常简单;
- 麻雀虽小,五脏俱全;
- 非常厉害的一点,任何一个算子,你都可以非常容易的把其他框架的实现集成进来,如果你觉得某个算子是瓶颈,直接把ppl.nn的高端优化拿过来完事,白嫖的最高境界
最后还有几个点。
- ncnn对多batch支持很弱,甚至是你必须用omp for去实现,wnn天生支持动态batch;
- wnn 所有的数据都是 Tensor。
但是也仅仅是如此,我们最复杂仅仅只支持到实现shufflenetv2的推理,而更复杂的模型例如我们的终极目标:推理YOLO还不行。
下一篇,我们将进一步推理更复杂的模型!
彩蛋
这是终章的前序,也就是意味着距离我们的目标更近了一些。但是还没有到底我们的终极目标。 不过,似乎已经达到了:
wnn 推理经典 yolo。
另外,可以从github阅览整个系列更新了;github.com/jinfagang/A…