写在前面
作为研究目标检测的研究僧一枚,接触深度学习许久,但是仍感觉自己的理论基础和代码基础超级薄弱。因此决定从最基础的网络框架以及代码重新学习,加油!!!
Resnet学习
一、原论文知识补充(2024.2.26)
1.残差网络实现功能:如果主干网络学习效果不加,直接通过残差边进行学习。
2.解决当时一个固有思路:网络越深,效果越好。实则不然,网络层数越深,可能会导致训练效果的退化————因此提出resnet。
二、代码复现(2024.2.26-3.1)
复现Resnet-18,主要应用下图残差网络块。
Jupyter Notebook补充知识
1.代码使用jupyter,安装下载后,cmd中输入jupyter notebook,即可打开。
jupyter notebook
2.将图片保存在Jupyter
3.代码补全快捷键:Tab键
1.导入torch框架
# 导入torch框架
import torch
import torch.nn as nn
2.搭建残差网络模块Residual block,网络框架如上图所示。
# Residual block残差网络块
# 继承nn.module类
class ResidualBlock(nn.Module):
# 初始化参数
def __init__(self, in_channels, out_channels, stride=1):
# 多继承
super(ResidualBlock, self).__init__()
# 两个卷积层设置
# 注:如果卷积层后有bn层,那么将偏执bias关闭,因为bn层有调节bias的作用
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size = 3, stride = stride, padding = 1, bias = False)
# 归一化,对数据进行处理,数据更规范,避免梯度爆炸,梯度消失
self.bn1 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU(inplace = True)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size = 3, stride = 1, padding = 1, bias = False)
self.bn2 = nn.BatchNorm2d(out_channels)
# 残差边与原始边如果维度不一致的话,无法拼接,所以需要做整合操作
# 利用squential,如果一致,则不需要改变
self.shortcut = nn.Sequential()
# 不一致,需要用1*1卷积核,步长等于原步长即可
if stride != 1 or in_channels != out_channels:
self.shortcut = nn.Sequential(nn.Conv2d(in_channels, out_channels, kernel_size = 1, stride = stride, bias = False),
nn.BatchNorm2d(out_channels))
# 前向传播
def forward(self, x):
identity = x
out1 = self.conv1(x)
out1 = self.bn1(out1)
out1 = self.relu(out1)
out2 = self.conv2(out1)
out2 = self.bn2(out2)
out2 += self.shortcut(identity)
out = self.relu(out2)
return out
3.利用搭建好的残差模块构建ResNet18网络框架
# Resnet18网络块,用于分类
class ResNet18(nn.Module):
def __init__(self, num_classes = 1000):
super(ResNet18, self).__init__()
# 依次定义各层网络
self.conv1 = nn.Conv2d(3, 64, kernel_size = 7, stride = 2, padding = 3, bias = False)
self.bn1 = nn.BatchNorm2d(64)
self.relu = nn.ReLU(inplace = True)
self.maxpool = nn.MaxPool2d(kernel_size = 3, stride = 2, padding = 1)
self.layer1 = self._make_layer(64, 64, 2, stride = 1)
self.layer2 = self._make_layer(64, 128, 2, stride = 2)
self.layer3 = self._make_layer(128, 256, 2, stride = 2)
self.layer4 = self._make_layer(256, 512, 2, stride = 2)
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(512, num_classes)
# 简化残差块的代码编写
def _make_layer(self, in_channels, out_channels, blocks, stride = 1):
# 建立列表
layer = []
layer.append(ResidualBlock(in_channels, out_channels, stride))
for _ in range(1, blocks):
layer.append(ResidualBlock(out_channels, out_channels))
# 打包成一个结构,*代表layer里面的所有元素以列表的形式返回给sequential
return nn.Sequential(*layer)
def forward(self, x):
out1 = self.conv1(x)
out1 = self.bn1(out1)
out1 = self.relu(out1)
out2 = self.maxpool(out1)
out3 = self.layer1(out2)
out3 = self.layer2(out3)
out3 = self.layer3(out3)
out3 = self.layer4(out3)
out4 = self.avgpool(out3)
out4 = touch.flatten(out4, 1)
out = self.fc(out4)
return out
注:为了简化残差块的代码编写,利用列表layer以及for循环,将残差块打包成一个结构,最后返回给sequential。
4.打印ResNet18模型并测试
# 建立模型并打印
model = ResNet18()
print(model)
# 利用summary函数测试输入输出
from torchsummary import summary
summary(model, (3, 224, 224))
三、总结
1.熟练运用Jupyter Notebook;
2.精化代码,使代码简单化。