- 作者:老汪软件技巧
- 发表时间:2024-08-19 00:05
- 浏览量:
前情提要
本文是传知代码平台中的相关前沿知识与技术的分享~
接下来我们即将进入一个全新的空间,对技术有一个全新的视角~
本文所涉及所有资源均在传知代码平台可获取
以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦!!!
以下内容干货满满,跟上步伐吧~
本章重点一. 概述
本文复现论文 Wide Residual Networks[1] 提出的深度神经网络模型。
为了解决深度神经网络梯度消失的问题,深度残差网络(Residual Network[2])被提出。然而,仅为了提高千分之一的准确率,也要将网络的层数翻倍,这使得网络的训练变得非常缓慢。为了解决这些问题,该论文对ResNet基本块的架构进行了改进并提出了一种新颖的架构——宽度残差网络(Wide Residual Network),其减少了深度并增加了残差网络的宽度。
我基于Pytorch复现了该网络并在CIFAR-10[3]、CIFAR-100[3]和SVHN[4]数据集上进行试验。此外,我提供了一个基于SVHN数据集训练的数字识别系统用于体验。
二. 模型结构
宽度残差网络共包含四组结构。其中,第一组固定为一个卷积神经网络,第二、三、四组都包含 n 个基本残差块。
基本残差块的结构如图所示:
与普通的残差块不同的地方在于,普通残差块中的批归一化层和激活层都放在卷积层之后,而该论文将批归一化层和激活层都放在卷积层之前,该做法一方面加快了计算,另一方面使得该网络可以不需要用于特征池化的瓶颈层。此外,宽度残差网络成倍地增加了普通残差网络的特征通道数。
宽度残差网络在第三、四组的第一个卷积层进行下采样,即设置卷积步长为2。
三.核心逻辑
Wide Residual Network 的模型代码如下所示:
import torch
import torch.nn as nn
import torch.nn.functional as F
class WideBasicBlock(nn.Module):
"""Wide Residual Network的基本单元"""
def __init__(self, in_channels, out_channels, stride, dropout):
super(WideBasicBlock, self).__init__()
self.stride = stride
# 批归一化层、激活层、卷积层、Dropout层
self.layers = nn.Sequential(
nn.BatchNorm2d(in_channels),
nn.ReLU(inplace=True),
nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False),
nn.BatchNorm2d(out_channels),
nn.ReLU(inplace=True),
nn.Dropout(dropout),
nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
)
def forward(self, x):
out = self.layers(x)
if self.stride != 1:
residual = F.adaptive_avg_pool2d(x, (out.size(2), out.size(3)))
else:
residual = x
if out.size(1) != residual.size(1):
# 对池化和升维的特殊处理
if out.size(1) % residual.size(1) == 0:
residual = residual.repeat(1, out.size(1) // residual.size(1), 1, 1)
else:
padding = torch.zeros(residual.size(0), out.size(1) - residual.size(1), residual.size(2), residual.size(3)).to(residual.device)
residual = torch.cat((residual, padding), dim=1)
out = out + residual
return out
class WideResidualNetwork(nn.Module):
"""Wide Residual Network"""
def __init__(self, in_channels, out_channels, depth, width, dropout=0):
super(WideResidualNetwork, self).__init__()
self.conv1 = nn.Conv2d(in_channels, 16, kernel_size=3, stride=1, padding=1, bias=False)
self.conv2 = self.add_block(
in_channels = 16,
out_channels = 16 * width,
depth = depth,
stride = 1,
dropout = dropout
)
self.conv3 = self.add_block(
in_channels = 16 * width,
out_channels = 32 * width,
depth = depth,
stride = 2,
dropout = dropout
)
self.conv4 = self.add_block(
in_channels = 32 * width,
out_channels = 64 * width,
depth = depth,
stride = 2,
dropout = dropout
)
self.linear = nn.Linear(64 * width, out_channels)
def add_block(self, in_channels, out_channels, depth, stride, dropout):
"""添加一个基本单元的组合"""
layers = nn.Sequential()
layers.add_module(
name = '0',
module = WideBasicBlock(
in_channels = in_channels,
out_channels = out_channels,
stride = stride,
dropout = dropout
)
)
for i in range(1, depth):
layers.add_module(
name = str(i),
module = WideBasicBlock(
in_channels = out_channels,
out_channels = out_channels,
stride = 1,
dropout = dropout
)
)
return layers
def forward(self, x):
out = self.conv1(x)
out = self.conv2(out)
out = self.conv3(out)
out = self.conv4(out)
out = F.adaptive_avg_pool2d(out, (1, 1))
out = torch.flatten(out, 1)
out = self.linear(out)
return out
四.实验
训练与测试
所有实验基于WRN-37-2进行且使用SGD进行优化。对于CIFAR-10和CIFAR-100,学习率为0.01并在第60、120、160轮衰减到20%,dropout采用0.3,weight_decay和momentum分别为0.0005和0.9。对于SVHN,学习率为0.01并在第80、120轮衰减到10%,dropout为0,weight_decay和momentum分别为0.0005和0.9。三个数据集的batch size均为128。
此外,CIFAR-10和CIFAR-100使用了数据增强操作,具体为随机水平翻转和随机裁剪。
具体的实验结果如下表所示:
总结
综上,我们基本了解了“一项全新的技术啦” :lollipop: ~~
恭喜你的内功又双叒叕得到了提高!!!
感谢你们的阅读:satisfied:
后续还会继续更新:heartbeat:,欢迎持续关注:pushpin:哟~
:dizzy:如果有错误❌,欢迎指正呀:dizzy:
:sparkles:如果觉得收获满满,可以点点赞支持一下哟~:sparkles: