Bỏ để qua phần nội dung

Xác thực chéo K-Fold với Ultralytics

Giới thiệu

Hướng dẫn toàn diện này minh họa việc triển khai Xác thực chéo K-Fold cho các bộ dữ liệu phát hiện đối tượng trong Ultralytics hệ sinh thái. Chúng tôi sẽ tận dụng YOLO Định dạng phát hiện và khóa Python các thư viện như sklearn, gấu trúc và PyYaml để hướng dẫn bạn thiết lập cần thiết, quy trình tạo vectơ tính năng và thực thi phân tách tập dữ liệu K-Fold.

Tổng quan về xác thực chéo K-Fold

Cho dù dự án của bạn liên quan đến tập dữ liệu Phát hiện trái cây hay nguồn dữ liệu tùy chỉnh, hướng dẫn này nhằm mục đích giúp bạn hiểu và áp dụng Xác thực chéo K-Fold để tăng cường độ tin cậy và mạnh mẽ của các mô hình học máy của bạn. Trong khi chúng tôi đang áp dụng k=5 Nếp gấp Đối với hướng dẫn này, hãy nhớ rằng số lần gấp tối ưu có thể thay đổi tùy thuộc vào tập dữ liệu của bạn và chi tiết cụ thể của dự án của bạn.

Không cần quảng cáo thêm, chúng ta hãy đi sâu vào!

Thiết lập

  • Chú thích của bạn phải nằm trong YOLO định dạng phát hiện.

  • Hướng dẫn này giả định rằng các tệp chú thích có sẵn cục bộ.

  • Để trình diễn, chúng tôi sử dụng bộ dữ liệu Phát hiện trái cây .

    • Tập dữ liệu này chứa tổng cộng 8479 hình ảnh.
    • Nó bao gồm 6 nhãn lớp, mỗi nhãn có tổng số phiên bản được liệt kê bên dưới.
Nhãn lớp học Số lượng phiên bản
Quả táo 7049
Nho 7202
Dứa 1613
Cam 15549
Chuối 3536
Dưa hấu 1976
  • Cần thiết Python Các gói bao gồm:

    • ultralytics
    • sklearn
    • pandas
    • pyyaml
  • Hướng dẫn này hoạt động với k=5 Gấp. Tuy nhiên, bạn nên xác định số lần gấp tốt nhất cho tập dữ liệu cụ thể của mình.

  • Bắt đầu một cái mới Python môi trường ảo (venv) cho dự án của bạn và kích hoạt nó. Dùng pip (hoặc trình quản lý gói ưa thích của bạn) để cài đặt:

    • Các Ultralytics thư viện: pip install -U ultralytics. Ngoài ra, bạn có thể sao chép chính thức Repo.
    • Scikit-learn, gấu trúc và PyYAML: pip install -U scikit-learn pandas pyyaml.
  • Xác minh rằng chú thích của bạn nằm trong YOLO định dạng phát hiện.

    • Đối với hướng dẫn này, tất cả các tệp chú thích được tìm thấy trong Fruit-Detection/labels thư mục.

Tạo vectơ tính năng cho tập dữ liệu phát hiện đối tượng

  1. Bắt đầu bằng cách tạo mới Python tệp và nhập các thư viện cần thiết.

    import datetime
    import shutil
    from collections import Counter
    from pathlib import Path
    
    import numpy as np
    import pandas as pd
    import yaml
    from sklearn.model_selection import KFold
    
    from ultralytics import YOLO
    
  2. Tiến hành truy xuất tất cả các tệp nhãn cho tập dữ liệu của bạn.

    dataset_path = Path("./Fruit-detection")  # replace with 'path/to/dataset' for your custom data
    labels = sorted(dataset_path.rglob("*labels/*.txt"))  # all data in 'labels'
    
  3. Bây giờ, hãy đọc nội dung của tệp YAML tập dữ liệu và trích xuất các chỉ số của nhãn lớp.

    yaml_file = "path/to/data.yaml"  # your data YAML with data directories and names dictionary
    with open(yaml_file, "r", encoding="utf8") as y:
        classes = yaml.safe_load(y)["names"]
    cls_idx = sorted(classes.keys())
    
  4. Khởi tạo một trống pandas Khung dữ liệu.

    indx = [l.stem for l in labels]  # uses base filename as ID (no extension)
    labels_df = pd.DataFrame([], columns=cls_idx, index=indx)
    
  5. Đếm các trường hợp của mỗi nhãn lớp có trong các tệp chú thích.

    for label in labels:
        lbl_counter = Counter()
    
        with open(label, "r") as lf:
            lines = lf.readlines()
    
        for l in lines:
            # classes for YOLO label uses integer at first position of each line
            lbl_counter[int(l.split(" ")[0])] += 1
    
        labels_df.loc[label.stem] = lbl_counter
    
    labels_df = labels_df.fillna(0.0)  # replace `nan` values with `0.0`
    
  6. Sau đây là dạng xem mẫu của DataFrame được điền:

                                                           0    1    2    3    4    5
    '0000a16e4b057580_jpg.rf.00ab48988370f64f5ca8ea4...'  0.0  0.0  0.0  0.0  0.0  7.0
    '0000a16e4b057580_jpg.rf.7e6dce029fb67f01eb19aa7...'  0.0  0.0  0.0  0.0  0.0  7.0
    '0000a16e4b057580_jpg.rf.bc4d31cdcbe229dd022957a...'  0.0  0.0  0.0  0.0  0.0  7.0
    '00020ebf74c4881c_jpg.rf.508192a0a97aa6c4a3b6882...'  0.0  0.0  0.0  1.0  0.0  0.0
    '00020ebf74c4881c_jpg.rf.5af192a2254c8ecc4188a25...'  0.0  0.0  0.0  1.0  0.0  0.0
     ...                                                  ...  ...  ...  ...  ...  ...
    'ff4cd45896de38be_jpg.rf.c4b5e967ca10c7ced3b9e97...'  0.0  0.0  0.0  0.0  0.0  2.0
    'ff4cd45896de38be_jpg.rf.ea4c1d37d2884b3e3cbce08...'  0.0  0.0  0.0  0.0  0.0  2.0
    'ff5fd9c3c624b7dc_jpg.rf.bb519feaa36fc4bf630a033...'  1.0  0.0  0.0  0.0  0.0  0.0
    'ff5fd9c3c624b7dc_jpg.rf.f0751c9c3aa4519ea3c9d6a...'  1.0  0.0  0.0  0.0  0.0  0.0
    'fffe28b31f2a70d4_jpg.rf.7ea16bd637ba0711c53b540...'  0.0  6.0  0.0  0.0  0.0  0.0
    

Các hàng lập chỉ mục các tệp nhãn, mỗi tệp tương ứng với một hình ảnh trong tập dữ liệu của bạn và các cột tương ứng với các chỉ mục nhãn lớp của bạn. Mỗi hàng đại diện cho một vectơ tính năng giả, với số lượng của mỗi nhãn lớp có trong tập dữ liệu của bạn. Cấu trúc dữ liệu này cho phép áp dụng Xác thực chéo K-Fold cho tập dữ liệu phát hiện đối tượng.

Phân tách tập dữ liệu K-Fold

  1. Bây giờ chúng ta sẽ sử dụng KFold lớp học từ sklearn.model_selection để tạo k phân tách tập dữ liệu.

    • Quan trọng:
      • Khung cảnh shuffle=True Đảm bảo phân phối ngẫu nhiên các lớp trong phần tách của bạn.
      • Bằng cách cài đặt random_state=M đâu M là một số nguyên đã chọn, bạn có thể thu được kết quả lặp lại.
    ksplit = 5
    kf = KFold(n_splits=ksplit, shuffle=True, random_state=20)  # setting random_state for repeatable results
    
    kfolds = list(kf.split(labels_df))
    
  2. Tập dữ liệu hiện đã được chia thành k nếp gấp, mỗi nếp gấp có một danh sách trainval chỉ số. Chúng ta sẽ xây dựng một DataFrame để hiển thị các kết quả này rõ ràng hơn.

    folds = [f"split_{n}" for n in range(1, ksplit + 1)]
    folds_df = pd.DataFrame(index=indx, columns=folds)
    
    for idx, (train, val) in enumerate(kfolds, start=1):
        folds_df[f"split_{idx}"].loc[labels_df.iloc[train].index] = "train"
        folds_df[f"split_{idx}"].loc[labels_df.iloc[val].index] = "val"
    
  3. Bây giờ chúng ta sẽ tính toán phân bố nhãn lớp cho mỗi lần gấp theo tỷ lệ của các lớp có trong val cho những người có mặt trong train.

    fold_lbl_distrb = pd.DataFrame(index=folds, columns=cls_idx)
    
    for n, (train_indices, val_indices) in enumerate(kfolds, start=1):
        train_totals = labels_df.iloc[train_indices].sum()
        val_totals = labels_df.iloc[val_indices].sum()
    
        # To avoid division by zero, we add a small value (1E-7) to the denominator
        ratio = val_totals / (train_totals + 1e-7)
        fold_lbl_distrb.loc[f"split_{n}"] = ratio
    

    Kịch bản lý tưởng là tất cả các tỷ lệ lớp học phải giống nhau một cách hợp lý cho mỗi lần phân chia và giữa các lớp. Tuy nhiên, điều này sẽ phụ thuộc vào các chi tiết cụ thể của tập dữ liệu của bạn.

  4. Tiếp theo, chúng tôi tạo các thư mục và tệp YAML tập dữ liệu cho mỗi lần tách.

    supported_extensions = [".jpg", ".jpeg", ".png"]
    
    # Initialize an empty list to store image file paths
    images = []
    
    # Loop through supported extensions and gather image files
    for ext in supported_extensions:
        images.extend(sorted((dataset_path / "images").rglob(f"*{ext}")))
    
    # Create the necessary directories and dataset YAML files (unchanged)
    save_path = Path(dataset_path / f"{datetime.date.today().isoformat()}_{ksplit}-Fold_Cross-val")
    save_path.mkdir(parents=True, exist_ok=True)
    ds_yamls = []
    
    for split in folds_df.columns:
        # Create directories
        split_dir = save_path / split
        split_dir.mkdir(parents=True, exist_ok=True)
        (split_dir / "train" / "images").mkdir(parents=True, exist_ok=True)
        (split_dir / "train" / "labels").mkdir(parents=True, exist_ok=True)
        (split_dir / "val" / "images").mkdir(parents=True, exist_ok=True)
        (split_dir / "val" / "labels").mkdir(parents=True, exist_ok=True)
    
        # Create dataset YAML files
        dataset_yaml = split_dir / f"{split}_dataset.yaml"
        ds_yamls.append(dataset_yaml)
    
        with open(dataset_yaml, "w") as ds_y:
            yaml.safe_dump(
                {
                    "path": split_dir.as_posix(),
                    "train": "train",
                    "val": "val",
                    "names": classes,
                },
                ds_y,
            )
    
  5. Cuối cùng, sao chép hình ảnh và nhãn vào thư mục tương ứng ('train' hoặc 'val') cho mỗi lần tách.

    • GHI: Thời gian cần thiết cho phần mã này sẽ thay đổi dựa trên kích thước tập dữ liệu và phần cứng hệ thống của bạn.
    for image, label in zip(images, labels):
        for split, k_split in folds_df.loc[image.stem].items():
            # Destination directory
            img_to_path = save_path / split / k_split / "images"
            lbl_to_path = save_path / split / k_split / "labels"
    
            # Copy image and label files to new directory (SamefileError if file already exists)
            shutil.copy(image, img_to_path / image.name)
            shutil.copy(label, lbl_to_path / label.name)
    

Lưu bản ghi (Tùy chọn)

Theo tùy chọn, bạn có thể lưu các bản ghi của DataFrames phân tách và phân phối nhãn K-Fold dưới dạng tệp CSV để tham khảo trong tương lai.

folds_df.to_csv(save_path / "kfold_datasplit.csv")
fold_lbl_distrb.to_csv(save_path / "kfold_label_distribution.csv")

Xe lửa YOLO sử dụng Chia tách dữ liệu K-Fold

  1. Đầu tiên, tải YOLO mẫu.

    weights_path = "path/to/weights.pt"
    model = YOLO(weights_path, task="detect")
    
  2. Tiếp theo, lặp lại các tệp YAML tập dữ liệu để chạy đào tạo. Kết quả sẽ được lưu vào một thư mục được chỉ định bởi projectname lập luận. Theo mặc định, thư mục này là 'exp/runs#' trong đó # là chỉ mục số nguyên.

    results = {}
    
    # Define your additional arguments here
    batch = 16
    project = "kfold_demo"
    epochs = 100
    
    for k in range(ksplit):
        dataset_yaml = ds_yamls[k]
        model.train(data=dataset_yaml, epochs=epochs, batch=batch, project=project)  # include any train arguments
        results[k] = model.metrics  # save output metrics for further analysis
    

Kết thúc

Trong hướng dẫn này, chúng tôi đã khám phá quá trình sử dụng xác thực chéo K-Fold để đào tạo YOLO Mô hình phát hiện đối tượng. Chúng tôi đã học cách chia tập dữ liệu của mình thành các phân vùng K, đảm bảo phân phối lớp cân bằng trên các nếp gấp khác nhau.

Chúng tôi cũng đã khám phá quy trình tạo DataFrames báo cáo để trực quan hóa việc phân tách dữ liệu và phân phối nhãn trên các phần tách này, cung cấp cho chúng tôi cái nhìn sâu sắc rõ ràng về cấu trúc của các bộ đào tạo và xác thực của chúng tôi.

Theo tùy chọn, chúng tôi đã lưu các bản ghi của mình để tham khảo trong tương lai, điều này có thể đặc biệt hữu ích trong các dự án quy mô lớn hoặc khi khắc phục sự cố hiệu suất mô hình.

Cuối cùng, chúng tôi đã thực hiện đào tạo mô hình thực tế bằng cách sử dụng mỗi lần chia trong một vòng lặp, lưu kết quả đào tạo của chúng tôi để phân tích và so sánh thêm.

Kỹ thuật xác thực chéo K-Fold này là một cách mạnh mẽ để tận dụng tối đa dữ liệu có sẵn của bạn và nó giúp đảm bảo rằng hiệu suất mô hình của bạn đáng tin cậy và nhất quán trên các tập hợp con dữ liệu khác nhau. Điều này dẫn đến một mô hình tổng quát và đáng tin cậy hơn, ít có khả năng phù hợp với các mẫu dữ liệu cụ thể.

Hãy nhớ rằng mặc dù chúng tôi đã sử dụng YOLO Trong hướng dẫn này, các bước này hầu hết có thể chuyển sang các mô hình học máy khác. Hiểu các bước này cho phép bạn áp dụng xác thực chéo một cách hiệu quả trong các dự án học máy của riêng bạn. Mã hóa vui vẻ!



Created 2023-11-12, Updated 2024-06-10
Authors: glenn-jocher (8), IvorZhu331 (1), Burhan-Q (1)

Ý kiến