Ultralytics YOLO27:

YOLOv7: Bag-of-Freebies có thể huấn luyện#

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

So sánh YOLOv7 với các bộ phát hiện đối tượng SOTA

So sánh các bộ phát hiện đối tượng SOTA#

Từ các 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ự đánh đổi toàn diện tốt nhất giữa tốc độ và độ chính xác. 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 có cùng AP chỉ đạt tốc độ khung hình 78 fps. Về mức sử dụng tham số, YOLOv7 ít hơn PPYOLOE-L 41%.

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

Hiệu năng
ModelSố tham số
(M)
FLOPs
(G)
Kích thước
(pixel)
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 theo 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 nhiều đối tượng, lái xe tự hành, roboticsphân tích ảnh y tế. Trong những năm gần đây, việc phát triển các hệ thống phát hiện đối tượng theo thời gian thực tập trung vào thiết kế các kiến trúc hiệu quả và cải thiện tốc độ inference trên nhiều CPU, GPU và đơn vị xử lý thần kinh (NPUs). YOLOv7 hỗ trợ cả thiết bị GPU di động và GPU, từ edge đến cloud.

Khác với các bộ phát hiện đối tượng theo thời gian thực truyền thống tập trung vào tối ưu hóa kiến trúc, YOLOv7 chú trọng vào việc tối ưu hóa quy trình huấn luyện. Đ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 phát hiện đối tượng mà không làm tăng chi phí inference, một khái niệm được gọi là "bag-of-freebies có thể huấn luyện".

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 Model: YOLOv7 đề xuất một model được tái tham số hóa có chủ đích, đây là một chiến lược có thể áp dụng cho các layer trong những network khác nhau dựa trên khái niệm đường truyền gradient.

  2. Gán Label Động: Việc huấn luyện model với nhiều layer đầu ra đặt ra một vấn đề mới: "Làm thế nào để gán các target động cho đầu ra của những 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 label mới có tên là gán label dẫn hướng từ thô đến tinh.

  3. Mở rộng và Scaling Hợp nhất: YOLOv7 đề xuất các phương pháp "mở rộng" và "scaling hợp nhất" cho bộ phát hiện đối tượng theo thời gian thực, có thể tận dụng hiệu quả các tham số và phép tính.

  4. Hiệu quả: Phương pháp được YOLOv7 đề xuất có thể giảm hiệu quả khoảng 40% số tham số và 50% lượng tính toán của bộ phát hiện đối tượng theo thời gian thực SOTA, đồng thời có tốc độ inference nhanh hơn và độ chính xác phát hiện cao hơn.

Ví dụ sử dụng#

Ultralytics không phát hành pretrained weights yolov7.pt hoặc YAML ultralytics/cfg/models/v7/, và việc huấn luyện cũng như inference YOLOv7 native bằng PyTorch không được Python package Ultralytics hỗ trợ. Tuy nhiên, bạn có thể đưa checkpoint YOLOv7 được huấn luyện trong repository YOLOv7 upstream vào Ultralytics bằng cách export sang ONNX hoặc TensorRT, như minh họa bên dưới.

Export ONNX#

Để sử dụng model YOLOv7 ONNX với Ultralytics:

  1. (Tùy chọn) Cài đặt Ultralytics và export một model ONNX để các dependency bắt buộc được tự động cài đặt:

    pip install ultralytics
    yolo export model=yolo26n.pt format=onnx
  2. Export model YOLOv7 mong muốn bằng exporter trong 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. Sửa đổi graph của model ONNX để tương thích với Ultralytics bằng script 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ể load model ONNX đã sửa đổi và chạy inference với model này trong Ultralytics như bình thường:

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

Export TensorRT#

  1. Thực hiện các bước 1-2 trong phần Export ONNX.

  2. Cài đặt Python package TensorRT:

    pip install tensorrt
  3. Chạy script sau để chuyển đổi model ONNX đã sửa đổi thành TensorRT engine:

    from ultralytics.utils.export import onnx2engine
    
    onnx2engine("yolov7-ultralytics.onnx", quantize=16)
  4. Load và chạy model 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 đóng góp#

Chúng tôi xin ghi nhận những đóng góp đáng kể của các tác giả YOLOv7 trong lĩnh vực phát hiện đối tượng theo thời gian thực:

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

Bài báo YOLOv7 chính thức được công bố trong CVF 2023 Open Access, cùng với bản preprint trên arXiv. Các tác giả đã công khai công trình của mình và có thể truy cập codebase trên GitHub. Chúng tôi trân trọng những nỗ lực của họ trong việc thúc đẩy lĩnh vực này và giúp cộng đồng rộng lớn hơn tiếp cận công trình.

FAQ#

  • YOLOv7, được phát hành vào tháng 7 năm 2022, là một model phát hiện đối tượng theo thời gian thực đáng chú ý, đạt tốc độ và độ chính xác xuất sắc tại thời điểm ra mắt. Model này vượt qua các model đương thời như YOLOX, YOLOv5 và PPYOLOE cả về mức sử dụng tham số lẫn tốc độ inference. Các tính năng nổi bật của YOLOv7 bao gồm tái tham số hóa model và gán label động, giúp tối ưu hiệu năng mà không làm tăng chi phí inference. Để 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 những bộ phát hiện đối tượng SOTA khác, hãy tham khảo bài báo YOLOv7.

  • YOLOv7 giới thiệu một số cải tiến, bao gồm tái tham số hóa model và gán label động, giúp nâng cao quy trình huấn luyện và cải thiện độ chính xác inference. 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 2.2% và giảm 22% số tham số so với YOLOv5-X. Có thể xem các so sánh chi tiết trong bảng hiệu năng So sánh YOLOv7 với các bộ phát hiện đối tượng SOTA.

  • Hiện tại, Ultralytics chỉ hỗ trợ inference YOLOv7 trên ONNX và TensorRT. Để chạy phiên bản YOLOv7 được export sang ONNX và TensorRT với Ultralytics, hãy xem phần Ví dụ sử dụng.

  • Để cài đặt và huấn luyện một model YOLOv7 tùy chỉnh, hãy thực hiện các bước sau:

    1. Clone repository YOLOv7:

      git clone https://github.com/WongKinYiu/yolov7
    2. Di chuyển đến thư mục đã clone và cài đặt các dependency:

      cd yolov7
      pip install -r requirements.txt
    3. Chuẩn bị dataset và cấu hình các tham số model theo hướng dẫn sử dụng được cung cấp trong repository. Để được hướng dẫn thêm, hãy truy cập repository YOLOv7 trên GitHub để xem thông tin và các bản cập nhật mới nhất.

    4. Sau khi huấn luyện, bạn có thể export model sang ONNX hoặc TensorRT để sử dụng trong Ultralytics như minh họa trong Ví dụ sử dụng.

  • YOLOv7 cung cấp một số tính năng chính mang tính cách mạng cho việc phát hiện đối tượng theo thời gian thực:

    • Tái tham số hóa Model: Cải thiện hiệu năng của model bằng cách tối ưu hóa các đường truyền gradient.
    • Gán Label Động: Sử dụng phương pháp dẫn hướng từ thô đến tinh để gán các target động cho đầu ra trên những nhánh khác nhau, qua đó cải thiện độ chính xác.
    • Mở rộng và Scaling Hợp nhất: Tận dụng hiệu quả các tham số và phép tính để scale model cho nhiều ứng dụng thời gian thực khác nhau.
    • Hiệu quả: Giảm 40% số tham số và 50% lượng tính toán so với các model SOTA khác, đồng thời đạt tốc độ inference 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