赞
踩
Transformers库的设计很容易扩展。每个模型都完整地编码在存储库的给定子文件夹中,没有任何抽象,因此您可以轻松地复制建模文件并根据需要对其进行调整。
如果您正在编写一个全新的模型,那么从头开始可能会更容易。在本教程中,我们将向您展示如何编写一个自定义模型及其配置,以便它可以在 Transformers 内部使用,以及如何与社区共享它(通过它所依赖的代码) ,以便任何人都可以使用它,即使它不在 Transformers 库中。
我们将通过将 timm 库的 ResNet 类包装到 PreTrainedModel 中,在 ResNet 模型上演示所有这些。
在深入研究模型之前,让我们首先编写它的配置。模型的配置是一个对象,它将包含构建模型所需的所有信息。正如我们将在下一节中看到的,模型只能带一个要初始化的配置,所以我们真的需要这个对象尽可能完整。
在我们的示例中,我们将接受一些可能需要调整的 ResNet 类的参数。然后,不同的配置将给我们提供可能的不同类型的 resnet。然后我们只是存储这些论点,在检查其中一些论点的有效性之后。
from transformers import PretrainedConfig from typing import List class ResnetConfig(PretrainedConfig): model_type = "resnet" def __init__( self, block_type="bottleneck", layers: List[int] = [3, 4, 6, 3], num_classes: int = 1000, input_channels: int = 3, cardinality: int = 1, base_width: int = 64, stem_width: int = 64, stem_type: str = "", avg_down: bool = False, **kwargs, ): if block_type not in ["basic", "bottleneck"]: raise ValueError(f"`block` must be 'basic' or bottleneck', got {block}.") if stem_type not in ["", "deep", "deep-tiered"]: raise ValueError(f"`stem_type` must be '', 'deep' or 'deep-tiered', got {block}.") self.block_type = block_type self.layers = layers self.num_classes = num_classes self.input_channels = input_channels self.cardinality = cardinality self.base_width = base_width self.stem_width = stem_width self.stem_type = stem_type self.avg_down = avg_down super().__init__(**kwargs)
在编写自己的配置时,需要记住以下三点:
继承是为了确保你从 Transformers 库中获得所有的功能,而另外两个限制来自于 PretrainedConfig 的字段比你设置的字段多。当用 from_pretrained 方法重新加载配置时,这些字段需要被配置接受,然后发送到超类。
为您的配置定义一个 model_type (这里是 model_type = “ resnet”)并不是强制性的,除非您想用 auto classes 注册您的模型(参见上一节)。
完成这些之后,您就可以轻松地创建和保存您的配置,就像使用库中的任何其他模型配置一样。下面是我们如何创建一个 resnet50d 配置文件并保存它:
resnet50d_config = ResnetConfig(block_type="bottleneck", stem_width=32, stem_type="deep", avg_down=True)
resnet50d_config.save_pretrained("custom-resnet")
这将在 custom-resnet 文件夹中保存一个名为 config.json 的文件。然后你可以使用 from_pretrained 方法重新加载你的配置:
resnet50d_config = ResnetConfig.from_pretrained("custom-resnet")
您还可以使用 PretrainedConfig 类的任何其他方法,如 push_to_Hub () ,将配置直接上传到 Hub。
现在我们已经有了我们的 ResNet 配置,我们可以继续编写模型了。我们实际上将编写两个: 一个是从一批图像中提取隐藏特征(如 BertModel) ,另一个是适合于图像分类(如 bertmodelforsequenclassification)。
正如我们前面提到的,我们将只编写模型的松散包装器,以保持本示例的简单性。在编写这个类之前,我们需要做的唯一一件事就是在块类型和实际块类之间建立映射。然后通过将所有内容传递给 ResNet 类,从配置中定义模型:
from transformers import PreTrainedModel from timm.models.resnet import BasicBlock, Bottleneck, ResNet from .configuration_resnet import ResnetConfig BLOCK_MAPPING = {"basic": BasicBlock, "bottleneck": Bottleneck} class ResnetModel(PreTrainedModel): config_class = ResnetConfig def __init__(self, config): super().__init__(config) block_layer = BLOCK_MAPPING[config.block_type] self.model = ResNet( block_layer, config.layers, num_classes=config.num_classes, in_chans=config.input_channels, cardinality=config.cardinality, base_width=config.base_width, stem_width=config.stem_width, stem_type=config.stem_type, avg_down=config.avg_down, ) def forward(self, tensor): return self.model.forward_features(tensor)
对于将图像分类的模型,我们只需改变正向方法:
class ResnetModelForImageClassification(PreTrainedModel): config_class = ResnetConfig def __init__(self, config): super().__init__(config) block_layer = BLOCK_MAPPING[config.block_type] self.model = ResNet( block_layer, config.layers, num_classes=config.num_classes, in_chans=config.input_channels, cardinality=config.cardinality, base_width=config.base_width, stem_width=config.stem_width, stem_type=config.stem_type, avg_down=config.avg_down, ) def forward(self, tensor, labels=None): logits = self.model(tensor) if labels is not None: loss = torch.nn.cross_entropy(logits, labels) return {"loss": loss, "logits": logits} return {"logits": logits}
在这两种情况下,请注意我们如何从 PreTrainedModel 继承并使用 config 调用超类初始化(有点像编写常规火炬.nn。单元)。设置 config_class 的行不是强制的,除非您想用 auto classes 注册您的模型(参见上一节)。
如果您的模型与库中的模型非常相似,那么您可以重复使用与此模型相同的配置。
你可以让你的模型返回任何你想要的东西,但是返回一个字典,就像我们在 resnetmodelforimagelassification 中做的那样,当标签被传递时包含了损失,将使你的模型在 Trainer 类中直接可用。只要您计划使用自己的培训循环或其他库进行培训,使用其他输出格式是可以的。
现在我们有了我们的模型类,让我们创建一个:
resnet50d = ResnetModelForImageClassification(resnet50d_config)
同样,您可以使用 PreTrainedModel 的任何方法,比如 save_pretrained ()或 push_to_hub ()。我们将在下一节中使用第二种方法,并且看看如何使用我们模型的代码来推动模型的权重。但是首先,让我们在模型中加载一些预先训练好的重量。
在您自己的用例中,您可能会根据您自己的数据来训练您的定制模型。为了快速完成本教程,我们将使用 resnet50d 的预训练版本。因为我们的模型只是一个包装器,所以很容易转移这些权重:
import timm
pretrained_model = timm.create_model("resnet50d", pretrained=True)
resnet50d.model.load_state_dict(pretrained_model.state_dict())
现在让我们看看如何确保在保存_pretrained ()或 push_to_hub ()时保存模型的代码。
这个 API 是实验性的,在下一个版本中可能会有一些细微的变化。
首先,确保您的模型在 Py 文件。只要所有文件都在同一个目录中(我们还不支持这个特性的子模块) ,它就可以依赖于其他一些文件的相对导入。对于我们的例子,我们将定义一个 modeling resnet.py 文件和一个 configuration resnet.py 文件,它们位于当前工作目录模型的一个名为 resnetmodel 的文件夹中。配置文件包含 ResnetConfig 的代码,建模文件包含 ResnetModel 和 ResnetModelForImageClassification 的代码。
.
└── resnet_model
├── __init__.py
├── configuration_resnet.py
└── modeling_resnet.py
Py 可以是空的,它就在那里,这样 Python 检测到的 resnet 模型就可以用作模块。
如果要从库中复制一个建模文件,你需要替换文件顶部所有要从Transformers文件包中导入的相关导入。
注意,您可以重用(或子类)现有的配置/模型。
要与社区共享你的模型,按照以下步骤: 首先从新创建的文件中导入 ResNet 模型和配置:
from resnet_model.configuration_resnet import ResnetConfig
from resnet_model.modeling_resnet import ResnetModel, ResnetModelForImageClassification
然后当你使用 save_pretrained 方法时,你必须告诉库你想要复制这些对象的代码文件,并且正确地将它们注册到一个给定的 Auto 类(特别是对于模型) ,只要运行:
ResnetConfig.register_for_auto_class()
ResnetModel.register_for_auto_class("AutoModel")
ResnetModelForImageClassification.register_for_auto_class("AutoModelForImageClassification")
注意,不需要为配置指定自动类(只有一个自动类,AutoConfig) ,但是对于模型不同。您的自定义模型可以适用于许多不同的任务,因此您必须指定哪个自动类适合您的模型。
接下来,让我们像之前那样创建配置和模型:
resnet50d_config = ResnetConfig(block_type="bottleneck", stem_width=32, stem_type="deep", avg_down=True)
resnet50d = ResnetModelForImageClassification(resnet50d_config)
pretrained_model = timm.create_model("resnet50d", pretrained=True)
resnet50d.model.load_state_dict(pretrained_model.state_dict())
现在要将模型发送到 Hub,请确保您已经登录。在您的终端中运行:
huggingface-cli login
或者从笔记本上:
from huggingface_hub import notebook_login
notebook_login()
然后,您可以像下面这样推送到您自己的名称空间(或您所属的组织) :
resnet50d.push_to_hub("custom-resnet50d")
除了建模权重和 json 格式的配置之外,还复制了建模和配置。定制-resnet50d 文件夹中的 py 文件,并将结果上载到 Hub。你可以在这个模型回收中检查结果。
有关 push to Hub 方法的更多信息,请参见共享教程。
您可以使用 auto-classes 和 from_pretrained 方法在其存储库中使用任何带有自定义代码文件的配置、模型或标记器。所有上传到 Hub 的文件和代码都会被扫描以防恶意软件(更多信息请参阅 Hub 安全文档) ,但是你仍然应该检查模型代码和作者,以避免在你的机器上执行恶意代码。设置 trust_remote_code = True 来使用带有自定义代码的模型:
from transformers import AutoModelForImageClassification
model = AutoModelForImageClassification.from_pretrained("sgugger/custom-resnet50d", trust_remote_code=True)
还强烈建议传递提交散列作为修订,以确保模型的作者没有用一些恶意的新行更新代码(除非您完全信任模型的作者)。
commit_hash = "ed94a7c6247d8aedce4647f00f20de6875b5b292"
model = AutoModelForImageClassification.from_pretrained(
"sgugger/custom-resnet50d", trust_remote_code=True, revision=commit_hash
)
请注意,当浏览 Hub 上模型 repo 的提交历史记录时,有一个按钮可以轻松地复制任何提交的提交哈希。
如果您正在编写一个扩展 Transformers 的库,那么您可能希望扩展 auto 类以包含您自己的模型。他的方法不同于将代码推送到 Hub,因为用户需要导入您的库来获取定制模型(相反,需要从 Hub 自动下载模型代码)。
只要你的配置有一个不同于现有模型类型的 model_type 属性,并且你的模型类有正确的 config_class 属性,你可以像这样把它们添加到 auto 类中:
from transformers import AutoConfig, AutoModel, AutoModelForImageClassification
AutoConfig.register("resnet", ResnetConfig)
AutoModel.register(ResnetConfig, ResnetModel)
AutoModelForImageClassification.register(ResnetConfig, ResnetModelForImageClassification)
注意,在将自定义配置注册到 AutoConfig 时使用的第一个参数需要与自定义配置的 model_type 匹配,在将自定义模型注册到任何自动模型类时使用的第一个参数需要与这些模型的 config_class 匹配。
本文是抱抱脸(Hugging Face)教程中文翻译,仅学习使用
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。