使用 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.xdnf.cn/news/140433.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

python随手小练3

题目: 写出一个判断闰年的python代码: 闰年的条件: 如果N能够被4整除,并且不能被100整除,则是闰年 或者:N能被400整除,也是闰年 即:4年一润并且百年不润,每400年再润一…

Verilog 不同编码风格对综合电路的影响

文章目录 示例 #1示例 #2示例 #3 Verilog是一种硬件描述语言(HDL),用于设计数字电路和系统。统一、良好的代码编写风格,可以提高代码的可维护性和可读性。 同样的功能,不同的Verilog 编码风格也会对综合过程产生重大影…

安全远程访问工具

什么是安全远程访问 安全远程访问是指一种 IT 安全策略,允许对企业网络、任务关键型系统或任何机密数据进行授权、受控访问。它使 IT 团队能够根据员工和第三方的角色和工作职责为其提供不同级别的访问权限,安全的远程访问方法可保护系统和应用程序&…

软件设计模式系列之十三——享元模式

1 模式的定义 享元模式(Flyweight Pattern)是一种结构型设计模式,它旨在减少内存占用或计算开销,通过共享大量细粒度对象来提高系统的性能。这种模式适用于存在大量相似对象实例,但它们的状态可以外部化(e…

Android滑动片段

本文所有的代码均存于 https://github.com/MADMAX110/BitsandPizzas 回到BitsandPizzas应用,之前已经创建过创建订单和发出反馈等功能。 修改披萨应用,让它使用标签页导航。在工具条下显示一组标签页,每个选项对应一个不同的标签页。用户单击…

ThreeJS-3D教学一基础场景创建

Three.js 是一个开源的 JS 3D 图形库,用于创建和展示高性能、交互式的 3D 图形场景。它建立在 WebGL 技术之上,并提供了丰富的功能和工具,使开发者可以轻松地构建令人惊叹的 3D 可视化效果。 Three.js 提供了一套完整的工具和 API&#xff0…

知识图谱:信息抽取简易流程

目录 一、标注训练数据 二、训练数据模型 三、实现NER 一、标注训练数据 使用工具:Brat ## BRAT安装 0、安装条件 (1)运行于Linux系统 (2)brat(v1.3p1)仅支持python2版本运行使用,否则会报错 File "standalone.py", line 257except SystemExit, sts:^Syn…

c++中关于Thread Affinity(线程亲和性)示例源码

win10下,可以在任务管理器里面设置某个进程的线程亲和性,如下图: 然后选择相关的cpu,如下图: 这么做可以使得相关的线程在某些密集型计算任务中只会运行在某些指定的cpu上,以便提高性能。 以下是windwos上c程序中应用Thread Affi…

Python 运行代码

一、Python运行代码 可以使用三种方式运行Python,如下: 1、交互式 通过命令行窗口进入 Python 并开始在交互式解释器中开始编写 Python 代码 2、命令行脚本 可以把代码放到文件中,通过python 文件名.py命令执行代码,如下&#xff…

使用 LangChain 和 Elasticsearch 对私人数据进行人工智能搜索

关于本博文的所有代码可以在地址下载:GitHub - liu-xiao-guo/python-vector-private 我将在本博文中其中深入研究人工智能和向量嵌入的深水区。 ChatGPT 令人大开眼界,但有一个主要问题。 这是一个封闭的托管系统。 在一个被大型网络公司改变的世界里生…

【轨道机器人】成功驱动伺服电机(学生电源、DCH调试软件、DH系列伺服驱动器)

1、硬件平台 工控机 学生电源 DH系列伺服驱动器 电机 调试平台:DCH 2、如何利用dch驱动电机 点击可驱动电机 下面的步骤是比较关键的几步: 3、遇到的问题 不能成功驱动电机,还和厂家那边打电话,询问 发现是这…

【C++】bitset位图的简单模拟实现及常见面试题

文章目录 前言一、 bitset模拟实现二、 常见面试题1.给你一百亿个整数,找到只出现一次的数字2. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集? 前言 快速查找某个数据是否在一个集合中排序 去重…

Hdoop伪分布式集群搭建

文章目录 Hadoop安装部署前言1.环境2.步骤3.效果图 具体步骤(一)前期准备(1)ping外网(2)配置主机名(3)配置时钟同步(4)关闭防火墙 (二&#xff09…

ddns有什么作用?无公网IP怎么将内网IP端口映射外网访问

DDNS是什么? DDNS英文全称Dynamic Domain Name Server,中文含义是指动态域名服务。很多普通路由器或者智能路由器设置中,都可以找到DDNS(动态DNS)功能。 上面的解释可能过于专业,其实DDNS通俗点说&#xf…

小程序社区团购demo

概述 实现了用户登录或者手机号,加入团长,邀请团长,各种佣金明细等页面 详细 需求: 根据市场信息反馈,社区团购比较火,有流量的用户可以推广页面 实现了功能: 实现了用户微信登录自动获取…

BottomNavigationView3个以上图标不显示文字

问题 当BottomNavigationView设置的菜单中超过三个图标时,出现只有焦点聚集到图标时才会显示底部设置的文字描述,当没有焦点聚集则只显示图标,效果如下: 解决办法 设置labelVisibilityMode值 如果BottomNavigationItemView类并…

Clock时钟电路PCB设计布局布线要求

时钟电路就是类似像时钟一样准确运动的震荡电路,任何工作都是依照时间顺序,那么产生这个时间的电路就是时钟电路,时钟电路一般是由晶体振荡器、晶振、控制芯片以及匹配电容组成,如图1所示。 图1 时钟电路 针对时钟电路PCB设计有以…

k8s pod概念、分类及策略

目录 一.pod相关概念 2.Kubrenetes集群中Pod两种使用方式 3.pause容器的Pod中的所有容器共享的资源 4.kubernetes中的pause容器主要为每个容器提供功能: 6.Pod分为两类: 二.Pod容器的分类 1.基础容器…

【C++心愿便利店】No.6---C++之拷贝构造函数

文章目录 一、拷贝构造函数的引入二、拷贝构造函数 👧个人主页:小沈YO. 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:C 心愿便利店 🔑本章内容:拷贝构造函数 记得 评…

PythonWeb服务器(HTTP协议)

一、HTTP协议与实现原理 HTTP(Hypertext Transfer Protocol,超文本传输协议)是一种用于在网络上传输超文本数据的协议。它是Web应用程序通信的基础,通过客户端和服务器之间的请求和响应来传输数据。在HTTP协议中连接客户与服务器的…