欢迎关注我的公众号 [极智视界],回复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;
其中 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 的计算方式对比,也是容易碰到的小坑,希望我的分享对你有一点帮助。
【公众号传送】
扫描下方二维码即可关注我的微信公众号【极智视界】,获取更多AI经验分享,让我们用极致+极客的心态来迎接AI !