🤗 Exporters
👷 正在进行中 👷
这个包允许你将 🤗 Transformers 模型导出为 Core ML。
对于将模型转换为 TFLite,我们推荐使用 Optimum。
何时使用 🤗 Exporters
🤗 Transformers 模型是用 PyTorch、TensorFlow 或 JAX 实现的。但是,为了部署,你可能想使用不同的框架,比如 Core ML。这个库使得将 Transformers 模型转换为这种格式变得容易。
Exporters 包的目标是比使用 coremltools 编写自己的转换脚本更方便,并与 🤗 Transformers 库和 Hugging Face Hub 紧密集成。
为了更加便利,Exporters
驱动了一个无代码 transformers 到 Core ML 转换 Space。你可以在不安装任何东西的情况下试用它,以检查你感兴趣的模型是否可以被转换。如果转换成功,转换后的 Core ML 权重将被推送到 Hub。如需更多灵活性和关于转换过程的详细信息,请继续阅读。
注意:请记住,Transformer 模型通常相当大,并不总是适合在移动设备上使用。最好先使用 🤗 Optimum 优化模型以进行推理。
安装
克隆此仓库:
$ git clone https://github.com/huggingface/exporters.git
将其安装为 Python 包:
$ cd exporters
$ pip install -e .
安装完成!
注意:Core ML 导出器可以在 Linux 上使用,但推荐使用 macOS。
Core ML
Core ML 是 Apple 的软件库,用于使用神经网络和其他类型的机器学习模型进行快速的设备上模型推理。它可以在 macOS、iOS、tvOS 和 watchOS 上使用,并针对使用 CPU、GPU 和 Apple Neural Engine 进行了优化。尽管 Core ML 框架是专有的,但 Core ML 文件格式是一种开放格式。
Core ML 导出器使用 coremltools 执行从 PyTorch 或 TensorFlow 到 Core ML 的转换。
exporters.coreml
包使你能够通过利用配置对象将模型检查点转换为 Core ML 模型。这些配置对象为许多模型架构提供了现成的解决方案,并设计为易于扩展到其他架构。
现成的配置包括以下架构:
- BEiT
- BERT
- ConvNeXT
- CTRL
- CvT
- DistilBERT
- DistilGPT2
- GPT2
- LeViT
- MobileBERT
- MobileViT
- SegFormer
- SqueezeBERT
- Vision Transformer (ViT)
- YOLOS
点击此处查看支持的模型的完整列表。
将模型导出为 Core ML
exporters.coreml
包可以从命令行作为 Python 模块使用。要使用现成的配置导出检查点,请执行以下操作:
python -m exporters.coreml --model=distilbert-base-uncased exported/
这会导出由 --model
参数定义的检查点的 Core ML 版本。在这个例子中是 distilbert-base-uncased
,但它可以是 Hugging Face Hub 上的任何检查点或本地存储的检查点。
生成的 Core ML 文件将保存到 exported
目录,名为 Model.mlpackage
。你也可以指定一个文件名,比如 DistilBERT.mlpackage
。
转换过程输出许多警告消息和其他日志信息是正常的。你可以安全地忽略这些。如果一切顺利,导出应该以以下日志结束:
验证 Core ML 模型...
-[✓] Core ML 模型输出名称与参考模型匹配 ({'last_hidden_state'})
- 验证 Core ML 模型输出 "last_hidden_state":
-[✓] (1, 128, 768) 匹配 (1, 128, 768)
-[✓] 所有值接近(误差:0.0001)
一切正常,模型已保存在:exported/Model.mlpackage
注意:虽然可以在 Linux 上将模型导出为 Core ML,但验证步骤只会在 Mac 上执行,因为它需要 Core ML 框架来运行模型。
生成的文件是 Model.mlpackage
。这个文件可以添加到 Xcode 项目中,并加载到 macOS 或 iOS 应用程序中。
导出的 Core ML 模型使用 mlpackage 格式和 ML Program 模型类型。这种格式于 2021 年推出,需要至少 iOS 15、macOS 12.0 和 Xcode 13。我们更倾向于使用这种格式,因为它是 Core ML 的未来。Core ML 导出器也可以生成旧的 .mlmodel
格式的模型,但不推荐这样做。
对于 Hub 上的 TensorFlow 检查点,过程是相同的。例如,你可以按如下方式导出来自 Keras 组织 的纯 TensorFlow 检查点:
python -m exporters.coreml --model=keras-io/transformers-qa exported/
要导出本地存储的模型,你需要将模型的权重和分词器文件存储在一个目录中。例如,我们可以按如下方式加载和保存检查点:
>>> from transformers import AutoTokenizer, AutoModelForSequenceClassification
>>> # 从 Hub 加载分词器和 PyTorch 权重
>>> tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
>>> pt_model = AutoModelForSequenceClassification.from_pretrained("distilbert-base-uncased")
>>> # 保存到磁盘
>>> tokenizer.save_pretrained("local-pt-checkpoint")
>>> pt_model.save_pretrained("local-pt-checkpoint")
一旦保存了检查点,你可以通过将 --model
参数指向持有检查点文件的目录来将其导出为 Core ML:
python -m exporters.coreml --model=local-pt-checkpoint exported/
为不同的模型拓扑选择特征
每个现成的配置都带有一组 特征,使你能够为不同类型的拓扑或任务导出模型。如下表所示,每个特征都与不同的自动类相关联:
特征 | 自动类 |
---|---|
default , default-with-past | AutoModel |
causal-lm , causal-lm-with-past | AutoModelForCausalLM |
ctc | AutoModelForCTC |
image-classification | AutoModelForImageClassification |
masked-im | AutoModelForMaskedImageModeling |
masked-lm | AutoModelForMaskedLM |
multiple-choice | AutoModelForMultipleChoice |
next-sentence-prediction | AutoModelForNextSentencePrediction |
object-detection | AutoModelForObjectDetection |
question-answering | AutoModelForQuestionAnswering |
semantic-segmentation | AutoModelForSemanticSegmentation |
seq2seq-lm , seq2seq-lm-with-past | AutoModelForSeq2SeqLM |
sequence-classification | AutoModelForSequenceClassification |
speech-seq2seq , speech-seq2seq-with-past | AutoModelForSpeechSeq2Seq |
token-classification | AutoModelForTokenClassification |
对于每个配置,你可以通过 FeaturesManager
找到支持的特征列表。例如,对于 DistilBERT,我们有:
>>> from exporters.coreml.features import FeaturesManager
>>> distilbert_features = list(FeaturesManager.get_supported_features_for_model_type("distilbert").keys())
>>> print(distilbert_features)
['default', 'masked-lm', 'multiple-choice', 'question-answering', 'sequence-classification', 'token-classification']
然后,你可以将这些特征之一传递给 exporters.coreml
包中的 --feature
参数。例如,要导出文本分类模型,我们可以从 Hub 中选择一个微调模型并运行:
python -m exporters.coreml --model=distilbert-base-uncased-finetuned-sst-2-english \
--feature=sequence-classification exported/
这将显示以下日志:
验证 Core ML 模型...
- Core ML 模型是分类器,验证输出
-[✓] 预测类 NEGATIVE 匹配 NEGATIVE
-[✓] 类别数 2 匹配 2
-[✓] 所有值接近(误差:0.0001)
一切正常,模型已保存在:exported/Model.mlpackage
请注意,在这种情况下,导出的模型是一个 Core ML 分类器,它除了预测概率字典外,还预测得分最高的类名,而不是我们之前在 distilbert-base-uncased
检查点中看到的 last_hidden_state
。这是预期的,因为微调模型有一个序列分类头。
带有 with-past
后缀的特征(例如 causal-lm-with-past
)对应于具有预计算隐藏状态(注意力块中的键和值)的模型拓扑,可用于快速自回归解码。
配置导出选项
要查看所有可能选项的完整列表,请从命令行运行以下命令:
python -m exporters.coreml --help
导出模型至少需要以下参数:
-m <模型>
: 来自Hugging Face Hub的模型ID,或加载模型的本地路径。--feature <任务>
: 模型应执行的任务,例如"image-classification"。请参阅上表了解可能的任务名称。<输出>
: 存储生成的Core ML模型的路径。
输出路径可以是一个文件夹,在这种情况下文件将被命名为Model.mlpackage
,或者您也可以直接指定文件名。
可以提供的其他参数:
--preprocessor <值>
: 使用哪种类型的预处理器。auto
尝试自动检测。可能的值有:auto
(默认),tokenizer
,feature_extractor
,processor
。--atol <数字>
: 验证模型时使用的绝对差异容差。默认值为1e-4。--quantize <值>
: 是否量化模型权重。可能的量化选项有:float32
表示不量化(默认)或float16
表示16位浮点。--compute_units <值>
: 是否针对CPU、GPU和/或神经引擎优化模型。可能的值有:all
(默认),cpu_and_gpu
,cpu_only
,cpu_and_ne
。
使用导出的模型
在应用程序中使用导出的模型就像使用任何其他Core ML模型一样。将模型添加到Xcode后,它将自动生成一个Swift类,让您可以从应用程序内进行预测。
根据选择的导出选项,您可能仍需要预处理或后处理输入和输出张量。
对于图像输入,无需执行任何预处理,因为Core ML模型将已经对像素进行归一化。对于分类器模型,Core ML模型将输出预测结果作为概率字典。对于其他模型,您可能需要做更多工作。
Core ML没有分词器的概念,因此文本模型仍然需要对输入数据进行手动分词。这里有一个示例说明如何在Swift中执行分词。
在配置对象中覆盖默认选择
Core ML的一个重要目标是使在应用程序中使用模型变得容易。在可能的情况下,Core ML导出器会向模型添加额外的操作,这样您就不必自己进行预处理和后处理。
特别是,
-
图像模型将自动执行像素归一化作为模型的一部分。除了可能需要调整大小或裁剪图像外,您无需自己预处理图像。
-
对于分类模型,会添加softmax层,并将标签包含在模型文件中。Core ML区分分类器模型和其他类型的神经网络。对于每个输入示例输出单个分类预测的模型,Core ML使模型预测获胜类别标签和概率字典,而不是原始logits张量。在可能的情况下,导出器使用这种特殊的分类器模型类型。
-
其他模型预测logits但不符合Core ML对分类器的定义,例如为序列中的每个token输出预测的"token-classification"任务。在这里,导出器还添加了softmax以将logits转换为概率。标签名称被添加到模型的元数据中。Core ML忽略这些标签名称,但可以通过编写几行Swift代码来检索它们。
-
"semantic-segmentation"模型将对输出图像进行上采样到原始空间维度,并应用argmax以获得预测的类别标签索引。它不会自动应用softmax。
Core ML导出器做出这些选择是因为它们是您最可能需要的设置。要覆盖上述任何默认值,您必须创建配置对象的子类,然后通过编写一个简短的Python程序将模型导出到Core ML。
示例:为了防止MobileViT语义分割模型对输出图像进行上采样,您可以创建MobileViTCoreMLConfig
的子类并覆盖outputs
属性以将do_upsample
设置为False。您可以为此输出设置的其他选项是do_argmax
和do_softmax
。
from collections import OrderedDict
from exporters.coreml.models import MobileViTCoreMLConfig
from exporters.coreml.config import OutputDescription
class MyCoreMLConfig(MobileViTCoreMLConfig):
@property
def outputs(self) -> OrderedDict[str, OutputDescription]:
return OrderedDict(
[
(
"logits",
OutputDescription(
"classLabels",
"每个像素的分类得分",
do_softmax=True,
do_upsample=False,
do_argmax=False,
)
),
]
)
config = MyCoreMLConfig(model.config, "semantic-segmentation")
在这里,您还可以将输出的名称从classLabels
更改为其他名称,或填写输出描述("每个像素的分类得分")。
也可以更改模型输入的属性。例如,对于文本模型,默认序列长度在1到128个token之间。要将DistilBERT模型的输入序列长度设置为固定的32个token,您可以按如下方式覆盖配置对象:
from collections import OrderedDict
from exporters.coreml.models import DistilBertCoreMLConfig
from exporters.coreml.config import InputDescription
class MyCoreMLConfig(DistilBertCoreMLConfig):
@property
def inputs(self) -> OrderedDict[str, InputDescription]:
input_descs = super().inputs
input_descs["input_ids"].sequence_length = 32
return input_descs
config = MyCoreMLConfig(model.config, "text-classification")
使用固定序列长度通常会输出更简单且可能更快的Core ML模型。但是,对于许多模型,输入需要具有灵活的长度。在这种情况下,为sequence_length
指定一个元组以设置(最小,最大)长度。使用(1, -1)表示序列长度没有上限。(注意:如果sequence_length
设置为固定值,则批量大小固定为1。)
要了解您感兴趣的模型可用的输入和输出选项,请创建其CoreMLConfig
对象并检查config.inputs
和config.outputs
属性。
并非总是需要所有输入或输出:对于文本模型,您可以删除attention_mask
输入。没有此输入时,注意力掩码始终假定填充为1(无填充)。但是,如果任务需要token_type_ids
输入,则还必须有attention_mask
输入。
通过创建CoreMLConfig
的子类并覆盖inputs
和outputs
属性来完成删除输入和/或输出。
默认情况下,会生成ML Program格式的模型。通过覆盖use_legacy_format
属性以返回True
,将使用较旧的NeuralNetwork格式。这不推荐,仅作为无法转换为ML Program格式的模型的解决方法。
一旦您有了修改后的config
实例,您可以按照下面"导出模型"部分的说明使用它来导出模型。
并非所有内容都由配置对象描述。转换后模型的行为也由模型的分词器或特征提取器决定。例如,要使用不同的输入图像大小,您可以使用不同的调整大小或裁剪设置创建特征提取器,并在转换过程中使用它而不是默认的特征提取器。
导出不受支持架构的模型
如果您希望导出库本身不支持的架构的模型,需要遵循三个主要步骤:
- 实现自定义Core ML配置。
- 将模型导出到Core ML。
- 验证PyTorch和导出模型的输出。
在本节中,我们将看看如何实现DistilBERT,以展示每个步骤涉及的内容。
实现自定义Core ML配置
TODO:尚未编写此部分,因为实现尚未完成
让我们从配置对象开始。我们提供了一个抽象类,您应该从中继承,CoreMLConfig
。
from exporters.coreml import CoreMLConfig
TODO:此处需要涵盖的内容:
modality
属性- 如何实现自定义操作 + 链接到coremltools关于此主题的文档
- 解码器模型(
use_past
)和编码器-解码器模型(seq2seq
)
导出模型
一旦实现了Core ML配置,下一步就是导出模型。这里我们可以使用exporters.coreml
包提供的export()
函数。该函数需要Core ML配置,以及基本模型和分词器(对于文本模型)或特征提取器(对于视觉模型):
from transformers import AutoConfig, AutoModelForSequenceClassification, AutoTokenizer
from exporters.coreml import export
from exporters.coreml.models import DistilBertCoreMLConfig
model_ckpt = "distilbert-base-uncased"
base_model = AutoModelForSequenceClassification.from_pretrained(model_ckpt, torchscript=True)
preprocessor = AutoTokenizer.from_pretrained(model_ckpt)
coreml_config = DistilBertCoreMLConfig(base_model.config, task="text-classification")
mlmodel = export(preprocessor, base_model, coreml_config)
注意:为获得最佳结果,在加载模型时将参数torchscript=True
传递给from_pretrained
。这允许模型为PyTorch跟踪配置自身,这是Core ML转换所需的。
可以传递给export()
的其他选项:
quantize
: 使用"float32"
表示不量化(默认),"float16"
将权重量化为16位浮点数。compute_units
: 是否针对CPU、GPU和/或神经引擎优化模型。默认为coremltools.ComputeUnit.ALL
。
要导出具有预计算隐藏状态(注意力块中的键和值)的模型以进行快速自回归解码,在创建CoreMLConfig
对象时传递参数use_past=True
。
Core ML导出器打印出大量警告和信息消息是正常的。特别是,您可能会看到如下消息:
TracerWarning: 将张量转换为Python布尔值可能会导致跟踪不正确。我们无法记录Python值的数据流,因此该值在将来将被视为常量。这意味着跟踪可能无法推广到其他输入!
这些消息是预期的,是转换过程的正常部分。如果存在真正的问题,转换器将抛出错误。
如果导出成功,export()
的返回值是一个coremltools.models.MLModel
对象。写入print(mlmodel)
以检查Core ML模型的输入、输出和元数据。
可选地填写模型的元数据:
mlmodel.short_description = "您出色的模型"
mlmodel.author = "您的姓名"
mlmodel.license = "在此填写版权信息"
mlmodel.version = "1.0"
最后,保存模型。您可以在Xcode中打开生成的mlpackage文件并在那里检查它。
mlmodel.save("DistilBert.mlpackage")
注意:如果使用的配置对象从 use_legacy_format
返回 True
,则可以将模型保存为 ModelName.mlmodel
而不是 .mlpackage
。
导出解码器模型
基于解码器的模型可以使用 past_key_values
输入,其中包含预先计算的隐藏状态(自注意力块中的键和值),这允许更快的顺序解码。通过向 Transformer 模型传递 use_cache=True
可以启用此功能。
要在 Core ML 导出器中启用此功能,请在创建 CoreMLConfig
对象时设置 use_past=True
参数:
coreml_config = CTRLCoreMLConfig(base_model.config, task="text-generation", use_past=True)
# 或者:
coreml_config = CTRLCoreMLConfig.with_past(base_model.config, task="text-generation")
这会向模型添加多个新的输入和输出,名称如 past_key_values_0_key
、past_key_values_0_value
、...(输入)和 present_key_values_0_key
、present_key_values_0_value
、...(输出)。
启用此选项使模型使用起来不太方便,因为你必须跟踪许多额外的张量,但它确实使序列推理速度更快。
Transformers 模型必须使用 is_decoder=True
加载,例如:
base_model = BigBirdForCausalLM.from_pretrained("google/bigbird-roberta-base", torchscript=True, is_decoder=True)
待办事项:如何在 Core ML 中使用此功能的示例。past_key_values
张量会随时间变大。attention_mask
张量必须具有 past_key_values
加上新 input_ids
的大小。
导出编码器-解码器模型
待办事项:正确编写此部分
你需要将模型导出为两个单独的 Core ML 模型:编码器和解码器。
按如下方式导出模型:
coreml_config = TODOCoreMLConfig(base_model.config, task="text2text-generation", seq2seq="encoder")
encoder_mlmodel = export(preprocessor, base_model.get_encoder(), coreml_config)
coreml_config = TODOCoreMLConfig(base_model.config, task="text2text-generation", seq2seq="decoder")
decoder_mlmodel = export(preprocessor, base_model, coreml_config)
使用 seq2seq
选项时,Core ML 模型中的序列长度始终是无界的。配置对象中指定的 sequence_length
将被忽略。
这也可以与 use_past=True
结合使用。待办事项:解释如何使用此功能。
验证模型输出
最后一步是验证基础模型和导出模型的输出在某个绝对容差范围内一致。你可以使用 exporters.coreml
包提供的 validate_model_outputs()
函数,如下所示。
首先启用日志记录:
from exporters.utils import logging
logger = logging.get_logger("exporters.coreml")
logger.setLevel(logging.INFO)
然后验证模型:
from exporters.coreml import validate_model_outputs
validate_model_outputs(
coreml_config, preprocessor, base_model, mlmodel, coreml_config.atol_for_validation
)
注意:validate_model_outputs
仅适用于 Mac 计算机,因为它依赖于 Core ML 框架来使用模型进行预测。
此函数使用 CoreMLConfig.generate_dummy_inputs()
方法为基础模型和导出模型生成输入,绝对容差可以在配置中定义。我们通常发现 1e-6 到 1e-4 范围内的数值一致性,尽管小于 1e-3 的任何值可能都没问题。
如果验证失败并出现以下错误,并不一定意味着模型已损坏:
ValueError: Output values do not match between reference model and Core ML exported model: Got max absolute difference of: 0.12345
比较是使用绝对差值进行的,在此示例中为 0.12345。这比默认容差值 1e-4 大得多,因此报告了错误。然而,激活的幅度也很重要。对于激活量级在 1e+3 左右的模型,0.12345 的最大绝对差通常是可以接受的。
如果验证失败并出现此错误,而你不完全确定这是否是真正的问题,请在虚拟输入张量上调用 mlmodel.predict()
,并查看输出张量中的最大绝对幅度。
为 🤗 Transformers 贡献新配置
我们希望扩展现成配置集,并欢迎社区的贡献!如果你想将你的添加贡献给库,你需要:
- 在
models.py
文件中实现 Core ML 配置 - 在 [
~coreml.features.FeatureManager
] 中包含模型架构和相应功能 - 将你的模型架构添加到
test_coreml.py
中的测试中
疑难解答:如果 Core ML Exporters 不适用于你的模型怎么办?
可能你希望导出的模型无法使用 Core ML Exporters 转换,甚至在你尝试直接使用 coremltools
时也会失败。运行这些自动转换工具时,转换很可能会因难以理解的错误消息而中止。或者,转换似乎成功了,但模型不工作或产生不正确的输出。
转换错误的最常见原因是:
-
你为转换器提供了不正确的参数。
task
参数应与所选模型架构匹配。例如,"feature-extraction"
任务只应与AutoModel
类型的模型一起使用,而不是AutoModelForXYZ
。此外,需要seq2seq
参数来区分编码器-解码器类型模型和仅编码器或仅解码器模型。为这些参数传递无效选择可能会在转换过程中出错,或者可能会创建一个工作但做错事的模型。 -
模型执行了 Core ML 或 coremltools 不支持的操作。coremltools 也可能存在错误或无法处理特别复杂的模型。
如果由于后者导致 Core ML 导出失败,你有几个选择:
-
在
CoreMLConfig
的patch_pytorch_ops()
函数中实现缺失的运算符。 -
修复原始模型。这需要深入了解模型的工作原理,并非易事。然而,有时修复方法是硬编码某些值,而不是让 PyTorch 或 TensorFlow 从张量的形状计算它们。
-
修复 coremltools。有时可以通过黑客方式让 coremltools 忽略该问题。
-
放弃自动转换,使用 MIL 从头开始构建模型。这是 coremltools 内部用于表示模型的中间语言。它在许多方面与 PyTorch 类似。
-
提交问题,我们会看看能做些什么。😀
已知问题
Core ML 导出器以 mlpackage 格式编写模型。不幸的是,对于某些模型,生成的 ML 程序是不正确的,在这种情况下,建议通过将配置对象的 use_legacy_format
属性设置为 True
来将模型转换为较旧的 NeuralNetwork 格式。在某些硬件上,较旧的格式也可能运行得更有效率。如果你不确定使用哪一个,请导出模型两次并比较两个版本。
已知需要使用 use_legacy_format=True
导出的模型有:GPT2、DistilGPT2。
对 GPT2 或 GPT-Neo 使用灵活的输入序列长度会导致转换器极其缓慢,并分配超过 200 GB 的 RAM。这显然是 coremltools 或 Core ML 框架的错误,因为分配的内存从未使用过(计算机不会开始交换)。经过多分钟后,转换确实成功了,但模型可能不是 100% 正确。之后加载模型需要很长时间,并进行类似的内存分配。同样适用于进行预测。虽然理论上转换成功了(如果你有足够的耐心),但模型实际上无法像这样使用。
将模型推送到 Hugging Face Hub
Hugging Face Hub 也可以托管你的 Core ML 模型。你可以使用 huggingface_hub
包 从 Python 将转换后的模型上传到 Hub。
首先使用以下命令登录你的 Hugging Face 账户:
huggingface-cli login
登录后,按如下方式将 mlpackage 保存到 Hub:
from huggingface_hub import Repository
with Repository(
"<model name>", clone_from="https://huggingface.co/<user>/<model name>",
use_auth_token=True).commit(commit_message="add Core ML model"):
mlmodel.save("<model name>.mlpackage")
确保将 <model name>
替换为模型的名称,将 <user>
替换为你的 Hugging Face 用户名。