YOLOv7: Bag-of-Freebies addestrabile
YOLOv7 è un rilevatore di oggetti in tempo reale all'avanguardia che supera tutti i rilevatori di oggetti conosciuti sia in velocità che in precisione nell'intervallo da 5 FPS a 160 FPS. Ha la massima precisione (56,8% AP) tra tutti i rilevatori di oggetti in tempo reale conosciuti con 30 FPS o superiore su GPU V100. Inoltre, YOLOv7 supera altri rilevatori di oggetti come YOLOR, YOLOX, Scaled-YOLOv4, YOLOv5 e molti altri in velocità e precisione. Il modello è addestrato sul set di dati MS COCO da zero senza utilizzare altri set di dati o pesi pre-addestrati. Il codice sorgente per YOLOv7 è disponibile su GitHub.
Confronto tra i rilevatori di oggetti SOTA
Dai risultati nella tabella di confronto di YOLO sappiamo che il metodo proposto ha il miglior compromesso velocità-precisione in modo completo. Se confrontiamo YOLOv7-tiny-SiLU con YOLOv5-N (r6.1), il nostro metodo è più veloce di 127 fps e più preciso del 10,7% su AP. Inoltre, YOLOv7 ha il 51,4% di AP a una frequenza di fotogrammi di 161 fps, mentre PPYOLOE-L con lo stesso AP ha solo una frequenza di fotogrammi 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 è più veloce di 31 fps. Inoltre, in termini di quantità di parametri e calcolo, YOLOv7-X riduce il 22% dei parametri e l'8% del calcolo rispetto a YOLOv5-X (r6.1), ma migliora l'AP del 2,2% (Fonte).
Prestazioni
Modello | Parametri (M) |
FLOPs (G) |
Dimensione (pixel) |
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% |
Panoramica
Il rilevamento di oggetti in tempo reale è una componente importante in molti sistemi di computer vision, tra cui il multi-object tracking, 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 GPU mobili che dispositivi 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 training. Ciò include moduli e metodi di ottimizzazione progettati per migliorare l'accuratezza 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:
-
Riparamentrizzazione del modello: YOLOv7 propone un modello riparametrizzato pianificato, che è una strategia applicabile ai livelli in diverse reti con il concetto di percorso di propagazione del gradiente.
-
Assegnazione dinamica delle etichette: Il training del modello con più livelli di output 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 etichette chiamato coarse-to-fine lead guided label assignment.
-
Scalabilità estesa e composta: YOLOv7 propone metodi di "estensione" e "scalabilità composta" per il rilevatore di oggetti in tempo reale, in grado di utilizzare efficacemente parametri e calcoli.
-
Efficienza: Il metodo proposto da YOLOv7 può ridurre efficacemente di circa il 40% i parametri e del 50% il calcolo del rilevatore di oggetti in tempo reale all'avanguardia, e ha una velocità di inferenza più elevata e una maggiore accuratezza di rilevamento.
Esempi di utilizzo
Al momento della stesura, Ultralytics supporta solo l'inferenza ONNX e TensorRT per YOLOv7.
Esportazione ONNX
Per utilizzare il modello YOLOv7 ONNX con Ultralytics:
-
(Opzionale) Installa Ultralytics ed esporta un modello ONNX per avere le dipendenze richieste installate automaticamente:
pip install ultralytics yolo export model=yolo11n.pt format=onnx
-
Esporta il modello YOLOv7 desiderato utilizzando l'exporter 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
-
Modifica il grafico del modello ONNX per essere 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")
-
Puoi quindi caricare il modello ONNX modificato ed eseguire normalmente l'inferenza con esso in Ultralytics:
from ultralytics import ASSETS, YOLO model = YOLO("yolov7-ultralytics.onnx", task="detect") results = model(ASSETS / "bus.jpg")
Esportazione TensorRT
-
Seguire i passaggi 1-2 nella sezione Esportazione ONNX.
-
Installa il
TensorRT
Pacchetto Python:pip install tensorrt
-
Esegui lo script seguente per convertire il modello ONNX modificato in un motore TensorRT:
from ultralytics.utils.export import export_engine export_engine("yolov7-ultralytics.onnx", half=True)
-
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 riconoscimenti
Desideriamo ringraziare gli autori di YOLOv7 per i loro significativi contributi nel campo del rilevamento di oggetti in tempo reale:
@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}
}
L'articolo originale su YOLOv7 è disponibile su arXiv. Gli autori hanno reso il loro lavoro pubblicamente disponibile e il codice può essere consultato su GitHub. Apprezziamo i loro sforzi nel far progredire il settore e nel rendere il loro lavoro accessibile alla comunità più ampia.
FAQ
Cos'è YOLOv7 e perché è considerato una svolta nella object detection in tempo reale?
YOLOv7 è un modello di rilevamento oggetti in tempo reale all'avanguardia che raggiunge velocità e precisione senza precedenti. Supera altri modelli, come YOLOX, YOLOv5 e PPYOLOE, sia nell'utilizzo dei parametri che nella velocità di inferenza. Le caratteristiche distintive di YOLOv7 includono la sua riparametrizzazione del modello e l'assegnazione dinamica delle etichette, che ne ottimizzano le prestazioni senza aumentare i costi di inferenza. Per maggiori dettagli tecnici sulla sua architettura e sulle metriche di confronto con altri rilevatori di oggetti all'avanguardia, fare riferimento al paper di YOLOv7.
In che modo YOLOv7 migliora i precedenti modelli YOLO come YOLOv4 e YOLOv5?
YOLOv7 introduce diverse innovazioni, tra cui la riparametrizzazione del modello e l'assegnazione dinamica delle etichette, che migliorano il processo di addestramento e aumentano l'accuratezza dell'inferenza. Rispetto a YOLOv5, YOLOv7 aumenta significativamente la velocità e l'accuratezza. Ad esempio, YOLOv7-X migliora l'accuratezza del 2,2% e riduce i parametri del 22% rispetto a YOLOv5-X. Confronti dettagliati sono disponibili nella tabella delle prestazioni Confronto tra YOLOv7 e i rilevatori di oggetti SOTA.
Posso utilizzare YOLOv7 con gli strumenti e le piattaforme Ultralytics?
Al momento, Ultralytics supporta solo l'inferenza YOLOv7 ONNX e TensorRT. Per eseguire la versione esportata ONNX e TensorRT di YOLOv7 con Ultralytics, consultare la sezione Esempi di utilizzo.
Come posso addestrare un modello YOLOv7 personalizzato utilizzando il mio dataset?
Per installare e addestrare un modello YOLOv7 personalizzato, segui questi passaggi:
- Clona il repository YOLOv7:
git clone https://github.com/WongKinYiu/yolov7
- Spostarsi nella directory clonata e installare le dipendenze:
cd yolov7 pip install -r requirements.txt
-
Prepara il tuo set di dati e configura i parametri del modello secondo le istruzioni d'uso fornite nel repository. Per ulteriori indicazioni, visita il repository YOLOv7 GitHub per le informazioni e gli aggiornamenti più recenti.
-
Dopo l'addestramento, è possibile esportare il modello in ONNX o TensorRT per l'uso in Ultralytics come mostrato in Esempi di utilizzo.
Quali sono le caratteristiche principali e le ottimizzazioni introdotte in YOLOv7?
YOLOv7 offre diverse caratteristiche chiave che rivoluzionano il rilevamento di oggetti in tempo reale:
- Riparamentrizzazione del modello: Migliora le prestazioni del modello ottimizzando i percorsi di propagazione del gradiente.
- Assegnazione dinamica delle etichette: Utilizza un metodo guidato coarse-to-fine per assegnare target dinamici per gli output tra diversi rami, migliorando la precisione.
- Scalabilità estesa e composta: 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 all'avanguardia, ottenendo al contempo velocità di inferenza più elevate.
Per ulteriori dettagli su queste funzionalità, consultare la sezione Panoramica di YOLOv7.