YOLOv7: مجموعة المزايا المجانية القابلة للتدريب#
كان YOLOv7، الذي أُصدر في يوليو 2022، تقدمًا مهمًا في اكتشاف الأجسام في الوقت الفعلي عند إطلاقه. وحقق 56.8% AP على GPU V100، مسجلًا معايير جديدة عند تقديمه. وتفوق YOLOv7 على كاشفات الأجسام المعاصرة مثل YOLOR وYOLOX وScaled-YOLOv4 وYOLOv5 من حيث السرعة والدقة. دُرِّب النموذج على مجموعة بيانات MS COCO من الصفر دون استخدام أي مجموعات بيانات أخرى أو أوزان مدرَّبة مسبقًا. تتوفر الشفرة المصدرية لـ YOLOv7 على GitHub. تجدر الإشارة إلى أن نماذج أحدث مثل YOLO11 وYOLO26 حققت منذ ذلك الحين دقة أعلى مع كفاءة محسّنة.

مقارنة كاشفات الأجسام SOTA#
نعلم من النتائج الواردة في جدول مقارنة YOLO أن الطريقة المقترحة تحقق أفضل مفاضلة شاملة بين السرعة والدقة. إذا قارنا YOLOv7-tiny-SiLU بـ YOLOv5-N (r6.1)، فإن طريقتنا أسرع بمقدار 127 إطارًا في الثانية وأكثر دقة بنسبة 10.7% وفق AP. بالإضافة إلى ذلك، يحقق YOLOv7 مقدار 51.4% AP بمعدل إطارات يبلغ 161 إطارًا في الثانية، بينما لا يحقق PPYOLOE-L، مع AP نفسه، سوى معدل 78 إطارًا في الثانية. ومن حيث استخدام المعلمات، يقل YOLOv7 بنسبة 41% عن PPYOLOE-L.
إذا قارنا YOLOv7-X بسرعة استدلال تبلغ 114 إطارًا في الثانية بـ YOLOv5-L (r6.1) بسرعة استدلال تبلغ 99 إطارًا في الثانية، فيمكن لـ YOLOv7-X تحسين AP بنسبة 3.9%. وإذا قورن YOLOv7-X بـ YOLOv5-X (r6.1) ذي الحجم المماثل، فإن سرعة استدلال YOLOv7-X تزيد بمقدار 31 إطارًا في الثانية. بالإضافة إلى ذلك، ومن حيث عدد المعلمات والحسابات، يقلل YOLOv7-X المعلمات بنسبة 22% والحسابات بنسبة 8% مقارنةً بـ YOLOv5-X (r6.1)، مع تحسين AP بنسبة 2.2% (المصدر).
| النموذج | المعلمات (M) | FLOPs (G) | الحجم (بكسل) | FPS | APالاختبار / التحقق 50-95 | APالاختبار 50 | APالاختبار 75 | APالاختبار S | APالاختبار M | APالاختبار L |
|---|---|---|---|---|---|---|---|---|---|---|
| YOLOX-S | 9.0 | 26.8 | 640 | 102 | 40.5% / 40.5% | - | - | - | - | - |
| YOLOX-M | 25.3 | 73.8 | 640 | 81 | 47.2% / 46.9% | - | - | - | - | - |
| YOLOX-L | 54.2 | 155.6 | 640 | 69 | 50.1% / 49.7% | - | - | - | - | - |
| YOLOX-X | 99.1 | 281.9 | 640 | 58 | 51.5% / 51.1% | - | - | - | - | - |
| PPYOLOE-S | 7.9 | 17.4 | 640 | 208 | 43.1% / 42.7% | 60.5% | 46.6% | 23.2% | 46.4% | 56.9% |
| PPYOLOE-M | 23.4 | 49.9 | 640 | 123 | 48.9% / 48.6% | 66.5% | 53.0% | 28.6% | 52.9% | 63.8% |
| PPYOLOE-L | 52.2 | 110.1 | 640 | 78 | 51.4% / 50.9% | 68.9% | 55.6% | 31.4% | 55.3% | 66.1% |
| PPYOLOE-X | 98.4 | 206.6 | 640 | 45 | 52.2% / 51.9% | 69.9% | 56.5% | 33.3% | 56.3% | 66.4% |
| YOLOv5-N (r6.1) | 1.9 | 4.5 | 640 | 159 | - / 28.0% | - | - | - | - | - |
| YOLOv5-S (r6.1) | 7.2 | 16.5 | 640 | 156 | - / 37.4% | - | - | - | - | - |
| YOLOv5-M (r6.1) | 21.2 | 49.0 | 640 | 122 | - / 45.4% | - | - | - | - | - |
| YOLOv5-L (r6.1) | 46.5 | 109.1 | 640 | 99 | - / 49.0% | - | - | - | - | - |
| YOLOv5-X (r6.1) | 86.7 | 205.7 | 640 | 83 | - / 50.7% | - | - | - | - | - |
| YOLOR-CSP | 52.9 | 120.4 | 640 | 106 | 51.1% / 50.8% | 69.6% | 55.7% | 31.7% | 55.3% | 64.7% |
| YOLOR-CSP-X | 96.9 | 226.8 | 640 | 87 | 53.0% / 52.7% | 71.4% | 57.9% | 33.7% | 57.1% | 66.8% |
| YOLOv7-tiny-SiLU | 6.2 | 13.8 | 640 | 286 | 38.7% / 38.7% | 56.7% | 41.7% | 18.8% | 42.4% | 51.9% |
| YOLOv7 | 36.9 | 104.7 | 640 | 161 | 51.4% / 51.2% | 69.7% | 55.9% | 31.8% | 55.5% | 65.0% |
| YOLOv7-X | 71.3 | 189.9 | 640 | 114 | 53.1% / 52.9% | 71.2% | 57.8% | 33.8% | 57.1% | 67.4% |
| YOLOv5-N6 (r6.1) | 3.2 | 18.4 | 1280 | 123 | - / 36.0% | - | - | - | - | - |
| YOLOv5-S6 (r6.1) | 12.6 | 67.2 | 1280 | 122 | - / 44.8% | - | - | - | - | - |
| YOLOv5-M6 (r6.1) | 35.7 | 200.0 | 1280 | 90 | - / 51.3% | - | - | - | - | - |
| YOLOv5-L6 (r6.1) | 76.8 | 445.6 | 1280 | 63 | - / 53.7% | - | - | - | - | - |
| YOLOv5-X6 (r6.1) | 140.7 | 839.2 | 1280 | 38 | - / 55.0% | - | - | - | - | - |
| YOLOR-P6 | 37.2 | 325.6 | 1280 | 76 | 53.9% / 53.5% | 71.4% | 58.9% | 36.1% | 57.7% | 65.6% |
| YOLOR-W6 | 79.8 | 453.2 | 1280 | 66 | 55.2% / 54.8% | 72.7% | 60.5% | 37.7% | 59.1% | 67.1% |
| YOLOR-E6 | 115.8 | 683.2 | 1280 | 45 | 55.8% / 55.7% | 73.4% | 61.1% | 38.4% | 59.7% | 67.7% |
| YOLOR-D6 | 151.7 | 935.6 | 1280 | 34 | 56.5% / 56.1% | 74.1% | 61.9% | 38.9% | 60.4% | 68.7% |
| YOLOv7-W6 | 70.4 | 360.0 | 1280 | 84 | 54.9% / 54.6% | 72.6% | 60.1% | 37.3% | 58.7% | 67.1% |
| YOLOv7-E6 | 97.2 | 515.2 | 1280 | 56 | 56.0% / 55.9% | 73.5% | 61.2% | 38.0% | 59.9% | 68.4% |
| YOLOv7-D6 | 154.7 | 806.8 | 1280 | 44 | 56.6% / 56.3% | 74.0% | 61.8% | 38.8% | 60.1% | 69.5% |
| YOLOv7-E6E | 151.7 | 843.2 | 1280 | 36 | 56.8% / 56.8% | 74.4% | 62.1% | 39.3% | 60.5% | 69.0% |
نظرة عامة#
يُعد اكتشاف الأجسام في الوقت الفعلي مكوّنًا مهمًا في العديد من أنظمة الرؤية الحاسوبية، بما في ذلك تتبع الأجسام المتعددة، والقيادة الذاتية، والروبوتات، وتحليل الصور الطبية. ركز تطوير اكتشاف الأجسام في الوقت الفعلي خلال السنوات الأخيرة على تصميم بنيات فعالة وتحسين سرعة الاستدلال لمختلف وحدات CPU وGPU ووحدات المعالجة العصبية (NPUs). ويدعم YOLOv7 كلاً من أجهزة GPU المحمولة وأجهزة GPU، بدءًا من الحافة وصولًا إلى السحابة.
على خلاف كاشفات الأجسام التقليدية في الوقت الفعلي التي تركز على تحسين البنية، يقدم YOLOv7 تركيزًا على تحسين عملية التدريب. ويشمل ذلك وحدات وأساليب تحسين مصممة لرفع دقة اكتشاف الأجسام دون زيادة تكلفة الاستدلال، وهو مفهوم يُعرف باسم «مجموعة المزايا المجانية القابلة للتدريب».
الميزات الرئيسية#
يقدم YOLOv7 العديد من الميزات الأساسية:
-
إعادة تحديد معلمات النموذج: يقترح YOLOv7 نموذجًا مخططًا لإعادة تحديد المعلمات، وهو استراتيجية قابلة للتطبيق على الطبقات في شبكات مختلفة، مستندة إلى مفهوم مسار انتشار التدرج.
-
التعيين الديناميكي للتسميات: يطرح تدريب النموذج باستخدام طبقات إخراج متعددة مشكلة جديدة: «كيف نعيّن الأهداف الديناميكية لمخرجات الفروع المختلفة؟» ولحل هذه المشكلة، يقدم YOLOv7 طريقة جديدة لتعيين التسميات تُسمى تعيين التسميات الموجَّه من القائد من الخشن إلى الدقيق.
-
التوسيع الممتد والمركب: يقترح YOLOv7 طريقتَي «التوسيع» و«التوسيع المركب» لكاشف الأجسام في الوقت الفعلي، بما يتيح استخدام المعلمات والحسابات بفعالية.
-
الكفاءة: تستطيع الطريقة التي يقترحها YOLOv7 تقليل المعلمات بنحو 40% والحسابات بنحو 50% في كاشف الأجسام SOTA في الوقت الفعلي، مع تحقيق سرعة استدلال أعلى ودقة اكتشاف أكبر.
أمثلة على الاستخدام#
لا تنشر Ultralytics أوزان yolov7.pt المدرَّبة مسبقًا أو ملفات YAML ultralytics/cfg/models/v7/، كما أن تدريب YOLOv7 واستدلاله الأصليين باستخدام PyTorch غير مدعومين في حزمة Ultralytics Python. ومع ذلك، يمكنك إدخال نقطة تحقق YOLOv7 المدرَّبة في مستودع YOLOv7 الأساسي إلى Ultralytics عبر تصديرها إلى ONNX أو TensorRT، كما هو موضح أدناه.
تصدير ONNX#
لاستخدام نموذج YOLOv7 بصيغة ONNX مع Ultralytics:
-
(اختياري) ثبّت Ultralytics وصدّر نموذج ONNX لتثبيت التبعيات المطلوبة تلقائيًا:
pip install ultralytics yolo export model=yolo26n.pt format=onnx -
صدّر نموذج YOLOv7 المطلوب باستخدام أداة التصدير في مستودع YOLOv7:
git clone https://github.com/WongKinYiu/yolov7 cd yolov7 python export.py --weights yolov7-tiny.pt --grid --end2end --simplify --topk-all 100 --iou-thres 0.65 --conf-thres 0.35 --img-size 640 640 --max-wh 640 -
عدّل مخطط نموذج ONNX ليصبح متوافقًا مع Ultralytics باستخدام البرنامج النصي التالي:
import numpy as np import onnx from onnx import helper, numpy_helper # Load the ONNX model model_path = "yolov7/yolov7-tiny.onnx" # Replace with your model path model = onnx.load(model_path) graph = model.graph # Fix input shape to batch size 1 input_shape = graph.input[0].type.tensor_type.shape input_shape.dim[0].dim_value = 1 # Define the output of the original model original_output_name = graph.output[0].name # Create slicing nodes sliced_output_name = f"{original_output_name}_sliced" # Define initializers for slicing (remove the first value) start = numpy_helper.from_array(np.array([1], dtype=np.int64), name="slice_start") end = numpy_helper.from_array(np.array([7], dtype=np.int64), name="slice_end") axes = numpy_helper.from_array(np.array([1], dtype=np.int64), name="slice_axes") steps = numpy_helper.from_array(np.array([1], dtype=np.int64), name="slice_steps") graph.initializer.extend([start, end, axes, steps]) slice_node = helper.make_node( "Slice", inputs=[original_output_name, "slice_start", "slice_end", "slice_axes", "slice_steps"], outputs=[sliced_output_name], name="SliceNode", ) graph.node.append(slice_node) # Define segment slicing seg1_start = numpy_helper.from_array(np.array([0], dtype=np.int64), name="seg1_start") seg1_end = numpy_helper.from_array(np.array([4], dtype=np.int64), name="seg1_end") seg2_start = numpy_helper.from_array(np.array([4], dtype=np.int64), name="seg2_start") seg2_end = numpy_helper.from_array(np.array([5], dtype=np.int64), name="seg2_end") seg3_start = numpy_helper.from_array(np.array([5], dtype=np.int64), name="seg3_start") seg3_end = numpy_helper.from_array(np.array([6], dtype=np.int64), name="seg3_end") graph.initializer.extend([seg1_start, seg1_end, seg2_start, seg2_end, seg3_start, seg3_end]) # Create intermediate tensors for segments segment_1_name = f"{sliced_output_name}_segment1" segment_2_name = f"{sliced_output_name}_segment2" segment_3_name = f"{sliced_output_name}_segment3" # Add segment slicing nodes graph.node.extend( [ helper.make_node( "Slice", inputs=[sliced_output_name, "seg1_start", "seg1_end", "slice_axes", "slice_steps"], outputs=[segment_1_name], name="SliceSegment1", ), helper.make_node( "Slice", inputs=[sliced_output_name, "seg2_start", "seg2_end", "slice_axes", "slice_steps"], outputs=[segment_2_name], name="SliceSegment2", ), helper.make_node( "Slice", inputs=[sliced_output_name, "seg3_start", "seg3_end", "slice_axes", "slice_steps"], outputs=[segment_3_name], name="SliceSegment3", ), ] ) # Concatenate the segments concat_output_name = f"{sliced_output_name}_concat" concat_node = helper.make_node( "Concat", inputs=[segment_1_name, segment_3_name, segment_2_name], outputs=[concat_output_name], axis=1, name="ConcatSwapped", ) graph.node.append(concat_node) # Reshape to [1, -1, 6] reshape_shape = numpy_helper.from_array(np.array([1, -1, 6], dtype=np.int64), name="reshape_shape") graph.initializer.append(reshape_shape) final_output_name = f"{concat_output_name}_batched" reshape_node = helper.make_node( "Reshape", inputs=[concat_output_name, "reshape_shape"], outputs=[final_output_name], name="AddBatchDimension", ) graph.node.append(reshape_node) # Get the shape of the reshaped tensor shape_node_name = f"{final_output_name}_shape" shape_node = helper.make_node( "Shape", inputs=[final_output_name], outputs=[shape_node_name], name="GetShapeDim", ) graph.node.append(shape_node) # Extract the second dimension dim_1_index = numpy_helper.from_array(np.array([1], dtype=np.int64), name="dim_1_index") graph.initializer.append(dim_1_index) second_dim_name = f"{final_output_name}_dim1" gather_node = helper.make_node( "Gather", inputs=[shape_node_name, "dim_1_index"], outputs=[second_dim_name], name="GatherSecondDim", ) graph.node.append(gather_node) # Subtract from 100 to determine how many values to pad target_size = numpy_helper.from_array(np.array([100], dtype=np.int64), name="target_size") graph.initializer.append(target_size) pad_size_name = f"{second_dim_name}_padsize" sub_node = helper.make_node( "Sub", inputs=["target_size", second_dim_name], outputs=[pad_size_name], name="CalculatePadSize", ) graph.node.append(sub_node) # Build the [2, 3] pad array: # 1st row -> [0, 0, 0] (no padding at the start of any dim) # 2nd row -> [0, pad_size, 0] (pad only at the end of the second dim) pad_starts = numpy_helper.from_array(np.array([0, 0, 0], dtype=np.int64), name="pad_starts") graph.initializer.append(pad_starts) zero_scalar = numpy_helper.from_array(np.array([0], dtype=np.int64), name="zero_scalar") graph.initializer.append(zero_scalar) pad_ends_name = "pad_ends" concat_pad_ends_node = helper.make_node( "Concat", inputs=["zero_scalar", pad_size_name, "zero_scalar"], outputs=[pad_ends_name], axis=0, name="ConcatPadEnds", ) graph.node.append(concat_pad_ends_node) pad_values_name = "pad_values" concat_pad_node = helper.make_node( "Concat", inputs=["pad_starts", pad_ends_name], outputs=[pad_values_name], axis=0, name="ConcatPadStartsEnds", ) graph.node.append(concat_pad_node) # Create Pad operator to pad with zeros pad_output_name = f"{final_output_name}_padded" pad_constant_value = numpy_helper.from_array( np.array([0.0], dtype=np.float32), name="pad_constant_value", ) graph.initializer.append(pad_constant_value) pad_node = helper.make_node( "Pad", inputs=[final_output_name, pad_values_name, "pad_constant_value"], outputs=[pad_output_name], mode="constant", name="PadToFixedSize", ) graph.node.append(pad_node) # Update the graph's final output to [1, 100, 6] new_output_type = onnx.helper.make_tensor_type_proto( elem_type=graph.output[0].type.tensor_type.elem_type, shape=[1, 100, 6] ) new_output = onnx.helper.make_value_info(name=pad_output_name, type_proto=new_output_type) # Replace the old output with the new one graph.output.pop() graph.output.extend([new_output]) # Save the modified model onnx.save(model, "yolov7-ultralytics.onnx") -
يمكنك بعد ذلك تحميل نموذج ONNX المعدّل وتشغيل الاستدلال به في Ultralytics بالطريقة المعتادة:
from ultralytics import ASSETS, YOLO model = YOLO("yolov7-ultralytics.onnx", task="detect") results = model(ASSETS / "bus.jpg")
تصدير TensorRT#
-
اتبع الخطوتين 1 و2 في قسم تصدير ONNX.
-
ثبّت حزمة Python
TensorRT:pip install tensorrt -
شغّل البرنامج النصي التالي لتحويل نموذج ONNX المعدّل إلى محرك TensorRT:
from ultralytics.utils.export import onnx2engine onnx2engine("yolov7-ultralytics.onnx", quantize=16) -
حمّل النموذج وشغّله في Ultralytics:
from ultralytics import ASSETS, YOLO model = YOLO("yolov7-ultralytics.engine", task="detect") results = model(ASSETS / "bus.jpg")
الاقتباسات والشكر والتقدير#
نود الإشادة بمؤلفي YOLOv7 لإسهاماتهم المهمة في مجال اكتشاف الأجسام في الوقت الفعلي:
@inproceedings{wang2023yolov7,
title={YOLOv7: Trainable Bag-of-Freebies Sets New State-of-the-Art for Real-Time Object Detectors},
author={Wang, Chien-Yao and Bochkovskiy, Alexey and Liao, Hong-Yuan Mark},
booktitle={Proceedings of the IEEE/CVF conference on Computer Vision and Pattern Recognition (CVPR)},
pages={7464--7475},
year={2023}
}نُشرت الورقة الرسمية لـ YOLOv7 في CVF 2023 Open Access، مع نسخة أولية على arXiv. وقد أتاح المؤلفون عملهم للعامة، ويمكن الوصول إلى قاعدة الشفرة على GitHub. نثمّن جهودهم في تطوير هذا المجال وإتاحة عملهم للمجتمع الأوسع.
الأسئلة الشائعة#
كان YOLOv7، الذي أُصدر في يوليو 2022، نموذجًا مهمًا لاكتشاف الأجسام في الوقت الفعلي، إذ حقق سرعة ودقة ممتازتين عند إطلاقه. وتفوق على نماذج معاصرة مثل YOLOX وYOLOv5 وPPYOLOE من حيث استخدام المعلمات وسرعة الاستدلال معًا. وتشمل ميزات YOLOv7 المميزة إعادة تحديد معلمات النموذج والتعيين الديناميكي للتسميات، اللذين يحسّنان أداءه دون زيادة تكاليف الاستدلال. لمزيد من التفاصيل التقنية حول بنيته ومقاييس مقارنته بكاشفات الأجسام SOTA الأخرى، راجع ورقة YOLOv7.
يقدم YOLOv7 عدة ابتكارات، منها إعادة تحديد معلمات النموذج والتعيين الديناميكي للتسميات، مما يعزز عملية التدريب ويحسن دقة الاستدلال. وبالمقارنة مع YOLOv5، يعزز YOLOv7 السرعة والدقة بدرجة كبيرة. فعلى سبيل المثال، يحسن YOLOv7-X الدقة بنسبة 2.2% ويقلل المعلمات بنسبة 22% مقارنةً بـ YOLOv5-X. ويمكن العثور على مقارنات مفصلة في جدول الأداء مقارنة YOLOv7 بكاشفات الأجسام SOTA.
في الوقت الحالي، تدعم Ultralytics استدلال YOLOv7 بصيغتَي ONNX وTensorRT فقط. لتشغيل إصدار YOLOv7 المصدَّر بصيغتَي ONNX وTensorRT مع Ultralytics، راجع قسم أمثلة الاستخدام.
لتثبيت نموذج YOLOv7 مخصص وتدريبه، اتبع الخطوات التالية:
-
استنسخ مستودع YOLOv7:
git clone https://github.com/WongKinYiu/yolov7 -
انتقل إلى الدليل المستنسخ وثبّت التبعيات:
cd yolov7 pip install -r requirements.txt -
جهّز مجموعة بياناتك واضبط معلمات النموذج وفقًا لـتعليمات الاستخدام المقدمة في المستودع. ولمزيد من الإرشادات، زُر مستودع YOLOv7 على GitHub للاطلاع على أحدث المعلومات والتحديثات.
-
بعد التدريب، يمكنك تصدير النموذج إلى ONNX أو TensorRT لاستخدامه في Ultralytics كما هو موضح في أمثلة الاستخدام.
-
يوفر YOLOv7 العديد من الميزات الأساسية التي تحدث تحولًا في اكتشاف الأجسام في الوقت الفعلي:
- إعادة تحديد معلمات النموذج: تحسن أداء النموذج عبر تحسين مسارات انتشار التدرج.
- التعيين الديناميكي للتسميات: يستخدم طريقة موجَّهة من القائد من الخشن إلى الدقيق لتعيين الأهداف الديناميكية للمخرجات عبر الفروع المختلفة، مما يحسن الدقة.
- التوسيع الممتد والمركب: يستخدم المعلمات والحسابات بكفاءة لتوسيع النموذج بما يلائم تطبيقات مختلفة في الوقت الفعلي.
- الكفاءة: يقلل عدد المعلمات بنسبة 40% والحسابات بنسبة 50% مقارنةً بنماذج SOTA الأخرى، مع تحقيق سرعات استدلال أعلى.
لمزيد من التفاصيل حول هذه الميزات، راجع قسم نظرة عامة على YOLOv7.