【nn-Unet代码注释】generic_UNet.py


一、generic_UNet.py

1.1 文件结构

unet的基础网络结构文件;
相对地址:nnUNet-master/nnunet/network_architecture/generic_UNet.py

二、class ConvDropoutNormNonlin(nn.Module)

2.1 类功能

卷积网络的基本块basic_block;
结构:Conv->Dropout->Norm->Nonlin

2.2 代码注释

class ConvDropoutNormNonlin(nn.Module):
    """
    fixes a bug in ConvDropoutNormNonlin where lrelu was used regardless of nonlin. Bad.
    修复了在ConvDropoutNormNonlin中使用lrelu而不管nonlin的错误。
    Conv-Dropout-Norm-Nonlin : norm归一化; nonlin:nonlinear非线性
    """
    def __init__(self, input_channels, output_channels,
                 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):
        """
         Conv-Dropout-Norm-Nonlin : norm归一化; nonlin:nonlinear非线性
         :param input_channels:输入通道
         :param output_channels:输出通道
         :param conv_op:卷积操作函数,默认nn.Conv2d
         :param conv_kwargs:卷积参数字典
         :param norm_op:归一化操作函数,默认nn.BatchNorm2d
         :param  norm_op_kwargs:归一化参数字典
         :param dropout_op:dropout操作函数,默认nn.Dropout2d
         :param dropout_op_kwargs:dropout参数字典
         :param nonlin:非线性激活函数,默认nn.LeakyReLU
         :param nonlin_kwargs:非线性激活函数参数字典
        """
        super(ConvDropoutNormNonlin, self).__init__()
        # _kwargs是对应调用函数的参数字典
        if nonlin_kwargs is None: # nonlin -> nonlinear
            # nonlin默认为torch.nn.LeakyReLU(negative_slope=0.01, inplace=False)
            # negative_slope :控制负斜率的角度。默认值:1e-2
            # inplace:可以选择就地执行操作。默认值:False
            nonlin_kwargs = {'negative_slope': 1e-2, 'inplace': True}

        if dropout_op_kwargs is None:
            # p:每个输出节点以概率p置0
            # inplace:true_用执行随机失活后的结果覆盖原来的输入,改变了存储值
            dropout_op_kwargs = {'p': 0.5, 'inplace': True}
        if norm_op_kwargs is None:
            # instanceNorm:实例归一化
            # eps :常数(避免分母为零)
            # affine :是否添加可学习的放射变换
            # momentum :动态均值和动态方差所使用的动量
            norm_op_kwargs = {'eps': 1e-5, 'affine': True, 'momentum': 0.1}
        if conv_kwargs is None:
            # conv:卷积
            # kernel_size:	卷积核尺寸
            # stride : 卷积步长
            # padding : 填充操作
            # dilation : 扩张操作,控制kernel点(卷积核点)的间距,默认值:1。
            # bias : 为真,则在输出中添加一个可学习的偏差。
            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
        self.dropout_op_kwargs = dropout_op_kwargs
        self.norm_op_kwargs = norm_op_kwargs
        self.conv_kwargs = conv_kwargs
        self.conv_op = conv_op
        self.norm_op = norm_op

        # 参数前的*或者** : 将任意个数的参数导入到 Python 函数
        # *(args) : 参数以元组(tuple)的形式导入
        # **(args) : 参数以字典的形式导入

        self.conv = self.conv_op(input_channels, output_channels, **self.conv_kwargs)  # 设置conv操作
        if self.dropout_op is not None and self.dropout_op_kwargs['p'] is not None and self.dropout_op_kwargs[
            'p'] > 0:  # if 存在dropout操作,并且存在输出节点概率p,并且概率p大于0,则设置dropout操作
            self.dropout = self.dropout_op(**self.dropout_op_kwargs)
        else:
            self.dropout = None

        # instanceNorm:实例归一化
        # 实例归一化将每个样本的均值和标准差归一化到特定的值。
        # 实例归一化通常用于卷积神经网络(CNN)中,可以加速训练和提高模型的稳健性。
        self.instnorm = self.norm_op(output_channels, **self.norm_op_kwargs)
        self.lrelu = self.nonlin(**self.nonlin_kwargs)

    def forward(self, x):
        """
        执行conv-dropout-lrelu(instnorm)
        Q:x是什么参数
        A:输入的数据集
        """
        x = self.conv(x)
        if self.dropout is not None:
            x = self.dropout(x)
        return self.lrelu(self.instnorm(x))

2.3 参数前的*或者**

  1. 作用: *(args) : 参数以元组(tuple)的形式导入; **(args) : 参数以字典的形式导入
  2. 例子:
 conv_kwargs = {'kernel_size': 3, 'stride': 1, 'padding': 1, 'dilation': 1, 'bias': True}
 self.conv = self.conv_op(input_channels, output_channels, **self.conv_kwargs) 

三、class StackedConvLayers(nn.Module)

3.1 类功能

堆叠卷积层函数,以基本块basic_block为单位,生成num_convs个基础块连接的卷积层

3.2 代码注释

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):
        """
        堆叠卷积层,拼接多个基础块basic_block
        :param input_feature_channels:输入特征图通道
        :param output_feature_channels:输出特征图通道
        :param num_convs: 堆叠的conv数量
        :param conv_op:卷积操作
        :param conv_kwargs:卷积参数字典
        :param norm_op:归一化 操作
        :param norm_op_kwargs:归一化参数字典
        :param dropout_op:dropout操作
        :param dropout_op_kwargs:dropout参数字典
        :param nonlin:非线性激活函数
        :param nonlin_kwargs:非线性激活函数参数字典
        :param first_stride:第一次步长
        :param basic_block:基础块,默认ConvDropoutNormNonlin
        """
        self.input_channels = input_feature_channels
        self.output_channels = output_feature_channels
        # 参数字典,参数注释见ConvDropoutNormNonlin
        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:  # 存在需要修改的第一步长
            # deepcopy():深拷贝,完全拷贝了父对象及其子对象
            self.conv_kwargs_first_conv = deepcopy(conv_kwargs)
            # 第一个卷积模块复制卷积参数字典
            self.conv_kwargs_first_conv['stride'] = first_stride
            # 修改步长参数,改为第一次步长
        else:  # 不存在第一步长,直接使用conv的参数字典
            self.conv_kwargs_first_conv = conv_kwargs

        super(StackedConvLayers, self).__init__()
        # 参数前的*或者** : 将任意个数的参数导入到 Python 函数
        # *(args) : 参数以元组(tuple)的形式导入
        # **(args) : 参数以字典的形式导入
        self.blocks = nn.Sequential(
            # basic_block默认用的是ConvDropoutNormNonlin
            # 堆叠的基础块个数由num_convs决定
            # 第一个基础块的input_channels和conv_kwargs与后续基础块不同;后续基础块的input_channels=output_channels=output_feature_channels
            *([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)

四、class Upsample(nn.Module)

4.1 类功能

上采样函数

4.2 代码注释

# 上采样
class Upsample(nn.Module):
    def __init__(self, size=None, scale_factor=None, mode='nearest', align_corners=False):
        super(Upsample, self).__init__()
        self.align_corners = align_corners
        self.mode = mode
        self.scale_factor = scale_factor
        self.size = size

    def forward(self, x):
        # nn.functional.interpolate:对输入的张量进行插值操作
        # x:输入张量
        # size:输出大小
        # scale_factor:输出为输入的多少倍
        # mode:上采样算法,有’nearest’, ‘linear’, ‘bilinear’, ‘bicubic’ , ‘trilinear’和’area’. 默认使用’nearest’
        # align_corners:如果设置为True,则输入和输出张量由其角像素的中心点对齐,从而保留角像素处的值。如果设置为False,则输入和输出张量由它们的角像素的角点对齐,插值使用边界外值的边值填充
        return nn.functional.interpolate(x, size=self.size, scale_factor=self.scale_factor, mode=self.mode,
                                         align_corners=self.align_corners)

五、class Generic_UNet(SegmentationNetwork)

5.1 类功能

网络结构,需要继承SegmentationNetwork类

5.2 代码注释

5.2.1 参数初始化

    DEFAULT_BATCH_SIZE_3D = 2  # 3d_batch_size
    DEFAULT_PATCH_SIZE_3D = (64, 192, 160)  # 3d_patch_size:图像处理中的补丁大小
    SPACING_FACTOR_BETWEEN_STAGES = 2  # ?阶段之间的间距因素
    BASE_NUM_FEATURES_3D = 30  # 3d特性基础数值
    MAX_NUMPOOL_3D = 999  # 最大的3d的numpool
    MAX_NUM_FILTERS_3D = 320  # 最大的3d的过滤器个数

    DEFAULT_PATCH_SIZE_2D = (256, 256)
    BASE_NUM_FEATURES_2D = 30  # 2d特性基础数值
    DEFAULT_BATCH_SIZE_2D = 50  # 2d_batch_size
    MAX_NUMPOOL_2D = 999  # 最大的2d的numpool
    MAX_FILTERS_2D = 480  # 最大的2d的过滤器个数

    use_this_for_batch_size_computation_2D = 19739648  # ?
    use_this_for_batch_size_computation_3D = 520000000  # 505789440

    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):
        """
        Generic_UNet
        :param input_channels:输入通道
        :param base_num_features:堆叠conv函数中的输出通道个数
        :param num_classes:
        :param num_pool:
        :param num_conv_per_stage:堆叠conv函数的基础块个数
        :param  feat_map_mul_on_downscale: 下采样因子?
        :param conv_op:卷积操作函数
        :param norm_op:归一化操作函数
        :param norm_op_kwargs:归一化参数字典
        :param dropout_op:dropout操作函数
        :param dropout_op_kwargs:dropout参数字典
        :param nonlin:非线性激活函数
        :param nonlin_kwargs:非线性激活函数的参数字典
        :param deep_supervision:
        :param dropout_in_localization:局部化的dropout
        :param  final_nonlin:最后一层的激活函数使用softmax
        :param weightInitializer:
        :param pool_op_kernel_sizes:
        :param conv_kernel_sizes:卷积核大小
        :param upscale_logits:
        :param convolutional_pooling:
        :param convolutional_upsampling:bool,卷积上采样
        :param  max_num_features
        :param basic_block:基础块,默认是ConvDropoutNormNonlin
        :param seg_output_use_bias:分割输出使用的偏向
        """
        # basically more flexible than v1, architecture is the same 基本上比v1更灵活,架构是一样的
        # Does this look complicated? Nah bro. Functionality > usability 看起来复杂吗?没有兄弟。功能性>可用性
        # This does everything you need, including world peace. 这能满足你的一切需要,包括世界和平。
        # Questions? -> f.isensee@dkfz.de
        super(Generic_UNet, self).__init__()
        """
        参数初始化
        """
        self.convolutional_upsampling = convolutional_upsampling  # init参数:bool(F),卷积上采样
        self.convolutional_pooling = convolutional_pooling  # init参数:bool(F)
        self.upscale_logits = upscale_logits  # init参数:bool(F),高等逻辑?
        # _kwargs:字典
        if nonlin_kwargs is None:  # 激活函数参数配置
            nonlin_kwargs = {'negative_slope': 1e-2, 'inplace': True}
        if dropout_op_kwargs is None:  # dropout 操作参数配置
            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}
        # 卷积参数
        self.conv_kwargs = {'stride': 1, 'dilation': 1, 'bias': True}

        self.nonlin = nonlin
        self.nonlin_kwargs = nonlin_kwargs
        self.dropout_op_kwargs = dropout_op_kwargs
        self.norm_op_kwargs = norm_op_kwargs
        self.weightInitializer = weightInitializer
        self.conv_op = conv_op  # nn.conv2d/3d,区分2/3d卷积
        self.norm_op = norm_op
        self.dropout_op = dropout_op
        self.num_classes = num_classes
        self.final_nonlin = final_nonlin
        self._deep_supervision = deep_supervision
        self.do_ds = deep_supervision
        # 卷积操作选择
        # 如果卷积是2d
        if conv_op == nn.Conv2d:
            upsample_mode = 'bilinear'  # 上采样:二线性插值
            pool_op = nn.MaxPool2d  # 池化:2d最大池化层
            transpconv = nn.ConvTranspose2d  # 2d转置卷积
            # 池化核大小默认2x2
            if pool_op_kernel_sizes is None:
                # 例子:[(2, 2)] * 3得到[(2, 2), (2, 2), (2, 2)]
                pool_op_kernel_sizes = [(2, 2)] * num_pool
            # 卷积核大小默认3x3
            if conv_kernel_sizes is None:
                conv_kernel_sizes = [(3, 3)] * (num_pool + 1)
        # 如果卷积是3d
        elif conv_op == nn.Conv3d:
            upsample_mode = 'trilinear'  # 上采样:三线性插值
            pool_op = nn.MaxPool3d  # 池化:3d最大池化层
            transpconv = nn.ConvTranspose3d  # 3d转置卷积
            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)
        else:  # conv_op未知报错
            raise ValueError("unknown convolution dimensionality, conv op: %s" % str(conv_op))
        # np.prod:计算数组中所有元素的乘积(array,axis=),axis=0指按照0轴运算
        # 例子:pool_op_kernel_sizes=[(2, 2), (2, 2), (2, 2)],axis=0,则np.pad结果为[8 8]
        self.input_shape_must_be_divisible_by = np.prod(pool_op_kernel_sizes, 0, dtype=np.int64)  # 输入大小必须是可分割的
        self.pool_op_kernel_sizes = pool_op_kernel_sizes
        self.conv_kernel_sizes = conv_kernel_sizes
        # 定义卷积过程中的卷积填充尺寸。当卷积核为3时,填充尺寸为1否则为0.
        # self.conv_pad_sizes:将conv_kernel_sizes中是3的转化为1,非3转化为0;()换成[]
        # 例子:conv_kernel_sizes = [(3, 1), (2, 3), (3, 3)],conv_pad_sizes[[1, 0], [0, 1], [1, 1]]
        self.conv_pad_sizes = []  # 卷积填充大小
        for krnl in self.conv_kernel_sizes:
            # append:添加的元素在列表的末尾
            self.conv_pad_sizes.append([1 if i == 3 else 0 for i in krnl])

        if max_num_features is None:
            if self.conv_op == nn.Conv3d:
                self.max_num_features = self.MAX_NUM_FILTERS_3D
            else:
                self.max_num_features = self.MAX_FILTERS_2D
        else:
            self.max_num_features = max_num_features

5.2.2 网络结构

编码->瓶颈结构->解码->分割生成图

        """
        网络结构
        """
        self.conv_blocks_context = []  # 网络结构内容
        self.conv_blocks_localization = []  # 网络结构局部化(本地化
        self.td = []  # down,下采样的池化层
        self.tu = []  # up,上采样,上采样卷积或者是转置
        self.seg_outputs = []  # 最后一层的输出

        output_features = base_num_features
        input_features = input_channels  # 输入通道
        """
        编码
        """
        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]  # 取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 添加定义的卷积模块 。
            # 调用堆叠卷积函数,加入num_conv_per_stage 个基础块,基础块:Conv-Dropout-Norm-Nonlin
            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:
                #   pool_op是最大池化层, nn.MaxPool2d
                self.td.append(pool_op(pool_op_kernel_sizes[d]))
            # 为下一次循环计算输入和输出的大小
            input_features = output_features
            # output_features:输出的featuremap的数量等于定义的数量乘以下采样因子
            output_features = int(np.round(output_features * feat_map_mul_on_downscale))
            output_features = min(output_features, self.max_num_features)  #不能大于最大值

        """
        瓶颈结构:从编码到解码的最底层
        """
        # now the bottleneck.
        #  bottleneck瓶颈结构指的是通道数的变化,即原通道数先减少,后增大到原来的大小,为了降低参数量
        # determine the first stride
        if self.convolutional_pooling:
            first_stride = pool_op_kernel_sizes[-1]
        else:
            first_stride = None

        # the output of the last conv must match the number of features from the skip connection if we are not using
        # convolutional upsampling. If we use convolutional upsampling then the reduction in feature maps will be
        # done by the transposed conv
        # 最终的输出特征大小
        if self.convolutional_upsampling:
            # 如果我们使用卷积上采样,那么特征映射的减少将通过转置卷积来完成
            final_num_features = output_features
        else:
            # 如果我们不使用卷积上采样,最后一个conv的输出必须匹配跳过连接的特征数量。
            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]
        # 加入卷积网络,也是num_conv_per_stage个,拆成两部分,最后一个卷积的输出final_num_features
        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)))

        # if we don't want to do dropout in the localization pathway then we set the dropout prob to zero here
        # 如果我们不想在局部路径中做dropout,那么我们在这里将dropout问题设置为零
        if not dropout_in_localization:
            old_dropout_p = self.dropout_op_kwargs['p']
            self.dropout_op_kwargs['p'] = 0.0

        """
        解码结构
        """
        # now lets build the localization pathway 现在让我们构建局部路径
        for u in range(num_pool):
            nfeatures_from_down = final_num_features
            # self.conv_blocks_context[-1]是 bottleneck, 所以从 -2开始取输出通道大小做跳跃连接
            # 对于解码部分,输入大小是输出大小的两倍
            nfeatures_from_skip = self.conv_blocks_context[
                -(2 + u)].output_channels  # nfeatures_from_skip是卷积部分的输出大小
            n_features_after_tu_and_concat = nfeatures_from_skip * 2  # 跳跃连接后的大小,是卷积部分的输入大小

            # the first conv reduces the number of features to match those of skip
            # the following convs work on that number of features
            # if not convolutional upsampling then the final conv reduces the num of features again
            # 第一个conv减少了与skip相匹配的特征数量,
            # 接下来的卷积都是针对这个数量的特征工作的,
            # 如果不是卷积上采样,那么最后的conv再次减少了特征的数量
            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基础块,输入大小是上采样跳跃连接之后的n_features_after_tu_and_concat
            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)

        if not dropout_in_localization:
            self.dropout_op_kwargs['p'] = old_dropout_p

5.2.3 网络模块封装

        """
        register all modules properly 正确注册所有模块,将模型封装在容器中
        """
        self.conv_blocks_localization = nn.ModuleList(self.conv_blocks_localization)  # 解码结构中的conv_blocks部分
        self.conv_blocks_context = nn.ModuleList(self.conv_blocks_context)  # 编码和瓶颈结构中的conv_blocks部分
        self.td = nn.ModuleList(self.td)  # 下采样,maxpools
        self.tu = nn.ModuleList(self.tu)  # 上采样,卷积上采样或者转置
        self.seg_outputs = nn.ModuleList(self.seg_outputs)  # 最后一层输出
        if self.upscale_logits:
            self.upscale_logits_ops = nn.ModuleList(
                # x不是一个模块,所以我们需要在这里进行区分
                self.upscale_logits_ops)  # lambda x:x is not a Module so we need to distinguish here
        if self.weightInitializer is not None:
            self.apply(self.weightInitializer)
            # self.apply(print_module_training_status)

5.2.4 def forward(self,x)

    def forward(self, x):
        skips = []  # 跳跃连接的内容
        seg_outputs = []
        # 编码过程
        for d in range(len(self.conv_blocks_context) - 1):
            x = self.conv_blocks_context[d](x)  # 编码conv_block的结果
            skips.append(x)  # 存储需要做跳跃连接的内容
            if not self.convolutional_pooling:
                x = self.td[d](x)  # 下采样
        # 瓶颈结构
        x = self.conv_blocks_context[-1](x)  # -1是瓶颈结构的内容
        # 解码过程
        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)  # 解码conv_block的结果
            seg_outputs.append(self.final_nonlin(self.seg_outputs[u](x)))  #?

        if self._deep_supervision and self.do_ds:
            # deep_supervision:深层监测
            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]


  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值