当前位置:   article > 正文

ResNet代码复现+超详细注释(PyTorch)

resnet代码复现

关于ResNet的原理和具体细节,可参见上篇解读:经典神经网络论文超详细解读(五)——ResNet(残差网络)学习笔记(翻译+精读+代码复现)

接下来我们就来复现一下代码。

源代码比较复杂,感兴趣的同学可以上官网学习: 

https://github.com/pytorch/vision/tree/master/torchvision

本篇是简化版本 


 一、BasicBlock模块

BasicBlock结构图如图所示:

 BasicBlock是基础版本,主要用来构建ResNet18ResNet34网络,里面只包含两个卷积层,使用了两个 3*3 的卷积,通道数都是64,卷积后接着 BN 和 ReLU

右边的曲线就是Shortcut Connections,将输入x加到输出。

代码:

  1. '''-------------一、BasicBlock模块-----------------------------'''
  2. # 用于ResNet18和ResNet34基本残差结构块
  3. class BasicBlock(nn.Module):
  4. def __init__(self, inchannel, outchannel, stride=1):
  5. super(BasicBlock, self).__init__()
  6. self.left = nn.Sequential(
  7. nn.Conv2d(inchannel, outchannel, kernel_size=3, stride=stride, padding=1, bias=False),
  8. nn.BatchNorm2d(outchannel),
  9. nn.ReLU(inplace=True), #inplace=True表示进行原地操作,一般默认为False,表示新建一个变量存储操作
  10. nn.Conv2d(outchannel, outchannel, kernel_size=3, stride=1, padding=1, bias=False),
  11. nn.BatchNorm2d(outchannel)
  12. )
  13. self.shortcut = nn.Sequential()
  14. #论文中模型架构的虚线部分,需要下采样
  15. if stride != 1 or inchannel != outchannel:
  16. self.shortcut = nn.Sequential(
  17. nn.Conv2d(inchannel, outchannel, kernel_size=1, stride=stride, bias=False),
  18. nn.BatchNorm2d(outchannel)
  19. )
  20. def forward(self, x):
  21. out = self.left(x) #这是由于残差块需要保留原始输入
  22. out += self.shortcut(x)#这是ResNet的核心,在输出上叠加了输入x
  23. out = F.relu(out)
  24. return out

二、Bottleneck 模块

Bottleneck结构图如图所示:

Bottleneck主要用在ResNet50及以上的网络结构,与BasicBlock不同的是这里有 3 个卷积,分别为 1*1,3*3,1*1大小的卷积核,分别用于压缩维度、卷积处理、恢复维度
这里的通道数是变化的,1*1卷积层的作用就是用于改变特征图的通数,使得可以和恒等映射x相叠加,另外这里的1*1卷积层改变维度的很重要的一点是可以降低网络参数量,这也是为什么更深层的网络采用BottleNeck而不是BasicBlock的原因。

注意:这里outchannel / 4是因为Bottleneck层输出通道都是输入的4倍

代码:

  1. '''-------------二、Bottleneck模块-----------------------------'''
  2. # 用于ResNet50及以上的残差结构块
  3. class Bottleneck(nn.Module):
  4. def __init__(self, inchannel, outchannel, stride=1):
  5. super(Bottleneck, self).__init__()
  6. self.left = nn.Sequential(
  7. nn.Conv2d(inchannel, int(outchannel / 4), kernel_size=1, stride=stride, padding=0, bias=False),
  8. nn.BatchNorm2d(int(outchannel / 4)),
  9. nn.ReLU(inplace=True),
  10. nn.Conv2d(int(outchannel / 4), int(outchannel / 4), kernel_size=3, stride=1, padding=1, bias=False),
  11. nn.BatchNorm2d(int(outchannel / 4)),
  12. nn.ReLU(inplace=True),
  13. nn.Conv2d(int(outchannel / 4), outchannel, kernel_size=1, stride=1, padding=0, bias=False),
  14. nn.BatchNorm2d(outchannel),
  15. )
  16. self.shortcut = nn.Sequential()
  17. if stride != 1 or inchannel != outchannel:
  18. self.shortcut = nn.Sequential(
  19. nn.Conv2d(inchannel, outchannel, kernel_size=1, stride=stride, bias=False),
  20. nn.BatchNorm2d(outchannel)
  21. )
  22. def forward(self, x):
  23. out = self.left(x)
  24. y = self.shortcut(x)
  25. out += self.shortcut(x)
  26. out = F.relu(out)
  27. return out

三、ResNet主体

介绍了上述BasicBlock基础块和BotteNeck结构后,我们就可以搭建ResNet结构了。

5种不同层数的ResNet结构图如图所示:

代码: 

ResNet18

  1. '''----------ResNet18----------'''
  2. class ResNet_18(nn.Module):
  3. def __init__(self, ResidualBlock, num_classes=10):
  4. super(ResNet_18, self).__init__()
  5. self.inchannel = 64
  6. self.conv1 = nn.Sequential(
  7. nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False),
  8. nn.BatchNorm2d(64),
  9. nn.ReLU(),
  10. )
  11. self.layer1 = self.make_layer(ResidualBlock, 64, 2, stride=1)
  12. self.layer2 = self.make_layer(ResidualBlock, 128, 2, stride=2)
  13. self.layer3 = self.make_layer(ResidualBlock, 256, 2, stride=2)
  14. self.layer4 = self.make_layer(ResidualBlock, 512, 2, stride=2)
  15. self.fc = nn.Linear(512, num_classes)
  16. def make_layer(self, block, channels, num_blocks, stride):
  17. strides = [stride] + [1] * (num_blocks - 1) # strides=[1,1]
  18. layers = []
  19. for stride in strides:
  20. layers.append(block(self.inchannel, channels, stride))
  21. self.inchannel = channels
  22. return nn.Sequential(*layers)
  23. def forward(self, x): # 3*32*32
  24. out = self.conv1(x) # 64*32*32
  25. out = self.layer1(out) # 64*32*32
  26. out = self.layer2(out) # 128*16*16
  27. out = self.layer3(out) # 256*8*8
  28. out = self.layer4(out) # 512*4*4
  29. out = F.avg_pool2d(out, 4) # 512*1*1
  30. out = out.view(out.size(0), -1) # 512
  31. out = self.fc(out)
  32. return out

ResNet34

  1. '''----------ResNet34----------'''
  2. class ResNet_34(nn.Module):
  3. def __init__(self, ResidualBlock, num_classes=10):
  4. super(ResNet_34, self).__init__()
  5. self.inchannel = 64
  6. self.conv1 = nn.Sequential(
  7. nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False),
  8. nn.BatchNorm2d(64),
  9. nn.ReLU(),
  10. )
  11. self.layer1 = self.make_layer(ResidualBlock, 64, 3, stride=1)
  12. self.layer2 = self.make_layer(ResidualBlock, 128, 4, stride=2)
  13. self.layer3 = self.make_layer(ResidualBlock, 256, 6, stride=2)
  14. self.layer4 = self.make_layer(ResidualBlock, 512, 3, stride=2)
  15. self.fc = nn.Linear(512, num_classes)
  16. def make_layer(self, block, channels, num_blocks, stride):
  17. strides = [stride] + [1] * (num_blocks - 1) # strides=[1,1]
  18. layers = []
  19. for stride in strides:
  20. layers.append(block(self.inchannel, channels, stride))
  21. self.inchannel = channels
  22. return nn.Sequential(*layers)
  23. def forward(self, x): # 3*32*32
  24. out = self.conv1(x) # 64*32*32
  25. out = self.layer1(out) # 64*32*32
  26. out = self.layer2(out) # 128*16*16
  27. out = self.layer3(out) # 256*8*8
  28. out = self.layer4(out) # 512*4*4
  29. out = F.avg_pool2d(out, 4) # 512*1*1
  30. out = out.view(out.size(0), -1) # 512
  31. out = self.fc(out)
  32. return out

ResNet50

  1. '''---------ResNet50--------'''
  2. class ResNet_50(nn.Module):
  3. def __init__(self, ResidualBlock, num_classes=10):
  4. super(ResNet_50, self).__init__()
  5. self.inchannel = 64
  6. self.conv1 = nn.Sequential(
  7. nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False),
  8. nn.BatchNorm2d(64),
  9. nn.ReLU(),
  10. )
  11. self.layer1 = self.make_layer(ResidualBlock, 256, 3, stride=1)
  12. self.layer2 = self.make_layer(ResidualBlock, 512, 4, stride=2)
  13. self.layer3 = self.make_layer(ResidualBlock, 1024, 6, stride=2)
  14. self.layer4 = self.make_layer(ResidualBlock, 2048, 3, stride=2)
  15. self.fc = nn.Linear(512 * 4, num_classes)
  16. # **************************
  17. def make_layer(self, block, channels, num_blocks, stride):
  18. strides = [stride] + [1] * (num_blocks - 1) # strides=[1,1]
  19. layers = []
  20. for stride in strides:
  21. layers.append(block(self.inchannel, channels, stride))
  22. self.inchannel = channels
  23. return nn.Sequential(*layers)
  24. def forward(self, x): # 3*32*32
  25. out = self.conv1(x) # 64*32*32
  26. out = self.layer1(out) # 64*32*32
  27. out = self.layer2(out) # 128*16*16
  28. out = self.layer3(out) # 256*8*8
  29. out = self.layer4(out) # 512*4*4
  30. out = F.avg_pool2d(out, 4) # 512*1*1
  31. # print(out.size())
  32. out = out.view(out.size(0), -1) # 512
  33. out = self.fc(out)
  34. return out

四、完整代码

  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as F
  4. '''-------------一、BasicBlock模块-----------------------------'''
  5. # 用于ResNet18和ResNet34基本残差结构块
  6. class BasicBlock(nn.Module):
  7. def __init__(self, inchannel, outchannel, stride=1):
  8. super(BasicBlock, self).__init__()
  9. self.left = nn.Sequential(
  10. nn.Conv2d(inchannel, outchannel, kernel_size=3, stride=stride, padding=1, bias=False),
  11. nn.BatchNorm2d(outchannel),
  12. nn.ReLU(inplace=True), #inplace=True表示进行原地操作,一般默认为False,表示新建一个变量存储操作
  13. nn.Conv2d(outchannel, outchannel, kernel_size=3, stride=1, padding=1, bias=False),
  14. nn.BatchNorm2d(outchannel)
  15. )
  16. self.shortcut = nn.Sequential()
  17. #论文中模型架构的虚线部分,需要下采样
  18. if stride != 1 or inchannel != outchannel:
  19. self.shortcut = nn.Sequential(
  20. nn.Conv2d(inchannel, outchannel, kernel_size=1, stride=stride, bias=False),
  21. nn.BatchNorm2d(outchannel)
  22. )
  23. def forward(self, x):
  24. out = self.left(x) #这是由于残差块需要保留原始输入
  25. out += self.shortcut(x)#这是ResNet的核心,在输出上叠加了输入x
  26. out = F.relu(out)
  27. return out
  28. '''-------------二、Bottleneck模块-----------------------------'''
  29. # 用于ResNet50及以上的残差结构块
  30. class Bottleneck(nn.Module):
  31. def __init__(self, inchannel, outchannel, stride=1):
  32. super(Bottleneck, self).__init__()
  33. self.left = nn.Sequential(
  34. nn.Conv2d(inchannel, int(outchannel / 4), kernel_size=1, stride=stride, padding=0, bias=False),
  35. nn.BatchNorm2d(int(outchannel / 4)),
  36. nn.ReLU(inplace=True),
  37. nn.Conv2d(int(outchannel / 4), int(outchannel / 4), kernel_size=3, stride=1, padding=1, bias=False),
  38. nn.BatchNorm2d(int(outchannel / 4)),
  39. nn.ReLU(inplace=True),
  40. nn.Conv2d(int(outchannel / 4), outchannel, kernel_size=1, stride=1, padding=0, bias=False),
  41. nn.BatchNorm2d(outchannel),
  42. )
  43. self.shortcut = nn.Sequential()
  44. if stride != 1 or inchannel != outchannel:
  45. self.shortcut = nn.Sequential(
  46. nn.Conv2d(inchannel, outchannel, kernel_size=1, stride=stride, bias=False),
  47. nn.BatchNorm2d(outchannel)
  48. )
  49. def forward(self, x):
  50. out = self.left(x)
  51. y = self.shortcut(x)
  52. out += self.shortcut(x)
  53. out = F.relu(out)
  54. return out
  55. '''-------------ResNet18---------------'''
  56. class ResNet_18(nn.Module):
  57. def __init__(self, ResidualBlock, num_classes=10):
  58. super(ResNet_18, self).__init__()
  59. self.inchannel = 64
  60. self.conv1 = nn.Sequential(
  61. nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False),
  62. nn.BatchNorm2d(64),
  63. nn.ReLU(),
  64. )
  65. self.layer1 = self.make_layer(ResidualBlock, 64, 2, stride=1)
  66. self.layer2 = self.make_layer(ResidualBlock, 128, 2, stride=2)
  67. self.layer3 = self.make_layer(ResidualBlock, 256, 2, stride=2)
  68. self.layer4 = self.make_layer(ResidualBlock, 512, 2, stride=2)
  69. self.fc = nn.Linear(512, num_classes)
  70. def make_layer(self, block, channels, num_blocks, stride):
  71. strides = [stride] + [1] * (num_blocks - 1) # strides=[1,1]
  72. layers = []
  73. for stride in strides:
  74. layers.append(block(self.inchannel, channels, stride))
  75. self.inchannel = channels
  76. return nn.Sequential(*layers)
  77. def forward(self, x): # 3*32*32
  78. out = self.conv1(x) # 64*32*32
  79. out = self.layer1(out) # 64*32*32
  80. out = self.layer2(out) # 128*16*16
  81. out = self.layer3(out) # 256*8*8
  82. out = self.layer4(out) # 512*4*4
  83. out = F.avg_pool2d(out, 4) # 512*1*1
  84. out = out.view(out.size(0), -1) # 512
  85. out = self.fc(out)
  86. return out
  87. '''-------------ResNet34---------------'''
  88. class ResNet_34(nn.Module):
  89. def __init__(self, ResidualBlock, num_classes=10):
  90. super(ResNet_34, self).__init__()
  91. self.inchannel = 64
  92. self.conv1 = nn.Sequential(
  93. nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False),
  94. nn.BatchNorm2d(64),
  95. nn.ReLU(),
  96. )
  97. self.layer1 = self.make_layer(ResidualBlock, 64, 3, stride=1)
  98. self.layer2 = self.make_layer(ResidualBlock, 128, 4, stride=2)
  99. self.layer3 = self.make_layer(ResidualBlock, 256, 6, stride=2)
  100. self.layer4 = self.make_layer(ResidualBlock, 512, 3, stride=2)
  101. self.fc = nn.Linear(512, num_classes)
  102. def make_layer(self, block, channels, num_blocks, stride):
  103. strides = [stride] + [1] * (num_blocks - 1) # strides=[1,1]
  104. layers = []
  105. for stride in strides:
  106. layers.append(block(self.inchannel, channels, stride))
  107. self.inchannel = channels
  108. return nn.Sequential(*layers)
  109. def forward(self, x): # 3*32*32
  110. out = self.conv1(x) # 64*32*32
  111. out = self.layer1(out) # 64*32*32
  112. out = self.layer2(out) # 128*16*16
  113. out = self.layer3(out) # 256*8*8
  114. out = self.layer4(out) # 512*4*4
  115. out = F.avg_pool2d(out, 4) # 512*1*1
  116. out = out.view(out.size(0), -1) # 512
  117. out = self.fc(out)
  118. return out
  119. '''-------------ResNet50---------------'''
  120. class ResNet_50(nn.Module):
  121. def __init__(self, ResidualBlock, num_classes=10):
  122. super(ResNet_50, self).__init__()
  123. self.inchannel = 64
  124. self.conv1 = nn.Sequential(
  125. nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False),
  126. nn.BatchNorm2d(64),
  127. nn.ReLU(),
  128. )
  129. self.layer1 = self.make_layer(ResidualBlock, 256, 3, stride=1)
  130. self.layer2 = self.make_layer(ResidualBlock, 512, 4, stride=2)
  131. self.layer3 = self.make_layer(ResidualBlock, 1024, 6, stride=2)
  132. self.layer4 = self.make_layer(ResidualBlock, 2048, 3, stride=2)
  133. self.fc = nn.Linear(512 * 4, num_classes)
  134. # **************************
  135. def make_layer(self, block, channels, num_blocks, stride):
  136. strides = [stride] + [1] * (num_blocks - 1) # strides=[1,1]
  137. layers = []
  138. for stride in strides:
  139. layers.append(block(self.inchannel, channels, stride))
  140. self.inchannel = channels
  141. return nn.Sequential(*layers)
  142. def forward(self, x): # 3*32*32
  143. out = self.conv1(x) # 64*32*32
  144. out = self.layer1(out) # 64*32*32
  145. out = self.layer2(out) # 128*16*16
  146. out = self.layer3(out) # 256*8*8
  147. out = self.layer4(out) # 512*4*4
  148. out = F.avg_pool2d(out, 4) # 512*1*1
  149. # print(out.size())
  150. out = out.view(out.size(0), -1) # 512
  151. out = self.fc(out)
  152. return out
  153. def ResNet18():
  154. return ResNet_18(BasicBlock)
  155. def ResNet34():
  156. return ResNet_34(BasicBlock)
  157. def ResNet50():
  158. return ResNet_50(Bottleneck)

本篇到这里就结束啦,有什么问题,欢迎大家留言讨论~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/598144
推荐阅读
相关标签
  

闽ICP备14008679号