1.总图

为了强化基本功,决定看YOLO的源代码,看起来之后发现这真是个坑……

主要参考资料见github.com/hgpvision/da,作者给出了darknet部分函数的详细中文注释。注释背后体现了作者专注而踏实的作风,向这位老师致敬!

由于能力有限,本人难以完成与hgpvision类似的代码注解,于是退一步,绘制了darknet代码主要函数的关系图,并且对每个函数的功能作概括性介绍。由于时间有限(急于从坑里跳出来),unsample层和route层函数的原理以及其他个别细节还没有搞懂,在图中打出了问号,希望热心读者给与补充。

darknet框架可以实现多种经典卷及网络算法,但本图只包括YOLOv3实现相关的函数;

红色背景函数为有hgpvision批注的,读者可以结合其批注进行阅读,但是遗憾的是其批注只覆盖大约一半的函数,而且在v3版本发布后并没有后续更新,蓝色为hgpvision没有批注或者v3版的新函数;

本图只针对CPU相关代码,暂不考虑GPU相关代码;

对任意函数,按照其子函数执行顺序,在图中围绕按顺时针方向布置。

2.数据结构

对网络数据结构的解释,参考blog.csdn.net/u01454071的思路,绘制了下图:

图中直角矩形代表list数据结构,圆形、椭圆形代表node数据结构,圆角矩形代表section数据结构,菱形代表KVP数据结构。

总的list用于存储整个网络参数,这个list被命名为sections,因为这个list索引的主要内容就是很多不同的section,这种命名容易让人产生迷惑。

每个section用于存储网络的每一层(就是cfg文件的每一段),但是它们并不是直接被装进list:sections,而是通过一串node联系起来:每个node记录3个指针,一个指向其唯一对应的section,另外两个分别指向本层网络的前一层和后一层所对应的node。总表list:sections只记录3个数据:网络层数即node数,首个node与末尾node的地址。

通过上述方式,程序实现了对一个网络的多个层进行寻址。同样的,每一层网络包又含多个参数(就是cfg文件的每一行),这些参数的寻址也通过上述方式实现。于是产生了第二种list和第二种节点:第二种list被上述section索引并与之一一对应;第二种node的指针不再指向section结构体,而是KVP结构体,用于存储具体网络参数。

darknet在read_cfg函数中定义sections、current、options;

在option_insert函数中定义kvp;

在list_insert函数中定义node,并将单个section:current插入section集合list:sections中,以及将kvp插入list:options中;

option_find函数功能与list_insert类似,用于读取标注信息?

3.gemm

对于实现卷积操作的gemm函数,请参考petewarden.com/2015/04/

4.BatchNorm及反向传播

对于BatchNorm以及反向传播的解释,参考kratzert.github.io/2016的思路,绘制了下图。

假设样本数=N(darknet中l.batch);当前层为l层;l-1层神经元数=C;权重个数(darknet中卷积核数l.n)=l层神经元数=D。

每个节点内是一层网络从输入到输出的前向传播变量,红色矩形框内的公式是相应变量反向传播时导数的计算方法。

红色/橙色的函数名,是darknet中相应反向传播计算调用的函数;红色变量是darknet中相应的求导计算结果。

请注意,本图沿袭参考资料的内容,推导公式表达的是全连接网络而不是卷积网络,但是各个环节与卷及网络可以一一对应。