Ultralytics YOLO27: Tham gia danh sách chờ.
Get Started
Ultralytics YOLO trong môi trường production·Hiệu chuẩn và quan sát·Bài học 7/9
Bài họctrung cấp

Suy luận đa luồng

Chạy nhiều camera qua một model bằng suy luận theo batch, hàng đợi có giới hạn và số liệu frame bị mất được đo lường.

Một hệ thống xử lý kịp một camera có thể chậm trễ hàng phút khi tăng lên bốn camera mà không có lỗi nào xảy ra: kết quả chỉ đến muộn dần cho đến khi hết bộ nhớ. Trên CPU laptop được dùng ở đây, một luồng 30 FPS được xử lý kịp, bốn luồng mất một nửa số frame, còn tám luồng mất hơn hai phần ba. Bài học này xây dựng pipeline để đo con số đó, vì đây là cách xác định cấu hình phần cứng.

Kết quả

Chạy từ hai luồng trở lên qua một model, báo cáo số frame đã xử lý và bị mất cho từng luồng, rồi tìm số luồng mà phần cứng của bạn không còn xử lý kịp.

Lộ trình cấp tốc
Nếu bạn đã nắm rõ cách thực hiện, đây là phiên bản tóm tắt.
  1. Tích hợp sẵn: liệt kê các nguồn trong file .streams rồi gọi model.track("cams.streams", stream=True).

  2. stream_buffer=False (mặc định) chỉ giữ frame mới nhất của mỗi luồng; True xử lý mọi frame của file video đã ghi.

  3. Tùy chỉnh: các thread giải mã, một hàng đợi có giới hạn, một vòng lặp consumer sở hữu model và thực hiện batching.

  4. Đếm số frame bị mất trên mỗi luồng; năng lực xử lý là số luồng lớn nhất không mất frame ở FPS mục tiêu.

Thực hành

Thời gian được phân bổ ở đâu#

NVIDIA DeepStream chạy Ultralytics YOLO trên bốn luồng camera với tốc độ khoảng 12 FPS mỗi luồng trên Jetson, kèm nhật ký PERF của từng luồng bên cạnh lưới phát hiện 2×2

Mỗi frame đi qua bốn giai đoạn:

Giai đoạnChi phí điển hìnhChạy trên
Giải mã (RTSP hoặc file → mảng)5–20 ms mỗi frame 1080pCPU
Tiền xử lý (letterbox, chuẩn hóa)khoảng 1 msCPU
Suy luậnvài ms (GPU) đến vài chục ms (CPU)Bộ tăng tốc
Hậu xử lý, tracking, đầu ravài ms, lâu hơn với cơ sở dữ liệu chậmCPU

Chạy lần lượt từng giai đoạn cho từng camera trong một thread khiến bộ tăng tốc ở trạng thái chờ trong khi frame được giải mã. Giải pháp là tách riêng các giai đoạn, như pipeline DeepStream ở trên thực hiện: bốn luồng dùng chung một thiết bị, mỗi luồng báo cáo FPS riêng (khoảng 12 ở đây). Cấu trúc tương tự trong Python:

Tích hợp sẵn: file .streams#

Ultralytics tự động batch nhiều nguồn cho bạn. Đặt mỗi nguồn trên một dòng trong file .streams rồi truyền file đó vào chế độ Predict hoặc Track. Mỗi luồng có reader thread và tracker riêng, đồng thời mỗi vòng lặp chạy một lượt forward theo batch trên tất cả luồng:

from collections import Counter
from pathlib import Path

from ultralytics import YOLO
from ultralytics.utils.downloads import safe_download

base = "https://github.com/ultralytics/assets/releases/download/v0.0.0/"
clips = [safe_download(base + name) for name in ("solutions_ci_demo.mp4", "ground-vehicles-airport.mp4")]
Path("cams.streams").write_text("\n".join(str(c) for c in clips))  # production: một URL rtsp:// trên mỗi dòng

model = YOLO("yolo26n.pt")  # dùng bản export đã tối ưu trong production
frames = Counter()
for r in model.track("cams.streams", stream=True, stream_buffer=True, tracker="bytetrack.yaml", verbose=False):
    frames[Path(r.path).name] += 1  # r.path cho biết kết quả này đến từ luồng nào
print(frames)
Counter({'solutions_ci_demo.mp4': 62, 'ground-vehicles-airport.mp4': 62})

Cả hai luồng đều tạo ra 62 kết quả vì quá trình lặp dừng khi luồng ngắn nhất kết thúc (clip sân bay có 150 frame). stream_buffer=True xử lý mọi frame, phù hợp với file video đã ghi; với camera trực tiếp, hãy giữ mặc định False để model chậm bỏ qua các frame cũ thay vì ngày càng tụt lại. Kết hợp với bản export TensorRT hoặc OpenVINO để đạt thông lượng tốt nhất cho từng luồng.

Tùy chỉnh: Producer-Consumer với bộ đếm frame bị mất#

Ba sơ đồ thời gian: tiến trình đơn luồng lần lượt chờ từng yêu cầu I/O; tiến trình đa luồng chồng lấp các khoảng chờ I/O nhưng mỗi lần chỉ thread giữ GIL được xử lý; asyncio chồng lấp các khoảng chờ trong một thread

Sơ đồ cho thấy vì sao thread phù hợp với pipeline này. Giải mã chủ yếu là chờ (các thanh I/O màu hồng), còn OpenCV và PyTorch giải phóng GIL của Python trong khi xử lý, nên các thread giải mã có thể chồng lấp hiệu quả (khung b). Hậu xử lý Python nặng thì không thể chồng lấp; hãy chuyển phần đó sang các process riêng.

Khi cần đầu ra riêng, logic theo từng camera hoặc chỉ số frame bị mất tường minh, hãy tự xây dựng pipeline. Các thread giải mã không bao giờ truy cập model; một vòng lặp consumer duy nhất sở hữu model, đồng thời giúp bạn tuân thủ các quy tắc suy luận an toàn luồng:

import queue
import threading
import time

import cv2

from ultralytics import YOLO
from ultralytics.utils.downloads import safe_download

base = "https://github.com/ultralytics/assets/releases/download/v0.0.0/"
SOURCES = [str(safe_download(base + n)) for n in ("solutions_ci_demo.mp4", "ground-vehicles-airport.mp4")]
frames = queue.Queue(maxsize=16)  # bounded: memory and latency stay flat under overload
dropped = [0] * len(SOURCES)

def decode(stream_id, source):
    """Producer: read frames at the camera's pace and never block on a full queue."""
    cap = cv2.VideoCapture(source)
    interval = 1 / (cap.get(cv2.CAP_PROP_FPS) or 30)
    while True:
        ok, frame = cap.read()
        if not ok:
            break
        try:
            frames.put_nowait((stream_id, frame))
        except queue.Full:
            dropped[stream_id] += 1  # consumer is behind: drop rather than serve stale frames
        time.sleep(interval)  # pace files like a live camera; remove for RTSP sources
    cap.release()

model = YOLO("yolo26n.pt")  # one model, used only by the consumer loop below
model.predict("https://ultralytics.com/images/bus.jpg", verbose=False)  # warm up before cameras start

decoders = [threading.Thread(target=decode, args=(i, s), daemon=True) for i, s in enumerate(SOURCES)]
for t in decoders:
    t.start()

processed = [0] * len(SOURCES)
while any(t.is_alive() for t in decoders) or not frames.empty():
    batch = []
    while len(batch) < 8:  # gather up to 8 frames from any camera
        try:
            batch.append(frames.get(timeout=0.05))
        except queue.Empty:
            break
    if not batch:
        continue
    results = model([frame for _, frame in batch], verbose=False)  # one batched forward pass
    for (stream_id, _), r in zip(batch, results):
        processed[stream_id] += 1  # per-stream postprocessing and sinks go here

print(f"processed={processed} dropped={dropped}")
processed=[56, 131] dropped=[6, 19]

Hai clip có lần lượt 62 và 150 frame, và mọi frame đều được xử lý hoặc bị mất (56 + 6 = 62, 131 + 19 = 150), vì vậy bộ đếm tính đủ tất cả frame. Có 25 trong số 212 frame bị mất: CPU của laptop này không thể chạy yolo26n bằng PyTorch trên hai luồng 30 FPS. Lặp lại các clip để mô phỏng nhiều camera hơn trong một lượt kiểm tra:

Số luồng (mỗi luồng 30 FPS)Số frame bị mất
10%
212%
451%
869%

Lập kế hoạch năng lực dựa trên số liệu đo#

Số luồng trên mỗi thiết bị ≈ thông lượng duy trì (số frame model xử lý mỗi giây ở batch size của bạn) ÷ FPS yêu cầu trên mỗi luồng. Đo thông lượng bằng bản export đã tối ưu từ các bài học trước trên phần cứng thực tế, duy trì 20–30% dự phòng cho các đợt tăng tải và phát cảnh báo khi số frame bị mất lớn hơn không. Độ phân giải cao hơn làm tăng chi phí gần tương ứng với số pixel: 1280×1280 cần khoảng 4× năng lực tính toán so với 640×640. Với các hệ thống quy mô lớn, Triton Inference Server và DeepStream trên Jetson là những nền tảng production phù hợp.

Các lỗi thường gặp#

Truyền danh sách video Python

Triệu chứng: model.track(["cam1.mp4", "cam2.mp4"]) coi danh sách là một batch ảnh, không phải các luồng. Cách khắc phục: ghi các nguồn vào tệp .streams, mỗi nguồn một dòng.

stream_buffer mặc định trên các tệp đã ghi

Triệu chứng: mỗi tệp chỉ trả về một hoặc hai frame. Cách khắc phục: stream_buffer=False loại bỏ các frame cũ, còn trình đọc tệp chạy nhanh hơn model; đặt stream_buffer=True cho tệp và giữ giá trị mặc định cho camera trực tiếp.

Dùng chung một model giữa các luồng suy luận

Triệu chứng: thỉnh thoảng có kết quả sai hoặc lẫn lộn khi hệ thống tải cao. Cách khắc phục: không bao giờ gọi cùng một instance YOLO đồng thời từ nhiều luồng. Hãy để một consumer sở hữu instance đó như trên, hoặc tạo model bên trong mỗi luồng suy luận.

Đưa mọi frame vào hàng đợi

Triệu chứng: độ trễ tăng lên theo từng phút và bộ nhớ tăng cho đến khi tiến trình bị dừng. Cách khắc phục: dùng hàng đợi có giới hạn, loại bỏ phần tử khi hàng đợi đầy và đếm số frame bị loại bỏ. Hệ thống thời gian thực nên bỏ frame khi quá tải thay vì để độ trễ tăng.

Dùng thử

Chạy producer_consumer.py với các nguồn của bạn (hoặc hai clip demo), sau đó dùng script lời giải để tăng dần số luồng cho đến khi bắt đầu mất frame. Cung cấp bảng thể hiện số luồng so với tỷ lệ frame bị mất trên phần cứng của bạn, cùng số luồng lớn nhất không mất frame ở FPS mục tiêu.

Điều kiện hoàn thành
Bạn đã hoàn thành bài học khi tất cả các điều kiện sau đều đúng.
  • Có từ hai luồng trở lên chạy qua một model và mọi kết quả đều được gắn với luồng tương ứng.

  • Pipeline của bạn sử dụng hàng đợi có giới hạn và báo cáo số frame đã xử lý cũng như bị mất trên từng luồng; tổng hai số này bằng số frame đã đọc.

  • Bạn có thể xác định bằng số liệu đo số luồng mà tại đó phần cứng bắt đầu làm mất frame.

Hiển thị lời giải

Thêm các dòng sau vào producer_consumer.py, ngay sau dòng SOURCES, để biến số luồng thành đối số dòng lệnh:

import sys

N = int(sys.argv[1]) if len(sys.argv) > 1 else 2  # số camera được mô phỏng
SOURCES = [SOURCES[i % len(SOURCES)] for i in range(N)]

Sau đó tăng dần số lượng:

for n in 1 2 4 8; do python producer_consumer.py $n; done
processed=[62] dropped=[0]
processed=[56, 131] dropped=[6, 19]
processed=[23, 82, 23, 81] dropped=[39, 68, 39, 69]
processed=[14, 53, 13, 53, 13, 52, 13, 53] dropped=[48, 97, 49, 97, 49, 98, 49, 97]

Kết quả trên máy này là một luồng: bắt đầu mất frame khi chạy hai luồng. Để xử lý bốn camera, hãy export sang runtime nhanh hơn (bài học 3 và 4), giảm imgsz hoặc chuyển sang bộ tăng tốc, rồi chạy lại phép kiểm tra.

Tiếp theo là gì

Chúng ta đã có thể chạy ở quy mô lớn. Tiếp theo: giám sát hệ thống đủ tốt để phát hiện khi hiệu năng âm thầm suy giảm.

Đăng nhập để lưu tiến độ