准备工作
import torch
import tensorflow as tf
import torch.nn.functional as F
Conv2D padding=SAME
Tensorflow中可以指定padding方式为SAME
,此时padding将自动设置为使得feature_map经过卷积后尺寸为$ \frac{n_{input}}{stride} $ ;
network = slim.conv2d(
network, 32, [3, 3], stride=1, activation_fn=nonlinearity,
padding="SAME", normalizer_fn=batch_norm_fn, scope="conv1_2",
weights_initializer=conv_weight_init, biases_initializer=conv_bias_init,
weights_regularizer=conv_regularizer)
找了一圈PyTorch社区,没有类似方便的接口可用,没办法只得自己算咯...参考的代码来源:https://zhuanlan.zhihu.com/p/36933397
from torch.nn.functional import pad
from torch.nn.modules.utils import _pair
class _ConvNd(nn.Module):
__constants__ = ['stride', 'padding', 'dilation', 'groups', 'bias',
'padding_mode', 'output_padding', 'in_channels',
'out_channels', 'kernel_size']
def __init__(self, in_channels, out_channels, kernel_size, stride,
padding, dilation, transposed, output_padding,
groups, bias, padding_mode='zeros'):
super(_ConvNd, self).__init__()
if in_channels % groups != 0:
raise ValueError('in_channels must be divisible by groups')
if out_channels % groups != 0:
raise ValueError('out_channels must be divisible by groups')
self.in_channels = in_channels
self.out_channels = out_channels
self.kernel_size = kernel_size
self.stride = stride
self.padding = padding
self.dilation = dilation
self.transposed = transposed
self.output_padding = output_padding
self.groups = groups
self.padding_mode = padding_mode
if transposed:
self.weight = nn.Parameter(torch.Tensor(
in_channels, out_channels // groups, *kernel_size))
else:
self.weight = nn.Parameter(torch.Tensor(
out_channels, in_channels // groups, *kernel_size))
if bias:
self.bias = nn.Parameter(torch.Tensor(out_channels))
else:
self.register_parameter('bias', None)
self.reset_parameters()
def reset_parameters(self):
nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5))
if self.bias is not None:
fan_in, _ = nn.init._calculate_fan_in_and_fan_out(self.weight)
bound = 1 / math.sqrt(fan_in)
nn.init.uniform_(self.bias, -bound, bound)
def extra_repr(self):
s = ('{in_channels}, {out_channels}, kernel_size={kernel_size}'
', stride={stride}')
if self.padding != (0,) * len(self.padding):
s += ', padding={padding}'
if self.dilation != (1,) * len(self.dilation):
s += ', dilation={dilation}'
if self.output_padding != (0,) * len(self.output_padding):
s += ', output_padding={output_padding}'
if self.groups != 1:
s += ', groups={groups}'
if self.bias is None:
s += ', bias=False'
if self.padding_mode != 'zeros':
s += ', padding_mode={padding_mode}'
return s.format(**self.__dict__)
def __setstate__(self, state):
super(_ConvNd, self).__setstate__(state)
if not hasattr(self, 'padding_mode'):
self.padding_mode = 'zeros'
class Conv2d_with_padding(_ConvNd):
def __init__(self, in_channels, out_channels, kernel_size, stride=1,
padding=0, dilation=1, groups=1, bias=True):
kernel_size = _pair(kernel_size)
stride = _pair(stride)
padding = _pair(padding)
dilation = _pair(dilation)
super(Conv2d_with_padding, self).__init__(
in_channels, out_channels, kernel_size, stride, padding, dilation,
False, _pair(0), groups, bias)
@staticmethod
def conv2d_same_padding(input, weight, bias=None, stride=(1, 1), padding=(1, 1), dilation=(1, 1), groups=1):
# padding not relevant, just to be compatible with conv2d
input_rows = input.size(2)
filter_rows = weight.size(2)
out_rows = (input_rows + stride[0] - 1) // stride[0]
padding_rows = max(0, (out_rows - 1) * stride[0] +
(filter_rows - 1) * dilation[0] + 1 - input_rows)
rows_odd = (padding_rows % 2 != 0)
padding_cols = max(0, (out_rows - 1) * stride[0] +
(filter_rows - 1) * dilation[0] + 1 - input_rows)
cols_odd = (padding_rows % 2 != 0)
if rows_odd or cols_odd:
input = pad(input, [0, int(cols_odd), 0, int(rows_odd)])
return F.conv2d(input, weight, bias, stride,
padding=(padding_rows // 2, padding_cols // 2),
dilation=dilation, groups=groups)
def forward(self, input):
return Conv2d_with_padding.conv2d_same_padding(input, self.weight, self.bias, self.stride,
self.padding, self.dilation, self.groups)
L2标准化(L2 Normalization)
features = tf.nn.l2_normalize(features, dim=1)
out = F.normalize(out, dim=1, p=2) # p=2 indicates an L2 Normalization
L2正则化/规范化(L2 Regularization)
Tensorflow Slim中通过weights_regularizer
可实现对网络模块的正则化;
conv_regularizer = slim.l2_regularizer(weight_decay)
network = slim.conv2d(
network, 32, [3, 3], stride=1, activation_fn=nonlinearity,
padding="SAME", normalizer_fn=batch_norm_fn, scope="conv1_1",
weights_initializer=conv_weight_init, biases_initializer=conv_bias_init,
weights_regularizer=conv_regularizer)
PyTorch的网络模块没有提供这样的接口,简单实现L2正则化需要通过优化器的weight_decay参数;
Tips: 在标准SGD上,weight_decay等同于L2正则化;而对自适应优化器(如Adam)而言,weight_decay不等同于L2正则化,详见:https://zhuanlan.zhihu.com/p/63982470, 因此可以考虑用AdamW替代Adam;