YOLOv7: Trainable Bag-of-Freebies

YOLOv7, veröffentlicht im Juli 2022, stellte zum Zeitpunkt seiner Einführung einen bedeutenden Fortschritt bei der Echtzeit-Objekterkennung dar. Es erreichte 56,8 % AP auf einer GPU V100 und setzte damit neue Maßstäbe. YOLOv7 übertraf zeitgenössische Objektdetektoren wie YOLOR, YOLOX, Scaled-YOLOv4 und YOLOv5 in Sachen Geschwindigkeit und Genauigkeit. Das Modell wurde von Grund auf auf dem MS COCO-Datensatz trainiert, ohne andere Datensätze oder vortrainierte Gewichte zu verwenden. Der Quellcode für YOLOv7 ist auf GitHub verfügbar. Beachte, dass neuere Modelle wie YOLO11 und YOLO26 seitdem eine höhere Genauigkeit bei verbesserter Effizienz erreicht haben.

YOLOv7 Vergleich mit SOTA-Objektdetektoren

Vergleich von SOTA-Objektdetektoren

Aus den Ergebnissen in der YOLO-Vergleichstabelle wissen wir, dass die vorgeschlagene Methode umfassend das beste Verhältnis zwischen Geschwindigkeit und Genauigkeit bietet. Wenn wir YOLOv7-tiny-SiLU mit YOLOv5-N (r6.1) vergleichen, ist unsere Methode um 127 fps schneller und um 10,7 % genauer beim AP. Zudem erreicht YOLOv7 51,4 % AP bei einer Bildrate von 161 fps, während PPYOLOE-L bei gleichem AP nur eine Bildrate von 78 fps aufweist. In Bezug auf die Parameterverwendung ist YOLOv7 um 41 % sparsamer als PPYOLOE-L.

Wenn wir YOLOv7-X mit 114 fps Inferenzgeschwindigkeit mit YOLOv5-L (r6.1) mit 99 fps Inferenzgeschwindigkeit vergleichen, kann YOLOv7-X den AP um 3,9 % verbessern. Wird YOLOv7-X mit dem ähnlich skalierten YOLOv5-X (r6.1) verglichen, ist die Inferenzgeschwindigkeit von YOLOv7-X um 31 fps höher. Darüber hinaus reduziert YOLOv7-X im Vergleich zu YOLOv5-X (r6.1) die Anzahl der Parameter um 22 % und den Rechenaufwand um 8 %, verbessert jedoch den AP um 2,2 % (Quelle).

Leistung
ModellParameter
(M)
FLOPs
(G)
Größe
(Pixel)
FPSAPtest / val
50-95
APtest
50
APtest
75
APtest
S
APtest
M
APtest
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%

Übersicht

Echtzeit-Objekterkennung ist eine wichtige Komponente in vielen computer vision Systemen, einschließlich Multi-object tracking, autonomem Fahren, robotics und medical image analysis. In den letzten Jahren hat sich die Entwicklung der Echtzeit-Objekterkennung auf das Design effizienter Architekturen und die Verbesserung der Inferenzgeschwindigkeit verschiedener CPUs, GPUs und neural processing units (NPUs) konzentriert. YOLOv7 unterstützt sowohl mobile GPU- als auch GPU-Geräte, von der Edge bis zur Cloud.

Im Gegensatz zu herkömmlichen Echtzeit-Objekterkennungsmodellen, die sich auf die Architekturoptimierung konzentrieren, legt YOLOv7 den Fokus auf die Optimierung des Trainingsprozesses. Dies beinhaltet Module und Optimierungsmethoden, die darauf ausgelegt sind, die Genauigkeit der Objekterkennung zu verbessern, ohne die Inferenzkosten zu erhöhen – ein Konzept, das als „trainable bag-of-freebies“ bekannt ist.

Hauptmerkmale

YOLOv7 führt mehrere wichtige Funktionen ein:

  1. Model Re-parameterization: YOLOv7 schlägt ein geplantes re-parameterisiertes Modell vor, eine Strategie, die auf Schichten in verschiedenen Netzwerken unter Berücksichtigung des Gradienten-Propagationspfads anwendbar ist.

  2. Dynamic Label Assignment: Das Training des Modells mit mehreren Ausgabeschichten bringt ein neues Problem mit sich: „Wie weist man dynamische Ziele für die Ausgaben verschiedener Zweige zu?“ Um dieses Problem zu lösen, führt YOLOv7 eine neue Label-Zuweisungsmethode ein, die als „coarse-to-fine lead guided label assignment“ bezeichnet wird.

  3. Extended and Compound Scaling: YOLOv7 schlägt „extend“ und „compound scaling“-Methoden für den Echtzeit-Objekterkennungsdetektor vor, die Parameter und Berechnungen effektiv nutzen können.

  4. Efficiency: Die von YOLOv7 vorgeschlagene Methode kann die Parameter um etwa 40 % und die Berechnungen um 50 % gegenüber dem Stand der Technik bei Echtzeit-Objekterkennungsmodellen reduzieren und bietet eine schnellere Inferenzgeschwindigkeit sowie eine höhere Erkennungsgenauigkeit.

Anwendungsbeispiele

Ultralytics veröffentlicht keine vorab trainierten yolov7.pt Gewichte oder ultralytics/cfg/models/v7/ YAMLs, und natives PyTorch-Training sowie Inferenz für YOLOv7 werden vom Ultralytics Python-Paket nicht unterstützt. Du kannst jedoch einen YOLOv7-Checkpunkt, der im upstream YOLOv7 repository trainiert wurde, in Ultralytics importieren, indem du ihn wie unten gezeigt nach ONNX oder TensorRT exportierst.

ONNX Export

So verwendest du das YOLOv7 ONNX-Modell mit Ultralytics:

  1. (Optional) Installiere Ultralytics und exportiere ein ONNX-Modell, um die erforderlichen Abhängigkeiten automatisch installieren zu lassen:

    pip install ultralytics
    yolo export model=yolo26n.pt format=onnx
  2. Exportiere das gewünschte YOLOv7-Modell mit dem Exporter im 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. Ändere den ONNX-Modellgraphen, um ihn mit dem folgenden Skript mit Ultralytics kompatibel zu machen:

    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. Du kannst dann das modifizierte ONNX-Modell laden und die Inferenz damit in Ultralytics wie gewohnt ausführen:

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

TensorRT Export

  1. Befolge die Schritte 1-2 im Abschnitt ONNX Export.

  2. Installiere das TensorRT Python-Paket:

    pip install tensorrt
  3. Führe das folgende Skript aus, um das modifizierte ONNX-Modell in eine TensorRT-Engine zu konvertieren:

    from ultralytics.utils.export import export_engine
    
    export_engine("yolov7-ultralytics.onnx", half=True)
  4. Lade das Modell und führe es in Ultralytics aus:

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

Zitate und Danksagungen

Wir möchten den Autoren von YOLOv7 für ihre bedeutenden Beiträge auf dem Gebiet der Echtzeit-Objekterkennung danken:

Zitat
@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}
}

Das originale YOLOv7-Paper ist auf arXiv zu finden. Die Autoren haben ihre Arbeit öffentlich zugänglich gemacht, und die Codebasis kann auf GitHub abgerufen werden. Wir schätzen ihre Bemühungen, das Feld voranzubringen und ihre Arbeit der breiteren Community zugänglich zu machen.

FAQ

Was ist YOLOv7 und warum gilt es als Durchbruch bei der Echtzeit-object detection?

YOLOv7 wurde im Juli 2022 veröffentlicht und war ein bedeutendes Modell zur Echtzeit-Objekterkennung, das zum Zeitpunkt seiner Veröffentlichung hervorragende Geschwindigkeit und Genauigkeit erreichte. Es übertraf zeitgenössische Modelle wie YOLOX, YOLOv5 und PPYOLOE sowohl bei der Parameterverwendung als auch bei der Inferenzgeschwindigkeit. Zu den herausragenden Merkmalen von YOLOv7 gehören die Modell-Re-parameterisierung und die dynamische Label-Zuweisung, die seine Leistung ohne Erhöhung der Inferenzkosten optimieren. Weitere technische Details zur Architektur und Vergleichskennzahlen mit anderen hochmodernen Objekterkennungsmodellen findest du im YOLOv7 paper.

Wie verbessert YOLOv7 frühere YOLO-Modelle wie YOLOv4 und YOLOv5?

YOLOv7 führt mehrere Innovationen ein, darunter Modell-Re-parameterisierung und dynamische Label-Zuweisung, die den Trainingsprozess verbessern und die Genauigkeit der Inferenz steigern. Im Vergleich zu YOLOv5 steigert YOLOv7 Geschwindigkeit und Genauigkeit erheblich. Zum Beispiel verbessert YOLOv7-X die Genauigkeit um 2,2 % und reduziert die Parameter um 22 % im Vergleich zu YOLOv5-X. Detaillierte Vergleiche findest du in der Leistungstabelle YOLOv7 comparison with SOTA object detectors.

Kann ich YOLOv7 mit Ultralytics-Tools und -Plattformen verwenden?

Aktuell unterstützt Ultralytics nur die YOLOv7 ONNX- und TensorRT-Inferenz. Um die exportierte ONNX- und TensorRT-Version von YOLOv7 mit Ultralytics auszuführen, sieh dir den Abschnitt Usage Examples an.

Wie trainiere ich ein benutzerdefiniertes YOLOv7-Modell mit meinem Datensatz?

Um ein benutzerdefiniertes YOLOv7-Modell zu installieren und zu trainieren, befolge diese Schritte:

  1. Klonen das YOLOv7-Repository:

    git clone https://github.com/WongKinYiu/yolov7
  2. Navigiere zum geklonten Verzeichnis und installiere die Abhängigkeiten:

    cd yolov7
    pip install -r requirements.txt
  3. Bereite deinen Datensatz vor und konfiguriere die Modellparameter gemäß den usage instructions, die im Repository bereitgestellt werden. Für weitere Anleitungen besuche das YOLOv7 GitHub-Repository für die neuesten Informationen und Updates.

  4. Nach dem Training kannst du das Modell nach ONNX oder TensorRT exportieren, um es in Ultralytics zu verwenden, wie unter Usage Examples gezeigt.

Was sind die Hauptmerkmale und Optimierungen von YOLOv7?

YOLOv7 bietet mehrere Schlüsselfunktionen, die die Echtzeit-Objekterkennung revolutionieren:

  • Model Re-parameterization: Verbessert die Leistung des Modells durch Optimierung der Gradienten-Propagationspfade.
  • Dynamic Label Assignment: Verwendet eine „coarse-to-fine lead guided“-Methode, um dynamische Ziele für Ausgaben über verschiedene Zweige hinweg zuzuweisen, was die Genauigkeit verbessert.
  • Extended and Compound Scaling: Nutzt Parameter und Berechnungen effizient, um das Modell für verschiedene Echtzeitanwendungen zu skalieren.
  • Efficiency: Reduziert die Parameteranzahl um 40 % und die Berechnungen um 50 % im Vergleich zu anderen hochmodernen Modellen bei gleichzeitig schnellerer Inferenzgeschwindigkeit.

Weitere Details zu diesen Funktionen findest du im Abschnitt YOLOv7 Overview.

Kommentare