目录
- 一、说明
- 二、自然语言处理
- 2.1 Pad
- 2.2 截断
- 2.3 构建张量
- 三、TensorFlow
- 四、处理语音
- 五、计算机视觉
- 六、填充
- 七、Multimodal
一、说明
在数据集上训练模型之前,需要将其预处理为预期的模型输入格式。无论您的数据是文本、图像还是音频,都需要将它们转换并组装成批量张量。🤗 Transformers 提供了一组预处理类来帮助为模型准备数据。在本教程中,您将了解以下内容:
- 文本,使用Tokenizer将文本转换为标记序列,创建标记的数字表示,并将它们组装成张量。
- 语音和音频,使用特征提取器从音频波形中提取顺序特征并将其转换为张量。
- 图像输入使用ImageProcessor将图像转换为张量。
- 多模态输入,使用处理器来组合分词器和特征提取器或图像处理器。
AutoProcessor 无论您使用分词器、图像处理器、特征提取器还是处理器,它始终有效并自动为您正在使用的模型选择正确的类。
在开始之前,请安装 🤗 数据集,以便您可以加载一些数据集进行实验:
pip install datasets
二、自然语言处理
预处理文本数据的主要工具是分词器。分词器根据一组规则将文本分割成标记。标记被转换为数字,然后是张量,成为模型输入。模型所需的任何额外输入均由分词器添加。
如果您计划使用预训练模型,那么使用关联的预训练分词器非常重要。这确保了文本以与预训练语料库相同的方式进行分割,并在预训练期间使用相同的相应标记到索引(通常称为词汇)。
首先使用AutoTokenizer.from_pretrained()方法加载预训练的分词器。这将下载模型预训练的词汇:
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
然后将您的文本传递给标记器:
encoded_input = tokenizer("Do not meddle in the affairs of wizards, for they are subtle and quick to anger.")
print(encoded_input)
{ ‘input_ids’ : [ 101 , 2079 , 2025 , 19960 , 10362 , 1999 , 1996 , 3821 , 1997 , 16657 , 1010 , 2005 , 2027 , 2024 , 11259 , 1998 , 4248 , 2000 , 4963 , 1012 , 102 ] ,
’ token_type_ids’ : [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
‘attention_mask’ : [ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ] }
分词器返回一个包含三个重要项目的字典:
input_ids是句子中每个标记对应的索引。
- Attention_mask指示是否应注意令牌。
- token_type_ids标识当存在多个序列时令牌属于哪一个序列。
- 通过解码返回您的输入input_ids:
tokenizer.decode(encoded_input["input_ids"])
'[CLS] 不要插手巫师的事务,因为他们很狡猾且容易生气。[九月]'正如您所看到的,分词器在句子中添加了两个特殊的标记 - CLSand SEP(分类器和分隔符)。并非所有模型都需要特殊标记,但如果需要,标记生成器会自动为您添加它们。
如果您想要预处理多个句子,请将它们作为列表传递给分词器:
batch_sentences = [
"But what about second breakfast?",
"Don't think he knows about second breakfast, Pip.",
"What about elevensies?",
]
encoded_inputs = tokenizer(batch_sentences)
print(encoded_inputs)
{ ‘input_ids’ : [[ 101 , 1252 , 1184 , 1164 , 1248 , 6462 , 136 , 102 ],
[ 101、1790、112、189、1341、1119、3520、1164、1248、6462、117、21902、1643、119、102 ] 、 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
[ 101 , 1327 , 1164 , 5450 , 23434 , 136 , 102 ]],
‘token_type_ids’ : [[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 0 , 0 , 0 , 0 , 0 , 0 , 0 ]],
‘attention_mask’ : [[ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ],
[ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ],
[ 1 , 1 , 1 , 1 , 1 , 1 , 1 ]]}
2.1 Pad
句子的长度并不总是相同,这可能是一个问题,因为张量(模型输入)需要具有统一的形状。填充是一种通过向较短句子添加特殊填充标记来确保张量为矩形的策略。
将参数设置padding为True以填充批次中的较短序列以匹配最长序列:
batch_sentences = [
"But what about second breakfast?",
"Don't think he knows about second breakfast, Pip.",
"What about elevensies?",
]
encoded_input = tokenizer(batch_sentences, padding=True)
print(encoded_input)
{ ‘input_ids’ : [[ 101 , 1252 , 1184 , 1164 , 1248 , 6462 , 136 , 102 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 101、1790、112、189、1341、1119、3520、1164、1248、6462、117、21902、1643、119、102 ] 、 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
[ 101 , 1327 , 1164 , 5450 , 23434 , 136 , 102 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]],
‘token_type_ids’ : [[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]],
‘attention_mask’ : [[ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ],
[ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]]}
第一个和第三个句子现在用0’ 填充,因为它们更短。
2.2 截断
另一方面,有时序列可能太长,模型无法处理。在这种情况下,您需要将序列截断为更短的长度。
将参数设置truncation为True将序列截断为模型接受的最大长度:
batch_sentences = [
"But what about second breakfast?",
"Don't think he knows about second breakfast, Pip.",
"What about elevensies?",
]
encoded_input = tokenizer(batch_sentences, padding=True, truncation=True)
print(encoded_input)
{ ‘input_ids’ : [[ 101 , 1252 , 1184 , 1164 , 1248 , 6462 , 136 , 102 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 101、1790、112、189、1341、1119、3520、1164、1248、6462、117、21902、1643、119、102 ] 、 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
[ 101 , 1327 , 1164 , 5450 , 23434 , 136 , 102 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]],
‘token_type_ids’ : [[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]],
‘attention_mask’ : [[ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ],
[ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]]}
查看填充和截断概念指南以了解更多不同的填充和截断参数。
2.3 构建张量
最后,您希望分词器返回输入模型的实际张量。
将return_tensors参数设置pt为 PyTorch 或tfTensorFlow:
batch_sentences = [
"But what about second breakfast?",
"Don't think he knows about second breakfast, Pip.",
"What about elevensies?",
]
encoded_input = tokenizer(batch_sentences, padding=True, truncation=True, return_tensors="pt")
print(encoded_input)
{ ‘input_ids’ : 张量([[ 101 , 1252 , 1184 , 1164 , 1248 , 6462 , 136 , 102 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 101、1790、112、189、1341、1119、3520、1164、1248、6462、117、21902、1643、119、102 ] 、 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
[ 101 , 1327 , 1164 , 5450 , 23434 , 136 , 102 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]]),
‘token_type_ids’ : 张量([[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]]),
‘attention_mask’ : 张量([[ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ],
[ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]])}
三、TensorFlow
batch_sentences = [
"But what about second breakfast?",
"Don't think he knows about second breakfast, Pip.",
"What about elevensies?",
]
encoded_input = tokenizer(batch_sentences, padding=True, truncation=True, return_tensors="tf")
print(encoded_input)
{ ‘input_ids’ : <tf.Tensor: shape=( 2 , 9 ), dtype=int32, numpy=
数组([[ 101 , 1252 , 1184 , 1164 , 1248 , 6462 , 136 , 102 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ,
[ 101、1790、112、189、1341、1119、3520、1164、1248、6462、117、21902、1643、119、102 ] 、 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
[ 101 , 1327 , 1164 , 5450 , 23434 , 136 , 102 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]],
dtype=int32)>,
‘token_type_ids’ : <tf.Tensor: shape=( 2 , 9 ), dtype=int32, numpy=
数组([[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ,
[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ],
[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]], dtype=int32)>,
‘attention_mask’ : <tf.Tensor: shape= ( 2 , 9 ), dtype=int32, numpy=
数组([[ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ,
[ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ],
[ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]], dtype=int32)>}
不同的管道在其“call()”中支持分词器参数的方式不同。“text-2-text- Generation”管道仅支持(即传递)“截断”。“文本生成”管道支持“max_length”、“truncation”、“padding”和“add_special_tokens”。在“fill-mask”管道中,分词器参数可以在“tokenizer_kwargs”参数(字典)中传递。
四、处理语音
对于音频任务,您需要一个特征提取器来为模型准备数据集。特征提取器旨在从原始音频数据中提取特征,并将其转换为张量。
加载MInDS-14数据集(有关如何加载数据集的更多详细信息,请参阅🤗数据集教程)以了解如何将特征提取器与音频数据集一起使用:
from datasets import load_dataset, Audio
dataset = load_dataset("PolyAI/minds14", name="en-US", split="train")
访问该列的第一个元素audio以查看输入。调用该audio列会自动加载音频文件并重新采样:
dataset[0]["audio"]
{ ‘数组’ : 数组([ 0. , 0.00024414 , - 0.00024414 , …, - 0.00024414 ,
0. , 0. ], dtype=float32),
‘路径’ : ‘/root/.cache/huggingface/datasets/下载/提取/f14948e0e84be638dd7943ac36518a4cf3324e8b7aa331c5ab11541518e9368c/en-US~JOINT_ACCOUNT/602ba55abb1e6d0fbce92065.wav’,‘sampling_rate’:8000 }
_
这将返回三个项目:
- array是作为一维数组加载并可能重新采样的语音信号。
- path指向音频文件的位置。
- sampling_rate指的是每秒测量语音信号中的多少个数据点。
在本教程中,您将使用Wav2Vec2模型。查看模型卡,您将了解到 Wav2Vec2 是在 16kHz 采样语音音频上进行预训练的。音频数据的采样率与用于预训练模型的数据集的采样率相匹配非常重要。如果您的数据的采样率不相同,那么您需要对数据重新采样。
- 使用Datasets的cast_column方法将采样率上采样到16kHz:
dataset = dataset.cast_column("audio", Audio(sampling_rate=16_000))
- 其次调用该audio列以重新采样音频文件:
dataset[0][“audio”]
{ ‘数组’ : 数组([ 2.3443763e-05 , 2.1729663e-04 , 2.2145823e-04 , …,
3.8356509e-05 , - 7.3497440e-06 , - 2.1754686e-05 ], dtype=float32),
‘路径’:‘/root/.cache/huggingface/datasets/downloads/extracted/f14948e0e84be638dd7943ac36518a4cf3324e8b7aa331c5ab11541518e9368c/en-US~JOINT_ACCOUNT/602ba55abb1e6d0fbce92065.wav’ ,
‘采样率’ : 16000 }
接下来,加载特征提取器以规范化并填充输入。填充文本数据时,0会添加 a 以表示较短的序列。同样的想法也适用于音频数据。特征提取器将0- 解释为静音 - 添加到array。
使用AutoFeatureExtractor.from_pretrained()加载特征提取器:
from transformers import AutoFeatureExtractor
feature_extractor = AutoFeatureExtractor.from_pretrained("facebook/wav2vec2-base")
将音频传递array到特征提取器。我们还建议sampling_rate在特征提取器中添加参数,以便更好地调试可能发生的任何静默错误。
audio_input = [dataset[0]["audio"]["array"]]
feature_extractor(audio_input, sampling_rate=16000)
{ ‘input_values’ : [array([ 3.8106556e-04 , 2.7506407e-03 , 2.8015103e-03 , …,
5.6335266e-04 , 4.6588284e-06 , - 1.7142107e-04 ], dtype=float32) ] }
就像分词器一样,您可以应用填充或截断来处理批量中的变量序列。看一下这两个音频样本的序列长度:
dataset[0]["audio"]["array"].shape
dataset[1]["audio"]["array"].shape
创建一个函数来预处理数据集,使音频样本具有相同的长度。指定最大样本长度,特征提取器将填充或截断序列以匹配它:
def preprocess_function(examples):
audio_arrays = [x["array"] for x in examples["audio"]]
inputs = feature_extractor(
audio_arrays,
sampling_rate=16000,
padding=True,
max_length=100000,
truncation=True,
)
return inputs
将 应用于preprocess_function数据集中的前几个示例:
processed_dataset = preprocess_function(dataset[:5])
样本长度现在相同并且与指定的最大长度匹配。您现在可以将处理后的数据集传递给模型!
processed_dataset["input_values"][0].shape
processed_dataset["input_values"][1].shape
五、计算机视觉
对于计算机视觉任务,您需要一个图像处理器来为模型准备数据集。图像预处理由几个步骤组成,将图像转换为模型期望的输入。这些步骤包括但不限于调整大小、标准化、颜色通道校正以及将图像转换为张量。
图像预处理通常遵循某种形式的图像增强。图像预处理和图像增强都对图像数据进行变换,但它们的目的不同:
图像增强以有助于防止过度拟合并提高模型稳健性的方式改变图像。您可以在增强数据的方式上发挥创意 - 调整亮度和颜色、裁剪、旋转、调整大小、缩放等。但是,请注意不要通过增强来改变图像的含义。
图像预处理可确保图像与模型的预期输入格式匹配。在微调计算机视觉模型时,必须与模型最初训练时完全一样地对图像进行预处理。
您可以使用任何您喜欢的库来进行图像增强。对于图像预处理,使用ImageProcessor与模型关联的。
加载food101数据集(有关如何加载数据集的更多详细信息,请参阅 🤗数据集教程)以了解如何将图像处理器与计算机视觉数据集结合使用:
使用 🤗 Datasetssplit参数仅从训练分割中加载小样本,因为数据集非常大!
from datasets import load_dataset
dataset = load_dataset("food101", split="train[:100]")
接下来,使用 🤗 数据集功能查看图像Image:
dataset[0]["image"]
使用AutoImageProcessor.from_pretrained()加载图像处理器:
from transformers import AutoImageProcessor
image_processor = AutoImageProcessor.from_pretrained("google/vit-base-patch16-224")
首先,让我们添加一些图像增强。您可以使用您喜欢的任何库,但在本教程中,我们将使用 torchvision 的transforms模块。如果您有兴趣使用其他数据增强库,请在Albumentations或Kornia 笔记本中了解如何操作。
1. 在这里,我们使用Compose将几个变换链接在一起 -RandomResizedCrop和ColorJitter。请注意,对于调整大小,我们可以从image_processor. 对于某些模型,需要精确的高度和宽度,而对于其他模型,仅shortest_edge定义了高度和宽度。
from torchvision.transforms import RandomResizedCrop, ColorJitter, Compose
size = (
image_processor.size["shortest_edge"]
if "shortest_edge" in image_processor.size
else (image_processor.size["height"], image_processor.size["width"])
)
_transforms = Compose([RandomResizedCrop(size), ColorJitter(brightness=0.5, hue=0.5)])
2. 该模型接受pixel_values 作为其输入。ImageProcessor可以负责标准化图像并生成适当的张量。创建一个函数,将一批图像的图像增强和图像预处理相结合并生成pixel_values:
def transforms(examples):
images = [_transforms(img.convert("RGB")) for img in examples["image"]]
examples["pixel_values"] = image_processor(images, do_resize=False, return_tensors="pt")["pixel_values"]
return examples
在上面的示例中,我们进行设置do_resize=False是因为我们已经在图像增强变换中调整了图像的大小,并利用了size适当的image_processor. 如果在图像增强期间不调整图像大小,请忽略此参数。默认情况下,ImageProcessor将处理调整大小。
如果您希望将图像标准化作为增强变换的一部分,请使用image_processor.image_mean, 和image_processor.image_std值。
3. 然后使用 🤗 数据集set_transform动态应用转换:
dataset.set_transform(transforms)
4. 现在,当您访问图像时,您会注意到图像处理器已添加pixel_values. 您现在可以将处理后的数据集传递给模型!
dataset[0].keys()
这是应用变换后图像的样子。该图像已被随机裁剪,并且其颜色属性有所不同。
import numpy as np
import matplotlib.pyplot as plt
img = dataset[0]["pixel_values"]
plt.imshow(img.permute(1, 2, 0))
对于对象检测、语义分割、实例分割和全景分割等任务,ImageProcessor 提供后处理方法。这些方法将模型的原始输出转换为有意义的预测,例如边界框或分割图。
六、填充
在某些情况下,例如,在微调DETR时,模型会在训练时应用尺度增强。这可能会导致一批图像的尺寸不同。您可以使用DetrImageProcessor.pad() DetrImageProcessor并定义一个自定义来将collate_fn图像一起批处理。
def collate_fn(batch):
pixel_values = [item["pixel_values"] for item in batch]
encoding = image_processor.pad(pixel_values, return_tensors="pt")
labels = [item["labels"] for item in batch]
batch = {}
batch["pixel_values"] = encoding["pixel_values"]
batch["pixel_mask"] = encoding["pixel_mask"]
batch["labels"] = labels
return batch
七、Multimodal
对于涉及多模式输入的任务,您需要一个处理器来为模型准备数据集。处理器将两个处理对象耦合在一起,例如分词器和特征提取器。
加载LJ 语音数据集(有关如何加载数据集的更多详细信息,请参阅 🤗数据集教程)以了解如何使用处理器进行自动语音识别 (ASR):
from datasets import load_dataset
lj_speech = load_dataset("lj_speech", split="train")
对于 ASR,您主要关注的是audio,text因此您可以删除其他列:
lj_speech = lj_speech.map(remove_columns=["file", "id", "normalized_text"])
现在看一下audio和text列:
lj_speech[0]["audio"]
{ ‘数组’ : 数组([- 7.3242188e-04 , - 7.6293945e-04 , - 6.4086914e-04 , …,
7.3242188e-04 , 2.1362305e-04 , 6.1035156e-05 ], dtype=float32) ,
‘路径’:‘/root/.cache/huggingface/datasets/downloads/extracted/917ece08c95cf0c4115e45294e3cd0dee724a1165b7fc11798369308a465bd26/LJSpeech-1.1/wavs/LJ001-0001.wav’,
‘放大率’:22050 }
lj_speech[0]["text"]
‘印刷,就我们目前所关心的唯一意义而言,与展览中展示的大多数艺术和手工艺不同,甚至是所有艺术和手工艺’
请记住,您应该始终对音频数据集的采样率进行重新采样,以匹配用于预训练模型的数据集的采样率!
lj_speech = lj_speech.cast_column("audio", Audio(sampling_rate=16_000))
使用AutoProcessor.from_pretrained()加载处理器:
from transformers import AutoProcessor
processor = AutoProcessor.from_pretrained("facebook/wav2vec2-base-960h")
1. 创建一个函数来将数组中包含的音频数据处理为 input_values,并将文本标记为标签。这些是模型的输入:
def prepare_dataset(example):
audio = example["audio"]
example.update(processor(audio=audio["array"], text=example["text"], sampling_rate=16000))
return example
2. 将prepare_dataset函数应用于样本:
prepare_dataset(lj_speech[0])
处理器现已添加input_values和labels,并且采样率也已正确下采样至 16kHz。您现在可以将处理后的数据集传递给模型!