تعلم الآلةرؤية الحاسب

توليد التسمية التوضيحية للصور (Image Caption Generation)

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

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

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

بعض محركات التعليق التوضيحي على الإنترنت، بما في ذلك محرك الوسم في الوقت الحقيقي الذي وضعه الباحثين في جامعة ولاية بنسلفانيا: ALIPR.com.

وقد بنيت شركات مثل:

Canotic.com ، Playment ، Edgecase.ai ، مايكروسوفت، VOTT.ai ،ataloop.ai

جميع المحركات التلقائية لشرح الفيديو، وتقدم شركات أخرى مثل:

Alegion.com ، Infolks ، DataPure.co ، Scaleapi.com ، Diffgram.com ، Figure-eight.com.

مزيجاً من التعليق التوضيحي التلقائي للصورة مع مزيج من النظرة العامة اليدوية عند فشل الأتمتة.

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

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

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

الجزء الأول

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

import os
import sys
sys.path.append('/opt/cocoapi/PythonAPI')
from pycocotools.coco import COCO

# initialize COCO API for instance annotations
dataDir = '/opt/cocoapi'
dataType = 'val2014'
instances_annFile = os.path.join(dataDir, 'annotations/instances_{}.json'.format(dataType))
coco = COCO(instances_annFile)

# initialize COCO API for caption annotations
captions_annFile = os.path.join(dataDir, 'annotations/captions_{}.json'.format(dataType))
coco_caps = COCO(captions_annFile)
# get image ids
ids = list(coco.anns.keys())

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

import skimage.io as io
import matplotlib.pyplot as plt
%matplotlib inline

# pick a random image and obtain the corresponding URL
ann_id = np.random.choice(ids)
img_id = coco.anns[ann_id]['image_id']
img = coco.loadImgs(img_id)[0]
url = img['coco_url']

# print URL and visualize corresponding image
print(url)
I = io.imread(url)
plt.axis('off')
plt.imshow(I)
plt.show()

# load and display captions
annIds = coco_caps.getAnnIds(imgIds=img['id'])
anns = coco_caps.loadAnns(annIds)
coco_caps.showAnns(anns)

المخرجات:

The man is walking with a bicycle covered with small green bananas.

A man walking a bike with piles of green bananas on it.

A man pushes a bicycle filled with many bananas.

A person pushing a bicycle that is loaded with bananas.

A man carrying a load of bananas through the marketplace on his bike.

الجزء الثاني

في هذا الجزء، سنتعلم كيفية تحميل البيانات ومعالجتها من (dataset). سنقوم أيضاً بتصميم نموذج CNN-RNN لإنشاء التسميات التوضيحية للصور تلقائياً.

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

الترميز:

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

فك الترميز:

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

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

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

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

بالنسبة لهذا المشروع، لن اقوم بشرح ملف 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).

import sys
sys.path.append('/opt/cocoapi/PythonAPI')
from pycocotools.coco import COCO
!pip install nltk
import nltk
nltk.download('punkt')
from data_loader import get_loader
from torchvision import transforms

# Define a transform to pre-process the training images.
transform_train = transforms.Compose([
transforms.Resize(256), # smaller edge of image resized to 256
transforms.RandomCrop(224), # get 224x224 crop from random location
transforms.RandomHorizontalFlip(), # horizontally flip image with probability=0.5
transforms.ToTensor(), # convert the PIL Image to a tensor
transforms.Normalize((0.485, 0.456, 0.406), # normalize image for pre-trained model
(0.229, 0.224, 0.225))])

# Set the minimum word count threshold.
vocab_threshold = 5

# Specify the batch size.
batch_size = 10

# Obtain the data loader.
data_loader = get_loader(transform=transform_train,
, mode='train'
,batch_size=batch_size
, vocab_threshold=vocab_threshold
( vocab_from_file=False

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

image = Image.open(os.path.join(self.img_folder, path)).convert('RGB')
image = self.transform(image)

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

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

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

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

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

يمكنك أن ترى أن (data_loader.dataset.vocab) هو مثيل لفئة المفردات من ملف (vocabulary.py). يمكنك التحقق من خلال النظر في التعليمات البرمجية الكاملة في data_loader.

def __init__(self, transform, mode, batch_size, vocab_threshold, vocab_file, start_word,
end_word, unk_word, annotations_file, vocab_from_file, img_folder):
   ...
   self.vocab = Vocabulary(vocab_threshold, vocab_file, start_word,
   end_word, unk_word, annotations_file, vocab_from_file)
    
   …

نستخدم هذا المثال لإجراء معالجة مسبقة للتسميات التوضيحية في COCO من الدالة (getitem) في الفئة (CoCoDataset):

# Convert caption to tensor of word ids.
tokens = nltk.tokenize.word_tokenize(str(caption).lower()) # line 1 
caption = [] # line 2 
caption.append(self.vocab(self.vocab.start_word)) # line 3 
caption.extend([self.vocab(token) for token in tokens]) # line 4
caption.append(self.vocab(self.vocab.end_word)) # line 5
caption = torch.Tensor(caption).long() # line 6

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

import nltk
sample_caption = 'A person doing a trick on a rail while riding a skateboard.'
sample_tokens = nltk.tokenize.word_tokenize(str(sample_caption).lower())
print(sample_tokens)

المخرجات:

[‘a’, ‘person’, ‘doing’, ‘a’, ‘trick’, ‘on’, ‘a’, ‘rail’, ‘while’, ‘riding’, ‘a’, ‘skateboard’, ‘.’]

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

sample_caption = []
start_word = data_loader.dataset.vocab.start_word
sample_caption.append(data_loader.dataset.vocab(start_word))
print(sample_caption)
sample_caption.extend([data_loader.dataset.vocab(token) for token in sample_tokens])
print(sample_caption)
end_word = data_loader.dataset.vocab.end_word
sample_caption.append(data_loader.dataset.vocab(end_word))
print(sample_caption)

المخرجات:

[0, 3, 98, 754, 3, 396, 39, 3, 1009, 207, 139, 3, 753, 18, 1]

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

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

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

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

import torch
sample_caption = torch.Tensor(sample_caption).long()
print(sample_caption)

المخرجات:

tensor([ 0, 3, 98, 754, 3, 396, 39, 3, 1009,207, 139, 3, 753, 18, 1])

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

[<start>, ‘a’, ‘person’, ‘doing’, ‘a’, ‘trick’, ‘while’, ‘riding’, ‘a’, ‘skateboard’, ‘.’, <end>]

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

[0 ، 3 ، 98 ، 754 ، 3 ، 396 ، 207 ، 139 ، 3 ، 753 ، 18 ، 1]

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

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

def __call__(self, word):
   if not word in self.word2idx:
      return self.word2idx[self.unk_word]
   return self.word2idx[word]

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

# Preview the word2idx dictionary.
dict(list(data_loader.dataset.vocab.word2idx.items())[:10])
{'<end>': 1,
'<start>': 0,
'<unk>': 2,
'a': 3,
'and': 6,
'clean': 5,
'decorated': 8,
'empty': 9,
'very': 4,
'well': 7}

كما سترى ما إذا كنت تفحص الكود في (vocabulary.py)، يتم إنشاء قاموس (word2idx) عن طريق التكرار فوق التسميات التوضيحية في مجموعة بيانات التدريب. إذا ظهر رمز مميز ما، لا يقل عن (vocab_threshold) في مجموعة التدريب، فسيتم إضافته كمفتاح للقاموس ويتم تعيين عدد صحيح فريد مطابق له.

الشيء الأخير الذي يجب ذكره هو أداة (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.

import numpy as np
import torch.utils.data as data

# Randomly sample a caption length, and sample indices with that length.
indices = data_loader.dataset.get_train_indices()
print('sampled indices:', indices)

# Create and assign a batch sampler to retrieve a batch with the sampled indices.
new_sampler = data.sampler.SubsetRandomSampler(indices=indices)
data_loader.batch_sampler.sampler = new_sampler

# Obtain the batch.
images, captions = next(iter(data_loader))
print('images.shape:', images.shape)
print('captions.shape:', captions.shape)

# Uncomment the lines of code below to print the pre-processed images and captions.
print('images:', images)
print('captions:', captions)

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

سنقوم بتدريب النموذج الخاص بنا في الجزء الثالث.

في الخطوات التالية، نركز على تعلم كيفية تحديد بنية CNN-RNN في PyTorch.

الخطوة 3: تجربة (CNN Encoder)

الكود أدناه لاستيراد EncoderCNN و DecoderRNN من model.py.

# Import EncoderCNN and DecoderRNN.
from model import EncoderCNN, DecoderRNN

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

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

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

# Specify the dimensionality of the image embedding.
embed_size = 256

# Initialize the encoder.
encoder = EncoderCNN(embed_size)

# Move the encoder to GPU if CUDA is available.
encoder.to(device)

# Move last batch of images (from Step 2) to GPU if CUDA is available.
images = images.to(device)

# Pass the images through the encoder.
features = encoder(images)
print('type(features):', type(features))
print('features.shape:', features.shape)
assert type(features)==torch.Tensor, "Encoder output needs to be a PyTorch Tensor."
assert (features.shape[0]==batch_size) & (features.shape[1]==embed_size), "The shape of the encoder output is incorrect."

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

الخطوة 4: تطبيق RNN لفك الترميز

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

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

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

يجب أن تكون المخرجات عبارة عن (tensor PyTorch) مع [batch_size ،captions.shape [1] ،vocab_size]. يجب تصميم مخرجاتك بحيث تحتوي المخرجات [i، j، k] على النتيجة المتوقعة للنموذج، مما يشير إلى مدى احتمال أن تكون الرمز المميز j في التسمية التوضيحية رقم i في الدفعة هو الرمز المميز k في المفردات. في الجزء الثالث، سنقوم بتوفير كود لتزويد هذه النتائج إلى (torch.nn.CrossEntropyLoss optimizer) في PyTorch.

# Specify the number of features in the hidden state of the RNN decoder.
hidden_size = 512

# Store the size of the vocabulary.
vocab_size = len(data_loader.dataset.vocab)

# Initialize the decoder.
decoder = DecoderRNN(embed_size, hidden_size, vocab_size)

# Move the decoder to GPU if CUDA is available.
decoder.to(device)

# Move last batch of captions (from Step 1) to GPU if CUDA is available
captions = captions.to(device)

# Pass the encoder output and captions through the decoder.
outputs = decoder(features, captions)
print('type(outputs):', type(outputs))
print('outputs.shape:', outputs.shape)
print('batch size ', batch_size)
print('captions.shape ', captions.shape[1])
print('captions ex ', captions[0])
print('vocab size ', vocab_size)

الجزء الثالث

في هذا الجزء سنقوم بتدريب نموذج 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) – تحديد بعد كم من المدة يتم عرض الخطأ في (Jupyter) أثناء التدريب.

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

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

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

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

هنا لم اجري تعديلات على transform_train لأن النموذج يعطي نتيجة جيدة.

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

import torch
import torch.nn as nn
from torchvision import transforms
import sys
sys.path.append('/opt/cocoapi/PythonAPI')
from pycocotools.coco import COCO
from data_loader import get_loader
from model import EncoderCNN, DecoderRNN
import math

batch_size = 128 # batch size
vocab_threshold = 5 # minimum word count threshold
vocab_from_file = False # if True, load existing vocab file
embed_size = 256 # dimensionality of image and word embeddings
hidden_size = 512 # number of features in hidden state of the RNN decoder
num_epochs = 1 # number of training epochs
save_every = 1 # determines frequency of saving model weights
print_every = 100 # determines window for printing average loss
log_file = 'training_log.txt' # name of file with saved training loss and perplexity

# 2: Amend the image transform below.
transform_train = transforms.Compose([
transforms.Resize(256), # smaller edge of image resized to 256
transforms.RandomCrop(224), # get 224x224 crop from random location
transforms.RandomHorizontalFlip(), # horizontally flip image with probability=0.5
transforms.ToTensor(), # convert the PIL Image to a tensor
transforms.Normalize((0.485, 0.456, 0.406), # normalize image for pre-trained model
(0.229, 0.224, 0.225))])

# Build data loader.
data_loader = get_loader(transform=transform_train,
mode='train',
batch_size=batch_size,
vocab_threshold=vocab_threshold,
vocab_from_file=vocab_from_file)

# The size of the vocabulary.
vocab_size = len(data_loader.dataset.vocab)

# Initialize the encoder and decoder.
encoder = EncoderCNN(embed_size)
decoder = DecoderRNN(embed_size, hidden_size, vocab_size)

# Move models to GPU if CUDA is available.
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
encoder.to(device)
decoder.to(device)

# Define the loss function.
criterion = nn.CrossEntropyLoss().cuda() if torch.cuda.is_available() else nn.CrossEntropyLoss()

# 3: Specify the learnable parameters of the model.
params = list(decoder.parameters()) + list(encoder.embed.parameters())

# 4: Define the optimizer.
optimizer = torch.optim.Adam(params, lr=0.001)

# Set the total number of training steps per epoch.
total_step = math.ceil(len(data_loader.dataset.caption_lengths) / data_loader.batch_sampler.batch_size)

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

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

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

# Load pre-trained weights before resuming training.
encoder.load_state_dict(torch.load(os.path.join('./models', encoder_file)))
decoder.load_state_dict(torch.load(os.path.join('./models', 

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

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

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

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

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

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

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

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

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

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

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

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

import sys
sys.path.append('/opt/cocoapi/PythonAPI')
from pycocotools.coco import COC
from data_loader import get_loader
from torchvision import transforms

# 1: Define a transform to pre-process the testing images.
transform_test = transforms.Compose([
transforms.Resize(256), # smaller edge of image resized to 256
transforms.RandomCrop(224), # get 224x224 crop from random location
transforms.RandomHorizontalFlip(), # horizontally flip image with probability=0.5
transforms.ToTensor(), # convert the PIL Image to a tensor
transforms.Normalize((0.485, 0.456, 0.406), # normalize image for pre-trained model
(0.229, 0.224, 0.225))])

# Create the data loader.
data_loader = get_loader(transform=transform_test
mode='test')

تم تحميل المفردات بنجاح من ملف الموجود في المشروع vocab.pkl، قم بفحص الكود أدناه لتصور مثال اختبار للصورة، قبل تطبيق المعالجة المسبقة.

import matplotlib.pyplot as plt
%matplotlib inline

# Obtain sample image before and after pre-processing.
orig_image, image = next(iter(data_loader))

# Visualize sample image, before pre-processing.
plt.imshow(np.squeeze(orig_image))
plt.title('example image')
plt.show()

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

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

import torch
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

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

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

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

import os
import torch
from model import EncoderCNN, DecoderRNN

#Specify the saved models to load.
encoder_file = 'encoder-1.pkl'
decoder_file = 'decoder-1.pkl'

# Select appropriate values for the Python variables below.
embed_size = 256
hidden_size = 512

# The size of the vocabulary.
vocab_size = len(data_loader.dataset.vocab)

# Initialize the encoder and decoder, and set each to inference mode.
encoder = EncoderCNN(embed_size)
encoder.eval()
decoder = DecoderRNN(embed_size, hidden_size, vocab_size)
decoder.eval()

# Load the trained weights.
encoder.load_state_dict(torch.load(os.path.join('./models', encoder_file)))
decoder.load_state_dict(torch.load(os.path.join('./models', decoder_file)))

# Move models to GPU if CUDA is available.
encoder.to(device)
decoder.to(device)
الخطوة 3: الانتهاء من العينات

قبل تنفيذ التعليمات البرمجية التالية، يجب أن تكتب دالة العينة DecoderRNN في model.py.

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

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

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

# Move image Pytorch Tensor to GPU if CUDA is available.
image = image.to(device)

# Obtain the embedded image features.
features = encoder(image).unsqueeze(1)

# Pass the embedded image features through the model to get a predicted caption.
output = decoder.sample(features)
print('example output:', output)
assert (type(output)==list), "Output needs to be a Python list"
assert all([type(x)==int for x in output]), "Output should be a list of integers."
assert all([x in data_loader.dataset.vocab.idx2word for x in output]), "Each entry in the output needs to correspond to an integer that indicates a token in the vocabulary."

example output: [0, 3, 2436, 130, 170, 77, 3, 204, 21, 498, 77, 32, 297, 18, 1, 1, 1, 1, 1, 1]

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

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

def clean_sentence(output):
   sentense = ''
   for i in output:
      word = data_loader.dataset.vocab.idx2word[i]
      if i == 0:
         continue
      if i == 1:
         break
      if i == 18:
         sentense = sentense + word
      else:
         sentense = sentense + ' ' + word
   return sentense.strip()

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

sentence = clean_sentence(output)
print('example sentence:', sentence)
assert type(sentence)==str, 'Sentence needs to be a Python string!'

المخرجات:

example sentence: a giraffe is standing in a field with trees in the background.

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

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

def get_prediction():
   orig_image, image = next(iter(data_loader))
   plt.imshow(np.squeeze(orig_image))
   plt.title('Sample Image')
   plt.show()
   image = image.to(device)
   features = encoder(image).unsqueeze(1)
   output = decoder.sample(features)
   sentence = clean_sentence(output)
   print(sentence)

المخرجات:

a herd of cattle grazing on a lush green field

a baseball player is swinging a bat at a ball.

a street scene with a traffic light and a street sign.

a train is traveling down the tracks in the countryside.

بإمكانك تحميل الكود الكامل لهذا الدرس من هنا.

المصدر
https://www.youtube.com/watch?v=xKt21ucdBY0https://cs.stanford.edu/people/karpathy/sfmltalk.pdfhttps://docs.microsoft.com/ar-sa/archive/blogs/machinelearning/rapid-progress-in-automatic-image-captioninghttp://cs224d.stanford.edu/reports/msoh.pdfhttps://arxiv.org/pdf/1411.4555.pdfhttps://arxiv.org/pdf/1502.03044.pdfhttps://arxiv.org/pdf/1512.03385.pdf
اظهر المزيد

Asrar

مبرمجة مهتمة بالذكاء الإصطناعي و الرؤية الحاسوبية.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

زر الذهاب إلى الأعلى