最近发现了一份不错的源代码,作者使用 PyTorch 实现了如今主流的卷积神经网络 CNN 框架,包含了 12 中模型架构。所有代码使用的数据集是 CIFAR。

项目地址:

https://github.com/BIGBALLON/CIFAR-ZOO

CNN 经典论文

该项目实现的是主流的 CNN 模型,涉及的论文包括:

1. CNN 模型(12 篇)

2. 正则化(3 篇)

3. 学习速率调度器(2 篇)

需求和使用

1. 需求

运行所有代码的开发环境需求为:

  • Python >= 3.5
  • PyTorch >= 0.4
  • TensorFlow/Tensorboard
  • 其它依赖项 (pyyaml, easydict, tensorboardX)

作者提供了一键安装、配置开发环境的方法:

  1. pip install -r requirements.txt

2. 模型代码

作者将所有的模型都存放在 model 文件夹下,我们来看一下 PyTorch 实现的 ResNet 网络结构:

  1. # -*-coding:utf-8-*-
  2. import math
  3. import torch
  4. import torch.nn as nn
  5. import torch.nn.functional as F
  6. __all__ = ['resnet20', 'resnet32', 'resnet44',
  7. 'resnet56', 'resnet110', 'resnet1202']
  8. def conv3x3(in_planes, out_planes, stride=1):
  9. "3x3 convolution with padding"
  10. return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
  11. padding=1, bias=False)
  12. class BasicBlock(nn.Module):
  13. expansion = 1
  14. def __init__(self, inplanes, planes, stride=1, downsample=None):
  15. super(BasicBlock, self).__init__()
  16. self.conv_1 = conv3x3(inplanes, planes, stride)
  17. self.bn_1 = nn.BatchNorm2d(planes)
  18. self.relu = nn.ReLU(inplace=True)
  19. self.conv_2 = conv3x3(planes, planes)
  20. self.bn_2 = nn.BatchNorm2d(planes)
  21. self.downsample = downsample
  22. self.stride = stride
  23. def forward(self, x):
  24. residual = x
  25. out = self.conv_1(x)
  26. out = self.bn_1(out)
  27. out = self.relu(out)
  28. out = self.conv_2(out)
  29. out = self.bn_2(out)
  30. if self.downsample is not None:
  31. residual = self.downsample(x)
  32. out += residual
  33. out = self.relu(out)
  34. return out
  35. class Bottleneck(nn.Module):
  36. expansion = 4
  37. def __init__(self, inplanes, planes, stride=1, downsample=None):
  38. super(Bottleneck, self).__init__()
  39. self.conv_1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
  40. self.bn_1 = nn.BatchNorm2d(planes)
  41. self.conv_2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,
  42. padding=1, bias=False)
  43. self.bn_2 = nn.BatchNorm2d(planes)
  44. self.conv_3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
  45. self.bn_3 = nn.BatchNorm2d(planes * 4)
  46. self.relu = nn.ReLU(inplace=True)
  47. self.downsample = downsample
  48. self.stride = stride
  49. def forward(self, x):
  50. residual = x
  51. out = self.conv_1(x)
  52. out = self.bn_1(out)
  53. out = self.relu(out)
  54. out = self.conv_2(out)
  55. out = self.bn_2(out)
  56. out = self.relu(out)
  57. out = self.conv_3(out)
  58. out = self.bn_3(out)
  59. if self.downsample is not None:
  60. residual = self.downsample(x)
  61. out += residual
  62. out = self.relu(out)
  63. return out
  64. class ResNet(nn.Module):
  65. def __init__(self, depth, num_classes, block_name='BasicBlock'):
  66. super(ResNet, self).__init__()
  67. # Model type specifies number of layers for CIFAR-10 model
  68. if block_name == 'BasicBlock':
  69. assert (
  70. depth - 2) % 6 == 0, 'depth should be 6n+2, e.g. 20, 32, 44, 56, 110, 1202'
  71. n = (depth - 2) // 6
  72. block = BasicBlock
  73. elif block_name == 'Bottleneck':
  74. assert (
  75. depth - 2) % 9 == 0, 'depth should be 9n+2, e.g. 20, 29, 47, 56, 110, 1199'
  76. n = (depth - 2) // 9
  77. block = Bottleneck
  78. else:
  79. raise ValueError('block_name shoule be Basicblock or Bottleneck')
  80. self.inplanes = 16
  81. self.conv_1 = nn.Conv2d(3, 16, kernel_size=3, padding=1,
  82. bias=False)
  83. self.bn_1 = nn.BatchNorm2d(16)
  84. self.relu = nn.ReLU(inplace=True)
  85. self.stage_1 = self._make_layer(block, 16, n)
  86. self.stage_2 = self._make_layer(block, 32, n, stride=2)
  87. self.stage_3 = self._make_layer(block, 64, n, stride=2)
  88. self.avgpool = nn.AvgPool2d(8)
  89. self.fc = nn.Linear(64 * block.expansion, num_classes)
  90. for m in self.modules():
  91. if isinstance(m, nn.Conv2d):
  92. # nn.init.xavier_normal(m.weight.data)
  93. nn.init.kaiming_normal_(m.weight.data)
  94. elif isinstance(m, nn.BatchNorm2d):
  95. m.weight.data.fill_(1)
  96. m.bias.data.zero_()
  97. def _make_layer(self, block, planes, blocks, stride=1):
  98. downsample = None
  99. if stride != 1 or self.inplanes != planes * block.expansion:
  100. downsample = nn.Sequential(
  101. nn.Conv2d(self.inplanes, planes * block.expansion,
  102. kernel_size=1, stride=stride, bias=False),
  103. nn.BatchNorm2d(planes * block.expansion),
  104. )
  105. layers = []
  106. layers.append(block(self.inplanes, planes, stride, downsample))
  107. self.inplanes = planes * block.expansion
  108. for i in range(1, blocks):
  109. layers.append(block(self.inplanes, planes))
  110. return nn.Sequential(*layers)
  111. def forward(self, x):
  112. x = self.conv_1(x)
  113. x = self.bn_1(x)
  114. x = self.relu(x) # 32x32
  115. x = self.stage_1(x) # 32x32
  116. x = self.stage_2(x) # 16x16
  117. x = self.stage_3(x) # 8x8
  118. x = self.avgpool(x)
  119. x = x.view(x.size(0), -1)
  120. x = self.fc(x)
  121. return x
  122. def resnet20(num_classes):
  123. return ResNet(depth=20, num_classes=num_classes)
  124. def resnet32(num_classes):
  125. return ResNet(depth=32, num_classes=num_classes)
  126. def resnet44(num_classes):
  127. return ResNet(depth=44, num_classes=num_classes)
  128. def resnet56(num_classes):
  129. return ResNet(depth=56, num_classes=num_classes)
  130. def resnet110(num_classes):
  131. return ResNet(depth=110, num_classes=num_classes)
  132. def resnet1202(num_classes):
  133. return ResNet(depth=1202, num_classes=num_classes)

其它模型也一并能找到。

3. 使用

简单运行下面的命令就可以运行程序了:

  1. ## 1 GPU for lenet
  2. CUDA_VISIBLE_DEVICES=0 python -u train.py --work-path ./experiments/cifar10/lenet
  3. ## resume from ckpt
  4. CUDA_VISIBLE_DEVICES=0 python -u train.py --work-path ./experiments/cifar10/lenet --resume
  5. ## 2 GPUs for resnet1202
  6. CUDA_VISIBLE_DEVICES=0,1 python -u train.py --work-path ./experiments/cifar10/preresnet1202
  7. ## 4 GPUs for densenet190bc
  8. CUDA_VISIBLE_DEVICES=0,1,2,3 python -u train.py --work-path ./experiments/cifar10/densenet190bc

我们使用 yaml 文件 config.yaml 保存参数,查看 ./experimets 中的任何文件以了解更多详细信息。您可以通过 tensorboard 中 tensorboard —logdir path-to-event —port your-port 查看训练曲线。培训日志将通过日志转储,请检查您工作路径中的 log.txt。

模型在 CIFAR 数据集上的结果

1. 12 种 CNN 模型:

[转]12 个常见 CNN 模型论文集锦与 PyTorch 实现 - 图1

2. 正则化

默认的数据扩充方法是 RandomCrop+RandomHorizontalLip+Normalize,而 √ 表示采用哪种附加方法。

[转]12 个常见 CNN 模型论文集锦与 PyTorch 实现 - 图2

PS:Shake_Resnet26_2X64d 通过剪切和混合达到 97.71% 的测试精度!很酷,对吧?

3. 不同的学习速率调度器

[转]12 个常见 CNN 模型论文集锦与 PyTorch 实现 - 图3

最后,再附上项目地址:

[https://github.com/BIGBALLON/CIFAR-ZOO]https://link.zhihu.com/?target=https%3A//github.com/BIGBALLON/CIFAR-ZOO)
https://zhuanlan.zhihu.com/p/64693337