一、前言
经网友提醒,yolo v2的 passthrough 层与 v5 的 focus 层很像,因为海思是支持 passthrough 层的,鉴于此,花了点时间了解了一下,提出一些浅见,抛砖引玉。另附:yolo v5 海思开发
二、区别
上文我说的是,二者很像,说明它们还是有区别的,现在说说区别。
1. passthrough 层
出于严谨,结合海思文档图片与 passthrough 源码来一起理解,先看看 passthrough 源码:
// 它的源码是 c++ 的,不是 python 格式
int reorg_cpu(THFloatTensor *x_tensor, int w, int h, int c, int batch, int stride, int forward, THFloatTensor *out_tensor)
3 {
4 // Grab the tensor
5 float * x = THFloatTensor_data(x_tensor);
6 float * out = THFloatTensor_data(out_tensor);
7
8 // https://github.com/pjreddie/darknet/blob/master/src/blas.c
9 int b,i,j,k;
10 int out_c = c/(stride*stride);
11
12 for(b = 0; b < batch; ++b){
//batch_size
13
14 for(k = 0; k < c; ++k){
//channel
15
16 for(j = 0; j < h; ++j){
//height
17
18 for(i = 0; i < w; ++i){
//width,可以看见passthrough 是行优先 !
19
20 int in_index = i + w*(j + h*(k + c*b));
21 int c2 = k % out_c;
22 int offset = k / out_c;
23 int w2 = i*stride + offset % stride;
24 int h2 = j*stride + offset / stride;
25 int out_index = w2 + w*stride*(h2 + h*stride*(c2 + out_c*b));
26 if(forward) out[out_index] = x[in_index]; // 压缩channel
27 else out[in_index] = x[out_index]; // 扩展channel
28 }
29 }
30 }
31 }
32
33 return 1;
34 }
再结合海思的文档的图示来看,更加清晰,上图可以看见数据重新排布的顺序是 红色 -> 天蓝色 -> 淡绿(左下) -> 深绿(右下),即行优先。再结合上文第18行代码可得出,passthrough 层确实是行优先,这个先记住。
2. focus 层
focus层没有上面的图示,只有代码,我们根据源码举个例子加强理解一下。
# 源码镇楼
class Focus(nn.Module):
# Focus wh information into c-space
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
super(Focus, self).__init__()
self.conv = Conv(c1 * 4, c2, k, s, p, g, act)
def forward(self, x): # x(b,c,h,w) -> y(b,4c,h/2,w/2)
return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))
下面举例:
a = np.array(range(8)).reshape(1, 2, 2, 2)
print(a.shape)
print(a)
d = np.concatenate([a[..., ::2, ::2], a