# Resposta a perguntas visuais

Visual Question Answering (VQA) é a tarefa de responder a perguntas abertas com base em uma imagem. A entrada para modelos que suportam essa tarefa é tipicamente uma combinação de uma imagem e uma pergunta, e a saída é uma resposta expressa em linguagem natural.

Alguns exemplos notáveis de casos de uso para VQA incluem:

* Aplicações de acessibilidade para pessoas com deficiência visual.
* Educação: formular perguntas sobre materiais visuais apresentados em aulas ou livros didáticos. O VQA também pode ser utilizado em exposições de museus interativas ou em locais históricos.
* Atendimento ao cliente e comércio eletrônico: a VQA pode aprimorar a experiência do usuário, permitindo que os clientes façam perguntas sobre os produtos.
* Recuperação de imagens: modelos de VQA podem ser usados para recuperar imagens com características específicas. Por exemplo, o usuário pode perguntar "Há um cachorro?" para encontrar todas as imagens com cachorros a partir de um conjunto de imagens.

Neste guia, você aprenderá como:

- Ajuste fino de um modelo de VQA de classificação, especificamente [ViLT](../model_doc/vilt), no [conjunto de dados `Graphcore/vqa`](https://huggingface.co/datasets/Graphcore/vqa).
- Use seu ViLT ajustado para inferência.
- Execute inferência de VQA zero-shot com um modelo generativo, como o BLIP-2.

## Fine-tuning do ViLT

O modelo ViLT incorpora embeddings de texto em um Vision Transformer (ViT), permitindo um design minimalista para o pré-treinamento de visão e linguagem (VLP). Este modelo pode ser usado para várias tarefas de nível inferior. Para a tarefa de VQA, uma cabeça de classificador é colocada no topo (uma camada linear no topo do estado oculto final do token `[CLS]`) e inicializada aleatoriamente. O Visual Question Answering é, portanto, tratado como um **problema de classificação**.

Modelos mais recentes, como BLIP, BLIP-2 e InstructBLIP, tratam a VQA como uma tarefa generativa. Mais adiante neste guia, ilustramos como usá-los para inferência de VQA zero-shot.

Antes de começar, verifique se todas as bibliotecas necessárias estão instaladas.

```bash
pip install -q transformers datasets
```

Incentivamos você a compartilhar seu modelo com a comunidade. Faça login na sua conta do Hugging Face para enviá-lo ao 🤗 Hub. Quando solicitado, insira seu token para fazer login:

```py
>>> from huggingface_hub import notebook_login

>>> notebook_login()
```

Vamos definir o checkpoint do modelo como uma variável global.

```py
>>> model_checkpoint = "dandelin/vilt-b32-mlm"
```

## Carregar os dados

Para fins de ilustração, neste guia usamos uma amostra muito pequena do conjunto de dados de resposta a perguntas visuais anotadas `Graphcore/vqa`. Você pode encontrar o conjunto de dados completo no [🤗 Hub](https://huggingface.co/datasets/Graphcore/vqa).

Como alternativa ao conjunto de dados [`Graphcore/vqa`](https://huggingface.co/datasets/Graphcore/vqa), você pode baixar os mesmos dados manualmente da página oficial do [conjunto de dados VQA](https://visualqa.org/download.html). Se você preferir seguir o tutorial com seus próprios dados, confira o guia [Criar um conjunto de dados de imagens](https://huggingface.co/docs/datasets/image_dataset#loading-script) na documentação do 🤗 Datasets.

Vamos carregar os primeiros 200 exemplos da divisão de validação e explorar os recursos do conjunto de dados:

```python
>>> 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
})
```

Vamos dar uma olhada em um exemplo para entender os recursos do conjunto de dados:

```py
>>> 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]}}
```

Os recursos relevantes para a tarefa incluem:

* `question`: a pergunta a ser respondida a partir da imagem
* `image_id`: o caminho da imagem à qual a pergunta se refere
* `label`: as anotações

Podemos remover o restante dos recursos, pois eles não serão necessários:

```py
>>> dataset = dataset.remove_columns(['question_type', 'question_id', 'answer_type'])
```

Como você pode ver, o recurso `label` contém várias respostas para a mesma pergunta (chamadas de `ids` aqui), coletadas por diferentes anotadores humanos. Isso ocorre porque a resposta a uma pergunta pode ser subjetiva. Neste caso, a pergunta é "para onde ele está olhando?". Algumas pessoas anotaram isso como "para baixo", outras como "na mesa", outra como "skateboard", etc.

Dê uma olhada na imagem e considere qual resposta você daria:

```python
>>> from PIL import Image

>>> image = Image.open(dataset[0]['image_id'])
>>> image
```

     

Devido à ambiguidade das perguntas e das respostas, conjuntos de dados como esse são tratados como um problema de classificação multirótulo (já que múltiplas respostas podem ser válidas). Além disso, em vez de simplesmente criar um vetor codificado em one-hot, cria-se uma codificação suave, baseada no número de vezes que uma determinada resposta apareceu nas anotações.

Por exemplo, no exemplo acima, como a resposta "down" é selecionada com muito mais frequência do que as outras respostas, ela tem uma pontuação (chamada de `weight` no conjunto de dados) de 1.0, e as demais respostas têm pontuações < 1.0.

Para instanciar o modelo posteriormente com uma cabeça de classificação adequada, vamos criar dois dicionários: um que mapeia o nome do rótulo para um inteiro e vice-versa:

```py
>>> 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()}
```

Agora que temos os mapeamentos, podemos substituir as respostas em texto pelos seus ids e achatar o conjunto de dados para um pré-processamento posterior mais conveniente.

```python
>>> 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)}
```

## Pré-processamento de dados

O próximo passo é carregar um processador ViLT para preparar os dados de imagem e texto para o modelo. [ViltProcessor](/docs/transformers/main/pt/model_doc/vilt#transformers.ViltProcessor) encapsula um tokenizador BERT e um processador de imagem ViLT em um único processador conveniente:

```py
>>> from transformers import ViltProcessor

>>> processor = ViltProcessor.from_pretrained(model_checkpoint)
```

Para pré-processar os dados, precisamos codificar as imagens e as perguntas usando o [ViltProcessor](/docs/transformers/main/pt/model_doc/vilt#transformers.ViltProcessor). O processador usará o [BertTokenizerFast](/docs/transformers/main/pt/model_doc/lxmert#transformers.BertTokenizer) para tokenizar o texto e criar `input_ids`, `attention_mask` e `token_type_ids` para os dados de texto. No que diz respeito às imagens, o processador aproveitará o [ViltImageProcessor](/docs/transformers/main/pt/model_doc/vilt#transformers.ViltImageProcessor) para redimensionar e normalizar a imagem, e criar `pixel_values` e `pixel_mask`.

Todos esses passos de pré-processamento são feitos por baixo dos panos; precisamos apenas chamar o `processor`. No entanto, ainda precisamos preparar os rótulos-alvo. Nessa representação, cada elemento corresponde a uma resposta possível (rótulo). Para as respostas corretas, o elemento contém sua respectiva pontuação (peso), enquanto os elementos restantes são definidos como zero.

A função a seguir aplica o `processor` às imagens e às perguntas e formata os rótulos conforme descrito acima:

```py
>>> 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
```

Para aplicar a função de pré-processamento em todo o conjunto de dados, use a função `map` do 🤗 Datasets. Você pode acelerar o `map` definindo `batched=True` para processar vários elementos do conjunto de dados de uma vez. Neste ponto, sinta-se à vontade para remover as colunas de que você não precisa.

```py
>>> 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
})
```

Como etapa final, crie um lote de exemplos usando [DefaultDataCollator](/docs/transformers/main/pt/main_classes/data_collator#transformers.DefaultDataCollator):

```py
>>> from transformers import DefaultDataCollator

>>> data_collator = DefaultDataCollator()
```

## Treinar o modelo

Agora você está pronto para começar a treinar seu modelo! Carregue o ViLT com [ViltForQuestionAnswering](/docs/transformers/main/pt/model_doc/vilt#transformers.ViltForQuestionAnswering). Especifique o número de rótulos junto com os mapeamentos de rótulos:

```py
>>> from transformers import ViltForQuestionAnswering

>>> model = ViltForQuestionAnswering.from_pretrained(model_checkpoint, num_labels=len(id2label), id2label=id2label, label2id=label2id)
```

Neste ponto, restam apenas três etapas:

1. Defina os hiperparâmetros de treinamento em [TrainingArguments](/docs/transformers/main/pt/main_classes/trainer#transformers.TrainingArguments):

```py
>>> 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,
... )
```

2. Passe os argumentos de treinamento para [Trainer](/docs/transformers/main/pt/main_classes/trainer#transformers.Trainer) junto com o modelo, o conjunto de dados, o processador e o data collator.

```py
>>> from transformers import Trainer

>>> trainer = Trainer(
...     model=model,
...     args=training_args,
...     data_collator=data_collator,
...     train_dataset=processed_dataset,
...     processing_class=processor,
... )
```

3. Chame [train()](/docs/transformers/main/pt/main_classes/trainer#transformers.Trainer.train) para fazer o ajuste fino do seu modelo.

```py
>>> trainer.train()
```

Quando o treinamento for concluído, compartilhe seu modelo no Hub com o método [push_to_hub()](/docs/transformers/main/pt/main_classes/trainer#transformers.Trainer.push_to_hub) para compartilhar seu modelo final no 🤗 Hub:

```py
>>> trainer.push_to_hub()
```

## Inferência

Agora que você ajustou um modelo ViLT e o enviou para o 🤗 Hub, você pode usá-lo para inferência.

1. Pegue uma imagem e uma pergunta e prepare-as para o modelo usando o processor do seu modelo.
2. Encaminhe o resultado ou o pré-processamento pelo modelo.
3. A partir dos logits, obtenha o id da resposta mais provável e encontre a resposta real no `id2label`.

```py
>>> 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 zero-shot

O modelo anterior tratava a VQA como uma tarefa de classificação. Alguns modelos recentes, como BLIP, BLIP-2 e InstructBLIP, abordam a VQA como uma tarefa generativa. Vamos tomar o [BLIP-2](../model_doc/blip-2) como exemplo. Ele introduziu um novo paradigma de pré-treinamento visual-linguístico no qual qualquer combinação de codificador visual pré-treinado e LLM pode ser utilizada (saiba mais no [post no blog do BLIP-2](https://huggingface.co/blog/blip-2)). Isso permite alcançar resultados de estado da arte em várias tarefas visuais-linguísticas, incluindo a resposta a perguntas visuais.

Vamos ilustrar como você pode usar este modelo para VQA. Primeiro, vamos carregar o modelo. Aqui, enviaremos explicitamente o modelo para uma GPU, se disponível, o que não precisávamos fazer anteriormente durante o treinamento, pois o [Trainer](/docs/transformers/main/pt/main_classes/trainer#transformers.Trainer) trata disso automaticamente:

```py
>>> from transformers import AutoProcessor, Blip2ForConditionalGeneration
from accelerate import Accelerator
>>> import torch

>>> processor = AutoProcessor.from_pretrained("Salesforce/blip2-opt-2.7b")
>>> model = Blip2ForConditionalGeneration.from_pretrained("Salesforce/blip2-opt-2.7b", dtype=torch.float16)
>>> device = Accelerator().device
>>> model.to(device)
```

O modelo recebe imagem e texto como entrada, então vamos usar exatamente o mesmo par de imagem/pergunta do primeiro exemplo no conjunto de dados VQA:

```py
>>> example = dataset[0]
>>> image = Image.open(example['image_id'])
>>> question = example['question']
```

Para usar o BLIP-2 na tarefa de resposta a perguntas visuais, o prompt textual deve seguir um formato específico: `Question: {} Answer:`.

```py
>>> prompt = f"Question: {question} Answer:"
```

Agora, precisamos pré-processar a imagem/prompt com o processador do modelo, passar a entrada processada pelo modelo e decodificar a saída:

```py
>>> 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"
```

Como você pode ver, o modelo reconheceu a multidão e a direção do rosto (olhando para baixo), no entanto, parece não perceber que a multidão está atrás do skater. Ainda assim, em casos em que a aquisição de datasets anotados por humanos não é viável, essa abordagem pode produzir resultados úteis rapidamente.

