【经验分享】pytorch|【经验分享】pytorch 与 darknet 计算卷积输出 shape 方式对比

? 欢迎关注我的公众号 [极智视界],回复001获取Google编程规范
? O_o ? >_< ? o_O ? O_o ? ~_~ ? o_O
? 本文记录了 pytorch 与 darknet 计算卷积输出 shape 方式对比,两者有一些区别。
?卷积算子是深度学习中最常用的算子,在神经网络中,往往需要涉及到算子 shape 形状的推导,这里主要说一下 cov 层的 shape 推导,拿 pytorch 和 darknet 进行对比。
1、pytorch 卷积输出 shape 推导 ?看源码,答案在 torch/nn/modules/conv.py 脚本 class Conv2d(_ConvNd) 类的注释中:

Shape: - Input: :math:`(N, C_{in}, H_{in}, W_{in})` - Output: :math:`(N, C_{out}, H_{out}, W_{out})` where.. math:: H_{out} = \left\lfloor\frac{H_{in}+ 2 \times \text{padding}[0] - \text{dilation}[0] \times (\text{kernel\_size}[0] - 1) - 1}{\text{stride}[0]} + 1\right\rfloor.. math:: W_{out} = \left\lfloor\frac{W_{in}+ 2 \times \text{padding}[1] - \text{dilation}[1] \times (\text{kernel\_size}[1] - 1) - 1}{\text{stride}[1]} + 1\right\rfloor

? 以上的计算公式为 LaTeX 格式,这是一种基于 TEX 的排版系统,由美国计算机学家莱斯利-兰伯特发明,利用这种格式,能够快速生成复杂表格和数学公式。不过这种格式对于我们来说可读性比较差,这里用富本编辑器的 公式 功能转换一下,来看 H_{out}:

? 这里考虑了空洞卷积的膨胀系数,如果把 dilation 去掉,shape 推导会是这样的:

? 以上是 pytorch 中关于卷积 shape 的推导方式,下面来看一下 darknet 中的情况。
2、darknet 卷积输出 shape 推导 ?同样看源码,在 parser.c 中有:
int size = option_find_int(options, "size", 1); int pad = option_find_int_quiet(options, "pad", 0); int padding = option_find_int_quiet(options, "padding", 0); if(pad) padding = size/2;

【【经验分享】pytorch|【经验分享】pytorch 与 darknet 计算卷积输出 shape 方式对比】?其中 option_find_int 和 option_find_int_quiet 均为链表查找函数,这里有点不一样的地方,可以把 pad 理解为填充标志位,实际填充值为 padding,从以上代码可以看出实际用的 padding 并不是直接从模型文件中读出来的 padding,而是通过标志位 pad 判断处理后得到的 padding。
?如上处理后再进行卷积输出 shape 的计算,代码在 convolutional_layer.c 中:
/// 调用接口 /// int out_h = convolutional_out_height(l); /// int out_w = convolutional_out_width(l); int convolutional_out_height(convolutional_layer l) { return (l.h + 2*l.pad - l.size) / l.stride_y + 1; }int convolutional_out_width(convolutional_layer l) { return (l.w + 2*l.pad - l.size) / l.stride_x + 1; }

?将以上的 out_h 计算过程可视化一下:

? 看到这里可能还有一个疑惑,怎么直接用的是 l.pad,而不是 l.padding 呢,答案在这里:
l.pad = padding;

?这样就说得通了,再来看个案例,如下图:

? 来看框出来的层,以 n c h w 布局的话输入为 [n , 16, 256, 256],从可视化看到经过这个卷积后的 shape 依旧为 [n, 16, 256, 256],如果我们以 pytorch 的计算方式:Hout = (Hin + 2 * padding - (size - 1) - 1) / stride + 1 来计算的话,你可能会认为 pad 即为 padding,则 Hout = (256 + 2 - (1 - 1) -1) / 1 + 1 = 258,此时得到的结果显然不对,故这种计算方式存在问题。我们改用 darknet 中的计算方式,即把 pad 当作标志位,先计算 padding,这里算子参数里没有写 padding,则令 padding = 0,计算过程如下:
size = 1 padding = 0 pad = 1/// if(pad) padding = size / 2; padding = 1 / 2 = 0/// Hout = (Hin + 2 * padding - size) / stride + 1 Hout = (256 + 2 * 0 - 1) + 1 = 256

?对于 C 语言中的 / 是普通的除法,得到的为浮点数时,直接把小数点后面的舍去,即向下取整。
? 这样结果就能对起来了。
?以上分享了 pytorch 和 darknet 中关于卷积输出 shape 的计算方式对比,也是容易碰到的小坑,希望我的分享对你有一点帮助。
【公众号传送】
《【经验分享】pytorch 与 darknet 计算卷积输出 shape 方式对比》

    推荐阅读