uuUNet 网络结构生成代码部分解读

uuUNet 网络结构生成代码部分解读

文章目录


前言

nnUNet 网络结构生成代码解读,研究参数含义。

一、网络结构生成位置

uuUNet 网络结构生成代码部分解读

二、参数及参数含义

1.函数构造函数

代码如下(示例):

   def __init__(self, input_channels, base_num_features, num_classes, num_pool, num_conv_per_stage=2,
                 feat_map_mul_on_downscale=2, conv_op=nn.Conv2d,
                 norm_op=nn.BatchNorm2d, norm_op_kwargs=None,
                 dropout_op=nn.Dropout2d, dropout_op_kwargs=None,
                 nonlin=nn.LeakyReLU, nonlin_kwargs=None, deep_supervision=True, dropout_in_localization=False,
                 final_nonlin=softmax_helper, weightInitializer=InitWeights_He(1e-2), pool_op_kernel_sizes=None,
                 conv_kernel_sizes=None,
                 upscale_logits=False, convolutional_pooling=False, convolutional_upsampling=False,
                 max_num_features=None, basic_block=ConvDropoutNormNonlin,
                 seg_output_use_bias=False):

2.参数含义及设置

代码如下(示例):

nonlin_kwargs:激活函数参数配置,如果为空则设置
	nonlin_kwargs = {'negative_slope': 1e-2, 'inplace': True}
dropout_op_kwargs :dropout 操作参数配置,如果为空则设置:
	dropout_op_kwargs = {'p': 0.5, 'inplace': True}
norm_op_kwargs :正则化操作参数配置,如果为空则设置:
	norm_op_kwargs = {'eps': 1e-5, 'affine': True, 'momentum': 0.1}
#设置卷积参数,步长为1,
self.conv_kwargs = {'stride': 1, 'dilation': 1, 'bias': True}

卷积操作选择
if conv_op == nn.Conv2d:
     upsample_mode = 'bilinear'
     pool_op = nn.MaxPool2d
     transpconv = nn.ConvTranspose2d
     if pool_op_kernel_sizes is None:
         pool_op_kernel_sizes = [(2, 2)] * num_pool
     if conv_kernel_sizes is None:
         conv_kernel_sizes = [(3, 3)] * (num_pool + 1)
2D卷积对应上采样操作为双线性插值,2D最大池化,2D转置卷积。
如果传入参数中 pool_op_kernel_sizes为空,即未定义池化核尺寸,则设置为[(2, 2)] * num_pool。
如果传入参数中 conv_kernel_sizes为空,即未定义卷积核核尺寸,则设置为[(3, 3)] * (num_pool+1)。
elif conv_op == nn.Conv3d:
     upsample_mode = 'trilinear'
     pool_op = nn.MaxPool3d
     transpconv = nn.ConvTranspose3d
     if pool_op_kernel_sizes is None:
         pool_op_kernel_sizes = [(2, 2, 2)] * num_pool
     if conv_kernel_sizes is None:
         conv_kernel_sizes = [(3, 3, 3)] * (num_pool + 1)
3D卷积对应上采样操作为三线性插值,3D最大池化,3D转置卷积。
如果传入参数中 pool_op_kernel_sizes为空,即未定义池化核尺寸,则设置为[(2, 2,2)] * num_pool。
如果传入参数中 conv_kernel_sizes为空,即未定义卷积核核尺寸,则设置为[(3, 3,3)] * (num_pool+1)。
else:
    raise ValueError("unknown convolution dimensionality, conv op: %s" % str(conv_op))

self.input_shape_must_be_divisible_by = np.prod(pool_op_kernel_sizes, 0, dtype=np.int64)#连乘操作,将所有的元素相乘

定义卷积过程中的卷积填充尺寸。当卷积核为3时,填充尺寸为1否则为0.
for krnl in self.conv_kernel_sizes:
            self.conv_pad_sizes.append([1 if i == 3 else 0 for i in krnl])

for d in range(num_pool):
           # determine the first stride
           if d != 0 and self.convolutional_pooling:
               first_stride = pool_op_kernel_sizes[d - 1]
           else:
               first_stride = None

           self.conv_kwargs['kernel_size'] = self.conv_kernel_sizes[d]
           self.conv_kwargs['padding'] = self.conv_pad_sizes[d]
           # add convolutions添加定义的卷积模块
           self.conv_blocks_context.append(StackedConvLayers(input_features, output_features, num_conv_per_stage,
                                                             self.conv_op, self.conv_kwargs, self.norm_op,
                                                             self.norm_op_kwargs, self.dropout_op,
                                                             self.dropout_op_kwargs, self.nonlin, self.nonlin_kwargs,
                                                             first_stride, basic_block=basic_block))
                                                            
           if not self.convolutional_pooling:
               self.td.append(pool_op(pool_op_kernel_sizes[d]))
           input_features = output_features
           output_features = int(np.round(output_features * feat_map_mul_on_downscale))#输出的featuremap的数量等于定义的数量乘以下采样因子

           output_features = min(output_features, self.max_num_features)#不能大于最大值

#接下来定义bottleneck
#首先确定第一个步长
if self.convolutional_pooling:
            first_stride = pool_op_kernel_sizes[-1]
        else:
            first_stride = None
#如果我们不使用卷积上采样,最后一个conv的输出必须与跳过连接的特征数匹配。如果我们使用卷积上采样,那么特征映射的约简将由转置的conv来完成
if self.convolutional_upsampling:
            final_num_features = output_features
        else:
            final_num_features = self.conv_blocks_context[-1].output_channels

self.conv_kwargs['kernel_size'] = self.conv_kernel_sizes[num_pool]
self.conv_kwargs['padding'] = self.conv_pad_sizes[num_pool]
#加入两个经典的卷积模块
self.conv_blocks_context.append(nn.Sequential(
    StackedConvLayers(input_features, output_features, num_conv_per_stage - 1, self.conv_op, self.conv_kwargs,
                      self.norm_op, self.norm_op_kwargs, self.dropout_op, self.dropout_op_kwargs, self.nonlin,
                      self.nonlin_kwargs, first_stride, basic_block=basic_block),
    StackedConvLayers(output_features, final_num_features, 1, self.conv_op, self.conv_kwargs,
                      self.norm_op, self.norm_op_kwargs, self.dropout_op, self.dropout_op_kwargs, self.nonlin,
                      self.nonlin_kwargs, basic_block=basic_block)))
#如果不想在局部路径中做dropout,就设置概率为0.
if not dropout_in_localization:
            old_dropout_p = self.dropout_op_kwargs['p']
            self.dropout_op_kwargs['p'] = 0.0
#构建局部路径
for u in range(num_pool):
    nfeatures_from_down = final_num_features
    nfeatures_from_skip = self.conv_blocks_context[
        -(2 + u)].output_channels  # self.conv_blocks_context[-1] is bottleneck, so start with -2,倒数第一个是Bottleneck,所以从倒数第二个开始
    n_features_after_tu_and_concat = nfeatures_from_skip * 2

    # 第一个卷积减少了与skip相匹配的特征数量
    # 接下来的卷积都是针对这个数量的特征工作的
    # 如果不是卷积上采样,那么最后的卷积将再次减少特征的数量
    if u != num_pool - 1 and not self.convolutional_upsampling:#如果不是最后一个模块并且不采用卷积上采样
        final_num_features = self.conv_blocks_context[-(3 + u)].output_channels
    else:
        final_num_features = nfeatures_from_skip

    if not self.convolutional_upsampling:
        self.tu.append(Upsample(scale_factor=pool_op_kernel_sizes[-(u + 1)], mode=upsample_mode))#采用插值算法进行上采样
    else:
        self.tu.append(transpconv(nfeatures_from_down, nfeatures_from_skip, pool_op_kernel_sizes[-(u + 1)],
                                  pool_op_kernel_sizes[-(u + 1)], bias=False))#否则则进行转置卷积

    self.conv_kwargs['kernel_size'] = self.conv_kernel_sizes[- (u + 1)]
    self.conv_kwargs['padding'] = self.conv_pad_sizes[- (u + 1)]
    #在局部模块中添加Conv-dropout-bn-lrelu模块,共两个 
    self.conv_blocks_localization.append(nn.Sequential(
        StackedConvLayers(n_features_after_tu_and_concat, nfeatures_from_skip, num_conv_per_stage - 1,
                          self.conv_op, self.conv_kwargs, self.norm_op, self.norm_op_kwargs, self.dropout_op,
                          self.dropout_op_kwargs, self.nonlin, self.nonlin_kwargs, basic_block=basic_block),
        StackedConvLayers(nfeatures_from_skip, final_num_features, 1, self.conv_op, self.conv_kwargs,
                          self.norm_op, self.norm_op_kwargs, self.dropout_op, self.dropout_op_kwargs,
                          self.nonlin, self.nonlin_kwargs, basic_block=basic_block)
    ))      
#分割预测图结果生成模块   
for ds in range(len(self.conv_blocks_localization)):
     self.seg_outputs.append(conv_op(self.conv_blocks_localization[ds][-1].output_channels, num_classes,
                                     1, 1, 0, 1, 1, seg_output_use_bias))   
                                 
self.upscale_logits_ops = []
cum_upsample = np.cumprod(np.vstack(pool_op_kernel_sizes), axis=0)[::-1]#通过累乘操作计算上采样的累计数
for usl in range(num_pool - 1):
   #如果设置有上采样因子则进行上采样,没有则不变
  if self.upscale_logits:
      self.upscale_logits_ops.append(Upsample(scale_factor=tuple([int(i) for i in cum_upsample[usl + 1]]),
                                              mode=upsample_mode))
  else:
      self.upscale_logits_ops.append(lambda x: x)  

 # register all modules properly将模型封装在容器中
self.conv_blocks_localization = nn.ModuleList(self.conv_blocks_localization)
self.conv_blocks_context = nn.ModuleList(self.conv_blocks_context)
self.td = nn.ModuleList(self.td)#初始值为空列表
self.tu = nn.ModuleList(self.tu)
self.seg_outputs = nn.ModuleList(self.seg_outputs)
if self.upscale_logits:
    self.upscale_logits_ops = nn.ModuleList(
        self.upscale_logits_ops)  # lambda x:x is not a Module so we need to distinguish here

def forward(self, x):#forward函数生成网络
   skips = []
   seg_outputs = []
   for d in range(len(self.conv_blocks_context) - 1):
       x = self.conv_blocks_context[d](x)
       skips.append(x)
       if not self.convolutional_pooling:
           x = self.td[d](x)

   x = self.conv_blocks_context[-1](x)

   for u in range(len(self.tu)):
       x = self.tu[u](x)
       x = torch.cat((x, skips[-(u + 1)]), dim=1)
       x = self.conv_blocks_localization[u](x)
       seg_outputs.append(self.final_nonlin(self.seg_outputs[u](x)))

   if self._deep_supervision and self.do_ds:
       return tuple([seg_outputs[-1]] + [i(j) for i, j in
                                         zip(list(self.upscale_logits_ops)[::-1], seg_outputs[:-1][::-1])])
   else:
       return seg_outputs[-1]

class ConvDropoutNonlinNorm(ConvDropoutNormNonlin):
    def forward(self, x):
        x = self.conv(x)
        if self.dropout is not None:
            x = self.dropout(x)
        return self.instnorm(self.lrelu(x))
#卷积过程中基本的模块操作

class StackedConvLayers(nn.Module):
    def __init__(self, input_feature_channels, output_feature_channels, num_convs,
                 conv_op=nn.Conv2d, conv_kwargs=None,
                 norm_op=nn.BatchNorm2d, norm_op_kwargs=None,
                 dropout_op=nn.Dropout2d, dropout_op_kwargs=None,
                 nonlin=nn.LeakyReLU, nonlin_kwargs=None, first_stride=None, basic_block=ConvDropoutNormNonlin):
        self.input_channels = input_feature_channels
        self.output_channels = output_feature_channels 输出featuremap的通道数

        if nonlin_kwargs is None:
            nonlin_kwargs = {'negative_slope': 1e-2, 'inplace': True}
        if dropout_op_kwargs is None:
            dropout_op_kwargs = {'p': 0.5, 'inplace': True}
        if norm_op_kwargs is None:
            norm_op_kwargs = {'eps': 1e-5, 'affine': True, 'momentum': 0.1}
        if conv_kwargs is None:
            conv_kwargs = {'kernel_size': 3, 'stride': 1, 'padding': 1, 'dilation': 1, 'bias': True}
        self.nonlin_kwargs = nonlin_kwargs
        self.nonlin = nonlin
        self.dropout_op = dropout_op #dropout操作选择
        self.dropout_op_kwargs = dropout_op_kwargs #dropout参数配置
        self.norm_op_kwargs = norm_op_kwargs #正则化参数配置
        self.conv_kwargs = conv_kwargs #卷积参数配置
        self.conv_op = conv_op #卷积操作
        self.norm_op = norm_op #正则化操作

        if first_stride is not None:
            self.conv_kwargs_first_conv = deepcopy(conv_kwargs)
            #如果为空第一个卷积模块则复制卷积参数配置
            self.conv_kwargs_first_conv['stride'] = first_stride
            #修改步长
        else:
            self.conv_kwargs_first_conv = conv_kwargs

       super(StackedConvLayers, self).__init__()
       self.blocks = nn.Sequential(
           *([basic_block(input_feature_channels, output_feature_channels, self.conv_op,
                          self.conv_kwargs_first_conv,
                          self.norm_op, self.norm_op_kwargs, self.dropout_op, self.dropout_op_kwargs,
                          self.nonlin, self.nonlin_kwargs)] +
             [basic_block(output_feature_channels, output_feature_channels, self.conv_op,
                          self.conv_kwargs,
                          self.norm_op, self.norm_op_kwargs, self.dropout_op, self.dropout_op_kwargs,
                          self.nonlin, self.nonlin_kwargs) for _ in range(num_convs - 1)]))

    def forward(self, x):
        return self.blocks(x)
        #返回两个卷积模块,模块结构为conv-dropout-lrelu-instnorm

该处使用的url网络请求的数据。


总结

以上就是今天要讲的内容,本文简单介绍了nnUNet网络结构生成。
上一篇:Python抓取哔哩哔哩up主信息:只要爬虫学的好,牢饭吃的早


下一篇:2021-11-06后端插件使用