Python之ResNet结构的解决办法
感兴趣的小伙伴,下面一起跟随php教程的雯雯来看看吧!
1.ResNet的创新
现
这篇文章主要为大家详细介绍了Python之ResNet结构的简单示例,具有一定的参考价值,可以用来参考一下。
感兴趣的小伙伴,下面一起跟随php教程的雯雯来看看吧!
1.ResNet的创新
现在重新稍微系统的介绍一下ResNet网络结构。 ResNet结构首先通过一个卷积层然后有一个池化层,然后通过一系列的残差结构,最后再通过一个平均池化下采样操作,以及一个全连接层的得到了一个输出。ResNet网络可以达到很深的层数的原因就是不断的堆叠残差结构而来的。
1)亮点
网络中的亮点 :
- 超深的网络结构( 突破1000 层)
- 提出residual 模块
- 使用Batch Normalization 加速训练( 丢弃dropout)
但是,一般来说,并不是一直的加深神经网络的结构就会得到一个更好的结果,一般太深的网络会出现过拟合的现象严重,可能还没有一些浅层网络要好。
2)原因
其中有两个原因:
- 梯度消失或梯度爆炸
当层数过多的时候,假设每一层的误差梯度都是一个小于1的数值,当进行方向传播的过程中,每向前传播一层,都要乘以一个小于1的误差梯度,当网络越来越深时,所成的小于1的系数也就越来越多,此时梯度便越趋近于0,这样梯度便会越来越小。这便会造成梯度消失的现象。
而当所成的误差梯度是一个大于1的系数,而随着网络层数的加深,梯度便会越来越大,这便会造成梯度爆炸的现象。
- 退化问题(degradation problem)
当解决了梯度消失或者梯度爆炸的问题之后,其实网络的效果可能还是不尽如意,还可能有退化问题。为此,ResNet提出了残差结构来解决这个退化问题。 也正是因为有这个残差的结构,所以才可以搭建这么深的网络。
2.ResNet的结构
残差结构如图所示
作图是针对ResNet-18/34层浅层网络的结构,右图是ResNet-50/101/152层深层网络的结构,其中注意:主分支与shortcut 的输出特征矩阵shape。
一下表格为网络的一些主要参数
可以看见,不同层数的网络结构其实框架是类似的,不同的至少堆叠的残差结构的数量。
1)浅层的残差结构
需要注意,有些残差结构的ShortCut是实线,而有的是虚线,这两者是不同的。对于左图来说,ShortCut是实线,这表明输入与输出的shape是一样的,所以可以直接的进行相加。而对于右图来说,其输入的shape与输出的shape是不一样的,这时候需要调整步长stribe与kernel size来使得两条路(主分支与捷径分支)所处理好的shape是一模一样的。
2)深层的残差结构
同样的,需要注意,主分支与shortcut 的输出特征矩阵shape必须相同,同样的通过步长来调整。
但是注意原论文中:
右侧虚线残差结构的主分支上、第一个1x1卷积层的步距是2,第二个3x3卷积层的步距是1.
而在pytorch官方实现的过程中是第一个1x1卷积层的步距是1,第二个3x3卷积层步距是2,这样能够在ImageNet的top1上提升大概0.5%的准确率。
所以在conv3_x,conv4_x,conv5_x中所对应的残差结构的第一层,都是指虚线的残差结构,其他的残差结构是实线的残差结构。
3)总结
对于每个大模块中的第一个残差结构,需要通过虚线分支来调整残差结构的输入与输出是同一个shape。此时使用了下采样的操作函数。对于每个大模块中的其他剩余的残差结构,只需要通过实线分支来调整残差网络结构,因为其输出和输入本身就是同一个shape的。
对于第一个大模块的第一个残差结构,其第二个3x3的卷积中,步长是1的,而其他的三个大模块的步长均为2.在每一个大模块的维度变换中,主要是第一个残差结构使得shape减半,而模块中其他的残差结构都是没有改变shape的。也真因为没有改变shape,所以这些残差结构才可以直接的通过实线进行相加。
3.Batch Normalization
Batch Normalization的目的是使我们的一批(Batch)特征矩阵feature map满足均值为0,方差为1的分布规律。
其中:μ,σ_2在正向传播过程中统计得到γ,β在反向传播过程中训练得到
Batch Normalization是google团队在2015年论文《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》提出的。通过该方法能够加速网络的收敛并提升准确率。
具体的相关原理见:Batch Normalization详解以及pytorch实验
4.参考代码
代码如下:
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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | <code> import torch import torch.nn as nn # 分类数目 num_class = 5 # 各层数目 resnet18_params = [2, 2, 2, 2] resnet34_params = [3, 4, 6, 3] resnet50_params = [3, 4, 6, 3] resnet101_params = [3, 4, 23, 3] resnet152_params = [3, 8, 36, 3] # 定义Conv1层 def Conv1(in_planes, places, stride=2): return nn.Sequential( nn.Conv2d(in_channels=in_planes,out_channels=places,kernel_size=7,stride=stride,padding=3, bias=False), nn.BatchNorm2d(places), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=3, stride=2, padding=1) ) # 浅层的残差结构 class BasicBlock(nn.Module): def __init__(self,in_places,places, stride=1,downsampling=False, expansion = 1): super(BasicBlock,self).__init__() self.expansion = expansion self.downsampling = downsampling # torch.Size([1, 64, 56, 56]), stride = 1 # torch.Size([1, 128, 28, 28]), stride = 2 # torch.Size([1, 256, 14, 14]), stride = 2 # torch.Size([1, 512, 7, 7]), stride = 2 self.basicblock = nn.Sequential( nn.Conv2d(in_channels=in_places, out_channels=places, kernel_size=3, stride=stride, padding=1, bias=False), nn.BatchNorm2d(places), nn.ReLU(inplace=True), nn.Conv2d(in_channels=places, out_channels=places, kernel_size=3, stride=1, padding=1, bias=False), nn.BatchNorm2d(places * self.expansion), ) # torch.Size([1, 64, 56, 56]) # torch.Size([1, 128, 28, 28]) # torch.Size([1, 256, 14, 14]) # torch.Size([1, 512, 7, 7]) # 每个大模块的第一个残差结构需要改变步长 if self.downsampling: self.downsample = nn.Sequential( nn.Conv2d(in_channels=in_places, out_channels=places*self.expansion, kernel_size=1, stride=stride, bias=False), nn.BatchNorm2d(places*self.expansion) ) self.relu = nn.ReLU(inplace=True) def forward(self, x): # 实线分支 residual = x out = self.basicblock(x) # 虚线分支 if self.downsampling: residual = self.downsample(x) out += residual out = self.relu(out) return out # 深层的残差结构 class Bottleneck(nn.Module): # 注意:默认 downsampling=False def __init__(self,in_places,places, stride=1,downsampling=False, expansion = 4): super(Bottleneck,self).__init__() self.expansion = expansion self.downsampling = downsampling self.bottleneck = nn.Sequential( # torch.Size([1, 64, 56, 56]),stride=1 # torch.Size([1, 128, 56, 56]),stride=1 # torch.Size([1, 256, 28, 28]), stride=1 # torch.Size([1, 512, 14, 14]), stride=1 nn.Conv2d(in_channels=in_places,out_channels=places,kernel_size=1,stride=1, bias=False), nn.BatchNorm2d(places), nn.ReLU(inplace=True), # torch.Size([1, 64, 56, 56]),stride=1 # torch.Size([1, 128, 28, 28]), stride=2 # torch.Size([1, 256, 14, 14]), stride=2 # torch.Size([1, 512, 7, 7]), stride=2 nn.Conv2d(in_channels=places, out_channels=places, kernel_size=3, stride=stride, padding=1, bias=False), nn.BatchNorm2d(places), nn.ReLU(inplace=True), # torch.Size([1, 256, 56, 56]),stride=1 # torch.Size([1, 512, 28, 28]), stride=1 # torch.Size([1, 1024, 14, 14]), stride=1 # torch.Size([1, 2048, 7, 7]), stride=1 nn.Conv2d(in_channels=places, out_channels=places * self.expansion, kernel_size=1, stride=1, bias=False), nn.BatchNorm2d(places * self.expansion), ) # torch.Size([1, 256, 56, 56]) # torch.Size([1, 512, 28, 28]) # torch.Size([1, 1024, 14, 14]) # torch.Size([1, 2048, 7, 7]) if self.downsampling: self.downsample = nn.Sequential( nn.Conv2d(in_channels=in_places, out_channels=places*self.expansion, kernel_size=1, stride=stride, bias=False), nn.BatchNorm2d(places*self.expansion) ) self.relu = nn.ReLU(inplace=True) def forward(self, x): # 实线分支 residual = x out = self.bottleneck(x) # 虚线分支 if self.downsampling: residual = self.downsample(x) out += residual out = self.relu(out) return out class ResNet(nn.Module): def __init__(self,blocks, blockkinds, num_classes=num_class): super(ResNet,self).__init__() self.blockkinds = blockkinds self.conv1 = Conv1(in_planes = 3, places= 64) # 对应浅层网络结构 if self.blockkinds == BasicBlock: self.expansion = 1 # 64 -> 64 self.layer1 = self.make_layer(in_places=64, places=64, block=blocks[0], stride=1) # 64 -> 128 self.layer2 = self.make_layer(in_places=64, places=128, block=blocks[1], stride=2) # 128 -> 256 self.layer3 = self.make_layer(in_places=128, places=256, block=blocks[2], stride=2) # 256 -> 512 self.layer4 = self.make_layer(in_places=256, places=512, block=blocks[3], stride=2) self.fc = nn.Linear(512, num_classes) # 对应深层网络结构 if self.blockkinds == Bottleneck: self.expansion = 4 # 64 -> 64 self.layer1 = self.make_layer(in_places = 64, places= 64, block=blocks[0], stride=1) # 256 -> 128 self.layer2 = self.make_layer(in_places = 256,places=128, block=blocks[1], stride=2) # 512 -> 256 self.layer3 = self.make_layer(in_places=512,places=256, block=blocks[2], stride=2) # 1024 -> 512 self.layer4 = self.make_layer(in_places=1024,places=512, block=blocks[3], stride=2) self.fc = nn.Linear(2048, num_classes) self.avgpool = nn.AvgPool2d(7, stride=1) # 初始化网络结构 for m in self.modules(): if isinstance(m, nn.Conv2d): # 采用了何凯明的初始化方法 nn.init.kaiming_normal_(m.weight, mode= 'fan_out' , nonlinearity= 'relu' ) elif isinstance(m, nn.BatchNorm2d): nn.init.constant_(m.weight, 1) nn.init.constant_(m.bias, 0) def make_layer(self, in_places, places, block, stride): layers = [] # torch.Size([1, 64, 56, 56]) -> torch.Size([1, 256, 56, 56]), stride=1 故w,h不变 # torch.Size([1, 256, 56, 56]) -> torch.Size([1, 512, 28, 28]), stride=2 故w,h变 # torch.Size([1, 512, 28, 28]) -> torch.Size([1, 1024, 14, 14]),stride=2 故w,h变 # torch.Size([1, 1024, 14, 14]) -> torch.Size([1, 2048, 7, 7]), stride=2 故w,h变 # 此步需要通过虚线分支,downsampling=True layers.append(self.blockkinds(in_places, places, stride, downsampling =True)) # torch.Size([1, 256, 56, 56]) -> torch.Size([1, 256, 56, 56]) # torch.Size([1, 512, 28, 28]) -> torch.Size([1, 512, 28, 28]) # torch.Size([1, 1024, 14, 14]) -> torch.Size([1, 1024, 14, 14]) # torch.Size([1, 2048, 7, 7]) -> torch.Size([1, 2048, 7, 7]) # print ( "places*self.expansion:" , places*self.expansion) # print ( "block:" , block) # 此步需要通过实线分支,downsampling=False, 每个大模块的第一个残差结构需要改变步长 for i in range(1, block): layers.append(self.blockkinds(places*self.expansion, places)) return nn.Sequential(*layers) def forward(self, x): # conv1层 x = self.conv1(x) # torch.Size([1, 64, 56, 56]) # conv2_x层 x = self.layer1(x) # torch.Size([1, 256, 56, 56]) # conv3_x层 x = self.layer2(x) # torch.Size([1, 512, 28, 28]) # conv4_x层 x = self.layer3(x) # torch.Size([1, 1024, 14, 14]) # conv5_x层 x = self.layer4(x) # torch.Size([1, 2048, 7, 7]) x = self.avgpool(x) # torch.Size([1, 2048, 1, 1]) / torch.Size([1, 512]) x = x.view(x.size(0), -1) # torch.Size([1, 2048]) / torch.Size([1, 512]) x = self.fc(x) # torch.Size([1, 5]) return x def ResNet18(): return ResNet(resnet18_params, BasicBlock) def ResNet34(): return ResNet(resnet34_params, BasicBlock) def ResNet50(): return ResNet(resnet50_params, Bottleneck) def ResNet101(): return ResNet(resnet101_params, Bottleneck) def ResNet152(): return ResNet(resnet152_params, Bottleneck) if __name__== '__main__' : # model = torchvision.models.resnet50() # 模型测试 # model = ResNet18() # model = ResNet34() # model = ResNet50() # model = ResNet101() model = ResNet152() # print (model) input = torch.randn(1, 3, 224, 224) out = model(input) print (out.shape)</code> |
以上就是pytorch实现ResNet结构的实例代码的详细内容,更多关于pytorch ResNet结构的资料请关注php教程其它相关文章!
注:关于Python之ResNet结构的简单示例的内容就先介绍到这里,更多相关文章的可以留意