使用 PyTorch 的计算机视觉简介 (5/6)

一、说明

本文主要介绍CNN中在pytorch的实现,其中VGG16网络,数据集来源,以及训练过程,模型生成和存储,模型调入等。

二、预训练模型和迁移学习

        训练 CNN 可能需要大量时间,并且该任务需要大量数据。但是,大部分时间都花在学习网络用来从图像中提取模式的最佳低级过滤器上。一个自然的问题出现了——我们是否可以使用在一个数据集上训练的神经网络,并在没有完整训练过程的情况下对其进行分类以对不同的图像进行分类?

        这种方法称为迁移学习,因为我们将一些知识从一个神经网络模型转移到另一个神经网络模型。在迁移学习中,我们通常从一个预先训练的模型开始,该模型已经在一些大型图像数据集(如 ImageNet)上进行了训练。这些模型已经可以从通用图像中提取不同的特征,在许多情况下,只需在这些提取的特征之上构建分类器就可以产生良好的结果。

!wget https://raw.githubusercontent.com/MicrosoftDocs/pytorchfundamentals/main/computer-vision-pytorch/pytorchcv.py
!pip install -r https://raw.githubusercontent.com/MicrosoftDocs/pytorchfundamentals/main/computer-vision-pytorch/requirements.txt
import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
from torchinfo import summary
import numpy as np
import osfrom pytorchcv import train, plot_results, display_dataset, train_long, check_image_dir

三、探索猫与狗数据集

        在本单元中,我们将解决对猫和狗的图像进行分类的现实问题。出于这个原因,我们将使用Kaggle Cats vs. Dogs数据集。让我们下载此数据集并将其提取到数据目录中:

if not os.path.exists('data/kagglecatsanddogs_5340.zip'):!wget -P data -q https://download.microsoft.com/download/3/E/1/3E1C3F21-ECDB-4869-8368-6DEBA77B919F/kagglecatsanddogs_5340.zip
import zipfile
if not os.path.exists('data/PetImages'):with zipfile.ZipFile('data/kagglecatsanddogs_5340.zip', 'r') as zip_ref:zip_ref.extractall('data')

        不幸的是,数据集中有一些损坏的图像文件。我们需要进行快速清理以检查损坏的文件。为了不破坏这个笔记本,我们将验证数据集的代码移到了一个模块中,我们在这里调用它。check_image_dir逐个图像遍历整个数据集,尝试加载图像并检查是否可以正确加载。所有损坏的图像都将被删除。

check_image_dir('data/PetImages/Cat/*.jpg')
check_image_dir('data/PetImages/Dog/*.jpg')
Corrupt image: data/PetImages/Cat/666.jpg
Corrupt image: data/PetImages/Dog/11702.jpg
/anaconda/envs/py38_default/lib/python3.8/site-packages/PIL/TiffImagePlugin.py:845: UserWarning: Truncated File Readwarnings.warn(str(msg))

        接下来,让我们将图像加载到 PyTorch 数据集中,将它们转换为张量并进行一些规范化。我们通过使用 Compose 组合几个基元转换来定义图像转换管道:

  • 调整大小会将图像大小调整为 256 × 256 个维度。
  • CenterCrop获取大小为224×224的图像的中心部分。预先训练的 VGG 网络已经在 224 × 224 张图像上进行了训练,因此我们需要将数据集调整到这个大小。
  • ToTensor 将像素强度标准化为 0 — 1 范围内,并将图像转换为 PyTorch 张量
  • std_normalize转换是特定于 VGG 网络的附加规范化步骤。在训练 VGG 网络时,来自 ImageNet 的原始图像通过用颜色减去数据集平均强度并除以标准差(也按颜色)进行转换。因此,我们需要对数据集应用相同的转换,以便正确处理所有图像。

我们将图像大小调整为 256 大小,然后裁剪为 224 像素有几个原因:

  • 我们想展示更多可能的转换。
  • 宠物通常位于图像的中央部分,因此我们可以通过更多地关注中心部分来改善分类。
  • 由于某些图像不是正方形的,因此我们最终会拥有不包含任何有用图片数据的图像填充部分,并且稍微裁剪图像会减少填充部分。
std_normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])
trans = transforms.Compose([transforms.Resize(256),transforms.CenterCrop(224),transforms.ToTensor(), std_normalize])
dataset = torchvision.datasets.ImageFolder('data/PetImages',transform=trans)
trainset, testset = torch.utils.data.random_split(dataset,[20000,len(dataset)-20000])display_dataset(dataset)

四、预训练模型

        火炬视觉模块中有许多不同的预训练模型,甚至可以在互联网上找到更多模型。让我们看看如何加载和使用最简单的 VGG-16 模型。首先,我们将下载存储在本地存储库中的 VGG-16 模型的权重。

# Download model weights in the sandbox environment
!mkdir -p models
!wget -P models https://github.com/MicrosoftDocs/pytorchfundamentals/raw/main/computer-vision-pytorch/vgg16-397923af.pth

        接下来,我们将使用 load_state_dict 方法将权重加载到预训练的 VGG-16 模型中。然后,使用 eval 方法将模型设置为推理模式。

file_path = 'models/vgg16-397923af.pth'vgg = torchvision.models.vgg16()
vgg.load_state_dict(torch.load(file_path))
vgg.eval()sample_image = dataset[0][0].unsqueeze(0)
res = vgg(sample_image)
print(res[0].argmax())
tensor(282)

        我们收到的结果是 ImageNet 类的编号,可以在此处查找。我们可以使用下面的代码来自动加载这个类表并返回结果:

import json, requests
class_map = json.loads(requests.get("https://raw.githubusercontent.com/MicrosoftDocs/pytorchfundamentals/main/computer-vision-pytorch/imagenet_class_index.json").text)
class_map = { int(k) : v for k,v in class_map.items() }class_map[res[0].argmax().item()]
['n02123159', 'tiger_cat']

        我们还可以看看 VGG-16 网络的架构:

==========================================================================================
Layer (type:depth-idx)                   Output Shape              Param #
==========================================================================================
VGG                                      [1, 1000]                 --
├─Sequential: 1-1                        [1, 512, 7, 7]            --
│    └─Conv2d: 2-1                       [1, 64, 224, 224]         1,792
│    └─ReLU: 2-2                         [1, 64, 224, 224]         --
│    └─Conv2d: 2-3                       [1, 64, 224, 224]         36,928
│    └─ReLU: 2-4                         [1, 64, 224, 224]         --
│    └─MaxPool2d: 2-5                    [1, 64, 112, 112]         --
│    └─Conv2d: 2-6                       [1, 128, 112, 112]        73,856
│    └─ReLU: 2-7                         [1, 128, 112, 112]        --
│    └─Conv2d: 2-8                       [1, 128, 112, 112]        147,584
│    └─ReLU: 2-9                         [1, 128, 112, 112]        --
│    └─MaxPool2d: 2-10                   [1, 128, 56, 56]          --
│    └─Conv2d: 2-11                      [1, 256, 56, 56]          295,168
│    └─ReLU: 2-12                        [1, 256, 56, 56]          --
│    └─Conv2d: 2-13                      [1, 256, 56, 56]          590,080
│    └─ReLU: 2-14                        [1, 256, 56, 56]          --
│    └─Conv2d: 2-15                      [1, 256, 56, 56]          590,080
│    └─ReLU: 2-16                        [1, 256, 56, 56]          --
│    └─MaxPool2d: 2-17                   [1, 256, 28, 28]          --
│    └─Conv2d: 2-18                      [1, 512, 28, 28]          1,180,160
│    └─ReLU: 2-19                        [1, 512, 28, 28]          --
│    └─Conv2d: 2-20                      [1, 512, 28, 28]          2,359,808
│    └─ReLU: 2-21                        [1, 512, 28, 28]          --
│    └─Conv2d: 2-22                      [1, 512, 28, 28]          2,359,808
│    └─ReLU: 2-23                        [1, 512, 28, 28]          --
│    └─MaxPool2d: 2-24                   [1, 512, 14, 14]          --
│    └─Conv2d: 2-25                      [1, 512, 14, 14]          2,359,808
│    └─ReLU: 2-26                        [1, 512, 14, 14]          --
│    └─Conv2d: 2-27                      [1, 512, 14, 14]          2,359,808
│    └─ReLU: 2-28                        [1, 512, 14, 14]          --
│    └─Conv2d: 2-29                      [1, 512, 14, 14]          2,359,808
│    └─ReLU: 2-30                        [1, 512, 14, 14]          --
│    └─MaxPool2d: 2-31                   [1, 512, 7, 7]            --
├─AdaptiveAvgPool2d: 1-2                 [1, 512, 7, 7]            --
├─Sequential: 1-3                        [1, 1000]                 --
│    └─Linear: 2-32                      [1, 4096]                 102,764,544
│    └─ReLU: 2-33                        [1, 4096]                 --
│    └─Dropout: 2-34                     [1, 4096]                 --
│    └─Linear: 2-35                      [1, 4096]                 16,781,312
│    └─ReLU: 2-36                        [1, 4096]                 --
│    └─Dropout: 2-37                     [1, 4096]                 --
│    └─Linear: 2-38                      [1, 1000]                 4,097,000
==========================================================================================
Total params: 138,357,544
Trainable params: 138,357,544
Non-trainable params: 0
Total mult-adds (G): 15.48
==========================================================================================
Input size (MB): 0.60
Forward/backward pass size (MB): 108.45
Params size (MB): 553.43
Estimated Total Size (MB): 662.49
==========================================================================================

        除了我们已经知道的图层之外,还有另一种称为 Dropout 的图层类型。这些层充当正则化技术。正则化对学习算法进行了轻微的修改,以便模型更好地泛化。在训练期间,辍学层丢弃前一层中一定比例(约30%)的神经元,并且在没有它们的情况下进行训练。这有助于使优化过程摆脱局部最小值,并在不同的神经路径之间分配决定性的力量,从而提高网络的整体稳定性。

五、图形处理计算

        深度神经网络,如VGG-16和其他更现代的架构,需要相当多的计算能力才能运行。如果可用,使用 GPU 加速是有意义的。为此,我们需要显式地将计算中涉及的所有张量移动到 GPU。

通常的做法是检查代码中 GPU 的可用性,并定义指向计算设备的设备变量 - GPU 或 CPU。

device = 'cuda' if torch.cuda.is_available() else 'cpu'print('Doing computations on device = {}'.format(device))vgg.to(device)
sample_image = sample_image.to(device)vgg(sample_image).argmax()

六、提取 VGG 功能

        如果我们想使用 VGG-16 从图像中提取特征,我们需要没有最终分类层的模型。事实上,这个“特征提取器”可以使用 vgg.features 方法获得:

res = vgg.features(sample_image).cpu()
plt.figure(figsize=(15,3))
plt.imshow(res.detach().view(-1,512))
print(res.size())
torch.Size([1, 512, 7, 7]) 

        特征张量的维度是 512 × 7 × 7,但为了可视化它,我们必须将其重塑为 2D 形式。

        现在让我们尝试看看这些功能是否可用于对图像进行分类。
让我们手动获取一部分图像(在我们的例子中为 800),并预先计算它们的特征向量。我们将结果存储在一个名为 feature_tensor 的大张量中,并将标签也存储在label_tensor中:

bs = 8
dl = torch.utils.data.DataLoader(dataset,batch_size=bs,shuffle=True)
num = bs*100
feature_tensor = torch.zeros(num,512*7*7).to(device)
label_tensor = torch.zeros(num).to(device)
i = 0
for x,l in dl:with torch.no_grad():f = vgg.features(x.to(device))feature_tensor[i:i+bs] = f.view(bs,-1)label_tensor[i:i+bs] = li+=bsprint('.',end='')if i>=num:break

        现在我们可以定义从这个张量中获取数据vgg_dataset,使用 random_split 函数将其拆分为训练集和测试集,并在提取的特征之上训练一个小型的单层密集分类器网络:

vgg_dataset = torch.utils.data.TensorDataset(feature_tensor,label_tensor.to(torch.long))
train_ds, test_ds = torch.utils.data.random_split(vgg_dataset,[700,100])train_loader = torch.utils.data.DataLoader(train_ds,batch_size=32)
test_loader = torch.utils.data.DataLoader(test_ds,batch_size=32)net = torch.nn.Sequential(torch.nn.Linear(512*7*7,2),torch.nn.LogSoftmax()).to(device)history = train(net,train_loader,test_loader)
Epoch  0, Train acc=0.899, Val acc=0.960, Train loss=0.099, Val loss=0.050
Epoch  1, Train acc=0.989, Val acc=0.970, Train loss=0.016, Val loss=0.037
Epoch  2, Train acc=0.996, Val acc=0.970, Train loss=0.003, Val loss=0.155
Epoch  3, Train acc=0.999, Val acc=0.960, Train loss=0.001, Val loss=0.024
Epoch  4, Train acc=1.000, Val acc=0.970, Train loss=0.000, Val loss=0.026
Epoch  5, Train acc=1.000, Val acc=0.970, Train loss=0.000, Val loss=0.026
Epoch  6, Train acc=1.000, Val acc=0.970, Train loss=0.000, Val loss=0.026
Epoch  7, Train acc=1.000, Val acc=0.970, Train loss=0.000, Val loss=0.026
Epoch  8, Train acc=1.000, Val acc=0.970, Train loss=0.000, Val loss=0.026
Epoch  9, Train acc=1.000, Val acc=0.970, Train loss=0.000, Val loss=0.026 

        结果很棒,我们可以以几乎 98% 的概率区分猫和狗!但是,我们只在所有图像的一小部分上测试了这种方法,因为手动特征提取似乎需要花费大量时间。

七、使用 VGG 网络进行迁移学习

        我们还可以通过在训练期间使用原始 VGG-16 网络作为一个整体来避免手动预计算特征。让我们看一下 VGG-16 对象结构:

print(vgg)
VGG((features): Sequential((0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(1): ReLU(inplace=True)(2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(3): ReLU(inplace=True)(4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(6): ReLU(inplace=True)(7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(8): ReLU(inplace=True)(9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(11): ReLU(inplace=True)(12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(13): ReLU(inplace=True)(14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(15): ReLU(inplace=True)(16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(18): ReLU(inplace=True)(19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(20): ReLU(inplace=True)(21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(22): ReLU(inplace=True)(23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(25): ReLU(inplace=True)(26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(27): ReLU(inplace=True)(28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(29): ReLU(inplace=True)(30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False))(avgpool): AdaptiveAvgPool2d(output_size=(7, 7))(classifier): Sequential((0): Linear(in_features=25088, out_features=4096, bias=True)(1): ReLU(inplace=True)(2): Dropout(p=0.5, inplace=False)(3): Linear(in_features=4096, out_features=4096, bias=True)(4): ReLU(inplace=True)(5): Dropout(p=0.5, inplace=False)(6): Linear(in_features=4096, out_features=1000, bias=True))
)

您可以看到网络包含:

  • 特征提取器(特征),由许多卷积层和池化层组成
  • 平均池化层(平均池)
  • 最终分类器 (fc),由几个密集层组成,可将 25088 个输入要素转换为 1000 个类(这是 ImageNet 中的类数)

要训练将对数据集进行分类的端到端模型,我们需要:

  • 最终分类器替换为将生成所需数量类的分类器。在我们的例子中,我们可以使用一个具有 25088 个输入和 2 个输出神经元的线性层。
  • 冻结卷积特征提取器的权重,以便不对其进行训练。建议首先进行此冻结,因为否则未经训练的分类器层可能会破坏卷积提取器的原始预训练权重。冻结权重可以通过将所有参数的属性设置为 False 来完成requires_grad
vgg.classifier = torch.nn.Linear(25088,2).to(device)for x in vgg.features.parameters():x.requires_grad = Falsesummary(vgg,(1, 3,244,244))
==========================================================================================
Layer (type:depth-idx)                   Output Shape              Param #
==========================================================================================
VGG                                      [1, 2]                    --
├─Sequential: 1-1                        [1, 512, 7, 7]            --
│    └─Conv2d: 2-1                       [1, 64, 244, 244]         (1,792)
│    └─ReLU: 2-2                         [1, 64, 244, 244]         --
│    └─Conv2d: 2-3                       [1, 64, 244, 244]         (36,928)
│    └─ReLU: 2-4                         [1, 64, 244, 244]         --
│    └─MaxPool2d: 2-5                    [1, 64, 122, 122]         --
│    └─Conv2d: 2-6                       [1, 128, 122, 122]        (73,856)
│    └─ReLU: 2-7                         [1, 128, 122, 122]        --
│    └─Conv2d: 2-8                       [1, 128, 122, 122]        (147,584)
│    └─ReLU: 2-9                         [1, 128, 122, 122]        --
│    └─MaxPool2d: 2-10                   [1, 128, 61, 61]          --
│    └─Conv2d: 2-11                      [1, 256, 61, 61]          (295,168)
│    └─ReLU: 2-12                        [1, 256, 61, 61]          --
│    └─Conv2d: 2-13                      [1, 256, 61, 61]          (590,080)
│    └─ReLU: 2-14                        [1, 256, 61, 61]          --
│    └─Conv2d: 2-15                      [1, 256, 61, 61]          (590,080)
│    └─ReLU: 2-16                        [1, 256, 61, 61]          --
│    └─MaxPool2d: 2-17                   [1, 256, 30, 30]          --
│    └─Conv2d: 2-18                      [1, 512, 30, 30]          (1,180,160)
│    └─ReLU: 2-19                        [1, 512, 30, 30]          --
│    └─Conv2d: 2-20                      [1, 512, 30, 30]          (2,359,808)
│    └─ReLU: 2-21                        [1, 512, 30, 30]          --
│    └─Conv2d: 2-22                      [1, 512, 30, 30]          (2,359,808)
│    └─ReLU: 2-23                        [1, 512, 30, 30]          --
│    └─MaxPool2d: 2-24                   [1, 512, 15, 15]          --
│    └─Conv2d: 2-25                      [1, 512, 15, 15]          (2,359,808)
│    └─ReLU: 2-26                        [1, 512, 15, 15]          --
│    └─Conv2d: 2-27                      [1, 512, 15, 15]          (2,359,808)
│    └─ReLU: 2-28                        [1, 512, 15, 15]          --
│    └─Conv2d: 2-29                      [1, 512, 15, 15]          (2,359,808)
│    └─ReLU: 2-30                        [1, 512, 15, 15]          --
│    └─MaxPool2d: 2-31                   [1, 512, 7, 7]            --
├─AdaptiveAvgPool2d: 1-2                 [1, 512, 7, 7]            --
├─Linear: 1-3                            [1, 2]                    50,178
==========================================================================================
Total params: 14,764,866
Trainable params: 50,178
Non-trainable params: 14,714,688
Total mult-adds (G): 17.99
==========================================================================================
Input size (MB): 0.71
Forward/backward pass size (MB): 128.13
Params size (MB): 59.06
Estimated Total Size (MB): 187.91
============================================================

        从摘要中可以看出,该模型总共包含大约 15 万个参数,但其中只有 50k 个参数是可训练的——这些是分类层的权重。这很好,因为我们能够用更少的示例微调较少数量的参数。

        现在,让我们使用原始数据集训练模型。这个过程需要很长时间,因此我们将使用 train_long 函数,该函数将打印一些中间结果,而无需等待纪元结束。强烈建议在启用 GPU 的计算上运行此培训!

trainset, testset = torch.utils.data.random_split(dataset,[20000,len(dataset)-20000])
train_loader = torch.utils.data.DataLoader(trainset,batch_size=16)
test_loader = torch.utils.data.DataLoader(testset,batch_size=16)train_long(vgg,train_loader,test_loader,loss_fn=torch.nn.CrossEntropyLoss(),epochs=1,print_freq=90)
Epoch 0, minibatch 0: train acc = 0.375, train loss = 0.049225371330976486
Epoch 0, minibatch 90: train acc = 0.945054945054945, train loss = 0.13971448206639553
Epoch 0, minibatch 180: train acc = 0.9537292817679558, train loss = 0.13658707971730943
Epoch 0, minibatch 270: train acc = 0.959409594095941, train loss = 0.12419342378848593
Epoch 0, minibatch 360: train acc = 0.9643351800554016, train loss = 0.1132235328906791
Epoch 0, minibatch 450: train acc = 0.9657705099778271, train loss = 0.1185816914966524
Epoch 0, minibatch 540: train acc = 0.9660351201478743, train loss = 0.1288116652511625
Epoch 0, minibatch 630: train acc = 0.9667194928684627, train loss = 0.12467173048886936
Epoch 0, minibatch 720: train acc = 0.9690533980582524, train loss = 0.11865157128703081
Epoch 0, minibatch 810: train acc = 0.9700215782983971, train loss = 0.12006495766222257
Epoch 0, minibatch 900: train acc = 0.9701720310765816, train loss = 0.13083069491201182
Epoch 0, minibatch 990: train acc = 0.9709258324924319, train loss = 0.13124172237638748
Epoch 0, minibatch 1080: train acc = 0.9714962997224792, train loss = 0.13153546215978407
Epoch 0, minibatch 1170: train acc = 0.9718723313407344, train loss = 0.13723152870810204
Epoch 0 done, validation acc = 0.9795918367346939, validation loss = 0.12322273880255227 

看起来我们已经获得了相当准确的猫与狗分类器!让我们保存它以备将来使用!

torch.save(vgg,'data/cats_dogs.pth')

然后,我们可以随时从文件加载模型。您可能会发现它在下一个实验破坏模型的情况下很有用。

vgg = torch.load('data/cats_dogs.pth')

八、微调迁移学习

        在上一节中,我们训练了最终分类器层来对自己数据集中的图像进行分类。但是,我们没有重新训练特征提取器,我们的模型依赖于模型在 ImageNet 数据上学习的特征。如果您的对象在视觉上与普通 ImageNet 图像不同,则这种功能组合可能无法发挥最佳效果。因此,开始训练卷积层也是有意义的。

为此,我们可以解冻之前冻结的卷积过滤器参数。

注意:请务必先冻结参数并执行多个训练周期,以稳定分类层中的权重。如果您立即开始使用未冻结的参数训练端到端网络,则较大的错误可能会破坏卷积层中预先训练的权重。

for x in vgg.features.parameters():x.requires_grad = True

解冻后,我们可以再做几个时期的训练。您还可以选择较低的学习率,以尽量减少对预先训练的权重的影响。但是,即使学习率较低,您也可以预期在训练开始时准确性会下降,直到最终达到比固定权重略高的水平。

注意:这种训练发生得要慢得多,因为我们需要将梯度传播回网络的许多层!您可能需要观察前几个小批量以查看趋势,然后停止计算。

train_long(vgg,train_loader,test_loader,loss_fn=torch.nn.CrossEntropyLoss(),epochs=1,print_freq=90,lr=0.0001)

九、其他CV模型

        VGG-16 是最简单的计算机视觉架构之一。
        火炬视觉软件包提供了更多预先训练的网络。其中最常用的是Microsoft开发的ResNet架构和        Google开发的Inception。例如,让我们探索最简单的 ResNet-18 模型的架构(ResNet 是具有不同深度的模型家族,如果你想看看真正深度的模型是什么样子,可以尝试使用 ResNet-151 进行实验):

resnet = torchvision.models.resnet18()
print(resnet)
ResNet((conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(relu): ReLU(inplace=True)(maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)(layer1): Sequential((0): BasicBlock((conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(relu): ReLU(inplace=True)(conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True))(1): BasicBlock((conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(relu): ReLU(inplace=True)(conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)))(layer2): Sequential((0): BasicBlock((conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)(bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(relu): ReLU(inplace=True)(conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(downsample): Sequential((0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2), bias=False)(1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)))(1): BasicBlock((conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(relu): ReLU(inplace=True)(conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)))(layer3): Sequential((0): BasicBlock((conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(relu): ReLU(inplace=True)(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(downsample): Sequential((0): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2), bias=False)(1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)))(1): BasicBlock((conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(relu): ReLU(inplace=True)(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)))(layer4): Sequential((0): BasicBlock((conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(relu): ReLU(inplace=True)(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(downsample): Sequential((0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)(1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)))(1): BasicBlock((conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(relu): ReLU(inplace=True)(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)))(avgpool): AdaptiveAvgPool2d(output_size=(1, 1))(fc): Linear(in_features=512, out_features=1000, bias=True)
)

        如您所见,该模型包含相同的构建块:特征提取器和最终分类器 (fc)。这使我们能够以与使用 VGG-16 进行迁移学习完全相同的方式使用此模型。您可以尝试使用上面的代码进行试验,使用不同的 ResNet 模型作为基本模型,并查看准确性如何变化。

十、批量规范化

        此网络包含另一种类型的层:批量规范化。批量归一化的想法是将流经神经网络的值带到正确的间隔。通常,当所有值都在 [-1, 1] 或 [0, 1] 范围内时,神经网络效果最好,这就是我们相应地缩放/规范化输入数据的原因。

        但是,在深度网络的训练过程中,可能会发生值明显超出此范围的情况,这使得训练成为问题。批量归一化层计算当前小批量所有值的平均值和标准偏差,并在信号通过神经网络层之前使用它们对信号进行归一化。这显著提高了深度网络的稳定性。

十一、结论

        使用迁移学习,我们能够快速为自定义对象分类任务组合分类器,并实现高精度。然而,这个例子并不完全公平,因为最初的VGG-16网络是预先训练来识别猫和狗的,因此我们只是重用了网络中已经存在的大多数模式。对于更奇特的领域特定对象,例如植物生产线的详细信息或不同的树叶,您可能会期望精度较低。V笔记本

        你可以看到,我们现在解决的更复杂的任务需要更高的计算能力,在CPU上不容易解决。在下一个单元中,我们将尝试使用更轻量级的实现来使用较低的计算资源来训练相同的模型,这会导致准确性略低。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python+Django前后端分离

程序示例精选 PythonDjango前后端分离 如需安装运行环境或远程调试,见文章底部个人QQ名片,由专业技术人员远程协助! 前言 这篇博客针对《PythonDjango前后端分离》编写代码,代码整洁,规则,易读。 学习与应…

安防视频/集中云存储平台EasyCVR(V3.3)部分通道显示离线该如何解决?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等,以及支持厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

c语言练习67:写一个宏,可以将一个整数的二进制位的奇数位和偶数位交换。

写一个宏&#xff0c;可以将一个整数的二进制位的奇数位和偶数位交换。 #define SwapIntBit(n) (((n) & 0x55555555) << 1 | ((n) & 0xaaaaaaaa) >> 1) 交换奇偶位&#xff0c;需要先分别拿出奇偶位。既然是宏&#xff0c;分别拿出用循环不是很现实&…

消息中间件(二)——kafka

文章目录 Apache Kafka综述什么是消息系统&#xff1f;点对点消息类型发布-订阅消息类型 什么是Kafka?优点关键术语Kafka基本原理用例 Apache Kafka综述 在大数据中&#xff0c;会使用到大量的数据。面对这些海量的数据&#xff0c;我们一是需要做到能够收集这些数据&#xf…

Vue3 中的虚拟DOM、 h() 函数,渲染函数,渲染器知识点一网打尽!

在平常开发阶段我们总是分不清虚拟 DOM、 h() 函数、渲染函数和渲染器的知识。笔者在翻阅相关文档之后&#xff0c;总结了下面这些知识点。 h() 函数用于创建虚拟 DOM&#xff0c;渲染函数的作用就是返回虚拟 DOM。因此&#xff0c;我们可以在渲染函数中使用 h() 创建虚拟 DOM…

【KingFusion】如何在3D场景实现流水效果

哈喽&#xff0c;大家好,我是雷工&#xff01; 在项目过程中&#xff0c;经常会涉及到实现管道水流动效果&#xff0c;此篇记录在KingFusion中的3D场景实现水流效果。 以下为简单流水效果的样例&#xff0c; 一、效果展示 当点击水泵&#xff0c;水泵启动&#xff0c;显示流水…

知网G4期刊-基础教育论坛-如何投稿?

《基础教育论坛》知网 3版5000字符 24年上半年刊期&#xff0c;可收中小学基础教育&#xff0c;幼儿教育等教育全科文章。 《基础教育论坛》主要刊登有关教育教学理论探讨及课程改革、教学改革、考试改革研究等方面的文章&#xff0c;为广大基础教育工作者提供学术交流的…

卫星图像应用 - 洪水检测 数据预处理

执行环境&#xff1a;Google Colab !pip install basemap下载basemap用于在地图上绘制2D数据。 import os import json import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimg import matplotlib.gridspec as gridspec from mpl_toolkits.ba…

逻辑像素与物理像素引发学习型探索

文章目录 目的关于像素从像素到分辨率DP(设备像素&#xff09;- 物理像素DIP(逻辑像素&#xff09;- 设备独立像素CSS 像素屏幕特性 DRP&#xff08;设备像素比&#xff09;PPI(Pixels Per Inch ) - 像素密度屏幕像素密度PPI 目的 做一个前端或或者产品开发者&#xff0c; 在涉…

MySQL基础篇-约束

目录 1.约束概述 2.分类 3.测试user表的约束情况 主键约束 非空约束及唯一约束 检查约束 默认约束 4.外键约束 外键约束的语法 外键约束的删除/更新行为 小结 1.约束概述 MySQL约束&#xff08;Constraints&#xff09;是用于确保表中数据完整性和一致性的规则。它们定…

ArrayList 的扩容机制

ArrayList扩容的本质就是计算出新的扩容数组的size后实例化&#xff0c;并将原有数组内容复制到新数组中去。默认情况下&#xff0c;新的容量会是原容量的1.5倍。以JDK1.8为例说明 ArrayList的构造方法有三种&#xff1a; 第一个构造方法用来返回一个初始容量为10的数组 在初…

C++学习资源

https://www.cnblogs.com/xueweihan/p/13928719.html GitHub - Light-City/CPlusPlusThings: C那些事 GitHub - 0voice/introduce_c-cpp_manual: 一个收集C/C新手学习的入门项目&#xff0c;整理收纳开发者开源的小项目、工具、框架、游戏等&#xff0c;视频&#xff0c;书籍&a…

Oracle实现主键字段自增

Oracle实现主键自增有4种方式&#xff1a; Identity Columns新特性自增&#xff08;Oracle版本≥12c&#xff09;创建自增序列&#xff0c;创建表时&#xff0c;给主键字段默认使用自增序列创建自增序列&#xff0c;使用触发器使主键自增创建自增序列&#xff0c;插入语句&…

ceph分布式存储部署

一、概述 是一个统一的分布式存储系统&#xff0c;设计初衷是提供较好的性能、可靠性和可扩展性。 特点 1、统一存储 虽然 ceph 底层是一个分布式文件系统&#xff0c;但由于在上层开发了支持对象和块的接口。所以在开源存储软件中&#xff0c;能够一统江湖。至于能不能千秋万…

【0223】源码剖析smgr底层设计机制(3)

1. smgr设计机制 PG内核中smgr完整磁盘存储介质的管理是通过下面三部分实现的。 1.1 函数指针结构体 f_smgr 函数指针结构体 f_smgr。 通过该函数指针类型,可完成类似于UNIX系统中的VFD功能,上层只需要调用open()、read()、write()等系统函数,用户不必去关系底层的文件系统…

【Vue.js】使用ElementUI搭建动态树数据表格与分页

一&#xff0c;动态树 本文章为上一篇文章拓展内容》》实现首页导航及左侧菜单 将左侧菜单结构更换为下面代码&#xff1a; 菜单结构&#xff1a; <el-menu><el-submenu index"" key""><template slot"title"><i class…

某高校的毕设

最近通过某个平台接的单子&#xff0c;最后Kali做的测试没有公开可以私聊给教程。 下面是规划与配置 1.vlan方面&#xff1a;推荐一个vlan下的所有主机为一个子网网段 连接电脑和http客户端的接口配置为access接口 交换机与交换机或路由器连接的接口配置为trunk接口---也可以…

MySQL详细案例 1:MySQL主从复制与读写分离

文章目录 1. MySQL主从复制1.1 使用场景1.2 MySQL的复制类型1.3 主从复制的作用1.4 主从复制的工作过程1.5 实现MySQL主从复制1.5.1 前置准备1.5.2 主服务器mysql配置1.5.3 从服务器1 mysql配置1.5.4 从服务器2 mysql配置 1.6 MySQL主从复制延时问题的原因和解决办法1.6.1 故障…

计算机专业毕业设计项目推荐09-个人医疗系统(Spring+Js+Mysql)

个人医疗系统&#xff08;SpringJsMysql&#xff09; **介绍****系统总体开发情况-功能模块****各部分模块实现** 介绍 本系列(后期可能博主会统一为专栏)博文献给即将毕业的计算机专业同学们,因为博主自身本科和硕士也是科班出生,所以也比较了解计算机专业的毕业设计流程以及…

【数据结构与算法】链表的实现以及一些基本算法

目录 单选链表的基本实现 有序列表的合并&#xff08;双指针法&#xff09; 链表的反转 链表实现两数之和 判定链表是否有环 单选链表的基本实现 public class LinkedList1 {//头节点Node first;//尾节点Node last;//大小int size 0;//头插法public void addFirst(int…