注:谨以此文纪念自己被这个移植任务折磨数月的惨痛经历…
源起
有一天,部门老大让我捣鼓了一个做分割任务的深度学习网络,搞了老半天在Tensorflow上训练完了,要移植到Windows平台上,我一想这简单呀,Tensorflow有支持C++ API啊,我编个Tensorflow的C++DLL库不就行了,而且网络中有几个custom_op,也能一起编进去结果搞了半天都没搞出来,编译过程极其困难,因为TF是用Bazel编译的,而且TF依赖库非常多,编译时要下载很久(墙内更麻烦),而且这个过程非常容易抛出各种奇怪的依赖方面的error,最终不得不放弃这条路,颓废。。。
Tip 1:如果实在没有办法,千万不要轻易尝试编译TF C++ DLL,除非你是大牛,当我没说。
TensorServing
重新振作之后,研究了一下Google的Tensorflow serving,想着能否将网络丢进Docker镜像,在用户内网架个服务器做推理,客户端只要通过发RESTful API指令就行,兴冲冲开始了捣鼓,结果又被custom op给搞懵了,搞了半天也没成功,大多数网络只要使用TF提供的Op就可以运行了,但是我这个网络不行,有些Op是自己写的,编入Docker镜像的时候,一直报错说custom op找不到,Github和StackOverflow找了好几种解决方法都不奏效,我佛了,最终因为客户没法接受架内网服务器的这个方案而再次搁浅。。。
Tip 2:Tensorflow serving对于自带custom op的支持并不是很好,如果真要搞定,需要你用Bazel编一个带有自己custom op的TF docker镜像,比较麻烦,不过这条路应该是可行的。
TensorRT
一个偶然的机会让我看到了NV家的TensorRT,当时如获至宝,啊!这就是我想要的神器啊,自带Window C++库,只为推理而生,透过中间格式UFF可以达到TF到TensorRT的完美转换,而且还支持custom op,只要转写成TensorRT的Plugin插件就完美支持了,最最最诱人的还有自动优化网络Op,加速推理的神效,Profiler让你精准测量网络的性能,这些特性看的我心花怒放,心旷神怡,不住的赞叹老黄家刀法的精妙,充值就能变强,真香!
迅速搭完环境,就开始啪啪啪敲起了代码,首先完成了TF导出UFF模型,随后实现了C++推理引擎和所有custom plugin,但是现实的巴掌总是如期而至,最终跑构建网络的时候扑街了,内存布局(memory layout)的问题显现了出来,TF训练的时候用的是NHWC布局,TensorRT用的是NCHW布局,在NV的论坛里问了问,结果无人问津,只得自己改网络,重新训练了NCHW版的网络,放Engine里一跑,我去,还是抛出了error,原来这TensorRT在Build engine的时候会自动加一些Transpose op进来,由于搞不清其中原理(没有源码,纯黑盒),论坛里问了也没人解答,于是一度搁置了很久,项目进度一拖再拖,这给我急的呀,都快放弃了。
Tip 3:TensorRT自动优化网络真的很香,但是整个就一黑盒,有些时候可控制的自由度不是很高,Debug非常麻烦,不能模块化的搭建网络,因为UFF读进来,整个网络没问题才能运行,否则就直接报错,连跑都跑不了。通过UFF转TensorRT还不是很完美,不是所有Op都支持的,毕竟是中间格式。另外NV的论坛回复速度真的太慢了,问个问题要很久才有人理,或者根本无人问津。
Pytorch C++ frontend
兜兜转转换了好几种框架或方法,却依然没能成功移植平台,这使我信心很受打击,难道就没有一种自由度极高,支持custom op,在Windows上有C++库的框架了吗?难道要自己从零手写C++网络吗?不可能,一定是我没找到!终于,经过了一番知乎查找,它出现在了我的视野内,Caffe!这个从深度学习诞生之初就一直很流行的C++库,现在的Caffe2被融合进了Pytorch,并改名叫Pytorch C++ frontend/Libtorch。
找对了工具真的是事半功倍,花了一周时间就把基本的CustomOp和网络都用Libtorch的Op都实现完了,那种单独模块拿出来就可以用,可以测试的感觉真的太爽了,而且C++版Op跟Pytorch几乎一一对应,所以虽然C++版现在的文档和支持非常稀少,可是有pytorch的文档和论坛问答作参考,也是够用了。网络搭好后,剩下的就是把TF训练的权重导出,用H5文件存储下来,之后用HDF5++读入C++程序,将权重assign给对应的layer,本以为大功告成,却遇到了和之前一样的问题,Memory layout不兼容,导致推理结果非常差,幸好Libtorch debug非常方便,直接将每一层layer的中间结果打印出来一一比较,才发现了这个问题,最终我用NCHW重新训练了一遍网络,再导回C++,才最终完成了这次移植,大功告成之时不禁喜极而泣(内心)!
Tip 4:经验很重要,如果我一开始就用Caffe就没这么多事儿了。
Tip 5:经验也是靠积累的,每一次踩坑,并且从中爬出来,都是经验。
Tip 6:我的经验就是告诉各位Caffe很靠谱,移植Windows/C++必备框架。
Tip 7:吹爆Pytorch,非常好用,没有Tensorflow那么多历史遗留问题,用来验证新网络新算法的必备利器!而且以后转换C++代码也方便,有TorchScript,虽然现在还有点弱,但是官方说现在他们的主要精力都放在完善TorchScript的转换上,非常值得期待!另外Pytorch的论坛活跃度高,发一个问题快则几个小时,慢则一天,就会有社区或者官方人员回复,虽然不一定一下就解决问题,但是只要有人提出思路,总归是有一些帮助,能促进一起思考的。
总结
以上就是我艰辛的Windows平台移植之路,希望对各位正在寻找类似方法的小白们有一定的帮助,谢谢~