当前位置:   article > 正文

抱抱脸(hugging face)教程-中文翻译-共享定制模型_trust_remote_code=true

trust_remote_code=true

共享定制模型

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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

在编写自己的配置时,需要记住以下三点:

  • 你必须继承 PretrainedConfig,
  • 你的 PretrainedConfig 的_init 必须接受任何 kwargs,
  • 这些 kwargs 需要传递给超类 init。

继承是为了确保你从 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")
  • 1
  • 2

这将在 custom-resnet 文件夹中保存一个名为 config.json 的文件。然后你可以使用 from_pretrained 方法重新加载你的配置:

resnet50d_config = ResnetConfig.from_pretrained("custom-resnet")
  • 1

您还可以使用 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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

对于将图像分类的模型,我们只需改变正向方法:

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}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

在这两种情况下,请注意我们如何从 PreTrainedModel 继承并使用 config 调用超类初始化(有点像编写常规火炬.nn。单元)。设置 config_class 的行不是强制的,除非您想用 auto classes 注册您的模型(参见上一节)。

如果您的模型与库中的模型非常相似,那么您可以重复使用与此模型相同的配置。

你可以让你的模型返回任何你想要的东西,但是返回一个字典,就像我们在 resnetmodelforimagelassification 中做的那样,当标签被传递时包含了损失,将使你的模型在 Trainer 类中直接可用。只要您计划使用自己的培训循环或其他库进行培训,使用其他输出格式是可以的。

现在我们有了我们的模型类,让我们创建一个:

resnet50d = ResnetModelForImageClassification(resnet50d_config)
  • 1

同样,您可以使用 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())
  • 1
  • 2
  • 3
  • 4

现在让我们看看如何确保在保存_pretrained ()或 push_to_hub ()时保存模型的代码。

将代码发送到Hub

这个 API 是实验性的,在下一个版本中可能会有一些细微的变化。

首先,确保您的模型在 Py 文件。只要所有文件都在同一个目录中(我们还不支持这个特性的子模块) ,它就可以依赖于其他一些文件的相对导入。对于我们的例子,我们将定义一个 modeling resnet.py 文件和一个 configuration resnet.py 文件,它们位于当前工作目录模型的一个名为 resnetmodel 的文件夹中。配置文件包含 ResnetConfig 的代码,建模文件包含 ResnetModel 和 ResnetModelForImageClassification 的代码。

.
└── resnet_model
    ├── __init__.py
    ├── configuration_resnet.py
    └── modeling_resnet.py
  • 1
  • 2
  • 3
  • 4
  • 5

Py 可以是空的,它就在那里,这样 Python 检测到的 resnet 模型就可以用作模块。

如果要从库中复制一个建模文件,你需要替换文件顶部所有要从Transformers文件包中导入的相关导入。

注意,您可以重用(或子类)现有的配置/模型。

要与社区共享你的模型,按照以下步骤: 首先从新创建的文件中导入 ResNet 模型和配置:

from resnet_model.configuration_resnet import ResnetConfig
from resnet_model.modeling_resnet import ResnetModel, ResnetModelForImageClassification
  • 1
  • 2

然后当你使用 save_pretrained 方法时,你必须告诉库你想要复制这些对象的代码文件,并且正确地将它们注册到一个给定的 Auto 类(特别是对于模型) ,只要运行:

ResnetConfig.register_for_auto_class()
ResnetModel.register_for_auto_class("AutoModel")
ResnetModelForImageClassification.register_for_auto_class("AutoModelForImageClassification")
  • 1
  • 2
  • 3

注意,不需要为配置指定自动类(只有一个自动类,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())
  • 1
  • 2
  • 3
  • 4
  • 5

现在要将模型发送到 Hub,请确保您已经登录。在您的终端中运行:

huggingface-cli login
  • 1

或者从笔记本上:

from huggingface_hub import notebook_login

notebook_login()
  • 1
  • 2
  • 3

然后,您可以像下面这样推送到您自己的名称空间(或您所属的组织) :

resnet50d.push_to_hub("custom-resnet50d")
  • 1

除了建模权重和 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)
  • 1
  • 2
  • 3

还强烈建议传递提交散列作为修订,以确保模型的作者没有用一些恶意的新行更新代码(除非您完全信任模型的作者)。

commit_hash = "ed94a7c6247d8aedce4647f00f20de6875b5b292"
model = AutoModelForImageClassification.from_pretrained(
    "sgugger/custom-resnet50d", trust_remote_code=True, revision=commit_hash
)
  • 1
  • 2
  • 3
  • 4

请注意,当浏览 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)
  • 1
  • 2
  • 3
  • 4
  • 5

注意,在将自定义配置注册到 AutoConfig 时使用的第一个参数需要与自定义配置的 model_type 匹配,在将自定义模型注册到任何自动模型类时使用的第一个参数需要与这些模型的 config_class 匹配。

本文是抱抱脸(Hugging Face)教程中文翻译,仅学习使用

原文链接

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/464384
推荐阅读
相关标签
  

闽ICP备14008679号