全卷积网络FCN
全卷积网络FCN
fcn是深度学习用于图像分割的鼻祖.后续的很多网络结构都是在此基础上演进而来.
图像分割即像素级别的分类.
语义分割的基本框架:
前端fcn(以及在此基础上的segnet,deconvnet,deeplab等) + 后端crf/mrf
FCN是分割网络的鼻祖,后面的很多网络都是在此基础上提出的.
论文地址
和传统的分类网络相比,就是将传统分类网络的全连接层用反卷积层替代.得到一个和图像大小一致的feature map。本篇文章用的网络是VGG.

主要关注两点
- 全连接层替换成卷积层.用反卷积的方式完成上采样
- 不同layer的输出要做相加.用以增强feature map的表达能力.
反卷积(deconvolutional)
关于反卷积(也叫转置卷积)的详细推导,可以参考:<https://blog.csdn.net/LoseInVain/article/details/81098502>
简单滴说就是:卷积的反向操作.以4x4矩阵A为例,卷积核C(3x3,stride=1),通过卷积操作得到一个2x2的矩阵B. 转置卷积即已知B,要得到A,我们要找到卷积核C,使得B相当于A通过C做正向卷积,得到B.
转置卷积是一种上采样的方法.

跳连(skip layer)
如果只用特征提取部分(也就是VGG全连接层之前的部分)得到的feature map做上采样将feature map还原到图像输入的size的话,feature不够精确.所以采用不同layer的feature map做上采样再组合起来.

代码解析
源码:https://github.com/pochih/FCN-pytorch
其中的核心代码如下:
class FCNs(nn.Module):
def __init__(self, pretrained_net, n_class):
super().__init__()
self.n_class = n_class
self.pretrained_net = pretrained_net
self.relu = nn.ReLU(inplace=True)
self.deconv1 = nn.ConvTranspose2d(512, 512, kernel_size=3, stride=2, padding=1, dilation=1, output_padding=1)
self.bn1 = nn.BatchNorm2d(512)
self.deconv2 = nn.ConvTranspose2d(512, 256, kernel_size=3, stride=2, padding=1, dilation=1, output_padding=1)
self.bn2 = nn.BatchNorm2d(256)
self.deconv3 = nn.ConvTranspose2d(256, 128, kernel_size=3, stride=2, padding=1, dilation=1, output_padding=1)
self.bn3 = nn.BatchNorm2d(128)
self.deconv4 = nn.ConvTranspose2d(128, 64, kernel_size=3, stride=2, padding=1, dilation=1, output_padding=1)
self.bn4 = nn.BatchNorm2d(64)
self.deconv5 = nn.ConvTranspose2d(64, 32, kernel_size=3, stride=2, padding=1, dilation=1, output_padding=1)
self.bn5 = nn.BatchNorm2d(32)
self.classifier = nn.Conv2d(32, n_class, kernel_size=1)
def forward(self, x):
output = self.pretrained_net(x)
x5 = output['x5'] # size=(N, 512, x.H/32, x.W/32)
x4 = output['x4'] # size=(N, 512, x.H/16, x.W/16)
x3 = output['x3'] # size=(N, 256, x.H/8, x.W/8)
x2 = output['x2'] # size=(N, 128, x.H/4, x.W/4)
x1 = output['x1'] # size=(N, 64, x.H/2, x.W/2)
score = self.bn1(self.relu(self.deconv1(x5))) # size=(N, 512, x.H/16, x.W/16)
score = score + x4 # element-wise add, size=(N, 512, x.H/16, x.W/16)
score = self.bn2(self.relu(self.deconv2(score))) # size=(N, 256, x.H/8, x.W/8)
score = score + x3 # element-wise add, size=(N, 256, x.H/8, x.W/8)
score = self.bn3(self.relu(self.deconv3(score))) # size=(N, 128, x.H/4, x.W/4)
score = score + x2 # element-wise add, size=(N, 128, x.H/4, x.W/4)
score = self.bn4(self.relu(self.deconv4(score))) # size=(N, 64, x.H/2, x.W/2)
score = score + x1 # element-wise add, size=(N, 64, x.H/2, x.W/2)
score = self.bn5(self.relu(self.deconv5(score))) # size=(N, 32, x.H, x.W)
score = self.classifier(score) # size=(N, n_class, x.H/1, x.W/1)
return score # size=(N, n_class, x.H/1, x.W/1)
train.py中
vgg_model = VGGNet(requires_grad=True, remove_fc=True)
fcn_model = FCNs(pretrained_net=vgg_model, n_class=n_class)
这里我们重点看FCN的forward函数
def forward(self, x):
output = self.pretrained_net(x)
x5 = output['x5'] # size=(N, 512, x.H/32, x.W/32)
x4 = output['x4'] # size=(N, 512, x.H/16, x.W/16)
x3 = output['x3'] # size=(N, 256, x.H/8, x.W/8)
x2 = output['x2'] # size=(N, 128, x.H/4, x.W/4)
x1 = output['x1'] # size=(N, 64, x.H/2, x.W/2)
score = self.bn1(self.relu(self.deconv1(x5))) # size=(N, 512, x.H/16, x.W/16)
score = score + x4 # element-wise add, size=(N, 512, x.H/16, x.W/16)
score = self.bn2(self.relu(self.deconv2(score))) # size=(N, 256, x.H/8, x.W/8)
score = score + x3 # element-wise add, size=(N, 256, x.H/8, x.W/8)
score = self.bn3(self.relu(self.deconv3(score))) # size=(N, 128, x.H/4, x.W/4)
score = score + x2 # element-wise add, size=(N, 128, x.H/4, x.W/4)
score = self.bn4(self.relu(self.deconv4(score))) # size=(N, 64, x.H/2, x.W/2)
score = score + x1 # element-wise add, size=(N, 64, x.H/2, x.W/2)
score = self.bn5(self.relu(self.deconv5(score))) # size=(N, 32, x.H, x.W)
score = self.classifier(score) # size=(N, n_class, x.H/1, x.W/1)
return score # size=(N, n_class, x.H/1, x.W/1)
可见FCN的输入为(batch_size,c,h,w),输出为(batch_size,class,h,w).
首先是经过vgg的特征提取层,可以得到feature map. 5个max_pool后的feature map的size分别为
x5 = output['x5'] # size=(N, 512, x.H/32, x.W/32)
x4 = output['x4'] # size=(N, 512, x.H/16, x.W/16)
x3 = output['x3'] # size=(N, 256, x.H/8, x.W/8)
x2 = output['x2'] # size=(N, 128, x.H/4, x.W/4)
x1 = output['x1'] # size=(N, 64, x.H/2, x.W/2)
之后每一个pool layer的feature map都经过一次2倍上采样,并与前一个pool layer的输出进行element-wise add.(resnet也有类似操作).从而使得上采样后的feature map信息更充分更精准,模型的鲁棒性会更好.
例如以输入图片尺寸为224x224为例,pool4的输出为(,512,14,14),pool5的输出为(,512,7,7),反卷积后得到(,512,14,14),再与pool4的输出做element-wise add。得到的仍然是(,512,14,14). 对这个输出做上采样得到(,256,28,28)再与pool3的输出相加. 依次类推,最终得到(,64,112,112).
此后,再做一次反卷积上采样得到(,32,224,224),之后卷积得到(,n_class,224,224)。即得到n_class张224x224的feature map。
下图显示了随着上采样的进行,得到的feature map细节越来越丰富.

损失函数
criterion = nn.BCEWithLogitsLoss()
损失函数采用二分类交叉熵.torch中有2个计算二分类交叉熵的函数
- BCELoss()
- BCEWithLogitsLoss()
后者只是在前者的基础上,对输入先做一个sigmoid将输入转换到0-1之间.即BCEWithLogitsLoss = Sigmoid + BCELoss

一个具体的例子可以参考:https://blog.csdn.net/qq_22210253/article/details/85222093
全卷积网络FCN的更多相关文章
- 全卷积网络 FCN 详解
背景 CNN能够对图片进行分类,可是怎么样才能识别图片中特定部分的物体,在2015年之前还是一个世界难题.神经网络大神Jonathan Long发表了<Fully Convolutional N ...
- 全卷积网络FCN详解
http://www.cnblogs.com/gujianhan/p/6030639.html CNN能够对图片进行分类,可是怎么样才能识别图片中特定部分的物体? (图像语义分割) FCN(Fully ...
- 语义分割--全卷积网络FCN详解
语义分割--全卷积网络FCN详解 1.FCN概述 CNN做图像分类甚至做目标检测的效果已经被证明并广泛应用,图像语义分割本质上也可以认为是稠密的目标识别(需要预测每个像素点的类别). 传统的基于C ...
- 全卷积网络(FCN)与图像分割
最近在做物体检测,也用到了全卷积网络,来此学习一波. 这篇文章写了很好,有利于入门,在此记录一下: http://blog.csdn.net/taigw/article/details/5140144 ...
- 全卷积网络(FCN)实战:使用FCN实现语义分割
摘要:FCN对图像进行像素级的分类,从而解决了语义级别的图像分割问题. 本文分享自华为云社区<全卷积网络(FCN)实战:使用FCN实现语义分割>,作者: AI浩. FCN对图像进行像素级的 ...
- 全卷积网络Fully Convolutional Networks (FCN)实战
全卷积网络Fully Convolutional Networks (FCN)实战 使用图像中的每个像素进行类别预测的语义分割.全卷积网络(FCN)使用卷积神经网络将图像像素转换为像素类别.与之前介绍 ...
- R-FCN:基于区域的全卷积网络来检测物体
http://blog.csdn.net/shadow_guo/article/details/51767036 原文标题为“R-FCN: Object Detection via Region-ba ...
- 使用Caffe完成图像目标检测 和 caffe 全卷积网络
一.[用Python学习Caffe]2. 使用Caffe完成图像目标检测 标签: pythoncaffe深度学习目标检测ssd 2017-06-22 22:08 207人阅读 评论(0) 收藏 举报 ...
- 全卷积神经网络FCN详解(附带Tensorflow详解代码实现)
一.导论 在图像语义分割领域,困扰了计算机科学家很多年的一个问题则是我们如何才能将我们感兴趣的对象和不感兴趣的对象分别分割开来呢?比如我们有一只小猫的图片,怎样才能够通过计算机自己对图像进行识别达到将 ...
随机推荐
- bootstrap 轮播craousel 采坑之(修改默认鼠标浮动轮播不停止)
首先上bootstrap 官网 https://v3.bootcss.com/javascript/#carousel 设置这个参数就可以,后面说如何采坑.见代码 html 部分 <!-- 轮播 ...
- awsl
from enum import Enum, uniquefrom math import sqrtfrom random import randint import pygame @uniquecl ...
- 1040 有几个PAT (25 分)C语言
字符串 APPAPT 中包含了两个单词 PAT,其中第一个 PAT 是第 2 位§,第 4 位(A),第 6 位(T):第二个 PAT 是第 3 位§,第 4 位(A),第 6 位(T). 现给定字符 ...
- 第一章 概述——1.TCP/IP设计遵循的两个原则
1.端到端原则(end-to-end principle) 当我们设计一个大的系统(如操作系统或协议族)时,随之而来的一个问题通常是在什么位置实现某个功能.影响TCP/IP协议族设计的一个重要原则是端 ...
- ubuntu下使用APT安装和卸载MySQL5.7
安装方式一: 向系统的软件仓库中列表中添加MySQL APT 仓库 去http://dev.mysql.com/downloads/repo/apt/.下载MySQL APT repository ...
- Spring Boot 2.X(十九):集成 mybatis-plus 高效开发
前言 之前介绍了 SpringBoot 整合 Mybatis 实现数据库的增删改查操作,分别给出了 xml 和注解两种实现 mapper 接口的方式:虽然注解方式干掉了 xml 文件,但是使用起来并不 ...
- nrm npm nvm
1.nvm: node version manager node版本管理器 可以来回切换node.js版本号,而直接使用node的.msi安装则版本比较固定,无法实现node版本的自行切换nvm安装方 ...
- IDEA中springboot的热部署
在pom.xml文件中添加依赖 <!--添加热部署--> <dependency> <groupId>org.springframework.boot</gr ...
- 机器学习-K最近邻算法
一.介绍 二.编程 练习一(K最近邻算法在单分类任务的应用): import numpy as np #导入科学计算包import matplotlib.pyplot as plt #导入画图工具fr ...
- linux DNS 服务器 配置
1:named.conf 2:正向区域配置文件 3:反向域名解析文件