YOLOv7: Trainable Bag-of-Freebies#
2022年7月にリリースされたYOLOv7は、リリース当時、リアルタイム物体検出において重要な進歩を遂げました。V100 GPU上で56.8%のAPを達成し、導入時に新たなベンチマークを設定しました。YOLOv7は、速度とaccuracyの面で、YOLOR、YOLOX、Scaled-YOLOv4、YOLOv5などの当時の物体検出モデルを上回りました。モデルは他のデータセットや事前学習済み重みを使用せず、最初からMS COCOデータセットで学習されています。YOLOv7のソースコードはGitHubで公開されています。なお、YOLO11やYOLO26などの新しいモデルは、その後効率が向上し、より高い精度を達成していることにご注意ください。

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%向上させています(Source)。
| モデル | パラメータ (M) | FLOPs (G) | サイズ (ピクセル) | FPS | APテスト / 検証 50-95 | APテスト 50 | APテスト 75 | APテスト S | APテスト M | APテスト 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% |
概要#
リアルタイム物体検出は、マルチオブジェクトトラッキング、自動運転、ロボティクス、医療画像解析など、多くのコンピュータビジョンシステムにおいて重要な構成要素です。近年、リアルタイム物体検出の開発は、効率的なアーキテクチャの設計と、さまざまなCPU、GPU、ニューラルプロセッシングユニット(NPU)における推論速度の向上に焦点を当てています。YOLOv7は、エッジからクラウドまで、モバイルGPUとGPUデバイスの両方をサポートしています。
アーキテクチャの最適化に重点を置く従来のリアルタイム物体検出器とは異なり、YOLOv7は学習プロセスの最適化に焦点を当てています。これには、推論コストを増加させることなく物体検出の精度を向上させるように設計されたモジュールや最適化手法が含まれており、このコンセプトは「訓練可能なBag-of-Freebies」として知られています。
主な特徴#
YOLOv7にはいくつかの重要な機能が導入されています。
-
モデルの再パラメータ化: YOLOv7は、勾配伝播パスのコンセプトを用いて、異なるネットワーク内のレイヤーに適用可能な戦略である、計画的な再パラメータ化モデルを提案しています。
-
動的ラベル割り当て: 複数の出力レイヤーを持つモデルの学習には、「異なるブランチの出力に対してどのように動的なターゲットを割り当てるか?」という新たな課題があります。この問題を解決するために、YOLOv7は「粗から細へ(coarse-to-fine)」のリードガイド付きラベル割り当てと呼ばれる新しいラベル割り当て手法を導入しました。
-
拡張および複合スケーリング: YOLOv7は、パラメータと計算量を効果的に活用できる、リアルタイム物体検出器のための「拡張(extend)」および「複合スケーリング(compound scaling)」手法を提案しています。
-
効率性: YOLOv7が提案する手法は、最先端のリアルタイム物体検出器と比較して、パラメータ数を約40%、計算量を約50%削減することができ、より高速な推論速度と高い検出精度を実現しています。
使用例#
Ultralyticsは yolov7.pt の事前学習済み重みや ultralytics/cfg/models/v7/ のYAMLを公開しておらず、YOLOv7のネイティブなPyTorch学習と推論はUltralytics Pythonパッケージではサポートされていません。ただし、以下に示すように、上流のYOLOv7リポジトリで学習したYOLOv7チェックポイントをONNXまたはTensorRTにエクスポートすることで、Ultralyticsに取り込むことができます。
ONNXエクスポート#
YOLOv7 ONNXモデルをUltralyticsで使用するには、以下の手順に従います。
-
(オプション)必要な依存関係を自動的にインストールするために、UltralyticsをインストールしてONNXモデルをエクスポートします:
pip install ultralytics yolo export model=yolo26n.pt format=onnx -
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 -
以下のスクリプトを使用して、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") -
その後、変更されたONNXモデルを読み込み、Ultralyticsで通常通り推論を実行できます:
from ultralytics import ASSETS, YOLO model = YOLO("yolov7-ultralytics.onnx", task="detect") results = model(ASSETS / "bus.jpg")
TensorRTエクスポート#
-
ONNX Exportセクションのステップ1~2に従います。
-
TensorRTPythonパッケージをインストールします。pip install tensorrt -
以下のスクリプトを実行して、変更されたONNXモデルをTensorRTエンジンに変換します:
from ultralytics.utils.export import export_engine export_engine("yolov7-ultralytics.onnx", half=True) -
Ultralyticsでモデルを読み込んで実行します:
from ultralytics import ASSETS, YOLO model = YOLO("yolov7-ultralytics.engine", task="detect") results = model(ASSETS / "bus.jpg")
引用と謝辞#
リアルタイム物体検出の分野に対する多大な貢献に対し、YOLOv7の著者に感謝の意を表します:
@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}
}YOLOv7の公式論文はCVF 2023 Open Accessに掲載され、プレプリントはarXivに公開されています。著者は研究成果を一般に公開しており、コードベースにはGitHubからアクセスできます。分野の発展に貢献し、より広いコミュニティが研究を利用できるようにしてくださった著者の努力に感謝いたします。
よくある質問 (FAQ)#
YOLOv7とは何ですか?また、なぜリアルタイムobject detectionにおけるブレイクスルーとみなされているのですか?#
2022年7月にリリースされたYOLOv7は、リリース当時、優れた速度と精度を達成した重要なリアルタイム物体検出モデルでした。パラメータの使用量と推論速度の両方において、YOLOX、YOLOv5、PPYOLOEなどの当時のモデルを凌駕しました。YOLOv7の際立った特徴には、推論コストを増加させることなくパフォーマンスを最適化するモデルの再パラメータ化と動的ラベル割り当てがあります。そのアーキテクチャや他の最先端の物体検出モデルとの比較指標に関する詳細な技術情報については、YOLOv7 paperをご参照ください。
YOLOv7は、YOLOv4やYOLOv5といった従来のYOLOモデルと比較してどのように改善されていますか?#
YOLOv7は、モデルの再パラメータ化や動的ラベル割り当てなど、いくつかの革新を導入しており、これによって学習プロセスが強化され、推論精度が向上します。YOLOv5と比較して、YOLOv7は速度と精度を大幅に向上させます。例えば、YOLOv7-Xは、YOLOv5-Xと比較して、精度が2.2%向上し、パラメータが22%削減されます。詳細な比較は、パフォーマンス表のYOLOv7 comparison with SOTA object detectorsに記載されています。
YOLOv7をUltralyticsのツールやプラットフォームで使用できますか?#
現在、UltralyticsはYOLOv7のONNXおよびTensorRT推論のみをサポートしています。UltralyticsでYOLOv7のONNXおよびTensorRTエクスポート版を実行するには、Usage Examplesセクションをご確認ください。
カスタムデータセットを使用してカスタムYOLOv7モデルを学習するにはどうすればよいですか?#
カスタムYOLOv7モデルをインストールして学習するには、以下の手順に従ってください:
-
YOLOv7リポジトリをクローンします:
git clone https://github.com/WongKinYiu/yolov7 -
クローンしたディレクトリに移動し、依存関係をインストールします:
cd yolov7 pip install -r requirements.txt -
リポジトリに提供されているusage instructionsに従って、データセットを準備し、モデルパラメータを設定してください。 詳細なガイダンスについては、YOLOv7のGitHubリポジトリで最新の情報と更新をご確認ください。
-
学習後、Usage Examplesに示されているように、Ultralyticsで使用するためにモデルをONNXまたはTensorRTにエクスポートできます。
YOLOv7で導入された主な機能と最適化は何ですか?#
YOLOv7は、リアルタイム物体検出を革新するいくつかの主要な機能を提供します:
- モデルの再パラメータ化: 勾配伝播パスを最適化することでモデルの性能を向上させます。
- 動的ラベル割り当て: 「粗から細へ」のリードガイド付き手法を使用して、異なるブランチ間の出力に対して動的にターゲットを割り当て、精度を向上させます。
- 拡張および複合スケーリング: パラメータと計算量を効率的に活用し、さまざまなリアルタイムアプリケーション向けにモデルをスケーリングします。
- 効率性: 他の最先端モデルと比較して、パラメータ数を40%、計算量を50%削減しながら、より高速な推論速度を実現しています。
これらの機能の詳細については、YOLOv7 Overviewセクションをご覧ください。