深度学习 Day29——J8Inception v1算法实战与解析


前言

关键字: pytorch实现Inception v1详解算法,pytorch实现Inception v1详解算法,Inception v1详解

1 我的环境

  • 电脑系统:Windows 11
  • 语言环境:python 3.8.6
  • 编译器:pycharm2020.2.3
  • 深度学习环境:
    torch == 1.9.1+cu111
    torchvision == 0.10.1+cu111
    TensorFlow 2.10.1
  • 显卡:NVIDIA GeForce RTX 4070

2 pytorch实现Inception v1算法

2.1 前期准备

2.1.1 引入库


import torch
import torch.nn as nn
import time
import copy
from torchvision import transforms, datasets
from pathlib import Path
from PIL import Image
import torchsummary as summary
import torch.nn.functional as F
from collections import OrderedDict
import re
import torch.utils.model_zoo as model_zoo
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
plt.rcParams['figure.dpi'] = 100  # 分辨率
import warnings

warnings.filterwarnings('ignore')  # 忽略一些warning内容,无需打印

2.1.2 设置GPU(如果设备上支持GPU就使用GPU,否则使用CPU)

"""前期准备-设置GPU"""
# 如果设备上支持GPU就使用GPU,否则使用CPU
 device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
 print("Using {} device".format(device))

输出

Using cuda device

2.1.3 导入数据

'''前期工作-导入数据'''
data_dir = r"D:\DeepLearning\data\monkeypox_recognition"
data_dir = Path(data_dir)

data_paths = list(data_dir.glob('*'))
classeNames = [str(path).split("\\")[-1] for path in data_paths]
print(classeNames)

输出

['Monkeypox', 'Others']

2.1.4 可视化数据

'''前期工作-可视化数据'''
subfolder = Path(data_dir) / "Monkeypox"
image_files = list(p.resolve() for p in subfolder.glob('*') if p.suffix in [".jpg", ".png", ".jpeg"])
plt.figure(figsize=(10, 6))
for i in range(len(image_files[:12])):
    image_file = image_files[i]
    ax = plt.subplot(3, 4, i + 1)
    img = Image.open(str(image_file))
    plt.imshow(img)
    plt.axis("off")
# 显示图片
plt.tight_layout()
plt.show()

在这里插入图片描述

2.1.4 图像数据变换

'''前期工作-图像数据变换'''
total_datadir = data_dir

# 关于transforms.Compose的更多介绍可以参考:https://blog.csdn.net/qq_38251616/article/details/124878863
train_transforms = transforms.Compose([
    transforms.Resize([224, 224]),  # 将输入图片resize成统一尺寸
    transforms.ToTensor(),  # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    transforms.Normalize(  # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
        mean=[0.485, 0.456, 0.406],
        std=[0.229, 0.224, 0.225])  # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
])
total_data = datasets.ImageFolder(total_datadir, transform=train_transforms)
print(total_data)
print(total_data.class_to_idx)

输出

Dataset ImageFolder
    Number of datapoints: 2142
    Root location: D:\DeepLearning\data\monkeypox_recognition
    StandardTransform
Transform: Compose(
               Resize(size=[224, 224], interpolation=bilinear, max_size=None, antialias=None)
               ToTensor()
               Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
           )
{'Monkeypox': 0, 'Others': 1}

2.1.4 划分数据集

'''前期工作-划分数据集'''
train_size = int(0.8 * len(total_data))  # train_size表示训练集大小,通过将总体数据长度的80%转换为整数得到;
test_size = len(total_data) - train_size  # test_size表示测试集大小,是总体数据长度减去训练集大小。
# 使用torch.utils.data.random_split()方法进行数据集划分。该方法将总体数据total_data按照指定的大小比例([train_size, test_size])随机划分为训练集和测试集,
# 并将划分结果分别赋值给train_dataset和test_dataset两个变量。
train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size])
print("train_dataset={}\ntest_dataset={}".format(train_dataset, test_dataset))
print("train_size={}\ntest_size={}".format(train_size, test_size))

输出

train_dataset=<torch.utils.data.dataset.Subset object at 0x000002A96E08E0D0>
test_dataset=<torch.utils.data.dataset.Subset object at 0x000002A96E04E640>
train_size=1713
test_size=429

2.1.4 加载数据

'''前期工作-加载数据'''
batch_size = 32

train_dl = torch.utils.data.DataLoader(train_dataset,
                                       batch_size=batch_size,
                                       shuffle=True,
                                       num_workers=1)
test_dl = torch.utils.data.DataLoader(test_dataset,
                                      batch_size=batch_size,
                                      shuffle=True,
                                      num_workers=1)

2.1.4 查看数据

'''前期工作-查看数据'''
for X, y in test_dl:
    print("Shape of X [N, C, H, W]: ", X.shape)
    print("Shape of y: ", y.shape, y.dtype)
    break

输出

Shape of X [N, C, H, W]:  torch.Size([32, 3, 224, 224])
Shape of y:  torch.Size([32]) torch.int64

2.2 搭建Inception v1模型

"""构建Inceptionv1网络"""
"""构建Inceptionv1网络"""


class InceptionV1(nn.Module):
    def __init__(self, num_classes=4):
        super(InceptionV1, self).__init__()

        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3)
        self.maxpool1 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        self.conv2 = nn.Conv2d(64, 64, kernel_size=1, stride=1, padding=0)
        self.conv3 = nn.Conv2d(64, 192, kernel_size=3, stride=1, padding=1)
        self.maxpool2 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

        self.inception3a = inception_block(192, 64, 96, 128, 16, 32, 32)
        self.inception3b = inception_block(256, 128, 128, 192, 32, 96, 64)
        self.maxpool3 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

        self.inception4a = inception_block(480, 192, 96, 208, 16, 48, 64)
        self.inception4b = inception_block(512, 160, 112, 224, 24, 64, 64)
        self.inception4c = inception_block(512, 128, 128, 256, 24, 64, 64)
        self.inception4d = inception_block(512, 112, 144, 288, 32, 64, 64)
        self.inception4e = inception_block(528, 256, 160, 320, 32, 128, 128)
        self.maxpool4 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

        self.inception5a = inception_block(832, 256, 160, 320, 32, 128, 128)
        self.inception5b = nn.Sequential(
            inception_block(832, 384, 192, 384, 48, 128, 128),
            nn.AvgPool2d(kernel_size=7, stride=1, padding=0),
            nn.Dropout(0.4)
        )

        # 全连接网络层,用于分类
        self.classifier = nn.Sequential(
            nn.Linear(in_features=1024, out_features=1024),
            nn.ReLU(),
            nn.Linear(in_features=1024, out_features=num_classes),
            nn.Softmax(dim=1)
        )

    def forward(self, x):
        x = self.conv1(x)
        x = F.relu(x)
        x = self.maxpool1(x)
        x = self.conv2(x)
        x = F.relu(x)
        x = self.conv3(x)
        x = F.relu(x)
        x = self.maxpool2(x)

        x = self.inception3a(x)
        x = self.inception3b(x)
        x = self.maxpool3(x)

        x = self.inception4a(x)
        x = self.inception4b(x)
        x = self.inception4c(x)
        x = self.inception4d(x)
        x = self.inception4e(x)
        x = self.maxpool4(x)

        x = self.inception5a(x)
        x = self.inception5b(x)

        x = torch.flatten(x, start_dim=1)
        x = self.classifier(x)

        return x


class inception_block(nn.Module):
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch5x5red, ch5x5, pool_proj):
        super(inception_block, self).__init__()

        # 1x1 conv branch
        self.branch1 = nn.Sequential(
            nn.Conv2d(in_channels, ch1x1, kernel_size=1),
            nn.BatchNorm2d(ch1x1),
            nn.ReLU(inplace=True)
        )

        # 1x1 conv -> 3x3 conv branch
        self.branch2 = nn.Sequential(
            nn.Conv2d(in_channels, ch3x3red, kernel_size=1),
            nn.BatchNorm2d(ch3x3red),
            nn.ReLU(inplace=True),
            nn.Conv2d(ch3x3red, ch3x3, kernel_size=3, padding=1),
            nn.BatchNorm2d(ch3x3),
            nn.ReLU(inplace=True)
        )

        # 1x1 conv -> 5x5 conv branch
        self.branch3 = nn.Sequential(
            nn.Conv2d(in_channels, ch5x5red, kernel_size=1),
            nn.BatchNorm2d(ch5x5red),
            nn.ReLU(inplace=True),
            nn.Conv2d(ch5x5red, ch5x5, kernel_size=5, padding=2),
            nn.BatchNorm2d(ch5x5),
            nn.ReLU(inplace=True)
        )

        # 3x3 max pooling -> 1x1 conv branch
        self.branch4 = nn.Sequential(
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels, pool_proj, kernel_size=1),
            nn.BatchNorm2d(pool_proj),
            nn.ReLU(inplace=True)
        )

    def forward(self, x):
        # compute forward pass through all branches
        # and concatenate the outout feature maps
        branch1_output = self.branch1(x)
        branch2_output = self.branch2(x)
        branch3_output = self.branch3(x)
        branch4_output = self.branch4(x)

        outputs = [branch1_output, branch2_output, branch3_output, branch4_output]
        return torch.cat(outputs, 1)

输出

----------------------------------------------------------------
        Layer (type)               Output Shape         Param #
================================================================
            Conv2d-1         [-1, 64, 112, 112]           9,472
         MaxPool2d-2           [-1, 64, 56, 56]               0
            Conv2d-3           [-1, 64, 56, 56]           4,160
            Conv2d-4          [-1, 192, 56, 56]         110,784
         MaxPool2d-5          [-1, 192, 28, 28]               0
            Conv2d-6           [-1, 64, 28, 28]          12,352
       BatchNorm2d-7           [-1, 64, 28, 28]             128
              ReLU-8           [-1, 64, 28, 28]               0
            Conv2d-9           [-1, 96, 28, 28]          18,528
      BatchNorm2d-10           [-1, 96, 28, 28]             192
             ReLU-11           [-1, 96, 28, 28]               0
           Conv2d-12          [-1, 128, 28, 28]         110,720
      BatchNorm2d-13          [-1, 128, 28, 28]             256
             ReLU-14          [-1, 128, 28, 28]               0
           Conv2d-15           [-1, 16, 28, 28]           3,088
      BatchNorm2d-16           [-1, 16, 28, 28]              32
             ReLU-17           [-1, 16, 28, 28]               0
           Conv2d-18           [-1, 32, 28, 28]          12,832
      BatchNorm2d-19           [-1, 32, 28, 28]              64
             ReLU-20           [-1, 32, 28, 28]               0
        MaxPool2d-21          [-1, 192, 28, 28]               0
           Conv2d-22           [-1, 32, 28, 28]           6,176
      BatchNorm2d-23           [-1, 32, 28, 28]              64
             ReLU-24           [-1, 32, 28, 28]               0
  inception_block-25          [-1, 256, 28, 28]               0
           Conv2d-26          [-1, 128, 28, 28]          32,896
      BatchNorm2d-27          [-1, 128, 28, 28]             256
             ReLU-28          [-1, 128, 28, 28]               0
           Conv2d-29          [-1, 128, 28, 28]          32,896
      BatchNorm2d-30          [-1, 128, 28, 28]             256
             ReLU-31          [-1, 128, 28, 28]               0
           Conv2d-32          [-1, 192, 28, 28]         221,376
      BatchNorm2d-33          [-1, 192, 28, 28]             384
             ReLU-34          [-1, 192, 28, 28]               0
           Conv2d-35           [-1, 32, 28, 28]           8,224
      BatchNorm2d-36           [-1, 32, 28, 28]              64
             ReLU-37           [-1, 32, 28, 28]               0
           Conv2d-38           [-1, 96, 28, 28]          76,896
      BatchNorm2d-39           [-1, 96, 28, 28]             192
             ReLU-40           [-1, 96, 28, 28]               0
        MaxPool2d-41          [-1, 256, 28, 28]               0
           Conv2d-42           [-1, 64, 28, 28]          16,448
      BatchNorm2d-43           [-1, 64, 28, 28]             128
             ReLU-44           [-1, 64, 28, 28]               0
  inception_block-45          [-1, 480, 28, 28]               0
        MaxPool2d-46          [-1, 480, 14, 14]               0
           Conv2d-47          [-1, 192, 14, 14]          92,352
      BatchNorm2d-48          [-1, 192, 14, 14]             384
             ReLU-49          [-1, 192, 14, 14]               0
           Conv2d-50           [-1, 96, 14, 14]          46,176
      BatchNorm2d-51           [-1, 96, 14, 14]             192
             ReLU-52           [-1, 96, 14, 14]               0
           Conv2d-53          [-1, 208, 14, 14]         179,920
      BatchNorm2d-54          [-1, 208, 14, 14]             416
             ReLU-55          [-1, 208, 14, 14]               0
           Conv2d-56           [-1, 16, 14, 14]           7,696
      BatchNorm2d-57           [-1, 16, 14, 14]              32
             ReLU-58           [-1, 16, 14, 14]               0
           Conv2d-59           [-1, 48, 14, 14]          19,248
      BatchNorm2d-60           [-1, 48, 14, 14]              96
             ReLU-61           [-1, 48, 14, 14]               0
        MaxPool2d-62          [-1, 480, 14, 14]               0
           Conv2d-63           [-1, 64, 14, 14]          30,784
      BatchNorm2d-64           [-1, 64, 14, 14]             128
             ReLU-65           [-1, 64, 14, 14]               0
  inception_block-66          [-1, 512, 14, 14]               0
           Conv2d-67          [-1, 160, 14, 14]          82,080
      BatchNorm2d-68          [-1, 160, 14, 14]             320
             ReLU-69          [-1, 160, 14, 14]               0
           Conv2d-70          [-1, 112, 14, 14]          57,456
      BatchNorm2d-71          [-1, 112, 14, 14]             224
             ReLU-72          [-1, 112, 14, 14]               0
           Conv2d-73          [-1, 224, 14, 14]         226,016
      BatchNorm2d-74          [-1, 224, 14, 14]             448
             ReLU-75          [-1, 224, 14, 14]               0
           Conv2d-76           [-1, 24, 14, 14]          12,312
      BatchNorm2d-77           [-1, 24, 14, 14]              48
             ReLU-78           [-1, 24, 14, 14]               0
           Conv2d-79           [-1, 64, 14, 14]          38,464
      BatchNorm2d-80           [-1, 64, 14, 14]             128
             ReLU-81           [-1, 64, 14, 14]               0
        MaxPool2d-82          [-1, 512, 14, 14]               0
           Conv2d-83           [-1, 64, 14, 14]          32,832
      BatchNorm2d-84           [-1, 64, 14, 14]             128
             ReLU-85           [-1, 64, 14, 14]               0
  inception_block-86          [-1, 512, 14, 14]               0
           Conv2d-87          [-1, 128, 14, 14]          65,664
      BatchNorm2d-88          [-1, 128, 14, 14]             256
             ReLU-89          [-1, 128, 14, 14]               0
           Conv2d-90          [-1, 128, 14, 14]          65,664
      BatchNorm2d-91          [-1, 128, 14, 14]             256
             ReLU-92          [-1, 128, 14, 14]               0
           Conv2d-93          [-1, 256, 14, 14]         295,168
      BatchNorm2d-94          [-1, 256, 14, 14]             512
             ReLU-95          [-1, 256, 14, 14]               0
           Conv2d-96           [-1, 24, 14, 14]          12,312
      BatchNorm2d-97           [-1, 24, 14, 14]              48
             ReLU-98           [-1, 24, 14, 14]               0
           Conv2d-99           [-1, 64, 14, 14]          38,464
     BatchNorm2d-100           [-1, 64, 14, 14]             128
            ReLU-101           [-1, 64, 14, 14]               0
       MaxPool2d-102          [-1, 512, 14, 14]               0
          Conv2d-103           [-1, 64, 14, 14]          32,832
     BatchNorm2d-104           [-1, 64, 14, 14]             128
            ReLU-105           [-1, 64, 14, 14]               0
 inception_block-106          [-1, 512, 14, 14]               0
          Conv2d-107          [-1, 112, 14, 14]          57,456
     BatchNorm2d-108          [-1, 112, 14, 14]             224
            ReLU-109          [-1, 112, 14, 14]               0
          Conv2d-110          [-1, 144, 14, 14]          73,872
     BatchNorm2d-111          [-1, 144, 14, 14]             288
            ReLU-112          [-1, 144, 14, 14]               0
          Conv2d-113          [-1, 288, 14, 14]         373,536
     BatchNorm2d-114          [-1, 288, 14, 14]             576
            ReLU-115          [-1, 288, 14, 14]               0
          Conv2d-116           [-1, 32, 14, 14]          16,416
     BatchNorm2d-117           [-1, 32, 14, 14]              64
            ReLU-118           [-1, 32, 14, 14]               0
          Conv2d-119           [-1, 64, 14, 14]          51,264
     BatchNorm2d-120           [-1, 64, 14, 14]             128
            ReLU-121           [-1, 64, 14, 14]               0
       MaxPool2d-122          [-1, 512, 14, 14]               0
          Conv2d-123           [-1, 64, 14, 14]          32,832
     BatchNorm2d-124           [-1, 64, 14, 14]             128
            ReLU-125           [-1, 64, 14, 14]               0
 inception_block-126          [-1, 528, 14, 14]               0
          Conv2d-127          [-1, 256, 14, 14]         135,424
     BatchNorm2d-128          [-1, 256, 14, 14]             512
            ReLU-129          [-1, 256, 14, 14]               0
          Conv2d-130          [-1, 160, 14, 14]          84,640
     BatchNorm2d-131          [-1, 160, 14, 14]             320
            ReLU-132          [-1, 160, 14, 14]               0
          Conv2d-133          [-1, 320, 14, 14]         461,120
     BatchNorm2d-134          [-1, 320, 14, 14]             640
            ReLU-135          [-1, 320, 14, 14]               0
          Conv2d-136           [-1, 32, 14, 14]          16,928
     BatchNorm2d-137           [-1, 32, 14, 14]              64
            ReLU-138           [-1, 32, 14, 14]               0
          Conv2d-139          [-1, 128, 14, 14]         102,528
     BatchNorm2d-140          [-1, 128, 14, 14]             256
            ReLU-141          [-1, 128, 14, 14]               0
       MaxPool2d-142          [-1, 528, 14, 14]               0
          Conv2d-143          [-1, 128, 14, 14]          67,712
     BatchNorm2d-144          [-1, 128, 14, 14]             256
            ReLU-145          [-1, 128, 14, 14]               0
 inception_block-146          [-1, 832, 14, 14]               0
       MaxPool2d-147            [-1, 832, 7, 7]               0
          Conv2d-148            [-1, 256, 7, 7]         213,248
     BatchNorm2d-149            [-1, 256, 7, 7]             512
            ReLU-150            [-1, 256, 7, 7]               0
          Conv2d-151            [-1, 160, 7, 7]         133,280
     BatchNorm2d-152            [-1, 160, 7, 7]             320
            ReLU-153            [-1, 160, 7, 7]               0
          Conv2d-154            [-1, 320, 7, 7]         461,120
     BatchNorm2d-155            [-1, 320, 7, 7]             640
            ReLU-156            [-1, 320, 7, 7]               0
          Conv2d-157             [-1, 32, 7, 7]          26,656
     BatchNorm2d-158             [-1, 32, 7, 7]              64
            ReLU-159             [-1, 32, 7, 7]               0
          Conv2d-160            [-1, 128, 7, 7]         102,528
     BatchNorm2d-161            [-1, 128, 7, 7]             256
            ReLU-162            [-1, 128, 7, 7]               0
       MaxPool2d-163            [-1, 832, 7, 7]               0
          Conv2d-164            [-1, 128, 7, 7]         106,624
     BatchNorm2d-165            [-1, 128, 7, 7]             256
            ReLU-166            [-1, 128, 7, 7]               0
 inception_block-167            [-1, 832, 7, 7]               0
          Conv2d-168            [-1, 384, 7, 7]         319,872
     BatchNorm2d-169            [-1, 384, 7, 7]             768
            ReLU-170            [-1, 384, 7, 7]               0
          Conv2d-171            [-1, 192, 7, 7]         159,936
     BatchNorm2d-172            [-1, 192, 7, 7]             384
            ReLU-173            [-1, 192, 7, 7]               0
          Conv2d-174            [-1, 384, 7, 7]         663,936
     BatchNorm2d-175            [-1, 384, 7, 7]             768
            ReLU-176            [-1, 384, 7, 7]               0
          Conv2d-177             [-1, 48, 7, 7]          39,984
     BatchNorm2d-178             [-1, 48, 7, 7]              96
            ReLU-179             [-1, 48, 7, 7]               0
          Conv2d-180            [-1, 128, 7, 7]         153,728
     BatchNorm2d-181            [-1, 128, 7, 7]             256
            ReLU-182            [-1, 128, 7, 7]               0
       MaxPool2d-183            [-1, 832, 7, 7]               0
          Conv2d-184            [-1, 128, 7, 7]         106,624
     BatchNorm2d-185            [-1, 128, 7, 7]             256
            ReLU-186            [-1, 128, 7, 7]               0
 inception_block-187           [-1, 1024, 7, 7]               0
       AvgPool2d-188           [-1, 1024, 1, 1]               0
         Dropout-189           [-1, 1024, 1, 1]               0
          Linear-190                 [-1, 1024]       1,049,600
            ReLU-191                 [-1, 1024]               0
          Linear-192                    [-1, 4]           4,100
         Softmax-193                    [-1, 4]               0
================================================================
Total params: 7,041,172
Trainable params: 7,041,172
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.57
Forward/backward pass size (MB): 69.61
Params size (MB): 26.86
Estimated Total Size (MB): 97.05
----------------------------------------------------------------

2.3 训练模型

2.3.1 设置超参数

"""训练模型--设置超参数"""
loss_fn = nn.CrossEntropyLoss()  # 创建损失函数,计算实际输出和真实相差多少,交叉熵损失函数,事实上,它就是做图片分类任务时常用的损失函数
learn_rate = 1e-4  # 学习率
optimizer1 = torch.optim.SGD(model.parameters(), lr=learn_rate)# 作用是定义优化器,用来训练时候优化模型参数;其中,SGD表示随机梯度下降,用于控制实际输出y与真实y之间的相差有多大
optimizer2 = torch.optim.Adam(model.parameters(), lr=learn_rate)  
lr_opt = optimizer2
model_opt = optimizer2
# 调用官方动态学习率接口时使用2
lambda1 = lambda epoch : 0.92 ** (epoch // 4)
# optimizer = torch.optim.SGD(model.parameters(), lr=learn_rate)
scheduler = torch.optim.lr_scheduler.LambdaLR(lr_opt, lr_lambda=lambda1) #选定调整方法

2.3.2 编写训练函数

"""训练模型--编写训练函数"""
# 训练循环
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)  # 训练集的大小,一共60000张图片
    num_batches = len(dataloader)  # 批次数目,1875(60000/32)

    train_loss, train_acc = 0, 0  # 初始化训练损失和正确率

    for X, y in dataloader:  # 加载数据加载器,得到里面的 X(图片数据)和 y(真实标签)
        X, y = X.to(device), y.to(device) # 用于将数据存到显卡

        # 计算预测误差
        pred = model(X)  # 网络输出
        loss = loss_fn(pred, y)  # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失

        # 反向传播
        optimizer.zero_grad()  # 清空过往梯度
        loss.backward()  # 反向传播,计算当前梯度
        optimizer.step()  # 根据梯度更新网络参数

        # 记录acc与loss
        train_acc += (pred.argmax(1) == y).type(torch.float).sum().item()
        train_loss += loss.item()

    train_acc /= size
    train_loss /= num_batches

    return train_acc, train_loss

2.3.3 编写测试函数

"""训练模型--编写测试函数"""
# 测试函数和训练函数大致相同,但是由于不进行梯度下降对网络权重进行更新,所以不需要传入优化器
def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)  # 测试集的大小,一共10000张图片
    num_batches = len(dataloader)  # 批次数目,313(10000/32=312.5,向上取整)
    test_loss, test_acc = 0, 0

    # 当不进行训练时,停止梯度更新,节省计算内存消耗
    with torch.no_grad(): # 测试时模型参数不用更新,所以 no_grad,整个模型参数正向推就ok,不反向更新参数
        for imgs, target in dataloader:
            imgs, target = imgs.to(device), target.to(device)

            # 计算loss
            target_pred = model(imgs)
            loss = loss_fn(target_pred, target)

            test_loss += loss.item()
            test_acc += (target_pred.argmax(1) == target).type(torch.float).sum().item()#统计预测正确的个数

    test_acc /= size
    test_loss /= num_batches

    return test_acc, test_loss

2.3.4 正式训练

"""训练模型--正式训练"""
epochs = 20
train_loss = []
train_acc = []
test_loss = []
test_acc = []
best_test_acc=0

for epoch in range(epochs):
    milliseconds_t1 = int(time.time() * 1000)

    # 更新学习率(使用自定义学习率时使用)
    # adjust_learning_rate(lr_opt, epoch, learn_rate)

    model.train()
    epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, model_opt)
    scheduler.step() # 更新学习率(调用官方动态学习率接口时使用)

    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)

    # 获取当前的学习率
    lr = lr_opt.state_dict()['param_groups'][0]['lr']

    milliseconds_t2 = int(time.time() * 1000)
    template = ('Epoch:{:2d}, duration:{}ms, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}, Lr:{:.2E}')
    if best_test_acc < epoch_test_acc:
        best_test_acc = epoch_test_acc
        #备份最好的模型
        best_model = copy.deepcopy(model)
        template = (
            'Epoch:{:2d}, duration:{}ms, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}, Lr:{:.2E},Update the best model')
    print(
        template.format(epoch + 1, milliseconds_t2-milliseconds_t1, epoch_train_acc * 100, epoch_train_loss, epoch_test_acc * 100, epoch_test_loss, lr))
# 保存最佳模型到文件中
PATH = './best_model.pth'  # 保存的参数文件名
torch.save(model.state_dict(), PATH)
print('Done')
Epoch: 1, duration:7310ms, Train_acc:64.2%, Train_loss:1.113, Test_acc:63.4%,Test_loss:1.112, Lr:1.00E-04,Update the best model
Epoch: 2, duration:6671ms, Train_acc:70.9%, Train_loss:1.035, Test_acc:69.5%,Test_loss:1.035, Lr:1.00E-04,Update the best model
Epoch: 3, duration:6831ms, Train_acc:70.2%, Train_loss:1.028, Test_acc:71.6%,Test_loss:1.022, Lr:1.00E-04,Update the best model
Epoch: 4, duration:6846ms, Train_acc:75.2%, Train_loss:0.984, Test_acc:73.7%,Test_loss:1.026, Lr:1.00E-04,Update the best model
Epoch: 5, duration:6801ms, Train_acc:75.9%, Train_loss:0.983, Test_acc:74.6%,Test_loss:0.981, Lr:1.00E-04,Update the best model
Epoch: 6, duration:6860ms, Train_acc:80.7%, Train_loss:0.935, Test_acc:75.5%,Test_loss:0.986, Lr:1.00E-04,Update the best model
Epoch: 7, duration:6804ms, Train_acc:77.9%, Train_loss:0.960, Test_acc:78.3%,Test_loss:0.960, Lr:1.00E-04,Update the best model
Epoch: 8, duration:6726ms, Train_acc:80.9%, Train_loss:0.929, Test_acc:80.0%,Test_loss:0.940, Lr:1.00E-04,Update the best model
Epoch: 9, duration:6706ms, Train_acc:83.9%, Train_loss:0.903, Test_acc:77.2%,Test_loss:0.966, Lr:1.00E-04
Epoch:10, duration:7202ms, Train_acc:84.6%, Train_loss:0.897, Test_acc:85.3%,Test_loss:0.890, Lr:1.00E-04,Update the best model
Epoch:11, duration:6793ms, Train_acc:86.6%, Train_loss:0.880, Test_acc:81.8%,Test_loss:0.919, Lr:1.00E-04
Epoch:12, duration:6759ms, Train_acc:83.9%, Train_loss:0.902, Test_acc:74.8%,Test_loss:0.998, Lr:1.00E-04
Epoch:13, duration:6781ms, Train_acc:85.8%, Train_loss:0.887, Test_acc:73.2%,Test_loss:1.009, Lr:1.00E-04
Epoch:14, duration:7041ms, Train_acc:85.7%, Train_loss:0.886, Test_acc:80.2%,Test_loss:0.925, Lr:1.00E-04
Epoch:15, duration:7624ms, Train_acc:85.8%, Train_loss:0.884, Test_acc:83.7%,Test_loss:0.899, Lr:1.00E-04
Epoch:16, duration:7630ms, Train_acc:86.8%, Train_loss:0.874, Test_acc:82.8%,Test_loss:0.913, Lr:1.00E-04
Epoch:17, duration:7646ms, Train_acc:88.4%, Train_loss:0.861, Test_acc:86.2%,Test_loss:0.886, Lr:1.00E-04,Update the best model
Epoch:18, duration:7625ms, Train_acc:89.0%, Train_loss:0.853, Test_acc:84.8%,Test_loss:0.897, Lr:1.00E-04
Epoch:19, duration:6874ms, Train_acc:87.2%, Train_loss:0.872, Test_acc:83.2%,Test_loss:0.900, Lr:1.00E-04
Epoch:20, duration:6847ms, Train_acc:87.9%, Train_loss:0.865, Test_acc:84.6%,Test_loss:0.892, Lr:1.00E-04



2.4 结果可视化

"""训练模型--结果可视化"""
epochs_range = range(epochs)

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 and Validation 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 and Validation Loss')
plt.show()

在这里插入图片描述

2.4 指定图片进行预测

def predict_one_image(image_path, model, transform, classes):
    test_img = Image.open(image_path).convert('RGB')
    plt.imshow(test_img)  # 展示预测的图片
    plt.show()

    test_img = transform(test_img)
    img = test_img.to(device).unsqueeze(0)

    model.eval()
    output = model(img)

    _, pred = torch.max(output, 1)
    pred_class = classes[pred]
    print(f'预测结果是:{pred_class}')
 
# 将参数加载到model当中
model.load_state_dict(torch.load(PATH, map_location=device))

"""指定图片进行预测"""
classes = list(total_data.class_to_idx)
# 预测训练集中的某张照片
predict_one_image(image_path=str(Path(data_dir) / "Monkeypox/M01_01_01.jpg"),
                  model=model,
                  transform=train_transforms,
                  classes=classes)

输出

预测结果是:Monkeypox

2.6 模型评估

"""模型评估"""
best_model.eval()
epoch_test_acc, epoch_test_loss = test(test_dl, best_model, loss_fn)
# 查看是否与我们记录的最高准确率一致
print(epoch_test_acc, epoch_test_loss)


输出

0.8624708624708625 0.8900041665349688

3 知识点详解

3.1Inception v1论文解读

论文:Going deeper with convolutions.pdf
在这里插入图片描述

3.1.1 Inception 模块

  为了加深网络,文章中提出了Inception 结构,它是一个能够有效减少参数数量的结构。在之前的卷积网络中,使用多个卷积核并行处理一张图片,不同卷积核之间的输出结果串联成了当前卷积层的输出。而在Inception 结构中,则是使用了多个不同的卷积核并行处理一张图片。具体来说,Inception 模块由四个分支组成:1x1卷积核,3x3卷积核、5x5卷积核以及3x3最大池化,最后将这些不同的分支进行拼接。其中,1x1卷积核的作用是对通道数进行降维,这样就可以降低模型的参数量。此外,文章还针对Inception中可能存在的梯度消失和梯度爆炸问题,提出了使用BN层进行正则化的方式。
在这里插入图片描述
卷积计算量推算过程,如下:
假如前一层的输出为100x100x128,经过具有256个5x5卷积核的卷积层之后(stride=1, pad=2), 输出数据为100x100x256.其中,卷积层的参数为5x5x128x256+256。例如上一层输出先经过具有32个1x1卷积核的卷积层(1x1卷积降低了通道数,且特征图尺寸不变),经过具有256个5x5卷积核的卷积层,最终的输出数据仍为100x100x256,但卷积参数量以及减少为(128x1x1x32+32)+(32x5x5x256+256),参数数量减少为原来的约四分之一。其计算量由原先的8.191x10e9,降低至2.048x10e9。
在这里插入图片描述

3.1.2 网络深度问题

  作者进一步讨论了如何在训练过程中解决网络深度问题。当网络的深度增加时,梯度消失和梯度爆炸问题也随之变得越来越严重,导致网络难以收敛。作者提出的方法是使用一个残差学习的结构来训练网络。Residual Block 将输入值与该层中间结果最后的输出相加,这样网络就可以保证每一层的输出都建立在前面层输出的基础上,避免梯度消失和爆炸问题。

3.1.3 全局平均池化

  此外,作者还提出了一个新的替代方案—— Global Average Pooling。在传统的卷积神经网络中,常常会通过全连接层将卷积层的输出转化为特征,再交给softmax分类。在过去,我们称这些特征为向量,其他领域则将其称之为“词嵌入”(word embedding)。然而,作者认为将这个过程替换为全局平均池化的过程就可以达到同样的效果,做到参数数量的大幅度减少,同时能够保留较好的特征。

3.1.4 卷积层的并行结构

  卷积操作是计算非常耗时的操作,大量的运算需要消耗大量的时间和计算资源。在卷积层的并行结构中,我们使用多组卷积核来同时处理一组待处理数据,以此来提高计算速度。

  在深度卷积网络中,一个卷积层通常使用多组卷积核来对输入数据进行处理,不同的卷积核会提取图片中不同尺寸和不同颜色空间的特征。这样在一个卷积层中我们就可以使用多组卷积核并行处理同一张图片生成多个特征图,每个卷积核专门处理输入数据的不同方面,最终再将每个卷积核生成的特征图进行并集,组合成一个更全面的特征图,这样实现了卷积层并行处理。

3.1.5 1x1 卷积核

  1x1 卷积核是卷积层的核心,在网络中被广泛使用,通常起到降维打击的作用。它一般用来在网络中调整特征图的深度,即指定输出特征含有多少个属性。以1x1的卷积核取代原来的全连接层,可以大大减少网络的参数量和计算量。

  在文章中,作者提出了一个经典的例子:假设有一个 28x28x192 的特征图,如果需要将其连接到一个 2048 个神经元的全连接层中,那么 GAP(全局平均池化)的做法就是使用一个 1x1x2048 的卷积核,而使用全连接层的做法就是需要的参数量为 28x28x192x2048 = 52.43M,而使用 1x1 卷积核的做法需要的参数量为 192x2048 = 393.216K,前者参数的数量相对后者大出100倍级别,这就是 1x1 卷积核的优秀性能所在。

  当然,1x1 卷积核的作用不仅仅是降维,同时还可以通过调整通道数进行增加宽度,增强特征图的多样性和丰富性。

3.1.6 增加了两个辅助分支的作用

(1)避免梯度消失,用于前向传导梯度。反向传播时,如果有一层求导为0,链式求导结果则为0。

(2)将中间某一层输出用作分类,起到模型融合作用,实际测试时,这两个辅助softmax分支会被去掉。 在后续模型的发展中,该方法采用较少。

3.2 网络结构

在这里插入图片描述

4 总结

   大部分流行的CNN是将网络的卷积层堆叠的越来越多,网络越来越深,同时channel越来越宽,网络越来越宽,以此来希望提取更高层的特征,从而得到更好的性能。但单纯的网络堆叠和加宽会带来副作用,包括梯度爆炸和数据量剧增而导致的训练困难的问题等。而Inception的提出,改善了此种现象。

   Inception是用多路分支来并行采用不同的卷积核大小,来提取不同大小感受野所代表的特征。这种分支结构,将单路改变为多路,并行计算,使得网络运行速度更快。而不同大小的卷积核,则代表在不同大小感受野的范围内提取的特征,使得网络可以同时“看到”该位置不同范围的特征,通过后续的concate操作,将不同大小感受野的特征融合起来,综合该位置不同范围的特征。其解读思想更接近于人类的解读方式。

   同时,为减少参数量,在分支中,使用1x1卷积将channel维度进行降维,提取特征后再次使用1x1卷积进行channel维度的回升,看似繁琐,却将参数量大大降低。而且,这样的操作,也在无形中增加了网络的深度,提取了更高维的特征。这种降维操作类似于将一个大矩阵转化为一个小矩阵,转化的过程中会提取大矩阵的“精华”,去除冗余信息。而升维操作则类似于将小矩阵又转化为原始大小的大矩阵,方便不同分支的特征融合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值