目录

一、为啥是Xception

二、Xception结构

2.1 Xception结构基本描述

2.2 实现细节

2.3 DeepLabV3+改进

三、记录pytorch采坑relu激活函数inplace=True


Xception笔记,记录一些自己认为重要的要点,以免日后遗忘。

复现Xception论文、DeepLabV+改进的Xception,代码地址https://github.com/Ascetics/Pytorch-Xception

一、为啥是Xception

Xception脱胎于Inception,Inception的思想是将卷积分成cross-channel conv和spatial conv,更准确的说是先用1x1卷积得到几个不同channel(小于输入channel)的结果,再在这些结果上分别用3x3、5x5 conv,也就是论文Figure 1描述的那样。Inception的这种算法背后,本质上是将cross-channel conv和spatial conv解耦。

考虑将Inception简化:去掉平均池化层,只用3x3 conv(2个3x3 conv相当于1个5x5 conv)。就测到了论文Figure 2描述的这种结构。

 

在Figure 2的基础上,用1个channel很大的1x1 conv 将输入映射到一个channel很大的输出上。再将这个输出“切成几段”,“切成几段”分别做3x3 spatial conv,就得到了论文中Figure 3的结构。作者在此提出一个问题,这样将cross-channel conv和spatial conv完全解耦分开合理吗?完全解耦分开,可以这样做吗?
基于Figure 3提出的假设,做一个极端的Inception模型。还是先用1个channel很大的1x1 conv 将输入映射到一个channel很大的输出上,然后“切成几段”变成“切片”,每个channel切一片。对每个channel做3x3卷积。这样极端的设计就接近于深度可分离卷积depthwise separable convolution。

为什么是“接近”,而不是“就是”呢?因为和depthwise separable convolution的操作顺序、操作内容不一样。

  1. 顺序上,depthwise separable convolution,先用3x3 conv进行spatial conv,后用1x1 conv进行cross-channel conv;极端版本Inception先用1x1 conv再用3x3 conv;
  2. 内容上,depthwise separable convolution,spatial conv和cross-channel conv之间没有非线性(ReLU激活函数);极端版本Inception,卷积之间有非线性(ReLU激活函数);

作者认为第一个区别是不重要的,特别是因为这些操作要在堆叠(深度学习)的环境中使用。第二个区别重要,作者研究了一下,结论见论文Figure 10。本文后面会解释。

要看懂Xception,需要了解VGG、Inception、Depthwise Separable Convlution和ResNet,都会用到。

二、Xception结构

2.1 Xception结构基本描述

卷积神经网络特征提取中的卷积都可以完全解耦,变成深度可分离卷积(Xception也就是Extreme Inception的意思)。接收了这一设定,Xception结构被解释为论文Figure 5的样子。

Xception的特征提取基础由36个conv layer构成。这36个conv layer被组织成14个module,除了第一个和最后一个module,其余的module都带有residual connection(残差,参看何凯明大神的ResNet)。简言之,Xception结构就是连续使用depthwise separable convolution layer和residual connection。

2.2 实现细节

如Figure 5 描述所述。

输入先经过Entry flow,不重复;再经过Middle flow,Middle flow重复8次;最后经过Exit flow,不重复。

所有的Conv 和 Separable Conv后面都加BN层,但是Figure 5没有画出来。

所有的Separable Conv都用depth=1,也就是每个depth-wise都是“切片”的。

注意, depthwise separable convolution在spatial conv和cross-channel conv之间不要加ReLU激活函数,任何激活函数都不要加。论文Figure 10展示了,这里不加激活函数效果最好,加ReLU、ELU都不好。

还有一些是论文中没有明说的细节。

Residual Connection在1x1卷积后面也加上BN。Residual Connection加上以后,不要着急做激活函数,仔细看图,激活函数ReLU是属于下一个Block的。这就导致了代码实现上采坑,下一节详细记录一下。也算长个记性。

2.3 DeepLabV3+改进

这一部分在下一篇博客,学DeepLabV3+中再记录。

三、记录pytorch采坑relu激活函数inplace=True

上面2.2写了一个细节,如果严格按照论文的示意图来实现Xception,那么每个block第一个操作不是SeparableConv,而是ReLU(红色框)。如果仅仅是第一个操作是ReLU也没有关系,但是旁边还有个Residual Connection(蓝色框)。自古红蓝出CP,于是坑来了,在反向传播的时候,报了个错,明显是因为inplace导致了某个对象被modify了,反向传播求梯度报错。(此时我所有代码的ReLU用的都是inplace=True,省内存嘛)

以Entry Flow的Block为例,先来欣赏一下错误的代码。

class _PoolEntryBlock(nn.Module):def __init__(self, in_channels, out_channels, relu1=True):"""Entry Flow的3个下采样module按论文所说,每个Conv和Separable Conv都需要跟BN论文Figure 5中,第1个Separable Conv前面没有ReLU,需要判断一下,论文Figure 5中,每个module的Separable Conv的out_channels一样,MaxPool做下采样:param in_channels: 输入channels:param out_channels: 输出channels:param relu1: 判断有没有第一个ReLU,默认是有的"""super(_PoolEntryBlock, self).__init__()self.project = ResidualConnection(in_channels, out_channels, stride=2)self.relu1 = Noneif relu1:self.relu1 = nn.ReLU(inplace=True)  self.sepconv1 = SeparableConv2d(in_channels, out_channels,kernel_size=3, padding=1, bias=False)self.bn1 = nn.BatchNorm2d(out_channels)self.relu2 = nn.ReLU(inplace=True)self.sepconv2 = SeparableConv2d(out_channels, out_channels,kernel_size=3, padding=1, bias=False)self.bn2 = nn.BatchNorm2d(out_channels)self.maxpool = nn.MaxPool2d(3, stride=2, padding=1)passdef forward(self, x):identity = self.project(x)  # residual connection 准备if self.relu1:  # 第1个Separable Conv前面没有ReLU,需要判断一下x = self.relu1(x)x = self.sepconv1(x)  # 第2个Separable Convx = self.bn1(x)x = self.relu2(x)x = self.sepconv2(x)  # 第2个Separable Convx = self.bn2(x)x = self.maxpool(x)  # 下采样2倍x = x + identity  # residual connection 相加return xpass

采坑时的做法,先算出Residual Connection,再做relu、SeparableConv。Residual Connection时,已经进行过一次卷积操作,此时要求输入x本身不能发生改变,不能再被modify。后面的ReLU(inplace=True)恰恰就modify了x。所以反向传播时报错。

改变执行的先后顺序呢?也不行。如果先ReLU(inplace=True),那么x也被modify了,再做Residual Connection时输入就不是block输入的那个x了。

解决的办法,改为ReLU(inplace=False),或者Residual Connection的输入改为x.clone(),总之不能省内存……正确的代码已经push到github上了,地址详见文章开头。

为此,我写了一个简化的模型:

  1. class Wrong就是采坑的错误实现;
  2. class RightOne就是改为ReLU(inplace=False);
  3. class RightTwo就是Residual Connection的输入改为x.clone();

一杯茶,一包烟,一个bug改一天……

import torch
import torch.nn as nnclass Wrong(nn.Module):def __init__(self):super(Wrong, self).__init__()self.convs = nn.Sequential(nn.ReLU(inplace=True),nn.Conv2d(3, 3, 3, padding=1))self.residual = nn.Conv2d(3, 3, 3, padding=1)passdef forward(self, x):r = self.residual(x)  # 卷积之后,x就不能modify了h = self.convs(x)  # relu就modify了x,反向传播时候会报错h = h + rreturn hpassclass RightOne(nn.Module):def __init__(self):super(RightOne, self).__init__()self.convs = nn.Sequential(nn.ReLU(inplace=False),  # 改法1,别省内存了nn.Conv2d(3, 3, 3, padding=1))self.residual = nn.Conv2d(3, 3, 3, padding=1)passdef forward(self, x):r = self.residual(x)h = self.convs(x)h = h + rreturn hpassclass RightTwo(nn.Module):def __init__(self):super(RightTwo, self).__init__()self.convs = nn.Sequential(nn.ReLU(inplace=True),nn.Conv2d(3, 3, 3, padding=1))self.residual = nn.Conv2d(3, 3, 3, padding=1)passdef forward(self, x):r = self.residual(x.clone())  # 改法2,clone还是消耗内存的h = self.convs(x)h = h + rreturn hpassif __name__ == '__main__':in_data = torch.randint(-2, 2, (1, 3, 2, 2), dtype=torch.float)in_label = torch.randint(0, 3, (1, 2, 2))print(in_data.shape)func = nn.CrossEntropyLoss()t = RightTwo()in_data = in_data.cuda()in_label = in_label.cuda()t.cuda()out_data = t(in_data)print(out_data.shape)loss = func(out_data, in_label)loss.backward()

CV06-Xception笔记相关推荐

  1. Xception深度可分离卷积-论文笔记

    Xception Xception: Deep Learning with Depthwise Separable Convolutions 角度:卷积的空间相关性和通道相关性 . 笔记还是手写好,都 ...

  2. Xception论文阅读笔记

    目录 原文链接: 简介: 网络结构改进: Xception架构: 结论: 原文链接: Xception: Deep Learning with Depthwise Separable Convolut ...

  3. Xception论文笔记

    论文:Franc¸ois Chollet.Xception: Deep Learning with Depthwise Separable Convolutions 引言 本文将Inception模块 ...

  4. 深度学习网络结构笔记----Depthwise卷积与Pointwise卷积--深度可分卷积-- GoogleNet,Xception,MobileNetv1--v3

    目录 1,常规卷积操作 1,什么是Depthwise Convolution 3,什么是Pointwise Convolution 4 ,参数对比 5,Depthwise Separable Conv ...

  5. CV07-DeepLab v3+笔记

    目录 一.Dilated Convolution 膨胀卷积 二.ASPP与Encoder & Decoder 三.深度可分离卷积 3.1 深度可分离卷积原理 3.2 深度可分离卷积减小参数量和 ...

  6. 《联邦学习实战》杨强 读书笔记十七——联邦学习加速方法

    目录 同步参数更新的加速方法 增加通信间隔 减少传输内容 非对称的推送和获取 计算和传输重叠 异步参数更新的加速方法 基于模型集成的加速方法 One-Shot联邦学习 基于有监督的集成学习方法 基于半 ...

  7. Tensorflow深度学习学习笔记

    Tensorflow学习笔记 一.Tensorflow基础及深度学习原理 1.Tensorflow中网络搭建的三种方法 1.keras.models.Sequential() 2.keras.mode ...

  8. 虚拟试穿VTNFP论文笔记

    VTNFP虚拟试穿笔记 文章目录 VTNFP虚拟试穿笔记 任务 相关工作: VTNFP Person Representation(人体表示) Clothing Deformation Module ...

  9. 学习笔记:深度学习(3)——卷积神经网络(CNN)理论篇

    学习时间:2022.04.10~2022.04.12 文章目录 3. 卷积神经网络CNN 3.1 卷积神经网络的概念 3.1.1 什么是CNN? 3.1.2 为什么要用CNN? 3.1.3 人类的视觉 ...

最新文章

  1. STM32开发 -- 启动流程
  2. 最后一条记录_幸好朋友圈没有访客记录。
  3. 基于ARM Cortex-M和Eclipse的SWO单总线输出
  4. Golang笔记——单元测试
  5. webpack源码分析(2)---- webpack\bin\webpack.js
  6. 一部分 数据 迁移_11项最佳实践,每次数据中心迁移都必不可少
  7. 获取客户端ip_获取客户端访问真实IP
  8. 为什么回归直线过平均值点_线性回归和梯度下降的初学者教程
  9. python题库选择题刷题训练_python题库刷题训练开源到了Github
  10. python制作表情,使用Python制作滑稽表情
  11. 程序员容易发福的原因及解决办法
  12. python stack unstack_Python之数据重塑
  13. 关于药物|新药|药品市场调研报告(实操资料分享)
  14. Smart200控制两台V90伺服,绝对定位和速度控制
  15. yolo-v3代码学习
  16. jxls2-java生成/导出excel工具!基于jxls2写的jxls增强版jxlss的完整教程
  17. ABB机器人机器人电路板电压检测法
  18. java塑形是什么意思,健身中该减脂还是该塑形,所谓“塑形”是个什么概念?...
  19. html_css_flex弹性盒模型_ZHOU125disorder_
  20. 销售税面试题二之设计方案

热门文章

  1. ie8加载js太慢_js ie8 慢
  2. 为何 Map接口不继承Collection接口
  3. mysql数据去重语句_数据库 mysql 语句
  4. iOS 图片处理-利用GPUImage 磨皮和美白图片
  5. [微信小程序]动画,从顶部掉花的效果(完整代码附效果图)
  6. 【React Native】iOS原生导航跳转RN页面
  7. maven项目中 把依赖的jar包一起打包
  8. 详解使用DockerHub官方的mysql镜像生成容器
  9. C#实现php的hash_hmac函数
  10. 顶尖程序员不同于常人的 5 个区别