تخطي إلى المحتوى

YOLOv7: حقيبة من الحيل المجانية قابلة للتدريب

YOLOv7 هو أحدث كاشف للأجسام في الوقت الفعلي يتفوق على جميع كاشفات الأجسام المعروفة من حيث السرعة و الدقة في النطاق من 5 إطارات في الثانية إلى 160 إطارًا في الثانية. يتمتع بأعلى دقة (56.8٪ AP) بين جميع كاشفات الأجسام المعروفة في الوقت الفعلي بسرعة 30 إطارًا في الثانية أو أعلى على GPU V100. علاوة على ذلك، يتفوق YOLOv7 على كاشفات الأجسام الأخرى مثل YOLOR و YOLOX و Scaled-YOLOv4 و YOLOv5 وغيرها الكثير من حيث السرعة والدقة. يتم تدريب النموذج على مجموعة بيانات MS COCO من البداية دون استخدام أي مجموعات بيانات أخرى أو أوزان مُدرَّبة مسبقًا. يتوفر كود المصدر لـ YOLOv7 على GitHub.

مقارنة YOLOv7 مع أحدث أدوات الكشف عن الأجسام (SOTA)

مقارنة بين أحدث أدوات الكشف عن الأجسام (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 العديد من الميزات الرئيسية:

  1. إعادة تحديد معلمات النموذج (Model Re-parameterization): يقترح YOLOv7 نموذجًا مُعاد تحديده مُخططًا، وهي استراتيجية قابلة للتطبيق على الطبقات في شبكات مختلفة مع مفهوم مسار انتشار التدرج.

  2. تعيين التصنيفات الديناميكي: يمثل تدريب النموذج بطبقات إخراج متعددة مشكلة جديدة: "كيفية تعيين الأهداف الديناميكية لمخرجات الفروع المختلفة؟" لحل هذه المشكلة، يقدم YOLOv7 طريقة جديدة لتعيين التصنيفات تسمى تعيين التصنيفات الموجه من الخشن إلى الدقيق.

  3. التحجيم الموسع والمركب: يقترح YOLOv7 طرق "التوسيع" و"التحجيم المركب" لكاشف الكائنات في الوقت الفعلي الذي يمكنه استخدام المعلمات والحساب بكفاءة.

  4. الكفاءة: يمكن للطريقة التي اقترحها YOLOv7 أن تقلل بشكل فعال حوالي 40٪ من المعلمات و 50٪ من حساب كاشف الكائنات في الوقت الفعلي الحديث، ولديها سرعة استدلال أسرع ودقة كشف أعلى.

أمثلة الاستخدام

في وقت كتابة هذا التقرير، تدعم Ultralytics استنتاج ONNX و TensorRT فقط لـ YOLOv7.

تصدير ONNX

لاستخدام نموذج YOLOv7 ONNX مع Ultralytics:

  1. (اختياري) قم بتثبيت Ultralytics وتصدير نموذج ONNX لتثبيت التبعيات المطلوبة تلقائيًا:

    pip install ultralytics
    yolo export model=yolo11n.pt format=onnx
    
  2. قم بتصدير نموذج 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
    
  3. قم بتعديل مخطط نموذج 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")
    
  4. يمكنك بعد ذلك تحميل نموذج ONNX المعدل وتشغيل الاستدلال به في Ultralytics بشكل طبيعي:

    from ultralytics import ASSETS, YOLO
    
    model = YOLO("yolov7-ultralytics.onnx", task="detect")
    
    results = model(ASSETS / "bus.jpg")
    

تصدير TensorRT

  1. اتبع الخطوتين 1-2 في قسم تصدير ONNX.

  2. قم بتثبيت TensorRT حزمة Python:

    pip install tensorrt
    
  3. شغّل البرنامج النصي التالي لتحويل نموذج ONNX المعدل إلى محرك TensorRT:

    from ultralytics.utils.export import export_engine
    
    export_engine("yolov7-ultralytics.onnx", half=True)
    
  4. قم بتحميل وتشغيل النموذج في 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 مخصص، اتبع الخطوات التالية:

  1. استنساخ مستودع YOLOv7:
    git clone https://github.com/WongKinYiu/yolov7
    
  2. انتقل إلى الدليل المستنسخ وقم بتثبيت التبعيات:
    cd yolov7
    pip install -r requirements.txt
    
  3. قم بإعداد مجموعة البيانات الخاصة بك وتهيئة معلمات النموذج وفقًا لإرشادات الاستخدام المتوفرة في المستودع. للحصول على مزيد من الإرشادات، تفضل بزيارة مستودع YOLOv7 GitHub للحصول على أحدث المعلومات والتحديثات.

  4. بعد التدريب، يمكنك تصدير النموذج إلى ONNX أو TensorRT لاستخدامه في Ultralytics كما هو موضح في أمثلة الاستخدام.

ما هي الميزات والتحسينات الرئيسية التي تم تقديمها في YOLOv7؟

يقدم YOLOv7 العديد من الميزات الرئيسية التي تحدث ثورة في الكشف عن الأجسام في الوقت الفعلي:

  • إعادة تحديد معلمات النموذج (Model Re-parameterization): يعزز أداء النموذج عن طريق تحسين مسارات انتشار التدرج.
  • تعيين التصنيفات الديناميكي: يستخدم طريقة موجهة من الخشن إلى الدقيق لتعيين الأهداف الديناميكية للمخرجات عبر الفروع المختلفة، مما يحسن الدقة.
  • التحجيم الموسع والمركب: يستخدم المعلمات والحساب بكفاءة لتوسيع نطاق النموذج لمختلف التطبيقات في الوقت الفعلي.
  • الكفاءة: يقلل من عدد المعلمات بنسبة 40٪ والحساب بنسبة 50٪ مقارنة بالنماذج الحديثة الأخرى مع تحقيق سرعات استدلال أسرع.

لمزيد من التفاصيل حول هذه الميزات، راجع قسم نظرة عامة على YOLOv7.



📅 تم إنشاؤه منذ سنة واحدة ✏️ تم التحديث منذ شهر واحد

تعليقات