一、批量归一化与残差网络
1.1 批量归一化BatchNormalization
对输入的标准化(浅层模型)
- 处理后的任意一个特征在数据集中所有样本上的均值为0、标准差为1。
- 标准化处理输入数据使各个特征的分布相近
批量归一化(深度模型)
- 利用小批量上的均值和标准差,不断调整神经网络中间输出,从而使整个神经网络在各层的中间输出的数值更稳定。
位置:
- 对全连接层做批量归一化: 全连接层中的仿射变换和激活函数之间
- 对卷积层做批量归⼀化:卷积计算之后、应⽤激活函数之前
- 预测时的批量归⼀化:训练:以batch为单位,对每个batch计算均值和方差;预测:用移动平均估算整个训练数据集的样本均值和方差
代码实现
def batch_norm(is_training, X, gamma, beta, moving_mean, moving_var, eps, momentum):
# 判断当前模式是训练模式还是预测模式
if not is_training:
# 如果是在预测模式下,直接使用传入的移动平均所得的均值和方差
X_hat = (X - moving_mean) / torch.sqrt(moving_var + eps)
else:
assert len(X.shape) in (2, 4)
if len(X.shape) == 2:
# 使用全连接层的情况,计算特征维上的均值和方差
mean = X.mean(dim=0)
var = ((X - mean) ** 2).mean(dim=0)
else:
# 使用二维卷积层的情况,计算通道维上(axis=1)的均值和方差。这里我们需要保持
# X的形状以便后面可以做广播运算
mean = X.mean(dim=0, keepdim=True).mean(dim=2, keepdim=True).mean(dim=3, keepdim=True)
var = ((X - mean) ** 2).mean(dim=0, keepdim=True).mean(dim=2, keepdim=True).mean(dim=3, keepdim=True)
# 训练模式下用当前的均值和方差做标准化
X_hat = (X - mean) / torch.sqrt(var + eps)
# 更新移动平均的均值和方差
moving_mean = momentum * moving_mean + (1.0 - momentum) * mean
moving_var = momentum * moving_var + (1.0 - momentum) * var
Y = gamma * X_hat + beta # 拉伸和偏移
return Y, moving_mean, moving_var
1.2 残差网络ResNet
深度学习的问题:深度CNN网络达到一定深度后再一味地增加层数并不能带来进一步地分类性能提高,反而会招致网络收敛变得更慢,准确率也变得更差
右边:f(x)-x=0 (易于捕捉恒等映射的细微波动)
在残差块中,输⼊可通过跨层的数据线路更快地向前传播。
class Residual(nn.Module):
#可以设定输出通道数、是否使用额外的1x1卷积层来修改通道数以及卷积层的步幅。
def __init__(self, in_channels, out_channels, use_1x1conv=False, stride=1):
super(Residual, self).__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, stride=stride)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1)
if use_1x1conv:
self.conv3 = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride)
else:
self.conv3 = None
self.bn1 = nn.BatchNorm2d(out_channels)
self.bn2 = nn.BatchNorm2d(out_channels)
def forward(self, X):
Y = F.relu(self.bn1(self.conv1(X)))
Y = self.bn2(self.conv2(Y))
if self.conv3:
X = self.conv3(X)
return F.relu(Y + X) #这里是相加操作,densenet是concat操作
ResNet= 卷积(kernel_size=7, stride=2, padding=3) + 批量归一化 + Relu + 最大池化(kernel_size=3, stride=2, padding=1) + 残差块x4 (通过步幅为2的残差块在每个模块之间减小高和宽) + 全局平均化 + 全连接
1.3 稠密网络DenseNet
主要构建模块:
- 稠密块(dense block): 定义了输入和输出是如何连结的。
- 过渡层(transition layer):用来控制通道数,使之不过大。
def conv_block(in_channels, out_channels):
blk = nn.Sequential(nn.BatchNorm2d(in_channels),
nn.ReLU(),
nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1))
return blk
class DenseBlock(nn.Module):
def __init__(self, num_convs, in_channels, out_channels):
super(DenseBlock, self).__init__()
net = []
for i in range(num_convs):
in_c = in_channels + i * out_channels
net.append(conv_block(in_c, out_channels))
self.net = nn.ModuleList(net)
self.out_channels = in_channels + num_convs * out_channels # 计算输出通道数
def forward(self, X):
for blk in self.net:
Y = blk(X)
X = torch.cat((X, Y), dim=1) # 在通道维上将输入和输出连结
return X
过渡层
- 1X1卷积层:来减小通道数
- 步幅为2的平均池化层:减半高和宽
def transition_block(in_channels, out_channels):
blk = nn.Sequential(
nn.BatchNorm2d(in_channels),
nn.ReLU(),
nn.Conv2d(in_channels, out_channels, kernel_size=1),
nn.AvgPool2d(kernel_size=2, stride=2))
return blk
DenseNet = 卷积(kernel_size=7, stride=2, padding=3) + 批量归一化 + Relu + 最大池化(kernel_size=3, stride=2, padding=1) + (稠密块+过渡)X3 + 稠密块 + 批量归一化+ Relu + 全局平均化 + 全连接
二、凸优化
优化在深度学习中的挑战¶
- 局部最小值
- 鞍点
- 梯度消失
凸性(convexity): 集合内任意两点的连线上的点都在该集合内。
凸函数与二阶导数的关系:
参考资料:《动手学pytorch》