YOLOv7: Trainable Bag-of-Freebies

YOLOv7, được ra mắt vào tháng 7 năm 2022, là một bước tiến quan trọng trong lĩnh vực phát hiện đối tượng thời gian thực vào thời điểm đó. Model này đạt được 56,8% AP trên GPU V100, thiết lập các tiêu chuẩn mới khi được giới thiệu. YOLOv7 vượt trội hơn các detector đối tượng cùng thời như YOLOR, YOLOX, Scaled-YOLOv4 và YOLOv5 về tốc độ và độ chính xác. Model được huấn luyện từ đầu trên bộ dữ liệu MS COCO mà không sử dụng bất kỳ bộ dữ liệu nào khác hoặc trọng số được huấn luyện trước. Mã nguồn cho YOLOv7 có sẵn trên GitHub. Lưu ý rằng các model mới hơn như YOLO11YOLO26 đã đạt được độ chính xác cao hơn với hiệu suất cải thiện.

So sánh YOLOv7 với các detector đối tượng SOTA

So sánh các detector đối tượng SOTA

Từ kết quả trong bảng so sánh YOLO, chúng ta biết rằng phương pháp được đề xuất có sự cân bằng tốt nhất giữa tốc độ và độ chính xác một cách toàn diện. Nếu so sánh YOLOv7-tiny-SiLU với YOLOv5-N (r6.1), phương pháp của chúng tôi nhanh hơn 127 fps và chính xác hơn 10,7% về AP. Ngoài ra, YOLOv7 đạt 51,4% AP ở tốc độ khung hình 161 fps, trong khi PPYOLOE-L với cùng chỉ số AP chỉ đạt tốc độ khung hình 78 fps. Về mức sử dụng tham số, YOLOv7 thấp hơn 41% so với PPYOLOE-L.

Nếu so sánh YOLOv7-X với tốc độ suy luận 114 fps với YOLOv5-L (r6.1) có tốc độ suy luận 99 fps, YOLOv7-X có thể cải thiện 3,9% AP. Nếu YOLOv7-X được so sánh với YOLOv5-X (r6.1) ở quy mô tương đương, tốc độ suy luận của YOLOv7-X nhanh hơn 31 fps. Ngoài ra, về số lượng tham số và tính toán, YOLOv7-X giảm 22% tham số và 8% tính toán so với YOLOv5-X (r6.1), nhưng cải thiện 2,2% AP (Nguồn).

Hiệu suất
ModelTham số
(M)
FLOPs
(G)
Kích thước
(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%

Tổng quan

Phát hiện đối tượng thời gian thực là một thành phần quan trọng trong nhiều hệ thống thị giác máy tính, bao gồm theo dõi đối tượng, lái xe tự động, robot họcphân tích hình ảnh y tế. Trong những năm gần đây, sự phát triển của công nghệ phát hiện đối tượng thời gian thực tập trung vào việc thiết kế các kiến trúc hiệu quả và cải thiện tốc độ suy luận trên nhiều CPU, GPU và bộ xử lý thần kinh (NPU). YOLOv7 hỗ trợ cả thiết bị GPU di động và GPU chuyên dụng, từ biên cho đến đám mây.

Không giống như các bộ phát hiện đối tượng thời gian thực truyền thống tập trung vào tối ưu hóa kiến trúc, YOLOv7 giới thiệu trọng tâm vào việc tối ưu hóa quy trình đào tạo. Điều này bao gồm các module và phương pháp tối ưu hóa được thiết kế để cải thiện độ chính xác của phát hiện đối tượng mà không làm tăng chi phí suy luận, một khái niệm được gọi là "trainable bag-of-freebies".

Các tính năng chính

YOLOv7 giới thiệu một số tính năng chính:

  1. Tái tham số hóa mô hình (Model Re-parameterization): YOLOv7 đề xuất một mô hình tái tham số hóa có kế hoạch, đây là một chiến lược có thể áp dụng cho các lớp trong các mạng khác nhau với khái niệm về lộ trình lan truyền gradient.

  2. Gán nhãn động (Dynamic Label Assignment): Việc đào tạo mô hình với nhiều lớp đầu ra tạo ra một vấn đề mới: "Làm thế nào để gán mục tiêu động cho các đầu ra của các nhánh khác nhau?" Để giải quyết vấn đề này, YOLOv7 giới thiệu một phương pháp gán nhãn mới gọi là gán nhãn có hướng dẫn từ thô đến tinh (coarse-to-fine lead guided label assignment).

  3. Mở rộng và mở rộng hợp nhất (Extended and Compound Scaling): YOLOv7 đề xuất các phương pháp "mở rộng" (extend) và "mở rộng hợp nhất" (compound scaling) cho bộ phát hiện đối tượng thời gian thực, giúp tận dụng hiệu quả các tham số và tính toán.

  4. Hiệu suất (Efficiency): Phương pháp được YOLOv7 đề xuất có thể giảm hiệu quả khoảng 40% tham số và 50% tính toán so với các bộ phát hiện đối tượng thời gian thực hiện đại nhất, đồng thời có tốc độ suy luận nhanh hơn và độ chính xác phát hiện cao hơn.

Ví dụ sử dụng

Ultralytics không công bố trọng số tiền huấn luyện yolov7.pt hoặc các tệp YAML ultralytics/cfg/models/v7/, và việc đào tạo cũng như suy luận PyTorch nguyên bản cho YOLOv7 không được hỗ trợ bởi gói Python Ultralytics. Tuy nhiên, bạn có thể mang một checkpoint YOLOv7 đã được đào tạo trong kho lưu trữ YOLOv7 gốc vào Ultralytics bằng cách xuất nó sang định dạng ONNX hoặc TensorRT, như được hiển thị dưới đây.

Xuất ONNX

Để sử dụng mô hình YOLOv7 ONNX với Ultralytics:

  1. (Tùy chọn) Cài đặt Ultralytics và xuất một mô hình ONNX để các phụ thuộc bắt buộc được tự động cài đặt:

    pip install ultralytics
    yolo export model=yolo26n.pt format=onnx
  2. Xuất mô hình YOLOv7 mong muốn bằng cách sử dụng bộ xuất trong kho lưu trữ 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. Chỉnh sửa đồ thị mô hình ONNX để tương thích với Ultralytics bằng tập lệnh sau:

    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. Sau đó, bạn có thể tải mô hình ONNX đã chỉnh sửa và chạy suy luận với nó trong Ultralytics như bình thường:

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

Xuất TensorRT

  1. Thực hiện theo các bước 1-2 trong phần Xuất ONNX.

  2. Cài đặt gói Python TensorRT:

    pip install tensorrt
  3. Chạy tập lệnh sau để chuyển đổi mô hình ONNX đã chỉnh sửa sang engine TensorRT:

    from ultralytics.utils.export import export_engine
    
    export_engine("yolov7-ultralytics.onnx", half=True)
  4. Tải và chạy mô hình trong Ultralytics:

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

Trích dẫn và Ghi nhận

Chúng tôi muốn ghi nhận các tác giả YOLOv7 vì những đóng góp quan trọng của họ trong lĩnh vực phát hiện đối tượng thời gian thực:

Trích dẫn
@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}
}

Bài báo gốc về YOLOv7 có thể được tìm thấy trên arXiv. Các tác giả đã công bố công trình của họ và cơ sở mã có thể được truy cập trên GitHub. Chúng tôi đánh giá cao những nỗ lực của họ trong việc thúc đẩy lĩnh vực này và làm cho công trình của họ dễ tiếp cận hơn với cộng đồng rộng lớn.

Câu hỏi thường gặp (FAQ)

YOLOv7 là gì và tại sao nó được coi là một bước đột phá trong phát hiện đối tượng thời gian thực?

YOLOv7, ra mắt vào tháng 7 năm 2022, là một mô hình phát hiện đối tượng thời gian thực quan trọng đạt được tốc độ và độ chính xác xuất sắc tại thời điểm ra mắt. Nó đã vượt qua các mô hình đương thời như YOLOX, YOLOv5 và PPYOLOE cả về mức sử dụng tham số và tốc độ suy luận. Các tính năng nổi bật của YOLOv7 bao gồm tái tham số hóa mô hình và gán nhãn động, giúp tối ưu hóa hiệu suất mà không làm tăng chi phí suy luận. Để biết thêm chi tiết kỹ thuật về kiến trúc và các chỉ số so sánh với các bộ phát hiện đối tượng hiện đại khác, hãy tham khảo bài báo YOLOv7.

YOLOv7 cải tiến như thế nào so với các mô hình YOLO trước đây như YOLOv4 và YOLOv5?

YOLOv7 giới thiệu một số đổi mới, bao gồm tái tham số hóa mô hình và gán nhãn động, giúp tăng cường quy trình đào tạo và cải thiện độ chính xác suy luận. So với YOLOv5, YOLOv7 tăng đáng kể tốc độ và độ chính xác. Ví dụ, YOLOv7-X cải thiện độ chính xác thêm 2,2% và giảm 22% số lượng tham số so với YOLOv5-X. Các so sánh chi tiết có thể được tìm thấy trong bảng hiệu suất So sánh YOLOv7 với các bộ phát hiện đối tượng SOTA.

Tôi có thể sử dụng YOLOv7 với các công cụ và nền tảng của Ultralytics không?

Hiện tại, Ultralytics chỉ hỗ trợ suy luận YOLOv7 ONNX và TensorRT. Để chạy phiên bản YOLOv7 đã xuất sang ONNX và TensorRT với Ultralytics, hãy kiểm tra phần Ví dụ sử dụng.

Làm thế nào để tôi đào tạo một mô hình YOLOv7 tùy chỉnh bằng bộ dữ liệu của mình?

Để cài đặt và đào tạo một mô hình YOLOv7 tùy chỉnh, hãy làm theo các bước sau:

  1. Clone kho lưu trữ YOLOv7:

    git clone https://github.com/WongKinYiu/yolov7
  2. Điều hướng đến thư mục đã clone và cài đặt các phụ thuộc:

    cd yolov7
    pip install -r requirements.txt
  3. Chuẩn bị bộ dữ liệu của bạn và cấu hình các tham số mô hình theo hướng dẫn sử dụng được cung cấp trong kho lưu trữ. Để được hướng dẫn thêm, hãy truy cập kho lưu trữ GitHub của YOLOv7 để có thông tin và cập nhật mới nhất.

  4. Sau khi đào tạo, bạn có thể xuất mô hình sang ONNX hoặc TensorRT để sử dụng trong Ultralytics như đã hiển thị trong Ví dụ sử dụng.

Đâu là các tính năng chính và tối ưu hóa được giới thiệu trong YOLOv7?

YOLOv7 cung cấp một số tính năng chính tạo ra cuộc cách mạng trong phát hiện đối tượng thời gian thực:

  • Tái tham số hóa mô hình: Nâng cao hiệu suất của mô hình bằng cách tối ưu hóa các lộ trình lan truyền gradient.
  • Gán nhãn động: Sử dụng phương pháp có hướng dẫn từ thô đến tinh để gán các mục tiêu động cho các đầu ra trên các nhánh khác nhau, giúp cải thiện độ chính xác.
  • Mở rộng và mở rộng hợp nhất: Tận dụng hiệu quả các tham số và tính toán để mở rộng mô hình cho nhiều ứng dụng thời gian thực khác nhau.
  • Hiệu suất: Giảm 40% số lượng tham số và 50% tính toán so với các mô hình hiện đại khác trong khi vẫn đạt được tốc độ suy luận nhanh hơn.

Để biết thêm chi tiết về các tính năng này, hãy xem phần Tổng quan về YOLOv7.

Bình luận