Усё пра YOLOs - Part5 - Як гэта зафіксаваць

У гэтым пасце я растлумачу, як выкарыстоўваць ванільную версію YOLOv3 для выяўлення аб'ектаў з набору дадзеных COCO, а таксама як наладзіць свой уласны набор дадзеных для ўласнага выпадку выкарыстання.

Код выяўлення Yolo заснаваны на рэалізацыі Эрыка Ліндэрнорэна Джозэфа Рэдмона і працы Алі Фархадзі.

Вось спасылкі на серыю.

Усё пра YOLOs - Part1 - крыху гісторыі

Усё пра YOLO - Частка 2 - Першая YOLO

Усё пра YOLOs - Частка 3 - Лепш, хутчэй і мацней YOLOv2

Усё пра YOLOs - Part4 - YOLOv3, паступовае ўдасканаленне

Усё пра YOLOs - Part5 - Up and Running

Калі ласка, знайдзіце тэчку праекта ў маім gitrepo.

У тэчцы праекта вы знойдзеце падпапку з назвай канфігурацыя з канфігурацыйнымі файламі, назвамі класаў і пераменнымі асяроддзямі, папкай дадзеных з наборамі дадзеных і тэчкай утыліт з некаторымі карыснымі функцыямі python.

Спачатку загрузіце файл вагі YOLOv3 і змесціце яго ў тэчку config у праекце, выканаўшы наступную каманду. (Я дадаў .sh файл, каб зрабіць гэта ў рэпа)

wget https://pjreddie.com/media/files/yolov3.weights

Пасля загрузкі змесціва папкі config павінна выглядаць прыкладна наступнае.

Як наладзіць працу YOLOv3 для выяўлення аб'ектаў COCO?

Давайце разгледзім рэалізацыю ванільнага YOLO для зараджэння COCO аб'ектаў.

  • Імпартуйце неабходныя модулі.
ад мадэляў імпарту * ад імпарту ўтыліты * імпартуе os, sys, час, час даты, выпадковы імпорт факела з torch.utils.data імпартуе DataLoader з набораў дадзеных torchvision, пераўтварае з torch.autograd імпарт Variableimport matplotlib.pyplot як plt import matplotlib.patches as патчы з малюнка імпарту PIL
  • Загрузіце загадзя падрыхтаваную канфігурацыю і вагу, а таксама назвы класаў дадзеных COCO, на якіх навучалася мадэль Darknet. img_size, conf_thres і num_thresold - гэта параметры, якія можна наладзіць на аснове выпадку выкарыстання.
Заўвага: Усталюйце мадэль у рэжым eval для атрымання дадатковай інфармацыі.
config_path = 'config / yolov3.cfg' weights_path = 'config / yolov3.weights' class_path = 'config / coco.names' img_size = 416 conf_thres = 0.8 nms_thres = 0.4
# Загрузіце мадэль і мадэль вагі = Darknet (config_path, img_size = img_size) model.load_weights (weights_path) model.cuda () model.eval () класы = utils.load_classes (class_path) Tensor = torch.cuda.FloatTensor
  • Напісаць функцыю для выканання асноўнага выяўлення дадзенага малюнка. Калі ласка, паглядзіце каментары, што робіць код. У асноўным гэта папярэдняя апрацоўка малюнка.
def detect_image (img): # маштаб і каэфіцыент малюнка пляцоўкі = min (img_size / img.size [0], img_size / img.size [1]) imw = круглы (img.size [0] * суадносіны)) imh = круглы ( img.size [1] * стаўленне) img_transforms = transforms.Compose ([transforms.Resize ((imh, imw)), transforms.Pad ((max (int ((imh-imw) / 2), 0), max ( int ((imw-imh) / 2), 0), max (int ((imh-imw) / 2), 0), max (int ((imw-imh) / 2), 0)), (128,128,128) ), transforms.ToTensor (),]) # пераўтварыць малюнак у Tensor image_tensor = img_transforms (img) .float () image_tensor = image_tensor.unsqueeze_ (0) input_img = Variable (image_tensor.type (Tensor)) # запусціце вывад па мадэлі і атрымаць выяў з torch.no_grad (): detections = мадэль (input_img) detections = utils.non_max_suppression (дэтэкцыі, 80, conf_thres, nms_thres) вяртанне дэтэктаў [0]
  • Цяпер код для выкарыстання гэтай функцыі для атрымання высноў. Гэта працуе з любым аб'ектам, які знаходзіцца ў наборы дадзеных COCO. Зноў жа большая частка кода тычыцца папярэдняй апрацоўкі малюнка і пабудовы абмежавальных скрынак.
# загрузіць малюнак і атрымаць выяў img_path = "images / blueangels.jpg" prev_time = time.time () img = Image.open (img_path) detections = detect_image (img) inference_time = datetime.timedelta (секунды = time.time () - prev_time) print ('Час высновы:% s'% (inference_time)) # Атрымаць каляровыя рамкі cmap = plt.get_cmap ('tab20b') color = [cmap (i) для i ў np.linspace (0, 1, 20)] img = np.array (img) plt.figure () fig, ax = plt.subplots (1, figsize = (12,9)) ax.imshow (img) pad_x = max (img.shape [0] - img.shape [1], 0) * (img_size / max (img.shape)) pad_y = max (img.shape [1] - img.shape [0], 0) * (img_size / max (img.shape )) unpad_h = img_size - pad_y unpad_w = img_size - pad_xif выяўлення няма None: unique_labels = detections [:, -1] .cpu (). unique () n_cls_preds = len (unique_labels) bbox_colors = random.sample (колеры, n_cls_preds) # праглядаць выяўлення і маляваць абмежавальныя скрынкі для x1, y1, x2, y2, conf, cls_conf, cls_pred пры выяўленні: box_h = ((y2 - y1) / unpad_h) * img.shape [0] box_w = ((x2 - x1) / unpad_w) * я mg.shape [1] y1 = ((y1 - pad_y // 2) / unpad_h) * img.shape [0] x1 = ((x1 - pad_x // 2) / unpad_w) * img.shape [1] color = bbox_colors [int (np.where (unique_labels == int (cls_pred)) [0])] bbox = patches.Rectangle ((x1, y1), box_w, box_h, linewidth = 2, edgecolor = color, facecolor = 'none' ) ax.add_patch (bbox) plt.text (x1, y1, s = класы [int (cls_pred)], color = 'white', verticalalignment = 'top', bbox = {'color': color, 'pad': 0}) plt.axis ('off') # захаваць малюнак plt.savefig (img_path.replace (". Jpg", "-det.jpg"), bbox_inches = 'туга', pad_inches = 0.0) plt.show ()
  • Прыведзены вышэй сцэнар апрацоўвае атрыманне выяўлення на малюнках. Зараз давайце разгледзім, як прымусіць яго працаваць на відэа.
videopath = 'video / sample_video.mp4'
% Інтэрнэт-імпарт cv2 з IPython.display Імпарт clear_outputcmap = plt.get_cmap ('tab20b') колеру = [cmap (i) [: 3] для i ў np.linspace (0, 1, 20)] # ініцыялізаваць vid = cv2 .VideoCapture (videopath) # while (Праўда): для ii ў дыяпазоне (40): ret, frame = vid.read () frame = cv2.cvtColor (frame, cv2.COLOR_BGR2RGB) pilimg = Image.fromarray (frame) detections = detect_image (pilimg) img = np.array (pilimg) pad_x = max (img.shape [0] - img.shape [1], 0) * (img_size / max (img.shape)) pad_y = max (img.shape [1] - img.shape [0], 0) * (img_size / max (img.shape)) unpad_h = img_size - pad_y unpad_w = img_size - pad_x, калі выяўленняў няма: unique_labels = detections [:, -1]. cpu (). унікальны () n_cls_preds = len (unique_labels) для x1, y1, x2, y2, conf, cls_conf, cls_pred ў выяўленнях: box_h = int (((y2 - y1) / unpad_h) * img.shape [0] ) box_w = int (((x2 - x1) / unpad_w) * img.shape [1]) y1 = int (((y1 - pad_y // 2) / unpad_h) * img.shape [0]) x1 = int ( ((x1 - pad_x // 2) / unpad_w) * img.shape [1]) color = color [int (cls_conf. item ())% len (color)] color = [i * 255 for i in color] cls = класы [int (cls_pred)] cv2.rectangle (frame, (x1, y1), (x1 + box_w, y1 + box_h ), колер, 4) cv2.rectangle (кадр, (x1, y1-35), (x1 + len (cls) * 19 + 60, y1), колер, -1) cv2.putText (кадр, cls + "- "+ str (cls_conf.item ()), (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,255,255), 3) fig = figure (figsize = (12, 8)) title (" Video Stream ") imshow (frame) show () clear_output (чакаць = праўда)
Заўвага: Вышэй прыведзены код можна выкарыстоўваць і для жывой трансляцыі, змяніўшы шлях відэа ў IP-паток.

Як наладзіць мадэль ванілі YOLOv3, каб працаваць на карыстацкія аб'екты?

Давайце паглядзім на карыстацкую падрыхтоўку YOLOv3 для ўласнага прыкладу выкарыстання. Гэта некалькі спосабаў дасягнуць гэтага. Але наступныя крокі тлумачаць самы просты спосаб, які я знайшоў у Інтэрнэце.

Перадумовы

Дадзеныя

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

Ёсць шмат інструментаў, каб зрабіць гэта. Самы просты спосаб, які я знайшоў, - гэта выкарыстанне LabelImg. Гэта інструмент для анатацыі графічных малюнкаў. Вы можаце ўсталяваць з дапамогай каманды pip.

pip ўсталяваць labelImg

Націсніце на спасылку, каб знайсці добры падручнік пра тое, як ім карыстацца.

Маркіроўка малюнка проста азначае пазначэнне каардынатаў і класа абмежавальнай скрынкі. Такім чынам, для кожнага малюнка створаная метка (.txt файл) будзе мець адзін радок. Гэта тое, што называецца фарматам YOLO.

Заўвага: Калі ласка, выберыце фармат YOLO пры пазначэнні малюнкаў з дапамогай інструмента LabelImg.
#class xy ​​шырыня вышыня 1 0,351466 0,427083 0,367168 0,570486

Пасля таго, як выявы пазначаныя і генеруюцца надпісы (.txt файлы), запусціце сцэнарый, каб падзяліць набор дадзеных для навучання і праверкі. Калі ласка, запусціце наступны код python толькі адзін раз, каб дасягнуць гэтага. datasplit.py - файл з гэтым кодам у РЭПО.

import global import os import numpy as np import syscurrent_dir = "./data/artifacts/images" split_pct = 10 # 10% Набор праверкі file_train = open ("дадзеныя / артэфакты / train.txt", "w") file_val = open ( "data / artefacts / val.txt", "w") counter = 1 index_test = круглы (100 / split_pct) для поўнага шляху ў glob.iglob (os.path.join (current_dir, "* .JPG")): назва, ext = os.path.splitext (os.path.basename (fullpath)) калі лічыльнік == index_test: counter = 1 file_val.write (current_dir + "/" + title + '.JPG' + "\ n") астатняе: file_train.write (current_dir + "/" + title + '.JPG' + "\ n") counter = counter + 1 file_train.close () file_val.close ()

Гэта створыць 2 файлы, train.txt і val.txt, якія змяшчаюць поўны шлях усіх малюнкаў з 90% у цягніку і 10% у вал.

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

Асноўная папка --- дадзеныя --- імя набору дадзеных --- выявы --- img1.jpg --- img2.jpg .......... --- надпісы --- img1.txt --- img2.txt .......... --- train.txt --- val.txt

Наладзьце

Цяпер пра канфігурацыйныя файлы ў config / папцы. Па-першае, coco.data будзе выглядаць прыблізна так:

класаў = 80 # колькасць класаў ідзе сюды train = data / alpha / train.txt # шлях train.txt ідзе сюды сапраўднае = data / alpha / val.txt # шлях val.txt ідзе тут імёны = канфігурацыя /coco.names # рэдагаваць файл імёнаў з класнымі пазнакамі backup = backup / # Захоўвайце гэты параметр як ёсць

Адрэдагуйце гэтыя значэнні ў адпаведнасці з уласным наборам дадзеных. Адрэдагуйце "класы", каб утрымліваць колькасць класаў, якія будуць выяўлены ў вашым выпадку выкарыстання. Цягнік і сапраўдны праходзіць шлях да train.txt і val.txt адпаведна. Адрэдагуйце файл "coco.names" з ярлыкамі класа. мяркуецца ў спісе, па адным у радку, назвы класаў (для файла анатацый першы адпавядае 0, побач 1 і г.д.)

class1 class2 ...
Заўвага: параметр рэзервовага капіравання не выкарыстоўваецца, але, здаецца, неабходны.

Зараз пра файл yolov3.cfg. Гэта змяшчае архітэктурныя дэталі алгарытму YOLOv3.

У першым [чыстым] раздзеле наладзьце партыйнае значэнне і падраздзяленні, каб адпавядаць памяці вашай графічнай працэсары. Чым большы памер партыі, тым лепш і хутчэй будзе трэніроўка, але тым больш памяці спатрэбіцца. Таксама тут вы можаце наладзіць learning_rate.

Для GPU Nvidia з памяццю 11Gb добра падыходзіць партыя з 16 і 1 падраздзяленнямі.

Ёсць дзве іншыя важныя рэчы, якія трэба змяніць, - гэта значэнні класаў і выніковыя пласты фільтраў. І вы павінны зрабіць гэта ў трох розных месцах файла.

Калі вы шукаеце файл, вы знойдзеце 3 [йоло] раздзелы. Унутры гэтага раздзела ўсталюйце класы на колькасць класаў у вашай мадэлі.

Вы таксама павінны змяніць значэнне фільтраў у раздзеле [згорткі] прама над [ёла] на наступнае.

фільтры = (класы + 5) х 3

маючы ўсё вышэйзгаданае на месцы, вы зараз будзеце гатовыя трэніраваць мадэль.

Кодэкс

Рэалізацыя вучэбнага цыкла

  • Імпарт бібліятэк
ад падраздзялення __future__ імпарту
ад імпарту мадэляў * ад імпарту utils.utils * ад імпарту utils.datasets * ад імпарту utils.parse_config *
import os import sys час імпарту import date dateport імпарт argparse
імпарт факела з torch.utils.data імпарт DataLoader з torchvision імпарт набораў дадзеных ад torchvision імпарт пераўтворыць з torch.autograd імпарт Variable import torch.optim як аптымальны
  • Настройка параметра
epochs = 20 image_folder = "data / database / images" batch_size = 16 model_config_path = "config / yolov3.cfg" data_config_path = "config / coco.data" weights_path = "config / yolov3.weights" class_path = "config / coco.name "conf_thres = 0,8 nms_thres = 0,4 n_cpu = 0 img_size = 416 checkpoint_interval = 1 checkpoint_dir = 'кантрольныя пункты" use_cuda = Праўда
  • Выкарыстоўвайце CUDA пры наяўнасці
cuda = torch.cuda.is_available () і use_cuda
  • Атрымаць канфігурацыю дадзеных і параметры ў памяць
# Загрузіць класы os.makedirs ("кантрольныя пункты", існуюць_к = Праўда), класы = load_classes (class_path)
# Атрымаць налады дадзеных data_config = parse_data_config (data_config_path) train_path = data_config ["цягнік"]
# Атрымаеце гіперпараметры гіперпарамы = parse_model_config (model_config_path) [0] learning_rate = float (гіперпарамы ["learning_rate"]) імпульс = float (гіперпарамы ["імпульс"]) распаду = float (гіперпарамы ["распаду"]) burn_in = int ( гіперпарамы ["burn_in"])
  • Пачаць мадэль і ўзяцца за трэніроўку.
# Ініцыяваць мадэль мадэлі = Darknet (model_config_path) model.load_weights (weights_path)
калі cuda: model = model.cuda () model.train ()
  • Атрымаць загрузчык дадзеных і ўсталяваць аптымізатар
# Атрымаць загрузчык дадзеных dataloader = torch.utils.data.DataLoader (ListDataset (train_path), batch_size = batch_size, shuffle = False, num_workers = n_cpu)
Tensor = факел.cuda.FloatTensor, калі cuda else факел.FloatTensor
# Атрымаць аптымізатар аптымізатара = torch.optim.Adam (фільтр (лямбда p: p.requires_grad, model.parameters ()))
  • Зараз пра асноўны трэнінгавы цыкл.
для эпохі ў дыяпазоне (эпохі): для batch_i, (_, imgs, target) у пералічэнні (dataloader): imgs = Variable (imgs.type (Tensor)) target = Variable (targets.type (Tensor), need_grad = False)
optimizer.zero_grad ()
loss = мадэль (логі, мэты)
loss.backward () optimizer.step ()
print ("[Эпоха% d /% d, Batch% d /% d] [Страты: x% f, y% f, w% f, h% f, conf% f, cls% f, total% f, Нагадаем :% .5f, дакладнасць:% .5f] "% (эпоха, эпохі, batch_i, len (загрузчык дадзеных), model.losses [" x "], model.losses [" y "], model.losses [" w " ], model.losses ["h"], model.losses ["conf"], model.losses ["cls"], loss.item (), model.losses ["ўспомніць"], model.losses ["дакладнасць" "],))
model.seen + = imgs.size (0)
калі эпоха% checkpoint_interval == 0: print ("эканомія") model.save_weights ("% s /% d.weights"% (checkpoint_dir, "апошняя"))

Вышэйапісаны цыкл рыхтуе і захоўвае файл цяжараў у тэчцы мадэляў для кожнай эпохі з нумарам эпохі. Ён таксама друкуе кучу страт для кантролю за ходам навучання.

Ажыццяўленне высновы

  • Імпарт бібліятэк
ад мадэляў імпарту * ад імпарту ўтыліты * імпарту cv2 імпарту OS, Sys, часу, даты, выпадковага факела імпарту з torch.utils.data імпарту DataLoader з набораў дадзеных torchvision імпарту, пераўтварэнняў з torch.autograd імпарту Пераменная
імпарт matplotlib.pyplot як plt імпарт matplotlib.patches у выглядзе патчаў з PIL імпарт малюнкаў, імпарт малюнкаў, імутылаў з imutils.video імпарт WebcamVideoStream
  • Усталюйце параметры. Каб выкарыстоўваць новую мадэль для выяўлення, заменіце апошні файл цяжару, створаны ў тэчцы мадэляў, на файл yolov3.weights ў папцы config. Пераканайцеся, што шлях вагаў у кодзе высновы паказвае на апошні шлях вагаў.
num_classes = 1 config_path = 'config / yolov3.cfg' weights_path = 'checkpoint_19.weights' class_path = 'config / coco.names' img_size = 416 conf_thres = 0,95 nms_thres = 0,95
  • Загрузіце мадэль і ўсталюйце ў eval для высноў
# Мадэль нагрузкі і вага - мадэль = Darknet (config_path, img_size = img_size) model.load_weights (weights_path) # model.cuda () model.eval () класы = load_classes (class_path) Tensor = torch.FloatTensor
  • Вызначце функцыі для загрузкі класаў і выяўлення малюнка.
def load_classes (path): "" "Загружае ярлыкі класаў на" path "" "" fp = open (path, "r") names = fp.read (). split ("\ n") [:] return names
def detect_image (img): # маштаб і каэфіцыент малюнка пляцоўкі = min (img_size / img.size [0], img_size / img.size [1]) imw = круглы (img.size [0] * суадносіны)) imh = круглы ( img.size [1] * стаўленне) img_transforms = transforms.Compose ([transforms.Resize ((imh, imw)), transforms.Pad ((max (int ((imh-imw) / 2), 0), max ( int ((imw-imh) / 2), 0), max (int ((imh-imw) / 2), 0), max (int ((imw-imh) / 2), 0)), (128,128,128) ), transforms.ToTensor (),]) # пераўтварыць малюнак у Tensor image_tensor = img_transforms (img) .float () image_tensor = image_tensor.unsqueeze_ (0) input_img = Variable (image_tensor.type (Tensor)) # запусціце вывад па мадэлі і атрымаць выяў з torch.no_grad (): detections = model (input_img) detections = utils.non_max_suppression (дэтэкцыі, num_classes, conf_thres, nms_thres) вяртанне дэтэктаў [0]
  • Зараз пра цыкл высновы.
videopath = 'video / sample_video.mp4'
% Інтэрнэт-імпарт cv2 з IPython.display Імпарт clear_outputcmap = plt.get_cmap ('tab20b') колеру = [cmap (i) [: 3] для i ў np.linspace (0, 1, 20)] # ініцыялізаваць vid = cv2 .VideoCapture (videopath) # while (Праўда): для ii ў дыяпазоне (40): ret, frame = vid.read () frame = cv2.cvtColor (frame, cv2.COLOR_BGR2RGB) pilimg = Image.fromarray (frame) detections = detect_image (pilimg) img = np.array (pilimg) pad_x = max (img.shape [0] - img.shape [1], 0) * (img_size / max (img.shape)) pad_y = max (img.shape [1] - img.shape [0], 0) * (img_size / max (img.shape)) unpad_h = img_size - pad_y unpad_w = img_size - pad_x, калі выяўленняў няма: unique_labels = detections [:, -1]. cpu (). унікальны () n_cls_preds = len (unique_labels) для x1, y1, x2, y2, conf, cls_conf, cls_pred ў выяўленнях: box_h = int (((y2 - y1) / unpad_h) * img.shape [0] ) box_w = int (((x2 - x1) / unpad_w) * img.shape [1]) y1 = int (((y1 - pad_y // 2) / unpad_h) * img.shape [0]) x1 = int ( ((x1 - pad_x // 2) / unpad_w) * img.shape [1]) color = color [int (cls_conf. item ())% len (color)] color = [i * 255 for i in color] cls = класы [int (cls_pred)] cv2.rectangle (frame, (x1, y1), (x1 + box_w, y1 + box_h ), колер, 4) cv2.rectangle (кадр, (x1, y1-35), (x1 + len (cls) * 19 + 60, y1), колер, -1) cv2.putText (кадр, cls + "- "+ str (cls_conf.item ()), (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,255,255), 3) fig = figure (figsize = (12, 8)) title (" Video Stream ") imshow (frame) show () clear_output (чакаць = праўда)

Калі ласка, знайдзіце сшыткі з юпітэрам як для навучання, так і для высновы ў маім git-repo.

Я спадзяюся, што гэтая серыя дала вам дакладнае ўяўленне пра ўсё, што трэба ведаць пра YOLO, і пачаць працу з уласнай рэалізацыі.

Калі вы хочаце паглядзець блог па пэўнай тэме, калі ласка, не саромейцеся згадваць яго ў раздзеле адказаў. Я зраблю ўсё магчымае :)

Рэсурсы:

YOLO: https://arxiv.org/pdf/1506.02640.pdf

YOLOv2 і YOLO9000: https://arxiv.org/pdf/1612.08242.pdf

YOLOv3: https: //arxiv.org/pdf/1804.02767.pdf

Пра мяне

Я старэйшы эксперт AI у Wavelabs.ai. Мы ў Wavelabs дапамагаем вам выкарыстоўваць штучны інтэлект (AI), каб зрабіць рэвалюцыю ў карыстацкіх досведах і скараціць выдаткі. Мы ўнікальна пашыраем вашу прадукцыю, выкарыстоўваючы AI, каб дасягнуць поўнага патэнцыялу рынку. Мы стараемся прыцягнуць дасканалыя даследаванні ў вашых дадатках.

Не саромейцеся даследаваць больш на Wavelabs.ai.

Ну вось і ўсё ў гэтым пасце. Дзякуй за чытанне :)

Будзьце цікаўныя!

Вы можаце звязацца са мной на LinkedIn.