Ultralytics YOLO27: Tham gia danh sách chờ.
Get Started
Xây dựng Dataset YOLO Hiệu năng Cao·Thu thập·Bài học 3/10
Bài họcngười mới bắt đầu

Thu thập dữ liệu chất lượng cao, đại diện

Lấy ảnh từ điều kiện triển khai thực tế, đưa metadata thu thập vào từng tên tệp, theo dõi độ bao phủ so với bản đặc tả và lấy mẫu video mà không làm tràn dataset.

Phần lớn thất bại của dataset bắt nguồn từ khâu thu thập. Một nhóm ghi lại các ví dụ rõ nét từ một camera trong một tuần, kết quả validation rất tốt, nhưng model lại hoạt động kém ở địa điểm thứ hai. Khâu thu thập đặt ra giới hạn tối đa: chất lượng nhãn, augmentation hay hyperparameter đều không thể giúp model học một cảnh chưa từng có trong dataset.

Kết quả

Xây dựng dataset có tên tệp chứa camera, ngày và khung hình; báo cáo độ bao phủ so với các chỉ tiêu trong bản đặc tả; đồng thời lấy mẫu video thành các khung hình khác biệt.

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. Thu thập từ các camera sản xuất mà model sẽ chạy trên đó.

  2. Đặt tên cho mọi tệp theo camera_day_frame để các bước sau có thể nhóm và kiểm tra dữ liệu.

  3. Đếm số ảnh theo camera, ngày và kịch bản so với bản đặc tả, rồi thu thập thêm để lấp các khoảng thiếu.

  4. Lấy mẫu video sao cho chỉ giữ lại các khung hình có cảnh thay đổi.

  5. Chỉ dùng ảnh công khai, dàn dựng hoặc tổng hợp để lấp các khoảng thiếu đã xác định.

Thực hành

Độ đa dạng quan trọng hơn số lượng#

So sánh hai dataset hướng đến cùng một môi trường triển khai:

DatasetKết quả validationTrong môi trường production
10.000 khung hình gần như giống hệt nhau, một camera, chỉ vào buổi trưaCao: khung hình validation giống khung hình trainHiệu suất giảm khi dùng camera khác và vào lúc chạng vạng
1.500 khung hình trải đều trên 12 kịch bản trong bản đặc tảKhiêm tốn hơnHoạt động ổn định vì môi trường production giống dữ liệu

Điểm validation của dataset đầu tiên chỉ cao vì validation đo lường đúng điều kiện duy nhất mà dataset bao phủ. Trong production, model chỉ tốt bằng kịch bản được bao phủ kém nhất mà model gặp phải. Hãy ưu tiên thu thập dữ liệu để bao phủ bản đặc tả trước, rồi mới tăng số lượng.

Chọn nguồn theo thứ tự phù hợp#

Phần lớn dataset nên đến từ thiết lập production thực tế. Hướng dẫn thu thập và gán nhãn dữ liệu trình bày chi tiết hơn về từng nguồn:

NguồnKhi nào hữu íchKhi nào gây bất lợi
Camera production, trực tiếpGần với môi trường triển khai nhấtChậm; có thể cần thỏa thuận chia sẻ dữ liệu
Camera production, lưu trữNhanh chóng có được nhiều tình huống đa dạngVideo cũ có thể không phù hợp với thiết lập hiện tại
Dataset công khai (COCO, Open Images, VOC)Khởi tạo với các lớp phổ biếnKhác biệt miền dữ liệu so với cảnh của bạn
Ảnh chụp dàn dựngTạo các trường hợp biên cụ thể theo yêu cầuQuá sạch; dễ phụ thuộc quá mức
Ảnh tổng hợp hoặc ảnh được tạoCác mối nguy và sự cố hiếm gặp mà bạn không thể chờ đợi để ghi lạiModel có thể học phong cách của bộ tạo ảnh
Thu thập dữ liệu webĐa dạng với chi phí thấpChất lượng không đồng nhất và rủi ro về giấy phép

Dùng ảnh dàn dựng, công khai và tổng hợp để lấp các khoảng thiếu cụ thể đã nêu trong bản đặc tả, đồng thời theo dõi tỷ lệ của chúng trong dataset để đo lường hiệu quả. Trên Ultralytics Platform, Tạo ảnh tương tự bổ sung các biến thể được tạo từ một ảnh hiện có vào dataset; ảnh chưa được gán nhãn và sẵn sàng để chú thích.

Đưa metadata vào tên tệp#

Mỗi ảnh cần có ít nhất ID camera, ngày thu thập và số khung hình hoặc clip. Nếu thiếu những thông tin này, bạn không thể chia dữ liệu theo nhóm sau này, truy nguyên hồi quy về nguồn hoặc thu thập thêm dữ liệu cho một kịch bản chưa được đại diện đầy đủ. Tên tệp là nơi lưu trữ bền vững nhất: cam2_2026-05-01_000124.jpg tồn tại qua mọi công cụ, lần xuất và lần tải lên. Tính năng tìm kiếm dataset của Ultralytics Platform hỗ trợ tìm theo tên tệp và metadata tùy chỉnh, vì vậy các khóa này vẫn có thể tìm kiếm sau khi tải lên.

Script này tạo dataset thực hành được dùng trong phần còn lại của khóa học. Script sao chép các ảnh đã gán nhãn của COCO128 và đổi tên chúng như thể bốn camera đã ghi lại ảnh trong bốn ngày, trong đó cam1 được thu thập quá mức như cách một camera tiện dụng thường gặp:

import shutil
from pathlib import Path

from ultralytics.data.utils import check_det_dataset, img2label_paths

data = check_det_dataset("coco128.yaml")  # 128 COCO images stand in for your camera captures
out = Path("datasets/practice")
(out / "images").mkdir(parents=True, exist_ok=True)
(out / "labels").mkdir(parents=True, exist_ok=True)

for i, image in enumerate(sorted(Path(data["train"]).glob("*.jpg"))):
    camera = ("cam1", "cam1", "cam2", "cam3", "cam4")[i % 5]  # cam1 over-collected, like a real site
    name = f"{camera}_2026-05-0{i // 32 + 1}_{i:06d}"  # camera, capture day, frame number
    shutil.copy(image, out / "images" / f"{name}.jpg")
    label = Path(img2label_paths([str(image)])[0])
    if label.exists():
        shutil.copy(label, out / "labels" / f"{name}.txt")

print(len(list((out / "images").glob("*.jpg"))), "images written to", out)
128 images written to datasets/practice

Hộp thoại New Dataset của Ultralytics Platform: thả ảnh, video hoặc dataset ở định dạng YOLO, COCO hoặc NDJSON vào, sau đó đặt tên dataset, URL, mô tả, loại tác vụ, giấy phép, làm mờ khuôn mặt và chế độ hiển thị

Tải lên Ultralytics Platform vẫn giữ nguyên các tên tệp đó. Hộp thoại tải lên cũng chấp nhận video và toàn bộ dataset, đồng thời có tùy chọn làm mờ khuôn mặt để bảo vệ quyền riêng tư.

Theo dõi độ bao phủ so với bản đặc tả#

Khi metadata đã có trong tên tệp, độ bao phủ có thể được biểu thị bằng số lượng. Đặt chỉ tiêu theo bản đặc tả và để script cho biết cần thu thập gì tiếp theo:

from collections import Counter
from pathlib import Path

QUOTA = {"cam1": 30, "cam2": 30, "cam3": 30, "cam4": 30}  # images per camera, from your spec

images = sorted(Path("datasets/practice/images").glob("*.jpg"))
cameras = Counter(p.stem.split("_")[0] for p in images)
days = Counter(p.stem.split("_")[1] for p in images)

for camera, quota in QUOTA.items():
    gap = quota - cameras[camera]
    print(f"{camera}: {cameras[camera]:3d} / {quota}  {'ok' if gap <= 0 else f'collect {gap} more'}")
print("days:", dict(sorted(days.items())))
cam1:  52 / 30  ok
cam2:  26 / 30  collect 4 more
cam3:  25 / 30  collect 5 more
cam4:  25 / 30  collect 5 more
days: {'2026-05-01': 32, '2026-05-02': 32, '2026-05-03': 32, '2026-05-04': 32}

Quy tắc ra quyết định: ngừng thu thập từ mọi nguồn đã vượt chỉ tiêu và dành công sức cho các khoảng thiếu. Ở đây, cam1 đã chiếm 40% số ảnh. Thu thập thêm video từ nguồn này trong một tuần nữa sẽ làm tăng số lượng nhưng không cải thiện độ bao phủ.

Lấy mẫu video mà không làm tràn dataset#

Video là nguồn có chi phí thấp nhất và cũng dễ bị sử dụng sai nhất. Ở tốc độ 30 khung hình/giây, một phút tạo ra 1.800 khung hình gần như giống hệt nhau; gán nhãn cùng một khoảnh khắc hai lần vừa tốn tiền vừa làm tăng chỉ số. Chỉ giữ một khung hình khi cảnh đã thay đổi so với khung hình được giữ gần nhất:

from pathlib import Path

import cv2

from ultralytics.utils.downloads import safe_download

video = "solutions_ci_demo.mp4"  # a short public demo clip; swap in your own footage
safe_download(f"https://github.com/ultralytics/assets/releases/download/v0.0.0/{video}")
Path("frames").mkdir(exist_ok=True)

cap = cv2.VideoCapture(video)
fps = cap.get(cv2.CAP_PROP_FPS)
read = kept = 0
last = None
while True:
    ok, frame = cap.read()
    if not ok:
        break
    thumb = cv2.cvtColor(cv2.resize(frame, (64, 64)), cv2.COLOR_BGR2GRAY).astype(float)
    if last is None or abs(thumb - last).mean() > 10:  # keep a frame only when the scene has changed
        cv2.imwrite(f"frames/clip01_{read:06d}.jpg", frame)  # the clip ID stays in the filename
        kept, last = kept + 1, thumb
    read += 1

print(f"{read} frames at {fps:.0f} FPS -> {kept} kept")
62 frames at 30 FPS -> 6 kept

Clip dài hai giây có 62 khung hình nhưng chỉ chứa 6 khoảnh khắc khác biệt về mặt hình ảnh. Script so sánh các thumbnail thang xám có kích thước nhỏ nên xử lý nhanh cả video dài hàng giờ. Tăng ngưỡng 10 để giữ ít khung hình hơn từ camera tĩnh; giảm ngưỡng cho cảnh chuyển động nhanh. Đồng thời phân bổ ngân sách trên nhiều clip: 20 khung hình từ mỗi clip trong 50 clip tốt hơn 1.000 khung hình từ một clip.

Các khung hình từ cùng một clip là một mẫu, không phải nhiều mẫu

Hai khung hình cách nhau một giây có chung điều kiện ánh sáng, phông nền và thường là cùng các đối tượng. Nếu chúng cùng xuất hiện trong train và val, model sẽ được đánh giá trên những ảnh mà thực chất nó đã thấy. Giữ ID clip trong mọi tên file để Chia dataset đúng cách có thể giữ các khung hình của một clip cùng nhau.

Tránh những sai lầm khi thu thập dữ liệu#

Sai lầmTriệu chứngCách khắc phục
Thu thập dữ liệu trong một tuần duy nhấtmAP trên tập validation cao nhưng bỏ sót dữ liệu ở các mùa khác hoặc khi có thay đổiTrải đều việc thu thập dữ liệu trong nhiều tuần hoặc nhiều tháng
Chỉ dùng một cameraKhông hoạt động tốt với camera thứ haiThu thập dữ liệu từ mọi loại camera mà bạn triển khai
Chỉ dùng dữ liệu dàn dựngKhông hoạt động tốt trong môi trường lộn xộn thực tếGiữ số ảnh dàn dựng ở mức thiểu số nhỏ và theo dõi tỷ lệ này
Các frame trùng lặpChỉ số bị thổi phồng, lãng phí công gán nhãnLấy mẫu theo thay đổi cảnh; Ultralytics Platform cũng bỏ qua các bản sao chính xác khi tải lên
Chỉ một người vận hành hoặc một ca làm việcPhân bố ngoại hình không có kế hoạch và mất cân bằng lớpLấy mẫu qua nhiều ca làm việc, địa điểm và người khác nhau
Thiếu các trường hợp hiếm gặpBỏ sót những sự cố an toàn quan trọng nhấtThu thập có mục tiêu, dàn dựng hoặc bổ sung dữ liệu tổng hợp

Thiên lệch là vấn đề thu thập dữ liệu, không phải vấn đề mô hình: không có siêu tham số nào có thể khắc phục việc dữ liệu chưa bao giờ chứa một nhóm nào đó. Hướng dẫn thu thập dữ liệu liệt kê bốn biện pháp: đa dạng hóa nguồn dữ liệu, cân bằng mức độ đại diện, thường xuyên kiểm tra lại độ bao phủ và áp dụng biện pháp giảm thiểu như lấy mẫu quá mức các nhóm ít được đại diện.

Nếu ảnh có khuôn mặt, biển số xe hoặc dữ liệu cá nhân khác, bạn cần có sự đồng thuận và chính sách lưu giữ dữ liệu trước khi bắt đầu thu thập. Mục từ quyền riêng tư dữ liệu trong bảng thuật ngữ là điểm khởi đầu phù hợp, còn Quyền riêng tư, Khu vực và Tuân thủ đề cập đến việc lưu trữ dữ liệu trong đúng khu vực pháp lý bằng dataRegion.

Dùng thử

Chạy make_practice_set.py, sau đó chỉnh sửa hạn ngạch trong coverage.py cho phù hợp với thông số kỹ thuật của bạn (hoặc giữ nguyên hạn ngạch thực hành) rồi chạy tập lệnh. Với dự án thực tế, đổi tên 50 ảnh gần đây theo mẫu camera_day_frame và chạy cùng phép đếm độ bao phủ trên các ảnh đó. Sản phẩm bàn giao: báo cáo độ bao phủ và kế hoạch thu thập liệt kê mọi nguồn vượt hạn ngạch cùng từng khoảng thiếu và quy mô của khoảng thiế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.
  • Tên tệp của mỗi ảnh chứa thông tin về camera (hoặc nguồn), ngày chụp và số frame hoặc clip.

  • Báo cáo độ bao phủ so sánh số lượng ảnh theo camera, ngày hoặc kịch bản với hạn ngạch trong thông số kỹ thuật.

  • Video được lấy mẫu theo thay đổi cảnh và các frame được giữ lại vẫn giữ ID clip.

Hiển thị lời giải

Với tập dữ liệu thực hành, báo cáo độ bao phủ đưa ra kế hoạch cụ thể:

NguồnSố lượng / hạn ngạchHành động
cam152 / 30Dừng thu thập; nguồn này đã chiếm 40% tổng số ảnh
cam226 / 30Thu thập thêm 4 ảnh
cam325 / 30Thu thập thêm 5 ảnh
cam425 / 30Thu thập thêm 5 ảnh

Mỗi camera ghi nhận dữ liệu trong cả bốn ngày, nên các khoảng thiếu được tính theo camera chứ không phải theo ngày. Phân bổ 14 ảnh mới cho ít nhất hai ngày mới để chúng bổ sung điều kiện mới thay vì thêm nhiều dữ liệu từ cùng một buổi chiều. Với camera không thể cung cấp đủ phần còn thiếu (camera chỉ ghi hình ban đêm), hãy thêm một hàng vào thông số kỹ thuật cho điều kiện còn thiếu và ghi lại xem ảnh dàn dựng hay ảnh được tạo tổng hợp sẽ bù vào, để có thể đo lường tác động sau này.

Tiếp theo là gì

Sau khi thu thập ảnh, bài học tiếp theo sẽ hướng dẫn cách gán nhãn nhất quán ngay từ lần đầu.

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