ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-06-21 21:31:53  阅读:405  来源: 互联网

标签:conv uuUNet 解读 num kwargs 网络结构 dropout self op


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

文章目录


前言

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

一、网络结构生成位置

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

二、参数及参数含义

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网络结构生成。

标签:conv,uuUNet,解读,num,kwargs,网络结构,dropout,self,op
来源: https://blog.csdn.net/weixin_44876554/article/details/118083658

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有