深度学习基础--ResNet网络的讲解,ResNet50的复现(pytorch)以及用复现的ResNet50做鸟类图像分类

  • 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
  • 🍖 原作者:K同学啊

前言

  • 如果说最经典的神经网络,ResNet肯定是一个,这篇文章是本人学习ResNet的学习笔记,并且用pytorch复现了ResNet50,后面用它做了一个鸟类图像分类demo
  • 欢迎收藏 + 关注,本人将会持续更新

文章目录

  • ResNet网络讲解
    • 什么是ResNet?
    • ResNet神经网络突出点
    • 为什么采用残差连接
      • 模型退化、梯度消失、梯度爆炸
      • 解决方法
    • 残差网络
  • ResNet-50复现
    • 1、导入数据
      • 1、导入库
      • 2、查看数据信息和导入数据
      • 3、展示数据
      • 4、数据导入
      • 5、数据划分
      • 6、动态加载数据
    • 2、构建ResNet-50网络
    • 3、模型训练
      • 1、构建训练集
      • 2、构建测试集
      • 3、设置超参数
    • 4、模型训练
    • 5、结果可视化
  • 参考资料

ResNet网络讲解

什么是ResNet?

ResNet网络是CNN的经典网络架构,是有大神何凯明提出的,主要为了解决随着网络的加深而引起的“ 退化 ”问题,主要用于图像分类。

可以说在如今的CV领域里面,大部分网络结构都有参考ResNet网络思想,无论是在图像分类、目标检测、图像识别上,甚至在Transformer网络模型中,也融合了ResNet网络的思想。

ResNet神经网络突出点

  • 网络结构超过1000层
    • ❔ ❔ 超过1000层网络结构不是很容易么? 小编在学习深度学习的时候,曾经遇到过这样一个问题,有时候加深网络结构,反而在准确率、损失率上更差,这种现象称为模型“ 退化 ”现象,而ResNet的残差连接可以保证下一层的输出不会比输入差,从而可以加深网络结构。
  • 提出残差模块(residual):这个是ResNet的核心;
  • 采用大量的归一化在卷积层与激活函数之间.

为什么采用残差连接

模型退化、梯度消失、梯度爆炸

  • 👉 模型退化:指随着网络层数的加深,其效果出现下降趋势,不如层数少的情况。如论文中图示,56层效果不如20层效果;

在这里插入图片描述

  • 👉 梯度消失:这个是指随着网络层数的增加,反向传播,梯度更新的时候可能会造成前面几层的梯度很小、接近于0,这就会导致权重的更新会特别慢,效率低下。
  • 👉 梯度爆炸:指随着网络层数的增加,在反向传播的时候,梯度变得非常大,从而在更新权重的时候,权重值发生大幅度变化,这可能导致网络不稳定,甚至是无法收敛

解决方法

  • 梯度消失、梯度爆炸:在数据预处理和网络层之间加入:BN层(Batch Normalization),从而对数据进行归一化
  • 模型退化:采用残差连接,如论文图,随着网络层数的增加,损失率更低了。

在这里插入图片描述

残差网络

在讲述前,这里先讲述一下恒等映射的概念:

  • 恒等映射核心是复制,就是复制网络层,什么也不干。

可以这么理解:假设在一种网络A的后面添加几层形成新的网络B,如果A的输出经过新的层级变成B的输出没有发送变化,那么就可以说网络A和网络B的错误率是相等的,这样就确保了加深的网络层不会比之前的网络层效果差。


resent网络说明了,更深的网络结构可以有更好的效果,而解决这个的核心就是残差连接,网络结果如图所示:

在这里插入图片描述

上图就是何凯明提出的残差结构,这种结构实现了恒等映射,网络层的输出由两大模块组成:

  • 其一:正常的卷积层;
  • 其二:有一个分支输出到连接上,这个输出值就是输入的值;

最终结果就是:卷积层输出+分支输出,数学公式如下:

在这里插入图片描述

其中F(x)是卷积层的输出,x是分支的输入值。

极端情况:F(x)的网络层中,所有参数都为0,那么H(x)就是恒等映射。这样就确保了最后的错误率不会因为网络层的增加而导致变大


在ResNet中有两个不同的ResNet模块,如图所示:

在这里插入图片描述

左边

  • 有两层残差单元,输出通道都是3*3
  • 使用情况:用于较浅的ResNet网络。

右边

  • 三层残差单元,称为blottlenck模块,作用是:现用1*1卷积进行降维,后用3*3卷积进行特征特权,最后用1*1卷积恢复原来的维度,这个可以很好的减少参数个数,用于较深的神经网络

下图参考一个csdn大神笔记图

在这里插入图片描述

CNN参数计算公式:卷积核尺寸 * 卷积核速度 * 卷积核组数 == 卷积核尺寸 * 输入特征矩阵深度 * 输出矩阵深度。

ResNet经典的网络结构有ResNet-50,ResNet-101等,本文将用pytorch复现ResNet-50,并用其做一个简单的实验–鸟类图片分类

ResNet-50网络结果如下:

在这里插入图片描述

ResNet-50复现

1、导入数据

1、导入库

import torch  
import torch.nn as nn
import torchvision 
import numpy as np 
import os, PIL, pathlib 

# 设置设备
device = "cuda" if torch.cuda.is_available() else "cpu"

device 
'cuda'

2、查看数据信息和导入数据

数据目录有两个文件:一个数据文件,一个权重。

data_dir = "./data/bird_photos"

data_dir = pathlib.Path(data_dir)

# 类别数量
classnames = [str(path).split('/')[0] for path in os.listdir(data_dir)]

classnames
['Bananaquit', 'Black Skimmer', 'Black Throated Bushtiti', 'Cockatoo']

3、展示数据

import matplotlib.pylab as plt  
from PIL import Image 

# 获取文件名称
data_path_name = "./data/bird_photos/Bananaquit/"
data_path_list = [f for f in os.listdir(data_path_name) if f.endswith(('jpg', 'png'))]

# 创建画板
fig, axes = plt.subplots(2, 8, figsize=(16, 6))

for ax, img_file in zip(axes.flat, data_path_list):
    path_name = os.path.join(data_path_name, img_file)
    img = Image.open(path_name) # 打开
    # 显示
    ax.imshow(img)
    ax.axis('off')
    
plt.show()


在这里插入图片描述

4、数据导入

from torchvision import transforms, datasets 

# 数据统一格式
img_height = 224
img_width = 224 

data_tranforms = transforms.Compose([
    transforms.Resize([img_height, img_width]),
    transforms.ToTensor(),
    transforms.Normalize(   # 归一化
        mean=[0.485, 0.456, 0.406],
        std=[0.229, 0.224, 0.225] 
    )
])

# 加载所有数据
total_data = datasets.ImageFolder(root="./data/bird_photos", transform=data_tranforms)

5、数据划分

# 大小 8 : 2
train_size = int(len(total_data) * 0.8)
test_size = len(total_data) - train_size 

train_data, test_data = torch.utils.data.random_split(total_data, [train_size, test_size])

6、动态加载数据

batch_size = 32 

train_dl = torch.utils.data.DataLoader(
    train_data,
    batch_size=batch_size,
    shuffle=True
)

test_dl = torch.utils.data.DataLoader(
    test_data,
    batch_size=batch_size,
    shuffle=False
)
# 查看数据维度
for data, labels in train_dl:
    print("data shape[N, C, H, W]: ", data.shape)
    print("labels: ", labels)
    break
data shape[N, C, H, W]:  torch.Size([32, 3, 224, 224])
labels:  tensor([0, 1, 0, 1, 2, 1, 1, 0, 2, 2, 1, 2, 1, 3, 1, 2, 2, 2, 2, 1, 2, 1, 2, 2,
        0, 3, 3, 3, 3, 2, 3, 3])

2、构建ResNet-50网络

在这里插入图片描述

import torch.nn.functional as F

# 定义残差模块一,这个用于处理输入和输出通道一样的情况
'''  
卷积核大小:1       3       1
核心特点:
    尺寸不变:输入和输出的尺寸保持一致。 
    没有下采样:没有使用步长大于1的卷积操作,因此没有改变特征图的空间尺寸
'''
class Identity_block(nn.Module):
    def __init__(self, in_channels, kernel_size, filters):
        super(Identity_block, self).__init__()
        
        # 输出通道
        filter1, filter2, filter3 = filters
        
        # 卷积层一
        self.conv1 = nn.Conv2d(in_channels, filter1, kernel_size=1, stride=1)
        self.bn1 = nn.BatchNorm2d(filter1)
        
        # 卷积层2
        self.conv2 = nn.Conv2d(filter1, filter2, kernel_size=kernel_size, padding=1)   # 通过卷积输入输出公式发现,padding=1,可以保证输入和输出尺寸相同
        self.bn2 = nn.BatchNorm2d(filter2)
        
        # 卷积层3
        self.conv3 = nn.Conv2d(filter2, filter3, kernel_size=1, stride=1)
        self.bn3 = nn.BatchNorm2d(filter3)
        
    def forward(self, x):
        # 记录原始值
        xx = x
        x = F.relu(self.bn1(self.conv1(x)))
        x = F.relu(self.bn2(self.conv2(x)))
        x = self.bn3(self.conv3(x))
        # 残差连接,输入、输出维度不变
        x += xx
        x = F.relu(x)
        
        return x 
    
# 定义卷积模块二:用于处理输入和输出不一样的情况
'''  
* 卷积核还是:1 3 1
* stride=2
* 这里的分支是采用一个Conv2D,和一个归一化BN层,也是为了处理数据维度吧, 这种维度的变化,可以用ai举例子

核心特点:
    尺寸变化,stride=2降维
'''
class ConvBlock(nn.Module):
    def __init__(self, in_channels, kernel_size, filters, stride=2):
        super(ConvBlock, self).__init__()
        
        filter1, filter2, filter3= filters
        
        # 卷积层1
        self.conv1 = nn.Conv2d(in_channels, filter1, kernel_size=1, stride=stride)
        self.bn1 = nn.BatchNorm2d(filter1)
        
        # 卷积2
        self.conv2 = nn.Conv2d(filter1, filter2, kernel_size=kernel_size, padding=1) # 需要维持维度不变
        self.bn2 = nn.BatchNorm2d(filter2)
        
        # 卷积3
        self.conv3 = nn.Conv2d(filter2, filter3, kernel_size=1, stride=1)  # stride = 1,维持通道不变
        self.bn3 = nn.BatchNorm2d(filter3)
        
        # 用于匹配维度的shortcut卷积,这个就是上面Identity_block的x分支
        self.shortcut = nn.Conv2d(in_channels, filter3, kernel_size=1, stride=stride)
        self.shortcut_bn = nn.BatchNorm2d(filter3)
        
    def forward(self, x):
        xx = x
        x = F.relu(self.bn1(self.conv1(x)))
        x = F.relu(self.bn2(self.conv2(x)))
        x = self.bn3(self.conv3(x))
        
        temp = self.shortcut_bn(self.shortcut(xx))
        
        x += temp
        
        x = F.relu(x)
        
        return x 
        
# 定义ResNet50
class ResNet50(nn.Module):
    def __init__(self, classes):   # 类别数量
        super().__init__()
        
        # 头顶
        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3)
        self.bn1 = nn.BatchNorm2d(64)
        self.max_pool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        
        # 第一部分
        self.part1_1 = ConvBlock(64, 3, [64, 64, 256], stride=1)
        self.part1_2 = Identity_block(256, 3, [64, 64, 256])
        self.part1_3 = Identity_block(256, 3, [64, 64, 256])
        
        # 第二部分
        self.part2_1 = ConvBlock(256, 3, [128, 128, 512])
        self.part2_2 = Identity_block(512, 3, [128, 128, 512])
        self.part2_3 = Identity_block(512, 3, [128, 128, 512])
        self.part2_4 = Identity_block(512, 3, [128, 128, 512])
        
        # 第三部分
        self.part3_1 = ConvBlock(512, 3, [256, 256, 1024])
        self.part3_2 = Identity_block(1024, 3, [256, 256, 1024])
        self.part3_3 = Identity_block(1024, 3, [256, 256, 1024])
        self.part3_4 = Identity_block(1024, 3, [256, 256, 1024])
        self.part3_5 = Identity_block(1024, 3, [256, 256, 1024])
        self.part3_6 = Identity_block(1024, 3, [256, 256, 1024])
        
        # 第四部分
        self.part4_1 = ConvBlock(1024, 3, [512, 512, 2048])
        self.part4_2 = Identity_block(2048, 3, [512, 512, 2048])
        self.part4_3 = Identity_block(2048, 3, [512, 512, 2048])
        
        # 平均池化
        self.avg_pool = nn.AvgPool2d(kernel_size=7)
        
        # 全连接
        self.fn1 = nn.Linear(2048, classes)
        
    def forward(self, x):
        # 头部
        x = F.relu(self.bn1(self.conv1(x)))
        x = self.max_pool(x)
        
        x = self.part1_1(x)
        x = self.part1_2(x)
        x = self.part1_3(x)
        
        x = self.part2_1(x)
        x = self.part2_2(x)
        x = self.part2_3(x)
        x = self.part2_4(x)
        
        x = self.part3_1(x)
        x = self.part3_2(x)
        x = self.part3_3(x)
        x = self.part3_4(x)
        x = self.part3_5(x)
        x = self.part3_6(x)
        
        x = self.part4_1(x)
        x = self.part4_2(x)
        x = self.part4_3(x)
        
        x = self.avg_pool(x)
        
        x = x.view(x.size(0), -1)  # 扁平化
        x = self.fn1(x)
        
        return x 
        
model = ResNet50(classes=len(classnames)).to(device)

model
ResNet50(
  (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3))
  (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (max_pool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
  (part1_1): ConvBlock(
    (conv1): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1))
    (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (shortcut): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1))
    (shortcut_bn): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (part1_2): Identity_block(
    (conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1))
    (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (part1_3): Identity_block(
    (conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1))
    (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (part2_1): ConvBlock(
    (conv1): Conv2d(256, 128, kernel_size=(1, 1), stride=(2, 2))
    (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (shortcut): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2))
    (shortcut_bn): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (part2_2): Identity_block(
    (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1))
    (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (part2_3): Identity_block(
    (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1))
    (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (part2_4): Identity_block(
    (conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1))
    (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (part3_1): ConvBlock(
    (conv1): Conv2d(512, 256, kernel_size=(1, 1), stride=(2, 2))
    (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (shortcut): Conv2d(512, 1024, kernel_size=(1, 1), stride=(2, 2))
    (shortcut_bn): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (part3_2): Identity_block(
    (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1))
    (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (part3_3): Identity_block(
    (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1))
    (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (part3_4): Identity_block(
    (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1))
    (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (part3_5): Identity_block(
    (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1))
    (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (part3_6): Identity_block(
    (conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1))
    (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (part4_1): ConvBlock(
    (conv1): Conv2d(1024, 512, kernel_size=(1, 1), stride=(2, 2))
    (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (shortcut): Conv2d(1024, 2048, kernel_size=(1, 1), stride=(2, 2))
    (shortcut_bn): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (part4_2): Identity_block(
    (conv1): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1))
    (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (part4_3): Identity_block(
    (conv1): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1))
    (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1))
    (bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (avg_pool): AvgPool2d(kernel_size=7, stride=7, padding=0)
  (fn1): Linear(in_features=2048, out_features=4, bias=True)
)
model(torch.randn(32, 3, 224, 224).to(device)).shape
torch.Size([32, 4])

3、模型训练

1、构建训练集

def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    batch_size = len(dataloader)
    
    train_acc, train_loss = 0, 0 
    
    for X, y in dataloader:
        X, y = X.to(device), y.to(device)
        
        # 训练
        pred = model(X)
        loss = loss_fn(pred, y)
        
        # 梯度下降法
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        # 记录
        train_loss += loss.item()
        train_acc += (pred.argmax(1) == y).type(torch.float).sum().item()
        
    train_acc /= size
    train_loss /= batch_size
    
    return train_acc, train_loss

2、构建测试集

def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    batch_size = len(dataloader)
    
    test_acc, test_loss = 0, 0 
    
    with torch.no_grad():
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
        
            pred = model(X)
            loss = loss_fn(pred, y)
        
            test_loss += loss.item()
            test_acc += (pred.argmax(1) == y).type(torch.float).sum().item()
        
    test_acc /= size
    test_loss /= batch_size
    
    return test_acc, test_loss

3、设置超参数

loss_fn = nn.CrossEntropyLoss()  # 损失函数     
learn_lr = 1e-4             # 超参数
optimizer = torch.optim.Adam(model.parameters(), lr=learn_lr)   # 优化器

4、模型训练

train_acc = []
train_loss = []
test_acc = []
test_loss = []

epoches = 30

for i in range(epoches):
    model.train()
    epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, optimizer)
    
    model.eval()
    epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
    
    train_acc.append(epoch_train_acc)
    train_loss.append(epoch_train_loss)
    test_acc.append(epoch_test_acc)
    test_loss.append(epoch_test_loss)
    
    # 输出
    template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}')
    print(template.format(i + 1, epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss))
    
print("Done")
Epoch: 1, Train_acc:38.1%, Train_loss:1.394, Test_acc:25.7%, Test_loss:1.419
Epoch: 2, Train_acc:66.4%, Train_loss:0.901, Test_acc:37.2%, Test_loss:1.432
Epoch: 3, Train_acc:80.1%, Train_loss:0.603, Test_acc:54.0%, Test_loss:1.646
Epoch: 4, Train_acc:85.4%, Train_loss:0.359, Test_acc:64.6%, Test_loss:1.385
Epoch: 5, Train_acc:84.7%, Train_loss:0.449, Test_acc:59.3%, Test_loss:2.418
Epoch: 6, Train_acc:86.9%, Train_loss:0.487, Test_acc:65.5%, Test_loss:1.457
Epoch: 7, Train_acc:88.1%, Train_loss:0.352, Test_acc:61.1%, Test_loss:1.388
Epoch: 8, Train_acc:89.4%, Train_loss:0.341, Test_acc:67.3%, Test_loss:1.243
Epoch: 9, Train_acc:88.7%, Train_loss:0.342, Test_acc:60.2%, Test_loss:2.570
Epoch:10, Train_acc:91.8%, Train_loss:0.220, Test_acc:74.3%, Test_loss:1.214
Epoch:11, Train_acc:93.1%, Train_loss:0.196, Test_acc:79.6%, Test_loss:1.164
Epoch:12, Train_acc:95.8%, Train_loss:0.133, Test_acc:69.9%, Test_loss:1.479
Epoch:13, Train_acc:98.0%, Train_loss:0.068, Test_acc:68.1%, Test_loss:1.583
Epoch:14, Train_acc:98.5%, Train_loss:0.056, Test_acc:78.8%, Test_loss:1.320
Epoch:15, Train_acc:98.2%, Train_loss:0.119, Test_acc:64.6%, Test_loss:2.112
Epoch:16, Train_acc:92.5%, Train_loss:0.339, Test_acc:68.1%, Test_loss:1.935
Epoch:17, Train_acc:94.2%, Train_loss:0.172, Test_acc:77.0%, Test_loss:1.505
Epoch:18, Train_acc:94.9%, Train_loss:0.123, Test_acc:81.4%, Test_loss:0.840
Epoch:19, Train_acc:97.8%, Train_loss:0.068, Test_acc:78.8%, Test_loss:1.116
Epoch:20, Train_acc:98.9%, Train_loss:0.044, Test_acc:79.6%, Test_loss:0.917
Epoch:21, Train_acc:99.6%, Train_loss:0.019, Test_acc:78.8%, Test_loss:0.962
Epoch:22, Train_acc:99.1%, Train_loss:0.114, Test_acc:83.2%, Test_loss:1.119
Epoch:23, Train_acc:96.7%, Train_loss:0.076, Test_acc:82.3%, Test_loss:0.804
Epoch:24, Train_acc:95.1%, Train_loss:0.195, Test_acc:72.6%, Test_loss:1.944
Epoch:25, Train_acc:96.2%, Train_loss:0.125, Test_acc:73.5%, Test_loss:1.172
Epoch:26, Train_acc:95.4%, Train_loss:0.312, Test_acc:75.2%, Test_loss:1.810
Epoch:27, Train_acc:95.1%, Train_loss:0.127, Test_acc:48.7%, Test_loss:3.517
Epoch:28, Train_acc:96.9%, Train_loss:0.102, Test_acc:76.1%, Test_loss:0.969
Epoch:29, Train_acc:98.5%, Train_loss:0.048, Test_acc:84.1%, Test_loss:0.754
Epoch:30, Train_acc:98.5%, Train_loss:0.049, Test_acc:83.2%, Test_loss:0.656
Done

5、结果可视化

import matplotlib.pyplot as plt
#隐藏警告
import warnings
warnings.filterwarnings("ignore")               #忽略警告信息

epochs_range = range(epoches)

plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)

plt.plot(epochs_range, train_acc, label='Training Accuracy')
plt.plot(epochs_range, test_acc, label='Test Accuracy')
plt.legend(loc='lower right')
plt.title('Training Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, label='Training Loss')
plt.plot(epochs_range, test_loss, label='Test Loss')
plt.legend(loc='upper right')
plt.title('Training= Loss')
plt.show()


在这里插入图片描述

看着欠缺了一点,从后期趋势来看,增大轮次,后面有趋向于收敛的情况(本人电脑跑的比较慢,先不跑了,后面 放到云上去跑)。

参考资料

【深度学习】ResNet网络讲解-CSDN博客

K同学啊,训练营文档

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/973459.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux 驱动编程配置(minis3c2440)

1.介绍 1. 启动过程:启动u-boot------>>启动linux内核----->>挂载根文件系统 2. uboot是一个裸机程序,是一个bootloader,用于启动linux系统以及系统初始化 ubootloader主要完成了哪些任务:1. 初始化异常向量表&a…

【Excel】【VBA】根据内容调整打印区域

Excel VBA:自动调整打印区域的实用代码解析 在Excel中,我们经常需要调整打印区域。今天介绍一段VBA代码,它可以根据C列的内容自动调整打印区域。 Dim ws As Worksheet Dim lastRow As Long Dim r As Long 设置当前工作表 Set ws ActiveSh…

wps中zotero插件消失,解决每次都需要重新开问题

参考 查看zotero目录 D:\zotero\integration\word-for-windows 加载项点击 dotm即可 长期解决 把dom 复制到 C:\Users\89735\AppData\Roaming\kingsoft\office6\templates\wps\zh_CN还是每次都需要重新开的话 重新加载一下

【Java八股文】09-计算机操作系统面试篇

文章目录 计算机操作系统面试篇用户态和内核态的区别?用户态和内核态的区别? 进程管理线程和进程的区别是什么?进程,线程,协程的区别是什么?创建一个协程的过程线程运行过程中申请到的东西在切换时是否全部…

CPU安装pytorch(别点进来)

终于! 深度学习环境配置5——windows下的torch-cpu1.2.0环境配置_requirement怎么写torch cu-CSDN博客

echarts找不到了?echarts社区最新地址

前言:在之前使用echarts的时候,还可以通过上边的导航栏找到echarts社区,但是如今的echarts变更之后,就找不到echarts社区了。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客 如今…

什么是事务?并发事务引发的问题?什么是MVCC?

文章目录 什么是事务?并发事务引发的问题?什么是MVCC?1.事务的四大特性2.并发事务下产生的问题:脏读、不可重复读、幻读3.如何应对并发事务引发的问题?4.什么是MVCC?5.可见性规则?参考资料 什么…

【算法基础】--前缀和

前缀和 一、一维前缀和示例模板[寻找数组的中心下标 ](https://leetcode.cn/problems/tvdfij/description/)除自身以外的数组乘积和可被k整除的子数组 一、一维前缀和 前缀和就是快速求出数组某一个连续区间内所有元素的和。 示例模板 已知一个数组arr,求前缀和 …

【含文档+PPT+源码】基于Django的新闻推荐系统的设计与实现

项目介绍 本课程演示的是一款基于Django的新闻推荐系统的设计与实现,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Python学习者。 1.包含:项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系统 3.…

内容中台架构下智能推荐系统的算法优化与分发策略

内容概要 在数字化内容生态中,智能推荐系统作为内容中台的核心引擎,承担着用户需求与内容资源精准匹配的关键任务。其算法架构的优化路径围绕动态特征建模与多模态数据融合展开,通过深度强化学习技术实现用户行为特征的实时捕捉与动态更新&a…

如何在WPS打开的word、excel文件中,使用AI?

1、百度搜索:Office AI官方下载 或者直接打开网址:https://www.office-ai.cn/static/introductions/officeai/smartdownload.html 打开后会直接提示开始下载中,下载完成后会让其选择下载存放位置: 选择位置,然后命名文…

QML 实现一个动态的启动界面

QML 实现一个动态的启动界面 一、效果查看二、源码分享三、所用到的资源下载 一、效果查看 二、源码分享 工程结构 main.qml import QtQuick import QtQuick.Controls import QtQuick.Dialogs import Qt.labs.platformWindow {id:windowwidth: 640height: 400visible: truetit…

springboot之解析请求参数时,无法获取方法参数的名称。

主要报错信息: java.lang.IllegalArgumentException: Name for argument of type [java.lang.Integer] not specified, and parameter name information not available via reflection. Ensure that the compiler uses the ‘-parameters’ flag. 改bug过程&#x…

开源免费文档翻译工具 可支持pdf、word、excel、ppt

项目介绍 今天给大家推荐一个开源的、超实用的免费文档翻译工具(DeeplxFile),相信很多人都有需要翻译文档的时刻,这款工具就能轻松解决你的需求。 它支持多种文档格式翻译,包括 Word、PDF、PPT、Excel ,使…

Github 开源 AI 知识库推荐

今天来聊聊那些好用的 GitHub 上开源的 AI 知识库。这些项目不仅能让开发者们快速上手,还能帮助我们解决实际问题,甚至让我们的应用更加智能化。它们为企业、开发者和研究人员提供了强大的工具,用于管理和查询海量的知识信息。废话不多说&…

uni-app开发app时 使用uni.chooseLocation遇到的问题

问题一:不显示 问题二:选择地址列表一直在加载中 因为 uni-app 接口文档 中已经说明,使用腾讯的话需要开启云服务,具体可看官网,这就是为什么使用时直接不显示的原因,所以我使用的高德,但又出现…

Cython学习笔记1:利用Cython加速Python运行速度

Cython学习笔记1:利用Cython加速Python运行速度 CythonCython 的核心特点:利用Cython加速Python运行速度1. Cython加速Python运行速度原理2. 不使用Cython3. 使用Cython加速(1)使用pip安装 cython 和 setuptools 库(2&…

【Deepseek】Linux 本地部署 Deepseek

前言 本文介绍在 Linux 系统上部署 Deepseek AI。本文教程是面向所有想体验 AI 玩家的一个简易教程,因此即使是小白也可以轻松完成体验,话不多说立马着手去干。 [注]:笔者使用的系统为 Ubuntu 24.10 1. 关于 ollama Ollama 是一款开源应用…

【python】网页批量转PDF

安装wkhtmltopdf 网站:wkhtmltopdf wkhtmltopdf http://www.baidu.com/ D:website1.pdf 安装pdfkit库 pip install pdfkit 批量转换代码 import os import pdfkit path_wkthmltopdf rE:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe config pdfkit.configu…

JSON格式,C语言自己实现,以及直接调用库函数(一)

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。以下为你提供不同场景下常见的 JSON 格式示例。 1. 简单对象 JSON 对象是由键值对组成,用花括号 {} 包裹&…