detectron程式碼理解(一):Resnet模型構建理解
這裡具體以resnet50為例進行說明,一句一句地分析程式碼,程式碼位置位於Resnet.py,具體的分析函式為add_ResNet_convX_body.
在分析之前首先貼上resnet50的程式碼結構圖:
# add the stem (by default, conv1 and pool1 with bn; can support gn)
p, dim_in = globals()[cfg.RESNETS.STEM_FUNC](model, 'data')
這裡p = gpu_0/pool1,dim_in = 64
從上面的圖可以看出,p就是經過開始的3×3的pooling得到的,因為前面的卷積個數為64,poll不改變特徵圖個數,所以輸出的dim_in = 64。這裡之所以稱為dim_in是相對與下面bottleneck是輸入。
dim_bottleneck = cfg.RESNETS.NUM_GROUPS * cfg.RESNETS.WIDTH_PER_GROUP
(n1, n2, n3) = block_counts[:3] #
dim_bottleneck的數值與GPU的數量有關,GPU的數量乘以這個一個基本的數值cfg.RESNETS.WIDTH_PER_GROUP就得到了dim_bottleneck,這裡dim_bottleneck = 64。
(n1, n2, n3) = (3,4,6)。這裡的n1是表格中conv2_x中的3,代表3個residual block,n2,n3同理
s, dim_in = add_stage(model, 'res2', p, n1, dim_in, 256, dim_bottleneck, 1)
新增第一個stage,其中(下面所述的表格中的都是,conv2_x這一行的)
(1)p為輸入,即gpu_0/pool1
(2)n1為block的數目,
(3)dim_in是輸入特徵圖的大小,即上面所述的64
(4)256表示經過這個stage後輸出的特徵圖數量,對應表格中的256
(5)dim_bottleneck=64,對應表格中的64
我們再來看stage函式,是一個for迴圈,同過迴圈來依次新增resiual block,n=3,也就是新增3個
def add_stage( model, prefix, blob_in, n, dim_in, dim_out, dim_inner, dilation, # 相當於padding引數 stride_init=2 ): """Add a ResNet stage to the model by stacking n residual blocks.""" # e.g., prefix = res2 for i in range(n): blob_in = add_residual_block( model, '{}_{}'.format(prefix, i), blob_in, dim_in, dim_out, dim_inner, dilation, stride_init, # Not using inplace for the last block; # it may be fetched externally or used by FPN inplace_sum=i < n - 1 ) print(blob_in) dim_in = dim_out return blob_in, dim_in
add_stage這個函式會呼叫到add_residual_block函式,應該很容易的理解到,對於conv2_x這個stage每一個residual block的輸出都是256個featuremap。
同時還要理清相關的關係,於第一個residual block,下面的dim_in是64,是上面pool的輸出。對於第二個residual block,這時候的dim_in就是第一個residual block的輸出了,也就是256,不過中間的dim_inner仍是64,其目的是減少引數,最後的輸出dim_out也還是256。第三個residual block同理,對應表格就能很清楚的明白。
def add_residual_block(
model,
prefix,
blob_in,
dim_in,
dim_out,
dim_inner,
dilation,
stride_init=2,
inplace_sum=False
):
"""Add a residual block to the model."""
# prefix = res<stage>_<sub_stage>, e.g., res2_3
# Max pooling is performed prior to the first stage (which is uniquely
# distinguished by dim_in = 64), thus we keep stride = 1 for the first stage
stride = stride_init if (
dim_in != dim_out and dim_in != 64 and dilation == 1
) else 1
# transformation blob dim_in表示輸入,dim_out表示輸出,dim_inner表示中間
tr = globals()[cfg.RESNETS.TRANS_FUNC](
model,
blob_in,
dim_in,
dim_out,
stride,
prefix,
dim_inner,
group=cfg.RESNETS.NUM_GROUPS,
dilation=dilation
)
# sum -> ReLU
# shortcut function: by default using bn; support gn 恆等對映
add_shortcut = globals()[cfg.RESNETS.SHORTCUT_FUNC]
sc = add_shortcut(model, prefix, blob_in, dim_in, dim_out, stride) #恆等對映
if inplace_sum:
s = model.net.Sum([tr, sc], tr) #第二個引數相當於命名
else:
s = model.net.Sum([tr, sc], prefix + '_sum')
return model.Relu(s, s)
最後生成的模型結構(加粗部分是該stage最終的輸出,res2_0表示第二層的第一個residual block,res2_1表示第二層的第二個residual block)
conv2_x | gpu_0/res2_0_branch2c_bn gpu_0/res2_1_branch2c_bn gpu_0/res2_2_sum |
conv3_x | gpu_0/res3_0_branch2c_bn gpu_0/res3_1_branch2c_bn gpu_0/res3_2_branch2c_bn gpu_0/res3_3_sum |
conv4_x | gpu_0/res4_0_branch2c_bn gpu_0/res4_1_branch2c_bn gpu_0/res4_2_branch2c_bn gpu_0/res4_3_branch2c_bn gpu_0/res4_4_branch2c_bn gpu_0/res4_5_sum |
conv5_x | gpu_0/res5_0_branch2c_bn gpu_0/res5_2_sum |