【计算机视觉 | timm】模型 API 和预训练权重(含代码)

一、支持的型号列表timm

timm支持各种预训练和非预训练模型,用于许多基于图像的任务。

要获取完整的模型列表,请使用以下函数。该函数返回按字母顺序排序的模型列表,这些模型受 支持。我们只看下面的前 5 个模型。

list_modelstimmlist_modelstimm

我们的代码为:

import timm 
timm.list_models()[:5]

在这里插入图片描述
一般来说,你总是想在里面使用工厂函数。具体而言,您希望使用 function from 创建任何模型。可以使用该函数创建中列出的任何模型。还有一些很棒的额外功能,我们稍后会介绍。但是,让我们看一个简单的例子。

timmcreate_modeltimmtimm.list_models()create_model

我们的代码为:

import random
import torch

random_model_to_create = random.choice(timm.list_models())
random_model_to_create

输出为:‘resnet50d’

model = timm.create_model(random_model_to_create)
x     = torch.randn(1, 3, 224, 224)
model(x).shape

输出的结果为:torch.Size([1, 1000])

在上面的例子中,我们随机选择一个模型名称,创建它并通过模型传递一些虚拟输入数据以获得一些输出。一般来说,你永远不想创建这样的随机模型,这只是一个例子,用来展示所有模型都受函数支持。使用 创建模型真的很容易。

timm.list_models()timm.list_models()timm.create_model()timm

二、这些模型是否有预训练的权重?

答案是肯定的! 希望让研究人员和实践者能够非常轻松地进行实验,并支持大量具有预训练权重的模型。这些预训练的权重是:

  • 直接从其原始来源使用
  • 由 Ross 从其原始实现移植到不同的框架(例如 Tensorflow 模型)中
  • 使用随附的训练脚本从头开始训练 ()。下面提到了用于训练这些单个模型的带有超参数的确切命令。train.pyTraining Scripts

要列出所有具有预训练权重的模型,请提供一个可以在函数中传递的便利参数,如下所示。我们只列出前 5 个返回的模型。timmpretrainedlist_models

timm.list_models(pretrained=True)[:5]

在这里插入图片描述
注意:仅通过列出前 5 个预训练模型,我们可以看到目前没有针对 or 等模型的预训练权重。对于具有硬件可用性的新贡献者来说,这是一个很好的机会,可以使用训练脚本在 Imagenet 数据集上预训练模型并共享这些权重。timmcspdarknet53_iabncspresnet50d

三、我的数据集不包含 3 通道图像 - 现在怎么办?

您可能已经知道,ImageNet 数据由 3 通道 RGB 图像组成。因此,为了能够在大多数库中使用预训练权重,该模型需要 3 通道输入图像。

3.1 torchvision提高Exception

import torchvision

m = torchvision.models.resnet34(pretrained=True)

# single-channel image (maybe x-ray)
x = torch.randn(1, 1, 224, 224)

# `torchvision` raises error
try: m(x).shape
except Exception as e: print(e)

输出结果为:

Given groups=1, weight of size [64, 3, 7, 7], expected input[1, 1, 224, 224] to have 3 channels, but got 1 channels instead

从上面可以看出,这些预训练的权重不适用于单通道输入图像。作为一种解决方法,大多数从业者通过复制单通道像素来创建 3 通道图像,从而将其单通道输入图像转换为 3 通道图像。torchvision

基本上,上面抱怨它期望输入有 3 个通道,但实际上得到了 1 个通道。

# 25-channel image (maybe satellite image)
x = torch.randn(1, 25, 224, 224)

# `torchvision` raises error
try: m(x).shape
except Exception as e: print(e)
Given groups=1, weight of size [64, 3, 7, 7], expected input[1, 25, 224, 224] to have 3 channels, but got 25 channels instead

再次,引发一个错误,这一次除了不使用预训练的权重并从随机初始化的权重开始之外,没有解决方法可以克服这个错误。

3.2 timm有办法处理这些exceptions

m = timm.create_model('resnet34', pretrained=True, in_chans=1)

# single channel image
x = torch.randn(1, 1, 224, 224)

m(x).shape

输出结果为:torch.Size([1, 1000])

我们将一个参数传递给函数,这不知何故神奇地工作!让我们看看 25 通道图像会发生什么?in_chanstimm.create_model

m = timm.create_model('resnet34', pretrained=True, in_chans=25)

# 25-channel image
x = torch.randn(1, 25, 224, 224)

m(x).shape

输出结果为:torch.Size([1, 1000])

3.3 如何使用预训练的权重并处理非 3 通道 RGB 图像的图像?

timm在调用函数以加载模型的预训练权重的函数中执行所有这些魔术。让我们看看如何实现预训练权重的加载。load_pretrainedtimm

from timm.models.resnet import ResNet, BasicBlock, default_cfgs
from timm.models.helpers import load_pretrained
from copy import deepcopy

下面,我们创建一个简单的模型,可以将单通道图像作为输入。我们通过在创建模型时传入构造函数类来实现这一点。resnet34in_chans=1ResNet

resnet34_default_cfg = default_cfgs['resnet34']
resnet34 = ResNet(BasicBlock, layers=[3, 4, 6, 3], in_chans=1)
resnet34.default_cfg = deepcopy(resnet34_default_cfg)

resnet34.conv1

输出结果为:Conv2d(1, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)

resnet34.conv1.weight.shape

输出结果为:torch.Size([64, 1, 7, 7])

从上面的第一个卷积中我们可以看出,输入通道数设置为 1。而且重量是形状的.这意味着输入通道数为 1,输出通道数为 64,内核大小为 。resnet34conv1[64, 1, 7, 7]7x7

但是预训练的权重呢?由于 ImageNet 由 3 通道输入图像组成,因此该层的预训练为 。让我们在下面确认:conv1[64, 3, 7, 7]

resnet34_default_cfg

在这里插入图片描述
让我们从模型中加载预训练的权重,并检查预期的输入通道数。conv1

import torch
state_dict = torch.hub.load_state_dict_from_url(resnet34_default_cfg['url'])

太好了,我们已经从 URL 加载了 resnet-34 的预训练权重,现在让我们检查一下下面的权重形状:'https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/resnet34-43635321.pth'conv1

state_dict['conv1.weight'].shape

输出结果为:torch.Size([64, 3, 7, 7])

因此,该层期望输入通道数为 3!

注意:我们知道这一点,因为 的形状是 ,这意味着输入通道的数量为 ,输出通道的数量为 ,内核大小为 。conv1.weight[64, 3, 7, 7]3647x7

注意:这就是为什么当我们尝试加载预训练权重时,torchvision 会给出一个错误,因为我们将输入通道数设置为 1,因此我们模型的层权重将是形状的。我希望我们上面看到的这个例外现在更有意义:conv1[64, 1, 7, 7]Given groups=1, weight of size [64, 3, 7, 7], expected input[1, 1, 224, 224] to have 3 channels, but got 1 channels instead.

3.4 那么如何能够加载这些重量呢?

内部函数内部发生了一些非常聪明的事情。基本上,当预期的输入通道数不等于 3 时,需要考虑两种主要情况。输入通道为 1 或不是。让我们看看这两种情况下会发生什么。load_pretrainedtimm

当输入通道数不等于 3 时,相应地更新预训练权重,以便能够加载预训练权重。timmconv1.weight

3.4.1 情况 1:当输入通道数为 1 时

如果输入通道数为 1,则只需将 3 个通道权重相加为单个通道即可更新 to be 的形状。这可以通过以下方式实现:timmconv1.weight[64, 1, 7, 7]

conv1_weight = state_dict['conv1.weight']
conv1_weight.sum(dim=1, keepdim=True).shape

>> torch.Size([64, 1, 7, 7])

因此,通过更新第一层的形状,我们现在可以安全地加载这些预训练的权重。conv1

3.4.2 情况2:当输入通道数不为1时

在这种情况下,我们只需根据需要重复多次,然后选择所需的输入通道权重数。conv1_weight

在这里插入图片描述
从上图中可以看出,假设我们的输入图像有 8 个通道。因此,输入通道数等于 8。

但是,正如我们所知,我们的预训练权重只有 3 个通道。那么,我们怎么才能利用预训练的权重呢?

好吧,上图中显示了发生的事情。我们将权重复制 3 次,这样现在通道总数变为 9,然后选择前 8 个通道作为层的权重。timmconv1

这一切都是在函数内部完成的,如下所示:load_pretrained

conv1_name = cfg['first_conv']
conv1_weight = state_dict[conv1_name + '.weight']
conv1_type = conv1_weight.dtype
conv1_weight = conv1_weight.float()
repeat = int(math.ceil(in_chans / 3))
conv1_weight = conv1_weight.repeat(1, repeat, 1, 1)[:, :in_chans, :, :]
conv1_weight *= (3 / float(in_chans))
conv1_weight = conv1_weight.to(conv1_type)
state_dict[conv1_name + '.weight'] = conv1_weight

因此,如上所示,我们首先重复这些复制的权重,然后选择所需的权重数量。conv1_weightin_chans

猜你喜欢

转载自blog.csdn.net/wzk4869/article/details/134794257