Ultralytics YOLO27 :

YOLOv7 : sac d’astuces entraînables#

YOLOv7, publié en juillet 2022, représentait une avancée majeure dans la détection d’objets en temps réel à sa sortie. Il a atteint 56,8 % d’AP sur un GPU V100, établissant de nouvelles références lors de son introduction. YOLOv7 a surpassé des détecteurs d’objets contemporains tels que YOLOR, YOLOX, Scaled-YOLOv4 et YOLOv5 en matière de vitesse et d’exactitude. Le modèle est entraîné à partir de zéro sur le jeu de données MS COCO, sans utiliser d’autres jeux de données ni de poids préentraînés. Le code source de YOLOv7 est disponible sur GitHub. Note que des modèles plus récents comme YOLO11 et YOLO26 ont depuis atteint une meilleure exactitude avec une efficacité accrue.

Comparaison de YOLOv7 avec des détecteurs d’objets SOTA

Comparaison de détecteurs d’objets SOTA#

D’après les résultats du tableau de comparaison de YOLO, nous savons que la méthode proposée offre globalement le meilleur compromis entre vitesse et exactitude. Si nous comparons YOLOv7-tiny-SiLU à YOLOv5-N (r6.1), notre méthode est 127 fps plus rapide et 10,7 % plus exacte en AP. De plus, YOLOv7 atteint 51,4 % d’AP à une cadence de 161 fps, tandis que PPYOLOE-L, avec le même AP, n’atteint qu’une cadence de 78 fps. En matière d’utilisation des paramètres, YOLOv7 en utilise 41 % de moins que PPYOLOE-L.

Si nous comparons YOLOv7-X, avec une vitesse d’inférence de 114 fps, à YOLOv5-L (r6.1), avec une vitesse d’inférence de 99 fps, YOLOv7-X peut améliorer l’AP de 3,9 %. Si YOLOv7-X est comparé à YOLOv5-X (r6.1), de taille similaire, sa vitesse d’inférence est supérieure de 31 fps. De plus, en matière de nombre de paramètres et de calculs, YOLOv7-X réduit de 22 % le nombre de paramètres et de 8 % les calculs par rapport à YOLOv5-X (r6.1), tout en améliorant l’AP de 2,2 % (Source).

Performances
ModèleParamètres
(M)
FLOPs
(G)
Taille
(pixels)
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%

Présentation#

La détection d’objets en temps réel est un composant important de nombreux systèmes de vision par ordinateur, notamment le suivi multi-objets, la conduite autonome, la robotique et l’analyse d’images médicales. Ces dernières années, le développement de la détection d’objets en temps réel s’est concentré sur la conception d’architectures efficaces et l’amélioration de la vitesse d’inférence de divers CPU, GPU et unités de traitement neuronal (NPU). YOLOv7 prend en charge les appareils équipés de GPU mobiles et de GPU, de la périphérie au cloud.

Contrairement aux détecteurs d’objets traditionnels en temps réel, qui se concentrent sur l’optimisation de l’architecture, YOLOv7 met l’accent sur l’optimisation du processus d’entraînement. Cela inclut des modules et des méthodes d’optimisation conçus pour améliorer l’exactitude de la détection d’objets sans augmenter le coût d’inférence, un concept connu sous le nom de « sac d’astuces entraînables ».

Fonctionnalités clés#

YOLOv7 introduit plusieurs fonctionnalités clés :

  1. Reparamétrisation du modèle : YOLOv7 propose un modèle reparamétrisé planifié, une stratégie applicable aux couches de différents réseaux reposant sur le concept de chemin de propagation du gradient.

  2. Attribution dynamique des étiquettes : l’entraînement du modèle avec plusieurs couches de sortie soulève un nouveau problème : « Comment attribuer des cibles dynamiques aux sorties de différentes branches ? » Pour résoudre ce problème, YOLOv7 introduit une nouvelle méthode d’attribution des étiquettes appelée attribution d’étiquettes guidée par une branche principale, du grossier au fin.

  3. Mise à l’échelle étendue et composée : YOLOv7 propose des méthodes d’« extension » et de « mise à l’échelle composée » pour les détecteurs d’objets en temps réel, qui permettent d’exploiter efficacement les paramètres et les calculs.

  4. Efficacité : la méthode proposée par YOLOv7 peut réduire efficacement d’environ 40 % le nombre de paramètres et de 50 % les calculs par rapport aux détecteurs d’objets en temps réel SOTA, tout en offrant une vitesse d’inférence plus élevée et une meilleure exactitude de détection.

Exemples d’utilisation#

Ultralytics ne publie pas de poids préentraînés yolov7.pt ni de fichiers YAML ultralytics/cfg/models/v7/, et l’entraînement et l’inférence natifs de YOLOv7 avec PyTorch ne sont pas pris en charge par le package Python Ultralytics. Cependant, tu peux importer dans Ultralytics un checkpoint YOLOv7 entraîné dans le dépôt YOLOv7 en amont en l’exportant vers ONNX ou TensorRT, comme indiqué ci-dessous.

Exportation ONNX#

Pour utiliser le modèle YOLOv7 ONNX avec Ultralytics :

  1. (Facultatif) Installe le package Ultralytics et exporte un modèle ONNX afin que les dépendances requises soient installées automatiquement :

    pip install ultralytics
    yolo export model=yolo26n.pt format=onnx
  2. Exporte le modèle YOLOv7 souhaité à l’aide de l’outil d’exportation du dépôt 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. Modifie le graphe du modèle ONNX pour le rendre compatible avec Ultralytics à l’aide du script suivant :

    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. Tu peux ensuite charger le modèle ONNX modifié et exécuter normalement l’inférence avec celui-ci dans Ultralytics :

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

Exportation TensorRT#

  1. Suis les étapes 1 et 2 de la section Exportation ONNX.

  2. Installe le package Python TensorRT :

    pip install tensorrt
  3. Exécute le script suivant pour convertir le modèle ONNX modifié en moteur TensorRT :

    from ultralytics.utils.export import onnx2engine
    
    onnx2engine("yolov7-ultralytics.onnx", quantize=16)
  4. Charge et exécute le modèle dans Ultralytics :

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

Citations et remerciements#

Nous souhaitons remercier les auteurs de YOLOv7 pour leurs contributions majeures au domaine de la détection d’objets en temps réel :

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

L’article officiel sur YOLOv7 a été publié dans CVF 2023 Open Access, avec une prépublication sur arXiv. Les auteurs ont mis leur travail à disposition du public, et la base de code est accessible sur GitHub. Nous saluons leurs efforts pour faire progresser le domaine et rendre leur travail accessible à une communauté plus large.

FAQ#

  • YOLOv7, publié en juillet 2022, était un modèle important de détection d’objets en temps réel, offrant une excellente vitesse et une excellente exactitude à sa sortie. Il a surpassé des modèles contemporains tels que YOLOX, YOLOv5 et PPYOLOE en matière d’utilisation des paramètres et de vitesse d’inférence. Les caractéristiques distinctives de YOLOv7 incluent la reparamétrisation du modèle et l’attribution dynamique des étiquettes, qui optimisent ses performances sans augmenter les coûts d’inférence. Pour plus de détails techniques sur son architecture et ses métriques de comparaison avec d’autres détecteurs d’objets SOTA, consulte l’article sur YOLOv7.

  • YOLOv7 introduit plusieurs innovations, notamment la reparamétrisation du modèle et l’attribution dynamique des étiquettes, qui améliorent le processus d’entraînement et l’exactitude de l’inférence. Comparé à YOLOv5, YOLOv7 augmente considérablement la vitesse et l’exactitude. Par exemple, YOLOv7-X améliore l’exactitude de 2,2 % et réduit le nombre de paramètres de 22 % par rapport à YOLOv5-X. Des comparaisons détaillées sont disponibles dans le tableau de performances Comparaison de YOLOv7 avec des détecteurs d’objets SOTA.

  • À l’heure actuelle, Ultralytics prend uniquement en charge l’inférence YOLOv7 avec ONNX et TensorRT. Pour exécuter avec Ultralytics la version de YOLOv7 exportée vers ONNX et TensorRT, consulte la section Exemples d’utilisation.

  • Pour installer et entraîner un modèle YOLOv7 personnalisé, suis les étapes suivantes :

    1. Clone le dépôt YOLOv7 :

      git clone https://github.com/WongKinYiu/yolov7
    2. Accède au répertoire cloné et installe les dépendances :

      cd yolov7
      pip install -r requirements.txt
    3. Prépare ton jeu de données et configure les paramètres du modèle conformément aux instructions d’utilisation fournies dans le dépôt. Pour plus de conseils, consulte le dépôt YOLOv7 sur GitHub afin d’obtenir les dernières informations et mises à jour.

    4. Après l’entraînement, tu peux exporter le modèle vers ONNX ou TensorRT pour l’utiliser dans Ultralytics, comme indiqué dans les Exemples d’utilisation.

  • YOLOv7 offre plusieurs fonctionnalités clés qui révolutionnent la détection d’objets en temps réel :

    • Reparamétrisation du modèle : améliore les performances du modèle en optimisant les chemins de propagation du gradient.
    • Attribution dynamique des étiquettes : utilise une méthode guidée par une branche principale, du grossier au fin, pour attribuer des cibles dynamiques aux sorties des différentes branches, améliorant ainsi l’exactitude.
    • Mise à l’échelle étendue et composée : exploite efficacement les paramètres et les calculs pour adapter le modèle à diverses applications en temps réel.
    • Efficacité : réduit de 40 % le nombre de paramètres et de 50 % les calculs par rapport à d’autres modèles SOTA, tout en offrant des vitesses d’inférence supérieures.

    Pour plus de détails sur ces fonctionnalités, consulte la section Présentation de YOLOv7.

Commentaires