马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
全毗连层存在的问题:参数过大,盘算成本过高。
一、网络中的网络(NiN)
1、NiN块
①NiN块的结构
NiN串联多个由卷积层和“全毗连”层构成的小网络来构建一个深层网络。这种由卷积层和“全毗连”层构成的小网络就是NiN块。
(1)为什么卷积层不后接真的全毗连层
卷积层的输入和输出通常是四维数组(样本,通道,高,宽),而全毗连层的输入和输出则通常是二维数组(样本,特性)。假如想在全毗连层后再接上卷积层,则必要将全毗连层的输出变更为四维。
(2)1x1卷积层
1×1卷积层可以看成全毗连层,其中空间维度(高和宽)上的每个元素相称于样本,通道相称于特性。因此,NiN使用1×1卷积层来替代全毗连层,从而使空间信息可以或许天然传递到后面的层中去。
- def nin_block(in_channels, out_channels, kernel_size, stride, padding):
- blk = nn.Sequential(nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding),
- nn.ReLU(),
- nn.Conv2d(out_channels, out_channels, kernel_size=1),
- nn.ReLU(),
- nn.Conv2d(out_channels, out_channels, kernel_size=1),
- nn.ReLU())
- return blk
复制代码 ②NiN模子
(1)组成结构
NiN模子在每个NiN块后接一个步幅为2、窗口外形为3×3的最大池化层。
(2)输出结构
NiN去掉了AlexNet末了的3个全毗连层,取而代之地,NiN使用了输出通道数即是标签类别数的NiN块,然后使用全局均匀池化层对每个通道中全部元素求均匀并直接用于分类。
如许设计的长处是:可以显著减小模子参数尺寸,从而缓解过拟合。
- net = nn.Sequential(
- nin_block(1, 96, kernel_size=11, stride=4, padding=0),
- nn.MaxPool2d(kernel_size=3, stride=2),
- nin_block(96, 256, kernel_size=5, stride=1, padding=2),
- nn.MaxPool2d(kernel_size=3, stride=2),
- nin_block(256, 384, kernel_size=3, stride=1, padding=1),
- nn.MaxPool2d(kernel_size=3, stride=2),
- nn.Dropout(0.5),
- # 标签类别数是10
- nin_block(384, 10, kernel_size=3, stride=1, padding=1),
- # 全局平均池化层可通过将窗口形状设置成输入的高和宽实现
- nn.AvgPool2d(kernel_size=5),
- # 将四维的输出转成二维的输出,其形状为(批量大小, 10)
- d2l.FlattenLayer())
复制代码
二、含并行连结的网络(GoogLeNet)
GoogLeNet中的基础卷积块叫作Inception块。
Inception块里有4条并行的线路:前3条线路使用窗口巨细分别是1×1、3×3和5×5的卷积层来抽取不同空间尺寸下的信息,其中中央2个线路会对输入先做1×1卷积来镌汰输入通道数,以低落模子复杂度。第四条线路则使用3×3最大池化层,后接1×1卷积层来改变通道数。4条线路都使用了合适的填充来使输入与输出的高和宽一致。末了我们将每条线路的输出在通道维上连结,并输入接下来的层中去。
Inception块中可以自定义的超参数是每个层的输出通道数,我们以此来控制模子复杂度。
每一条线路的输出在空间维度(即高和宽)上保持一致,不同线路之间的重要差异在于输出通道数,将每条线路的输出在通道维度上拼接。假如当每条线路经过自己的卷积或池化操作后,输出的特性图的外形是 H×W×C1,H×W×C2,H×W×C43,H×W×C4,其中 C1、C2、C3、C4 是不同线路输出的通道数。
- class Inception(nn.Module):
- # c1 - c4为每条线路里的层的输出通道数
- def __init__(self, in_c, c1, c2, c3, c4):
- super(Inception, self).__init__()
- # 线路1,单1 x 1卷积层
- self.p1_1 = nn.Conv2d(in_c, c1, kernel_size=1)
- # 线路2,1 x 1卷积层后接3 x 3卷积层
- self.p2_1 = nn.Conv2d(in_c, c2[0], kernel_size=1)
- self.p2_2 = nn.Conv2d(c2[0], c2[1], kernel_size=3, padding=1)
- # 线路3,1 x 1卷积层后接5 x 5卷积层
- self.p3_1 = nn.Conv2d(in_c, c3[0], kernel_size=1)
- self.p3_2 = nn.Conv2d(c3[0], c3[1], kernel_size=5, padding=2)
- # 线路4,3 x 3最大池化层后接1 x 1卷积层
- self.p4_1 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)
- self.p4_2 = nn.Conv2d(in_c, c4, kernel_size=1)
- def forward(self, x):
- p1 = F.relu(self.p1_1(x))
- p2 = F.relu(self.p2_2(F.relu(self.p2_1(x))))
- p3 = F.relu(self.p3_2(F.relu(self.p3_1(x))))
- p4 = F.relu(self.p4_2(self.p4_1(x)))
- return torch.cat((p1, p2, p3, p4), dim=1) # 在通道维上连结输出
复制代码
三、批量归一化
底部的层指的是靠近原始输入数据的层。
1、为什么要做批量归一化
即使输入数据已做标准化,练习中模子参数的更新依然很容易造成靠近输出层输出的剧烈变化。这种盘算数值的不稳固性通常令我们难以练习出有用的深度模子。
2、批量归一化思路
在模子练习时,批量归一化使用小批量上的均值和标准差,不停调整神经网络中央输出,从而使整个神经网络在各层的中央输出的数值更稳固。
3、对全毗连层做批量归一化
①批量归一化层处于的位置
位于全毗连层中的仿射变更和激活函数之间。
②方法实现
全毗连层的输入为u,权重参数和毛病参数分别为W和b,激活函数为ϕ,设批量归一化的运算符为BN:
小批量B由若干样本组成,其中恣意样本x_i是d维的,批量归一化层的输出同样是d维向量:
具体的批量归一化实现如下:
(1)对小批量B求均值和方差,其中的平方盘算是按元素求平方:
(2)使用按元素开方和按元素除法对x(i)标准化,这里ϵ>0是一个很小的常数,保证分母大于0:
(3)在上面标准化的基础上,批量归一化层引入了两个可以学习的模子参数,拉伸(scale)参数 γ 和偏移(shift)参数 β,这两个参数和x(i)外形雷同,皆为d维向量。它们与x̂ (i)分别做按元素乘法(符号⊙)和加法盘算::
可学习的拉伸和偏移参数保存了不对x(i)做批量归一化的可能:此时只需学出√γ=σB2+ϵ和β==μB。可以如许明白:假如批量归一化无益,理论上,学出的模子可以不使用批量归一化。
4、对卷积层做批量归一化
①批量归一化层处于的位置
对卷积层来说,批量归一化发生在卷积盘算之后、应用激活函数之前。
②方法实现
假如卷积盘算输出多个通道,我们必要对这些通道的输出分别做批量归一化,且每个通道都拥有独立的拉伸和偏移参数,并均为标量。
设小批量中有m个样本。在单个通道上,假设卷积盘算输出的高和宽分别为p和q。我们必要对该通道中m×p×q个元素同时做批量归一化。对这些元素做标准化盘算时,我们使用雷同的均值和方差,即该通道中m×p×q个元素的均值和方差。
5、猜测时的批量归一化
批量归一化确着实练习模式和猜测模式下体现不同,这重要是因为在练习时必要使用小批量样本的均值和方差,而在猜测时为了确保输出简直定性,使用的是整个练习数据的均值和方差的估计值。
- nn.BatchNorm1d:用于全毗连层输出或一维序列数据的归一化。
- nn.BatchNorm2d:用于卷积层输出的归一化,特别是图像或二维特性图数据。
- 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
复制代码- net = nn.Sequential(
- nn.Conv2d(1, 6, 5), # in_channels, out_channels, kernel_size
- nn.BatchNorm2d(6),
- nn.Sigmoid(),
- nn.MaxPool2d(2, 2), # kernel_size, stride
- nn.Conv2d(6, 16, 5),
- nn.BatchNorm2d(16),
- nn.Sigmoid(),
- nn.MaxPool2d(2, 2),
- d2l.FlattenLayer(),
- nn.Linear(16*4*4, 120),
- nn.BatchNorm1d(120),
- nn.Sigmoid(),
- nn.Linear(120, 84),
- nn.BatchNorm1d(84),
- nn.Sigmoid(),
- nn.Linear(84, 10)
- )
复制代码 四、残差网络(ResNet)
1、ResNet的提出配景
ResNet重要解决了深度神经网络在练习过程中出现的退化问题。在深层网络中,随着网络深度的增长,模子的练习效果并不总是变好,反而可能变得更差。具体体现为,较深的网络不仅会导致梯度消失或梯度爆炸的问题,还会使得练习误差增大,模子性能下降。
2、残差块
ResNet的焦点设计是残差学习(Residual Learning)。它通过引入残差毗连(skip connections)解决了深层网络中的优化问题,焦点设计点包括:
①残差块
Net中每一层并不直接学习渴望的输出,而是学习残差,即输入与输出之间的差异。对于每一个残差块,输入直接通过“跳跃毗连”传递到输出,并且与通过卷积层学习的输出叠加:
F(x)是通过若干层卷积和非线性变更学习得到的残差函数(F(x)即输入与输出之间的差值),y 是残差块的输出,x 是输入数据。
- 假如 F(x)很小(接近零),意味着输出与输入相差不大,此时网络可以很容易地学到恒等映射 H(x)≈x,因此即使网络非常深也不会出现退化问题。
- 假如 F(x)较大,网络会逐渐学习输入与输出之间的差异,这也比直接去学习复杂的 H(x)容易得多。
②跳跃毗连(Skip Connection)
传统的深度网络在每一层都会完全依赖前一层的输出,而在ResNet中,通过跳跃毗连,输入 x 直接跳过中央的非线性变更,到场到后面的盘算中。这种设计镌汰了信息丢失,使得梯度更容易传播,缓解了梯度消失的问题。
③恒等映射(Identity Mapping)
假如某一层的输出与输入雷同,理论上通过跳跃毗连可以确保这一层对网络的最终体现没有负面影响。这使得即使网络加深,也不会因为层数增长而使练习误差变得更高。
3、ResNet模子
①ResNet块的毗连
②ResNet团体架构图
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |