Conv相关

相比之下,显得自己本科学的和研究生所需要的basic相差好多啊!不断补充中……不断细节化……

Kaiming init

Motivation

  • 好的初始化权重方法可以让网络在前向传播或者反向传播的时候,卷积的输出和前向传播的梯度比较稳定。即数值的分布是稳定的。
  • 合理的方差能使得数值不相同,但是数值又是相对稳定的。

算法

  • 前向传播中,每一层的卷积计算结果的方差为1
  • 后向传播中,每一层的传播梯度方差为1【反向传播中有两个梯度,一个是更新当前层的梯度,另一个是传播用于前面层梯度的计算】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
def kaiming_normal_(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu'):
# fan_in表示前向传播, fan_out表示反向传播;# 通过mode判断是前向传播还是反向传播, 生成不同的一个fan值.
fan = _calculate_correct_fan(tensor, mode)
# 通过判断是哪种激活函数生成一个gain值
gain = calculate_gain(nonlinearity, a)
std = gain / math.sqrt(fan) # 通过fan值和gain值进行标准差的计算
with torch.no_grad():
return tensor.normal_(0, std)
def _calculate_fan_in_and_fan_out(tensor):
dimensions = tensor.dim() # 返回的是维度
if dimensions < 2:
raise ValueError("Fan in and fan out can not be computed for tensor with fewer than 2 dimensions")
if dimensions == 2: # Linear
fan_in = tensor.size(1)
fan_out = tensor.size(0)
else:
num_input_fmaps = tensor.size(1) # 卷积的输入通道大小
num_output_fmaps = tensor.size(0) # 卷积的输出通道大小
receptive_field_size = 1
if tensor.dim() > 2:
receptive_field_size = tensor[0][0].numel() # 卷积核的大小:k*k
fan_in = num_input_fmaps * receptive_field_size # 输入通道数量*卷积核的大小. 用于前向传播
fan_out = num_output_fmaps * receptive_field_size # 输出通道数量*卷积核的大小. 用于反向传播
return fan_in, fan_out
def _calculate_correct_fan(tensor, mode):
mode = mode.lower()
valid_modes = ['fan_in', 'fan_out']
if mode not in valid_modes:
raise ValueError("Mode {} not supported, please use one of {}".format(mode, valid_modes))
fan_in, fan_out = _calculate_fan_in_and_fan_out(tensor)
return fan_in if mode == 'fan_in' else fan_out
def calculate_gain(nonlinearity, param=None):
r"""Return the recommended gain value for the given nonlinearity function.
The values are as follows:
================= ====================================================
nonlinearity gain
================= ====================================================
Linear / Identity :math:`1`
Conv{1,2,3}D :math:`1`
Sigmoid :math:`1`
Tanh :math:`\frac{5}{3}`
ReLU :math:`\sqrt{2}`
Leaky Relu :math:`\sqrt{\frac{2}{1 + \text{negative\_slope}^2}}`
================= ====================================================
Args:
nonlinearity: the non-linear function (`nn.functional` name)
param: optional parameter for the non-linear function
Examples:
>>> gain = nn.init.calculate_gain('leaky_relu', 0.2) # leaky_relu with negative_slope=0.2
"""
linear_fns = ['linear', 'conv1d', 'conv2d', 'conv3d', 'conv_transpose1d', 'conv_transpose2d', 'conv_transpose3d']
if nonlinearity in linear_fns or nonlinearity == 'sigmoid':
return 1
elif nonlinearity == 'tanh':
return 5.0 / 3
elif nonlinearity == 'relu':
return math.sqrt(2.0)
elif nonlinearity == 'leaky_relu':
if param is None:
negative_slope = 0.01
elif not isinstance(param, bool) and isinstance(param, int) or isinstance(param, float):
# True/False are instances of int, hence check above
negative_slope = param
else:
raise ValueError("negative_slope {} not a valid number".format(param))
return math.sqrt(2.0 / (1 + negative_slope ** 2))
else:
raise ValueError("Unsupported nonlinearity {}".format(nonlinearity))

对于均匀分布,通过方差算出均匀分布的最小值和最大值

1
2
3
4
5
6
7
8
def kaiming_uniform_(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu'):
fan = _calculate_correct_fan(tensor, mode)
gain = calculate_gain(nonlinearity, a)
std = gain / math.sqrt(fan)
bound = math.sqrt(3.0) * std # Calculate uniform bounds from standard deviation
with torch.no_grad():
return tensor.uniform_(-bound, bound)
买糖果去喽