一、使用PyTorch搭建ResNet18网络并使用CIFAR10数据集训练测试

1. ResNet18网络结构

所有不同层数的ResNet:

这里给出了我认为比较详细的ResNet18网络具体参数和执行流程图:

2. 实现代码

这里并未采用BasicBlock和BottleNeck复现ResNet18
具体ResNet原理细节这里不多做描述,直接上代码

model.py网络模型部分:

import torch
import torch.nn as nn
from torch.nn import functional as F"""
把ResNet18的残差卷积单元作为一个Block,这里分为两种:一种是CommonBlock,另一种是SpecialBlock,最后由ResNet18统筹调度
其中SpecialBlock负责完成ResNet18中带有虚线(升维channel增加和下采样操作h和w减少)的Block操作
其中CommonBlock负责完成ResNet18中带有实线的直接相连相加的Block操作
注意ResNet18中所有非shortcut部分的卷积kernel_size=3, padding=1,仅仅in_channel, out_channel, stride的不同
注意ResNet18中所有shortcut部分的卷积kernel_size=1, padding=0,仅仅in_channel, out_channel, stride的不同
"""class CommonBlock(nn.Module):def __init__(self, in_channel, out_channel, stride):        # 普通Block简单完成两次卷积操作super(CommonBlock, self).__init__()self.conv1 = nn.Conv2d(in_channel, out_channel, kernel_size=3, stride=stride, padding=1, bias=False)self.bn1 = nn.BatchNorm2d(out_channel)self.conv2 = nn.Conv2d(out_channel, out_channel, kernel_size=3, stride=stride, padding=1, bias=False)self.bn2 = nn.BatchNorm2d(out_channel)def forward(self, x):identity = x                                            # 普通Block的shortcut为直连,不需要升维下采样x = F.relu(self.bn1(self.conv1(x)), inplace=True)       # 完成一次卷积x = self.bn2(self.conv2(x))                             # 第二次卷积不加relu激活函数x += identity                                           # 两路相加return F.relu(x, inplace=True)                          # 添加激活函数输出class SpecialBlock(nn.Module):                                  # 特殊Block完成两次卷积操作,以及一次升维下采样def __init__(self, in_channel, out_channel, stride):        # 注意这里的stride传入一个数组,shortcut和残差部分stride不同super(SpecialBlock, self).__init__()self.change_channel = nn.Sequential(                    # 负责升维下采样的卷积网络change_channelnn.Conv2d(in_channel, out_channel, kernel_size=1, stride=stride[0], padding=0, bias=False),nn.BatchNorm2d(out_channel))self.conv1 = nn.Conv2d(in_channel, out_channel, kernel_size=3, stride=stride[0], padding=1, bias=False)self.bn1 = nn.BatchNorm2d(out_channel)self.conv2 = nn.Conv2d(out_channel, out_channel, kernel_size=3, stride=stride[1], padding=1, bias=False)self.bn2 = nn.BatchNorm2d(out_channel)def forward(self, x):identity = self.change_channel(x)                       # 调用change_channel对输入修改,为后面相加做变换准备x = F.relu(self.bn1(self.conv1(x)), inplace=True)x = self.bn2(self.conv2(x))                             # 完成残差部分的卷积x += identityreturn F.relu(x, inplace=True)                          # 输出卷积单元class ResNet18(nn.Module):def __init__(self, classes_num):super(ResNet18, self).__init__()self.prepare = nn.Sequential(           # 所有的ResNet共有的预处理==》[batch, 64, 56, 56]nn.Conv2d(3, 64, 7, 2, 3),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.MaxPool2d(3, 2, 1))self.layer1 = nn.Sequential(            # layer1有点特别,由于输入输出的channel均是64,故两个CommonBlockCommonBlock(64, 64, 1),CommonBlock(64, 64, 1))self.layer2 = nn.Sequential(            # layer234类似,由于输入输出的channel不同,故一个SpecialBlock,一个CommonBlockSpecialBlock(64, 128, [2, 1]),CommonBlock(128, 128, 1))self.layer3 = nn.Sequential(SpecialBlock(128, 256, [2, 1]),CommonBlock(256, 256, 1))self.layer4 = nn.Sequential(SpecialBlock(256, 512, [2, 1]),CommonBlock(512, 512, 1))self.pool = nn.AdaptiveAvgPool2d(output_size=(1, 1))    # 卷积结束,通过一个自适应均值池化==》 [batch, 512, 1, 1]self.fc = nn.Sequential(                # 最后用于分类的全连接层,根据需要灵活变化nn.Dropout(p=0.5),nn.Linear(512, 256),nn.ReLU(inplace=True),nn.Dropout(p=0.5),nn.Linear(256, classes_num)         # 这个使用CIFAR10数据集,定为10分类)def forward(self, x):x = self.prepare(x)         # 预处理x = self.layer1(x)          # 四个卷积单元x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)x = self.pool(x)            # 池化x = x.reshape(x.shape[0], -1)   # 将x展平,输入全连接层x = self.fc(x)return x

train.py训练部分(使用CIFAR10数据集):

import torch
import visdom
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
from PIL import Image
from matplotlib import pyplot as plt
import numpy as np
from ResNet18 import ResNet18
from torch.nn import CrossEntropyLoss
from torch import optimBATCH_SIZE = 512                        # 超参数batch大小
EPOCH = 30                              # 总共训练轮数
save_path = "./CIFAR10_ResNet18.pth"    # 模型权重参数保存位置
# classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')    # CIFAR10数据集类别
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")                         # 创建GPU运算环境
print(device)data_transform = {                      # 数据预处理"train": transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),"val": transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
}
# 加载数据集,指定训练或测试数据,指定于处理方式
train_data = datasets.CIFAR10(root='./CIFAR10/', train=True, transform=data_transform["train"], download=True)
test_data = datasets.CIFAR10(root='./CIFAR10/', train=False, transform=data_transform["val"], download=True)train_dataloader = torch.utils.data.DataLoader(train_data, BATCH_SIZE, True, num_workers=0)
test_dataloader = torch.utils.data.DataLoader(test_data, BATCH_SIZE, False, num_workers=0)# # 展示图片
# x = 0
# for images, labels in train_data:
#     plt.subplot(3,3,x+1)
#     plt.tight_layout()
#     images = images.numpy().transpose(1, 2, 0)  # 把channel那一维放到最后
#     plt.title(str(classes[labels]))
#     plt.imshow(images)
#     plt.xticks([])
#     plt.yticks([])
#     x += 1
#     if x == 9:
#         break
# plt.show()# 创建一个visdom,将训练测试情况可视化
viz = visdom.Visdom()# 测试函数,传入模型和数据读取接口
def evalute(model, loader):# correct为总正确数量,total为总测试数量correct = 0total = len(loader.dataset)# 取测试数据for x, y in loader:x, y = x.to(device), y.to(device)# validation和test过程不需要反向传播model.eval()with torch.no_grad():out = model(x)       # 计算测试数据的输出logits# 计算出out在第一维度上最大值对应编号,得模型的预测值prediction = out.argmax(dim=1)# 预测正确的数量correctcorrect += torch.eq(prediction, y).float().sum().item()# 最终返回正确率return correct / totalnet = ResNet18()
net.to(device)                                      # 实例化网络模型并送入GPU
net.load_state_dict(torch.load(save_path))          # 使用上次训练权重接着训练
optimizer = optim.Adam(net.parameters(), lr=0.001)  # 定义优化器
loss_function = CrossEntropyLoss()                  # 多分类问题使用交叉熵损失函数best_acc, best_epoch = 0.0, 0                       # 最好准确度,出现的轮数
global_step = 0                                     # 全局的step步数,用于画图
for epoch in range(EPOCH):      running_loss = 0.0                              # 一次epoch的总损失net.train()                                     # 开始训练for step, (images, labels) in enumerate(train_dataloader, start=0):images, labels = images.to(device), labels.to(device)optimizer.zero_grad()outputs = net(images)loss = loss_function(outputs, labels)loss.backward()optimizer.step()running_loss += loss.item()                 # 将一个epoch的损失累加# 打印输出当前训练的进度rate = (step + 1) / len(train_dataloader)a = "*" * int(rate * 50)b = "." * int((1 - rate) * 50)print("\repoch: {} train loss: {:^3.0f}%[{}->{}]{:.3f}".format(epoch+1, int(rate * 100), a, b, loss), end="")# 记录test的lossviz.line([loss.item()], [global_step], win='loss', update='append')# 每次记录之后将横轴x的值加一global_step += 1# 在每一个epoch结束,做一次testif epoch % 1 == 0:# 使用上面定义的evalute函数,测试正确率,传入测试模型net,测试数据集test_dataloadertest_acc = evalute(net, test_dataloader)print("  epoch{} test acc:{}".format(epoch+1, test_acc))# 根据目前epoch计算所得的acc,看看是否需要保存当前状态(即当前的各项参数值)以及迭代周期epoch作为最好情况if test_acc > best_acc:# 保存最好数据best_acc = test_accbest_epoch = epoch# 保存最好的模型参数值状态torch.save(net.state_dict(), save_path)# 记录validation的val_accviz.line([test_acc], [global_step], win='test_acc', update='append')
print("Finish !")

3. 训练测试结果

训练损失:

每一个epoch结束之后的测试:

训练时多次修改超参数,最后经过30次epoch之后的测试准确度达到了0.7471,没有在训练下去也没有明显提升,初学深度神经网络,第一次搭建ResNet,我的数据处理等方面处理的有一定欠缺,大家有好的建议也可以提出来

二、使用PyTorch搭建ResNet34网络

1. ResNet34网络结构


参照ResNet18的搭建,由于34层和18层几乎相同,叠加卷积单元数即可,所以没有写注释,具体可以参考我的ResNet18搭建中的注释,ResNet34的训练部分也可以参照。

2. 实现代码

import torch
import torch.nn as nn
from torch.nn import functional as Fclass CommonBlock(nn.Module):def __init__(self, in_channel, out_channel, stride):super(CommonBlock, self).__init__()self.conv1 = nn.Conv2d(in_channel, out_channel, kernel_size=3, stride=stride, padding=1, bias=False)self.bn1 = nn.BatchNorm2d(out_channel)self.conv2 = nn.Conv2d(out_channel, out_channel, kernel_size=3, stride=stride, padding=1, bias=False)self.bn2 = nn.BatchNorm2d(out_channel)def forward(self, x):identity = xx = F.relu(self.bn1(self.conv1(x)), inplace=True)x = self.bn2(self.conv2(x))x += identityreturn F.relu(x, inplace=True)class SpecialBlock(nn.Module):def __init__(self, in_channel, out_channel, stride):super(SpecialBlock, self).__init__()self.change_channel = nn.Sequential(nn.Conv2d(in_channel, out_channel, kernel_size=1, stride=stride[0], padding=0, bias=False),nn.BatchNorm2d(out_channel))self.conv1 = nn.Conv2d(in_channel, out_channel, kernel_size=3, stride=stride[0], padding=1, bias=False)self.bn1 = nn.BatchNorm2d(out_channel)self.conv2 = nn.Conv2d(out_channel, out_channel, kernel_size=3, stride=stride[1], padding=1, bias=False)self.bn2 = nn.BatchNorm2d(out_channel)def forward(self, x):identity = self.change_channel(x)x = F.relu(self.bn1(self.conv1(x)), inplace=True)x = self.bn2(self.conv2(x))x += identityreturn F.relu(x, inplace=True)class ResNet34(nn.Module):def __init__(self, classes_num):super(ResNet34, self).__init__()self.prepare = nn.Sequential(nn.Conv2d(3, 64, 7, 2, 3),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.MaxPool2d(3, 2, 1))self.layer1 = nn.Sequential(CommonBlock(64, 64, 1),CommonBlock(64, 64, 1),CommonBlock(64, 64, 1))self.layer2 = nn.Sequential(SpecialBlock(64, 128, [2, 1]),CommonBlock(128, 128, 1),CommonBlock(128, 128, 1),CommonBlock(128, 128, 1))self.layer3 = nn.Sequential(SpecialBlock(128, 256, [2, 1]),CommonBlock(256, 256, 1),CommonBlock(256, 256, 1),CommonBlock(256, 256, 1),CommonBlock(256, 256, 1),CommonBlock(256, 256, 1))self.layer4 = nn.Sequential(SpecialBlock(256, 512, [2, 1]),CommonBlock(512, 512, 1),CommonBlock(512, 512, 1))self.pool = nn.AdaptiveAvgPool2d(output_size=(1, 1))self.fc = nn.Sequential(nn.Dropout(p=0.5),nn.Linear(512, 256),nn.ReLU(inplace=True),nn.Dropout(p=0.5),nn.Linear(256, classes_num))def forward(self, x):x = self.prepare(x)x = self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)x = self.pool(x)x = x.reshape(x.shape[0], -1)x = self.fc(x)return x

三、使用PyTorch搭建ResNet50网络

看过我之前ResNet18和ResNet34搭建的朋友可能想着可不可以把搭建18和34层的方法直接用在50层以上的ResNet的搭建中,我也尝试过。但是ResNet50以上的网络搭建不像是18到34层只要简单修改卷积单元数目就可以完成,ResNet50以上的三种网络都是一个样子,只是层数不同,所以完全可以将34到50层作为一个搭建分水岭。
加上我初学PyTorch和深度神经网络,对于采用BasicBlock和BottleNeck的高效率构建还不是很懂,所以这里给出了类似前两种ResNet的简单暴力堆叠网络层的构建方法

1. ResNet50网络结构

所有不同层数的ResNet:

这里给出了我认为比较详细的ResNet50网络具体参数和执行流程图:

2. 实现代码

model.py模型部分:

import torch
import torch.nn as nn
from torch.nn import functional as F"""
这里的ResNet50的搭建是暴力形式,直接累加完成搭建,没采用BasicBlock和BottleNeck
第一个DownSample类,用于定义shortcut的模型函数,完成两个layer之间虚线的shortcut,负责layer1虚线的升4倍channel以及其他layer虚线的升2倍channel
观察每一个layer的虚线处升channel仅仅是升channel前后的数量不同以及stride不同,对于kernel_size和padding都分别是1和0,不作为DownSample网络类的模型参数
参数in_channel即是升之前的通道数, out_channel即是升之后的通道数, stride即是每一次升channel不同的stride步长,对于layer1升通道的stride=1,其他layer升通道的stride=2,注意不同
"""
"""
运行时一定要注意:
本网络中的ResNet50类中forward函数里面:layer1_shortcut1.to('cuda:0');layer2_shortcut1.to('cuda:0')等语句,是将实例化的DownSample
网络模型放到train.py训练脚本中定义的GPU同一环境下,不加此句一般会如下报错:
Input type (torch.cuda.FloatTensor) and weight type (torch.FloatTensor) should be the same
"""class DownSample(nn.Module):def __init__(self, in_channel, out_channel, stride):            # 传入下采样的前后channel数以及stride步长super(DownSample, self).__init__()                          # 继承父类self.down = nn.Sequential(                                  # 定义一个模型容器downnn.Conv2d(in_channel, out_channel, kernel_size=1, stride=stride, padding=0, bias=False),    # 负责虚线shortcut的唯一且重要的一次卷积nn.BatchNorm2d(out_channel),                            # 在卷积和ReLU非线性激活之间,添加BatchNormalizationnn.ReLU(inplace=True)                                   # shortcut最后加入一个激活函数,置inplace=True原地操作,节省内存)def forward(self, x):out = self.down(x)                                          # 前向传播函数仅仅完成down这个容器的操作return out"""
第一个ResNet50类,不使用BottleNeck单元完成ResNet50层以上的搭建,直接使用forward再加上前面的DownSample模型类函数,指定ResNet50所有参数构建模型
"""class ResNet50(nn.Module):def __init__(self, classes_num):                                # ResNet50仅传一个分类数目,将涉及的所有数据写死,具体数据可以参考下面的图片super(ResNet50, self).__init__()# 在进入layer1234之间先进行预处理,主要是一次卷积一次池化,从[batch, 3, 224, 224] => [batch, 64, 56, 56]self.pre = nn.Sequential(# 卷积channel从原始数据的3通道,采用64个卷积核,升到64个channel,卷积核大小、步长、padding均固定nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False),nn.BatchNorm2d(64),                                     # 卷积后紧接一次BatchNormalizationnn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=3, stride=2, padding=1)        # 预处理最后的一次最大池化操作,数据固定)"""每一个layer的操作分为使用一次的first,和使用多次的next组成,first负责每个layer的第一个单元(有虚线)的三次卷积,next负责剩下单元(直连)的三次卷积"""# --------------------------------------------------------------self.layer1_first = nn.Sequential(nn.Conv2d(64, 64, kernel_size=1, stride=1, padding=0, bias=False),      # layer1_first第一次卷积保持channel不变,和其他layer的first区别nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1, bias=False),      # layer1_first第二次卷积stride和其他layer_first的stride不同nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.Conv2d(64, 256, kernel_size=1, stride=1, padding=0, bias=False),     # layer1_first第三次卷积和其他layer一样,channel升4倍nn.BatchNorm2d(256)                                         # 注意最后一次卷积结束不加ReLU激活函数)self.layer1_next = nn.Sequential(nn.Conv2d(256, 64, kernel_size=1, stride=1, padding=0, bias=False),     # layer1_next的第一次卷积负责将channel减少,减少训练参数量nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.Conv2d(64, 256, kernel_size=1, stride=1, padding=0, bias=False),     # layer1_next的最后一次卷积负责将channel增加至可以与shortcut相加nn.BatchNorm2d(256))# --------------------------------------------------------------    # layer234操作基本相同,这里仅介绍layer2self.layer2_first = nn.Sequential(nn.Conv2d(256, 128, kernel_size=1, stride=1, padding=0, bias=False),    # 与layer1_first第一次卷积不同,需要降channel至1/2nn.BatchNorm2d(128),nn.ReLU(inplace=True),nn.Conv2d(128, 128, kernel_size=3, stride=2, padding=1, bias=False),    # 注意这里的stride=2与layer34相同,与layer1区别nn.BatchNorm2d(128),nn.ReLU(inplace=True),nn.Conv2d(128, 512, kernel_size=1, stride=1, padding=0, bias=False),    # 再次升channelnn.BatchNorm2d(512))self.layer2_next = nn.Sequential(nn.Conv2d(512, 128, kernel_size=1, stride=1, padding=0, bias=False),    # 负责循环普通的操作nn.BatchNorm2d(128),nn.ReLU(inplace=True),nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(128),nn.ReLU(inplace=True),nn.Conv2d(128, 512, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(512))# --------------------------------------------------------------self.layer3_first = nn.Sequential(nn.Conv2d(512, 256, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(256),nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, stride=2, padding=1, bias=False),nn.BatchNorm2d(256),nn.ReLU(inplace=True),nn.Conv2d(256, 1024, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(1024))self.layer3_next = nn.Sequential(nn.Conv2d(1024, 256, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(256),nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(256),nn.ReLU(inplace=True),nn.Conv2d(256, 1024, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(1024))# --------------------------------------------------------------self.layer4_first = nn.Sequential(nn.Conv2d(1024, 512, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, stride=2, padding=1, bias=False),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 2048, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(2048))self.layer4_next = nn.Sequential(nn.Conv2d(2048, 512, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 2048, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(2048))self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))     # 经过最后的自适应均值池化为[batch, 2048, 1, 1]# 定义最后的全连接层self.fc = nn.Sequential(nn.Dropout(p=0.5),                  # 以0.5的概率失活神经元nn.Linear(2048 * 1 * 1, 1024),      # 第一个全连接层nn.ReLU(inplace=True),nn.Dropout(p=0.5),nn.Linear(1024, classes_num)        # 第二个全连接层,输出类结果)"""forward()前向传播函数负责将ResNet类中定义的网络层复用,再与上面的DownSample类完美组合"""def forward(self, x):out = self.pre(x)                                       # 对输入预处理,输出out = [batch, 64, 56, 56]"""每一层layer操作由两个部分组成,第一个是带有虚线的卷积单元,其他的是循环完成普通的shortcut为直连的卷积单元"""layer1_shortcut1 = DownSample(64, 256, 1)               # 使用DownSample实例化一个网络模型layer1_shortcut1,参数即是虚线处升channel数据,注意stride=1layer1_shortcut1.to('cuda:0')layer1_identity1 = layer1_shortcut1(out)                # 调用layer1_shortcut1对卷积单元输入out计算虚线处的identity,用于后面与卷积单元输出相加out = self.layer1_first(out)                            # 调用layer1_first完成layer1的第一个特殊的卷积单元out = F.relu(out + layer1_identity1, inplace=True)      # 将identity与卷积单元输出相加,经过relu激活函数for i in range(2):                                      # 使用循环完成后面几个相同输入输出相同操作的卷积单元layer_identity = out                                # 直接直连identity等于输入out = self.layer1_next(out)                         # 输入经过普通卷积单元out = F.relu(out + layer_identity, inplace=True)    # 两路结果相加,再经过激活函数# --------------------------------------------------------------后面layer234都是类似的,这里仅介绍layer2layer2_shortcut1 = DownSample(256, 512, 2)              # 注意后面layer234输入输出channel不同,stride=2都是如此layer2_shortcut1.to('cuda:0')layer2_identity1 = layer2_shortcut1(out)out = self.layer2_first(out)out = F.relu(out + layer2_identity1, inplace=True)      # 完成layer2的第一个卷积单元for i in range(3):                                      # 循环执行layer2剩下的其他卷积单元layer_identity = outout = self.layer2_next(out)out = F.relu(out + layer_identity, inplace=True)# --------------------------------------------------------------layer3_shortcut1 = DownSample(512, 1024, 2)layer3_shortcut1.to('cuda:0')layer3_identity1 = layer3_shortcut1(out)out = self.layer3_first(out)out = F.relu(out + layer3_identity1, inplace=True)for i in range(5):layer_identity = outout = self.layer3_next(out)out = F.relu(out + layer_identity, inplace=True)# --------------------------------------------------------------layer4_shortcut1 = DownSample(1024, 2048, 2)layer4_shortcut1.to('cuda:0')layer4_identity1 = layer4_shortcut1(out)out = self.layer4_first(out)out = F.relu(out + layer4_identity1, inplace=True)for i in range(2):layer_identity = outout = self.layer4_next(out)out = F.relu(out + layer_identity, inplace=True)# 最后一个全连接层out = self.avg_pool(out)                # 经过最后的自适应均值池化为[batch, 2048, 1, 1]out = out.reshape(out.size(0), -1)      # 将卷积输入[batch, 2048, 1, 1]展平为[batch, 2048*1*1]out = self.fc(out)                  # 经过最后一个全连接单元,输出分类outreturn out

ResNet50的训练可以参照前面ResNet18搭建中的训练和测试部分:

经过手写ResNet50网络模型的暴力搭建,我认识到了要想把ResNet及其其他复杂网络的搭建,前提必须要把模型整个流程环节全部弄清楚
例如,ResNet50里面每一次的shortcut里面的升维操作的in_channel,out_channel,kernel_size,stride,padding的参数大小变化
每一个卷积单元具体参数都是什么样的,如何才能最大化简化代码;
还有就是搭建复杂的网络模型中,一定要做到步步为营,一步步搭建并检验,每一步都要理解有理有据,最后才能将整个网络搭建起来
还有一个意外收获就是在训练过程中,发现了这样的报错:
Input type (torch.cuda.FloatTensor) and weight type (torch.FloatTensor) should be the same
原来是因为输入的数据类型为torch.cuda.FloatTensor,说明输入数据在GPU中。模型参数的数据类型为torch.FloatTensor,说明模型还在CPU
故在ResNet50的forward()函数中对实例化的DownSample网络添加到和train.py对ResNet50实例化的网络模型的同一个GPU下,解决了错误

四、使用PyTorch搭建ResNet101、ResNet152网络

参照前面ResNet50的搭建,由于50层以上几乎相同,叠加卷积单元数即可,所以没有写注释。
ResNet101和152的搭建注释可以参照我的ResNet50搭建中的注释
ResNet101和152的训练可以参照我的ResNet18搭建中的训练部分

ResNet101和152可以依旧参照ResNet50的网络图片:

1. 网络结构

2. 实现代码

(1)ResNet101的model.py模型:

import torch
import torch.nn as nn
from torch.nn import functional as Fclass DownSample(nn.Module):def __init__(self, in_channel, out_channel, stride):super(DownSample, self).__init__()self.down = nn.Sequential(nn.Conv2d(in_channel, out_channel, kernel_size=1, stride=stride, padding=0, bias=False),nn.BatchNorm2d(out_channel),nn.ReLU(inplace=True))def forward(self, x):out = self.down(x)return outclass ResNet101(nn.Module):def __init__(self, classes_num):            # 指定分类数super(ResNet101, self).__init__()self.pre = nn.Sequential(nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=3, stride=2, padding=1))# --------------------------------------------------------------------self.layer1_first = nn.Sequential(nn.Conv2d(64, 64, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.Conv2d(64, 256, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(256))self.layer1_next = nn.Sequential(nn.Conv2d(256, 64, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.Conv2d(64, 256, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(256))# --------------------------------------------------------------------self.layer2_first = nn.Sequential(nn.Conv2d(256, 128, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(128),nn.ReLU(inplace=True),nn.Conv2d(128, 128, kernel_size=3, stride=2, padding=1, bias=False),nn.BatchNorm2d(128),nn.ReLU(inplace=True),nn.Conv2d(128, 512, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(512))self.layer2_next = nn.Sequential(nn.Conv2d(512, 128, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(128),nn.ReLU(inplace=True),nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(128),nn.ReLU(inplace=True),nn.Conv2d(128, 512, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(512))# --------------------------------------------------------------------self.layer3_first = nn.Sequential(nn.Conv2d(512, 256, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(256),nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, stride=2, padding=1, bias=False),nn.BatchNorm2d(256),nn.ReLU(inplace=True),nn.Conv2d(256, 1024, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(1024))self.layer3_next = nn.Sequential(nn.Conv2d(1024, 256, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(256),nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(256),nn.ReLU(inplace=True),nn.Conv2d(256, 1024, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(1024))# --------------------------------------------------------------------self.layer4_first = nn.Sequential(nn.Conv2d(1024, 512, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, stride=2, padding=1, bias=False),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 2048, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(2048))self.layer4_next = nn.Sequential(nn.Conv2d(2048, 512, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 2048, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(2048))# --------------------------------------------------------------------self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))self.fc = nn.Sequential(nn.Dropout(p=0.5),nn.Linear(2048 * 1 * 1, 1000),nn.ReLU(inplace=True),nn.Dropout(p=0.5),nn.Linear(1000, classes_num))def forward(self, x):out = self.pre(x)# --------------------------------------------------------------------layer1_shortcut = DownSample(64, 256, 1)layer1_shortcut.to('cuda:0')layer1_identity = layer1_shortcut(out)out = self.layer1_first(out)out = F.relu(out + layer1_identity, inplace=True)for i in range(2):identity = outout = self.layer1_next(out)out = F.relu(out + identity, inplace=True)# --------------------------------------------------------------------layer2_shortcut = DownSample(256, 512, 2)layer2_shortcut.to('cuda:0')layer2_identity = layer2_shortcut(out)out = self.layer2_first(out)out = F.relu(out + layer2_identity, inplace=True)for i in range(3):identity = outout = self.layer2_next(out)out = F.relu(out + identity, inplace=True)# --------------------------------------------------------------------layer3_shortcut = DownSample(512, 1024, 2)layer3_shortcut.to('cuda:0')layer3_identity = layer3_shortcut(out)out = self.layer3_first(out)out = F.relu(out + layer3_identity, inplace=True)for i in range(22):identity = outout = self.layer3_next(out)out = F.relu(out + identity, inplace=True)# --------------------------------------------------------------------layer4_shortcut = DownSample(1024, 2048, 2)layer4_shortcut.to('cuda:0')layer4_identity = layer4_shortcut(out)out = self.layer4_first(out)out = F.relu(out + layer4_identity, inplace=True)for i in range(2):identity = outout = self.layer4_next(out)out = F.relu(out + identity, inplace=True)# --------------------------------------------------------------------out = self.avg_pool(out)out = out.reshape(out.size(0), -1)out = self.fc(out)return out

(2) ResNet152的model.py模型:

import torch
import torch.nn as nn
from torch.nn import functional as Fclass DownSample(nn.Module):def __init__(self, in_channel, out_channel, stride):super(DownSample, self).__init__()self.down = nn.Sequential(nn.Conv2d(in_channel, out_channel, kernel_size=1, stride=stride, padding=0, bias=False),nn.BatchNorm2d(out_channel),nn.ReLU(inplace=True))def forward(self, x):out = self.down(x)return outclass ResNet152(nn.Module):def __init__(self, classes_num):            # 指定了分类数目super(ResNet152, self).__init__()self.pre = nn.Sequential(nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=3, stride=2, padding=1))# -----------------------------------------------------------------------self.layer1_first = nn.Sequential(nn.Conv2d(64, 64, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.Conv2d(64, 256, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(256))self.layer1_next = nn.Sequential(nn.Conv2d(256, 64, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.Conv2d(64, 256, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(256))# -----------------------------------------------------------------------self.layer2_first = nn.Sequential(nn.Conv2d(256, 128, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(128),nn.ReLU(inplace=True),nn.Conv2d(128, 128, kernel_size=3, stride=2, padding=1, bias=False),nn.BatchNorm2d(128),nn.ReLU(inplace=True),nn.Conv2d(128, 512, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(512))self.layer2_next = nn.Sequential(nn.Conv2d(512, 128, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(128),nn.ReLU(inplace=True),nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(128),nn.ReLU(inplace=True),nn.Conv2d(128, 512, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(512))# -----------------------------------------------------------------------self.layer3_first = nn.Sequential(nn.Conv2d(512, 256, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(256),nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, stride=2, padding=1, bias=False),nn.BatchNorm2d(256),nn.ReLU(inplace=True),nn.Conv2d(256, 1024, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(1024))self.layer3_next = nn.Sequential(nn.Conv2d(1024, 256, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(256),nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(256),nn.ReLU(inplace=True),nn.Conv2d(256, 1024, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(1024))# -----------------------------------------------------------------------self.layer4_first = nn.Sequential(nn.Conv2d(1024, 512, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, stride=2, padding=1, bias=False),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 2048, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(2048))self.layer4_next = nn.Sequential(nn.Conv2d(2048, 512, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(512),nn.ReLU(inplace=True),nn.Conv2d(512, 2048, kernel_size=1, stride=1, padding=0, bias=False),nn.BatchNorm2d(2048))# -----------------------------------------------------------------------self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))self.fc = nn.Sequential(nn.Dropout(p=0.5),nn.Linear(2048 * 1 * 1, 1000),nn.ReLU(inplace=True),nn.Dropout(p=0.5),nn.Linear(1000, classes_num))def forward(self, x):out = self.pre(x)# -----------------------------------------------------------------------layer1_shortcut = DownSample(64, 256, 1)# layer1_shortcut.to('cuda:0')layer1_identity = layer1_shortcut(out)out = self.layer1_first(out)out = F.relu(out + layer1_identity, inplace=True)for i in range(2):identity = outout = self.layer1_next(out)out = F.relu(out + identity, inplace=True)# -----------------------------------------------------------------------layer2_shortcut = DownSample(256, 512, 2)# layer2_shortcut.to('cuda:0')layer2_identity = layer2_shortcut(out)out = self.layer2_first(out)out = F.relu(out + layer2_identity, inplace=True)for i in range(7):identity = outout = self.layer2_next(out)out = F.relu(out + identity, inplace=True)# -----------------------------------------------------------------------layer3_shortcut = DownSample(512, 1024, 2)# layer3_shortcut.to('cuda:0')layer3_identity = layer3_shortcut(out)out = self.layer3_first(out)out = F.relu(out + layer3_identity, inplace=True)for i in range(35):identity = outout = self.layer3_next(out)out = F.relu(out + identity, inplace=True)# -----------------------------------------------------------------------layer4_shortcut = DownSample(1024, 2048, 2)# layer4_shortcut.to('cuda:0')layer4_identity = layer4_shortcut(out)out = self.layer4_first(out)out = F.relu(out + layer4_identity, inplace=True)for i in range(2):identity = outout = self.layer4_next(out)out = F.relu(out + identity, inplace=True)# -----------------------------------------------------------------------out = self.avg_pool(out)out = out.reshape(out.size(0), -1)out = self.fc(out)return out

【深度学习】基于PyTorch搭建ResNet18、ResNet34、ResNet50、ResNet101、ResNet152网络相关推荐

  1. 深度学习必备书籍——《Python深度学习 基于Pytorch》

    作为一名机器学习|深度学习的博主,想和大家分享几本深度学习的书籍,让大家更快的入手深度学习,成为AI达人!今天给大家介绍的是:<Python深度学习 基于Pytorch> 文章目录 一.背 ...

  2. 【深度学习】——pytorch搭建模型及相关模型

    目录 1.搭建模型的流程 1)步骤 2)完整代码--手写minist数据集为例(这里使用的数据集是自带的) 2.搭建模型的四种方法 1)方法一--利用nn.Sequential() 2)方法二--利用 ...

  3. python深度学习基于pytorch代码1.1生成Numpy数组

    import numpy as np lst1 = [3.14, 2.17, 0, 1, 2] nd1 = np.array(lst1) print(nd1) print(type(nd1)) lst ...

  4. Facebook 发布深度学习工具包 PyTorch Hub,让论文复现变得更容易

    近日,PyTorch 社区发布了一个深度学习工具包 PyTorchHub, 帮助机器学习工作者更快实现重要论文的复现工作.PyTorchHub 由一个预训练模型仓库组成,专门用于提高研究工作的复现性以 ...

  5. 基于pytorch搭建神经网络的花朵种类识别(深度学习)

    基于pytorch搭建神经网络的花朵种类识别(深度学习) 文章目录 基于pytorch搭建神经网络的花朵种类识别(深度学习) 一.知识点 1.特征提取.神经元逐层判断 2.中间层(隐藏层) 3.学习权 ...

  6. 《深度学习之PyTorch物体检测实战》—读书笔记

    随书代码 物体检测与PyTorch 深度学习 为了赋予计算机以人类的理解能力与逻辑思维,诞生了人工智能(Artificial Intelligence, AI)这一学科.在实现人工智能的众多算法中,机 ...

  7. (d2l-ai/d2l-zh)《动手学深度学习》pytorch 笔记(2)前言(介绍各种机器学习问题)以及数据操作预备知识Ⅰ

    开源项目地址:d2l-ai/d2l-zh 教材官网:https://zh.d2l.ai/ 书介绍:https://zh-v2.d2l.ai/ 笔记基于2021年7月26日发布的版本,书及代码下载地址在 ...

  8. [深度学习] 分布式Pytorch介绍(三)

    [深度学习] 分布式模式介绍(一) [深度学习] 分布式Tensorflow介绍(二) [深度学习] 分布式Pytorch介绍(三) [深度学习] 分布式Horovod介绍(四)  一  Pytorc ...

  9. 深度学习三(PyTorch物体检测实战)

    深度学习三(PyTorch物体检测实战) 文章目录 深度学习三(PyTorch物体检测实战) 1.网络骨架:Backbone 1.1.神经网络基本组成 1.1.1.卷积层 1.1.2.激活函数层 1. ...

  10. 深度学习二(Pytorch物体检测实战)

    深度学习二(Pytorch物体检测实战) 文章目录 深度学习二(Pytorch物体检测实战) 1.PyTorch基础 1.1.基本数据结构:Tensor 1.1.1.Tensor数据类型 1.1.2. ...

最新文章

  1. MySQL面试题 | 附答案解析(五)
  2. JavaSE 基础面试题
  3. mongod无法启动
  4. intel i218v千兆网卡 linux驱动,适用于英特尔® 千兆位以太网网络连接的 Linux* 基础驱动程序...
  5. Openfire on Centos7
  6. 再问数据中台 - 数据中台和业务中台服务有什么区别 应该如何去界定和划分?
  7. 字节跳动笔试题:1. 小于N的质数数量;2. 逆时针二维数组;3. 判断a+bc
  8. Python 入门,看这篇就够了
  9. RTMPDump源代码分析 0: 主要函数调用分析
  10. 压力测试 Monkey 应用程序无响应ANR Application No Response(转)
  11. rs422/rs485通信接口原理图
  12. 求多项式的值之polyval 和polyvalm
  13. WARNING: There was an error checking the latest version of pip.
  14. 计算生活中常见的Π值
  15. oracle创建bigfile表空间,表空间管理之bigfile表空间设置
  16. V-Play 使用粗体验
  17. openwrt 遍译php_OpenWrt CI 在线集成编译环境使用教程
  18. 端口映射/dmz主机_在DMZ中使用Notes / Domino SMTP
  19. 正确涂CPU硅脂的方法要这样
  20. 8266+DS3231时钟之语音MP3-TF-16P模块使用【五】

热门文章

  1. XShell4 连接ubuntu时SSH服务器拒绝了密码解决办法
  2. 蓝牙耳机测试软件apk_Bose Connect(蓝牙耳机控制器)
  3. LiveReload for mac 软件下载
  4. GIT版本控制(详解)
  5. python 回调函数的使用_Python回调函数用法实例详解
  6. 集成电路产业的芯片设计、芯片制造、封装测试
  7. Java写的答题助手项目分析与总结(三)
  8. 【2022年的STM32】 02-STM32总览
  9. SAP 软件PS 模块系统操作之一工作分解结构(WBS)的创建
  10. 西北大学计算机课表,西北大学课表