YOLOv7: Trainierbare Bag-of-Freebies
YOLOv7 ist ein hochmoderner Echtzeit-Objektdetektor, der alle bekannten Objektdetektoren sowohl in der Geschwindigkeit als auch in der Genauigkeit im Bereich von 5 FPS bis 160 FPS übertrifft. Er hat die höchste Genauigkeit (56,8 % AP) unter allen bekannten Echtzeit-Objektdetektoren mit 30 FPS oder mehr auf GPU V100. Darüber hinaus übertrifft YOLOv7 andere Objektdetektoren wie YOLOR, YOLOX, Scaled-YOLOv4, YOLOv5 und viele andere in Geschwindigkeit und Genauigkeit. Das Modell wurde von Grund auf mit dem MS COCO-Datensatz trainiert, ohne dass andere Datensätze oder vortrainierte Gewichte verwendet wurden. Der Quellcode für YOLOv7 ist auf GitHub verfügbar.
Vergleich der SOTA-Objektdetektoren
Aus den Ergebnissen in der YOLO geht hervor, dass die vorgeschlagene Methode das beste Verhältnis zwischen Geschwindigkeit und Genauigkeit aufweist. Wenn wir YOLOv7-tiny-SiLU mit YOLOv5(r6.1) vergleichen, ist unsere Methode 127 fps schneller und 10,7 % genauer bei der AP. Darüber hinaus hat YOLOv7 51,4% AP bei einer Framerate von 161 fps, während PPYOLOE-L mit der gleichen AP nur 78 fps Framerate hat. In Bezug auf die Verwendung von Parametern ist YOLOv7 41% weniger als PPYOLOE-L.
Vergleicht man YOLOv7-X mit einer Inferenzgeschwindigkeit von 114 fps mit YOLOv5(r6.1) mit einer Inferenzgeschwindigkeit von 99 fps, kann YOLOv7-X die AP um 3,9 % verbessern. Vergleicht man YOLOv7-X mit YOLOv5(r6.1) in ähnlicher Größenordnung, so ist die Inferenzgeschwindigkeit von YOLOv7-X um 31 fps schneller. Darüber hinaus reduziert YOLOv7-X im Vergleich zu YOLOv5(r6.1) 22 % der Parameter und 8 % der Berechnungen, verbessert aber die AP um 2,2 %(Quelle).
Leistung
Modell | Params (M) |
FLOPs (G) |
Größe (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-winzig-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% |
Übersicht
Die Objekterkennung in Echtzeit ist eine wichtige Komponente in vielen Computer-Vision-Systemen, darunter dieVerfolgung mehrerer Objekte, autonomes Fahren, Robotik und medizinische Bildanalyse. In den letzten Jahren hat sich die Entwicklung der Echtzeit-Objekterkennung auf die Entwicklung effizienter Architekturen und die Verbesserung der Inferenzgeschwindigkeit verschiedener CPUs, GPUs und neuronaler Verarbeitungseinheiten (NPUs) konzentriert. YOLOv7 unterstützt sowohl mobile GPU als auch GPU , vom Edge bis zur Cloud.
Im Gegensatz zu herkömmlichen Echtzeit-Objektdetektoren, die sich auf die Optimierung der Architektur konzentrieren, legt YOLOv7 den Schwerpunkt auf die Optimierung des Trainingsprozesses. Dazu gehören Module und Optimierungsmethoden, die die Genauigkeit der Objekterkennung verbessern, ohne die Inferenzkosten zu erhöhen - ein Konzept, das als "trainierbare Bag-of-Freebies" bekannt ist.
Wesentliche Merkmale
YOLOv7 führt mehrere wichtige Funktionen ein:
-
Modell-Neuparametrisierung: YOLOv7 schlägt ein geplantes neu parametrisiertes Modell vor, eine Strategie, die auf Schichten in verschiedenen Netzen mit dem Konzept des Gradientenausbreitungspfades anwendbar ist.
-
Dynamische Label-Zuweisung: Das Training eines Modells mit mehreren Ausgabeschichten wirft ein neues Problem auf: "Wie kann man dynamische Ziele für die Ausgänge der verschiedenen Zweige zuweisen?" Um dieses Problem zu lösen, führt YOLOv7 eine neue Methode der Label-Zuweisung ein, die grob-zu-fein-geführte Label-Zuweisung.
-
Erweiterte und zusammengesetzte Skalierung: YOLOv7 schlägt "erweiterte" und "zusammengesetzte Skalierungsmethoden" für den Echtzeit-Objektdetektor vor, mit denen Parameter und Berechnungen effektiv genutzt werden können.
-
Effizienz: Die von YOLOv7 vorgeschlagene Methode kann etwa 40 % der Parameter und 50 % der Berechnungen des modernsten Echtzeit-Objektdetektors reduzieren und bietet eine schnellere Inferenzgeschwindigkeit und höhere Erkennungsgenauigkeit.
Beispiele für die Verwendung
Zum Zeitpunkt der Erstellung dieses Artikels unterstützt Ultralytics nur ONNX und TensorRT für YOLOv7.
ONNX
Zur Verwendung des YOLOv7 ONNX mit Ultralytics:
- (Optional) Installieren Sie Ultralytics und exportieren Sie ein ONNX , um die erforderlichen Abhängigkeiten automatisch installieren zu lassen:
pip install ultralytics
yolo export model=yolo11n.pt format=onnx
- Exportieren Sie das gewünschte YOLOv7-Modell mit Hilfe des Exporters aus dem YOLOv7-Repo:
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
- Ändern Sie den ONNX so, dass er mit Ultralytics kompatibel ist, indem Sie das folgende Skript verwenden:
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")
- Sie können dann das geänderte ONNX laden und die Inferenz mit ihm in Ultralytics normal ausführen:
from ultralytics import ASSETS, YOLO
model = YOLO("yolov7-ultralytics.onnx", task="detect")
results = model(ASSETS / "bus.jpg")
TensorRT
-
Führen Sie die Schritte 1-2 im Abschnitt ONNX aus.
-
Installieren Sie die
TensorRT
Python :
pip install tensorrt
- Führen Sie das folgende Skript aus, um das modifizierte ONNX in die TensorRT zu konvertieren:
# Based off of https://github.com/NVIDIA/TensorRT/blob/release/10.7/samples/python/introductory_parser_samples/onnx_resnet50.py
import tensorrt as trt
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
def GiB(val):
return val * 1 << 30
def build_engine_onnx(model_file):
builder = trt.Builder(TRT_LOGGER)
network = builder.create_network(0)
config = builder.create_builder_config()
parser = trt.OnnxParser(network, TRT_LOGGER)
config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, GiB(4))
with open(model_file, "rb") as model:
if not parser.parse(model.read()):
print("ERROR: Failed to parse the ONNX file.")
for error in range(parser.num_errors):
print(parser.get_error(error))
return None
config.set_flag(trt.BuilderFlag.FP16)
engine_bytes = builder.build_serialized_network(network, config)
with open(model_file.replace("onnx", "engine"), "wb") as f:
f.write(engine_bytes)
build_engine_onnx("yolov7-ultralytics.onnx") # path to the modified ONNX file
- Laden Sie das Modell in Ultralytics und führen Sie es aus:
from ultralytics import ASSETS, YOLO
model = YOLO("yolov7-ultralytics.engine", task="detect")
results = model(ASSETS / "bus.jpg")
Zitate und Danksagungen
Wir möchten den YOLOv7-Autoren für ihre bedeutenden Beiträge auf dem Gebiet der Echtzeit-Objekterkennung danken:
@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}
}
Die ursprüngliche YOLOv7-Veröffentlichung ist auf arXiv zu finden. Die Autoren haben ihre Arbeit öffentlich zugänglich gemacht, und die Codebasis kann auf GitHub abgerufen werden. Wir schätzen ihre Bemühungen, das Feld voranzubringen und ihre Arbeit einer breiteren Gemeinschaft zugänglich zu machen.
FAQ
Was ist YOLOv7 und warum wird es als ein Durchbruch in der Echtzeit-Objekterkennung angesehen?
YOLOv7 ist ein hochmodernes Echtzeit-Objekterkennungsmodell, das eine beispiellose Geschwindigkeit und Genauigkeit erreicht. Es übertrifft andere Modelle wie YOLOX, YOLOv5 und PPYOLOE sowohl bei der Nutzung der Parameter als auch bei der Geschwindigkeit der Schlussfolgerungen. Zu den besonderen Merkmalen von YOLOv7 gehören die Neuparametrisierung des Modells und die dynamische Label-Zuweisung, die seine Leistung optimieren, ohne die Inferenzkosten zu erhöhen. Weitere technische Details über die Architektur und Vergleichsmetriken mit anderen hochmodernen Objektdetektoren finden Sie in der YOLOv7-Veröffentlichung.
Wie verbessert sich YOLOv7 gegenüber den Vorgängermodellen YOLO wie YOLOv4 und YOLOv5?
YOLOv7 führt mehrere Innovationen ein, darunter die Neuparametrisierung des Modells und die dynamische Zuweisung von Bezeichnungen, die den Trainingsprozess verbessern und die Genauigkeit der Schlussfolgerungen erhöhen. Im Vergleich zu YOLOv5 steigert YOLOv7 die Geschwindigkeit und Genauigkeit erheblich. Beispielsweise verbessert YOLOv7-X die Genauigkeit um 2,2 % und reduziert die Parameter um 22 % im Vergleich zu YOLOv5-X. Detaillierte Vergleiche finden Sie in der Leistungstabelle YOLOv7 Vergleich mit SOTA-Objektdetektoren.
Kann ich YOLOv7 mit Ultralytics Tools und Plattformen verwenden?
Zurzeit unterstützt Ultralytics nur YOLOv7 ONNX und TensorRT Inferenz. Um die exportierte ONNX und TensorRT von YOLOv7 mit Ultralytics auszuführen, sehen Sie sich den Abschnitt Anwendungsbeispiele an.
Wie trainiere ich ein benutzerdefiniertes YOLOv7-Modell mit meinem Datensatz?
Um ein benutzerdefiniertes YOLOv7-Modell zu installieren und zu trainieren, gehen Sie folgendermaßen vor:
- Klonen Sie das YOLOv7-Repository:
git clone https://github.com/WongKinYiu/yolov7
- Navigieren Sie zu dem geklonten Verzeichnis und installieren Sie die Abhängigkeiten:
cd yolov7 pip install -r requirements.txt
-
Bereiten Sie Ihren Datensatz vor und konfigurieren Sie die Modellparameter gemäß den im Repository bereitgestellten Nutzungsanweisungen. Für weitere Anleitungen besuchen Sie das YOLOv7 GitHub-Repository für die neuesten Informationen und Updates.
-
Nach dem Training können Sie das Modell in ONNX oder TensorRT exportieren, um es in Ultralytics zu verwenden, wie in den Verwendungsbeispielen gezeigt.
Was sind die wichtigsten Funktionen und Optimierungen, die in YOLOv7 eingeführt wurden?
YOLOv7 bietet mehrere wichtige Funktionen, die die Objekterkennung in Echtzeit revolutionieren:
- Modell-Neuparametrisierung: Verbessert die Leistung des Modells durch Optimierung der Gradientenausbreitungswege.
- Dynamische Label-Zuweisung: Verwendet eine grob bis fein geführte Methode zur Zuweisung dynamischer Ziele für Ausgänge über verschiedene Zweige hinweg und verbessert so die Genauigkeit.
- Erweiterte und zusammengesetzte Skalierung: Effiziente Nutzung von Parametern und Berechnungen zur Skalierung des Modells für verschiedene Echtzeitanwendungen.
- Effizienz: Reduziert die Anzahl der Parameter um 40 % und den Rechenaufwand um 50 % im Vergleich zu anderen hochmodernen Modellen und erreicht gleichzeitig eine schnellere Inferenzgeschwindigkeit.
Weitere Einzelheiten zu diesen Funktionen finden Sie im Abschnitt YOLOv7 Überblick.