YOLOv7: Trainable Bag-of-Freebies
YOLOv7, lanzado en julio de 2022, fue un avance significativo en la detección de objetos en tiempo real en el momento de su lanzamiento. Logró un 56,8% de AP en una GPU V100, estableciendo nuevos puntos de referencia cuando se presentó. YOLOv7 superó a detectores de objetos contemporáneos como YOLOR, YOLOX, Scaled-YOLOv4 y YOLOv5 en velocidad y precisión. El modelo se entrena desde cero en el conjunto de datos MS COCO sin utilizar otros conjuntos de datos ni pesos preentrenados. El código fuente de YOLOv7 está disponible en GitHub. Ten en cuenta que modelos más recientes como YOLO11 y YOLO26 han logrado desde entonces una mayor precisión con una eficiencia mejorada.

Comparación de detectores de objetos SOTA
A partir de los resultados en la tabla de comparación de YOLO, sabemos que el método propuesto tiene el mejor equilibrio entre velocidad y precisión de manera integral. Si comparamos YOLOv7-tiny-SiLU con YOLOv5-N (r6.1), nuestro método es 127 fps más rápido y un 10,7% más preciso en AP. Además, YOLOv7 tiene un 51,4% de AP a una tasa de fotogramas de 161 fps, mientras que PPYOLOE-L con el mismo AP solo tiene una tasa de 78 fps. En términos de uso de parámetros, YOLOv7 utiliza un 41% menos que PPYOLOE-L.
Si comparamos YOLOv7-X, con una velocidad de inferencia de 114 fps, con YOLOv5-L (r6.1), con 99 fps, YOLOv7-X puede mejorar el AP en un 3,9%. Si comparamos YOLOv7-X con YOLOv5-X (r6.1) de escala similar, la velocidad de inferencia de YOLOv7-X es 31 fps más rápida. Además, en términos de cantidad de parámetros y cómputo, YOLOv7-X reduce un 22% los parámetros y un 8% el cómputo en comparación con YOLOv5-X (r6.1), pero mejora el AP en un 2,2% (Fuente).
| Modelo | Parámetros (M) | FLOPs (G) | Tamaño (píxeles) | 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% |
Descripción general
La detección de objetos en tiempo real es un componente importante en muchos sistemas de visión artificial, incluyendo el multi-seguimiento de objetos, la conducción autónoma, la robótica y el análisis de imágenes médicas. En los últimos años, el desarrollo de la detección de objetos en tiempo real se ha centrado en diseñar arquitecturas eficientes y mejorar la velocidad de inferencia de diversas CPU, GPU y unidades de procesamiento neuronal (NPU). YOLOv7 es compatible tanto con dispositivos móviles como con GPU, desde el borde hasta la nube.
A diferencia de los detectores de objetos tradicionales en tiempo real que se centran en la optimización de la arquitectura, YOLOv7 introduce un enfoque en la optimización del proceso de entrenamiento. Esto incluye módulos y métodos de optimización diseñados para mejorar la precisión de la detección de objetos sin aumentar el coste de inferencia, un concepto conocido como "trainable bag-of-freebies".
Características clave
YOLOv7 introduce varias características clave:
-
Re-parametrización de modelos: YOLOv7 propone un modelo re-parametrizado planificado, que es una estrategia aplicable a capas en diferentes redes con el concepto de ruta de propagación de gradiente.
-
Asignación dinámica de etiquetas: El entrenamiento del modelo con múltiples capas de salida presenta un nuevo problema: "¿Cómo asignar objetivos dinámicos para las salidas de diferentes ramas?". Para resolver este problema, YOLOv7 introduce un nuevo método de asignación de etiquetas llamado asignación de etiquetas guiada por guía de grueso a fino.
-
Escalado extendido y compuesto: YOLOv7 propone métodos de "escalado extendido y compuesto" para el detector de objetos en tiempo real que pueden utilizar eficazmente los parámetros y el cálculo.
-
Eficiencia: El método propuesto por YOLOv7 puede reducir eficazmente alrededor de un 40% de los parámetros y un 50% del cálculo de los detectores de objetos en tiempo real de última generación, y tiene una velocidad de inferencia más rápida y una mayor precisión de detección.
Ejemplos de uso
Ultralytics no publica pesos preentrenados yolov7.pt ni archivos YAML ultralytics/cfg/models/v7/, y el entrenamiento e inferencia nativos de PyTorch para YOLOv7 no son compatibles con el paquete Python de Ultralytics. Sin embargo, puedes importar un punto de control de YOLOv7 entrenado en el repositorio original de YOLOv7 a Ultralytics exportándolo a ONNX o TensorRT, como se muestra a continuación.
Exportación a ONNX
Para utilizar el modelo ONNX de YOLOv7 con Ultralytics:
-
(Opcional) Instala Ultralytics y exporta un modelo ONNX para que las dependencias requeridas se instalen automáticamente:
pip install ultralytics yolo export model=yolo26n.pt format=onnx -
Exporta el modelo YOLOv7 deseado utilizando el exportador en el repositorio de 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 el gráfico del modelo ONNX para que sea compatible con Ultralytics utilizando el siguiente 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") -
Puedes entonces cargar el modelo ONNX modificado y ejecutar la inferencia con él en Ultralytics normalmente:
from ultralytics import ASSETS, YOLO model = YOLO("yolov7-ultralytics.onnx", task="detect") results = model(ASSETS / "bus.jpg")
Exportación a TensorRT
-
Sigue los pasos 1-2 en la sección Exportación a ONNX.
-
Instala el paquete Python
TensorRT:pip install tensorrt -
Ejecuta el siguiente script para convertir el modelo ONNX modificado a motor TensorRT:
from ultralytics.utils.export import export_engine export_engine("yolov7-ultralytics.onnx", half=True) -
Carga y ejecuta el modelo en Ultralytics:
from ultralytics import ASSETS, YOLO model = YOLO("yolov7-ultralytics.engine", task="detect") results = model(ASSETS / "bus.jpg")
Citas y reconocimientos
Nos gustaría agradecer a los autores de YOLOv7 por sus importantes contribuciones en el campo de la detección de objetos en tiempo real:
@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}
}El artículo original de YOLOv7 se puede encontrar en arXiv. Los autores han hecho público su trabajo y la base de código se puede encontrar en GitHub. Agradecemos sus esfuerzos por hacer avanzar el campo y hacer que su trabajo sea accesible a la comunidad en general.
Preguntas frecuentes
¿Qué es YOLOv7 y por qué se considera un avance en la detección de objetos en tiempo real?
YOLOv7, lanzado en julio de 2022, fue un modelo de detección de objetos en tiempo real significativo que alcanzó una velocidad y precisión excelentes en el momento de su lanzamiento. Superó a modelos contemporáneos como YOLOX, YOLOv5 y PPYOLOE tanto en el uso de parámetros como en la velocidad de inferencia. Las características distintivas de YOLOv7 incluyen su re-parametrización de modelos y la asignación dinámica de etiquetas, que optimizan su rendimiento sin aumentar los costes de inferencia. Para obtener más detalles técnicos sobre su arquitectura y métricas de comparación con otros detectores de objetos de última generación, consulta el artículo de YOLOv7.
¿Cómo mejora YOLOv7 a los modelos YOLO anteriores como YOLOv4 y YOLOv5?
YOLOv7 introduce varias innovaciones, incluyendo la re-parametrización de modelos y la asignación dinámica de etiquetas, que mejoran el proceso de entrenamiento y la precisión de la inferencia. Comparado con YOLOv5, YOLOv7 aumenta significativamente la velocidad y la precisión. Por ejemplo, YOLOv7-X mejora la precisión en un 2,2% y reduce los parámetros en un 22% en comparación con YOLOv5-X. Las comparaciones detalladas se pueden encontrar en la tabla de rendimiento Comparación de YOLOv7 con detectores de objetos SOTA.
¿Puedo usar YOLOv7 con herramientas y plataformas de Ultralytics?
A día de hoy, Ultralytics solo admite la inferencia de YOLOv7 en ONNX y TensorRT. Para ejecutar la versión exportada de YOLOv7 a ONNX y TensorRT con Ultralytics, consulta la sección Ejemplos de uso.
¿Cómo entreno un modelo YOLOv7 personalizado usando mi conjunto de datos?
Para instalar y entrenar un modelo YOLOv7 personalizado, sigue estos pasos:
-
Clona el repositorio de YOLOv7:
git clone https://github.com/WongKinYiu/yolov7 -
Navega al directorio clonado e instala las dependencias:
cd yolov7 pip install -r requirements.txt -
Prepara tu conjunto de datos y configura los parámetros del modelo de acuerdo con las instrucciones de uso proporcionadas en el repositorio. Para obtener más orientación, visita el repositorio de GitHub de YOLOv7 para obtener la información y actualizaciones más recientes.
-
Después del entrenamiento, puedes exportar el modelo a ONNX o TensorRT para usarlo en Ultralytics como se muestra en Ejemplos de uso.
¿Cuáles son las características y optimizaciones clave introducidas en YOLOv7?
YOLOv7 ofrece varias características clave que revolucionan la detección de objetos en tiempo real:
- Re-parametrización de modelos: Mejora el rendimiento del modelo optimizando las rutas de propagación de gradiente.
- Asignación dinámica de etiquetas: Utiliza un método guiado de grueso a fino para asignar objetivos dinámicos para las salidas en diferentes ramas, mejorando la precisión.
- Escalado extendido y compuesto: Utiliza de forma eficiente los parámetros y el cálculo para escalar el modelo para diversas aplicaciones en tiempo real.
- Eficiencia: Reduce el recuento de parámetros en un 40% y el cálculo en un 50% en comparación con otros modelos de última generación, logrando velocidades de inferencia más rápidas.
Para obtener más detalles sobre estas características, consulta la sección Visión general de YOLOv7.