title | date | category |
---|---|---|
2022_06_07_11_WNNX从零实现一个AI推理引擎(六) |
2022-06-07 16:58:58 -0700 |
默认分类 |
本文介绍 2022_06_07_11_WNNX从零实现一个AI推理引擎(六)
本文由在当地较为英俊的男子金天大神原创,版权所有,欢迎转载,本文首发地址 https://jinfagang.github.io 。但请保留这段版权信息,多谢合作,有任何疑问欢迎通过微信联系我交流:
jintianiloveu
手写一个前推框架的系列博客已经更新到第六篇了,从一开始的一个替代onnx算子碎片化、直接从torchscript到推理引擎的想法,到建造我们自己的IR,并完成了可视化,最后到逐渐构建自己的推理引擎,基本上我们已经打通了大部分的链条,继续向下一个目标进发。
上一次,我们推理了几个简单的FC模型,例如一个带有layernorm的FC模型,单输入输出,多入多出都可以推理。
上面的结果可以推理。两个输入的问题不大,下面这种才是真的杂乱:
4个输入也没问题,而且下面这个模型的layernorm是动态权重的。
到目前为止我没有看到哪个框架真正支持动态layernorm权重,可能是需求比较小。
上面那个结构,其实是简化之后的,这就是wnnx为什么有用,我们来看看onnx 的结构:
两个模型一模一样,但是onnx要复杂很多,因为引入了太多的胶水算子。
这就是wnnx的 用途。
我们一开始的规划并不满足于简单的FC推理,我们最起码要跑卷积。那么VGG就是第一不了,如果VGG都推理不了,那基本上就没啥用。
来看vgg的推理代码:
#include "core/utils/log.h"
#include "wnn/forward.h"
#include "wnn/tensor.h"
#include <cstddef>
#include <cstdio>
#include <string>
#include <vector>
int main(int argc, char **argv) {
std::string model_f = argv[1];
auto wnn_model = wnn::NNForward();
if (!wnn_model.load_from_file(model_f)) {
LOG(INFO) << "model might load failed.";
return -1;
}
auto input_tensor = wnn::from_numpy_bin("in.bin", {1, 3, 224, 224});
vector<wnn::Tensor> inputs;
inputs.push_back(input_tensor);
LOG(INFO) << "my input tensor: ";
std::cout << input_tensor;
std::vector<size_t> out_shape = {1, 1000};
wnn::Tensor output_tensor =
wnn::Tensor(out_shape, wnn::DeviceType::DeviceType_kCPU);
vector<wnn::Tensor> outputs;
outputs.push_back(output_tensor);
auto res2 = wnn_model.infer(inputs, outputs);
LOG(INFO) << "result: ";
std::cout << output_tensor;
auto gt = wnn::from_numpy_bin("data.bin", out_shape);
gt.allclose(output_tensor);
return 0;
}
可以看到,整个推理非常精简,你甚至可以直接从numpy读数据,这对于调试很有用,你可以保持和pyotorch测的数据完全一致,并且可以很方便的对比输出结果。
这里就不得不说wnnx框架的优势:
- 你甚至感觉不到这是在C++里面推理,Tensor的操作跟pytorch几乎一模一样;
- 所有的Tensor结果都可以打印。
一个典型的CNN的wnnx网络结构:
直接开始推理!
PyTorch下的输出结果:
wnnx下的推理结果:
可以看到,那个输出的Tensor是可以直接打印的:
std::cout << out_tensor;
你就可以看到每个数值和维度,非常方便。
最终结果,完全对齐,误差不超过10e-5。
从0打造一个推理引擎差不多到这里就结束了。
下一篇,将是本系列的终结篇,也是一个小的milestone: 下一篇将尝试用我们自己的推理引擎来推理YOLOX检测模型,并加入移动端优化加速。