Khả năng quan sát và drift
Ghi nhật ký các metric phát hiện tình trạng giảm độ chính xác âm thầm, kiểm tra phân phối confidence để phát hiện drift và chỉ cảnh báo về các tín hiệu có thể hành động.
Một dịch vụ thị giác máy tính có thể đang hoạt động, nhanh nhưng cho kết quả sai. Camera bị xê dịch, ánh sáng thay đổi, một dòng sản phẩm mới xuất hiện, nhưng model vẫn trả về các hộp bao quanh trong khi mọi dashboard hạ tầng đều báo trạng thái tốt. Trong bài học này, việc làm tối clip để mô phỏng chạng vạng chỉ khiến confidence trung bình thay đổi 0.01, khó nhận ra trên biểu đồ, nhưng kiểm định KS hai mẫu đã phát hiện thay đổi ở p = 0.007. Bạn sẽ xây dựng phép kiểm tra đó và hệ thống ghi nhật ký cung cấp dữ liệu cho phép kiểm tra.
Thực hành
Ba lớp tín hiệu#
| Lớp | Câu hỏi | Tín hiệu |
|---|---|---|
| Hạ tầng | Dịch vụ có hoạt động và xử lý kịp không? | Độ trễ, thông lượng, frame bị mất, mức sử dụng CPU và GPU |
| Hành vi của model | Model có hoạt động như tuần trước không? | Số detection mỗi frame, confidence trung bình, cơ cấu class, kích thước hộp |
| Độ chính xác của model | Model vẫn đưa ra kết quả đúng chứ? | mAP trên tập giữ lại (metric hiệu năng), đánh giá thủ công trên mẫu, tỷ lệ cảnh báo giả |
Hầu hết nhóm bao phủ lớp đầu tiên bằng các công cụ tiêu chuẩn rồi dừng lại. Lớp thứ hai và thứ ba là nơi các hệ thống thị giác máy tính gặp lỗi âm thầm; hướng dẫn giám sát model trình bày toàn bộ quy trình bảo trì.

Nếu triển khai qua Ultralytics Platform, trang triển khai cung cấp lớp hạ tầng (request, tỷ lệ lỗi, độ trễ P95) và nhật ký request có thể lọc theo lỗi như minh họa ở trên; bài học Giám sát tình trạng và độ chính xác trình bày các nội dung này. Bạn cần tự xây dựng lớp hành vi và độ chính xác; phần còn lại của bài học này sẽ hướng dẫn cách thực hiện.
Xuất metric cho từng frame#
Mỗi đối tượng Results đều chứa thông tin thời gian và hộp. Ghi một dòng JSON cho mỗi frame rồi gửi đến hệ thống metric bạn đang dùng:
import json
import time
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
video = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
with open("metrics.jsonl", "w") as log:
for r in model.predict(video, stream=True, verbose=False):
conf = r.boxes.conf
record = {
"ts": time.time(),
"camera": "lobby-1",
"inference_ms": round(r.speed["inference"], 1),
"detections": len(r.boxes),
"mean_conf": round(conf.mean().item(), 3) if len(conf) else None,
"confs": [round(c, 3) for c in conf.tolist()], # các giá trị thô dùng cho kiểm định drift
}
log.write(json.dumps(record) + "\n")
print({k: v for k, v in record.items() if k != "confs"}){'ts': 1792003254.33, 'camera': 'lobby-1', 'inference_ms': 34.7, 'detections': 22, 'mean_conf': 0.543}Công dụng của từng trường:
- inference_ms: cảnh báo khi p95 vượt quá ngân sách.
- detections: số lượng giảm đột ngột xuống gần bằng không thường có nghĩa camera bị chặn hoặc hỏng; tăng vọt nghĩa là cảnh đã thay đổi hoặc xuất hiện nhiều false positive.
- mean_conf: đường xu hướng nhanh, nhưng quá thô để tự phát hiện drift.
- confs: toàn bộ phân phối, cần thiết cho phép kiểm tra drift bên dưới.
Kiểm tra drift trong phân phối độ tin cậy#
Data drift thể hiện trong độ tin cậy từ lâu trước khi có người gán nhãn dữ liệu mới. Thu thập độ tin cậy lúc khởi chạy làm đường cơ sở, thu thập một cửa sổ dữ liệu gần đây, rồi so sánh hai phân phối bằng kiểm định Kolmogorov-Smirnov hai mẫu (pip install scipy). Script này mô phỏng thời điểm chạng vạng bằng cách làm tối cùng một đoạn video:
import cv2
import numpy as np
from scipy.stats import ks_2samp
from ultralytics import YOLO
from ultralytics.utils.downloads import safe_download
model = YOLO("yolo26n.pt")
video = str(safe_download("https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"))
def confidences(darken=1.0):
"""Collect every detection confidence from the clip, optionally darkened to simulate dusk."""
cap, out = cv2.VideoCapture(video), []
while True:
ok, frame = cap.read()
if not ok:
break
out += model((frame * darken).astype(np.uint8), verbose=False)[0].boxes.conf.tolist()
return np.array(out)
baseline = confidences() # what "normal" looked like at launch
for name, recent in (("same scene", confidences()), ("dusk (35% brightness)", confidences(darken=0.35))):
stat, p = ks_2samp(baseline, recent)
print(f"{name:22s} n={len(recent):4d} mean_conf={recent.mean():.3f} KS={stat:.3f} p={p:.2g}")same scene n=1376 mean_conf=0.519 KS=0.000 p=1
dusk (35% brightness) n=1337 mean_conf=0.508 KS=0.064 p=0.0068Đọc lần lượt từng dòng:
- Cùng cảnh: các phân phối giống hệt nhau cho thống kê KS bằng 0 và p = 1, đây là trường hợp đối chứng.
- Chạng vạng: số detections giảm 39 và giá trị trung bình chỉ thấp hơn 0.011, mức chênh lệch không ai nhận ra trên biểu đồ. Thống kê KS (khoảng cách lớn nhất giữa hai phân phối tích lũy) là 0.064, và p = 0.0068 < 0.01 cho thấy độ lệch là có thật.
Cảnh báo drift là lý do để xem các frame mẫu, không phải bằng chứng cho thấy hệ thống bị lỗi. Khi drift là có thật, hãy thu thập và gán nhãn frame từ các camera bị ảnh hưởng: hướng dẫn thu thập và chú thích dữ liệu trình bày những gì cần thu thập, còn hướng dẫn fine-tune trình bày quy trình huấn luyện lại. Kích thước bounding box (width × height) là một tín hiệu drift dễ theo dõi khác: các giá treo và ống kính mới thường thể hiện thay đổi ở đó trước tiên.
Xác thực tập holdout hằng tuần#
Các tín hiệu hành vi cho biết có điều gì đó đã thay đổi; chỉ nhãn mới cho biết độ chính xác có giảm hay không. Duy trì một tập holdout gồm 200–500 ảnh production đã gán nhãn và xác thực mọi phiên bản model, cũng như hằng tuần:
yolo val model=yolo26n.pt data=coco8.yaml > weekly_metrics.txt # use your production model and holdout YAMLGhi lại mAP50-95 và AP theo từng lớp theo thời gian, đồng thời cảnh báo khi mAP giảm hơn 1 điểm so với tuần trước. Nguyên nhân phổ biến nhất là thay model trong khi dự kiến không làm thay đổi kết quả; nguyên nhân thứ hai là drift. Confusion matrix trên cùng tập holdout giúp dễ nhận ra các hồi quy theo từng lớp.

Tập holdout giúp trả lời câu hỏi trong biểu đồ này. Hãy huấn luyện lại khi tập cho thấy chất lượng bắt đầu giảm, hoặc khi bạn có đủ dữ liệu mới được gán nhãn từ các điều kiện đã drift, thay vì theo một lịch cố định.
Cảnh báo dựa trên hành động, không phải con số#
| Tín hiệu | Kích hoạt khi | Hành động |
|---|---|---|
p95 inference_ms | Vượt ngân sách trong hơn 3 phút | Mở rộng quy mô hoặc chuyển sang bản export đã tối ưu |
| Detections trên mỗi frame | Giảm từ 50% trở lên trong 5 phút | Kiểm tra luồng camera, sau đó kiểm tra phiên bản model |
| Frame bị rớt | Vượt 5% trong 10 phút | Bổ sung capacity hoặc giảm FPS trên mỗi luồng |
| Kiểm định KS độ tin cậy | p < 0.01 trên các cửa sổ dữ liệu hằng ngày | Xem lại frame, sau đó chạy kiểm tra holdout |
| mAP50-95 trên tập holdout | Giảm từ 1 điểm trở lên so với tuần trước | Thu thập dữ liệu, gán nhãn và fine-tune |
Các lỗi thường gặp#
Triệu chứng: kiểm định KS kích hoạt vào mỗi buổi tối và thứ Hai hàng tuần. Cách khắc phục: chu kỳ theo ngày và tuần là những thay đổi phân phối thực sự. Hãy so sánh dữ liệu tương đồng (cùng camera, cùng giờ, cùng thứ trong tuần) và dùng các cửa sổ gồm hàng nghìn detection.
Triệu chứng: mAP trên holdout có vẻ ổn nhưng người dùng báo bỏ sót đối tượng. Cách khắc phục: holdout phải phản ánh production hiện tại. Cập nhật mỗi quý bằng các frame mới được gán nhãn từ những camera quan trọng.
Mỗi cảnh báo cần có hành động tiếp theo được ghi rõ. Tín hiệu bạn chỉ muốn theo dõi nên nằm trên dashboard, không phải trong hệ thống gửi cảnh báo đến người trực.
Hiển thị lời giải
Giữ cửa sổ đầu tiên có trạng thái ổn định từ đầu ra monitor.py làm baseline.jsonl, sau đó so sánh từng cửa sổ mới với cửa sổ đó:
import json
import numpy as np
from scipy.stats import ks_2samp
def load(path):
"""Flatten the per-frame 'confs' lists written by monitor.py."""
with open(path) as f:
return np.array([c for line in f for c in json.loads(line)["confs"]])
baseline, recent = load("baseline.jsonl"), load("metrics.jsonl")
stat, p = ks_2samp(baseline, recent)
print(f"KS={stat:.3f} p={p:.2g} -> {'review frames' if p < 0.01 else 'ok'}")KS=0.000 p=1 -> okĐây là kết quả dự kiến khi cả hai tệp đều được tạo từ cùng một đoạn video. Chạy quy trình này hằng ngày bằng bộ lập lịch, gửi thống kê và p-value đến cùng hệ thống metrics dùng cho log theo từng frame, và chuyển review frames đến một người phụ trách thay vì hệ thống gọi báo động.