Точная настройка моделей YOLOv8 для пользовательских приложений компьютерного зрения

Добро пожаловать в третью и последнюю часть нашей серии из трех частей о YOLOv8! В этой серии мы покажем вам, как работать с YOLOv8, от загрузки готовых моделей до точной настройки этих моделей для конкретных случаев использования и всего, что между ними.

На протяжении всей серии мы будем использовать две библиотеки: FiftyOne, набор инструментов компьютерного зрения с открытым исходным кодом, и Ultralytics, библиотеку, которая даст нам доступ к YOLOv8.

Здесь, в части 3, мы покажем, как точно настроить модель YOLOv8 для вашего конкретного варианта использования.

Этот пост организован следующим образом:

Продолжайте читать, чтобы узнать, как включить модели YOLOv8 в рабочие процессы компьютерного зрения!

Резюме частей 1 и 2

В Части 1 мы загрузили проверочное разделение набора данных COCO 2017 в FiftyOne с обнаружением наземных объектов. Затем мы сгенерировали прогнозы с помощью модели обнаружения YOLOv8n из Ultralytics YOLOv8 Github и добавили их в наш набор данных в поле метки yolov8n наших образцов.

Во Части 2 мы использовали Evaluation API FiftyOne для оценки качества этих обнаружений. Мы обнаружили, что эта базовая модель имеет относительно хорошую точность, но имеет проблемы с отзывом для определенных классов. Когда мы углубились в детали, мы увидели, что для некоторых классов, таких как класс book, несовершенства в метках истинности COCO были, по крайней мере, частью проблемы, но для других классов, таких как bird, модель могла бы выиграть от точной настройки на более широкий набор примеров.

Обнаружение птиц с помощью YOLOv8

               precision    recall  f1-score   support

       person       0.85      0.68      0.76     11573
          car       0.71      0.52      0.60      1971
        chair       0.62      0.34      0.44      1806
         book       0.61      0.12      0.20      1182
       bottle       0.68      0.39      0.50      1051
          cup       0.61      0.44      0.51       907
 dining table       0.54      0.42      0.47       697
traffic light       0.66      0.36      0.46       638
         bowl       0.63      0.49      0.55       636
      handbag       0.48      0.12      0.19       540
         bird       0.79      0.39      0.52       451
         boat       0.58      0.29      0.39       430
        truck       0.57      0.35      0.44       415
        bench       0.58      0.27      0.37       413
     umbrella       0.65      0.52      0.58       423
          cow       0.81      0.61      0.70       397
       banana       0.68      0.34      0.45       397
       carrot       0.56      0.29      0.38       384
   motorcycle       0.77      0.58      0.66       379
     backpack       0.51      0.16      0.24       371

    micro avg       0.76      0.52      0.61     25061
    macro avg       0.64      0.38      0.47     25061
 weighted avg       0.74      0.52      0.60     25061

Как мы видели в предыдущем разделе, несмотря на то, что YOLOv8 имеет достойную производительность из коробки, он может не подходить для конкретных случаев использования без некоторых модификаций. Например, для класса bird базовая модель YOLOv8n достигла полноты только 39%.

Предположим, вы работаете в группе по охране птиц, внедряя модели компьютерного зрения в полевые условия для отслеживания и защиты исчезающих видов. Ваша цель — обнаружить в режиме реального времени как можно больше птиц.

Учитывая скорость вывода, архитектура YOLOv8 кажется очевидным выбором. Однако вы не удовлетворены отзывом 39% в отчете об оценке данных проверки COCO. Поскольку это приложение с высокими ставками, вы хотите выжать все возможное из этой архитектуры обнаружения в реальном времени.

Если вы хотите, вы можете обучить новую модель обнаружения YOLOv8 с нуля, как показано в Руководстве по быстрому запуску YOLOv8, но в идеале вы хотели бы использовать существующие знания предварительно обученной модели. К счастью, настроить существующую модель YOLOv8 довольно просто.

Прежде чем продолжить, давайте упростим нашу задачу. Гибкий язык запросов, встроенный в FiftyOne, позволяет легко нарезать наборы данных и находить интересные представления всего за одну строку кода или нажатие кнопки в приложении.

На данный момент у нас есть FiftyOne с нашими проверочными изображениями COCO, обнаружением истинности и прогнозами YOLOv8n в поле метки yolov8n для каждого образца. Учитывая, что в нашем случае нас интересует только обнаружение птиц, давайте создадим тестовый набор, отфильтровав все не-bird наземные обнаружения правды, используя filter_labels(). Мы также будем отфильтровывать прогнозы, отличные от bird, но передадим аргумент only_matches = False в filter_labels(), чтобы убедиться, что мы сохраняем изображения, которые имеют наземные истинные обнаружения bird без прогнозов YOLOv8n bird.

test_dataset = dataset.filter_labels(
    "ground_truth", 
    F("label") == "bird"
).filter_labels(
    "yolov8n", 
    F("label") == "bird",
    only_matches=False
).clone()

test_dataset.name = "birds-test-dataset"
test_dataset.persistent = True

## set classes to just include birds
classes = ["bird"]

Затем мы даем набору данных имя, делаем его постоянным и сохраняем в базовой базе данных. В этом тестовом наборе всего 125 изображений, которые мы можем визуализировать в FiftyOne App.

Мы также можем запустить evaluate_detections() на этих данных, чтобы оценить производительность модели YOLOv8n на изображениях с обнаружением птиц на земле. Мы будем хранить результаты под ключом оценки base:

base_bird_results = test_dataset.evaluate_detections(
    "yolov8n", 
    eval_key="base",
    compute_mAP=True,
)

print(base_bird_results.mAP())
## 0.24897924786479841

base_bird_results.print_report(classes=classes)
precision recall f1-score support bird 0.87 0.39 0.54 451

Мы отмечаем, что, хотя отзыв такой же, как и в первоначальном отчете об оценке для всего разделения проверки COCO, точность выше. Это означает, что есть изображения, которые имеют предсказания YOLOv8n bird, но неназемные обнаружения bird.

Последним шагом в подготовке этого тестового набора является экспорт данных в формат YOLOv8, чтобы мы могли выполнить вывод только на этих образцах с помощью нашей точно настроенной модели, когда мы закончим обучение. Мы сделаем это, используя функцию export_yolo_data(), которую мы определили в Части 1.

export_yolo_data(
    test_dataset, 
    "birds_test", 
    classes
)

Выбор обучающих данных

Наиболее важным компонентом тонкой настройки модели являются данные, на которых модель обучается. Если мы хотим, чтобы наша модель демонстрировала высокую производительность на определенном подмножестве данных, наша цель должна заключаться в создании высококачественного обучающего набора данных, примеры которого охватывают все ожидаемые сценарии в этом подмножестве.

Это и искусство, и наука. Это может включать в себя извлечение данных из других наборов данных, аннотирование большего количества данных, которые вы уже собрали, с помощью меток истинности, дополнение ваших данных с помощью таких инструментов, как альбументации, или создание синтетических данных с помощью моделей распространения или GAN.

В этой статье мы воспользуемся первым подходом и включим существующие высококачественные данные из набора данных Open Images Google. Подробное руководство по работе с данными Open Images см. в разделе Загрузка Open Images V6 и пользовательских наборов данных с помощью FiftyOne.

Данные обучения COCO, на которых обучался YOLOv8, содержат 3237 изображений с bird обнаружениями. Открытые изображения более обширны: обучение, тестирование и проверка разделены на более чем 20 000 изображений с Bird обнаружениями.

Давайте создадим наш обучающий набор данных. Во-первых, мы создадим набор данных train_dataset, загрузив метки обнаружения bird из разделения поезда COCO с помощью FiftyOne Dataset Zoo и клонировав его в новый объект Dataset:

train_dataset = foz.load_zoo_dataset(
    'coco-2017',
    split='train',
    classes=classes
).clone()

train_dataset.name = "birds-train-data"
train_dataset.persistent = True
train_dataset.save()

Затем мы загрузим образцы Open Images с Bird метками обнаружения, передав only_matching=True, чтобы загрузить только Bird меток. Затем мы преобразуем эти метки в формат меток COCO, заменив Bird на bird.

oi_samples = foz.load_zoo_dataset(
    "open-images-v6",
    classes = ["Bird"],
    only_matching=True,
    label_types="detections"
).map_labels(
    "ground_truth",
    {"Bird":"bird"}
)

Мы можем добавить эти новые образцы в наш набор обучающих данных с помощью merge_samples():

train_dataset.merge_samples(oi_samples)

Этот набор данных содержит 24 226 образцов с метками bird, что более чем в семь раз превышает количество птиц, на которых обучалась базовая модель YOLOv8n. В следующем разделе мы покажем, как настроить модель на этих данных с помощью класса YOLO Trainer.

Тонкая настройка YOLOv8 для индивидуального варианта использования

Последним шагом в подготовке наших данных является их разделение на наборы для обучения и проверки и экспорт в формат YOLO. Мы будем использовать разбиение поездов на 80–20, которое мы выберем случайным образом с помощью случайных утилит FiftyOne.

import fiftyone.utils.random as four

## delete existing tags to start fresh
train_dataset.untag_samples(train_dataset.distinct("tags"))

## split into train and val
four.random_split(
    train_dataset,
    {"train": 0.8, "val": 0.2}
)

## export in YOLO format
export_yolo_data(
    train_dataset, 
    "birds_train", 
    classes, 
    split = ["train", "val"]
)

Теперь осталось заняться тонкой настройкой! Мы будем использовать тот же синтаксис командной строки YOLO, но вместо установки mode=predict мы установим mode=train. Мы укажем начальные веса в качестве отправной точки для обучения, количество эпох, размер изображения и размер пакета.

yolo task=detect mode=train model=yolov8n.pt data=birds_train/dataset.yaml epochs=100 imgsz=640 batch=16

Для тонкой настройки я использовал NVIDIA TITAN GPU. Я настроил обучение на 100 эпох, но в основном оно сходилось после 60 эпох, поэтому я остановился на этом. Вы можете обнаружить, что ваш конкретный вариант использования требует меньшего или потенциально большего количества итераций для достижения желаемой производительности.

Image sizes 640 train, 640 val
Using 8 dataloader workers
Logging results to runs/detect/train
Starting training for 100 epochs...

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
      1/100      6.65G      1.392      1.627      1.345         22        640: 1
                 Class     Images  Instances      Box(P          R      mAP50  m
                   all       4845      12487      0.677      0.524      0.581      0.339

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
      2/100      9.58G      1.446      1.407      1.395         30        640: 1
                 Class     Images  Instances      Box(P          R      mAP50  m
                   all       4845      12487      0.669       0.47       0.54      0.316

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
      3/100      9.58G       1.54      1.493      1.462         29        640: 1
                 Class     Images  Instances      Box(P          R      mAP50  m
                   all       4845      12487      0.529      0.329      0.349      0.188

                                            ......

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     58/100      9.59G      1.263     0.9489      1.277         47        640: 1
                 Class     Images  Instances      Box(P          R      mAP50  m
                   all       4845      12487      0.751      0.631      0.708      0.446

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     59/100      9.59G      1.264     0.9476      1.277         29        640: 1
                 Class     Images  Instances      Box(P          R      mAP50  m
                   all       4845      12487      0.752      0.631      0.708      0.446

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     60/100      9.59G      1.257     0.9456      1.274         41        640: 1
                 Class     Images  Instances      Box(P          R      mAP50  m
                   all       4845      12487      0.752      0.631      0.709      0.446

После завершения тонкой настройки мы можем генерировать прогнозы на основе наших тестовых данных с «лучшими» весами, найденными в процессе обучения, которые хранятся в runs/detect/train/weights/best.pt:

yolo task=detect mode=predict model=runs/detect/train/weights/best.pt source=birds_test/images/val save_txt=True save_conf=True

И загрузите эти прогнозы в наши данные и визуализируйте прогнозы в приложении FiftyOne:

filepaths = test_dataset.values("filepath")
prediction_filepaths = [get_prediction_filepath(fp, run_number=2) for fp in filepaths]

test_dataset.set_values(
    "yolov8n_bird_det_filepath",
    prediction_filepaths
)

add_yolo_detections(
    birds_test_dataset, 
    "yolov8n_bird", 
    "yolov8n_bird_det_filepath", 
    classes
)

Оценка улучшения

На целостном уровне мы можем сравнить производительность точно настроенной модели с исходной, предварительно обученной моделью, сопоставив их стандартные метрики друг с другом.

Самый простой способ получить эти показатели — использовать Evaluation API от FiftyOne:

finetune_bird_results = test_dataset.evaluate_detections(
    "yolov8n_bird", 
    eval_key="finetune",
    compute_mAP=True,
)

Из этого мы можем сразу увидеть улучшение средней точности (mAP):

print("yolov8n mAP: {}.format(base_bird_results.mAP()))
print("fine-tuned mAP: {}.format(finetune_bird_results.mAP()))
yolov8n mAP: 0.24897924786479841
fine-tuned mAP: 0.31339033693212076

Распечатав отчет, мы видим, что отзыв улучшился с 0,39 до 0,56. Это значительное улучшение компенсирует незначительное снижение точности, давая в целом более высокий балл F1 (0,67 по сравнению с 0,54).

              precision    recall  f1-score   support

        bird       0.81      0.56      0.67       506

Мы также можем более внимательно рассмотреть отдельные изображения, чтобы увидеть, где у отлаженной модели возникают проблемы. Когда мы это делаем, мы видим, что модель изо всех сил пытается правильно обрабатывать мелкие функции. Это справедливо как для ложноположительных, так и для ложноотрицательных результатов.

Эта низкая производительность может быть частично связана с качеством данных, поскольку многие из этих функций являются зернистыми. Это также может быть связано с параметрами обучения, поскольку как для предварительного обучения, так и для точной настройки для этой модели использовался размер изображения 640 пикселей, что может не позволить захватывать мелкие детали.

Чтобы еще больше улучшить производительность модели, мы могли бы попробовать различные подходы, в том числе:

  • Использование увеличения изображения для увеличения доли изображений с маленькими птицами
  • Сбор и аннотирование большего количества изображений с маленькими птицами
  • Увеличение размера изображения во время тонкой настройки

Заключение

Тонкая настройка, представленная в предыдущем разделе, предназначена только для иллюстрации.

Несмотря на то, что YOLOv8 представляет собой шаг вперед в моделях обнаружения и сегментации объектов в реальном времени, изначально он предназначен для использования в общих целях. Перед развертыванием модели важно понять, как она работает с вашими данными. Только тогда вы сможете эффективно настроить архитектуру YOLOv8 в соответствии со своими конкретными потребностями.

В этой серии мы показали вам, как вы можете использовать FiftyOne для визуализации, оценки и лучшего понимания прогнозов модели YOLOv8.

В то время как YOLO может посмотреть только один раз, добросовестный инженер по компьютерному зрению или исследователь наверняка посмотрит дважды (или больше)!

Присоединяйтесь к сообществу FiftyOne!

Присоединяйтесь к тысячам инженеров и специалистов по данным, уже использующих FiftyOne для решения некоторых из самых сложных задач в области компьютерного зрения уже сегодня!

Что дальше?

Первоначально опубликовано на https://voxel51.com 21 февраля 2023 г.