Kemarin, “Deci AI” merilis model deteksi objek canggih baru bernama “YOLO-NAS”, yang mencapai presisi rata-rata lebih tinggi dibandingkan model sebelumnya yang berjalan dengan latensi yang sama.

Dalam postingan blog ini, kami akan menunjukkan cara membuat prediksi dengan YOLO-NAS dan memuatnya ke FiftyOne! Jika Anda baru mengenal "FiftyOne", ini adalah perangkat visi komputer sumber terbuka untuk menyusun data yang lebih baik dan membuat model yang lebih baik.

Mempersiapkan

Jika Anda belum melakukannya, Anda perlu menginstal FiftyOne:

pip install fiftyone

Anda juga perlu menginstal paket SuperGradients Deci AI:

pip install super-gradients

Langkah selanjutnya adalah mengimpor FiftyOne dan memuat kumpulan data dari FiftyOne Dataset Zoo. Kami akan menggunakan subset acak dari pemisahan validasi dari dataset MS COCO. Kami juga akan membuat kumpulan data tetap ada:

​​import fiftyone as fo 
import fiftyone.zoo as foz 
dataset = foz.load_zoo_dataset(
    "coco-2017", 
    split = "validation", 
    max_samples=1000
)

dataset.name = "YOLO-NAS-demo" 
dataset.persistent = True

Kita juga akan menggunakan metode compute_metadata() untuk menyimpan lebar dan tinggi gambar, sehingga kita dapat menggunakannya untuk mengkonversi antara koordinat absolut dan relatif untuk kotak pembatas:

dataset.compute_metadata()

Sekarang kita memuat model YOLO-NAS. Kami akan menggunakan arsitektur besar (karenanya disebut "l") dan akan mengunduh bobot yang telah dilatih sebelumnya untuk versi model yang dilatih pada data COCO. Untuk daftar bobot yang tersedia, lihat di sini.

from super_gradients.training import models 
model = models.get("yolo_nas_l", pretrained_weights="coco")

Menghasilkan Prediksi YOLO-NAS

Kita dapat menghasilkan prediksi untuk satu sampel dengan meneruskan jalur file untuk gambar sampel ke metode `predict()` model kita, bersama dengan ambang keyakinan opsional:

sample = dataset.first() 
prediction = model.predict(sample.filepath, conf = 0.25)

Kita kemudian dapat memvisualisasikan kotak pembatas objek yang digambar pada gambar dengan metode show() untuk SuperGradient DetectionResult:

prediction.show()

Untuk menghasilkan prediksi secara efisien untuk semua gambar dalam kumpulan data kami, kami mengelompokkan operasi ini dengan meneruskan nama semua jalur file untuk semua gambar kami ke model:

fps, widths, heights = dataset.values(
    ["filepath", "metadata.width", "metadata.height"]
) 

## batch predictions 
preds = model.predict(fps, conf = confidence)._images_prediction_lst

Untuk memuat prediksi ini ke dalam FiftyOne, pertama-tama kita perlu mengubahnya menjadi objek label Detection. Hal ini memerlukan akses internal objek prediksi, mengekstraksi kepercayaan, label, dan kotak pembatas.

Kita dapat mengakses informasi ini melalui atribut _images_prediction_lst pada objek prediksi. Mari kita lihat seperti apa tampilannya untuk satu gambar.

pred = model.predict(sample.filepath, conf = 0.9) 
print(next(pred._images_prediction_lst))
ImageDetectionPrediction(image=array([[[170, 136,  73],
        [173, 142,  77],
        [175, 144,  79],
        ...,
        [ 69,  76,  42],
        [ 68,  76,  39],
        [ 70,  71,  37]],

       [[172, 141,  77],
        [176, 145,  80],
        [177, 146,  81],
        ...,
        [ 69,  77,  40],
        [ 72,  80,  43],
        [ 71,  75,  40]],

       [[175, 144,  79],
        [177, 146,  81],
        [178, 147,  80],
        ...,
        [ 70,  78,  39],
        [ 69,  77,  40],
        [ 71,  75,  40]],

       ...,

       [[188, 189, 157],
        [183, 183, 149],
        [193, 187, 153],
        ...,
        [186, 157, 153],
        [186, 157, 153],
        [187, 156, 154]],

       [[186, 183, 152],
        [187, 184, 153],
        [186, 183, 152],
        ...,
        [198, 134, 134],
        [195, 120, 124],
        [186,  88, 101]],

       [[186, 183, 150],
        [187, 184, 151],
        [186, 183, 152],
        ...,
        [129,  60,  63],
        [126,  57,  60],
        [107,  41,  45]]], dtype=uint8), prediction=DetectionPrediction(bboxes_xyxy=array([[  5.661982, 166.75662 , 154.55098 , 261.8113  ],
       [292.11624 , 217.75893 , 352.51135 , 318.72244 ]], dtype=float32), confidence=array([0.96724653, 0.9323513 ], dtype=float32), labels=array([62., 56.], dtype=float32)), class_names=['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush'])

Karena atribut _images_prediction_lst adalah generator, kami menggunakan next() untuk melihat apa yang dihasilkannya, yaitu objek ImageDetectionPrediction. Dalam hasil ini, kita dapat melihat kotak pembatas yang disimpan dalam format xyxy, array skor kepercayaan, dan array bilangan bulat yang mewakili indeks kelas label dalam array class_names untuk deteksi tersebut. Nama kelasnya tepatnya adalah nama kelas COCO.

Berikut cara mengonversi kotak pembatas dari koordinat keluaran YOLO-NAS:

def convert_bboxes(bboxes, w, h):
    tmp = np.copy(bboxes[:, 1])
    bboxes[:, 1] = h - bboxes[:, 3]
    bboxes[:, 3] = h - tmp
    bboxes[:, 0]/= w
    bboxes[:, 2]/= w
    bboxes[:, 1]/= h
    bboxes[:, 3]/= h
    bboxes[:, 2] -= bboxes[:, 0]
    bboxes[:, 3] -= bboxes[:, 1]
    bboxes[:, 1] = 1 - (bboxes[:, 1] + bboxes[:, 3])
    return bboxes

Dengan menerapkan konversi kotak pembatas ini, kita dapat membuat objek FiftyOne Detection untuk setiap objek, dan membuat objek Detections berisi daftar objek yang terdeteksi untuk gambar tertentu:

def generate_detections(p, width, height):
    class_names = p.class_names
    dp = p.prediction
    bboxes, confs, labels = np.array(dp.bboxes_xyxy), dp.confidence, dp.labels.astype(int)
    if 0 in bboxes.shape:
        return fo.Detections(detections = [])
    
    bboxes = convert_bboxes(bboxes, width, height)
    labels = [class_names[l] for l in labels]
    detections = [
        fo.Detection(
            label = l,
            confidence = c,
            bounding_box = b
        )
        for (l, c, b) in zip(labels, confs, bboxes)
    ]
    return fo.Detections(detections=detections)

Dengan menggabungkan semuanya, kami dapat secara efisien menambahkan prediksi deteksi YOLO-NAS ke kumpulan data kami:

def add_YOLO_NAS_predictions(dataset, confidence = 0.9):
    ## aggregation to minimize expensive operations
    fps, widths, heights = dataset.values(
        ["filepath", "metadata.width", "metadata.height"]
    )
    
    ## batch predictions
    preds = model.predict(fps, conf = confidence)._images_prediction_lst
    
    ## add all predictions to dataset at once
    dets = [
        generate_detections(pred, w, h)
        for pred, w, h in zip(preds, widths, heights)
    ]
    dataset.set_values("YOLO-NAS", dets)

Menerapkan ini ke kumpulan data kami dan meluncurkan sesi Aplikasi FiftyOne, kami dapat memvisualisasikan hasilnya:

add_YOLO_NAS_predictions(dataset, confidence = 0.7) 
session = fo.launch_app(dataset)

Evaluasi

Dengan data yang dimuat ke FiftyOne, kita dapat mengevaluasi kualitas prediksi deteksi objek terhadap “kebenaran dasar” dengan metode evaluate_detections() FiftyOne. Kami akan menyimpan hasilnya dengan kunci evaluasi sehingga kami dapat melihat patch evaluasi yang dihasilkan di Aplikasi FiftyOne:

res = dataset.evaluate_detections(
    "YOLO-NAS", 
    eval_key="eval_yolonas"
) 
session.view = dataset.to_evaluation_patches("eval_yolonas")

Kita dapat mengklik salah satu patch evaluasi ini dan, dengan mengarahkan kursor ke deteksi, kita dapat melihat atribut seperti skor interseksi atas gabungan (IoU), dan apakah prediksi tersebut benar-benar positif, positif palsu, atau negatif palsu.

Jika kami ingin melihat gambar dengan hasil positif palsu terbanyak, kami dapat mengurutkan kumpulan data berdasarkan:

### get images with the most FP's first 
fp_view = dataset.sort_by("eval_yolonas_fp", reverse = True)

Kami juga dapat menggali data kinerja secara lebih kuantitatif dengan mencetak metrik untuk kelas yang paling sering muncul dalam kumpulan data:

counts = dataset.count_values("ground_truth.detections.label") 
classes_top10 = sorted(counts, key=counts.get, reverse=True)[:10] 
# Print a report for the top-10 classes 
res.print_report(classes=classes_top10)
              precision    recall  f1-score   support

      person       0.98      0.52      0.68      2259
       chair       0.88      0.33      0.48       401
         car       0.93      0.42      0.58       348
        book       1.00      0.03      0.06       212
      bottle       0.88      0.28      0.43       210
         cup       0.97      0.45      0.61       185
dining table       0.85      0.18      0.30       153
        bowl       0.86      0.34      0.49       128
        bird       1.00      0.39      0.56       103
    backpack       1.00      0.07      0.13        98

   micro avg       0.95      0.42      0.58      4097
   macro avg       0.93      0.30      0.43      4097
weighted avg       0.95      0.42      0.57      4097

Kesimpulan

Jika Anda ingin melangkah lebih jauh, Anda mungkin tertarik pada:

  • Menerapkan YOLO-NAS ke data video, dan memuat prediksi ini ke FiftyOne
  • Sempurnakan YOLO-NAS pada data Anda sendiri, dan gunakan Evaluation API FiftyOne untuk membandingkan model dasar dan model yang telah disempurnakan

Bergabunglah dengan komunitas FiftyOne!

Bergabunglah dengan ribuan insinyur dan ilmuwan data yang telah menggunakan FiftyOne untuk memecahkan beberapa masalah paling menantang dalam visi komputer saat ini!

Awalnya diterbitkan di https://voxel51.com pada 4 Mei 2023.