YOLOv7: Trainable Bag-of-Freebies

2022年7月にリリースされたYOLOv7は、当時のリアルタイム物体検出において重要な進歩を遂げました。GPU V100上で56.8%のAPを達成し、登場時に新たなベンチマークを確立しました。YOLOv7は、速度と精度の面で、当時のYOLOR、YOLOX、Scaled-YOLOv4、YOLOv5といった物体検出モデルを上回りました。このモデルは、他のデータセットや事前学習済みウェイトを使用せず、MS COCOデータセットからゼロから学習されています。YOLOv7のソースコードはGitHubで公開されています。なお、現在ではYOLO11YOLO26のような新しいモデルが、効率を向上させつつ、さらに高い精度を達成しています。

SOTA物体検出モデルとYOLOv7の比較

SOTA物体検出モデルの比較

YOLOの比較表の結果から、提案手法が速度と精度のトレードオフにおいて包括的に最も優れていることがわかります。YOLOv7-tiny-SiLUとYOLOv5-N (r6.1)を比較すると、提案手法は127 fps高速であり、APにおいて10.7%高い精度を誇ります。さらに、YOLOv7は161 fpsのフレームレートで51.4%のAPを達成していますが、同等のAPを持つPPYOLOE-Lのフレームレートはわずか78 fpsです。パラメータ使用量に関しては、YOLOv7はPPYOLOE-Lより41%少なくなっています。

推論速度114 fpsのYOLOv7-Xと、推論速度99 fpsのYOLOv5-L (r6.1)を比較すると、YOLOv7-XはAPを3.9%向上させることができます。YOLOv7-Xを同規模のYOLOv5-X (r6.1)と比較した場合、YOLOv7-Xの推論速度は31 fps高速です。さらに、パラメータ数と計算量の点では、YOLOv7-XはYOLOv5-X (r6.1)と比較してパラメータを22%、計算量を8%削減しつつ、APを2.2%向上させています(出典)。

性能
モデルパラメータ数
(M)
FLOPs
(G)
サイズ
(ピクセル)
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%

概要

Real-time object detection is an important component in many computer vision systems, including multi-object tracking, autonomous driving, robotics, and medical image analysis. In recent years, real-time object detection development has focused on designing efficient architectures and improving the inference speed of various CPUs, GPUs, and neural processing units (NPUs). YOLOv7 supports both mobile GPU and GPU devices, from the edge to the cloud.

従来のアーキテクチャの最適化に注力するリアルタイム物体検出器とは異なり、YOLOv7はトレーニングプロセスの最適化に焦点を当てています。これには、推論コストを増加させることなく物体検出の精度を向上させるように設計されたモジュールと最適化手法が含まれており、これは「trainable bag-of-freebies」として知られる概念です。

主な特徴

YOLOv7にはいくつかの重要な機能が導入されています。

  1. Model Re-parameterization: YOLOv7は、勾配伝播パスの概念を用いて、異なるネットワークのレイヤーに適用可能な戦略である計画的な再パラメータ化モデルを提案しています。

  2. Dynamic Label Assignment: 複数の出力レイヤーを持つモデルのトレーニングは、「異なるブランチの出力に対してどのように動的なターゲットを割り当てるか?」という新しい課題を提示します。この問題を解決するために、YOLOv7は「coarse-to-fine lead guided label assignment」と呼ばれる新しいラベル割り当て手法を導入しています。

  3. Extended and Compound Scaling: YOLOv7は、パラメータと計算を効率的に利用できる、リアルタイム物体検出器のための「extend」および「compound scaling」手法を提案しています。

  4. Efficiency: YOLOv7によって提案された手法は、最先端のリアルタイム物体検出器と比較して、パラメータを約40%、計算量を約50%削減することが可能であり、より高速な推論速度と高い検出精度を実現しています。

使用例

Ultralyticsはyolov7.ptの事前学習済み重みやultralytics/cfg/models/v7/のYAMLを提供しておらず、Ultralytics PythonパッケージではYOLOv7のネイティブなPyTorchトレーニングおよび推論はサポートされていません。ただし、upstream YOLOv7 repositoryでトレーニングされたYOLOv7チェックポイントを、以下に示すようにONNXまたはTensorRTにエクスポートすることで、Ultralyticsで使用することができます。

ONNX Export

YOLOv7 ONNXモデルをUltralyticsで使用するには:

  1. (オプション) Ultralyticsをインストールし、必要な依存関係を自動的にインストールするためにONNXモデルをエクスポートします:

    pip install ultralytics
    yolo export model=yolo26n.pt format=onnx
  2. YOLOv7 repoのエクスポーターを使用して、目的の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. 以下のスクリプトを使用して、Ultralyticsと互換性を持つようにONNXモデルグラフを修正します:

    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. その後、修正されたONNXモデルを読み込み、通常通りUltralyticsで推論を実行できます:

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

TensorRT Export

  1. ONNX Exportセクションのステップ1〜2に従います。

  2. TensorRT Pythonパッケージをインストールします:

    pip install tensorrt
  3. 以下のスクリプトを実行して、修正されたONNXモデルをTensorRTエンジンに変換します:

    from ultralytics.utils.export import export_engine
    
    export_engine("yolov7-ultralytics.onnx", half=True)
  4. Ultralyticsでモデルを読み込んで実行します:

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

引用と謝辞

リアルタイム物体検出の分野における重要な貢献に対し、YOLOv7の著者に謝意を表します:

引用
@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}
}

オリジナルのYOLOv7の論文はarXivでご覧いただけます。著者は研究成果を公開しており、コードベースはGitHubからアクセス可能です。私たちは、この分野を発展させ、その研究成果をより広いコミュニティで利用できるようにした彼らの努力に感謝します。

FAQ

YOLOv7とは何ですか?また、なぜリアルタイムobject detectionにおけるブレイクスルーと見なされているのですか?

2022年7月にリリースされたYOLOv7は、リリース当時、優れた速度と精度を実現した重要なリアルタイム物体検出モデルでした。パラメータ使用量と推論速度の両方で、YOLOX、YOLOv5、PPYOLOEなどの当時のモデルを凌駕しました。YOLOv7の際立った特徴には、推論コストを増加させることなくパフォーマンスを最適化する「モデル再パラメータ化」と「動的ラベル割り当て」があります。アーキテクチャや他の最先端物体検出器との比較指標に関する技術的な詳細は、YOLOv7論文を参照してください。

YOLOv7は、YOLOv4やYOLOv5のような以前のYOLOモデルからどのように改善されていますか?

YOLOv7は、トレーニングプロセスを強化し推論精度を向上させる、モデル再パラメータ化や動的ラベル割り当てを含むいくつかの革新を導入しています。YOLOv5と比較して、YOLOv7は速度と精度を大幅に向上させました。例えば、YOLOv7-XはYOLOv5-Xと比較して精度が2.2%向上し、パラメータが22%削減されています。詳細な比較は、YOLOv7 comparison with SOTA object detectorsのパフォーマンス表で確認できます。

UltralyticsのツールやプラットフォームでYOLOv7を使用できますか?

現在、UltralyticsはYOLOv7のONNXおよびTensorRT推論のみをサポートしています。YOLOv7のONNXおよびTensorRTエクスポートバージョンをUltralyticsで実行するには、Usage Examplesセクションを確認してください。

データセットを使用してカスタムYOLOv7モデルをトレーニングするにはどうすればよいですか?

カスタムYOLOv7モデルをインストールしてトレーニングするには、以下の手順に従ってください:

  1. YOLOv7リポジトリをクローンします:

    git clone https://github.com/WongKinYiu/yolov7
  2. クローンしたディレクトリに移動し、依存関係をインストールします:

    cd yolov7
    pip install -r requirements.txt
  3. リポジトリで提供されているusage instructionsに従ってデータセットを準備し、モデルパラメータを設定します。さらなるガイダンスについては、YOLOv7 GitHubリポジトリにアクセスして最新の情報とアップデートを確認してください。

  4. トレーニング後、Usage Examplesに示すように、モデルをONNXまたはTensorRTにエクスポートしてUltralyticsで使用できます。

YOLOv7で導入された主な機能と最適化は何ですか?

YOLOv7は、リアルタイム物体検出を革新するいくつかの主要な機能を提供します:

  • Model Re-parameterization: 勾配伝播パスを最適化することでモデルのパフォーマンスを向上させます。
  • Dynamic Label Assignment: 「coarse-to-fine lead guided」手法を使用して、異なるブランチ全体の出力に対して動的なターゲットを割り当て、精度を向上させます。
  • Extended and Compound Scaling: パラメータと計算を効率的に利用し、さまざまなリアルタイムアプリケーション向けにモデルをスケーリングします。
  • Efficiency: 他の最先端モデルと比較して、パラメータ数を40%、計算量を50%削減しながら、より高速な推論速度を実現します。

これらの機能の詳細については、YOLOv7 Overviewセクションを参照してください。

コメント