赞
踩
在本文中,我们将介绍一些使用 TorchVision 模块中存在的预训练网络的实践示例——用于图像分类的预训练模型。
预训练模型是在 ImageNet 等大型基准数据集上训练的神经网络模型。深度学习社区从这些开源模型中受益匪浅。此外,预训练模型是计算机视觉研究快速发展的一个主要因素。其他研究人员和实践者可以使用这些最先进的模型,而不是从头开始重新发明一切。
下面给出了最先进模型如何随着时间的推移而改进的粗略时间表。我们仅包含 Torchvision 包中存在的那些模型。
在详细介绍如何使用预训练模型进行图像分类之前,让我们先看看可用的各种预训练模型是什么。我们将在这里讨论 AlexNet 和 ResNet101 作为两个主要示例。这两个网络都已在 ImageNet 数据集上进行了训练。
ImageNet 数据集拥有斯坦福大学维护的超过 1400 万张图像。它广泛用于各种与图像相关的深度学习项目。这些图像属于不同的类别或标签。 AlexNet 和 ResNet101 等预训练模型的目的是将图像作为输入并预测其类别。
这里的“预训练”一词意味着深度学习架构 AlexNet 和 ResNet101 已经在某些(巨大)数据集上进行了训练,因此带有最终的权重和偏差。
由于我们将重点关注如何使用预训练模型来预测输入的类别(标签),因此我们也讨论一下其中涉及的过程。此过程称为模型推理。整个过程由以下主要步骤组成。
让我们从 torchvision 模块导入模型,看看我们可以使用的不同模型和架构。
from torchvision import models
print(dir(models))
仔细观察上面得到的输出。
['AlexNet', 'AlexNet_Weights', 'ConvNeXt', 'ConvNeXt_Base_Weights', 'ConvNeXt_Large_Weights', 'ConvNeXt_Small_Weights', 'ConvNeXt_Tiny_Weights', 'DenseNet', 'DenseNet121_Weights', 'DenseNet161_Weights', 'DenseNet169_Weights', 'DenseNet201_Weights', 'EfficientNet', 'EfficientNet_B0_Weights', 'EfficientNet_B1_Weights', 'EfficientNet_B2_Weights', 'EfficientNet_B3_Weights', 'EfficientNet_B4_Weights', 'EfficientNet_B5_Weights', 'EfficientNet_B6_Weights', 'EfficientNet_B7_Weights', 'EfficientNet_V2_L_Weights', 'EfficientNet_V2_M_Weights', 'EfficientNet_V2_S_Weights', 'GoogLeNet', 'GoogLeNetOutputs', 'GoogLeNet_Weights', 'Inception3', 'InceptionOutputs', 'Inception_V3_Weights', 'MNASNet', 'MNASNet0_5_Weights', 'MNASNet0_75_Weights', 'MNASNet1_0_Weights', 'MNASNet1_3_Weights', 'MobileNetV2', 'MobileNetV3', 'MobileNet_V2_Weights', 'MobileNet_V3_Large_Weights', 'MobileNet_V3_Small_Weights', 'RegNet', 'RegNet_X_16GF_Weights', 'RegNet_X_1_6GF_Weights', 'RegNet_X_32GF_Weights', 'RegNet_X_3_2GF_Weights', 'RegNet_X_400MF_Weights', 'RegNet_X_800MF_Weights', 'RegNet_X_8GF_Weights', 'RegNet_Y_128GF_Weights', 'RegNet_Y_16GF_Weights', 'RegNet_Y_1_6GF_Weights', 'RegNet_Y_32GF_Weights', 'RegNet_Y_3_2GF_Weights', 'RegNet_Y_400MF_Weights', 'RegNet_Y_800MF_Weights', 'RegNet_Y_8GF_Weights', 'ResNeXt101_32X8D_Weights', 'ResNeXt101_64X4D_Weights', 'ResNeXt50_32X4D_Weights', 'ResNet', 'ResNet101_Weights', 'ResNet152_Weights', 'ResNet18_Weights', 'ResNet34_Weights', 'ResNet50_Weights', 'ShuffleNetV2', 'ShuffleNet_V2_X0_5_Weights', 'ShuffleNet_V2_X1_0_Weights', 'ShuffleNet_V2_X1_5_Weights', 'ShuffleNet_V2_X2_0_Weights', 'SqueezeNet', 'SqueezeNet1_0_Weights', 'SqueezeNet1_1_Weights', 'SwinTransformer', 'Swin_B_Weights', 'Swin_S_Weights', 'Swin_T_Weights', 'VGG', 'VGG11_BN_Weights', 'VGG11_Weights', 'VGG13_BN_Weights', 'VGG13_Weights', 'VGG16_BN_Weights', 'VGG16_Weights', 'VGG19_BN_Weights', 'VGG19_Weights', 'ViT_B_16_Weights', 'ViT_B_32_Weights', 'ViT_H_14_Weights', 'ViT_L_16_Weights', 'ViT_L_32_Weights', 'VisionTransformer', 'Wide_ResNet101_2_Weights', 'Wide_ResNet50_2_Weights', '_GoogLeNetOutputs', '_InceptionOutputs', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '_api', '_meta', '_utils', 'alexnet', 'convnext', 'convnext_base', 'convnext_large', 'convnext_small', 'convnext_tiny', 'densenet', 'densenet121', 'densenet161', 'densenet169', 'densenet201', 'detection', 'efficientnet', 'efficientnet_b0', 'efficientnet_b1', 'efficientnet_b2', 'efficientnet_b3', 'efficientnet_b4', 'efficientnet_b5', 'efficientnet_b6', 'efficientnet_b7', 'efficientnet_v2_l', 'efficientnet_v2_m', 'efficientnet_v2_s', 'get_weight', 'googlenet', 'inception', 'inception_v3', 'mnasnet', 'mnasnet0_5', 'mnasnet0_75', 'mnasnet1_0', 'mnasnet1_3', 'mobilenet', 'mobilenet_v2', 'mobilenet_v3_large', 'mobilenet_v3_small', 'mobilenetv2', 'mobilenetv3', 'optical_flow', 'quantization', 'regnet', 'regnet_x_16gf', 'regnet_x_1_6gf', 'regnet_x_32gf', 'regnet_x_3_2gf', 'regnet_x_400mf', 'regnet_x_800mf', 'regnet_x_8gf', 'regnet_y_128gf', 'regnet_y_16gf', 'regnet_y_1_6gf', 'regnet_y_32gf', 'regnet_y_3_2gf', 'regnet_y_400mf', 'regnet_y_800mf', 'regnet_y_8gf', 'resnet', 'resnet101', 'resnet152', 'resnet18', 'resnet34', 'resnet50', 'resnext101_32x8d', 'resnext101_64x4d', 'resnext50_32x4d', 'segmentation', 'shufflenet_v2_x0_5', 'shufflenet_v2_x1_0', 'shufflenet_v2_x1_5', 'shufflenet_v2_x2_0', 'shufflenetv2', 'squeezenet', 'squeezenet1_0', 'squeezenet1_1', 'swin_b', 'swin_s', 'swin_t', 'swin_transformer', 'vgg', 'vgg11', 'vgg11_bn', 'vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn', 'vgg19', 'vgg19_bn', 'video', 'vision_transformer', 'vit_b_16', 'vit_b_32', 'vit_h_14', 'vit_l_16', 'vit_l_32', 'wide_resnet101_2', 'wide_resnet50_2']
请注意,有一个名为 AlexNet 的条目,还有一个名为 alexnet。大写的名称指的是 Python 类 (AlexNet),而 alexnet 是一个便捷函数,它返回从 AlexNet 类实例化的模型。这些便利函数也可能具有不同的参数集。例如,densenet121、densenet161、densenet169、densenet201 都是 DenseNet 类的实例,但具有不同的层数——分别为 121,161,169 和 201。
我们首先从 AlexNet 开始。它是图像识别领域早期突破性网络之一。如果您有兴趣了解 AlexNet 的架构,可以查看我们关于了解 AlexNet 的文章。
alexnet = models.alexnet(pretrained=True)
# You will see a similar output as below
# Downloading: "https://download.pytorch.org/models/alexnet-owt-7be5be79.pth" to C:\Users\Administrator/.cache\torch\hub\checkpoints\alexnet-owt-7be5be79.pth
请注意,通常 PyTorch 模型的扩展名为 .pt 或 .pth。
下载权重后,我们就可以继续其他步骤。我们还可以查看网络架构的一些细节,如下所示。
print(alexnet)
运行结果如下:
AlexNet( (features): Sequential( (0): Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4), padding=(2, 2)) (1): ReLU(inplace=True) (2): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False) (3): Conv2d(64, 192, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2)) (4): ReLU(inplace=True) (5): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False) (6): Conv2d(192, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (7): ReLU(inplace=True) (8): Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (9): ReLU(inplace=True) (10): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (11): ReLU(inplace=True) (12): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False) ) (avgpool): AdaptiveAvgPool2d(output_size=(6, 6)) (classifier): Sequential( (0): Dropout(p=0.5, inplace=False) (1): Linear(in_features=9216, out_features=4096, bias=True) (2): ReLU(inplace=True) (3): Dropout(p=0.5, inplace=False) (4): Linear(in_features=4096, out_features=4096, bias=True) (5): ReLU(inplace=True) (6): Linear(in_features=4096, out_features=1000, bias=True) ) )
一旦我们有了模型,下一步就是转换输入图像,使它们具有正确的形状和其他特征,例如平均值和标准差。这些值应与训练模型时使用的值类似。这确保网络能够产生有意义的答案。
from torchvision import transforms
transform = transforms.Compose([ # [1]
transforms.Resize(256), # [2]
transforms.CenterCrop(224), # [3]
transforms.ToTensor(), # [4]
transforms.Normalize( # [5]
mean=[0.485, 0.456, 0.406], # [6]
std=[0.229, 0.224, 0.225] # [7]
)
])
[1] 这里我们定义一个变量transform,它是对输入图像执行的所有图像变换的组合。
[2] 将图像大小调整为 256×256 像素。
[3] 将图像裁剪为中心周围的 224×224 像素。
[4] 将图像转换为 PyTorch Tensor 数据类型。
[5-7] 通过将图像的平均值和标准差设置为指定值来标准化图像。
from PIL import Image
img = Image.open("dog.jpg")
img_t = transform(img)
batch_t = torch.unsqueeze(img_t, 0)
alexnet.eval()
out = alexnet(batch_t)
print(out.shape) # torch.Size([1, 1000])
我们仍然没有得到图像的类别(或标签)。为此,我们首先从包含所有 1000 个标签的列表的json文件中读取并存储标签。
with open('imagenet_classes.json', 'r') as f:
classes = json.load(f)
现在,我们需要找到输出向量 out 中出现最大分数的索引。我们将使用这个索引来找出预测。
_, index = torch.max(out, 1)
index = index[0].item()
percentage = torch.nn.functional.softmax(out, dim=1)[0] * 100
print(classes[str(index)], percentage[index].item())
运行结果如下:
[‘Labrador retriever’] 42.46735763549805
该模型以 42.47% 的置信度预测该图像是拉布拉多猎犬。
但这看起来太低了。让我们看看模型认为图像属于哪些其他类别。
_, indices = torch.sort(out, descending=True)
for idx in indices[0][:5]:
print(classes[str(idx.item())], percentage[idx.item()].item())
运行结果如下:
['Labrador retriever'] 42.46735763549805
['golden retriever'] 16.6086483001709
['Saluki', 'gazelle hound'] 15.473832130432129
['whippet'] 2.7881932258605957
['Ibizan hound', 'Ibizan Podenco'] 2.3617053031921387
可以看出,这些都是狗品种。因此,模型以相当高的置信度成功地预测出这是一只狗,但对狗的品种却不太确定。
其他图像测试结果:
['strawberry'] 99.99411010742188
['banana'] 0.0008383149397559464
['custard apple'] 0.0008333750884048641
['orange'] 0.000746806850656867
['lemon'] 0.000567478418815881
我们将使用 resnet101 – 101 层卷积神经网络。 resnet101 在训练过程中调整了大约 4450 万个参数。
我们只需将模型改为:
resnet = models.resnet101(pretrained=True)
运行结果如下:
['Labrador retriever'] 48.86918640136719
['dingo', 'warrigal', 'warragal', 'Canis dingo'] 8.17878532409668
['golden retriever'] 6.944648742675781
['Eskimo dog', 'husky'] 3.563744306564331
['bull mastiff'] 3.0799121856689453
就像 AlexNet 一样,ResNet 以非常高的置信度预测这是一只狗,并以 48.25% 的置信度预测这是一只拉布拉多猎犬。
from torchvision import models import torch from torchvision import transforms from PIL import Image import json # alexnet = models.alexnet(pretrained=True) resnet = models.resnet101(pretrained=True) transform = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize( mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] ) ]) img = Image.open("../img/dog.jpg") img_t = transform(img) batch_t = torch.unsqueeze(img_t, 0) resnet.eval() out = resnet(batch_t) with open('../json/imagenet_classes.json', 'r') as f: classes = json.load(f) _, index = torch.max(out, 1) index = index[0].item() percentage = torch.nn.functional.softmax(out, dim=1)[0] * 100 # top-1 print(classes[str(index)], percentage[index].item()) _, indices = torch.sort(out, descending=True) for idx in indices[0][:5]: # top-5 print(classes[str(idx.item())], percentage[idx.item()].item())
在本节中,我们将根据以下标准比较预训练模型:
所有实验均在同一输入图像上进行多次,以便可以分析特定模型的所有结果的平均值。实验是在 Google Colab 上进行的。现在,让我们看看获得的结果。
我们将讨论的第一个标准包括 Top-1 和 Top-5 错误。
从图中可以看出,两个误差都遵循相似的趋势。 AlexNet是基于深度学习的第一次尝试,此后误差得到了改善。值得注意的是 GoogLeNet、ResNet、VGGNet、ResNext。
接下来,我们将根据模型预测所需的时间来比较模型。向每个模型多次提供一张图像,并对所有迭代的推理时间进行平均。在 Google Colab 上对 CPU 执行了类似的过程,然后对 GPU 执行了类似的过程。尽管顺序有所不同,但我们可以看到 SqueezeNet、ShuffleNet 和 ResNet-18 的推理时间非常短。
很多时候,当我们在 Android 或 iOS 设备上使用深度学习模型时,模型大小成为决定因素,有时甚至比准确性更重要。 SqueezeNet 的模型大小最小 (5 MB),其次是 ShuffleNet V2 (6 MB) 和 MobileNet V2 (14 MB)。很明显,为什么这些模型在利用深度学习的移动应用程序中受到青睐。
我们根据特定标准讨论了哪种模型表现更好。我们可以将所有这些重要细节压缩在一张气泡图中,然后我们可以参考这些细节来根据我们的要求决定采用哪种模型。
我们使用的 x 坐标是 Top-1 误差(越低越好)。 y 坐标是 GPU 上的预测时间(以毫秒为单位)(越低越好)。气泡大小代表模型大小(越低越好)。
注意:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。