Transformers documentation
الإجابة على الأسئلة البصرية
الإجابة على الأسئلة البصرية
إجابة الأسئلة البصرية (VQA) هي مهمة الإجابة عن أسئلة مفتوحة بناءً على صورة. عادةً، يكون مدخل النماذج الداعمة لهذه المهمة عبارة عن مزيج من صورة وسؤال، ويكون المخرج إجابة معبَّرًا عنها باللغة الطبيعية.
تشمل بعض أمثلة حالات الاستخدام الجديرة بالانتباه لـ VQA ما يلي:
- تطبيقات إمكانية الوصول للأشخاص ذوي الإعاقة البصرية.
- التعليم: طرح أسئلة حول المواد البصرية المعروضة في المحاضرات أو الكتب المدرسية. كما يمكن استخدام VQA في المعارض التفاعلية للمتاحف أو المواقع التاريخية.
- خدمة العملاء والتجارة الإلكترونية: يمكن لنموذج VQA تحسين تجربة المستخدم من خلال تمكينهم من طرح أسئلة حول المنتجات.
- استرجاع الصور: يمكن استخدام نماذج VQA لاسترجاع الصور ذات الخصائص المحددة. على سبيل المثال، يمكن للمستخدم أن يسأل "هل يوجد كلب؟" للعثور على جميع الصور التي تحتوي على كلاب من مجموعة من الصور.
في هذا الدليل ستتعلم كيفية:
- قم بضبط نموذج تصنيف VQA، وتحديداً ViLT، على مجموعة البيانات
Graphcore/vqa. - استخدم نموذج ViLT المُحسَّن لديك للاستدلال.
- شغّل استنتاج VQA بدون تدريب مسبق باستخدام نموذج توليدي، مثل BLIP-2.
ضبط ViLT
يتضمن نموذج ViLT تضمينات النص في محوّل الرؤية (ViT)، مما يتيح له تصميمًا بسيطًا للتدريب المسبق على الرؤية واللغة (VLP). يمكن استخدام هذا النموذج لعدة مهام لاحقة. بالنسبة لمهمة VQA، يُوضع رأس مصنّف في الأعلى (طبقة خطية فوق الحالة المخفية النهائية لرمز [CLS]) ويُهيأ عشوائيًا. وبالتالي، يُعامل الإجابة على الأسئلة البصرية على أنه مشكلة تصنيف.
تعامل النماذج الأحدث، مثل BLIP وBLIP-2 وInstructBLIP، مع VQA كمهمة توليدية. في وقت لاحق من هذا الدليل، سنوضح كيفية استخدامها للاستدلال في VQA بدون تدريب مسبق (zero-shot).
قبل أن تبدأ، تأكد من تثبيت جميع المكتبات اللازمة.
pip install -q transformers datasets
ننصحك بمشاركة نموذجك مع المجتمع. سجّل الدخول إلى حسابك على Hugging Face لرفعه إلى 🤗 Hub. عند الطلب، أدخل رمزك (token) لتسجيل الدخول:
>>> from huggingface_hub import notebook_login
>>> notebook_login()لنعرّف نقطة التفتيش للنموذج كمتغير عام.
>>> model_checkpoint = "dandelin/vilt-b32-mlm"تحميل البيانات
لأغراض التوضيح، نستخدم في هذا الدليل عينة صغيرة جداً من مجموعة بيانات Graphcore/vqa الخاصة بالإجابة على الأسئلة البصرية المُعلَّمة.
يمكنك العثور على المجموعة الكاملة على 🤗 Hub.
كبديل لمجموعة بيانات Graphcore/vqa، يمكنك تنزيل البيانات نفسها يدوياً من صفحة مجموعة بيانات VQA الرسمية. إذا كنت تفضل اتباع الشرح التعليمي باستخدام بياناتك المخصصة، فاطّلع على دليل إنشاء مجموعة بيانات للصور في توثيق 🤗 Datasets.
لنقم بتحميل أول 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
نظرًا لغموض الأسئلة والإجابات، تُعامل مجموعات البيانات من هذا النوع كمسألة تصنيف متعددة التسميات (إذ قد تكون إجابات متعددة صحيحة). علاوة على ذلك، بدلًا من إنشاء متجه مشفّر أحادي (one-hot encoded) فحسب، يُنشأ تشفير ناعم (soft encoding) يستند إلى عدد المرات التي ظهرت فيها إجابة معينة في التسميات.
على سبيل المثال، في المثال أعلاه، نظرًا لأن الإجابة «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()}الآن، بما أننا حصلنا على خرائط التحويل، يمكننا استبدال الإجابات النصية بمعرّفاتها، وتسطيح مجموعة البيانات لتسهيل المعالجة المسبقة اللاحقة.
>>> 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لتطبيق دالة المعالجة المسبقة على مجموعة البيانات بأكملها، استخدم دالة map في 🤗 Datasets. يمكنك تسريع map عن طريق تعيين batched=True لمعالجة عدة عناصر من مجموعة البيانات في وقت واحد. في هذه المرحلة، يمكنك إزالة الأعمدة التي لا تحتاجها بحرية.
>>> 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()تدريب النموذج
أنت مستعد الآن لبدء تدريب نموذجك! حمّل ViLT باستخدام ViltForQuestionAnswering. حدّد عدد التسميات معًا مع خرائط التسميات:
>>> 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,
... processing_class=processor,
... )- استدعِ train() لضبط نموذجك.
>>> trainer.train()بمجرد اكتمال التدريب، شارك نموذجك على الـ Hub باستخدام طريقة push_to_hub() لمشاركة نموذجك النهائي على 🤗 Hub:
>>> trainer.push_to_hub()الاستدلال
الآن بعد أن قمت بضبط نموذج ViLT بدقة، ورفعته إلى 🤗 Hub، يمكنك استخدامه للاستدلال.
- خذ صورةً وسؤالاً، ثم جهّزهما للنموذج باستخدام المعالج الخاص بنموذجك.
- مرّر النتيجة أو المعالجة المسبقة عبر النموذج.
- From the logits, get the most likely answer’s id, and find the actual answer in the
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 بدون تدريب مسبق
عامل النموذج السابق مسألة الإجابة عن الأسئلة البصرية كمهمة تصنيف. تَعالج بعض النماذج الحديثة، مثل BLIP وBLIP-2 وInstructBLIP، مسألة الإجابة عن الأسئلة البصرية كمهمة توليدية. لنأخذ BLIP-2 مثالاً. فقد قدّم نموذجاً جديداً للتدريب المسبق على الرؤية واللغة يمكن فيه استخدام أي تركيبة من مُشفِّر الرؤية المُدرَّب مسبقاً ونموذج اللغة الكبير (تعرّف المزيد في منشور BLIP-2 على المدونة). ويتيح هذا تحقيق نتائج رائدة في مهام متعددة تجمع بين الرؤية واللغة، بما في ذلك الإجابة عن الأسئلة البصرية.
لنوضّح كيف يمكنك استخدام هذا النموذج لمهمة VQA. أولاً، لنقم بتحميل النموذج. هنا سنرسل النموذج صراحةً إلى وحدة معالجة رسومية (GPU) إن توفرت، وهو ما لم نكن بحاجة إلى القيام به سابقاً أثناء التدريب، لأن Trainer يتولى ذلك تلقائياً:
>>> 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)يستقبل النموذج صورةً ونصًا كمدخلات، لذا دعونا نستخدم نفس زوج الصورة/السؤال من المثال الأول في مجموعة بيانات VQA:
>>> example = dataset[0]
>>> image = Image.open(example['image_id'])
>>> question = example['question']لاستخدام BLIP-2 في مهمة الإجابة على الأسئلة البصرية، يجب أن يتبع الموجه النصي تنسيقًا محددًا: Question: {} Answer:.
>>> 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"كما ترون، تمكّن النموذج من التعرف على الحشد واتجاه الوجه (النظر إلى الأسفل)، ومع ذلك يبدو أنه أغفل حقيقة أن الحشد خلف المتزلج. ومع ذلك، في الحالات التي لا يكون فيها الحصول على مجموعات بيانات مُعلَّمة يدويًا ممكنًا، يمكن لهذا النهج إنتاج نتائج مفيدة بسرعة.
Update on GitHub