Concurrent Spatial and Channel ‘Squeeze & Excitation’ in Fully Convolutional Networks(附代码)

摘要

 全卷积网络为大量应用设置了图像分割的最新技术,F-CNN中的结构创新集中在空间编码或网络连接来帮助梯度流。本文,探索了一种自适应地重新校准特征图的方向,来增加有意义的特征,同时抑制弱的特征。我们从最近提出的squeeze & excitation(SE)模块获得灵感,该模块是用于图像分类特征图的通道重新校准。为此,我们介绍了用于图像分割的三种SE模块变体:

  1. 压缩空间和激励信道(cSE)
  2. 压缩信道和激励空间(sSE)
  3. 并行的空间和信道压缩和激励(scSE)

 我们有效的将这些SE模块整合到DenseNet, SD-Net, U-Net框架中,并观察性能的持续改进,同时最小化对模型复杂性的影响。对两个具有挑战性的应用进行了评估:MRI扫描上的全脑分割和全身增强CT扫描上的器官分割。

介绍

 卷积网络已经成为图像分类的标准方式,全卷机已经成为许多图像分类任务的首先工具。这些架构的基础是卷积层,通过过滤器对所有输入channel捕捉局部空间特征,并生成共同编码空间和通道信息的特征图。虽然对空间信息和通道信息的联合编码进行了大量的改进,但对空间模式和通道模式的独立编码探索却较少。最近的工作试图通过明确建模特征映射通道间的相互依赖性来解决上述问题,介绍了一种称为SE的模块,它可以无缝集成到任何一个CNN模型中。
 SE模块通过全局平均池排除空间依赖性,来学习特定于通道的描述符,该描述符用于重新校准功能图,强调有用的通道。SE沿空间域挤压,并沿通道激励或重新加权。一个带有SE块的卷积网络在ILSVRC 2017年图像网数据集分类竞争中获得第一名,表明其有效性。
在这项工作中,我们希望利用SE块的高性能对F-CNN图像进行分类。我们将前面介绍的SE块称为信道SE(CSE),因为它只激励信道方向,这对分类是有效的。我们假设,对于图像分割,像素空间信息更具信息性。因此,我们引入另一个SE块,它沿着通道“挤压”并在空间上“激发”,称为空间SE(SSE)。最后,我们提出了并行空间和信道SE块(SCSE),分别沿信道和空间重新校准特征映射,然后结合输出。激发特征图在空间和渠道上的更多信息。

方法

如图1(a)所示,通过将SE块放在每个编码器(池化层)和解码器块(上采样层)之后,可以无缝集成到任何F-CNN模型中。
在这里插入图片描述

空间压缩和信道激励(cSE)

压缩空间通过全局平均池化层,产生一个z向量(1x1xC),然后通过一个全连接层和激活层(RELU),再通过一个全连接层。这对通道依赖项进行了编码。
在这里插入图片描述
然后通过sigmoid层获取激活值使其介于[0,1],并用于激励或重新校准通道内的值。激活值可以表示重要的通道并忽略不重要的通道。
在这里插入图片描述

def cse_block(prevlayer, prefix):
    mean = Lambda(lambda xin: K.mean(xin, axis=[1, 2]))(prevlayer) # H W 求均值
    # K.int_shape() Returns the shape of tensor or variable as a tuple of int or None entries
    lin1 = Dense(K.int_shape(prevlayer)[3] // 2, name=prefix + 'cse_lin1', activation='relu')(mean)
    lin2 = Dense(K.int_shape(prevlayer)[3], name=prefix + 'cse_lin2', activation='sigmoid')(lin1)
    x = Multiply()([prevlayer, lin2])
    return x

通道压缩和空间激励

该SE模块在通道挤压和空间激励,对于细粒度图像分割具有重要意义。
操作,文中将输入看做U,每个 u i , j u^{i,j} ui,j维度为(1,1,C),然后通过一个卷积 q = W s q ∗ U , W s q 属 于 ( 1 , 1 , C , 1 ) , q 属 于 ( H , W ) q=W_{sq}*U,W_{sq}属于(1,1,C,1),q属于(H,W) q=WsqU,Wsq(1,1,C,1),q(H,W),这里生成的结果q可以理解,而生成q的方式应该是将卷积作为一种全连接的方式。
在这里插入图片描述
将获取的q通过sigmoid激活,生成的[0,1],参与每个位置的计算。
在这里插入图片描述
关于这个操作我是有疑问的,所以将原文贴到这里,也希望看懂的可以评论解惑。
在这里插入图片描述
附上找到的代码,同样有疑惑,但代码的参数个数与论文一致,结果的维度并不一致。

def sse_block(prevlayer, prefix):
    # Bug? Should be 1 here?
    conv = Conv2D(K.int_shape(prevlayer)[3], (1, 1), padding="same", kernel_initializer="he_normal",
                  activation='sigmoid', strides=(1, 1),
                  name=prefix + "_conv")(prevlayer)
    conv = Multiply(name=prefix + "_mul")([prevlayer, conv])
    return conv

最后附上自己理解的

def sse_block(prevlayer, prefix):
    # Bug? Should be 1 here?
    conv = Conv2D(1, (1, 1), padding="valid", kernel_initializer="he_normal",
                  activation='sigmoid', strides=(1, 1),
                  name=prefix + "_conv")(prevlayer)
    conv = Multiply(name=prefix + "_mul")([prevlayer, conv])
    return conv

q的每一个激活值与所给特征地图的空间信息相关,这种再校准提供了更多的权重到相关的空间信息,忽略了不相关的空间信息

空间和通道的压缩和激励模块(scSE)

scSE模块则是将上述两个模块相加。
在这里插入图片描述
代码如下

def csse_block(x, prefix):
    cse = cse_block(x, prefix)
    sse = sse_block(x, prefix)
    x = Add(name=prefix + "_csse_mul")([cse, sse])
    return x

模型复杂性

cSE模块有 C 2 C^2 C2个参数,sCE有 C C C个参数,因而一个block的参数就是 C 2 + C C^2+C C2+C个参数。以一个例子来看U-net模型有 2.1 ∗ 1 0 6 2.1*10^6 2.1106个参数,scSE模块添加了 3.3 ∗ 1 0 4 3.3*10^4 3.3104个参数,增加了约1.5%,可以看出SE模块增加了很小的模型复杂性。
最后附上论文地址

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
concurrent.futures是Python标准库中的一个模块,可以用于实现并发执行的任务。其中包括两个子模块:ThreadPoolExecutor和ProcessPoolExecutor,分别用于多线程和多进程执行任务。 以下是一个使用ThreadPoolExecutor的例子: ```python import concurrent.futures import time def task(n): print(f"Start task {n}") time.sleep(2) print(f"End task {n}") return n * n if __name__ == "__main__": with concurrent.futures.ThreadPoolExecutor() as executor: # 提交5个任务 tasks = [executor.submit(task, i) for i in range(5)] # 等待任务完成并获取结果 results = [task.result() for task in concurrent.futures.as_completed(tasks)] print(f"Results: {results}") ``` 输出: ``` Start task 0 Start task 1 Start task 2 Start task 3 Start task 4 End task 0 End task 1 End task 2 End task 3 End task 4 Results: [0, 1, 4, 9, 16] ``` 代码解释: 1. 定义了一个task函数,接受一个参数n,并打印开始和结束信息,最后返回n的平方。 2. 在主程序中,使用with语句创建一个ThreadPoolExecutor对象,该对象会自动管理线程池的生命周期。 3. 使用executor.submit方法提交5个任务,并将返回的Future对象保存到tasks列表中。 4. 使用concurrent.futures.as_completed方法等待所有任务完成,并获取每个任务的结果。该方法返回一个生成器,可以迭代得到每个任务的Future对象,当Future对象完成时,会返回对应的结果。 5. 最后打印结果。 注意事项: 1. 多线程和多进程的使用方法类似,只需要将ThreadPoolExecutor替换为ProcessPoolExecutor即可。 2. 在使用多线程和多进程时,需要注意线程/进程之间的安全问题,如共享变量的访问等。 3. 在使用多进程时,需要注意对象的序列化问题,只有可序列化的对象才能在进程之间传递。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值