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 | APtest / val 50-95 | APtest 50 | APtest 75 | APtest S | APtest M | APtest 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% |
نظرة عامة
يعد الكشف عن الكائنات في الوقت الفعلي مكونًا مهمًا في العديد من أنظمة رؤية الكمبيوتر، بما في ذلك تتبع متعدد الكائنات، والقيادة الذاتية، والروبوتات، وتحليل الصور الطبية. في السنوات الأخيرة، ركز تطوير الكشف عن الكائنات في الوقت الفعلي على تصميم معماريات فعالة وتحسين سرعة الاستدلال لوحدات المعالجة المركزية ووحدات معالجة الرسومات ووحدات المعالجة العصبية المختلفة (NPUs). يدعم YOLOv7 كلاً من أجهزة GPU المحمولة وأجهزة GPU، من الحافة إلى السحابة.
بخلاف كاشفات الكائنات في الوقت الفعلي التقليدية التي تركز على تحسين الهندسة المعمارية، يقدم YOLOv7 تركيزًا على تحسين عملية التدريب. يتضمن ذلك وحدات وأساليب تحسين مصممة لتحسين دقة اكتشاف الكائنات دون زيادة تكلفة الاستدلال، وهو مفهوم يعرف باسم "حقيبة الحيل المجانية القابلة للتدريب".
الميزات الرئيسية
يقدم YOLOv7 العديد من الميزات الرئيسية:
إعادة تحديد معلمات النموذج: يقترح YOLOv7 نموذجًا مُعاد تحديده مُخطط له، وهي إستراتيجية قابلة للتطبيق على الطبقات في الشبكات المختلفة مع مفهوم مسار انتشار التدرج.
تعيين تسمية ديناميكية: يطرح تدريب النموذج بطبقات إخراج متعددة مشكلة جديدة: "كيفية تعيين أهداف ديناميكية لمخرجات الفروع المختلفة؟" لحل هذه المشكلة، يقدم YOLOv7 طريقة جديدة لتعيين التسميات تسمى تعيين التسميات الموجه من الخشن إلى الدقيق.
التحجيم الممتد والمركب: تقترح YOLOv7 طرق "التحجيم الممتد" و"التحجيم المركب" لكاشف الكائنات في الوقت الفعلي الذي يمكنه استخدام المعلمات والحساب بشكل فعال.
الكفاءة: يمكن للطريقة التي اقترحها YOLOv7 أن تقلل بشكل فعال حوالي 40٪ من المعلمات و 50٪ من حسابات أحدث أجهزة الكشف عن الكائنات في الوقت الفعلي، ولديها سرعة استدلال أسرع ودقة detect أعلى.
أمثلة الاستخدام
حتى وقت كتابة هذه السطور، تدعم Ultralytics استدلال ONNX و TensorRT فقط لـ YOLOv7.
تصدير ONNX
لاستخدام نموذج YOLOv7 ONNX مع Ultralytics:
(اختياري) قم بتثبيت Ultralytics وتصدير نموذج ONNX لتثبيت التبعيات المطلوبة تلقائيًا:
pip install ultralytics yolo export model=yolo11n.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.
قم بتثبيت
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.