基於pytorch搭建ResNet神經網絡用於花類識別
持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第11天,點擊查看活動詳情
🍊作者簡介:禿頭小蘇,致力於用最通俗的語言描述問題
🍊往期回顧:基於pytorch搭建VGGNet神經網絡用於花類識別 基於pytorch搭建AlexNet神經網絡用於花類識別
🍊近期目標:擁有5000粉絲
🍊支持小蘇:點贊👍🏼、收藏⭐、留言📩
基於pytorch搭建ResNet神經網絡用於花類識別
寫在前面
這一系列已經寫了好幾篇了,這篇結束後可能就會停更一系列了,因為一方面,看懂了已經更新的這些我認為其他的網絡大概就是照葫蘆畫瓢,自己多多少少是能看明白個大概的。【當然這是要在你對這部分網絡結構的理論有充分的瞭解之後】另一方面,我覺得這部分真的得你自己切切實實的鑽研,自己一步步的調試,看別人的文章、甚至是視頻,你可能會得到短暫的滿足,但是許多細節你是體驗不到的。所以這裏給出基於pytorch搭建ResNet神經網絡用於花類識別的完整代碼,希望大家下去後仔細閲讀🌹🌹🌹
至於這一系列再次更新的話不出意外會講講一些輕量級網絡像MobileNet、shuffleNet等,當然了這部分都已經做過理論部分的概述了🍋🍋🍋
還是迴歸到本文上來,首先你需要具備以下知識:
- ResNet的理論
- pytorch搭建網絡基礎
當然,這些我在前文都已經介紹過,大家抱着缺啥補啥的態度去看看唄🌾🌾🌾
ResNet網絡模型搭建✨✨✨
自己寫文章的宗旨是致力於用最通俗的語言描述問題嘛🎯🎯🎯但是對於一些關乎於代碼的文章,有的時候單純的文字確實很難將問題表述清楚,因此我建議你先觀看此視頻,對ResNet網絡模型搭建的整理流程有了一個大致的瞭解後再來閲讀此文,然後再以這篇文章為輔進行學習,這樣我覺得是高效的學習方式🍀🍀🍀【當然這樣還是不夠的,你一定要自己去獨立的閲讀代碼,一步步的調試運行,這一點我想我再強調也不為過】
ResNeta網絡是有大量重複的結構堆疊而成的,它的網絡層數主要有18層、34層、50層、101層和152層。對於18層和34層的網絡它的基礎模塊為basic block,而對於50層、101層和152層的網絡其基礎模塊為bottleneck block。我們可以分別來定義這兩個基礎模塊,如下:
```python
定義BasicBlock
class BasicBlock(nn.Module): expansion = 1
def __init__(self, in_channel, out_channel, stride=1, downsample=None, **kwargs):
super(BasicBlock, self).__init__()
self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel,
kernel_size=3, stride=stride, padding=1, bias=False) # 特徵圖尺寸不變
self.bn1 = nn.BatchNorm2d(out_channel) # BN層建議放在卷積和激活層之間
self.relu = nn.ReLU()
self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel,
kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(out_channel)
self.downsample = downsample
def forward(self, x):
identity = x
if self.downsample is not None:
identity = self.downsample(x)
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out += identity
out = self.relu(out)
return out
```
```python
定義Bottleneck
class Bottleneck(nn.Module): """ 注意:原論文中,在虛線殘差結構的主分支上,第一個1x1卷積層的步距是2,第二個3x3卷積層步距是1。 但在pytorch官方實現過程中是第一個1x1卷積層的步距是1,第二個3x3卷積層步距是2, 這麼做的好處是能夠在top1上提升大概0.5%的準確率。 可參考Resnet v1.5 http://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch """ expansion = 4
def __init__(self, in_channel, out_channel, stride=1, downsample=None,
groups=1, width_per_group=64):
super(Bottleneck, self).__init__()
width = int(out_channel * (width_per_group / 64.)) * groups
self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=width,
kernel_size=1, stride=1, bias=False) # squeeze channels
self.bn1 = nn.BatchNorm2d(width)
# -----------------------------------------
self.conv2 = nn.Conv2d(in_channels=width, out_channels=width, groups=groups,
kernel_size=3, stride=stride, bias=False, padding=1)
self.bn2 = nn.BatchNorm2d(width)
# -----------------------------------------
self.conv3 = nn.Conv2d(in_channels=width, out_channels=out_channel*self.expansion,
kernel_size=1, stride=1, bias=False) # unsqueeze channels
self.bn3 = nn.BatchNorm2d(out_channel*self.expansion)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
def forward(self, x):
identity = x
if self.downsample is not None:
identity = self.downsample(x)
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = self.bn3(out)
out += identity
out = self.relu(out)
return out
```
接着我們就可以來定義我們的ResNet網絡了:
```python class ResNet(nn.Module):
def __init__(self,
block,
blocks_num,
num_classes=1000,
include_top=True,
groups=1,
width_per_group=64):
super(ResNet, self).__init__()
self.include_top = include_top
self.in_channel = 64
self.groups = groups
self.width_per_group = width_per_group
self.conv1 = nn.Conv2d(3, self.in_channel, kernel_size=7, stride=2,
padding=3, bias=False)
self.bn1 = nn.BatchNorm2d(self.in_channel)
self.relu = nn.ReLU(inplace=True)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.layer1 = self._make_layer(block, 64, blocks_num[0])
self.layer2 = self._make_layer(block, 128, blocks_num[1], stride=2)
self.layer3 = self._make_layer(block, 256, blocks_num[2], stride=2)
self.layer4 = self._make_layer(block, 512, blocks_num[3], stride=2)
if self.include_top:
self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) # output size = (1, 1)
self.fc = nn.Linear(512 * block.expansion, num_classes)
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.maxpool(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
if self.include_top:
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.fc(x)
return x
```
我們可以看出再ResNet 的定義中有這樣的函數:
該函數表示對ResNet的每個基礎模塊一個整合,即layer1對應conv2_x、layer2對應conv3_x、layer3對應conv4_x、layer4對應conv5_x🍚🍚🍚
_make_layer
函數的定義如下:
```python def _make_layer(self, block, channel, block_num, stride=1): downsample = None if stride != 1 or self.in_channel != channel * block.expansion: downsample = nn.Sequential( nn.Conv2d(self.in_channel, channel * block.expansion, kernel_size=1, stride=stride, bias=False), nn.BatchNorm2d(channel * block.expansion))
layers = []
layers.append(block(self.in_channel,
channel,
downsample=downsample,
stride=stride,
groups=self.groups,
width_per_group=self.width_per_group))
self.in_channel = channel * block.expansion
for _ in range(1, block_num):
layers.append(block(self.in_channel,
channel,
groups=self.groups,
width_per_group=self.width_per_group))
return nn.Sequential(*layers)
def forward(self, x): x = self.conv1(x) x = self.bn1(x) x = self.relu(x) x = self.maxpool(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
if self.include_top:
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.fc(x)
return x
```
最後我們來看看如何定義一個具體的網絡:
```python def resnet34(num_classes=1000, include_top=True): # http://download.pytorch.org/models/resnet34-333f7ec4.pth return ResNet(BasicBlock, [3, 4, 6, 3], num_classes=num_classes, include_top=include_top)
def resnet50(num_classes=1000, include_top=True): # http://download.pytorch.org/models/resnet50-19c8e357.pth return ResNet(Bottleneck, [3, 4, 6, 3], num_classes=num_classes, include_top=include_top)
def resnet101(num_classes=1000, include_top=True): # http://download.pytorch.org/models/resnet101-5d3b4d8f.pth return ResNet(Bottleneck, [3, 4, 23, 3], num_classes=num_classes, include_top=include_top) ```
訓練結構展示
ResNet34訓練結果:
ResNet50訓練結果:
ResNet101訓練結果:
遷移學習使用ResNet34預加載模型:
下面給出各種模型生成的權重文件,如下:
小結
這一部分我感到有一些的奇怪,即上文用resnet訓時,resnet101和resnet50的效果要比resnet34效果差,但是理論部分不是説resnet層數深效果越好嘛,這是什麼原因呢?希望知道的可以告知。🌿🌿🌿
問了一些大佬,對於上述問題他們的解答是:這個和自己任務也有關係,簡單的任務可能用小網絡效果更好。
參考視頻:http://www.bilibili.com/video/BV14E411H7Uw/?spm_id_from=333.788
如若文章對你有所幫助,那就🛴🛴🛴 咻咻咻咻~~duang\~~點個讚唄
- 兔年到了,一起來寫個春聯吧
- CV攻城獅入門VIT(vision transformer)之旅——VIT代碼實戰篇
- 對抗生成網絡GAN系列——GANomaly原理及源碼解析
- 對抗生成網絡GAN系列——WGAN原理及實戰演練
- CV攻城獅入門VIT(vision transformer)之旅——近年超火的Transformer你再不瞭解就晚了!
- 對抗生成網絡GAN系列——DCGAN簡介及人臉圖像生成案例
- 對抗生成網絡GAN系列——CycleGAN簡介及圖片春冬變換案例
- 對抗生成網絡GAN系列——AnoGAN原理及缺陷檢測實戰
- 目標檢測系列——Faster R-CNN原理詳解
- 目標檢測系列——Fast R-CNN原理詳解
- 目標檢測系列——開山之作RCNN原理詳解
- 【古月21講】ROS入門系列(4)——參數使用與編程方法、座標管理系統、tf座標系廣播與監聽的編程實現、launch啟動文件的使用方法
- 使用kitti數據集實現自動駕駛——繪製出所有物體的行駛軌跡
- 使用kitti數據集實現自動駕駛——發佈照片、點雲、IMU、GPS、顯示2D和3D偵測框
- 基於pytorch搭建ResNet神經網絡用於花類識別
- 基於pytorch搭建GoogleNet神經網絡用於花類識別
- 基於pytorch搭建VGGNet神經網絡用於花類識別
- UWB原理分析
- 論文閲讀:RRPN:RADAR REGION PROPOSAL NETWORK FOR OBJECT DETECTION IN AUTONOMOUS
- 凸優化理論基礎2——凸集和錐