YOLOv7: حقيبة من الحيل المجانية قابلة للتدريب
YOLOv7 هو أحدث كاشف للأجسام في الوقت الفعلي يتفوق على جميع كاشفات الأجسام المعروفة من حيث السرعة و الدقة في النطاق من 5 إطارات في الثانية إلى 160 إطارًا في الثانية. يتمتع بأعلى دقة (56.8٪ AP) بين جميع كاشفات الأجسام المعروفة في الوقت الفعلي بسرعة 30 إطارًا في الثانية أو أعلى على GPU V100. علاوة على ذلك، يتفوق YOLOv7 على كاشفات الأجسام الأخرى مثل YOLOR و YOLOX و Scaled-YOLOv4 و YOLOv5 وغيرها الكثير من حيث السرعة والدقة. يتم تدريب النموذج على مجموعة بيانات MS COCO من البداية دون استخدام أي مجموعات بيانات أخرى أو أوزان مُدرَّبة مسبقًا. يتوفر كود المصدر لـ YOLOv7 على GitHub.
مقارنة بين أحدث أدوات الكشف عن الأجسام (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% |
نظرة عامة
يعد الكشف عن الأجسام في الوقت الفعلي مكونًا مهمًا في العديد من أنظمة رؤية الكمبيوتر، بما في ذلك تتبع الأجسام المتعددة، والقيادة الذاتية، و الروبوتات، و تحليل الصور الطبية. في السنوات الأخيرة، ركز تطوير الكشف عن الأجسام في الوقت الفعلي على تصميم هياكل فعالة وتحسين سرعة الاستدلال لوحدات المعالجة المركزية (CPUs) ووحدات معالجة الرسومات (GPUs) ووحدات المعالجة العصبية (NPUs) المختلفة. يدعم YOLOv7 كلاً من أجهزة GPU المحمولة وأجهزة GPU، من الحافة إلى السحابة.
على عكس كاشفات الكائنات في الوقت الفعلي التقليدية التي تركز على تحسين الهيكلة، يقدم YOLOv7 تركيزًا على تحسين عملية التدريب. يتضمن ذلك الوحدات وطرق التحسين المصممة لتحسين دقة الكشف عن الكائنات دون زيادة تكلفة الاستدلال، وهو مفهوم يُعرف باسم "حقيبة الحيل المجانية القابلة للتدريب".
الميزات الرئيسية
يقدم YOLOv7 العديد من الميزات الرئيسية:
-
إعادة تحديد معلمات النموذج (Model Re-parameterization): يقترح YOLOv7 نموذجًا مُعاد تحديده مُخططًا، وهي استراتيجية قابلة للتطبيق على الطبقات في شبكات مختلفة مع مفهوم مسار انتشار التدرج.
-
تعيين التصنيفات الديناميكي: يمثل تدريب النموذج بطبقات إخراج متعددة مشكلة جديدة: "كيفية تعيين الأهداف الديناميكية لمخرجات الفروع المختلفة؟" لحل هذه المشكلة، يقدم YOLOv7 طريقة جديدة لتعيين التصنيفات تسمى تعيين التصنيفات الموجه من الخشن إلى الدقيق.
-
التحجيم الموسع والمركب: يقترح YOLOv7 طرق "التوسيع" و"التحجيم المركب" لكاشف الكائنات في الوقت الفعلي الذي يمكنه استخدام المعلمات والحساب بكفاءة.
-
الكفاءة: يمكن للطريقة التي اقترحها YOLOv7 أن تقلل بشكل فعال حوالي 40٪ من المعلمات و 50٪ من حساب كاشف الكائنات في الوقت الفعلي الحديث، ولديها سرعة استدلال أسرع ودقة كشف أعلى.
أمثلة الاستخدام
في وقت كتابة هذا التقرير، تدعم Ultralytics استنتاج ONNX و TensorRT فقط لـ YOLOv7.
تصدير ONNX
لاستخدام نموذج YOLOv7 ONNX مع Ultralytics:
-
(اختياري) قم بتثبيت Ultralytics وتصدير نموذج ONNX لتثبيت التبعيات المطلوبة تلقائيًا:
pip install ultralytics yolo export model=yolo11n.pt format=onnx
-
قم بتصدير نموذج YOLOv7 المطلوب باستخدام أداة التصدير في YOLOv7 repo:
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.
-
قم بتثبيت
TensorRT
حزمة Python:pip install tensorrt
-
شغّل البرنامج النصي التالي لتحويل نموذج ONNX المعدل إلى محرك TensorRT:
from ultralytics.utils.export import export_engine export_engine("yolov7-ultralytics.onnx", half=True)
-
قم بتحميل وتشغيل النموذج في Ultralytics:
from ultralytics import ASSETS, YOLO model = YOLO("yolov7-ultralytics.engine", task="detect") results = model(ASSETS / "bus.jpg")
الاقتباسات والشكر والتقدير
نود أن نعرب عن تقديرنا لمؤلفي YOLOv7 لمساهماتهم الكبيرة في مجال الكشف عن الأجسام في الوقت الفعلي:
@article{wang2022yolov7,
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},
journal={arXiv preprint arXiv:2207.02696},
year={2022}
}
يمكن العثور على ورقة YOLOv7 الأصلية على arXiv. وقد أتاح المؤلفون أعمالهم للجمهور، ويمكن الوصول إلى قاعدة التعليمات البرمجية على GitHub. نحن نقدر جهودهم في تطوير هذا المجال وإتاحة أعمالهم للمجتمع الأوسع.
الأسئلة الشائعة
ما هو YOLOv7 ولماذا يعتبر طفرة في الكشف عن الأجسام في الوقت الفعلي؟
YOLOv7 هو نموذج متطور للكشف عن الكائنات في الوقت الفعلي يحقق سرعة ودقة لا مثيل لهما. يتفوق على النماذج الأخرى، مثل YOLOX و YOLOv5 و PPYOLOE، في كل من استخدام المعلمات وسرعة الاستدلال. تتضمن الميزات المميزة لـ YOLOv7 إعادة تحديد معلمات النموذج وتعيين الملصقات الديناميكي، مما يحسن أدائه دون زيادة تكاليف الاستدلال. لمزيد من التفاصيل التقنية حول بنيته ومقاييس المقارنة مع كاشفات الكائنات الحديثة الأخرى، راجع ورقة YOLOv7.
كيف يحسن YOLOv7 نماذج YOLO السابقة مثل YOLOv4 و YOLOv5؟
يقدم YOLOv7 العديد من الابتكارات، بما في ذلك إعادة تحديد معلمات النموذج وتعيين الملصقات الديناميكي، مما يعزز عملية التدريب ويحسن دقة الاستدلال. بالمقارنة مع YOLOv5، يعزز YOLOv7 السرعة والدقة بشكل كبير. على سبيل المثال، يحسن YOLOv7-X الدقة بنسبة 2.2٪ ويقلل المعلمات بنسبة 22٪ مقارنة بـ YOLOv5-X. يمكن العثور على مقارنات تفصيلية في جدول الأداء مقارنة YOLOv7 مع كاشفات الكائنات SOTA.
هل يمكنني استخدام YOLOv7 مع أدوات ومنصات Ultralytics؟
حتى الآن، تدعم Ultralytics استنتاج YOLOv7 ONNX و TensorRT فقط. لتشغيل إصدار YOLOv7 المصدر بتنسيق ONNX و TensorRT باستخدام Ultralytics، تحقق من قسم أمثلة الاستخدام.
كيف يمكنني تدريب نموذج YOLOv7 مخصص باستخدام مجموعة البيانات الخاصة بي؟
لتثبيت وتدريب نموذج YOLOv7 مخصص، اتبع الخطوات التالية:
- استنساخ مستودع YOLOv7:
git clone https://github.com/WongKinYiu/yolov7
- انتقل إلى الدليل المستنسخ وقم بتثبيت التبعيات:
cd yolov7 pip install -r requirements.txt
-
قم بإعداد مجموعة البيانات الخاصة بك وتهيئة معلمات النموذج وفقًا لإرشادات الاستخدام المتوفرة في المستودع. للحصول على مزيد من الإرشادات، تفضل بزيارة مستودع YOLOv7 GitHub للحصول على أحدث المعلومات والتحديثات.
-
بعد التدريب، يمكنك تصدير النموذج إلى ONNX أو TensorRT لاستخدامه في Ultralytics كما هو موضح في أمثلة الاستخدام.
ما هي الميزات والتحسينات الرئيسية التي تم تقديمها في YOLOv7؟
يقدم YOLOv7 العديد من الميزات الرئيسية التي تحدث ثورة في الكشف عن الأجسام في الوقت الفعلي:
- إعادة تحديد معلمات النموذج (Model Re-parameterization): يعزز أداء النموذج عن طريق تحسين مسارات انتشار التدرج.
- تعيين التصنيفات الديناميكي: يستخدم طريقة موجهة من الخشن إلى الدقيق لتعيين الأهداف الديناميكية للمخرجات عبر الفروع المختلفة، مما يحسن الدقة.
- التحجيم الموسع والمركب: يستخدم المعلمات والحساب بكفاءة لتوسيع نطاق النموذج لمختلف التطبيقات في الوقت الفعلي.
- الكفاءة: يقلل من عدد المعلمات بنسبة 40٪ والحساب بنسبة 50٪ مقارنة بالنماذج الحديثة الأخرى مع تحقيق سرعات استدلال أسرع.
لمزيد من التفاصيل حول هذه الميزات، راجع قسم نظرة عامة على YOLOv7.