DeepLabv3

Rethinking Atrous Convolution for Semantic Image Segmentation

原文地址:DeepLabv3

代码:

  • TensorFlow

Abstract

DeepLabv3进一步探讨空洞卷积,这是一个在语义分割任务中:可以调整滤波器视野、控制卷积神经网络计算的特征响应分辨率的强大工具。为了解决多尺度下的目标分割问题,我们设计了空洞卷积级联或不同采样率空洞卷积并行架构。此外,我们强调了ASPP(Atrous Spatial Pyramid Pooling)模块,该模块可以在获取多个尺度上卷积特征,进一步提升性能。同时,我们分享了实施细节和训练方法,此次提出的DeepLabv3相比先前的版本有显著的效果提升,在PASCAL VOC 2012上获得了先进的性能。


Introduction

对于语义分割任务,在应用深度卷积神经网络中的有两个挑战:

  • 第一个挑战:连续池化和下采样,让高层特征具有局部图像变换的内在不变性,这允许DCNN学习越来越抽象的特征表示。但同时引起的特征分辨率下降,会妨碍密集的定位预测任务,因为这需要详细的空间信息。DeepLabv3系列解决这一问题的办法是使用空洞卷积(前两个版本会使用CRF细化分割结果),这允许我们可以保持参数量和计算量的同时提升计算特征响应的分辨率,从而获得更多的上下文。

  • 第二个挑战:多尺度目标的存在。现有多种处理多尺度目标的方法,我们主要考虑4种,如下图:

  • a. Image Pyramid: 将输入图片放缩成不同比例,分别应用在DCNN上,将预测结果融合得到最终输出
  • b. Encoder-Decoder: 利用Encoder阶段的多尺度特征,运用到Decoder阶段上恢复空间分辨率(代表工作有FCN、SegNet、PSPNet等工作)
  • c. Deeper w. Atrous Convolution: 在原始模型的顶端增加额外的模块,例如DenseCRF,捕捉像素间长距离信息
  • d. Spatial Pyramid Pooling: 空间金字塔池化具有不同采样率和多种视野的卷积核,能够以多尺度捕捉对象

DeepLabv3的主要贡献在于:

  • 本文重新讨论了空洞卷积的使用,这让我们在级联模块和空间金字塔池化的框架下,能够获取更大的感受野从而获取多尺度信息。

  • 改进了ASPP模块:由不同采样率的空洞卷积和BN层组成,我们尝试以级联或并行的方式布局模块。

  • 讨论了一个重要问题:使用大采样率的 3 × 3 3×3 3×3的空洞卷积,因为图像边界响应无法捕捉远距离信息,会退化为1×1的卷积, 我们建议将图像级特征融合到ASPP模块中。

  • 阐述了训练细节并分享了训练经验,论文提出的"DeepLabv3"改进了以前的工作,获得了很好的结果


Related Work

现有多个工作表明全局特征或上下文之间的互相作用有助于做语义分割,我们讨论四种不同类型利用上下文信息做语义分割的全卷积网络。

  • 图像金字塔(Image pyramid): 通常使用共享权重的模型,适用于多尺度的输入。小尺度的输入响应控制语义,大尺寸的输入响应控制细节。通过拉布拉斯金字塔对输入变换成多尺度,传入DCNN,融合输出。这类的缺点是:因为GPU存储器的限制,对于更大/更深的模型不方便扩展。通常应用于推断阶段

  • 编码器-解码器(Encoder-decoder): **编码器的高层次的特征容易捕获更长的距离信息,在解码器阶段使用编码器阶段的信息帮助恢复目标的细节和空间维度。**例如SegNet利用下采样的池化索引作为上采样的指导;U-Net增加了编码器部分的特征跳跃连接到解码器;RefineNet等证明了Encoder-Decoder结构的有效性。

  • 上下文模块(Context module):包含了额外的模块用于级联编码长距离的上下文。一种有效的方法是DenseCRF并入DCNN中,共同训练DCNN和CRF。

  • 空间金字塔池化(Spatial pyramid pooling):**采用空间金字塔池化可以捕捉多个层次的上下文。**在ParseNet中从不同图像等级的特征中获取上下文信息;DeepLabv2提出ASPP,以不同采样率的并行空洞卷积捕捉多尺度信息。最近PSPNet在不同网格尺度上执行空间池化,并在多个数据集上获得优异的表现。还有其他基于LSTM方法聚合全局信息。

我们的工作主要探讨空洞卷积作为上下文模块和一个空间金字塔池化的工具,这适用于任何网络。具体来说,我们取ResNet最后一个block,复制多个级联起来,送入到ASPP模块后。我们通过实验发现使用BN层有利于训练过程,为了进一步捕获全局上下文,我们建议在ASPP上融入图像级特征.


Method

空洞卷积应用于密集的特征提取

这在DeepLabv1和DeepLabv2都已经讲过,这里不详解了~

深层次的空洞卷积

我们首先探讨将空洞卷积应用在级联模块。具体来说,我们取ResNet中最后一个block,在下图中为block4,并在其后面增加级联模块。

  • 上图(a)所示,整体图片的信息总结到后面非常小的特征映射上,但实验证明这是不利于语义分割的。如下图:

使用步幅越长的特征映射,得到的结果反倒会差,结果最好的out_stride = 8 需要占用较多的存储空间。因为连续的下采样会降低特征映射的分辨率,细节信息被抽取,这对语义分割是有害的。

  • 上图(b)所示,可使用不同采样率的空洞卷积保持输出步幅的为out_stride = 16.这样不增加参数量和计算量同时有效的缩小了步幅。

### Atrous Spatial Pyramid Pooling

对于在DeepLabv2中提出的ASPP模块,其在特征顶部映射图并行使用了四种不同采样率的空洞卷积。这表明以不同尺度采样是有效的,我们在DeepLabv3中向ASPP中添加了BN层。不同采样率的空洞卷积可以有效的捕获多尺度信息,但是,我们发现随着采样率的增加,滤波器的有效权重(权重有效的应用在特征区域,而不是填充0)逐渐变小。如下图所示:

当我们不同采样率的 3 × 3 3×3 3×3卷积核应用在 65 × 65 65×65 65×65的特征映射上,当采样率接近特征映射大小时, 3 × 3 3×3 3×3的滤波器不是捕捉全图像的上下文,而是退化为简单的 1 × 1 1×1 1×1滤波器,只有滤波器中心点的权重起了作用。

为了克服这个问题,我们考虑使用图片级特征。具体来说,我们在模型最后的特征映射上应用全局平均,将结果经过 1 × 1 1×1 1×1的卷积,再双线性上采样得到所需的空间维度。最终,我们改进的ASPP包括:

  • 一个 1 × 1 1×1 1×1卷积和三个 3 × 3 3×3 3×3的采样率为 r a t e s = { 6 , 12 , 18 } rates = \{6,12,18\} rates={6,12,18}的空洞卷积,滤波器数量为256,包含BN层。针对output_stride=16的情况。如下图(a)部分Atrous Spatial Pyramid Pooling
  • 图像级特征,即将特征做全局平均池化,经过卷积,再融合。如下图(b)部分Image Pooling.

改进后的ASPP模块如下图所示:

注意当output_stride=8时,加倍了采样率。所有的特征通过 1 × 1 1×1 1×1级联到一起,生成最终的分数.


Experiment

采用的是预训练的ResNet为基础层,并配合使用了空洞卷积控制输出步幅。因为输出步幅output_stride(定义为输入图像的分辨率与最终输出分辨率的比值)。当我们输出步幅为8时,原ResNet的最后两个block包含的空洞卷积的采样率为 r = 2 r=2 r=2和 r = 4 r=4 r=4。

模型的训练设置:

部分 设置
数据集 PASCAL VOC 2012
工具 TensorFlow
裁剪尺寸 采样513大小的裁剪尺寸
学习率策略 采用poly策略, 在初始学习率基础上,乘以 ( 1 − i t e r m a x _ i t e r ) p o w e r (1-\frac{iter}{max\_iter})^{power} (1−max_iteriter​)power,其中 p o w e r = 0.9 power=0.9 power=0.9
BN层策略 当output_stride=16时,我们采用batchsize=16,同时BN层的参数做参数衰减0.9997。
在增强的数据集上,以初始学习率0.007训练30K后,冻结BN层参数。
采用output_stride=8时,再使用初始学习率0.001训练30K。
训练output_stride=16比output_stride=8要快很多,因为中间的特征映射在空间上小的四倍。但因为output_stride=16在特征映射上粗糙是牺牲了精度。
上采样策略 在先前的工作上,
我们是将最终的输出与GroundTruth下采样8倍做比较
现在我们发现保持GroundTruth更重要,故我们是将最终的输出上采样8倍与完整的GroundTruth比较。

Going Deeper with Atrous Convolution实验

我们首先试试级联使用多个带空洞卷积的block模块。

  • ResNet50:如下图,我们探究输出步幅的影响,当输出步幅为256时,由于严重的信号抽取,性能大大的下降了。

    而当我们使用不同采样率的空洞卷积,结果大大的上升了,这表现在语义分割中使用空洞卷积的必要性。

  • ResNet50 vs. ResNet101: 用更深的模型,并改变级联模块的数量。如下图,当block增加性能也随之增加。

  • Multi-grid: 我们使用的变体残差模块,采用Multi-gird策略,即主分支的三个卷积都使用空洞卷积,采样率设置Multi-gird策略。按照如下图:


- 应用不同策略通常比单倍数 ( r 1 , r 2 , r 3 ) = ( 1 , 1 , 1 ) (r_1,r_2,r_3)=(1,1,1) (r1​,r2​,r3​)=(1,1,1)效果要好
- 简单的提升倍数是无效的 ( r 1 , r 2 , r 3 ) = ( 2 , 2 , 2 ) (r_1,r_2,r_3)=(2,2,2) (r1​,r2​,r3​)=(2,2,2)
- 最好的随着网络的深入提升性能.即block7下 ( r 1 , r 2 , r 3 ) = ( 1 , 2 , 1 ) (r_1,r_2,r_3)=(1,2,1) (r1​,r2​,r3​)=(1,2,1)

  • Inference strategy on val set
    推断期间使用output_stride = 8,有着更丰富的细节内容:

Atrous Spatial Pyramid Pooling实验

  • ASPP模块相比以前增加了BN层,对比multi-grid策略和图片层级特征提升实验结果:

  • Inference strategy on val set
    推断期间使用output_stride = 8,有着更丰富的细节内容,采用多尺度输入和翻转,性能进一步提升了:

在PASCAL VOC 2012上表现:

Cityscapes表现

多种技巧配置结果:

与其他模型相比:

其他参数的影响

  • 上采样策略和裁剪大小和BN层的影响:

  • 不同batchsize的影响:

  • 不同评估步幅的影响:

Conclusion

DeepLabv3重点探讨了空洞卷积的使用,同时改进了ASPP模块,便于更好的捕捉多尺度上下文。


代码分析

因为没找到官方的代码,在github上找了一个DeepLabV3-TensorFlow版本.

训练脚本分析

先找到train_voc12.py训练文件。

找到关键的main方法:

创建训练模型 & 计算loss

def main():"""创建模型 and 准备训练."""h = args.input_sizew = args.input_sizeinput_size = (h, w)# 设置随机种子tf.set_random_seed(args.random_seed)# 创建线程队列,准备数据coord = tf.train.Coordinator()# 读取数据image_batch, label_batch = read_data(is_training=True)# 创建训练模型net, end_points = deeplabv3(image_batch,num_classes=args.num_classes,depth=args.num_layers,is_training=True,)# 对于小的batchsize,保持BN layers的统计参数更佳(即冻结预训练模型的BN参数)# If is_training=True, 统计参数在训练期间会被更新# 注意的是:即使is_training=False ,BN参数gamma (scale) and beta (offset) 也会更新# 取出模型预测值raw_output = end_points['resnet{}/logits'.format(args.num_layers)]# Which variables to load. Running means and variances are not trainable,# thus all_variables() should be restored.restore_var = [v for v in tf.global_variables() if 'fc' not in v.name or not args.not_restore_last]if args.freeze_bn:all_trainable = [v for v in tf.trainable_variables() if 'beta' not in v.name and 'gamma' not in v.name]else:all_trainable = [v for v in tf.trainable_variables()]conv_trainable = [v for v in all_trainable if 'fc' not in v.name] # 上采样logits输出,取代ground truth下采样raw_output_up = tf.image.resize_bilinear(raw_output, [h, w]) # 双线性插值放大到原大小# Predictions: 忽略标签中大于或等于n_classes的值label_proc = tf.squeeze(label_batch) # 删除数据标签tensor的shape中维度值为1mask = label_proc <= args.num_classes # 忽略标签中大于或等于n_classes的值seg_logits = tf.boolean_mask(raw_output_up, mask)  #取出预测值中感兴趣的maskseg_gt = tf.boolean_mask(label_proc, mask) # 取出数据标签中标注的mask(感兴趣的mask)seg_gt = tf.cast(seg_gt, tf.int32)  # 转换一下数据类型        # 逐像素做softmax loss.loss = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=seg_logits,labels=seg_gt)seg_loss = tf.reduce_mean(loss)seg_loss_sum = tf.summary.scalar('loss/seg', seg_loss) # TensorBoard记录# 增加正则化损失reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)reg_loss = tf.add_n(reg_losses)reg_loss_sum = tf.summary.scalar('loss/reg', reg_loss)tot_loss = seg_loss + reg_losstot_loss_sum = tf.summary.scalar('loss/tot', tot_loss)seg_pred = tf.argmax(seg_logits, axis=1)#  计算MIOU train_mean_iou, train_update_mean_iou = streaming_mean_iou(seg_pred, seg_gt, args.num_classes, name="train_iou")  train_iou_sum = tf.summary.scalar('accuracy/train_mean_iou', train_mean_iou)

关于streaming_mean_iou方法代码见metric_ops.py,该方法用于计算每步的平均交叉点(mIOU),即先计算每个类别的IOU,再平均到各个类上。

IOU的计算定义如下: I O U = t r u e _ p o s i t i v e t r u e _ p o s i t i v e + f a l s e _ p o s i t i v e + f a l s e _ n e g a t i v e IOU = \frac{true\_positive}{true\_positive+false\_positive+false\_negative} IOU=true_positive+false_positive+false_negativetrue_positive​该方法返回一个update_op操作用于估计数据流上的度量,更新变量并返回mean_iou.

上面代码初始化了DeepLabv3模型,并取出模型输出,计算了loss,并计算了mIOU.

训练参数设置

这里学习率没有使用poly策略,该github说学习率设置0.00001效果更好点~

    # 初始化训练参数train_initializer = tf.variables_initializer(var_list=tf.get_collection(tf.GraphKeys.LOCAL_VARIABLES, scope="train_iou"))# 定义 loss and 优化参数.# 这里学习率没采用poly策略  base_lr = tf.constant(args.learning_rate)step_ph = tf.placeholder(dtype=tf.float32, shape=())# learning_rate = tf.scalar_mul(base_lr, #    tf.pow((1 - step_ph / args.num_steps), args.power))learning_rate = base_lrlr_sum = tf.summary.scalar('params/learning_rate', learning_rate)train_sum_op = tf.summary.merge([seg_loss_sum, reg_loss_sum, tot_loss_sum, train_iou_sum, lr_sum])

创建交叉验证模型,并设置输出值

    # 交叉验证模型image_batch_val, label_batch_val = read_data(is_training=False)_, end_points_val = deeplabv3(image_batch_val,num_classes=args.num_classes,depth=args.num_layers,reuse=True,is_training=False,)raw_output_val = end_points_val['resnet{}/logits'.format(args.num_layers)] # 交叉验证输出nh, nw = tf.shape(image_batch_val)[1], tf.shape(image_batch_val)[2]seg_logits_val = tf.image.resize_bilinear(raw_output_val, [nh, nw])seg_pred_val = tf.argmax(seg_logits_val, axis=3)seg_pred_val = tf.expand_dims(seg_pred_val, 3)seg_pred_val = tf.reshape(seg_pred_val, [-1,])seg_gt_val = tf.cast(label_batch_val, tf.int32)seg_gt_val = tf.reshape(seg_gt_val, [-1,])mask_val = seg_gt_val <= args.num_classes - 1seg_pred_val = tf.boolean_mask(seg_pred_val, mask_val)seg_gt_val = tf.boolean_mask(seg_gt_val, mask_val)val_mean_iou, val_update_mean_iou = streaming_mean_iou(seg_pred_val, seg_gt_val, num_classes=args.num_classes, name="val_iou")        val_iou_sum = tf.summary.scalar('accuracy/val_mean_iou', val_mean_iou)

训练模型

    val_initializer = tf.variables_initializer(var_list=tf.get_collection(tf.GraphKeys.LOCAL_VARIABLES, scope="val_iou"))test_sum_op = tf.summary.merge([val_iou_sum])global_step = tf.train.get_or_create_global_step()opt = tf.train.MomentumOptimizer(learning_rate, args.momentum)grads_conv = tf.gradients(tot_loss, conv_trainable)# train_op = opt.apply_gradients(zip(grads_conv, conv_trainable))train_op = slim.learning.create_train_op(tot_loss, opt,global_step=global_step,variables_to_train=conv_trainable,summarize_gradients=True)# Set up tf session and initialize variables. config = tf.ConfigProto()config.gpu_options.allow_growth = Truesess = tf.Session(config=config)sess.run(tf.global_variables_initializer())sess.run(tf.local_variables_initializer())# Saver for storing checkpoints of the model.saver = tf.train.Saver(var_list=tf.global_variables(), max_to_keep=20)# 如果有checkpoint则加载if args.ckpt > 0 or args.restore_from is not None:loader = tf.train.Saver(var_list=restore_var)load(loader, sess, args.snapshot_dir)# 开始线程队列threads = tf.train.start_queue_runners(coord=coord, sess=sess)# tf.get_default_graph().finalize()summary_writer = tf.summary.FileWriter(args.snapshot_dir,sess.graph)# 迭代训练for step in range(args.ckpt, args.num_steps):start_time = time.time()feed_dict = { step_ph : step }tot_loss_float, seg_loss_float, reg_loss_float, _, lr_float, _,train_summary = sess.run([tot_loss, seg_loss, reg_loss, train_op,learning_rate, train_update_mean_iou, train_sum_op], feed_dict=feed_dict)train_mean_iou_float = sess.run(train_mean_iou)duration = time.time() - start_timesys.stdout.write('step {:d}, tot_loss = {:.6f}, seg_loss = {:.6f}, ' \'reg_loss = {:.6f}, mean_iou = {:.6f}, lr: {:.6f}({:.3f}' \'sec/step)\n'.format(step, tot_loss_float, seg_loss_float,reg_loss_float, train_mean_iou_float, lr_float, duration))sys.stdout.flush()if step % args.save_pred_every == 0 and step > args.ckpt:summary_writer.add_summary(train_summary, step)sess.run(val_initializer)for val_step in range(NUM_VAL-1):_, test_summary = sess.run([val_update_mean_iou, test_sum_op],feed_dict=feed_dict)summary_writer.add_summary(test_summary, step)val_mean_iou_float= sess.run(val_mean_iou)save(saver, sess, args.snapshot_dir, step)sys.stdout.write('step {:d}, train_mean_iou: {:.6f}, ' \'val_mean_iou: {:.6f}\n'.format(step, train_mean_iou_float, val_mean_iou_float))sys.stdout.flush()sess.run(train_initializer)if coord.should_stop():coord.request_stop()coord.join(threads)

模型分析

上面看完了训练脚本,下面看看DeepLabv3的模型定义脚本libs.nets.deeplabv3.py.

deeplabv3中ResNet变体

def deeplabv3(inputs,num_classes,depth=50,aspp=True,reuse=None,is_training=True):"""DeepLabV3Args:inputs: A tensor of size [batch, height, width, channels].depth: ResNet的深度 一般为101或51.aspp: 是否使用ASPP module, if True, 使用4 blocks with multi_grid=(1,2,4), if False, 使用7 blocks with multi_grid=(1,2,1).reuse: 模型参数重用(验证会重用训练的模型参数)Returns:net: A rank-4 tensor of size [batch, height_out, width_out, channels_out].end_points: 模型的组合"""if aspp:multi_grid = (1,2,4)else:multi_grid = (1,2,1)scope ='resnet{}'.format(depth)with tf.variable_scope(scope, [inputs], reuse=reuse) as sc:end_points_collection = sc.name + '_end_points'with slim.arg_scope(resnet_arg_scope(weight_decay=args.weight_decay, batch_norm_decay=args.bn_weight_decay)):with slim.arg_scope([slim.conv2d, bottleneck, bottleneck_hdc],outputs_collections=end_points_collection):with slim.arg_scope([slim.batch_norm], is_training=is_training):net = inputsnet = resnet_utils.conv2d_same(net, 64, 7, stride=2, scope='conv1')net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool1')with tf.variable_scope('block1', [net]) as sc:base_depth = 64for i in range(2):with tf.variable_scope('unit_%d' % (i + 1), values=[net]):net = bottleneck(net, depth=base_depth * 4, depth_bottleneck=base_depth, stride=1)with tf.variable_scope('unit_3', values=[net]):net = bottleneck(net, depth=base_depth * 4, depth_bottleneck=base_depth, stride=2)net = slim.utils.collect_named_outputs(end_points_collection, sc.name, net)with tf.variable_scope('block2', [net]) as sc:base_depth = 128for i in range(3):with tf.variable_scope('unit_%d' % (i + 1), values=[net]):net = bottleneck(net, depth=base_depth * 4, depth_bottleneck=base_depth, stride=1)with tf.variable_scope('unit_4', values=[net]):net = bottleneck(net, depth=base_depth * 4, depth_bottleneck=base_depth, stride=2)net = slim.utils.collect_named_outputs(end_points_collection, sc.name, net)with tf.variable_scope('block3', [net]) as sc:base_depth = 256num_units = 6if depth == 101:num_units = 23elif depth == 152:num_units = 36for i in range(num_units):with tf.variable_scope('unit_%d' % (i + 1), values=[net]):net = bottleneck(net, depth=base_depth * 4, depth_bottleneck=base_depth, stride=1)net = slim.utils.collect_named_outputs(end_points_collection, sc.name, net)with tf.variable_scope('block4', [net]) as sc:base_depth = 512for i in range(3):with tf.variable_scope('unit_%d' % (i + 1), values=[net]):net = bottleneck_hdc(net, depth=base_depth * 4, depth_bottleneck=base_depth, stride=1, rate=2, multi_grid=multi_grid)net = slim.utils.collect_named_outputs(end_points_collection, sc.name, net)

这部分实现的变体的ResNet结构,包括带mutli-grid的残差模块由libs.nets.deeplabv3.py中的bottleneck_hdc方法提供。

带mutli-grid策略的bottleneck_hdc残差结构代码如下:

@slim.add_arg_scope
def bottleneck_hdc(inputs,depth,depth_bottleneck,stride,rate=1,multi_grid=(1,2,4),outputs_collections=None,scope=None,use_bounded_activations=False):"""Hybrid Dilated Convolution Bottleneck.Multi_Grid = (1,2,4)See Understanding Convolution for Semantic Segmentation.When putting together two consecutive ResNet blocks that use this unit, oneshould use stride = 2 in the last unit of the first block.Args:inputs: A tensor of size [batch, height, width, channels].depth: The depth of the ResNet unit output.depth_bottleneck: The depth of the bottleneck layers.stride: The ResNet unit's stride. Determines the amount of downsampling ofthe units output compared to its input.rate: An integer, rate for atrous convolution.multi_grid: multi_grid sturcture.outputs_collections: Collection to add the ResNet unit output.scope: Optional variable_scope.use_bounded_activations: Whether or not to use bounded activations. Boundedactivations better lend themselves to quantized inference.Returns:The ResNet unit's output."""with tf.variable_scope(scope, 'bottleneck_v1', [inputs]) as sc:depth_in = slim.utils.last_dimension(inputs.get_shape(), min_rank=4)# 是否降采样if depth == depth_in:shortcut = resnet_utils.subsample(inputs, stride, 'shortcut')else:shortcut = slim.conv2d(inputs,depth, [1, 1],stride=stride,activation_fn=tf.nn.relu6 if use_bounded_activations else None,scope='shortcut')# 残差结构的主分支residual = slim.conv2d(inputs, depth_bottleneck, [1, 1], stride=1, rate=rate*multi_grid[0], scope='conv1')residual = resnet_utils.conv2d_same(residual, depth_bottleneck, 3, stride,rate=rate*multi_grid[1], scope='conv2')residual = slim.conv2d(residual, depth, [1, 1], stride=1, rate=rate*multi_grid[2], activation_fn=None, scope='conv3')# 是否后接激活函数if use_bounded_activations:# Use clip_by_value to simulate bandpass activation.residual = tf.clip_by_value(residual, -6.0, 6.0)output = tf.nn.relu6(shortcut + residual)else:output = tf.nn.relu(shortcut + residual)return slim.utils.collect_named_outputs(outputs_collections,sc.name,output)

下面是关于aspp模块和后期的空洞卷积策略使用

          if aspp:with tf.variable_scope('aspp', [net]) as sc:aspp_list = []branch_1 = slim.conv2d(net, 256, [1,1], stride=1, scope='1x1conv')branch_1 = slim.utils.collect_named_outputs(end_points_collection, sc.name, branch_1)aspp_list.append(branch_1)for i in range(3):branch_2 = slim.conv2d(net, 256, [3,3], stride=1, rate=6*(i+1), scope='rate{}'.format(6*(i+1)))branch_2 = slim.utils.collect_named_outputs(end_points_collection, sc.name, branch_2)aspp_list.append(branch_2)aspp = tf.add_n(aspp_list)aspp = slim.utils.collect_named_outputs(end_points_collection, sc.name, aspp)# 增加图像级特征,即全局平均池化with tf.variable_scope('img_pool', [net]) as sc:"""Image PoolingSee ParseNet: Looking Wider to See Better"""pooled = tf.reduce_mean(net, [1, 2], name='avg_pool', keep_dims=True)pooled = slim.utils.collect_named_outputs(end_points_collection, sc.name, pooled)pooled = slim.conv2d(pooled, 256, [1,1], stride=1, scope='1x1conv')pooled = slim.utils.collect_named_outputs(end_points_collection, sc.name, pooled)pooled = tf.image.resize_bilinear(pooled, tf.shape(net)[1:3])pooled = slim.utils.collect_named_outputs(end_points_collection, sc.name, pooled)# 将图像级特征融合到aspp中with tf.variable_scope('fusion', [aspp, pooled]) as sc:net = tf.concat([aspp, pooled], 3)net = slim.utils.collect_named_outputs(end_points_collection, sc.name, net)net = slim.conv2d(net, 256, [1,1], stride=1, scope='1x1conv')net = slim.utils.collect_named_outputs(end_points_collection, sc.name, net)# 如果不使用aspp, 则使用带mutli-grid的残差结构else:with tf.variable_scope('block5', [net]) as sc:base_depth = 512for i in range(3):with tf.variable_scope('unit_%d' % (i + 1), values=[net]):net = bottleneck_hdc(net, depth=base_depth * 4, depth_bottleneck=base_depth, stride=1, rate=4)net = slim.utils.collect_named_outputs(end_points_collection, sc.name, net)with tf.variable_scope('block6', [net]) as sc:base_depth = 512for i in range(3):with tf.variable_scope('unit_%d' % (i + 1), values=[net]):net = bottleneck_hdc(net, depth=base_depth * 4, depth_bottleneck=base_depth, stride=1, rate=8)net = slim.utils.collect_named_outputs(end_points_collection, sc.name, net)with tf.variable_scope('block7', [net]) as sc:base_depth = 512for i in range(3):with tf.variable_scope('unit_%d' % (i + 1), values=[net]):net = bottleneck_hdc(net, depth=base_depth * 4, depth_bottleneck=base_depth, stride=1, rate=16)net = slim.utils.collect_named_outputs(end_points_collection, sc.name, net)# 输出with tf.variable_scope('logits',[net]) as sc:net = slim.conv2d(net, num_classes, [1,1], stride=1, activation_fn=None, normalizer_fn=None)net = slim.utils.collect_named_outputs(end_points_collection, sc.name, net)end_points = slim.utils.convert_collection_to_dict(end_points_collection)return net, end_pointsif __name__ == "__main__":x = tf.placeholder(tf.float32, [None, 512, 512, 3])net, end_points = deeplabv3(x, 21)for i in end_points:print(i, end_points[i])

代码本身还是很容易理解的~


到这里整个DeepLabv3就算结束了~

Semantic Segmentation -- (DeepLabv3)Rethinking Atrous Convolution for Semantic Image Segmentation论文解相关推荐

  1. 【语义分割】ASPP:Rethinking Atrous Convolution for Semantic Image Segmentation

    文章目录 一.主要思想 二.实现 三.代码 一.主要思想 为了提高对不同尺度目标的语义分割,作者串联或并联使用不同扩张率的空洞卷积来实现对多尺度上下文的语义信息捕捉. Atrous Spatial P ...

  2. DeepLabv3:《Rethinking Atrous Convolution for Semantic Image Segmentataion》

    论文地址:https://arxiv.org/abs/1706.05587 Abstract   在这篇文章中,我们重温了atrous convolution(带孔卷积),它可以很好的调整过滤器的感受 ...

  3. 论文阅读:Rethinking Atrous Convolution for Semantic Image Segmentation

    论文地址:https://arxiv.org/pdf/1706.05587.pdf 发表时间:2017 注:2018年提出了deeplab3+,论文详细解读可以参考 https://blog.csdn ...

  4. 语义分割 DeepLabv3--Rethinking Atrous Convolution for Semantic Image Segmentation

    Rethinking Atrous Convolution for Semantic Image Segmentation https://arxiv.org/abs/1706.05587v1 代码还 ...

  5. 论文笔记-Understanding Convolution for Semantic Segmentation

    图森和CMU的合作工作. 论文链接[https://arxiv.org/abs/1702.08502](https://arxiv.org/abs/1702.08502) 主要提出DUC(dense ...

  6. 总结-空洞卷积(Dilated/Atrous Convolution)、gridding问题以及解决方案、训练技巧BN和PReLU、CReLU

    这里有个可视化.直观的图:https://github.com/vdumoulin/conv_arithmetic https://zhuanlan.zhihu.com/p/50369448 http ...

  7. ICCV2019-SSF-DAN: Separated Semantic Feature based Domain Adaptation Network for Semantic Segmentati

    SSF-DAN: Separated Semantic Feature based Domain Adaptation Network for Semantic Segmentation 基于分离语义 ...

  8. 论文阅读:Enconder-Decoder with Atrous Separabel Convolution for Semantic Image Segmentation(deeplabv3+)

    语义分割系列论文-Enconder-Decoder with Atrous Separabel Convolution for Semantic Image Segmentation(deeplabv ...

  9. FastFCN: Rethinking Dilated Convolution in the Backbone for Semantic Segmentation

    FastFCN: Rethinking Dilated Convolution in the Backbone for Semantic Segmentation (CVPR2019) 1.摘要 目前 ...

最新文章

  1. 结对编程2——单元测试
  2. Bootstrap全局css样式_按钮
  3. 02.uri-search
  4. unix修改服务器时间格式,处理unix中的日期格式
  5. this指向、数据双向流、传递参数、JSX中循环、React中样式、路由、引入资源的其它方式、create-react-app脚手架、事件处理、获取数据、UI框架推荐、pc桌面应用electronjs
  6. 周围剃光头顶留长发型_?22岁亿万富翁凯莉登杂志,顶着5斤“鸟窝头”凹造型,绝代艳后...
  7. python numpy数组切片_python中numpy数组切片实验解释
  8. 如何把Spring Boot 项目变成一个XML配置的Spring项目
  9. GB35114—②、公共安全视频监控联网信息安全系统互联结构
  10. 建筑企业数字化转型关键问题及解决方案
  11. 如何测试电脑网速情况
  12. 级数 p级数 q级数 调和级数为什么叫做“调和”级数 等比数列求和公式和等差数列求和 斜率:
  13. 0x8007000e错误
  14. 100 位测试工程师的 2018 年度关键词与 2019 展望
  15. 做数据分析很重要的思维模式!!!
  16. QQ空间点赞Jquery代码
  17. keras构建卷积神经网络
  18. 做SEO需要学习什么?做好SEO必备的七大技能
  19. 1251:丛林中的路
  20. Linux 10GE端口,ES5D001VST00 2端口 万兆10GE SFP 华为 S5700交换机 光接口板

热门文章

  1. 远程视频监控组网方案:4G工业设备实现林区中心端汇聚!
  2. 基于视频的车辆识别技术
  3. 苹果手机话筒声音小怎么办_手机的声音小怎么办
  4. html如何画出抽奖的转盘,css 如何“画”一个抽奖转盘
  5. 《软件功能测试自动化实战教程》—第6章6.7节文件数据源的数据驱测试
  6. Windows7系统删除文件的相关问题【mfxp.com】
  7. 计算机学院青年教师讲课,计算机学院第六届青年教师讲课比赛成功举行
  8. webpack的作用
  9. 网狐、6878游戏清除卡线用户
  10. socket读写返回值的处理