Image Caption Generation

يعد توليد التسمية التوضيحية للصور (Image Caption Generation) تحدياً في الذكاء الاصطناعي، وهي مهمة الوصف التلقائي لمحتوى صورة باللغة الطبيعية. وقد اجتذبت التسمية التوضيحية للصور اهتمامات متزايدة في رؤية الحاسب. حيث أنها تهدف إلى فهم معلومات بصرية ضخمة والتعبير عنها باللغة الطبيعية.

وهي تقنية تتطلب الجمع بين رؤية الحاسب (Computer Vision) لفهم محتوى الصورة ومجال معالجة اللغة الطبيعية (Natural Language Processing) لتحويل مفهوم الصورة إلى كلمات بالترتيب الصحيح.


يدوياً يتطلب ذلك من المستخدمين البحث بواسطة مفاهيم الصور مثل اللون والأشكال، وقد تتجاوز بعض ميزات الصورة المفهوم الذي يركز عليه المستخدم. وقد اعتمدت الأساليب التقليدية لإسترجاع الصور مثل تلك المستخدمة من قبل المكتبات على الصور المشروحة يدوياً، والتي تعد مكلفة وتستغرق وقتا طويلاً، وخاصة بالنظر إلى قاعدة بيانات الصور الكبيرة والمتنامية.

في البداية دعونا نتعرف على COCO، هي مجموعة بيانات ضخمة للصور يتم استخدامها بشكل شائع لتدريب ومعايرة خوارزميات الكشف عن الكائنات (detection) والتقسيم (segmentation) والتسمية التوضيحية (captioning). يمكنك قراءة المزيد حول مجموعة البيانات من هنا أو في ورقة البحث هذه.


إعداد COCO API (كما هو موضح في الملف التمهيدي هنا)


سنقوم بتقسيم المشروع إلى 4 أجزاء، وكل جزء يحتوي على خطوات

الجزء الأول

    .التي سنستخدمها للحصول على البيانات COCO API نبدأ بتهيئة

الجزء الثاني

في هذا الجزء، سنتعلم كيفية تحميل البيانات ومعالجتها من (dataset).

سنقوم أيضاً بتصميم نموذج CNN-RNN لإنشاء التسميات التوضيحية للصور تلقائياً.

ستتكون مجموعة البيانات في النموذج من الصور المدخلة والتعليقات التوضيحية الناتجة المقابلة لها [image → captions].

الترميز:

يمكن اعتبار الشبكة العصبية الترشيحية (CNN) بمثابة مُرَمِّز. يتم إعطاء صورة الإدخال إلى CNN لاستخراج الخصائص (features), و آخر طبقة مخفية لـ CNN متصلة بفك الترميز.

فك الترميز:

وحدة فك الترميز هي شبكة عصبية تكرارية (RNN) تقوم بإستقبال مخرجات الترميز من

المُرَمِّز.

الخطوة 1: أداة تحميل البيانات

أداة تحميل البيانات يمكن استخدامها لتحميل مجموعة بيانات COCO على دفعات.

في التعليمات البرمجية أدناه، سنقوم بتهيئة أداة تحميل البيانات باستخدام دالة get_loader في الملف الموجود في الاسفل مع كامل المشروع data_loader.py.

إذا كنت غير ملم بتحميل البيانات ومجموعة البيانات انصحك بهذا الدرس. تأخذ الدالة (get_loader) إدخال عدد من الوسائط التي يمكن استكشافها في (data_loader).


سأشرح هنا بعض الدوال (methods):

(transform): تحدد كيفية المعالجة المسبقة للصور وتحويلها إلى PyTorch قبل استخدامها كمدخلات إلى الترميز عن طريق CNN.

(mode ): تأخذ واحدة من قيمتين، إما ‘train’ (تحميل البيانات التدريبية على دفعات) أو ‘test’ (بيانات الاختبار). سنقول إن data loader في وضع التدريب أو الاختبار، على التوالي.

(batch_size): يحدد حجم الدفعة عند تدريب النموذج.

(vocab_threshold): إجمالي عدد المرات التي يجب أن تظهر فيها كلمة في التسميات التوضيحية للتدريب قبل استخدامها كجزء من الجملة. تعتبر الكلمات التي تحتوي على أقل من vocab_threshold كلمات غير معروفة.

(vocab_from_file): قيمة منطقية تقرر ما إذا كان سيتم تحميل المفردات من الملف.

عند تشغيل التعليمات البرمجية أدناه، سيتم تخزين محمل البيانات في المتغير (data_loader).

تحدد الدالة (getitem) في (CoCoDataset) كيفية معالجة التسمية التوضيحية للصورة مسبقاً. وهذا ينطبق على كافة فئات (Dataset) في PyTorch. إذا كانت غير ملم بهذا يرجى مراجعة البرنامج التعليمي هنا. عندما يكون محمل البيانات في وضع التدريب، تبدأ هذه الدالة أولاً بالحصول على مسار الملف للصورة التدريب والتسمية التوضيحية المناظرة لها:

بعد تحميل الصورة في مجلد التدريب مع اسم المسار، تتم معالجة الصورة باستخدام نفس التحويل (transform_train) الذي تم توفيره عند إنشاء أداة تحميل البيانات.

قبل معالجة التسميات التوضيحية

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

لفهم مزيد من التفاصيل حول كيفية معالجة التسميات التوضيحية (COCO) مسبقًا، سنحتاج أولاً إلى إلقاء نظرة على المتغير (vocab) في (CoCoDataset).

أدناه جزء الكود من الدالة ( init) الخاصة بـ (CoCoDataset):

نستخدم هذا المثال لإجراء معالجة مسبقة للتسميات التوضيحية .

يحول الكود أعلاه أي جملة توضيح إلى كلمات، قبل إرسالها إلى ( PyTorch)، لمعرفة كيفية عمله سأقوم بشرح الكود.

في السطر 1، يتم تحويل كل حرف في التسمية التوضيحية إلى أحرف صغيرة، ويتم استخدام الدالة (nltk.tokenize.word_tokenize) للحصول على قائمة بالأرقام المميزة.

في السطر 2 والسطر 3، نقوم بتهيئة قائمة فارغة وإلحاق عدد صحيح لوضع علامة على بداية التسمية التوضيحية. تستخدم هذه الورقة كلمة (start)، وكلمة (end) لتحديد بداية ونهاية التسمية التوضيحية.

يتم استخدام العدد الصحيح 0 دائمًا لتحديد بداية التسمية التوضيحية، والرقم 1 لتحديد النهاية.

في السطر 4، نواصل القائمة بإضافة أعداد صحيحة تتوافق مع كل من الرموز المميزة في التسمية التوضيحية.

أخيرًا، نقوم بتحويل قائمة الأعداد الصحيحة إلى (PyTorch). يمكنك قراءة المزيد حول الأنواع المختلفة من TORCH.TENSOR.

باختصار، يتم تحويل أي تسمية توضيحية إلى قائمة بالرموز المميزة، مع رموز خاصة بالبداية والنهاية في بداية الجملة ونهايتها:

ثم يتم تحويل قائمة الرموز هذه إلى قائمة من الأعداد الصحيحة، حيث يكون لكل كلمة مميزة في المفردات قيمة عدد صحيح مرتبطة به:

أخيرًا، يتم تحويل هذه القائمة إلى (PyTorch). تتم معالجة جميع التسميات التوضيحية في مجموعة بيانات (COCO) مسبقًا باستخدام الإجراء نفسه من الأسطر الموضحة أعلاه.


كما رأينا، من أجل تحويل رمز مميز إلى عدد صحيح مماثل، ندعو الدالة (data_loader.dataset.vocab). يمكن استكشاف تفاصيل كيفية عمل هذه الاستدعاء في الدالة (call) في (vocabulary.py).

متغير (word2idx) هو قاموس (Python) الذي يتم فهرسته بواسطة مفاتيح ذات قيمة متسلسلة (معظمها رموز تم الحصول عليها من التسميات التوضيحية للتدريب). بالنسبة لكل مفتاح، تكون القيمة المطابقة هي العدد الصحيح الذي تم تعيين الرمز المميز له في خطوة المعالجة السابقة.

الشيء الأخير الذي يجب ذكره هو أداة (vocab_from_file) التي يتم توفيرها عند إنشاء ملف تحميل البيانات. لفهم هذه الأداة، عند إنشاء محمل بيانات جديد يتم حفظ المفردات (data_loader.dataset.vocab) كملف في مجلد المشروع، مع اسم الملف (vocab.pkl).

إذا كنت لا تزال تقوم بتعديل قيمة (vocab_threshold)، فيجب عليك تعيين vocab_from_file = False لتصبح التغييرات نافذة المفعول.

ولكن بمجرد رضاك ​​عن القيمة التي اخترتها لـ (vocab_threshold)، ستحتاج فقط إلى تشغيل أداة تحميل البيانات مرة أخرى باستخدام (vocab_threshold) التي اخترتها لحفظ المفردات الجديدة في الملف. بعد ذلك يمكنك تعيين vocab_from_file = True لتحميل المفردات من الملف.


الخطوة 2: استخدام محمل البيانات للحصول على دفعات تدريب

تختلف التسميات التوضيحية في مجموعة البيانات اختلافًا كبيرًا في الطول. يمكنك مشاهدة ذلك عن طريق فحص (data_loader.dataset.caption_lengths)، غالبية التسميات التوضيحية لها طول 10. كما أن التسميات التوضيحية القصيرة جدًا والطويلة للغاية نادرة جدًا.

لإنشاء مجموعات من بيانات التدريب، نبدأ أولاً بأخذ عينات من طول التسمية التوضيحية. بعد ذلك، نسترجع مجموعة من batch_size من الصور حيث يكون طول جميع التسميات التوضيحية بطول العينات. يطابق هذا النهج لتجميع الدُفعات الإجراء الوارد في هذه الورقة وقد ثبت أنه فعال من الناحية الحسابية دون تدهور في الأداء.

تقوم دالة (get_train_indices) في فئة (CoCoDataset) أولاً بتجميع عينات طول التسمية التوضيحية، ثم تقوم بتجميع (batch_size) المطابقة لنقاط بيانات التدريب مع تسميات توضيحية بهذا الطول. يتم تخزين هذه المؤشرات في indices.

يتم تزويد هذه المؤشرات إلى أداة تحميل البيانات، والتي يتم استخدامها بعد ذلك لإسترداد نقاط البيانات المقابلة. يتم تخزين الصور والتسميات التوضيحية التي تمت معالجتها مسبقًا في images و captions.



الخطوة 3: (EncoderCNN)

نستخدم للمُرمز pre-trained ResNet-50 Model (مع إزالة الطبقة الاخيرة تامة الاتصال) لاستخراج الخصائص من مجموعة من الصور التي تمت معالجتها مسبقًا. يتم بعد ذلك تسطيح الإخراج إلى الناقل، قبل تمريره عبر طبقة خطية لتحويل ناقلات الخصائص.

ثم نقوم بإستيراد EncoderCNN و DecoderRNN من model.py.

في السطر التالي ، نعرّف الجهاز الذي سنستخدمه لنقل PyTorch إلى GPU (إذا كانت CUDA متوفرة).

بعد ذلك يتم تمرير الصور التي تمت معالجتها مسبقًا من الدُفعة في الخطوة 2 عبر المرمز، ويتم تخزين المخرجات في features.

الخطوة 4: DecoderRNN

ستكون وحدة فك الترميز DecoderRNN تقبل كمدخلات:

خصائص PyTorch التي تحتوي على خصائص الصورة المدمجة (تم إخراجها في الخطوة 3، عندما تم تمرير الدفعة الأخيرة من الصور من الخطوة 2 عبر الترميز) ، إلى جانب PyTorch المطابق لآخر دفعة من التسميات التوضيحية من الخطوة 2.

على الرغم من تنفيذ وحدة فك الترميز الموضحة في هذه الورقة، يمكنك تنفيذ أي بنية من اختيارك، طالما أنها تستخدم طبقة RNN واحدة على الأقل.


الجزء الثالث

في هذا الجزء سنقوم بتدريب نموذج CNN-RNN.

دعنا نلخص وظائف بعض المتغيرات:

(batch_size) – حجم الدفعة لكل دفعة تدريب. هو عدد أزواج الصور التوضيحية المستخدمة لتعديل أوزان النموذج في كل خطوة تدريب.

(vocab_threshold) – الحد الأدنى لعدد الكلمات. لاحظ أن العتبة (threshold) الأكبر ستؤدي إلى مفردات أصغر، في حين أن العتبة الأصغر ستتضمن كلمات أكثر ندرة وينتج عنها مفردات أكبر.

(vocab_from_file) – متغير منطقي يقرر ما إذا كان سيتم تحميل المفردات من الملف.

(embed_size) – أبعاد الصورة وكلمات المضمنة.

(hidden_size) – عدد الخصائص في الطبقة المخفية لوحدة فك ترميز RNN.

(num_epochs) – عدد الحلقات لتدريب النموذج. أوصيك بتعيين num_epochs = 3 ، لكن لا تتردد في زيادة أو تقليل هذا الرقم كما تشاء. دربت هذه الورقة نموذجًا للتسميات التوضيحية على وحدة معالجة رسومية (GPU) على أحدث طراز لمدة 3 أيام، ولكن يمكنك الحصول على نتائج معقولة في غضون بضع ساعات! ( بالطبع، إذا كنت تريد أن يتنافس النموذج الخاص بك مع الأبحاث الحالية فسيتعين عليك التدريب لفترة أطول بكثير).

(save_every) – يحدد فترات حفظ أوزان النموذج. اوصي بتعيين save_every = 1، لحفظ أوزان النموذج بعد كل فترة. وبهذه الطريقة سيتم حفظ أوزان الترميز وفك الترميز في المجلد كـ encoder-i.pkl و decoder-i.pkl، على التوالي.

(print_every) – تحديد بعد كم من المدة يتم عرض الخطأ أثناء التدريب.

(log_file) – اسم الملف الذي يحتوي على المعلومات الخاصة بالتدريب.

إذا كنت غير متأكد من أين تبدأ في تعيين بعض القيم أعلاه، فيمكنك الاطلاع على هذه الورقة وهذه الورقة للحصول على إرشادات مفيدة! أوصيك بالرجوع إلى هذه الأوراق البحثية المقترحة للحصول على تخمين مبدئي قوي.

دعنا نلخص هيكلة CNN-RNN، تستخدم التسمية التوضيحية للصورة، الترميز وفك الترميز، حيث يقوم الترميز بتفكيك خصائص من الصور المعينة، وتقوم وحدة فك الترميز بإنشاء تسميات توضيحية بناءً على الخصائص الموجودة في أداة الترميز. يستخدم Encoder شبكة CNN لأنها جيدة في معالجة الصور، ويستخدم Decoder شبكة LSTM. يتم اختيار حجم التضمين كـ 256، والحجم المخفي هو 512 بالإشارة إلى الأوراق البحثية. يتم اختيار الحد الأقصى لعدد المفردات ليكون 5 من أجل تجاهل الكلمات التي تظهر بشكل نادر .

(transform_train) عند تعديل هذا التحويل، ضع في اعتبارك أن الصور في مجموعة البيانات لها ارتفاعات وعرض مختلفة، وإذا كنت تستخدم نموذجًا تم تدريبه مسبقًا، فيجب إجراء التغيير المناسب.

و أخترت محسن optimizer) Adam SGD) كما أوصت الورقة باستخدامه.

لخطوة 2: تدريب النموذج

بمجرد تنفيذ التعليمات البرمجية في الخطوة 1، يجب تشغيل الإجراء التدريبي الموضح أدناه دون مشكلة.

في هذه الحالة، لاحظ أسماء الملفات التي تحتوي على أوزان الترميز وفك الترميز التي ترغب في تحميلها (encoder_file و decoder_file). بعد ذلك يمكنك تحميل الأوزان باستخدام النص البرمجي أدناه:

الطريقة المقترحة للتحقق من صحة النموذج إنشاء ملف json مثل هذا الملف الذي يحتوي على التسميات التوضيحية المتوقعة للنموذج بعد ذلك، يمكنك كتابة السيناريو الخاص بك أو استخدام برنامج نصي تجده عبر هذا الموقع لحساب درجة BLEU الخاصة بنموذجك. يمكنك قراءة المزيد حول درجة BLEU، إلى جانب مقاييس التقييم الأخرى (مثل TEOR و Cider) في القسم 4.1 من هذه الورقة. لمزيد من المعلومات حول كيفية استخدام ملف التسميات التوضيحية، راجع موقع الويب الخاص بمجموعة بيانات COCO.

الجزء الرابع و الأخير

في هذا الجزء سنستخدم النموذج لإنشاء تعليقات للصور في مجموعة بيانات الاختبار.

الخطوة 1: الحصول على محمل البيانات لمجموعة بيانات الاختبار

الخطوة 2: تحميل النماذج المدربة

الخطوة 3: الانتهاء من العينات

الخطوة 4: تنظيف التسميات التوضيحية

الخطوة 5: توليد التوقعات!

الخطوة 1: الحصول على محمل البيانات لمجموعة بيانات الاختبار

قبل تشغيل الكود أدناه، وتحديد التحويل في (transform_test) التي ترغب في استخدامها للمعالجة المسبقة لصور الإختبار.

تأكد من أن التحويل الذي تحدده هنا متوافق مع التحويل الذي استخدمته لمعالجة الصور التدريبية في الجزء الثالث.

على سبيل المثال، إذا قمت بتحسين صور التدريب، فيجب عليك أيضًا تطبيق نفس الإجراء على صور الاختبار.

الخطوة 2: تحميل النماذج المدربة

سنقوم بتحميل المرمز وفك الترميز المدربين من (الجزء الثالث ).

لإنجاز ذلك، يجب تحديد أسماء ملفات الترميز وفك الترميز المحفوظة في المجلد (على سبيل المثال، يجب أن تكون هذه الأسماء هي encoder-5.pkl و decoder-5.pkl، إذا قمت بتدريب النموذج لمدة 5 دفعات وحفظت الأوزان بعد كل دفعة.

الآن نقوم بتوصيل كلاً من حجم التضمين وحجم الطبقة المخفية لوحدة فك الترميز المقابلة للملف المحدد في decoder_file.

الخطوة 3: الانتهاء من العينات

يمكن استكشاف التحولات بين الأعداد الصحيحة والرموز عن طريق فحص إما

(data_loader.dataset.vocab.word2idx) أو (data_loader.dataset.vocab.idx2word).

بعد تطبيق الطريقة أعلاه، نقوم بتشغيل الكود أدناه. إذا أرجعت خطأ فالرجاء تصحيح الكود قبل المتابعة.

الخطوة 4: تنظيف التسميات التوضيحية

في التعليمات البرمجية أدناه، الدالة clean_sentence يجب أن تأخذ قائمة من الأعداد الصحيحة (المقابلة لمتغير الناتج في الخطوة 3) كمدخل وإرجاع الجملة المتوقعة المقابلة كسلسلة واحدة:

الخطوة 5: توليد التنبؤات!

هنا دالة (get_prediction()) يمكنك استخدامها في التكرار فوق الصور في مجموعة بيانات الاختبار وطباعة التسمية التوضيحية المتوقعة للنموذج

عينة من المخرجات:

لتحميل الكود هنا