# الإجابة على الأسئلة البصرية

إجابة الأسئلة البصرية (VQA) هي مهمة الإجابة عن أسئلة مفتوحة بناءً على صورة. يكون مدخل النماذج الداعمة لهذه المهمة عادةً مزيجًا من صورة وسؤال، ويكون المخرج إجابة معبَّرًا عنها باللغة الطبيعية.

تشمل بعض أمثلة حالات الاستخدام الجديرة بالانتباه لـ VQA ما يلي:

* تطبيقات إمكانية الوصول للأشخاص ذوي الإعاقة البصرية.
* التعليم: طرح أسئلة حول المواد البصرية المعروضة في المحاضرات أو الكتب المدرسية. كما يمكن استخدام VQA في المعارض التفاعلية للمتاحف أو المواقع التاريخية.
* خدمة العملاء والتجارة الإلكترونية: يمكن لنموذج VQA تحسين تجربة المستخدم من خلال تمكينهم من طرح أسئلة حول المنتجات.
* استرجاع الصور: يمكن استخدام نماذج VQA لاسترجاع الصور ذات الخصائص المحددة. على سبيل المثال، يمكن للمستخدم أن يسأل "هل يوجد كلب؟" للعثور على جميع الصور التي تحتوي على كلاب من مجموعة من الصور.

في هذا الدليل ستتعلم كيفية:

- قم بضبط نموذج تصنيف VQA، وتحديداً [ViLT](../model_doc/vilt)، على مجموعة البيانات [`Graphcore/vqa`](https://huggingface.co/datasets/Graphcore/vqa).
- استخدم نموذج ViLT المُحسَّن لديك للاستدلال.
- شغّل استنتاج VQA بدون تدريب مسبق باستخدام نموذج توليدي، مثل BLIP-2.

## ضبط ViLT

يدمج نموذج ViLT تمثيلات النص في محوّل الرؤية (ViT)، مما يتيح له تصميمًا بسيطًا للتدريب المسبق على الرؤية واللغة (VLP). يمكن استخدام هذا النموذج لعدة مهام لاحقة. بالنسبة لمهمة VQA، يُوضع رأس مُصنِّف في الأعلى (طبقة خطية فوق الحالة المخفية النهائية لرمز `[CLS]`) ويُهيّأ عشوائيًا. وبالتالي، يُعامل الإجابة على الأسئلة البصرية على أنه **مشكلة تصنيف**.

تعامل النماذج الأحدث، مثل BLIP وBLIP-2 وInstructBLIP، مع VQA كمهمة توليدية. في وقت لاحق من هذا الدليل، سنوضح كيفية استخدامها للاستدلال في VQA بدون تدريب مسبق (zero-shot).

قبل أن تبدأ، تأكد من تثبيت جميع المكتبات اللازمة.

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

ننصحك بمشاركة نموذجك مع المجتمع. سجّل الدخول إلى حسابك على Hugging Face لرفعه إلى 🤗 Hub. عند الطلب، أدخل رمزك (token) لتسجيل الدخول:

```py
>>> from huggingface_hub import notebook_login

>>> notebook_login()
```

لنعرّف نقطة التفتيش للنموذج كمتغير عام.

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

## تحميل البيانات

لأغراض التوضيح، نستخدم في هذا الدليل عينة صغيرة جداً من مجموعة بيانات `Graphcore/vqa` الخاصة بالإجابة على الأسئلة البصرية المُعلَّمة. يمكنك العثور على المجموعة الكاملة على [🤗 Hub](https://huggingface.co/datasets/Graphcore/vqa).

كبديل عن مجموعة بيانات [`Graphcore/vqa`](https://huggingface.co/datasets/Graphcore/vqa)، يمكنك تنزيل البيانات نفسها يدوياً من [صفحة مجموعة بيانات VQA الرسمية](https://visualqa.org/download.html). إذا كنت تفضل اتباع الشرح التعليمي باستخدام بياناتك المخصصة، فاطّلع على دليل [إنشاء مجموعة بيانات للصور](https://huggingface.co/docs/datasets/image_dataset#loading-script) في توثيق 🤗 Datasets.

لنقم بتحميل أول 200 مثال من تقسيم التحقق واستكشاف خصائص مجموعة البيانات:

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

لنلقِ نظرة على مثال لفهم خصائص مجموعة البيانات:

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

تشمل الميزات ذات الصلة بالمهمة ما يلي:

* `question`: السؤال الذي يجب الإجابة عنه من الصورة
* `image_id`: المسار إلى الصورة التي يشير إليها السؤال
* `label`: التسميات

يمكننا إزالة الميزات المتبقية لأنها لن تكون ضرورية:

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

كما ترون، يحتوي الميزة `label` على عدة إجابات على السؤال نفسه (المُشار إليها هنا باسم `ids`) جمعها مُعلِّقون بشريون مختلفون. والسبب في ذلك أن الإجابة على سؤال ما قد تكون ذاتية. في هذه الحالة، السؤال هو "إلى أين ينظر؟". فقد علّق بعض الأشخاص عليها بـ "أسفل"، وآخرون بـ "على الطاولة"، وثالث بـ "اللوحة التزلجية"، وهكذا.

انظر إلى الصورة وفكّر في الإجابة التي ستختارها:

```python
>>> from PIL import Image

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

     

بسبب غموض الأسئلة والإجابات، تُعامل مجموعات البيانات من هذا النوع كمسألة تصنيف متعددة التسميات (لأنه من الممكن أن تكون عدة إجابات صحيحة). علاوة على ذلك، بدلاً من إنشاء متجه مشفّر أحادي (one-hot encoded) فحسب، يتم إنشاء تشفير ناعم (soft encoding) يعتمد على عدد مرات ظهور إجابة معينة في التسميات.

على سبيل المثال، في المثال أعلاه، نظرًا لأن الإجابة "down" يتم اختيارها بشكل متكرر أكثر بكثير من الإجابات الأخرى، فإن لها درجة (تُسمى `weight` في مجموعة البيانات) تساوي 1.0، بينما الإجابات الأخرى لها درجات < 1.0.

لإنشاء نموذج برأس تصنيف مناسب لاحقًا، لننشئ قاموسين: أحدهما يربط اسم التسمية برقم صحيح والعكس:

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

الآن، بما أننا حصلنا على خرائط التحويل، يمكننا استبدال الإجابات النصية بمعرّفاتها، وتسطيح مجموعة البيانات لتسهيل المعالجة المسبقة اللاحقة.

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

## معالجة البيانات مسبقًا

الخطوة التالية هي تحميل معالج ViLT لتحضير بيانات الصورة والنص للنموذج. [ViltProcessor](/docs/transformers/main/ar/model_doc/vilt#transformers.ViltProcessor) يجمع بين مُجزِّئ BERT ومعالج صور ViLT في معالج واحد ميسّر:

```py
>>> from transformers import ViltProcessor

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

لمعالجة البيانات مسبقاً، نحتاج إلى ترميز الصور والأسئلة باستخدام [ViltProcessor](/docs/transformers/main/ar/model_doc/vilt#transformers.ViltProcessor). سيقوم المعالج باستخدام [BertTokenizerFast](/docs/transformers/main/ar/model_doc/lxmert#transformers.BertTokenizer) لتجزئة النص وإنشاء `input_ids` و`attention_mask` و`token_type_ids` لبيانات النص. أما بالنسبة للصور، فسيستفيد المعالج من [ViltImageProcessor](/docs/transformers/main/ar/model_doc/vilt#transformers.ViltImageProcessor) لتغيير حجم الصورة وتطبيعها، وإنشاء `pixel_values` و`pixel_mask`.

تُنفَّذ جميع خطوات المعالجة المسبقة هذه في الخلفية، وكل ما علينا فعله هو استدعاء `processor`. ومع ذلك، لا يزال علينا تجهيز التسميات المستهدفة. في هذا التمثيل، يتوافق كل عنصر مع إجابة محتملة (تسمية). وفي حالة الإجابات الصحيحة، يحتفظ العنصر بدرجة (وزن) كل منها، بينما تُضبط العناصر المتبقية على الصفر.

تطبّق الدالة التالية `processor` على الصور والأسئلة وتنسّق التسميات كما هو موضح أعلاه:

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

لتطبيق دالة المعالجة المسبقة على مجموعة البيانات بأكملها، استخدم دالة `map` في 🤗 Datasets. يمكنك تسريع `map` عن طريق تعيين `batched=True` لمعالجة عدة عناصر من مجموعة البيانات في وقت واحد. في هذه المرحلة، يمكنك إزالة الأعمدة التي لا تحتاجها.

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

كخطوة أخيرة، أنشئ دفعة من الأمثلة باستخدام [DefaultDataCollator](/docs/transformers/main/ar/main_classes/data_collator#transformers.DefaultDataCollator):

```py
>>> from transformers import DefaultDataCollator

>>> data_collator = DefaultDataCollator()
```

## تدريب النموذج

أنت مستعد الآن لبدء تدريب نموذجك! حمّل ViLT باستخدام [ViltForQuestionAnswering](/docs/transformers/main/ar/model_doc/vilt#transformers.ViltForQuestionAnswering). حدّد عدد التسميات مع خرائط التسميات:

```py
>>> from transformers import ViltForQuestionAnswering

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

في هذه المرحلة، تبقى ثلاث خطوات فقط:

1. حدّد معاملات التدريب الفائقة في [TrainingArguments](/docs/transformers/main/ar/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. مرّر حُجج التدريب إلى [Trainer](/docs/transformers/main/ar/main_classes/trainer#transformers.Trainer) مع النموذج، ومجموعة البيانات، والمعالج، ومُجمِّع البيانات.

```py
>>> from transformers import Trainer

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

3. استدعِ [train()](/docs/transformers/main/ar/main_classes/trainer#transformers.Trainer.train) لضبط نموذجك.

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

بمجرد اكتمال التدريب، شارك نموذجك على الـ Hub باستخدام طريقة [push_to_hub()](/docs/transformers/main/ar/main_classes/trainer#transformers.Trainer.push_to_hub) لمشاركة نموذجك النهائي على 🤗 Hub:

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

## الاستدلال

الآن بعد أن قمت بضبط نموذج ViLT بدقة، ورفعته إلى 🤗 Hub، يمكنك استخدامه للاستدلال.

1. خذ صورةً وسؤالاً، ثم جهّزهما للنموذج باستخدام المعالج الخاص بنموذجك.
2. مرّر النتيجة أو المعالجة المسبقة عبر النموذج.
3. From the logits, get the most likely answer's id, and find the actual answer in the `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 بدون تدريب مسبق

عامل النموذج السابق مسألة الإجابة عن الأسئلة البصرية كمهمة تصنيف. تَعالج بعض النماذج الحديثة، مثل BLIP وBLIP-2 وInstructBLIP، مسألة الإجابة عن الأسئلة البصرية كمهمة توليدية. لنأخذ [BLIP-2](../model_doc/blip-2) مثالاً. فقد قدّم نموذجاً جديداً للتدريب المسبق على الرؤية واللغة، يمكن فيه استخدام أي تركيبة من مُشفِّر الرؤية المُدرَّب مسبقاً ونموذج اللغة الكبير (LLM) (اقرأ المزيد في [منشور BLIP-2 على المدونة](https://huggingface.co/blog/blip-2)). وهذا يتيح تحقيق نتائج متقدمة على مستوى الفن في مهام الرؤية واللغة المتعددة، بما في ذلك الإجابة عن الأسئلة البصرية.

لنوضح كيف يمكنك استخدام هذا النموذج لمهمة VQA. أولاً، لنقم بتحميل النموذج. هنا سنرسل النموذج إلى وحدة معالجة رسومية (GPU) بشكل صريح، إن كانت متاحة، وهو ما لم نكن بحاجة إلى القيام به سابقاً أثناء التدريب، لأن [Trainer](/docs/transformers/main/ar/main_classes/trainer#transformers.Trainer) يتولى ذلك تلقائياً:

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

يستقبل النموذج صورةً ونصًا كمدخلات، لذا دعونا نستخدم نفس زوج الصورة/السؤال من المثال الأول في مجموعة بيانات VQA:

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

لاستخدام BLIP-2 في مهمة الإجابة على الأسئلة البصرية، يجب أن يتبع الموجه النصي تنسيقًا محددًا: `Question: {} Answer:`.

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

الآن نحتاج إلى معالجة الصورة/الموجّه مسبقًا باستخدام مُعالِج النموذج، ثم تمرير المدخل المعالج عبر النموذج، وفك ترميز المخرجات:

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

كما ترون، تمكّن النموذج من التعرف على الحشد واتجاه الوجه (النظر إلى الأسفل)، ومع ذلك يبدو أنه أغفل حقيقة أن الحشد خلف المتزلج. ومع ذلك، في الحالات التي لا يكون فيها الحصول على مجموعات بيانات مُعلَّمة يدويًا ممكنًا، يمكن لهذا النهج إنتاج نتائج مفيدة بسرعة.

