译者丨Oldpan网志
撰稿丨极市网络平台
写作源标识符这类是一类自学,就像儿时解题那样,看自己的成短也就会仿效许多好的语句,许多好的章节。看源标识符也那样,相同小厂的源标识符写的艺术风格也不一样,惯常的基本功也不那样,特别强调的规范化也不那样,采用的C++标示也不那样(C++11、C++14之类)。
但假如想深入细致自学两个架构的下层,看源标识符是要的。
我看完许多源标识符,也仿效过许多小厂源标识符的范例。最常见的范例是tcsh、厂房、科枫、注册登记之类监督机制,这也是结构设计数学模型里边最常用的。假如觉得看的挺熟了想重大贡献不然,适当的也有标识符规范化(code-guidelines)。像TensorRT[1]开放源码库房的重大贡献标识符规范化:
https://github.com/NVIDIA/TensorRT/blob/master/CODING-GUIDELINES.md里头会明确要求,采用namespace要在结尾加之该namespace中文名称的注解。
namespace foo { … } // namespace foo或者比如写C++尽量用const和constexpr而不是#define,因为#define是预编译的,编译器对此无能为力检查不了。
之类之类。
有些是很小的细节、有些则是涉及到C++的语法和实用性以及标识符后期的维护性以及稳定性。假如觉着自己的标识符写的不是很好看,或者觉得不满意,或者有强迫症,就想要写完美没有问题的标识符,这时你可以参考下这些小厂的标准…
其实对于C++的标识符规范化和许多准则,我们平常看的C++ primer里边就有很多。类似的资料也有很多,一般都是用比较新的C++11到C++17的标准。我们多看看多学学就差不多会了:
C++ Core Guidelines[2](More)Effective C++Effective Modern C++推荐看的许多源标识符库
搞深度自学,除了纯粹的研究岗,不论是深度自学研究员还是深度自学算法工程师,或者是深度自学架构工程师、高性能计算工程师…许多源标识符总是要看的。
我们平常用的Pytorch、Caffe、TensorFlow、Paddle、TVM,这些都是开放源码的。不论公司还是个人都在用,很好用也很强大,大佬们也会经常魔改。
这都是赤裸裸的宝贵资料啊,不花点时间看真的是可惜了。
我这里也推荐许多值得深入细致自学的架构。
Caffe[3]
Caffe有多经典就不用说了,大部分的标识符是用C++写的,对于自学C++的许多语言很有帮助。
Caffe标识符语言分布
值得看的部分都在src目录下:
Caffe核心的部分
值得我们自学的东西有很多:
类的结构设计、继承、模板、工厂im2col、矩阵运算、反向传播很多算子的CPU和GPU实现,例如BN、softmax等SyncedMemory,内部的blob数据是怎样在CPU和GPU之间传递的// Caffe中layer的注册登记监督机制 // ~/include/caffe/layer_factory.hpp #define REGISTER_LAYER_CREATOR(type, creator) \ staticLayerRegisterer<float> g_creator_f_##type(#type, creator<float>); \ static LayerRegisterer<double> g_creator_d_##type(#type, creator<double>) \ #define REGISTER_LAYER_CLASS(type) \template <typename Dtype> \ shared_ptr<Layer<Dtype> > Creator_##type##Layer(const LayerParameter& param) \ { \ returnshared_ptr<Layer<Dtype> >(new type##Layer<Dtype>(param)); \ } \ REGISTER_LAYER_CREATOR(type, Creator_##type##Layer)到现在为止,假如追求性能话,Caffe可能已经不是很合适了。CPU和GPU端有OpenVino和TensorRT两位大哥,也有其他优秀的架构。Caffe到现在更加适合作为应用部署入门自学的架构,或者许多对吞吐量明确要求不高的任务,许多工业嵌入式端现在也会用到Caffe。
Caffe也是前几年面试的常客,这些年可能问的少了,不过我认为还有必要投入一点精力去自学。
Pytorch
Pytorch中值得自学的部分更多。不过Pytorch源标识符很复杂,我建议还是带着任务去自学比较有效率和针对性。比如你想写CUDA自定义算子、又或者你想理解Pytorch的自动求导是什么样的,最好是有需求去看。
还有个最直接的方法,自己把Pytorch的源标识符编译一遍,尽量遇到坑,逼着自己看下是什么问题,这样对Pytorch的整理架构会有一定的了解。Pytorch值得自学的地方很多:
autograd[4],也有适当长达40多页的PDF讲解[5]Tensor,Pytorch中的Tensor是怎样存储的,怎样和op交互,怎样在CPU和GPU之前无缝传递的我之前有过很多源标识符介绍和源标识符编译,这里就不赘述了,感兴趣的可以看之前的网志。这里翻出篇有关Tensor的:https://zhuanlan.zhihu.com/p/348179896
除了这类有很多自学的地方,Pytorch也附带了很多优秀的第三方库,也值得我们自学,简单列举两个:
fbgemmQNNPACKtriton-inference-server
推理服务器架构,这里更多的是用我们已知的推理架构去实现服务器推理功能,涉及到调用各种推理架构(libtorch、TensorRT、TF)的正确姿势以及多线程推理相关。主要语言也是C++。
包括却不限于:
多线程调度以及队列、动态组batch处理HTTP、GRPC协议;json处理监督机制(rapidjson)通过这个源标识符库房,我们可以学到很多部署数学模型推理的实际标识符,这个库房很大,要看的标识符很多,你忍一下。源标识符地址:
https://github.com/triton-inference-server/server标识符规范化:
https://github.com/triton-inference-server/server/blob/main/CONTRIBUTING.mdTVM[6]
TVM是两个C++与python混合的工程,并不是单纯的C++或者python语言所编写的。其中,TVM中两种语言的结合方式是采用python中内置模块ctypes来构建起来的,看到ctypes我们可能想到,这不是和C语言结合的吗,当然是这样的,TVM采用的与python的组合语言是C,而核心的实现语言还是C++,而C++通过C的接口与ctypes相连。
TVM中有很多值得我们自学的点:
type-erased函数是怎样实现的反射监督机制和奇异的递归模板式可以兼容一切的PackedFunc又是怎样实现的TVM的官方文档有很多其内部实现细节的讲解,在TVM的TVM Runtime System[7]一文中,其实已经简单介绍了TVM的核心部件的使用方式,在运行端中,第两个首要介绍的是可以接受和返回任意类型参数的函数,通过这个特性TVM实现了很多函数接口来完成TVM的一系列任务:
void MyAdd(TVMArgs args, TVMRetValue* rv) { // automatically convert arguments to desired type. int a = args[0]; int b = args[1]; // automatically assign value return to rv *rv = a + b; }TVM老潘之前留了很大的坑没有填,后续一定会补上的。
还有很多值得深入细致的架构就不一一介绍了。
看源标识符工具
工欲善其事必先利其器,要写作源标识符没有好的写作器(IDE)怎么行呢?
看源标识符的工具我推荐采用VSCODE,谁用谁知道。看标识符的高亮、查找reference、全局查找、宏定义展开、甚至可以运行,大大提升写作标识符的效率。
VSCODE
对于某个文件,还可以看到这个标识符文件的OUTLINE以及TIMELINE,可以说是非常强大了。
文件的outline和timeline
假如你就想快速看看标识符,可以直接浏览器将某个github的地址换为github1s,有人专门开发了工具,可以通过这种方式直接在github源标识符处开启网页版VScode,可以方便地写作源标识符:
github1s看标识符
虽然githubs偶尔会抽风,抽风概率20%,不过确实是两个非常好用的看标识符工具。
DEBUG标识符
还有很重要的一点,光看标识符有些细节的运行逻辑下层实现肉眼一般是看不透的(除非你有写轮眼!),最好跑一下!假如自己不知道跑啥,建议可以看一下源标识符中的test部分,test部分是QA,也是测试源标识符功能实现是否符合预期的重要一步。我们可以跑许多这里的测试样例,所有功能都会覆盖到,边跑边debug,所有细节一清二楚!
比如Pytorch源标识符中的,test/test_quantization.py源标识符test部分,我们可以通过python test/test_quantization.py TESTNAME命令执行来测试所有有关量化的功能模块:
# # 1. Quantized Kernels # TODO: merge the different quantized op tests into one test class fromquantization.core.test_quantized_opimport TestQuantizedOps # noqa: F401 fromquantization.core.test_quantized_opimport TestQNNPackOps # noqa: F401 from quantization.core.test_quantized_op import TestQuantizedLinear # noqa: F401 from quantization.core.test_quantized_op import TestQuantizedConv # noqa: F401 fromquantization.core.test_quantized_opimport TestDynamicQuantizedLinear # noqa: F401 fromquantization.core.test_quantized_opimport TestComparatorOps # noqa: F401 from quantization.core.test_quantized_op import TestPadding # noqa: F401 from quantization.core.test_quantized_op import TestQuantizedEmbeddingOps # noqa: F401 fromquantization.core.test_quantized_opimport TestDynamicQuantizedRNNOp # noqa: F401 # 2. Quantized Functional/Workflow Ops from quantization.core.test_quantized_functional import TestQuantizedFunctionalOps # noqa: F401 from quantization.core.test_workflow_ops import TestFakeQuantizeOps # noqa: F401 fromquantization.core.test_workflow_opsimport TestFusedObsFakeQuant # noqa: F401 # 3. Quantized Tensor fromquantization.core.test_quantized_tensorimport TestQuantizedTensor # noqa: F401 # 4. Modules fromquantization.core.test_workflow_moduleimport TestFakeQuantize # noqa: F401 from quantization.core.test_workflow_module importTestObserver# noqa: F401 from quantization.core.test_quantized_module import TestStaticQuantizedModule # noqa: F401 from quantization.core.test_quantized_module import TestDynamicQuantizedModule # noqa: F401 fromquantization.core.test_workflow_moduleimport TestRecordHistogramObserver # noqa: F401 fromquantization.core.test_workflow_moduleimport TestHistogramObserver # noqa: F401 from quantization.core.test_workflow_module import TestDistributed # noqa: F401 from quantization.core.test_workflow_module importTestFusedObsFakeQuantModule# noqa: F401对于很多源标识符,大部分是Python和C++混搭的。Python充当前端接口,后端核心逻辑和算法用C++和CUDA实现。对于这种情况,可能运行debug起来比较麻烦,但也不是没有办法,只要编译出带debug符号表的.so,然后gdb-attach一下,是可以从python端口debug到c++内部逻辑的:
各种姿势的debug(从python一路debug到C++)(https://mp.weixin.qq.com/s/Pr6PWNkcs4YE7ML2Q2FGug)自学地儿
看源标识符没人交流怎么行,最好的交流方式是各大GITHUB源标识符的issue。
issue多看、pull request多学、很多精华都在里边了。要搞AI部署工程研究,各种推理架构(TF、Pytorch、TFLITE、TVM、NCNN)的源标识符更是要看的,需要自学什么,也需要一步一步来。
issue和pull request都是很好的自学论坛
issue区可以提中文也可以提英文,要知道很多大佬也都是中国人,而且和你英文交流的说不定也是中国人。
中英混杂的交流圈
重大贡献标识符
假如你某个项目的源标识符看的非常熟悉了,然后你发现这个架构的许多bug或者许多改进点,你就可以试着去修改源标识符然后提交上去。一般的流程:
fork一份源标识符到自己的库房git clone下来进行修改,记得在新的分支上修改修改好后更新自己的仓库到官方的源标识符库房上提pull request,将自己库房上的某一分支merge到官方源标识符库房中merge成功整个过程还是很有意思,或者说,很有成就感的。毕竟你的标识符需要被很多大佬查阅,你的标识符需要符合这个库房的规范化,无论是标识符艺术风格还是标识符能够提供的功能性。大佬们帮你review标识符的同时也会帮你提出修改意见,让你的标识符能够更好地merge到这个库房中,同时你的标识符也焕然一新。
随便点开两个TVM[8]库房的Pull requests:
Pull requests流程
Conversation表示这个pull requests的重大贡献过程,有重大贡献者与项目维护者的交流以及标识符review环节。Commits即有关这个共享的提交过程;我们可以直接看Files changged来看共享者对源标识符做了哪些更改,当然也是最核心的部分。
当你成功重大贡献标识符的时候,写作源标识符这门课就可以毕业啦!
参考资料
[1]TensorRT: https://github.com/NVIDIA/TensorRT
[2]C++ Core Guidelines: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#main
[3]Caffe: https://github.com/BVLC/caffe
[4]autograd: https://pytorch.org/docs/stable/notes/autograd.html
[5]PDF讲解: https://arxiv.org/pdf/1502.05767.pdf
[6]TVM: https://github.com/apache/tvm
[7]TVM Runtime System: https://docs.tvm.ai/dev/runtime.html?highlight=reflect
[8]TVM: https://github.com/apache/tvm