inferno.extensions.layers.convolutional介绍及使用
inferno简介
Inferno是一个库,提供了围绕PyTorch的实用程序和方便的函数/类,为深度学习和实现神经网络提供便利。关于inferno的其他模块介绍:
inferno Pytorch: inferno.extensions.layers.convolutional 介绍及使用
inferno Pytorch: inferno.io.box.cifar下载cifar10 cifar100数据集 介绍及使用
inferno Pytorch: inferno.io.transform 介绍及使用
inferno安装
pip install inferno-pytorch
调用
from inferno.extensions.layers.convolutional import (
Conv2D,
ConvSigmoid2D,
)
没有报错就安装成功了。
卷积相关介绍
这部分是卷积的基本知识,可以跳过。
卷积尺寸变换
先定义几个参数
输入图片大小 W×W
Filter大小 K×K
步长 S t r i d e Stride Stride
padding的像素数 P
于是我们可以得出:
W o u t = ( W i n − K + 2 P ) / S t r i d e + 1 W_{out} = (W_{in} − K + 2P) / Stride + 1 Wout=(Win−K+2P)/Stride+1
输出图片大小为 N×N。
以上是最基本的卷积。对于dilation不为1的情况:
使用带dilation的卷积输出尺寸计算公式
K ′ = k e r n e l + ( k e r n e l − 1 ) ⋅ ( d i l a t i o n − 1 ) K'=kernel+(kernel-1)\cdot(dilation-1) K′=kernel+(kernel−1)⋅(dilation−1)
W o u t = w i n − K ′ + 2 p a d d i n g s t r i d e + 1 W_{out}=\frac{w_{in}-K'+2padding}{stride}+1 Wout=stridewin−K′+2padding+1
卷积方式
首先我们知道卷积有 valid,same,full三种模式 (卷积的三种模式:full, same, valid),三种卷积的具体方式见下图。简单来说,full模式使得feature map变大,same模式保持原来的尺寸,valid模式使得feature map减小。
-
full mode
-
same mode
-
valid mode
inferno.extensions.layers.convolutional 主要提供了valid 和same 两种填充方式的卷积。
inferno.extensions.layers.convolutional
inferno库提供了以下实现卷积的方式:'ConvActivation',
'ConvELU2D', 'ConvELU3D',
'ConvSigmoid2D', 'ConvSigmoid3D',
'DeconvELU2D', 'DeconvELU3D',
'StridedConvELU2D', 'StridedConvELU3D',
'DilatedConvELU2D', 'DilatedConvELU3D',
'Conv2D', 'Conv3D',
'BNReLUConv2D', 'BNReLUConv3D',
'BNReLUDepthwiseConv2D',
'ConvSELU2D', 'ConvSELU3D',
'ConvReLU2D', 'ConvReLU3D',
'BNReLUDilatedConv2D', 'DilatedConv2D',
'GlobalConv2D'
ConvActivation基类源码
same的卷积方式+一个激活函数。
ConvActivation是以下其他类的基类,源码如下:
class ConvActivation(nn.Module):
"""Convolutional layer with 'SAME' padding followed by an activation."""
def __init__(self, in_channels, out_channels, kernel_size, dim, activation,
stride=1, dilation=1, groups=None, depthwise=False, bias=True,
deconv=False, initialization=None):
super(ConvActivation, self).__init__()
# Validate dim
assert_(dim in [2, 3], "`dim` must be one of [2, 3], got {}.".format(dim), ShapeError)
self.dim = dim
# Check if depthwise
if depthwise:
assert_(in_channels == out_channels,
"For depthwise convolutions, number of input channels (given: {}) "
"must equal the number of output channels (given {})."
.format(in_channels, out_channels),
ValueError)
assert_(groups is None or groups == in_channels,
"For depthwise convolutions, groups (given: {}) must "
"equal the number of channels (given: {}).".format(groups, in_channels))
groups = in_channels
else:
groups = 1 if groups is None else groups
self.depthwise = depthwise
if not deconv:
# Get padding
padding = self.get_padding(kernel_size, dilation)
self.conv = getattr(nn, 'Conv{}d'.format(self.dim))(in_channels=in_channels,
out_channels=out_channels,
kernel_size=kernel_size,
padding=padding,
stride=stride,
dilation=dilation,
groups=groups,
bias=bias)
else:
self.conv = getattr(nn, 'ConvTranspose{}d'.format(self.dim))(in_channels=in_channels,
out_channels=out_channels,
kernel_size=kernel_size,
stride=stride,
dilation=dilation,
groups=groups,
bias=bias)
if initialization is None:
pass
elif isinstance(initialization, Initializer):
self.conv.apply(initialization)
else:
raise NotImplementedError
if isinstance(activation, str):
self.activation = getattr(nn, activation)()
elif isinstance(activation, nn.Module):
self.activation = activation
elif activation is None:
self.activation = None
else:
raise NotImplementedError
def forward(self, input):
conved = self.conv(input)
if self.activation is not None:
activated = self.activation(conved)
else:
# No activation
activated = conved
return activated
def _pair_or_triplet(self, object_):
if isinstance(object_, (list, tuple)):
assert len(object_) == self.dim
return object_
else:
object_ = [object_] * self.dim
return object_
def _get_padding(self, _kernel_size, _dilation):
assert isinstance(_kernel_size, int)
assert isinstance(_dilation, int)
assert _kernel_size % 2 == 1
return ((_kernel_size - 1) // 2) * _dilation
def get_padding(self, kernel_size, dilation):
kernel_size = self._pair_or_triplet(kernel_size)
dilation = self._pair_or_triplet(dilation)
padding = [self._get_padding(_kernel_size, _dilation)
for _kernel_size, _dilation in zip(kernel_size, dilation)]
return tuple(padding)
其他一些子类的介绍
ValidConvActivation
valid的卷积方式+一个激活函数
class ValidConvActivation(ConvActivation):
"""Convolutional layer with 'VALID' padding followed by an activation."""
def _get_padding(self, _kernel_size, _dilation):
return 0
Conv2D/Conv3D
具有相同填充和正交权值初始化的二维/三维卷积层。
默认情况下,此层不应用激活函数。
class Conv2D(ConvActivation):
"""
2D convolutional layer with same padding and orthogonal weight initialization.
By default, this layer does not apply an activation function.
"""
def __init__(self, in_channels, out_channels, kernel_size, dilation=1, stride=1,
activation=None):
super(Conv2D, self).__init__(in_channels=in_channels,
out_channels=out_channels,
kernel_size=kernel_size,
dilation=dilation,
stride=stride,
dim=2,
activation=activation,
initialization=OrthogonalWeightsZeroBias())
class Conv3D(ConvActivation):
"""
3D convolutional layer with same padding and orthogonal weight initialization.
By default, this layer does not apply an activation function.
"""
def __init__(self, in_channels, out_channels, kernel_size, dilation=1, stride=1,
activation=None):
super(Conv3D, self).__init__(in_channels=in_channels,
out_channels=out_channels,
kernel_size=kernel_size,
dilation=dilation,
stride=stride,
dim=3,
activation=activation,
initialization=OrthogonalWeightsZeroBias())
ConvELU2D/ConvELU3D
具有“same”卷积、ELU和正交权值初始化的2D/3D卷积层
class ConvELU2D(ConvActivation):
"""2D Convolutional layer with 'SAME' padding, ELU and orthogonal weight initialization."""
def __init__(self, in_channels, out_channels, kernel_size):
super(ConvELU2D, self).__init__(in_channels=in_channels,
out_channels=out_channels,
kernel_size=kernel_size,
dim=2,
activation='ELU',
initialization=OrthogonalWeightsZeroBias())
class ConvELU3D(ConvActivation):
"""3D Convolutional layer with 'SAME' padding, ELU and orthogonal weight initialization."""
def __init__(self, in_channels, out_channels, kernel_size):
super(ConvELU3D, self).__init__(in_channels=in_channels,
out_channels=out_channels,
kernel_size=kernel_size,
dim=3,
activation='ELU',
initialization=OrthogonalWeightsZeroBias())
ConvSigmoid2D/ConvSigmoid3D
具有“same”填充、sigmoid和正交权值初始化的2D/3D卷积层
class ConvSigmoid2D(ConvActivation):
"""2D Convolutional layer with 'SAME' padding, Sigmoid and orthogonal weight initialization."""
def __init__(self, in_channels, out_channels, kernel_size):
super(ConvSigmoid2D, self).__init__(in_channels=in_channels,
out_channels=out_channels,
kernel_size=kernel_size,
dim=2,
activation='Sigmoid',
initialization=OrthogonalWeightsZeroBias())
class ConvSigmoid3D(ConvActivation):
"""3D Convolutional layer with 'SAME' padding, Sigmoid and orthogonal weight initialization."""
def __init__(self, in_channels, out_channels, kernel_size):
super(ConvSigmoid3D, self).__init__(in_channels=in_channels,
out_channels=out_channels,
kernel_size=kernel_size,
dim=3,
activation='Sigmoid',
initialization=OrthogonalWeightsZeroBias())
上面提到的其他子类就不一一介绍了,注意输入参数,初始化方式和激活函数就可以了。
使用示例(可以直接运行)
需要注意inferno封装的类基本都带有初始化方式,例如orthogonal weight initialization,Kaiming normal weight initialization等等,而nn.Conv2d的权值初始化需要你手动设置。因此,在编写神经网络的时候使用inferno比直接使用Pytorch可能更为方便一点点。
但是我个人觉得其实也没方便多少,例如,当你要写一个bias=False
的卷积核的时候,它的Conv2D
并没有接受这个参数,而基类ConvActivation
默认是True
(泪目)。
- 使用same卷积编写一个简单的三层网络如下:
import torch
import torch.nn as nn
from inferno.extensions.initializers import KaimingNormalWeightsZeroBias
from inferno.extensions.layers.convolutional import (
Conv2D,
ConvSigmoid2D,
ValidConvReLU2D,
)
class Net_by_inferno(nn.Module):
def __init__(self):
super(Net_by_inferno,self).__init__()
ReLU = nn.ReLU(inplace=True)
self.conv1 = Conv2D(in_channels=1, out_channels=32, kernel_size=3, dilation=1, stride=1, activation=ReLU) # 可以自己设置激活函数
self.conv2 = Conv2D(in_channels=32, out_channels=32, kernel_size=3, dilation=1, stride=1, activation=None) # 默认无激活函数
self.conv3 = ConvSigmoid2D(in_channels=32, out_channels=1, kernel_size=3) # 默认激活函数sigmoid
def forward(self,x):
x = self.conv1(x)
x = self.conv2(x)
x = self.conv3(x)
print(x.shape) # (batchsize,1,100,100)
# from torchsummary import summary
net = Net_by_inferno()
print(net)
# summary(net.cuda(),(1,100,100))
x = torch.rand(4,1,100,100)
x = torch.autograd.Variable(x)
y = net(x)
三层网络如下图所示:
- 使用valid卷积,减小feature map的尺寸
class Net_by_inferno_valid(nn.Module):
def __init__(self):
super(Net_by_inferno_valid,self).__init__()
self.conv = ValidConvReLU2D(in_channels=1, out_channels=1, kernel_size=3) # 激活函数relu
def forward(self,x):
x = self.conv(x)
print(x.shape) # (4,1,100,100)
net = Net_by_inferno_valid()
print(net)
x = torch.rand(4,1,100,100)
x = torch.autograd.Variable(x)
y = net(x)