كل شيء عن YOLOs - Part5 - كيفية ترميزها

في هذا المنشور ، سأشرح كيفية استخدام إصدار الفانيليا من YOLOv3 للكشف عن كائنات من مجموعة بيانات COCO وأيضًا كيفية تدريب مجموعة البيانات الخاصة بك لحالة الاستخدام الخاصة بك.

يعتمد رمز الكشف عن Yolo هنا على تنفيذ إريك ليندرنورين لورقة جوزيف ريدمون وعلي فرهادي.

فيما يلي روابط المسلسل.

كل شيء عن YOLOs - Part1 - القليل من التاريخ

كل شيء عن YOLOs - Part2 - أول YOLO

كل شيء عن YOLOs - Part3 - YOLOv2 أفضل وأسرع وأقوى

كل شيء عن YOLOs - Part4 - YOLOv3 ، تحسن تدريجي

كل شيء عن YOLOs - Part5 - Up and Running

تجدون مجلد المشروع في gitrepo الخاص بي.

في مجلد المشروع ، ستجد مجلدًا فرعيًا يسمى التكوين مع ملفات التكوين وأسماء الفئات ومتغيرات البيئة ومجلد البيانات مع مجموعات البيانات ومجلد الأدوات مع بعض وظائف الثعبان المفيدة.

أولاً ، قم بتنزيل ملف أوزان YOLOv3 ووضعه في مجلد التكوين في المشروع عن طريق تشغيل الأمر التالي. (لقد أضفت ملف .sh للقيام بذلك في الريبو)

wget https://pjreddie.com/media/files/yolov3.weights

بمجرد تنزيل محتويات مجلد التكوين ، يجب أن تبدو كما يلي.

كيفية إعداد وتشغيل YOLOv3 للكشف عن كائنات COCO؟

دعونا نلقي نظرة على تنفيذ الفانيليا YOLO لاستنتاج كائنات COCO.

  • استيراد الوحدات المطلوبة.
من نماذج الاستيراد * من أدوات الاستيراد * استيراد نظام التشغيل ، sys ، الوقت ، التاريخ والوقت ، استيراد الشعلة العشوائية من torch.utils.data استيراد DataLoader من مجموعات بيانات استيراد torchvision ، التحويل من torch.autograd استيراد Variableimport matplotlib.pyplot كـ plt import matplotlib.patches as بقع من صورة استيراد PIL
  • قم بتحميل التكوين والأوزان المدربين مسبقًا ، بالإضافة إلى أسماء فئات مجموعة بيانات COCO التي تم تدريب نموذج Darknet عليها. img_size و conf_thres و num_thresold هي معلمات يمكن تعديلها بناءً على حالة الاستخدام.
ملاحظة: اضبط النموذج على وضع التقييم للاستدلال.
config_path = 'config / yolov3.cfg' weights_path = 'config / yolov3.weights' class_path = 'config / coco.names' img_size = 416 conf_thres = 0.8 nms_thres = 0.4
# نموذج تحميل وأوزان = Darknet (config_path ، img_size = img_size) model.load_weights (weights_path) model.cuda () model.eval () class = utils.load_classes (class_path) Tensor = torch.cuda.FloatTensor
  • اكتب وظيفة لتنفيذ الكشف الأساسي المعطى للصورة. يرجى إلقاء نظرة على التعليقات لما يفعله الرمز. في الغالب ، يتم معالجة الصورة مسبقًا.
def detect_image (img): # مقياس ونسبة صورة لوح = min (img_size / img.size [0]، img_size / img.size [1]) imw = round (img.size [0] * ratio) imh = round ( img.size [1] * ratio) img_transforms = converts.Compose ([converts.Resize ((imh، imw))، converts.Pad ((max (int ((imh-imw) / 2)، 0)، max ( int ((imw-imh) / 2)، 0)، max (int ((imh-imw) / 2)، 0)، max (int ((imw-imh) / 2)، 0))، (128،128،128) )، converts.ToTensor ()،]) # تحويل الصورة إلى Tensor image_tensor = img_transforms (img) .float () image_tensor = image_tensor.unsqueeze_ (0) input_img = متغير (image_tensor.type (Tensor)) # استدلال على النموذج والحصول على عمليات الكشف باستخدام torch.no_grad (): detections = model (input_img) detections = utils.non_max_suppression (detections، 80، conf_thres، nms_thres) اكتشافات العودة [0]
  • الآن رمز استخدام هذه الوظيفة للحصول على استنتاجات. يعمل هذا مع أي كائن موجود في مجموعة بيانات COCO. مرة أخرى ، تتعامل معظم الشفرة مع المعالجة المسبقة للصورة ورسم المربعات المحيطة.
# تحميل صورة والحصول على عمليات الكشف img_path = "images / blueangels.jpg" prev_time = time.time () img = Image.open (img_path) Detection = detect_image (img) inference_time = datetime.timedelta (ثواني = time.time () - prev_time) طباعة ('وقت الاستدلال:٪ s'٪ (inference_time)) # الحصول على ألوان المربع المحيط cmap = plt.get_cmap ('tab20b') الألوان = [cmap (i) لـ i في np.linspace (0، 1، 20)] img = np.array (img) plt.figure () fig، ax = plt.subplots (1، figsize = (12،9)) ax.imshow (img) pad_x = max (img.shape [0] - img.shape [1] ، 0) * (img_size / max (img.shape)) pad_y = max (img.shape [1] - img.shape [0] ، 0) * (img_size / max (img.shape )) unpad_h = img_size - pad_y unpad_w = img_size - عمليات اكتشاف pad_xif ليست بلا: unique_labels = detections [:، -1] .cpu (). unique () n_cls_preds = len (unique_labels) bbox_colors = random.sample (الألوان ، n_cls_preds) # تصفح الاكتشافات ورسم المربعات المحيطة لـ x1 و y1 و x2 و y2 و conf و cls_conf و cls_pred في الاكتشافات: box_h = ((y2 - y1) / unpad_h) * img.shape [0] box_w = ((x2 - x1) / unpad_w) * i mg.shape [1] y1 = ((y1 - pad_y // 2) / unpad_h) * img.shape [0] x1 = ((x1 - pad_x // 2) / unpad_w) * img.shape [1] color = bbox_colors [int (np.where (unique_labels == int (cls_pred)) [0])] bbox = patches.Rectangle ((x1، y1)، box_w، box_h، linewidth = 2، edgecolor = color، facecolor = 'none' ) ax.add_patch (bbox) plt.text (x1، y1، s = فئات [int (cls_pred)]، color = 'white'، verticalalignment = 'top'، bbox = {'color': color، 'pad': 0}) plt.axis ('off') # save image plt.savefig (img_path.replace (". jpg"، "-det.jpg")، bbox_inches = 'tight'، pad_inches = 0.0) plt.show ()
  • يعالج النص أعلاه الحصول على الكشف عن الصور. الآن دعونا نلقي نظرة على كيفية تشغيله لمقاطع الفيديو.
videopath = "video / sample_video.mp4"
٪ pylab للاستيراد المضمن cv2 من IPython.display import clear_outputcmap = plt.get_cmap ('tab20b') الألوان = [cmap (i) [: 3] لـ i في np.linspace (0 ، 1 ، 20)] # تهيئة vid = cv2 .VideoCapture (videopath) # while (True): for ii in range (40): ret، frame = vid.read () frame = cv2.cvtColor (frame، cv2.COLOR_BGR2RGB) pilimg = Image.fromarray (frame) detection = كشف_صورة (pilimg) img = np.array (pilimg) pad_x = max (img.shape [0] - img.shape [1] ، 0) * (img_size / max (img.shape)) pad_y = max (img.shape [1] - img.shape [0] ، 0) * (img_size / max (img.shape)) unpad_h = img_size - pad_y unpad_w = img_size - pad_x إذا كانت عمليات الكشف ليست بلا: unique_labels = عمليات الكشف [:، -1]. cpu (). unique () n_cls_preds = len (unique_labels) لـ x1 و y1 و x2 و y2 و conf و cls_conf و cls_pred في الاكتشافات: box_h = int (((y2 - y1) / unpad_h) * img.shape [0] ) box_w = int (((x2 - x1) / unpad_w) * img.shape [1]) y1 = int (((y1 - pad_y // 2) / unpad_h) * img.shape [0]) x1 = int ( ((x1 - pad_x // 2) / unpad_w) * img.shape [1]) color = colors [int (cls_conf. item ())٪ len (الألوان)] color = [i * 255 for i in color] cls = class [int (cls_pred)] cv2.rectangle (frame، (x1، y1)، (x1 + box_w، y1 + box_h ) ، اللون ، 4) cv2.rectangle (الإطار ، (x1 ، y1-35) ، (x1 + len (cls) * 19 + 60 ، y1) ، اللون ، -1) cv2.putText (frame ، cls + "- "+ str (cls_conf.item ())، (x1، y1-10)، cv2.FONT_HERSHEY_SIMPLEX، 1، (255،255،255)، 3) fig = figure (figsize = (12، 8)) title (" Video Stream ") imshow (frame) show () clear_output (wait = True)
ملاحظة: يمكن استخدام الرمز أعلاه للبث المباشر أيضًا عن طريق تغيير مسار الفيديو إلى دفق كاميرا IP.

كيفية ضبط نموذج الفانيليا YOLOv3 للعمل مع الكائنات المخصصة؟

دعونا نلقي نظرة على التدريب المخصص YOLOv3 لحالة الاستخدام الخاصة بنا. هم طرق متعددة لتحقيق ذلك. لكن الخطوات التالية تشرح أسهل طريقة وجدتها على الإنترنت.

المتطلبات الأساسية

البيانات

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

هناك العديد من الأدوات لإنجاز ذلك. أسهل طريقة وجدتها هي استخدام LabelImg. إنها أداة توضيحية للصور الرسومية. يمكنك التثبيت باستخدام الأمر pip.

تسمية تثبيت نقطة

انقر على الرابط للعثور على برنامج تعليمي لطيف حول كيفية استخدامه.

يعني تسمية صورة بسيطة وضع علامات على إحداثيات وفئة المربع المحيط. لذلك ، بالنسبة لكل صورة ، سيكون للعلامة التي تم إنشاؤها (ملف .txt) سطر واحد. هذا شيء يسمى تنسيق YOLO.

ملاحظة: يرجى التأكد من تحديد تنسيق YOLO أثناء وضع علامات على الصور باستخدام أداة LabelImg.
# فئة xy عرض الارتفاع 1 0.351466 0.427083 0.367168 0.570486

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

import glob import os numpy as np import syscurrent_dir = "./data/artifacts/images" split_pct = 10 # 10٪ Validation set file_train = open ("data / artifacts / train.txt"، "w") file_val = open ( "data / artifacts / val.txt"، "w") counter = 1 index_test = round (100 / split_pct) للمسار الكامل في glob.iglob (os.path.join (current_dir، "* .JPG")): العنوان ، ext = os.path.splitext (os.path.basename (fullpath)) if counter == index_test: counter = 1 file_val.write (current_dir + "/" + title + '.JPG' + "\ n") آخر: file_train.write (current_dir + "/" + title + '.JPG' + "\ n") counter = counter + 1 file_train.close () file_val.close ()

سيؤدي هذا إلى إنشاء ملفين ، train.txt و val.txt يحتويان على المسارات الكاملة لجميع الصور بنسبة 90٪ في القطار و 10٪ في فال.

بمجرد الانتهاء ، تأكد من الحصول على مجموعة البيانات في بنية المجلد التالية.

المجلد الرئيسي --- البيانات --- اسم مجموعة البيانات --- الصور --- img1.jpg --- img2.jpg .......... --- التصنيفات --- img1.txt --- img2.txt .......... --- train.txt --- val.txt

التكوين

الآن لملفات التكوين في مجلد التكوين /. أولاً ، ستبدو coco.data على النحو التالي:

classes = 80 # عدد الفصول يذهب هنا train = data / alpha / train.txt # مسار ملف train.txt هنا صالح = data / alpha / val.txt # مسار ملف val.txt هنا الأسماء = config /coco.names # قم بتحرير ملف الأسماء مع نسخ تصنيفات احتياطية = backup / # احتفظ بهذه المعلمة كما هي

قم بتحرير هذه القيم وفقًا لمجموعة البيانات المخصصة الخاصة بك. عدّل "الفئات" بحيث تحتوي على عدد الفئات التي سيتم اكتشافها في حالة الاستخدام الخاصة بك. تدريب وصالح يحمل المسار إلى train.txt و val.txt على التوالي. قم بتحرير ملف "coco.names" باستخدام عناوين الصف. من المفترض أن تُدرج ، واحدة في كل سطر ، أسماء الفئات (لملف التعليقات التوضيحية ، الأول يقابل 0 ، بجانب 1 ، إلخ)

class1 class2 ...
ملاحظة: لم يتم استخدام معلمة النسخ الاحتياطي ولكن يبدو أنها مطلوبة.

الآن لملف yolov3.cfg. يحتوي هذا على التفاصيل المعمارية لخوارزمية YOLOv3.

في القسم الأول [net] ، اضبط قيمة الدفعة والأقسام الفرعية لتناسب ذاكرة GPU الخاصة بك. كلما كان حجم الدفعة أكبر ، كان التدريب أسرع وأفضل ، ولكن كلما زادت الذاكرة. يمكنك هنا أيضًا ضبط معدل التعلم.

بالنسبة إلى وحدة معالجة الرسومات Nvidia مع ذاكرة 11 جيجا بايت ، فإن دفعة من 16 و 1 قسم فرعي جيدة.

هناك شيئان حاسمان آخران يجب تغييرهما ، وهما قيم الطبقات والفلاتر النهائية. وعليك القيام بذلك في ثلاثة أماكن مختلفة في الملف.

إذا بحثت في الملف ، ستجد 3 أقسام [yolo]. داخل هذا القسم ، قم بتعيين الفئات إلى عدد الفئات في النموذج الخاص بك.

عليك أيضًا تغيير قيمة الفلاتر في القسم [التلافيفي] أعلى [yolo] مباشرة إلى ما يلي.

مرشحات = (فئات + 5) × 3

مع وضع كل ما سبق في المكان ، ستكون جاهزًا الآن لتدريب النموذج.

الشفرة

تنفيذ الحلقة التدريبية

  • استيراد مكتبات
من قسم الاستيراد __future__
من استيراد النماذج * من الاستخدامات. استيراد البيانات من الاستخدامات. استيراد مجموعات البيانات * من الاستخدامات.
استيراد نظام التشغيل استيراد استيراد استيراد وقت الاستيراد
استيراد الشعلة من torch.utils.data استيراد DataLoader من مجموعات بيانات استيراد torchvision من تحويلات استيراد torchvision من torch.autograd استيراد متغير استيراد torch.optim كأفضل
  • ضبط القياسات
epochs = 20 image_folder = "data / dataset / images" batch_size = 16 model_config_path = "config / yolov3.cfg" data_config_path = "config / coco.data" weights_path = "config / yolov3.weights" class_path = "config / coco.names "conf_thres = 0.8 nms_thres = 0.4 n_cpu = 0 img_size = 416 checkpoint_interval = 1 checkpoint_dir = 'checkpoints' use_cuda = True
  • استخدم CUDA إن وجد
cuda = torch.cuda.is_available () و use_cuda
  • احصل على تكوين البيانات والمعلمات في الذاكرة
# تحميل الفئات os.makedirs ("checkpoints"، موجودة_ok = True) فئات = load_classes (class_path)
# الحصول على تكوين البيانات data_config = parse_data_config (data_config_path) train_path = data_config ["train"]
# احصل على المعلمات المفرطة hyperparams = parse_model_config (model_config_path) [0] hyperparams ["burn_in"])
  • بدء النموذج وتعيينه للتدريب.
# بدء نموذج النموذج = Darknet (model_config_path) model.load_weights (weights_path)
if cuda: model = model.cuda () model.train ()
  • الحصول على محمل البيانات ، وتعيين محسن
# Get dataloader dataloader = torch.utils.data.DataLoader (ListDataset (train_path)، batch_size = batch_size، shuffle = False، num_workers = n_cpu)
Tensor = torch.cuda.FloatTensor إذا كان كودا آخر الشعلة.
# الحصول على محسن محسن = torch.optim.Adam (مرشح (لامدا p: p.requires_grad ، model.parameters ()))
  • الآن لحلقة التدريب الرئيسية.
للحقبة في النطاق (epochs): لـ batch_i ، (_ ، imgs ، target) في التعداد (dataloader): imgs = Variable (imgs.type (Tensor)) الأهداف = Variable (target.type (Tensor) ، تتطلب_grad = False)
optimizer.zero_grad ()
الخسارة = النموذج (الصور والأهداف)
loss.backward () optimizer.step ()
طباعة ("Epoch٪ d /٪ d، Batch٪ d /٪ d] [الخسائر: x٪ f، y٪ f، w٪ f، h٪ f، conf٪ f، cls٪ f، إجمالي٪ f ، استدعاء :٪ .5f ، الدقة:٪ .5f] "٪ (epoch ، epochs ، batch_i ، len (dataloader) ، model.losses [" x "] ، model.losses [" y "] ، model.losses [" w " ] ، model.losses ["h"] ، model.losses ["conf"] ، model.losses ["cls"] ، loss.item () ، model.losses ["استدعاء"] ، model.losses ["الدقة "] ،))
model.seen + = imgs.size (0)
if epoch٪ checkpoint_interval == 0: print ("save") model.save_weights ("٪ s /٪ d.weights"٪ (checkpoint_dir، "latest"))

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

تنفيذ الاستدلال

  • استيراد مكتبات
من نماذج الاستيراد * من أدوات الاستيراد * استيراد cv2 استيراد نظام التشغيل ، sys ، الوقت ، التاريخ والوقت ، استيراد الشعلة العشوائية من torch.utils.data استيراد DataLoader من مجموعات بيانات استيراد torchvision ، التحويل من torch.autograd استيراد متغير
استيراد matplotlib.pyplot كـ plt استيراد matplotlib.patches كبقع من PIL استيراد الصور استيراد imutils من imutils.video استيراد WebcamVideoStream
  • قم بتعيين المعلمات. لاستخدام النموذج الجديد للكشف ، استبدل أحدث ملف أوزان تم إنشاؤه في مجلد النماذج بملف yolov3.weights في مجلد التكوين. تأكد من أن مسار الأوزان في رمز الاستدلال يشير إلى أحدث مسار للأوزان.
num_classes = 1 config_path = 'config / yolov3.cfg' weights_path = 'checkpoint_19.weights' class_path = 'config / coco.names' img_size = 416 conf_thres = 0.95 nms_thres = 0.95
  • تحميل النموذج وتعيينه لتقييم الاستدلالات
# نموذج التحميل والأوزان = Darknet (config_path ، img_size = img_size) model.load_weights (weights_path) # model.cuda () model.eval () class = load_classes (class_path) Tensor = torch.FloatTensor
  • تحديد الوظائف لتحميل الفئات واكتشاف الصورة.
def load_classes (path): "" "تحميل تسميات الفئات عند 'path'" "" fp = open (path، "r") names = fp.read (). Split ("\ n") [:] أسماء الإرجاع
def detect_image (img): # مقياس ونسبة صورة لوح = min (img_size / img.size [0]، img_size / img.size [1]) imw = round (img.size [0] * ratio) imh = round ( img.size [1] * ratio) img_transforms = converts.Compose ([converts.Resize ((imh، imw))، converts.Pad ((max (int ((imh-imw) / 2)، 0)، max ( int ((imw-imh) / 2)، 0)، max (int ((imh-imw) / 2)، 0)، max (int ((imw-imh) / 2)، 0))، (128،128،128) )، converts.ToTensor ()،]) # تحويل الصورة إلى Tensor image_tensor = img_transforms (img) .float () image_tensor = image_tensor.unsqueeze_ (0) input_img = متغير (image_tensor.type (Tensor)) # استدلال على النموذج والحصول على الاكتشافات باستخدام torch.no_grad (): عمليات الكشف = model (input_img) Detection = utils.non_max_suppression (detections، num_classes، conf_thres، nms_thres) عمليات الكشف عن العودة [0]
  • الآن لحلقة الاستدلال.
videopath = "video / sample_video.mp4"
٪ pylab للاستيراد المضمن cv2 من IPython.display import clear_outputcmap = plt.get_cmap ('tab20b') الألوان = [cmap (i) [: 3] لـ i في np.linspace (0 ، 1 ، 20)] # تهيئة vid = cv2 .VideoCapture (videopath) # while (True): for ii in range (40): ret، frame = vid.read () frame = cv2.cvtColor (frame، cv2.COLOR_BGR2RGB) pilimg = Image.fromarray (frame) detection = كشف_صورة (pilimg) img = np.array (pilimg) pad_x = max (img.shape [0] - img.shape [1] ، 0) * (img_size / max (img.shape)) pad_y = max (img.shape [1] - img.shape [0] ، 0) * (img_size / max (img.shape)) unpad_h = img_size - pad_y unpad_w = img_size - pad_x إذا كانت عمليات الكشف ليست بلا: unique_labels = عمليات الكشف [:، -1]. cpu (). unique () n_cls_preds = len (unique_labels) لـ x1 و y1 و x2 و y2 و conf و cls_conf و cls_pred في الاكتشافات: box_h = int (((y2 - y1) / unpad_h) * img.shape [0] ) box_w = int (((x2 - x1) / unpad_w) * img.shape [1]) y1 = int (((y1 - pad_y // 2) / unpad_h) * img.shape [0]) x1 = int ( ((x1 - pad_x // 2) / unpad_w) * img.shape [1]) color = colors [int (cls_conf. item ())٪ len (الألوان)] color = [i * 255 for i in color] cls = class [int (cls_pred)] cv2.rectangle (frame، (x1، y1)، (x1 + box_w، y1 + box_h ) ، اللون ، 4) cv2.rectangle (الإطار ، (x1 ، y1-35) ، (x1 + len (cls) * 19 + 60 ، y1) ، اللون ، -1) cv2.putText (frame ، cls + "- "+ str (cls_conf.item ())، (x1، y1-10)، cv2.FONT_HERSHEY_SIMPLEX، 1، (255،255،255)، 3) fig = figure (figsize = (12، 8)) title (" Video Stream ") imshow (frame) show () clear_output (wait = True)

يرجى العثور على دفاتر jupyter لكل من التدريب والاستدلال في git-repo.

آمل أن تكون هذه السلسلة قد أعطتك صورة واضحة لكل شيء يمكن معرفته عن YOLO وتبدأ في تطبيقاتك الخاصة.

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

مصادر:

يولو: https://arxiv.org/pdf/1506.02640.pdf

YOLOv2 و YOLO9000: https://arxiv.org/pdf/1612.08242.pdf

YOLOv3: https: //arxiv.org/pdf/1804.02767.pdf

عني

أنا خبير أول في منظمة العفو الدولية في Wavelabs.ai. نحن في Wavelabs نساعدك على الاستفادة من الذكاء الاصطناعي (AI) لإحداث ثورة في تجارب المستخدم وخفض التكاليف. نحن نعزز منتجاتك بشكل فريد باستخدام الذكاء الاصطناعي للوصول إلى إمكانات السوق الكاملة الخاصة بك. نحن نحاول تقديم أحدث الأبحاث في تطبيقاتك.

لا تتردد في استكشاف المزيد في Wavelabs.ai.

حسنًا ، هذا كل شيء في هذا المنشور. شكرا للقراءة :)

ابقى فضوليا!

يمكنك التواصل معي على LinkedIn.