Transformers documentation
Menjawab pertanyaan visual
Menjawab pertanyaan visual
Visual Question Answering (VQA) adalah tugas menjawab pertanyaan terbuka berdasarkan sebuah gambar. Input untuk model yang mendukung tugas ini biasanya berupa kombinasi gambar dan pertanyaan, dan outputnya adalah jawaban yang dinyatakan dalam bahasa alami.
Beberapa contoh kasus penggunaan VQA yang patut diperhatikan meliputi:
- Aplikasi aksesibilitas untuk individu tunanetra.
- Pendidikan: mengajukan pertanyaan tentang materi visual yang disajikan dalam kuliah atau buku teks. VQA juga dapat dimanfaatkan dalam pameran museum interaktif atau situs bersejarah.
- Layanan pelanggan dan e-commerce: VQA dapat meningkatkan pengalaman pengguna dengan memungkinkan pengguna mengajukan pertanyaan tentang produk.
- Pencarian gambar: Model VQA dapat digunakan untuk mengambil gambar dengan karakteristik tertentu. Misalnya, pengguna dapat bertanya “Apakah ada anjing?” untuk menemukan semua gambar yang berisi anjing dari sekumpulan gambar.
Dalam panduan ini, Anda akan belajar cara:
- Lakukan fine-tuning pada model klasifikasi VQA, khususnya ViLT, menggunakan dataset
Graphcore/vqa. - Gunakan ViLT yang telah Anda fine-tune untuk inferensi.
- Jalankan inferensi VQA zero-shot dengan model generatif, seperti BLIP-2.
Fine-tuning ViLT
Model ViLT mengintegrasikan embedding teks ke dalam Vision Transformer (ViT), sehingga memungkinkan desain yang minimal untuk Vision-and-Language Pre-training (VLP). Model ini dapat digunakan untuk beberapa tugas turunan. Untuk tugas VQA, sebuah kepala klasifikasi ditempatkan di atasnya (lapisan linear di atas keadaan tersembunyi akhir dari token [CLS]) dan diinisialisasi secara acak. Dengan demikian, Visual Question Answering diperlakukan sebagai masalah klasifikasi.
Model yang lebih baru, seperti BLIP, BLIP-2, dan InstructBLIP, memperlakukan VQA sebagai tugas generatif. Di bagian selanjutnya dari panduan ini, kami akan menunjukkan cara menggunakannya untuk inferensi VQA zero-shot.
Sebelum memulai, pastikan Anda telah menginstal semua pustaka yang diperlukan.
pip install -q transformers datasets
Kami mendorong Anda untuk membagikan model Anda kepada komunitas. Masuk ke akun Hugging Face Anda untuk mengunggahnya ke 🤗 Hub. Saat diminta, masukkan token Anda untuk masuk:
>>> from huggingface_hub import notebook_login
>>> notebook_login()Mari kita definisikan checkpoint model sebagai variabel global.
>>> model_checkpoint = "dandelin/vilt-b32-mlm"Muat data
Sebagai ilustrasi, dalam panduan ini kami menggunakan sampel kecil dari dataset visual question answering berlabel Graphcore/vqa.
Anda dapat menemukan dataset lengkap di 🤗 Hub.
Sebagai alternatif dari dataset Graphcore/vqa, Anda dapat mengunduh data yang sama secara manual dari halaman dataset VQA resmi. Jika Anda lebih memilih mengikuti tutorial dengan data kustom Anda, lihat panduan Membuat dataset gambar di dokumentasi 🤗 Datasets.
Mari kita muat 200 contoh pertama dari split validasi dan jelajahi fitur dataset:
>>> 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
})Mari kita lihat sebuah contoh untuk memahami fitur dataset:
>>> 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]}}Fitur yang relevan untuk tugas tersebut meliputi:
question: pertanyaan yang akan dijawab dari gambarimage_id: path ke gambar yang dimaksud oleh pertanyaanlabel: anotasi
Kita dapat menghapus fitur-fitur lainnya karena tidak akan diperlukan:
>>> dataset = dataset.remove_columns(['question_type', 'question_id', 'answer_type'])Seperti yang dapat Anda lihat, fitur label berisi beberapa jawaban untuk pertanyaan yang sama (disebut ids di sini) yang dikumpulkan oleh berbagai anotator manusia.
Hal ini terjadi karena jawaban untuk sebuah pertanyaan dapat bersifat subjektif. Dalam kasus ini, pertanyaannya adalah “where is he looking?”. Beberapa orang
mengannotasi ini dengan “down”, yang lain dengan “at table”, dan satu lagi dengan “skateboard”, dan seterusnya.
Lihatlah gambar tersebut dan pertimbangkan jawaban mana yang akan Anda berikan:
>>> from PIL import Image
>>> image = Image.open(dataset[0]['image_id'])
>>> image
Karena adanya ambiguitas pada pertanyaan dan jawaban, dataset seperti ini diperlakukan sebagai masalah klasifikasi multi-label (karena beberapa jawaban mungkin valid). Selain itu, alih-alih hanya membuat vektor one-hot encoded, dibuatlah soft encoding berdasarkan jumlah kemunculan jawaban tertentu dalam anotasi.
Sebagai contoh, pada contoh di atas, karena jawaban “down” dipilih jauh lebih sering daripada jawaban lainnya, jawaban tersebut memiliki skor (disebut weight dalam dataset) sebesar 1,0, sedangkan jawaban lainnya memiliki skor < 1,0.
Untuk kemudian menginstansiasi model dengan kepala klasifikasi yang sesuai, buatlah dua kamus: satu yang memetakan nama label ke bilangan bulat dan sebaliknya:
>>> 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()}Sekarang bahwa kita sudah memiliki pemetaan, kita dapat mengganti jawaban string dengan id-nya, dan meratakan dataset untuk pra-pemrosesan lebih lanjut yang lebih nyaman.
>>> 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)}Prapemrosesan data
Langkah selanjutnya adalah memuat pemroses ViLT untuk menyiapkan data gambar dan teks bagi model. ViltProcessor membungkus tokenizer BERT dan pemroses gambar ViLT menjadi satu pemroses yang praktis:
>>> from transformers import ViltProcessor
>>> processor = ViltProcessor.from_pretrained(model_checkpoint)Untuk memproses data, kita perlu mengodekan gambar dan pertanyaan menggunakan ViltProcessor. Prosesor ini akan menggunakan
BertTokenizerFast untuk melakukan tokenisasi teks dan membuat input_ids, attention_mask, serta token_type_ids untuk data teks.
Adapun untuk gambar, prosesor akan memanfaatkan ViltImageProcessor untuk mengubah ukuran dan menormalisasi gambar, serta membuat pixel_values dan pixel_mask.
Semua langkah pra-pemrosesan ini dilakukan di balik layar, kita hanya perlu memanggil processor. Namun, kita masih perlu menyiapkan label target. Dalam representasi ini, setiap elemen sesuai dengan satu kemungkinan jawaban (label). Untuk jawaban yang benar, elemen tersebut menyimpan skor (bobot) masing-masing, sementara elemen-elemen lainnya diatur ke nol.
Fungsi berikut menerapkan processor pada gambar dan pertanyaan, lalu memformat label seperti yang dijelaskan di atas:
>>> 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 encodingUntuk menerapkan fungsi pra-pemrosesan ke seluruh dataset, gunakan fungsi map dari 🤗 Datasets. Anda dapat mempercepat map dengan
mengatur batched=True untuk memproses beberapa elemen dataset sekaligus. Pada tahap ini, silakan hapus kolom yang tidak Anda butuhkan.
>>> 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
})Sebagai langkah terakhir, buat satu batch contoh menggunakan DefaultDataCollator:
>>> from transformers import DefaultDataCollator
>>> data_collator = DefaultDataCollator()Latih model
Anda siap untuk mulai melatih model Anda sekarang! Muat ViLT dengan ViltForQuestionAnswering. Tentukan jumlah label beserta pemetaan labelnya:
>>> from transformers import ViltForQuestionAnswering
>>> model = ViltForQuestionAnswering.from_pretrained(model_checkpoint, num_labels=len(id2label), id2label=id2label, label2id=label2id)Pada titik ini, hanya tersisa tiga langkah:
- Definisikan hiperparameter pelatihan Anda di 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,
... )- Lewatkan argumen pelatihan ke Trainer bersama dengan model, dataset, processor, dan data collator.
>>> from transformers import Trainer
>>> trainer = Trainer(
... model=model,
... args=training_args,
... data_collator=data_collator,
... train_dataset=processed_dataset,
... processing_class=processor,
... )- Panggil train() untuk melakukan finetuning pada model Anda.
>>> trainer.train()Setelah pelatihan selesai, bagikan model Anda ke Hub dengan metode push_to_hub() untuk membagikan model akhir Anda di 🤗 Hub:
>>> trainer.push_to_hub()Inferensi
Sekarang setelah Anda melakukan fine-tuning pada model ViLT dan mengunggahnya ke 🤗 Hub, Anda dapat menggunakannya untuk inferensi.
- Ambil sebuah gambar dan pertanyaan, lalu siapkan keduanya untuk model menggunakan processor dari model Anda.
- Lewatkan hasil atau prapemrosesan melalui model.
- Dari logits, dapatkan id jawaban yang paling mungkin, lalu temukan jawaban sebenarnya di
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: downVQA Zero-shot
Model sebelumnya memperlakukan VQA sebagai tugas klasifikasi. Beberapa model terbaru, seperti BLIP, BLIP-2, dan InstructBLIP, mendekati VQA sebagai tugas generatif. Mari kita ambil BLIP-2 sebagai contoh. Model ini memperkenalkan paradigma pra-pelatihan visual-bahasa baru di mana kombinasi apa pun dari encoder visual yang telah dilatih sebelumnya dan LLM dapat digunakan (pelajari lebih lanjut di BLIP-2 blog post). Hal ini memungkinkan pencapaian hasil state-of-the-art pada berbagai tugas visual-bahasa, termasuk visual question answering.
Mari kita ilustrasikan cara menggunakan model ini untuk VQA. Pertama, mari kita muat modelnya. Di sini, kita secara eksplisit mengirim model ke GPU, jika tersedia, yang tidak perlu kita lakukan sebelumnya saat pelatihan, karena Trainer menangani hal ini secara otomatis:
>>> 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)Model ini menerima gambar dan teks sebagai input, jadi mari kita gunakan pasangan gambar/soal yang persis sama dari contoh pertama dalam dataset VQA:
>>> example = dataset[0]
>>> image = Image.open(example['image_id'])
>>> question = example['question']Untuk menggunakan BLIP-2 untuk tugas visual question answering, prompt teks harus mengikuti format tertentu: Question: {} Answer:.
>>> prompt = f"Question: {question} Answer:"Sekarang kita perlu memproses praproses gambar/prompt dengan prosesor model, melewatkan input yang sudah diproses melalui model, dan mendekode output:
>>> 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"Seperti yang dapat Anda lihat, model mengenali kerumunan dan arah wajah (menunduk), namun tampaknya melewatkan fakta bahwa kerumunan berada di belakang peseluncur. Meskipun demikian, dalam kasus di mana memperoleh dataset anotasi manusia tidak memungkinkan, pendekatan ini dapat dengan cepat menghasilkan hasil yang berguna.
Update on GitHub