Ultralytics YOLO27:

YOLOv7: مجموعة المزايا المجانية القابلة للتدريب#

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

مقارنة 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)
الحجم
(بكسل)
FPSAPالاختبار / التحقق
50-95
APالاختبار
50
APالاختبار
75
APالاختبار
S
APالاختبار
M
APالاختبار
L
YOLOX-S9.026.864010240.5% / 40.5%-----
YOLOX-M25.373.86408147.2% / 46.9%-----
YOLOX-L54.2155.66406950.1% / 49.7%-----
YOLOX-X99.1281.96405851.5% / 51.1%-----
PPYOLOE-S7.917.464020843.1% / 42.7%60.5%46.6%23.2%46.4%56.9%
PPYOLOE-M23.449.964012348.9% / 48.6%66.5%53.0%28.6%52.9%63.8%
PPYOLOE-L52.2110.16407851.4% / 50.9%68.9%55.6%31.4%55.3%66.1%
PPYOLOE-X98.4206.66404552.2% / 51.9%69.9%56.5%33.3%56.3%66.4%
YOLOv5-N (r6.1)1.94.5640159- / 28.0%-----
YOLOv5-S (r6.1)7.216.5640156- / 37.4%-----
YOLOv5-M (r6.1)21.249.0640122- / 45.4%-----
YOLOv5-L (r6.1)46.5109.164099- / 49.0%-----
YOLOv5-X (r6.1)86.7205.764083- / 50.7%-----
YOLOR-CSP52.9120.464010651.1% / 50.8%69.6%55.7%31.7%55.3%64.7%
YOLOR-CSP-X96.9226.86408753.0% / 52.7%71.4%57.9%33.7%57.1%66.8%
YOLOv7-tiny-SiLU6.213.864028638.7% / 38.7%56.7%41.7%18.8%42.4%51.9%
YOLOv736.9104.764016151.4% / 51.2%69.7%55.9%31.8%55.5%65.0%
YOLOv7-X71.3189.964011453.1% / 52.9%71.2%57.8%33.8%57.1%67.4%
YOLOv5-N6 (r6.1)3.218.41280123- / 36.0%-----
YOLOv5-S6 (r6.1)12.667.21280122- / 44.8%-----
YOLOv5-M6 (r6.1)35.7200.0128090- / 51.3%-----
YOLOv5-L6 (r6.1)76.8445.6128063- / 53.7%-----
YOLOv5-X6 (r6.1)140.7839.2128038- / 55.0%-----
YOLOR-P637.2325.612807653.9% / 53.5%71.4%58.9%36.1%57.7%65.6%
YOLOR-W679.8453.212806655.2% / 54.8%72.7%60.5%37.7%59.1%67.1%
YOLOR-E6115.8683.212804555.8% / 55.7%73.4%61.1%38.4%59.7%67.7%
YOLOR-D6151.7935.612803456.5% / 56.1%74.1%61.9%38.9%60.4%68.7%
YOLOv7-W670.4360.012808454.9% / 54.6%72.6%60.1%37.3%58.7%67.1%
YOLOv7-E697.2515.212805656.0% / 55.9%73.5%61.2%38.0%59.9%68.4%
YOLOv7-D6154.7806.812804456.6% / 56.3%74.0%61.8%38.8%60.1%69.5%
YOLOv7-E6E151.7843.212803656.8% / 56.8%74.4%62.1%39.3%60.5%69.0%

نظرة عامة#

يُعد اكتشاف الأجسام في الوقت الفعلي مكوّنًا مهمًا في العديد من أنظمة الرؤية الحاسوبية، بما في ذلك تتبع الأجسام المتعددة، والقيادة الذاتية، والروبوتات، وتحليل الصور الطبية. ركز تطوير اكتشاف الأجسام في الوقت الفعلي خلال السنوات الأخيرة على تصميم بنيات فعالة وتحسين سرعة الاستدلال لمختلف وحدات CPU وGPU ووحدات المعالجة العصبية (NPUs). ويدعم YOLOv7 كلاً من أجهزة GPU المحمولة وأجهزة GPU، بدءًا من الحافة وصولًا إلى السحابة.

على خلاف كاشفات الأجسام التقليدية في الوقت الفعلي التي تركز على تحسين البنية، يقدم YOLOv7 تركيزًا على تحسين عملية التدريب. ويشمل ذلك وحدات وأساليب تحسين مصممة لرفع دقة اكتشاف الأجسام دون زيادة تكلفة الاستدلال، وهو مفهوم يُعرف باسم «مجموعة المزايا المجانية القابلة للتدريب».

الميزات الرئيسية#

يقدم YOLOv7 العديد من الميزات الأساسية:

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

  2. التعيين الديناميكي للتسميات: يطرح تدريب النموذج باستخدام طبقات إخراج متعددة مشكلة جديدة: «كيف نعيّن الأهداف الديناميكية لمخرجات الفروع المختلفة؟» ولحل هذه المشكلة، يقدم YOLOv7 طريقة جديدة لتعيين التسميات تُسمى تعيين التسميات الموجَّه من القائد من الخشن إلى الدقيق.

  3. التوسيع الممتد والمركب: يقترح YOLOv7 طريقتَي «التوسيع» و«التوسيع المركب» لكاشف الأجسام في الوقت الفعلي، بما يتيح استخدام المعلمات والحسابات بفعالية.

  4. الكفاءة: تستطيع الطريقة التي يقترحها 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:

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

    pip install ultralytics
    yolo export model=yolo26n.pt format=onnx
  2. صدّر نموذج 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
  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. ثبّت حزمة Python TensorRT:

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

    from ultralytics.utils.export import onnx2engine
    
    onnx2engine("yolov7-ultralytics.onnx", quantize=16)
  4. حمّل النموذج وشغّله في 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 مخصص وتدريبه، اتبع الخطوات التالية:

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

    • إعادة تحديد معلمات النموذج: تحسن أداء النموذج عبر تحسين مسارات انتشار التدرج.
    • التعيين الديناميكي للتسميات: يستخدم طريقة موجَّهة من القائد من الخشن إلى الدقيق لتعيين الأهداف الديناميكية للمخرجات عبر الفروع المختلفة، مما يحسن الدقة.
    • التوسيع الممتد والمركب: يستخدم المعلمات والحسابات بكفاءة لتوسيع النموذج بما يلائم تطبيقات مختلفة في الوقت الفعلي.
    • الكفاءة: يقلل عدد المعلمات بنسبة 40% والحسابات بنسبة 50% مقارنةً بنماذج SOTA الأخرى، مع تحقيق سرعات استدلال أعلى.

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

التعليقات