YOLOv7: Trainable Bag-of-Freebies

YOLOv7, rilasciato a luglio 2022, è stato un progresso significativo nel rilevamento di oggetti in tempo reale al momento del suo lancio. Ha raggiunto il 56,8% di AP su GPU V100, stabilendo nuovi benchmark al momento dell'introduzione. YOLOv7 ha superato i rilevatori di oggetti contemporanei come YOLOR, YOLOX, Scaled-YOLOv4 e YOLOv5 in termini di velocità e accuratezza. Il modello è addestrato sul dataset MS COCO da zero senza utilizzare altri dataset o pesi pre-addestrati. Il codice sorgente per YOLOv7 è disponibile su GitHub. Tieni presente che modelli più recenti come YOLO11 e YOLO26 hanno da allora raggiunto una maggiore accuratezza con una migliore efficienza.

Confronto di YOLOv7 con rilevatori di oggetti SOTA

Confronto dei rilevatori di oggetti SOTA

Dai risultati nella tabella di confronto YOLO sappiamo che il metodo proposto offre complessivamente il miglior compromesso tra velocità e accuratezza. Se confrontiamo YOLOv7-tiny-SiLU con YOLOv5-N (r6.1), il nostro metodo è 127 fps più veloce e ha un'accuratezza AP superiore del 10,7%. Inoltre, YOLOv7 ha un AP del 51,4% a una frequenza di 161 fps, mentre PPYOLOE-L con lo stesso AP ha solo una frequenza di 78 fps. In termini di utilizzo dei parametri, YOLOv7 è inferiore del 41% rispetto a PPYOLOE-L.

Se confrontiamo YOLOv7-X con una velocità di inferenza di 114 fps con YOLOv5-L (r6.1) con una velocità di inferenza di 99 fps, YOLOv7-X può migliorare l'AP del 3,9%. Se YOLOv7-X viene confrontato con YOLOv5-X (r6.1) di scala simile, la velocità di inferenza di YOLOv7-X è di 31 fps superiore. Inoltre, in termini di quantità di parametri e calcolo, YOLOv7-X riduce i parametri del 22% e il calcolo dell'8% rispetto a YOLOv5-X (r6.1), ma migliora l'AP del 2,2% (Fonte).

Prestazioni
ModelloParametri
(M)
FLOPs
(G)
Dimensione
(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%

Panoramica

Il rilevamento di oggetti in tempo reale è un componente importante in molti sistemi di computer vision, inclusi il tracking di oggetti multipli, la guida autonoma, la robotica e l' analisi di immagini mediche. Negli ultimi anni, lo sviluppo del rilevamento di oggetti in tempo reale si è concentrato sulla progettazione di architetture efficienti e sul miglioramento della velocità di inferenza di varie CPU, GPU e unità di elaborazione neurale (NPU). YOLOv7 supporta sia dispositivi GPU mobile che GPU, dall'edge al cloud.

A differenza dei tradizionali rilevatori di oggetti in tempo reale che si concentrano sull'ottimizzazione dell'architettura, YOLOv7 introduce un focus sull'ottimizzazione del processo di addestramento. Questo include moduli e metodi di ottimizzazione progettati per migliorare la precisione del rilevamento degli oggetti senza aumentare il costo di inferenza, un concetto noto come "trainable bag-of-freebies".

Caratteristiche principali

YOLOv7 introduce diverse caratteristiche chiave:

  1. Ri-parametrizzazione del modello: YOLOv7 propone un modello ri-parametrizzato pianificato, che è una strategia applicabile ai layer in diverse reti con il concetto di percorso di propagazione del gradiente.

  2. Assegnazione dinamica delle label: L'addestramento del modello con layer di output multipli presenta un nuovo problema: "Come assegnare target dinamici per gli output di diversi rami?" Per risolvere questo problema, YOLOv7 introduce un nuovo metodo di assegnazione delle label chiamato assegnazione delle label guidata da lead coarse-to-fine.

  3. Scaling esteso e composto: YOLOv7 propone metodi di "estensione" e "scaling composto" per il rilevatore di oggetti in tempo reale che possono utilizzare efficacemente parametri e calcolo.

  4. Efficienza: Il metodo proposto da YOLOv7 può ridurre efficacemente circa il 40% dei parametri e il 50% del calcolo dei moderni rilevatori di oggetti in tempo reale, e ha una velocità di inferenza più rapida e una maggiore precisione di rilevamento.

Esempi di utilizzo

Ultralytics non pubblica pesi pre-addestrati yolov7.pt o YAML ultralytics/cfg/models/v7/, e l'addestramento e l'inferenza nativi in PyTorch per YOLOv7 non sono supportati dal pacchetto Python di Ultralytics. Tuttavia, puoi importare un checkpoint di YOLOv7 addestrato nel repository originale YOLOv7 in Ultralytics esportandolo in ONNX o TensorRT, come mostrato di seguito.

Esportazione ONNX

Per utilizzare il modello ONNX di YOLOv7 con Ultralytics:

  1. (Opzionale) Installa Ultralytics ed esporta un modello ONNX per far installare automaticamente le dipendenze richieste:

    pip install ultralytics
    yolo export model=yolo26n.pt format=onnx
  2. Esporta il modello YOLOv7 desiderato utilizzando l'esportatore nel repository 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. Modifica il grafo del modello ONNX per renderlo compatibile con Ultralytics utilizzando il seguente script:

    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. Puoi quindi caricare il modello ONNX modificato ed eseguire l'inferenza con esso in Ultralytics normalmente:

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

Esportazione TensorRT

  1. Segui i passaggi 1-2 nella sezione Esportazione ONNX.

  2. Installa il pacchetto Python TensorRT:

    pip install tensorrt
  3. Esegui il seguente script per convertire il modello ONNX modificato in un engine TensorRT:

    from ultralytics.utils.export import export_engine
    
    export_engine("yolov7-ultralytics.onnx", half=True)
  4. Carica ed esegui il modello in Ultralytics:

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

Citazioni e ringraziamenti

Desideriamo ringraziare gli autori di YOLOv7 per i loro contributi significativi nel campo del rilevamento di oggetti in tempo reale:

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

Il paper originale di YOLOv7 può essere trovato su arXiv. Gli autori hanno reso pubblico il loro lavoro e la codebase può essere consultata su GitHub. Apprezziamo i loro sforzi nel far avanzare il campo e nel rendere il loro lavoro accessibile alla comunità più ampia.

FAQ

Cos'è YOLOv7 e perché è considerato una svolta nel rilevamento di oggetti in tempo reale?

YOLOv7, rilasciato a luglio 2022, è stato un importante modello di rilevamento di oggetti in tempo reale che ha raggiunto una velocità e una precisione eccellenti al momento del rilascio. Ha superato modelli contemporanei come YOLOX, YOLOv5 e PPYOLOE sia nell'uso dei parametri che nella velocità di inferenza. Le caratteristiche distintive di YOLOv7 includono la ri-parametrizzazione del modello e l'assegnazione dinamica delle label, che ottimizzano le prestazioni senza aumentare i costi di inferenza. Per ulteriori dettagli tecnici sulla sua architettura e sulle metriche di confronto con altri rilevatori di oggetti allo stato dell'arte, consulta il paper di YOLOv7.

Come migliora YOLOv7 i precedenti modelli YOLO come YOLOv4 e YOLOv5?

YOLOv7 introduce diverse innovazioni, tra cui la ri-parametrizzazione del modello e l'assegnazione dinamica delle label, che migliorano il processo di addestramento e aumentano la precisione dell'inferenza. Rispetto a YOLOv5, YOLOv7 incrementa significativamente velocità e precisione. Ad esempio, YOLOv7-X migliora la precisione del 2,2% e riduce i parametri del 22% rispetto a YOLOv5-X. Confronti dettagliati possono essere trovati nella tabella delle prestazioni Confronto di YOLOv7 con rilevatori di oggetti SOTA.

Posso usare YOLOv7 con strumenti e piattaforme Ultralytics?

Al momento, Ultralytics supporta solo l'inferenza ONNX e TensorRT di YOLOv7. Per eseguire la versione esportata in ONNX e TensorRT di YOLOv7 con Ultralytics, controlla la sezione Esempi di utilizzo.

Come addestro un modello YOLOv7 personalizzato usando il mio dataset?

Per installare e addestrare un modello YOLOv7 personalizzato, segui questi passaggi:

  1. Clona il repository YOLOv7:

    git clone https://github.com/WongKinYiu/yolov7
  2. Naviga nella directory clonata e installa le dipendenze:

    cd yolov7
    pip install -r requirements.txt
  3. Prepara il tuo dataset e configura i parametri del modello secondo le istruzioni per l'uso fornite nel repository. Per ulteriori indicazioni, visita il repository GitHub di YOLOv7 per le informazioni e gli aggiornamenti più recenti.

  4. Dopo l'addestramento, puoi esportare il modello in ONNX o TensorRT per l'uso in Ultralytics come mostrato in Esempi di utilizzo.

Quali sono le caratteristiche chiave e le ottimizzazioni introdotte in YOLOv7?

YOLOv7 offre diverse caratteristiche chiave che rivoluzionano il rilevamento di oggetti in tempo reale:

  • Ri-parametrizzazione del modello: Migliora le prestazioni del modello ottimizzando i percorsi di propagazione del gradiente.
  • Assegnazione dinamica delle label: Utilizza un metodo guidato da lead coarse-to-fine per assegnare target dinamici per gli output attraverso diversi rami, migliorando la precisione.
  • Scaling esteso e composto: Utilizza in modo efficiente parametri e calcoli per scalare il modello per varie applicazioni in tempo reale.
  • Efficienza: Riduce il numero di parametri del 40% e il calcolo del 50% rispetto ad altri modelli allo stato dell'arte, ottenendo velocità di inferenza più elevate.

Per ulteriori dettagli su queste caratteristiche, consulta la sezione Panoramica di YOLOv7.

Commenti