Использование графических процессоров Google Colab для ускорения обучения YOLOv4.

ИИ для нищих, скряг и скряг

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

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

К счастью, у Google есть бесплатный уровень для своих облачных сервисов (конечно, нет ничего действительно бесплатного, и это история в другой раз!), Который доступен из Google Colab. В то время как основное внимание Google Colab уделяет запуску кода Python с использованием блокнотов Jupyter, с помощью магических команд также может выполняться не-Python-код. Это открывает множество возможностей, так как многие модели ИИ написаны не на Python.

В этой статье мы покажем, как использовать Google Colab для передачи обучения на YOLO, хорошо известной модели компьютерного зрения для глубокого обучения, написанной на C и CUDA.

Настройка графического процессора Google Colab

Прежде чем делать что-либо еще, необходимо настроить графический процессор Google Colab, так как ЦП будет использоваться по умолчанию.

Чтобы использовать графический процессор, перейдите к «Изменить тип среды выполнения» в разделе «Время выполнения» в строке меню и выберите «Графический процессор» в разделе «Аппаратное ускорение». Не забудьте «Сохранить»!

На бесплатном уровне доступен только T4 GPU. Плата за премиальные уровни позволит разблокировать более мощные графические процессоры, такие как A100 или V100 GPU.

Подробнее о графических процессорах, доступных в Google Cloud, можно узнать здесь.

Загрузка данных напрямую из Kaggle

В этой демонстрации мы будем загружать данные непосредственно из Kaggle в Google Colab для обучения модели. Для этого вам нужно сначала создать Kaggle API token. Загруженный файл токена Kaggle API kaggle.json необходимо загрузить в Google Colab.

После загрузки kaggle.json со страницы своей учетной записи Kaggle создайте каталог с именем .kaggle в среде Google Colab.

!mkdir ~/.kaggle

Загрузите загруженный kaggle.json в Google Colab и скопируйте его в .kaggle.

!cp ./kaggle.json ~/.kaggle/
!chmod 600 ~/.kaggle/kaggle.json

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

!kaggle datasets download -d andrewmvd/hard-hat-detection

Компиляция даркнета

Далее нам нужно скомпилировать даркнет в Google Colab для обучения и использования YOLO.

Во-первых, убедитесь, что активированный ранее графический процессор доступен. На момент написания статьи Google Colab использует CUDA 11.8 для графического процессора T4.

!/usr/local/cuda/bin/nvcc - version

nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2022 NVIDIA Corporation
Built on Wed_Septest.txt10:33:58_PDT_2022
Cuda compilation tools, release 11.8, V11.8.89
Build cuda_11.8.r11.8/compiler.31833905_0

Далее клонируем репозиторий даркнет.

!git clone https://github.com/AlexeyAB/darknet

Как только даркнет будет клонирован, нам нужно изменить его make-файл, чтобы разрешить использование графического процессора YOLO. Следующие команды изменяют каталог на загруженный репозиторий даркнета и включают переключатели для OpenCV, GPU и CUDNN в Makefile.

%cd darknet
!sed -i 's/OPENCV=0/OPENCV=1/' Makefile
!sed -i 's/GPU=0/GPU=1/' Makefile
!sed -i 's/CUDNN=0/CUDNN=1/' Makefile
!sed -i 's/CUDNN_HALF=0/CUDNN_HALF=1/' Makefile

Как только Makefile будет правильно настроен, скомпилируйте darknet.

!make

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

Загрузка предварительно обученных весов для трансферного обучения

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

Для трансферного обучения частичные веса для первых 29 слоев крошечной модели можно загрузить с помощью команды:

# Download partial yolo-tiny weights for transfer learning.
!wget https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v4_pre/yolov4-tiny.conv.29

Подготовка данных для YOLO

Теперь, когда YOLO скомпилирован и веса загружены, пришло время подготовить данные для YOLO.

Сначала скопируйте загруженный zip-файл в darknet,

!cp ../hard-hat-detection.zip ./

и распакуйте файл.

!unzip hard-hat-detection.zip

Для данных обнаружения каски будут созданы каталоги — images и annotations.

Каждому изображению в images соответствует файл .xml в annotations. Нам нужно извлечь все отдельные аннотации ограничивающей рамки из каждого файла.xml, переформатировать ограничивающую рамку, а затем сохранить аннотации в файл .txt, по одной для каждого изображения.

Поскольку аннотации ограничительной рамки для этого конкретного набора данных представлены в формате Pascal VOC, нам нужно изменить их в формат YOLO, чтобы YOLO мог принимать их. Мы будем использовать Python, чтобы помочь нам сделать это.

import os

from google.colab import files

import cv2
import matplotlib.pyplot as plt
import numpy as np

from sklearn.model_selection import train_test_split

import xml.etree.ElementTree as ET


# Functions to convert Pascal VOC bounding boxes to YOLO format.
def xml_to_yolo_bbox(bndbox, image_size):
    # xml to yolo bounding box.
    xmin, xmax, ymin, ymax = bndbox
    # x and y mid points of the bounding box.
    x_mid = (xmin + xmax) * 0.5
    y_mid = (ymin + ymax) * 0.5
    # Height and width of the box.
    height = ymax - ymin
    width = xmax - xmin

    image_width, image_height = image_size
    image_width_inv = 1 / image_width
    image_height_inv = 1 / image_height

    x_mid = x_mid * image_width_inv
    width = width * image_width_inv
    y_mid = y_mid * image_height_inv
    height = height * image_height_inv
    return [x_mid, y_mid, width, height]


def xml_to_yolo(xml_file_path, txt_file_path, wanted_classes):
    tree = ET.parse(xml_file_path)
    root = tree.getroot()

    size = root.find("size")
    img_width = int(size.find("width").text)
    img_height = int(size.find("height").text)

    output_string = ""

    for o in root.iter("object"):
        obj_class = o.find("name").text
        if obj_class in wanted_classes:
          obj_class_id = str(wanted_classes.index(obj_class))
          bndbox = o.find("bndbox")
          bndbox = [float(bndbox.find("xmin").text),
                    float(bndbox.find("xmax").text),
                    float(bndbox.find("ymin").text),
                    float(bndbox.find("ymax").text)]

          yolo_box = xml_to_yolo_bbox(bndbox, [img_width, img_height])

          output_string = output_string + obj_class_id + " "
          output_string = output_string + " ".join([str(b) for b in yolo_box])
          output_string = output_string + "\n"

    with open(txt_file_path, "w") as f:
        f.write(output_string)

Сначала создайте файл с именем obj.names, который содержит уникальные имена категорий в наборе данных. Для загруженного набора данных нас интересуют классы «шлем» и «голова».

# Prepare the obj.names, obj.data and .cfg files required for Yolov4 training.
wanted_classes = ["helmet", "head"]
image_extension = ".png"

# 1. obj.names
# This file contains the categories of all the objects in the dataset.
# The index of the category serves as its numerical category.
# Therefore class 0 is helmet and class 1 is head.
obj_names_output_string = ""
for wc in wanted_classes:
    obj_names_output_string = obj_names_output_string + wc + "\n"

with open("obj.names", "w") as f:
    f.write(obj_names_output_string)

obj.names будет иметь следующее содержимое.

helmet
head

Затем мы создаем файлы аннотаций .txt YOLO, по одному для каждого изображения.

# 2. .txt annotation files.
# YOLO requires that each image file has its own .txt annotation file with the
# bounding box format:
# category x_mid_point y_mid_point width height
ann_xml_file_list = os.listdir("annotations/")
image_file_list = []

# Get all image names.
for i in os.listdir("images/"):
    if i[-4:] == image_extension:
        image_file_list.append(i)

print("{} images.".format(len(image_file_list)))

# Create the .txt annotations from the .xml files.
for a in ann_xml_file_list:
    if a[-4:] == ".xml":
        file_root_name = a[:-4]
        if file_root_name + image_extension in image_file_list:
            txt_name = file_root_name + ".txt"
            xml_to_yolo(os.path.join("annotations/", a),
                        os.path.join("images/", txt_name),
                        wanted_classes)

В дополнение к отдельным файлам аннотаций .txt мы также создаем train.txt и test.txt, которые явно указывают список обучающих и проверочных изображений соответственно.

# In addition to the annotation .txt files, we also need to explicitly specify
# the training images and test images as .txt files.

# Make the train test split.
train_image_list, test_image_list = train_test_split(image_file_list,
                                                     test_size = 0.2,
                                                     random_state = 42)
train_image_list.sort()
test_image_list.sort()
print("Train images: {}, test images: {}.".format(len(train_image_list),
                                                  len(test_image_list)))

# Make the train.txt file.
# This will contain a list of all training images.
train_txt_output_string = ""
for f in train_image_list:
    train_txt_output_string = train_txt_output_string + "images/" + f + "\n"

with open("train.txt", "w") as f:
    f.write(train_txt_output_string)

# Make the test.txt file.
# This will contain a list of all testing images.
test_txt_output_string = ""
for f in test_image_list:
    test_txt_output_string = test_txt_output_string + "images/" + f + "\n"

with open("test.txt", "w") as f:
    f.write(test_txt_output_string)

Например, train.txt будет иметь следующее содержимое.

images/hard_hat_workers0.png
images/hard_hat_workers1.png
images/hard_hat_workers10.png
images/hard_hat_workers100.png
images/hard_hat_workers1000.png

Наконец, мы создаем obj.data, который связывает вместе все файлы, созданные выше. Этот файл также сообщает YOLO, где сохранять выходные данные. В этом случае мы указываем расположение резервной копии backup.

# 3. obj.data
# This file tells YOLO how many classes there are in the dataset, the paths to
# train.txt and test.txt, obj.names as well as the output location "backup".
obj_data_output_string = """classes = 2
train = train.txt
valid = test.txt
names = obj.names
backup = backup"""

with open("obj.data", "w") as f:
    f.write(obj_data_output_string)

obj.data будет иметь следующее содержимое.

classes = 2
train = train.txt
valid = test.txt
names = obj.names
backup = backup

Подготовка файла конфигурации

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

!cp cfg/yolov4-tiny-custom.cfg ./

Мы следуем инструкциям, представленным в репозитории даркнета github, чтобы изменить скопированный файл .cfg в соответствии с нашими требованиями. В частности, загруженный набор данных имеет 2 класса, поэтому количество filters перед каждым слоем yolo, а также количество classes необходимо соответствующим образом скорректировать.

# Modify .cfg as outlined in https://github.com/AlexeyAB/darknet.
with open("yolov4-tiny-custom.cfg", "r") as f:
    cfg_string = f.read()

cfg_string = cfg_string.split("\n")

# change line subdivisions to subdivisions=16
cfg_string[6] = "subdivisions=16"
# change line max_batches to (classes*2000, but not less than number of training
# images and not less than 6000), f.e. max_batches=6000 if you train for 3
# classes
cfg_string[19] = "max_batches=6000"
# change line steps to 80% and 90% of max_batches, f.e. steps=4800,5400
cfg_string[21] = "steps=4800,5400"
# change [filters=255] to filters=(classes + 5)x3 in the 3 [convolutional]
# before each [yolo] layer, keep in mind that it only has to be the last
# [convolutional] before each of the [yolo] layers
# change line classes=80 to your number of objects in each of 3 [yolo]-layers
# For YOLO-Mini there are only 2 [yolo] layers
cfg_string[211] = "filters=21"
cfg_string[219] = "classes=2"
cfg_string[262] = "filters=21"
cfg_string[268] = "classes=2"

cfg_string = "\n".join(cfg_string)

with open("yolov4-tiny-custom.cfg", "w") as f:
    f.write(cfg_string)

YOLO Transfer Learning в Google Colab

Это было довольно много работы — однако, если все было сделано правильно, мы, наконец, можем начать процесс трансферного обучения.

Выполняем скомпилированный исполняемый файл darknet в режиме train и передаем ему файл настроек данных obj.data, файл конфигурации модели yolov4-tiny-custom.cfg и частичные предварительно обученные веса yolov4-tiny.conv.29. Мы решили не показывать историю тренировок с помощью -dont_show, поскольку визуализация не поддерживается Google Colab. Наконец, мы решили использовать среднюю среднюю точность для отслеживания прогресса обучения с помощью -map.

!./darknet detector train obj.data yolov4-tiny-custom.cfg yolov4-tiny.conv.29 -dont_show -map

Даже при использовании графических процессоров это обучение займет некоторое время. В моем случае обучение заняло почти 1,5 часа даже для крошечной модели.

В конце модель отображает метрики обучения, а также имена точно настроенных файлов веса.

 Tensor Cores are used.
 Last accuracy [email protected] = 90.96 %, best = 90.96 % 
 6000: 0.609664, 0.596759 avg loss, 0.000026 rate, 0.849180 seconds, 384000 images, 0.025016 hours left

 calculation mAP (mean average precision)...
 Detection layer: 30 - type = 28 
 Detection layer: 37 - type = 28 
1000
 detections_count = 18411, unique_truth_count = 4822  
class_id = 0, name = helmet, ap = 92.04%     (TP = 3236, FP = 510) 
class_id = 1, name = head, ap = 89.79%     (TP = 1025, FP = 175) 

 for conf_thresh = 0.25, precision = 0.86, recall = 0.88, F1-score = 0.87 
 for conf_thresh = 0.25, TP = 4261, FP = 685, FN = 561, average IoU = 69.20 % 

 IoU threshold = 50 %, used Area-Under-Curve for each unique Recall 
 mean average precision ([email protected]) = 0.909143, or 90.91 % 
Total Detection Time: 13 Seconds

Set -points flag:
 `-points 101` for MS COCO 
 `-points 11` for PascalVOC 2007 (uncomment `difficult` in voc.data) 
 `-points 0` (AUC) for ImageNet, PascalVOC 2010-2012, your custom dataset

 mean_average_precision ([email protected]) = 0.909143 
Saving weights to backup/yolov4-tiny-custom_6000.weights
Saving weights to backup/yolov4-tiny-custom_last.weights
Saving weights to backup/yolov4-tiny-custom_final.weights
If you want to train from the beginning, then use flag in the end of training command: -clear 

Вывод YOLO в Google Colab

После завершения процесса переноса обучения мы можем сделать вывод, используя точно настроенные веса, сохраненные в backup.

Выполняем скомпилированный исполняемый файл darknet в режиме test, и передаем ему файл настроек данных obj.data, файл конфигурации модели yolov4-tiny-custom.cfg. На этот раз мы передаем точно настроенные веса backup/yolov4-tiny-custom_last.weights. Мы также передаем изображение для вывода images/hard_hat_workers10.png. Вместо того, чтобы показывать вывод, мы решили сохранить его в файл с именем predictions.jpg.

!./darknet detector test obj.data yolov4-tiny-custom.cfg backup/yolov4-tiny-custom_last.weights images/hard_hat_workers10.png --dont_show --out_filename predictions.jpg

Загрузка и открытие predictions.jpg показывает, что точно настроенная модель смогла правильно предсказать расположение касок на входном изображении!

Краткое содержание

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

Блокнот Jupyter для кода выше доступен на GitHub.

Рекомендации

  1. https://github.com/AlexeyAB/даркнет
  2. https://github.com/theAIGuysCode/YOLOv4-Cloud-Tutorial/blob/master/YOLOv4_Training_Tutorial.ipynb
  3. https://albumentations.ai/docs/getting_started/bounding_boxes_augmentation/
  4. https://jonathan-hui.medium.com/map-mean-average-precision-for-object-detection-45c121a31173

СТАНЬТЕ ПИСАТЕЛЕМ на MLearning.ai //БЕСПЛАТНЫЕ инструменты ML// AI Кинокритики