视觉问答
原文链接:
huggingface.co/docs/transformers/v4.37.2/en/tasks/visual_question_answering
视觉问答(VQA)是根据图像回答开放式问题的任务。支持此任务的模型的输入通常是图像和问题的组合,输出是用自然语言表达的答案。
VQA 的一些值得注意的用例示例包括:
- 视障人士的辅助应用程序。
- 教育:提出关于讲座或教科书中呈现的视觉材料的问题。VQA 也可以用于互动博物馆展览或历史遗址。
- 客户服务和电子商务:VQA 可以通过让用户询问有关产品的问题来增强用户体验。
- 图像检索:VQA 模型可用于检索具有特定特征的图像。例如,用户可以询问“有狗吗?”以找到一组图像中所有带有狗的图像。
在本指南中,您将学习如何:
- 在
Graphcore/vqa数据集上对分类 VQA 模型(特别是 ViLT)进行微调。 - 使用您微调的 ViLT 进行推断。
- 使用生成模型(如 BLIP-2)进行零样本 VQA 推断。
微调 ViLT
ViLT 模型将文本嵌入集成到 Vision Transformer(ViT)中,使其在视觉和语言预训练(VLP)方面具有最小的设计。该模型可用于多个下游任务。对于 VQA 任务,分类器头部放置在顶部(线性层放在[CLS]标记的最终隐藏状态之上)并随机初始化。因此,视觉问答被视为分类问题。
最近的模型,如 BLIP、BLIP-2 和 InstructBLIP,将 VQA 视为生成任务。在本指南中,我们将说明如何将它们用于零样本 VQA 推断。
在开始之前,请确保已安装所有必要的库。
pip install -q transformers datasets
我们鼓励您与社区分享您的模型。登录到您的 Hugging Face 帐户将其上传到🤗 Hub。在提示时,输入您的令牌以登录:
>>> from huggingface_hub import notebook_login >>> notebook_login()
让我们将模型检查点定义为全局变量。
>>> model_checkpoint = "dandelin/vilt-b32-mlm"
加载数据
出于说明目的,在本指南中,我们使用了带注释的视觉问答Graphcore/vqa数据集的一个非常小的样本。您可以在🤗 Hub上找到完整的数据集。
作为对Graphcore/vqa数据集的替代,您可以从官方的VQA 数据集页面手动下载相同的数据。如果您希望使用自定义数据跟随教程,请查看🤗数据集文档中的创建图像数据集指南。
让我们加载验证集中的前 200 个示例并探索数据集的特点:
>>> from datasets import load_dataset >>> dataset = load_dataset("Graphcore/vqa", split="validation[:200]") >>> dataset Dataset({ features: ['question', 'question_type', 'question_id', 'image_id', 'answer_type', 'label'], num_rows: 200 })
让我们看一个例子来了解数据集的特点:
>>> dataset[0] {'question': 'Where is he looking?', 'question_type': 'none of the above', 'question_id': 262148000, 'image_id': '/root/.cache/huggingface/datasets/downloads/extracted/ca733e0e000fb2d7a09fbcc94dbfe7b5a30750681d0e965f8e0a23b1c2f98c75/val2014/COCO_val2014_000000262148.jpg', 'answer_type': 'other', 'label': {'ids': ['at table', 'down', 'skateboard', 'table'], 'weights': [0.30000001192092896, 1.0, 0.30000001192092896, 0.30000001192092896]}}
与任务相关的特征包括:
question:要从图像回答的问题image_id:问题所指图像的路径label:注释
我们可以删除其余的特征,因为它们不会是必要的:
>>> dataset = dataset.remove_columns(['question_type', 'question_id', 'answer_type'])
正如您所看到的,label特征包含了同一个问题的几个答案(这里称为ids),这些答案是由不同的人类注释者收集的。这是因为对问题的答案可能是主观的。在这种情况下,问题是“他在看哪里?”。有些人用“向下”注释,其他人用“看着桌子”,另一个人用“滑板”等等。
看一看图像,考虑你会给出什么答案:
>>> from PIL import Image >>> image = Image.open(dataset[0]['image_id']) >>> image
由于问题和答案的模糊性,像这样的数据集被视为多标签分类问题(因为可能有多个答案有效)。此外,与其只创建一个独热编码向量,不如创建一个软编码,基于某个答案在注释中出现的次数。
例如,在上面的示例中,因为答案“down”被选中的次数远远超过其他答案,它的得分(数据集中称为weight)为 1.0,而其余答案的得分<1.0。
为了以后用适当的分类头实例化模型,让我们创建两个字典:一个将标签名称映射到整数,另一个将整数映射回标签名称:
>>> import itertools >>> labels = [item['ids'] for item in dataset['label']] >>> flattened_labels = list(itertools.chain(*labels)) >>> unique_labels = list(set(flattened_labels)) >>> label2id = {label: idx for idx, label in enumerate(unique_labels)} >>> id2label = {idx: label for label, idx in label2id.items()}
现在我们有了映射,我们可以用它们的 id 替换字符串答案,并将数据集扁平化,以便进行更方便的进一步预处理。
>>> def replace_ids(inputs): ... inputs["label"]["ids"] = [label2id[x] for x in inputs["label"]["ids"]] ... return inputs >>> dataset = dataset.map(replace_ids) >>> flat_dataset = dataset.flatten() >>> flat_dataset.features {'question': Value(dtype='string', id=None), 'image_id': Value(dtype='string', id=None), 'label.ids': Sequence(feature=Value(dtype='int64', id=None), length=-1, id=None), 'label.weights': Sequence(feature=Value(dtype='float64', id=None), length=-1, id=None)}
数据预处理
下一步是加载 ViLT 处理器,为模型准备图像和文本数据。ViltProcessor 将 BERT 标记器和 ViLT 图像处理器封装到一个方便的单处理器中:
>>> from transformers import ViltProcessor >>> processor = ViltProcessor.from_pretrained(model_checkpoint)
为了预处理数据,我们需要使用 ViltProcessor 对图像和问题进行编码。处理器将使用 BertTokenizerFast 对文本进行标记化,并为文本数据创建input_ids、attention_mask和token_type_ids。至于图像,处理器将利用 ViltImageProcessor 来调整大小和规范化图像,并创建pixel_values和pixel_mask。
所有这些预处理步骤都是在幕后完成的,我们只需要调用processor。但是,我们仍然需要准备目标标签。在这种表示中,每个元素对应一个可能的答案(标签)。对于正确答案,元素保存其相应的分数(权重),而其余元素设置为零。
以下函数将processor应用于图像和问题,并按上述描述格式化标签:
>>> import torch >>> def preprocess_data(examples): ... image_paths = examples['image_id'] ... images = [Image.open(image_path) for image_path in image_paths] ... texts = examples['question'] ... encoding = processor(images, texts, padding="max_length", truncation=True, return_tensors="pt") ... for k, v in encoding.items(): ... encoding[k] = v.squeeze() ... targets = [] ... for labels, scores in zip(examples['label.ids'], examples['label.weights']): ... target = torch.zeros(len(id2label)) ... for label, score in zip(labels, scores): ... target[label] = score ... targets.append(target) ... encoding["labels"] = targets ... return encoding
要在整个数据集上应用预处理函数,使用🤗 Datasets 的map函数。您可以通过设置batched=True来加速map,以一次处理数据集的多个元素。此时,可以随意删除不需要的列。
>>> processed_dataset = flat_dataset.map(preprocess_data, batched=True, remove_columns=['question','question_type', 'question_id', 'image_id', 'answer_type', 'label.ids', 'label.weights']) >>> processed_dataset Dataset({ features: ['input_ids', 'token_type_ids', 'attention_mask', 'pixel_values', 'pixel_mask', 'labels'], num_rows: 200 })
作为最后一步,使用 DefaultDataCollator 创建一批示例:
>>> from transformers import DefaultDataCollator >>> data_collator = DefaultDataCollator()
训练模型
现在您已经准备好开始训练您的模型了!使用 ViltForQuestionAnswering 加载 ViLT。指定标签数量以及标签映射:
>>> from transformers import ViltForQuestionAnswering >>> model = ViltForQuestionAnswering.from_pretrained(model_checkpoint, num_labels=len(id2label), id2label=id2label, label2id=label2id)
此时,只剩下三个步骤:
- 在 TrainingArguments 中定义您的训练超参数:
>>> from transformers import TrainingArguments >>> repo_id = "MariaK/vilt_finetuned_200" >>> training_args = TrainingArguments( ... output_dir=repo_id, ... per_device_train_batch_size=4, ... num_train_epochs=20, ... save_steps=200, ... logging_steps=50, ... learning_rate=5e-5, ... save_total_limit=2, ... remove_unused_columns=False, ... push_to_hub=True, ... )
- 将训练参数传递给 Trainer,同时还需要传递模型、数据集、处理器和数据收集器。
>>> from transformers import Trainer >>> trainer = Trainer( ... model=model, ... args=training_args, ... data_collator=data_collator, ... train_dataset=processed_dataset, ... tokenizer=processor, ... )
- 调用 train()来微调您的模型。
>>> trainer.train()
一旦训练完成,使用 push_to_hub()方法将您的模型分享到🤗 Hub 上:
>>> trainer.push_to_hub()
推理
现在您已经对 ViLT 模型进行了微调,并将其上传到🤗 Hub,您可以用它进行推理。尝试使用 Pipeline 中的微调模型进行推理的最简单方法。
>>> from transformers import pipeline >>> pipe = pipeline("visual-question-answering", model="MariaK/vilt_finetuned_200")
本指南中的模型仅在 200 个示例上进行了训练,因此不要对其抱有很大期望。让我们看看它是否至少从数据中学到了一些东西,并从数据集中取第一个示例来说明推理:
>>> example = dataset[0] >>> image = Image.open(example['image_id']) >>> question = example['question'] >>> print(question) >>> pipe(image, question, top_k=1) "Where is he looking?" [{'score': 0.5498199462890625, 'answer': 'down'}]
尽管不是很自信,但模型确实学到了一些东西。有了更多的例子和更长的训练,你会得到更好的结果!
如果愿意,您也可以手动复制管道的结果:
- 拿一张图片和一个问题,使用你模型的处理器为模型准备它们。
- 将结果或预处理通过模型传递。
- 从 logits 中获取最可能答案的 id,并在
id2label中找到实际答案。
>>> processor = ViltProcessor.from_pretrained("MariaK/vilt_finetuned_200") >>> image = Image.open(example['image_id']) >>> question = example['question'] >>> # prepare inputs >>> inputs = processor(image, question, return_tensors="pt") >>> model = ViltForQuestionAnswering.from_pretrained("MariaK/vilt_finetuned_200") >>> # forward pass >>> with torch.no_grad(): ... outputs = model(**inputs) >>> logits = outputs.logits >>> idx = logits.argmax(-1).item() >>> print("Predicted answer:", model.config.id2label[idx]) Predicted answer: down
零样本 VQA
先前的模型将 VQA 视为分类任务。一些最近的模型,如 BLIP、BLIP-2 和 InstructBLIP,将 VQA 视为生成任务。让我们以 BLIP-2 为例。它引入了一种新的视觉语言预训练范式,其中可以使用任何组合的预训练视觉编码器和 LLM(在BLIP-2 博客文章中了解更多)。这使得在多个视觉语言任务中包括视觉问答上实现了最先进的结果。
让我们说明如何使用这个模型进行 VQA。首先,让我们加载模型。在这里,如果可用,我们将明确将模型发送到 GPU,这在训练时不需要做,因为 Trainer 会自动处理:
>>> from transformers import AutoProcessor, Blip2ForConditionalGeneration >>> import torch >>> processor = AutoProcessor.from_pretrained("Salesforce/blip2-opt-2.7b") >>> model = Blip2ForConditionalGeneration.from_pretrained("Salesforce/blip2-opt-2.7b", torch_dtype=torch.float16) >>> device = "cuda" if torch.cuda.is_available() else "cpu" >>> model.to(device)
该模型将图像和文本作为输入,因此让我们使用 VQA 数据集中第一个示例中完全相同的图像/问题对:
>>> example = dataset[0] >>> image = Image.open(example['image_id']) >>> question = example['question']
要将 BLIP-2 用于视觉问答任务,文本提示必须遵循特定格式:问题:{} 答案:。
>>> prompt = f"Question: {question} Answer:"
现在我们需要使用模型的处理器对图像/提示进行预处理,通过模型传递处理后的输入,并解码输出:
>>> inputs = processor(image, text=prompt, return_tensors="pt").to(device, torch.float16) >>> generated_ids = model.generate(**inputs, max_new_tokens=10) >>> generated_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0].strip() >>> print(generated_text) "He is looking at the crowd"
正如您所看到的,模型识别了人群和脸部的方向(向下看),但似乎忽略了人群在滑冰者后面的事实。然而,在无法获取人类注释数据集的情况下,这种方法可以快速产生有用的结果。
文本到语音
原始文本:
huggingface.co/docs/transformers/v4.37.2/en/tasks/text-to-speech
文本到语音(TTS)是从文本创建自然语音的任务,语音可以用多种语言和多个说话者生成。目前在🤗 Transformers 中有几种文本到语音模型,如 Bark、MMS、VITS 和 SpeechT5。
您可以轻松使用"text-to-audio"流水线(或其别名"text-to-speech")生成音频。一些模型,如 Bark,还可以被调节以生成非语言交流,如笑声、叹息和哭泣,甚至添加音乐。以下是您如何使用"text-to-speech"流水线与 Bark 的示例:
>>> from transformers import pipeline >>> pipe = pipeline("text-to-speech", model="suno/bark-small") >>> text = "[clears throat] This is a test ... and I just took a long pause." >>> output = pipe(text)
以下是一个代码片段,您可以使用它在笔记本中听取生成的音频:
>>> from IPython.display import Audio >>> Audio(output["audio"], rate=output["sampling_rate"])
有关 Bark 和其他预训练 TTS 模型的更多示例,请参考我们的音频课程。
如果您想要微调 TTS 模型,目前在🤗 Transformers 中唯一可用的文本到语音模型是 SpeechT5 和 FastSpeech2Conformer,未来将会添加更多。SpeechT5 在文本到语音和语音到文本数据的组合上进行了预训练,使其能够学习文本和语音共享的隐藏表示空间。这意味着相同的预训练模型可以用于不同的任务。此外,SpeechT5 通过 x-vector 说话者嵌入支持多个说话者。
本指南的其余部分将说明如何:
- 微调 SpeechT5,该模型最初是在英语语音上进行训练的,在VoxPopuli数据集的荷兰语(
nl)语言子集上。 - 使用您精炼的模型进行推理的两种方式之一:使用流水线或直接。
在开始之前,请确保已安装所有必要的库:
pip install datasets soundfile speechbrain accelerate
从源代码安装🤗Transformers,因为并非所有 SpeechT5 功能都已合并到官方发布中:
pip install git+https://github.com/huggingface/transformers.git
要按照本指南操作,您将需要一个 GPU。如果您在笔记本中工作,请运行以下命令以检查 GPU 是否可用:
!nvidia-smi
或者适用于 AMD GPU:
!rocm-smi
我们鼓励您登录您的 Hugging Face 账户,将您的模型上传并与社区分享。在提示时,输入您的令牌以登录:
>>> from huggingface_hub import notebook_login >>> notebook_login()
加载数据集
VoxPopuli是一个大规模的多语音语料库,包含 2009-2020 年欧洲议会活动录音的数据。它包含了 15 种欧洲语言的带标签音频转录数据。在本指南中,我们使用荷兰语子集,可以随意选择其他子集。
请注意,VoxPopuli 或任何其他自动语音识别(ASR)数据集可能不是训练 TTS 模型的最佳选择。对于 ASR 有益的特性,如过多的背景噪音,在 TTS 中通常是不希望的。然而,找到高质量、多语言和多说话者的 TTS 数据集可能会非常具有挑战性。
让我们加载数据:
>>> from datasets import load_dataset, Audio >>> dataset = load_dataset("facebook/voxpopuli", "nl", split="train") >>> len(dataset) 20968
20968 个示例应该足够进行微调。SpeechT5 期望音频数据的采样率为 16 kHz,因此请确保数据集中的示例符合此要求:
dataset = dataset.cast_column("audio", Audio(sampling_rate=16000))
Transformers 4.37 中文文档(六)(2)https://developer.aliyun.com/article/1564080