Transformers 4.37 中文文档(七)(1)https://developer.aliyun.com/article/1564148
特征提取器
特征提取器处理音频输入。它继承自基础 FeatureExtractionMixin 类,并且还可以继承自 SequenceFeatureExtractor 类来处理音频输入。
要使用,创建一个与您正在使用的模型相关联的特征提取器。例如,如果您正在使用 Wav2Vec2 进行音频分类,则创建一个默认的 Wav2Vec2FeatureExtractor:
>>> from transformers import Wav2Vec2FeatureExtractor >>> w2v2_extractor = Wav2Vec2FeatureExtractor() >>> print(w2v2_extractor) Wav2Vec2FeatureExtractor { "do_normalize": true, "feature_extractor_type": "Wav2Vec2FeatureExtractor", "feature_size": 1, "padding_side": "right", "padding_value": 0.0, "return_attention_mask": false, "sampling_rate": 16000 }
如果您不需要任何自定义,只需使用from_pretrained
方法加载模型的默认特征提取器参数。
修改任何 Wav2Vec2FeatureExtractor 参数以创建您自定义的特征提取器:
>>> from transformers import Wav2Vec2FeatureExtractor >>> w2v2_extractor = Wav2Vec2FeatureExtractor(sampling_rate=8000, do_normalize=False) >>> print(w2v2_extractor) Wav2Vec2FeatureExtractor { "do_normalize": false, "feature_extractor_type": "Wav2Vec2FeatureExtractor", "feature_size": 1, "padding_side": "right", "padding_value": 0.0, "return_attention_mask": false, "sampling_rate": 8000 }
处理器
对于支持多模态任务的模型,🤗 Transformers 提供了一个处理器类,方便地将处理类(如特征提取器和分词器)包装成一个单一对象。例如,让我们使用 Wav2Vec2Processor 来进行自动语音识别任务(ASR)。ASR 将音频转录为文本,因此您将需要一个特征提取器和一个分词器。
创建一个特征提取器来处理音频输入:
>>> from transformers import Wav2Vec2FeatureExtractor >>> feature_extractor = Wav2Vec2FeatureExtractor(padding_value=1.0, do_normalize=True)
创建一个分词器来处理文本输入:
>>> from transformers import Wav2Vec2CTCTokenizer >>> tokenizer = Wav2Vec2CTCTokenizer(vocab_file="my_vocab_file.txt")
将特征提取器和分词器组合在 Wav2Vec2Processor 中:
>>> from transformers import Wav2Vec2Processor >>> processor = Wav2Vec2Processor(feature_extractor=feature_extractor, tokenizer=tokenizer)
通过两个基本类 - 配置和模型 - 以及额外的预处理类(分词器、图像处理器、特征提取器或处理器),您可以创建🤗 Transformers 支持的任何模型。这些基本类都是可配置的,允许您使用您想要的特定属性。您可以轻松设置一个用于训练的模型或修改现有的预训练模型进行微调。
构建自定义模型
原文链接:
huggingface.co/docs/transformers/v4.37.2/en/custom_models
🤗 Transformers 库被设计为易于扩展。每个模型都完全在存储库的给定子文件夹中编码,没有抽象,因此您可以轻松复制一个建模文件并根据需要进行调整。
如果您正在编写全新的模型,从头开始可能更容易。在本教程中,我们将向您展示如何编写自定义模型及其配置,以便可以在 Transformers 中使用,并且您可以与社区共享(以及它所依赖的代码),以便任何人都可以使用它,即使它不在🤗 Transformers 库中。我们将看到如何在 transformers 上构建并扩展框架,使用您的钩子和自定义代码。
我们将在 ResNet 模型上说明所有这些,通过将timm 库的 ResNet 类包装到 PreTrainedModel 中。
编写自定义配置
在深入研究模型之前,让我们先编写其配置。模型的配置是一个对象,其中包含构建模型所需的所有信息。正如我们将在下一节中看到的,模型只能接受一个config
进行初始化,因此我们确实需要该对象尽可能完整。
在我们的示例中,我们将获取 ResNet 类的一些参数,可能需要进行调整。然后,不同的配置将给我们不同类型的可能的 ResNets。然后我们只需存储这些参数,之前检查其中一些参数的有效性。
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_type` must be 'basic' or bottleneck', got {block_type}.") if stem_type not in ["", "deep", "deep-tiered"]: raise ValueError(f"`stem_type` must be '', 'deep' or 'deep-tiered', got {stem_type}.") 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)
编写自定义配置时需要记住的三个重要事项如下:
- 您必须继承自
PretrainedConfig
, - 您的
PretrainedConfig
的__init__
必须接受任何 kwargs, - 这些
kwargs
需要传递给超类__init__
。
继承是为了确保您从🤗 Transformers 库中获得所有功能,而另外两个约束来自于PretrainedConfig
拥有比您设置的字段更多。当使用from_pretrained
方法重新加载配置时,这些字段需要被您的配置接受,然后发送到超类。
为您的配置定义model_type
(这里model_type="resnet"
)不是强制性的,除非您希望将您的模型注册到自动类(请参见最后一节)。
完成后,您可以像处理库中任何其他模型配置一样轻松创建和保存您的配置。以下是我们如何创建一个 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),一个适用于图像分类的模型(类似于 BertForSequenceClassification)。
如前所述,我们将只编写模型的松散包装,以使示例简单化。在编写此类之前,我们需要做的唯一事情是将块类型与实际块类之间建立映射。然后通过将所有内容传递给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)
对于将对图像进行分类的模型,我们只需更改前向方法:
import torch 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
调用超类初始化(有点像当您编写常规torch.nn.Module
时)。设置config_class
的行不是强制性的,除非您想将您的模型注册到自动类(请参见最后一节)。
如果您的模型与库中的模型非常相似,您可以重用与该模型相同的配置。
您可以让您的模型返回任何您想要的内容,但是像我们为ResnetModelForImageClassification
所做的那样返回一个包含损失的字典,当传递标签时,将使您的模型可以直接在 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())
现在让我们看看如何确保在执行 save_pretrained()或 push_to_hub()时,模型的代码被保存。
将带有自定义代码的模型注册到自动
如果您正在编写一个扩展🤗 Transformers 的库,您可能希望扩展自动类以包括您自己的模型。这与将代码推送到 Hub 不同,用户需要导入您的库才能获取自定义模型(与自动从 Hub 下载模型代码相反)。
只要您的配置具有与现有模型类型不同的model_type
属性,并且您的模型类具有正确的config_class
属性,您就可以像这样将它们添加到自动类中:
from transformers import AutoConfig, AutoModel, AutoModelForImageClassification AutoConfig.register("resnet", ResnetConfig) AutoModel.register(ResnetConfig, ResnetModel) AutoModelForImageClassification.register(ResnetConfig, ResnetModelForImageClassification)
注意,在将自定义配置注册到 AutoConfig 时使用的第一个参数需要与自定义配置的model_type
匹配,并且在将自定义模型注册到任何自动模型类时使用的第一个参数需要与这些模型的config_class
匹配。
将代码发送到 Hub
此 API 是实验性的,可能在下一个版本中有一些轻微的破坏性更改。
首先,请确保您的模型在一个.py
文件中完全定义。它可以依赖于一些其他文件的相对导入,只要所有文件都在同一个目录中(我们目前还不支持此功能的子模块)。对于我们的示例,我们将在当前工作目录中的一个名为resnet_model
的文件夹中定义一个modeling_resnet.py
文件和一个configuration_resnet.py
文件。配置文件包含ResnetConfig
的代码,建模文件包含ResnetModel
和ResnetModelForImageClassification
的代码。
. └── resnet_model ├── __init__.py ├── configuration_resnet.py └── modeling_resnet.py
__init__.py
可以为空,只是为了让 Python 检测resnet_model
可以用作模块。
如果从库中复制建模文件,则需要将文件顶部的所有相对导入替换为从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),但对于模型来说情况不同。您的自定义模型可能适用于许多不同的任务,因此您必须指定哪个自动类是您模型的正确类。
如果要将代码文件复制,可以使用register_for_auto_class()
。如果您更喜欢从另一个存储库中的 Hub 使用代码,则无需调用它。在存在多个自动类的情况下,可以直接修改config.json
,使用以下结构:
"auto_map": { "AutoConfig": "<your-repo-name>--<config-name>", "AutoModel": "<your-repo-name>--<config-name>", "AutoModelFor<Task>": "<your-repo-name>--<config-name>", },
接下来,让我们像以前一样创建配置和模型:
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 格式复制建模权重和配置外,还将建模和配置的.py
文件复制到custom-resnet50d
文件夹中,并将结果上传到 Hub。您可以在此model repo中查看结果。
有关将模型推送到 Hub 的方法的更多信息,请参阅共享教程。
使用具有自定义代码的模型
您可以在其存储库中使用任何配置、模型或分词器与自定义代码文件,使用自动类和from_pretrained
方法。所有上传到 Hub 的文件和代码都会被扫描以检测恶意软件(有关更多信息,请参阅Hub 安全文档),但您仍应查看模型代码和作者,以避免在您的计算机上执行恶意代码。设置trust_remote_code=True
以使用具有自定义代码的模型:
from transformers import AutoModelForImageClassification model = AutoModelForImageClassification.from_pretrained("sgugger/custom-resnet50d", trust_remote_code=True)
强烈建议传递提交哈希作为revision
,以确保模型的作者没有使用一些恶意的新代码更新代码(除非您完全信任模型的作者)。
commit_hash = "ed94a7c6247d8aedce4647f00f20de6875b5b292" model = AutoModelForImageClassification.from_pretrained( "sgugger/custom-resnet50d", trust_remote_code=True, revision=commit_hash )
请注意,在 Hub 浏览模型存储库的提交历史时,有一个按钮可以轻松复制任何提交的提交哈希。
Transformers 4.37 中文文档(七)(3)https://developer.aliyun.com/article/1564150