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

news2024/11/25 2:58:23

一、说明

        本文主要介绍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 os

from 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 Read
  warnings.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] = l
        i+=bs
        print('.',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 = False

summary(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.coloradmin.cn/o/1033809.html

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

相关文章

【每日一题】441. 排列硬币

441. 排列硬币 - 力扣(LeetCode) 你总共有 n 枚硬币,并计划将它们按阶梯状排列。对于一个由 k 行组成的阶梯,其第 i 行必须正好有 i 枚硬币。阶梯的最后一行 可能 是不完整的。 给你一个数字 n ,计算并返回可形成 完整…

位段 联合体 枚举

Hello好久不见,今天分享的是接上次结构体没有分享完的内容,这次我们讲讲位段 枚举和联合体的概念以及他们的用法。 2.1 什么是位段 位段的声明和结构是类似的,有两个不同: 1.位段的成员必须是 int、unsigned int 或signed int 。 …

Unity下tga和png格式图片打包成AB包大小和加载速度测试

测试素材 测试素材,一张tga格式,一张png格式,他们的图像尺寸一样都是8K图。 两张图在AssetBundles里显示 Tga格式的图明显大很多,我们打包成ab包看看。 在PC 打包后看,明显大小一样,我们进行ab包加载&am…

通俗易懂讲解拥塞控制

文章目录 前言一、拥塞控制是什么?二、什么是拥塞窗口?和发送窗口有什么关系呢?三、慢启动四、阻塞避免算法五、拥塞发生状态时的算法以及快速重传以及快速恢复总结 前言 TCP的拥塞控制,以前觉得老复杂了,今天重温了一…

HTML - input type=file 允许用户选择多个文件

效果 示例 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><body><!-- When the multiple Boolean attribute is specified, the file input allows the user to select more than o…

【漏洞复现】企望制造 ERP命令执行

漏洞描述 由于企望制造 ERP comboxstore.action接口权限设置不当&#xff0c;默认的配置可执行任意SQL语句&#xff0c;利用xp_cmdshell函数可远程执行命令&#xff0c;未经认证的攻击者可通过该漏洞获取服务器权限。 免责声明 技术文章仅供参考&#xff0c;任何个人和组织…

Python经典练习题(二)

文章目录 &#x1f340;题目一&#x1f340;第二题&#x1f340;第三题&#x1f340;第四题&#x1f340;第五题 &#x1f340;题目一 古典问题&#xff1a;有一对兔子&#xff0c;从出生后第3个月起每个月都生一对兔子&#xff0c;小兔子长到第三个月后每个月又生一对兔子&am…

【操作系统笔记二】链接阶段ELF文件

链接阶段&#xff1a;符号解析 链接阶段主要包含&#xff1a; 符号解析重定位 一般情况下&#xff0c;每个 C 文件可以看成一个程序模块&#xff0c;比如下边的main.c就是一个程序模块 #include <stdio.h>extern int shared; int sum(int *a, int n); int array[2] …

【C语言】指针的进阶(四)—— 企业笔试题解析

笔试题1&#xff1a; int main() {int a[5] { 1, 2, 3, 4, 5 };int* ptr (int*)(&a 1);printf("%d,%d", *(a 1), *(ptr - 1));return 0; } 【答案】在x86环境下运行 【解析】 &a是取出整个数组的地址&#xff0c;&a就表示整个数组&#xff0c;因此…

oracle11g升级12c

目录 背景&#xff1a;oracle11.2.0.4升级至12.2.0.1版本 当前数据库版本 1&#xff09;上传12g安装包&#xff0c;解压开始安装 2&#xff09;不勾选 3&#xff09;升级数据库 4&#xff09;下一步 5&#xff09;修改路径11.2.0换成12.2.0 6&#xff09;下一步 7&…

shardingjdbc分库分表数据均衡性讨论

问题引入 最近一个业务系统中&#xff0c;因为数据量很大&#xff0c;经过技术选型&#xff0c;综合权衡选择了sharding-Jdbc&#xff0c;本文主要讨论的是分库分表的表达式 我们有一个批次总表A&#xff0c;还有一个明细表B&#xff0c;我们需要对明细表B进行水平拆分&#…

论文总结《A Closer Look at Few-shot Classification Again》

原文链接 A Closer Look at Few-shot Classification Again 摘要 这篇文章主要探讨了在少样本图像分类问题中&#xff0c;training algorithm 和 adaptation algorithm的相关性问题。给出了training algorithm和adaptation algorithm是完全不想关的&#xff0c;这意味着我们…

瑞芯微RK3568:Debian系统如何安装Docker

本文基于HD-RK3568-IOT评估板演示Debian系统安装Docker&#xff0c;该方法适用于RK356X全系产品。 HD-RK3568-IOT评估板基于HD-RK3568-CORE 工业级核心板设计&#xff08;双网口、双CAN、5路串口&#xff09;&#xff0c;接口丰富&#xff0c;适用于工业现场应用需求&#xff…

怒刷LeetCode的第13天(Java版)

目录 第一题 题目来源 题目内容 解决方法 方法一&#xff1a;滑动窗口 方法二&#xff1a;哈希表和双指针 方法三&#xff1a;动态规划 第二题 题目来源 题目内容 解决方法 方法一&#xff1a;深度优先搜索&#xff08;DFS&#xff09; 方法二&#xff1a;树结构 …

rv1126-rv1109-RkLunch.sh

RkLunch.sh是个脚本,来自哪里 书接上回:rv1126-rv1109-瑞芯微的 IPC 程序_旋风旋风的博客-CSDN博客 修改之后 屏蔽了两行之后,就没有开机自启rklunch了: # [ -f /oem/RkLunch.sh ] && source /oem/RkLunch.sh 就是运行source /oem/RkLunch.sh 这里就跑了RkL…

基于微信小程序的驾校报名系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言运行环境说明用户微信小程序端的主要功能有&#xff1a;驾校教练的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考论文参考源码…

动手学深度学习(pytorch版)第二章-2.2数据预处理Note-pandas

1. 创建 import osos.makedirs(os.path.join(.., data), exist_okTrue) data_file os.path.join(.., data, house_tiny.csv) with open(data_file, w) as f:f.write(NumRooms,Alley,Price\n) # 列名f.write(NA,Pave,127500\n) # 每行表示一个数据样本f.write(2,NA,106000\…

平板用的触控笔什么牌子好?性价比高的触控笔推荐

随着平板电脑的普及&#xff0c;越来越多用户为了方便都选择了电容笔&#xff0c;电容笔已经完全代替了我们的手指&#xff0c;并且使我们的书写速度得到了极大的提升。然而&#xff0c;因为其的独特的重力压感功能与芯片技术&#xff0c;导致了原装笔的售价一直居高不下&#…

深入探析NCV7356D1R2G 单线CAN收发器各项参数

NCV7356D1R2G深力科是一款用于单线数据链路的物理层器件&#xff0c;能够使用多种具碰撞分解的载波感测多重存取 (CSMA/CR) 协议运行&#xff0c;如博世控制器区域网络 (CAN) 2.0 版。此串行数据链路网络适用于不需要高速数据的应用&#xff0c;低速数据可在物理介质部件和微处…

用selenium和xpath定位元素并获取属性值以及str字符型转json型

页面html如图所示&#xff1a; 要使用xpath定位这个div元素&#xff0c;并且获取其属性data-config的内容值。 from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.chrome.options import Optionshost127.0.0.1 port10808 …