运维开发网
广告位招商联系QQ:123077622
 
广告位招商联系QQ:123077622

ConvTranspose2d(逆卷积)的原理和计算ConvTranspose2d原理,深度网络如何进行上采样?

运维开发网 https://www.qedev.com 2021-05-09 19:54 出处:51CTO 作者:wx60962e30e0986
ConvTranspose2d(逆卷积)的原理和计算ConvTranspose2d原理,深度网络如何进行上采样?,wx60962e30e0986的博客原创的深度学习文章。

 

原理

解释什么是逆卷积,先得明白什么是卷积。

先说卷积:对于一个图片A,设定它的高度和宽度分别为Height,Width,通道数为Channels。 然后我们用卷积核(kernel * kernel)去做卷积,(这里设定卷积核为正方形,实际长方形也可以类推,相信我,不会很难),步长为stride(同样的,不区分高宽方向),做padding。卷积后得到B。

重复上面的话就是利用一个卷积操作将A变成B。

那么,在这个前提下,逆卷积就是将B变成A。

那么怎么规定卷积核这些参数呢,这些又是什么意思?

对于卷积操作,我不多说了,这里不做解释。而且我们应该比较清楚如何从输入的图片大小格式等得到新的图片大小,或许有的人熟悉用特征图来代替图片。

对于逆卷积操作,卷积核的设置就是和卷积操作相同。如:给定一个特征图x,并输入卷积核设置。我们就是想得到一个特征图y经过输入的卷积核进行卷积,然后得到特征图x,这里我们要求的就是特征图y。

写到这里大家应该比较理解我为什么把它翻译成逆卷积了吧。

如果不懂,也没事,可以看下面的例子。

举个例子:

ConvTranspose2d(逆卷积)的原理和计算ConvTranspose2d原理,深度网络如何进行上采样?

上面这个是一个卷积操作。

我们输入的特征图为:x: 44channels_in,channels_in表示通道数

卷积核设置:无padding, kernel size为3*3, 步长stride 为1,

输出的特征图为y,2 * 2 * channels_out,channels_out也是通道数。

逆卷积操作的输入就是特征图y, 卷积核设置同上。要求上面的特征图x。

这里先给出这个对应逆卷积的说明图。后面给出泛化的说明。

ConvTranspose2d(逆卷积)的原理和计算ConvTranspose2d原理,深度网络如何进行上采样?

计算公式

shape: 

输入: (N,C_in,H_in,W_in) 

输出: (N,C_out,H_out,W_out) 

H_{out}=(H_{in}-1)*stride[0]-2*padding[0]+kernel_size[0]+output_padding[0]

W_{out}=(W_{in}-1)*stride[1]-2*padding[1]+kernel_size[1]+output_padding[1]

输入特征图:3 × 3 

输入卷积核参数:kernel为3 × 3 , stride为2, padding为1,output_padding为1

新的特征图A’: (3-1)×2-2+3+1=6 。

所以输出的特征图大小:6×6

这样就实现了将特征图放大一倍。

Keras中的Conv2DTranspose详解

tf.keras.layers.Conv2DTranspose(
    filters, kernel_size, strides=(1, 1), padding='valid',
    output_padding=None, data_format=None, dilation_rate=(1, 1), activation=None,
    use_bias=True, kernel_initializer='glorot_uniform',
    bias_initializer='zeros', kernel_regularizer=None,
    bias_regularizer=None, activity_regularizer=None, kernel_constraint=None,
    bias_constraint=None, **kwargs
)

 

参数:

  • filters:整数,输出空间的维数(即卷积中的滤波器数).
  • kernel_size:一个元组或2个正整数的列表,指定过滤器的空间维度;可以是单个整数,以指定所有空间维度的相同值.
  • strides:一个元组或2个正整数的列表,指定卷积的步幅;可以是单个整数,以指定所有空间维度的相同值.
  • padding:可以是一个"valid"或"same"(不区分大小写).
  • output_padding:一个由2个整数组成的整数或元组/列表,指定沿输出张量的高度和宽度填充的数量。可以是单个整数,为所有空间维度指定相同的值。给定维度上的输出填充量必须低于同一维度上的步长。如果设置为None(默认),输出形状将被推断。
  • data_format:一个字符串,可以是一个channels_last(默认)或channels_first,表示输入中维度的顺序.channels_last对应于具有形状(batch, height, width, channels)的输入,而channels_first对应于具有形状(batch, channels, height, width)的输入.
  • dilation_rate:一个由2个整数组成的整数或元组/列表,指定用于膨胀卷积的膨胀率。可以是单个整数,为所有空间维度指定相同的值。目前,指定任何dilation_rate值!= 1与指定任何stride值!= 1是不兼容的。
  • activation:激活功能,将其设置为“None”以保持线性激活.
  • use_bias:Boolean,表示该层是否使用偏差.
  • kernel_initializer:卷积内核的初始化程序.
  • bias_initializer:偏置向量的初始化器,如果为None,将使用默认初始值设定项.
  • kernel_regularizer:卷积内核的可选正则化器.
  • bias_regularizer:偏置矢量的可选正则化器.
  • activity_regularizer:输出的可选正则化函数.
  • kernel_constraint:由Optimizer更新后应用于内核的可选投影函数(例如,用于实现层权重的范数约束或值约束);该函数必须将未投影的变量作为输入,并且必须返回投影变量(必须具有相同的形状);在进行异步分布式培训时,使用约束是不安全的.
  • bias_constraint:由Optimizer更新后应用于偏差的可选投影函数.

实例

def get_model():

    inputs = Input(shape=(64, 64, 3))

    conv_1 = Conv2D(1, (3, 3), strides=(1, 1), padding='same')(inputs)
    act_1 = Activation('relu')(conv_1)

    conv_2 = Conv2D(64, (3, 3), strides=(1, 1), padding='same')(act_1)
    act_2 = Activation('relu')(conv_2)

    deconv_1 = Conv2DTranspose(64, (3, 3), strides=(1, 1), padding='same')(act_2)
    act_3 = Activation('relu')(deconv_1)

    merge_1 = concatenate([act_3, act_1], axis=3)

    deconv_2 = Conv2DTranspose(1, (3, 3), strides=(1, 1), padding='same')(merge_1)
    act_4 = Activation('relu')(deconv_2)

    model = Model(inputs=[inputs], outputs=[act_4])

    model.compile(optimizer='adadelta', loss=dice_coef_loss, metrics=[dice_coef])

    return model

 

pytorch中的ConvTranspose2d参数详解

class torch.nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True)

说明

stride: 控制相关系数的计算步长 

dilation: 用于控制内核点之间的距离,详细描述在这里 

groups: 控制输入和输出之间的连接: group=1,输出是所有的输入的卷积;group=2,此时相当于有并排的两个卷积层,每个卷积层计算输入通道的一半,并且产生的输出是输出通道的一半,随后将这两个输出连接起来。

参数kernel_size,stride,padding,dilation数据类型: 可以是一个int类型的数据,此时卷积height和width值相同; 也可以是一个tuple数组(包含来两个int类型的数据),第一个int数据表示height的数值,第二个int类型的数据表示width的数值。

注意

由于内核的大小,输入的最后的一些列的数据可能会丢失。因为输入和输出是不是完全的互相关。因此,用户可以进行适当的填充(padding操作)。

参数:

  • in_channels(int) – 输入信号的通道数
  • out_channels(int) – 卷积产生的通道数
  • kerner_size(int or tuple) - 卷积核的大小
  • stride(int or tuple,optional) - 卷积步长
  • padding(int or tuple, optional) - 输入的每一条边补充0的层数
  • output_padding(int or tuple, optional) - 输出的每一条边补充0的层数
  • dilation(int or tuple, optional) – 卷积核元素之间的间距
  • groups(int, optional) – 从输入通道到输出通道的阻塞连接数
  • bias(bool, optional) - 如果bias=True,添加偏置

 

 实例

ConvTranspose2d在UNet的应用:

import torch.nn as nn
import torch
from torch import autograd

#把常用的2个卷积操作简单封装下
class DoubleConv(nn.Module):
    def __init__(self, in_ch, out_ch):
        super(DoubleConv, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(in_ch, out_ch, 3, padding=1),
            nn.BatchNorm2d(out_ch), #添加了BN层
            nn.ReLU(inplace=True),
            nn.Conv2d(out_ch, out_ch, 3, padding=1),
            nn.BatchNorm2d(out_ch),
            nn.ReLU(inplace=True)
        )

    def forward(self, input):
        return self.conv(input)

class Unet(nn.Module):
    def __init__(self, in_ch, out_ch):
        super(Unet, self).__init__()
        self.conv1 = DoubleConv(in_ch, 64)
        self.pool1 = nn.MaxPool2d(2)
        self.conv2 = DoubleConv(64, 128)
        self.pool2 = nn.MaxPool2d(2)
        self.conv3 = DoubleConv(128, 256)
        self.pool3 = nn.MaxPool2d(2)
        self.conv4 = DoubleConv(256, 512)
        self.pool4 = nn.MaxPool2d(2)
        self.conv5 = DoubleConv(512, 1024)
        # 逆卷积,也可以使用上采样(保证k=stride,stride即上采样倍数)
        self.up6 = nn.ConvTranspose2d(1024, 512, 2, stride=2)
        self.conv6 = DoubleConv(1024, 512)
        self.up7 = nn.ConvTranspose2d(512, 256, 2, stride=2)
        self.conv7 = DoubleConv(512, 256)
        self.up8 = nn.ConvTranspose2d(256, 128, 2, stride=2)
        self.conv8 = DoubleConv(256, 128)
        self.up9 = nn.ConvTranspose2d(128, 64, 2, stride=2)
        self.conv9 = DoubleConv(128, 64)
        self.conv10 = nn.Conv2d(64, out_ch, 1)

    def forward(self, x):
        c1 = self.conv1(x)
        p1 = self.pool1(c1)
        c2 = self.conv2(p1)
        p2 = self.pool2(c2)
        c3 = self.conv3(p2)
        p3 = self.pool3(c3)
        c4 = self.conv4(p3)
        p4 = self.pool4(c4)
        c5 = self.conv5(p4)
        up_6 = self.up6(c5)
        merge6 = torch.cat([up_6, c4], dim=1)
        c6 = self.conv6(merge6)
        up_7 = self.up7(c6)
        merge7 = torch.cat([up_7, c3], dim=1)
        c7 = self.conv7(merge7)
        up_8 = self.up8(c7)
        merge8 = torch.cat([up_8, c2], dim=1)
        c8 = self.conv8(merge8)
        up_9 = self.up9(c8)
        merge9 = torch.cat([up_9, c1], dim=1)
        c9 = self.conv9(merge9)
        c10 = self.conv10(c9)
        out = nn.Sigmoid()(c10)
        return out

缺点

在基于CNN的超分辨率中,经常在最后一层使用stride>1的deconv layer,而这会造成棋盘格噪声。如下图所示

ConvTranspose2d(逆卷积)的原理和计算ConvTranspose2d原理,深度网络如何进行上采样?

 

具体产生原因

上面的黑格子是表示原始图像中的某一个像素点,白色的表示转置卷积中的stride,一般是用0去填充。下面一层就是deconv生成的图像。可以看到stride不能整除size的时候,就会出现棋盘格效应(当然,就算整除也不能完全消除)。

 

ConvTranspose2d(逆卷积)的原理和计算ConvTranspose2d原理,深度网络如何进行上采样?

ConvTranspose2d(逆卷积)的原理和计算ConvTranspose2d原理,深度网络如何进行上采样?

 

 如何避免呢?

采用一般的插值算法(NN或bilinear)先把图像放大到目标分辨率,再用普通的conv去做计算,替代deconv layer。

 

最后效果

ConvTranspose2d(逆卷积)的原理和计算ConvTranspose2d原理,深度网络如何进行上采样?

扫码领视频副本.gif

0

精彩评论

暂无评论...
验证码 换一张
取 消