Merge pull request #1 from ultralytics/master
update from original master
This commit is contained in:
commit
648ed20717
19
README.md
19
README.md
|
@ -64,7 +64,7 @@ HS**V** Intensity | +/- 50%
|
||||||
## Speed
|
## Speed
|
||||||
|
|
||||||
https://cloud.google.com/deep-learning-vm/
|
https://cloud.google.com/deep-learning-vm/
|
||||||
**Machine type:** n1-highmem-4 (4 vCPUs, 26 GB memory)
|
**Machine type:** n1-standard-8 (8 vCPUs, 30 GB memory)
|
||||||
**CPU platform:** Intel Skylake
|
**CPU platform:** Intel Skylake
|
||||||
**GPUs:** 1-4 x NVIDIA Tesla P100
|
**GPUs:** 1-4 x NVIDIA Tesla P100
|
||||||
**HDD:** 100 GB SSD
|
**HDD:** 100 GB SSD
|
||||||
|
@ -72,19 +72,22 @@ https://cloud.google.com/deep-learning-vm/
|
||||||
GPUs | `batch_size` | speed | COCO epoch
|
GPUs | `batch_size` | speed | COCO epoch
|
||||||
--- |---| --- | ---
|
--- |---| --- | ---
|
||||||
(P100) | (images) | (s/batch) | (min/epoch)
|
(P100) | (images) | (s/batch) | (min/epoch)
|
||||||
1 | 16 | 0.54s | 66min
|
1 | 16 | 0.39s | 48min
|
||||||
2 | 32 | 0.99s | 61min
|
2 | 32 | 0.48s | 29min
|
||||||
4 | 64 | 1.61s | 49min
|
4 | 64 | 0.65s | 20min
|
||||||
|
|
||||||
# Inference
|
# Inference
|
||||||
|
|
||||||
Run `detect.py` to apply trained weights to an image, such as `zidane.jpg` from the `data/samples` folder:
|
Run `detect.py` to apply trained weights to an image, such as `zidane.jpg` from the `data/samples` folder:
|
||||||
|
|
||||||
**YOLOv3:** `detect.py --cfg cfg/yolov3.cfg --weights weights/yolov3.pt`
|
**YOLOv3:** `python3 detect.py --cfg cfg/yolov3.cfg --weights weights/yolov3.weights`
|
||||||
<img src="https://user-images.githubusercontent.com/26833433/50524393-b0adc200-0ad5-11e9-9335-4774a1e52374.jpg" width="700">
|
<img src="https://user-images.githubusercontent.com/26833433/50524393-b0adc200-0ad5-11e9-9335-4774a1e52374.jpg" width="600">
|
||||||
|
|
||||||
**YOLOv3-tiny:** `detect.py --cfg cfg/yolov3-tiny.cfg --weights weights/yolov3-tiny.pt`
|
**YOLOv3-tiny:** `python3 detect.py --cfg cfg/yolov3-tiny.cfg --weights weights/yolov3-tiny.weights`
|
||||||
<img src="https://user-images.githubusercontent.com/26833433/50374155-21427380-05ea-11e9-8d24-f1a4b2bac1ad.jpg" width="700">
|
<img src="https://user-images.githubusercontent.com/26833433/50374155-21427380-05ea-11e9-8d24-f1a4b2bac1ad.jpg" width="600">
|
||||||
|
|
||||||
|
**YOLOv3-SPP:** `python3 detect.py --cfg cfg/yolov3-spp.cfg --weights weights/yolov3-spp.weights`
|
||||||
|
<img src="https://user-images.githubusercontent.com/26833433/54747926-e051ff00-4bd8-11e9-8b5d-93a41d871ec7.jpg" width="600">
|
||||||
|
|
||||||
## Webcam
|
## Webcam
|
||||||
|
|
||||||
|
|
|
@ -174,9 +174,6 @@ class Darknet(nn.Module):
|
||||||
self.module_defs[0]['cfg'] = cfg_path
|
self.module_defs[0]['cfg'] = cfg_path
|
||||||
self.module_defs[0]['height'] = img_size
|
self.module_defs[0]['height'] = img_size
|
||||||
self.hyperparams, self.module_list = create_modules(self.module_defs)
|
self.hyperparams, self.module_list = create_modules(self.module_defs)
|
||||||
self.img_size = img_size
|
|
||||||
self.loss_names = ['loss', 'xy', 'wh', 'conf', 'cls', 'nT']
|
|
||||||
self.losses = []
|
|
||||||
|
|
||||||
def forward(self, x, var=None):
|
def forward(self, x, var=None):
|
||||||
img_size = x.shape[-1]
|
img_size = x.shape[-1]
|
||||||
|
|
21
test.py
21
test.py
|
@ -3,6 +3,8 @@ import json
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from torch.utils.data import DataLoader
|
||||||
|
|
||||||
from models import *
|
from models import *
|
||||||
from utils.datasets import *
|
from utils.datasets import *
|
||||||
from utils.utils import *
|
from utils.utils import *
|
||||||
|
@ -39,16 +41,21 @@ def test(
|
||||||
|
|
||||||
model.to(device).eval()
|
model.to(device).eval()
|
||||||
|
|
||||||
# Get dataloader
|
# Dataloader
|
||||||
# dataloader = torch.utils.data.DataLoader(LoadImagesAndLabels(test_path), batch_size=batch_size)
|
dataset = LoadImagesAndLabels(test_path, img_size=img_size)
|
||||||
dataloader = LoadImagesAndLabels(test_path, batch_size=batch_size, img_size=img_size)
|
dataloader = DataLoader(dataset, batch_size=batch_size, num_workers=4)
|
||||||
|
|
||||||
mean_mAP, mean_R, mean_P, seen = 0.0, 0.0, 0.0, 0
|
mean_mAP, mean_R, mean_P, seen = 0.0, 0.0, 0.0, 0
|
||||||
print('%11s' * 5 % ('Image', 'Total', 'P', 'R', 'mAP'))
|
print('%11s' * 5 % ('Image', 'Total', 'P', 'R', 'mAP'))
|
||||||
mP, mR, mAPs, TP, jdict = [], [], [], [], []
|
mP, mR, mAPs, TP, jdict = [], [], [], [], []
|
||||||
AP_accum, AP_accum_count = np.zeros(nC), np.zeros(nC)
|
AP_accum, AP_accum_count = np.zeros(nC), np.zeros(nC)
|
||||||
coco91class = coco80_to_coco91_class()
|
coco91class = coco80_to_coco91_class()
|
||||||
for (imgs, targets, paths, shapes) in dataloader:
|
for imgs, targets, paths, shapes in dataloader:
|
||||||
|
# Unpad and collate targets
|
||||||
|
for j, t in enumerate(targets):
|
||||||
|
t[:, 0] = j
|
||||||
|
targets = torch.cat([t[t[:, 5].nonzero()] for t in targets], 0).squeeze(1)
|
||||||
|
|
||||||
targets = targets.to(device)
|
targets = targets.to(device)
|
||||||
t = time.time()
|
t = time.time()
|
||||||
output = model(imgs.to(device))
|
output = model(imgs.to(device))
|
||||||
|
@ -71,7 +78,7 @@ def test(
|
||||||
if save_json:
|
if save_json:
|
||||||
# [{"image_id": 42, "category_id": 18, "bbox": [258.15, 41.29, 348.26, 243.78], "score": 0.236}, ...
|
# [{"image_id": 42, "category_id": 18, "bbox": [258.15, 41.29, 348.26, 243.78], "score": 0.236}, ...
|
||||||
box = detections[:, :4].clone() # xyxy
|
box = detections[:, :4].clone() # xyxy
|
||||||
scale_coords(img_size, box, shapes[si]) # to original shape
|
scale_coords(img_size, box, (shapes[0][si], shapes[1][si])) # to original shape
|
||||||
box = xyxy2xywh(box) # xywh
|
box = xyxy2xywh(box) # xywh
|
||||||
box[:, :2] -= box[:, 2:] / 2 # xy center to top-left corner
|
box[:, :2] -= box[:, 2:] / 2 # xy center to top-left corner
|
||||||
|
|
||||||
|
@ -129,7 +136,7 @@ def test(
|
||||||
|
|
||||||
# Print image mAP and running mean mAP
|
# Print image mAP and running mean mAP
|
||||||
print(('%11s%11s' + '%11.3g' * 4 + 's') %
|
print(('%11s%11s' + '%11.3g' * 4 + 's') %
|
||||||
(seen, dataloader.nF, mean_P, mean_R, mean_mAP, time.time() - t))
|
(seen, len(dataset), mean_P, mean_R, mean_mAP, time.time() - t))
|
||||||
|
|
||||||
# Print mAP per class
|
# Print mAP per class
|
||||||
print('\nmAP Per Class:')
|
print('\nmAP Per Class:')
|
||||||
|
@ -139,7 +146,7 @@ def test(
|
||||||
|
|
||||||
# Save JSON
|
# Save JSON
|
||||||
if save_json:
|
if save_json:
|
||||||
imgIds = [int(Path(x).stem.split('_')[-1]) for x in dataloader.img_files]
|
imgIds = [int(Path(x).stem.split('_')[-1]) for x in dataset.img_files]
|
||||||
with open('results.json', 'w') as file:
|
with open('results.json', 'w') as file:
|
||||||
json.dump(jdict, file)
|
json.dump(jdict, file)
|
||||||
|
|
||||||
|
|
77
train.py
77
train.py
|
@ -1,6 +1,8 @@
|
||||||
import argparse
|
import argparse
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from torch.utils.data import DataLoader
|
||||||
|
|
||||||
import test # Import test.py to get mAP after each epoch
|
import test # Import test.py to get mAP after each epoch
|
||||||
from models import *
|
from models import *
|
||||||
from utils.datasets import *
|
from utils.datasets import *
|
||||||
|
@ -17,6 +19,7 @@ def train(
|
||||||
accumulate=1,
|
accumulate=1,
|
||||||
multi_scale=False,
|
multi_scale=False,
|
||||||
freeze_backbone=False,
|
freeze_backbone=False,
|
||||||
|
num_workers=0
|
||||||
):
|
):
|
||||||
weights = 'weights' + os.sep
|
weights = 'weights' + os.sep
|
||||||
latest = weights + 'latest.pt'
|
latest = weights + 'latest.pt'
|
||||||
|
@ -34,47 +37,39 @@ def train(
|
||||||
# Initialize model
|
# Initialize model
|
||||||
model = Darknet(cfg, img_size).to(device)
|
model = Darknet(cfg, img_size).to(device)
|
||||||
|
|
||||||
# Get dataloader
|
# Optimizer
|
||||||
dataloader = LoadImagesAndLabels(train_path, batch_size, img_size, augment=True)
|
|
||||||
# dataloader = torch.utils.data.DataLoader(dataloader, batch_size=batch_size, num_workers=0)
|
|
||||||
|
|
||||||
lr0 = 0.001 # initial learning rate
|
lr0 = 0.001 # initial learning rate
|
||||||
|
optimizer = torch.optim.SGD(model.parameters(), lr=lr0, momentum=.9)
|
||||||
|
|
||||||
|
# Dataloader
|
||||||
|
dataset = LoadImagesAndLabels(train_path, img_size=img_size, augment=True)
|
||||||
|
dataloader = DataLoader(dataset, batch_size=batch_size, num_workers=num_workers)
|
||||||
|
|
||||||
cutoff = -1 # backbone reaches to cutoff layer
|
cutoff = -1 # backbone reaches to cutoff layer
|
||||||
start_epoch = 0
|
start_epoch = 0
|
||||||
best_loss = float('inf')
|
best_loss = float('inf')
|
||||||
if resume:
|
if resume: # Load previously saved PyTorch model
|
||||||
checkpoint = torch.load(latest, map_location=device)
|
checkpoint = torch.load(latest, map_location=device) # load checkpoint
|
||||||
|
|
||||||
# Load weights to resume from
|
|
||||||
model.load_state_dict(checkpoint['model'])
|
model.load_state_dict(checkpoint['model'])
|
||||||
|
|
||||||
# Transfer learning (train only YOLO layers)
|
|
||||||
# for i, (name, p) in enumerate(model.named_parameters()):
|
|
||||||
# p.requires_grad = True if (p.shape[0] == 255) else False
|
|
||||||
|
|
||||||
# Set optimizer
|
|
||||||
optimizer = torch.optim.SGD(filter(lambda x: x.requires_grad, model.parameters()), lr=lr0, momentum=.9)
|
|
||||||
|
|
||||||
start_epoch = checkpoint['epoch'] + 1
|
start_epoch = checkpoint['epoch'] + 1
|
||||||
if checkpoint['optimizer'] is not None:
|
if checkpoint['optimizer'] is not None:
|
||||||
optimizer.load_state_dict(checkpoint['optimizer'])
|
optimizer.load_state_dict(checkpoint['optimizer'])
|
||||||
best_loss = checkpoint['best_loss']
|
best_loss = checkpoint['best_loss']
|
||||||
|
|
||||||
del checkpoint # current, saved
|
del checkpoint # current, saved
|
||||||
|
|
||||||
else:
|
else: # Initialize model with backbone (optional)
|
||||||
# Initialize model with backbone (optional)
|
|
||||||
if cfg.endswith('yolov3.cfg'):
|
if cfg.endswith('yolov3.cfg'):
|
||||||
cutoff = load_darknet_weights(model, weights + 'darknet53.conv.74')
|
cutoff = load_darknet_weights(model, weights + 'darknet53.conv.74')
|
||||||
elif cfg.endswith('yolov3-tiny.cfg'):
|
elif cfg.endswith('yolov3-tiny.cfg'):
|
||||||
cutoff = load_darknet_weights(model, weights + 'yolov3-tiny.conv.15')
|
cutoff = load_darknet_weights(model, weights + 'yolov3-tiny.conv.15')
|
||||||
|
|
||||||
# Set optimizer
|
|
||||||
optimizer = torch.optim.SGD(model.parameters(), lr=lr0, momentum=.9)
|
|
||||||
|
|
||||||
if torch.cuda.device_count() > 1:
|
if torch.cuda.device_count() > 1:
|
||||||
|
print('WARNING: MultiGPU Issue: https://github.com/ultralytics/yolov3/issues/146')
|
||||||
model = nn.DataParallel(model)
|
model = nn.DataParallel(model)
|
||||||
model.to(device).train()
|
|
||||||
|
# Transfer learning (train only YOLO layers)
|
||||||
|
# for i, (name, p) in enumerate(model.named_parameters()):
|
||||||
|
# p.requires_grad = True if (p.shape[0] == 255) else False
|
||||||
|
|
||||||
# Set scheduler
|
# Set scheduler
|
||||||
# scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[54, 61], gamma=0.1)
|
# scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[54, 61], gamma=0.1)
|
||||||
|
@ -94,10 +89,7 @@ def train(
|
||||||
# scheduler.step()
|
# scheduler.step()
|
||||||
|
|
||||||
# Update scheduler (manual)
|
# Update scheduler (manual)
|
||||||
if epoch > 250:
|
lr = lr0 / 10 if epoch > 250 else lr0
|
||||||
lr = lr0 / 10
|
|
||||||
else:
|
|
||||||
lr = lr0
|
|
||||||
for x in optimizer.param_groups:
|
for x in optimizer.param_groups:
|
||||||
x['lr'] = lr
|
x['lr'] = lr
|
||||||
|
|
||||||
|
@ -107,14 +99,28 @@ def train(
|
||||||
if int(name.split('.')[1]) < cutoff: # if layer < 75
|
if int(name.split('.')[1]) < cutoff: # if layer < 75
|
||||||
p.requires_grad = False if (epoch == 0) else True
|
p.requires_grad = False if (epoch == 0) else True
|
||||||
|
|
||||||
ui = -1
|
|
||||||
rloss = defaultdict(float)
|
rloss = defaultdict(float)
|
||||||
for i, (imgs, targets, _, _) in enumerate(dataloader):
|
for i, (imgs, targets, _, _) in enumerate(dataloader):
|
||||||
targets = targets.to(device)
|
# Unpad and collate targets
|
||||||
nT = targets.shape[0]
|
for j, t in enumerate(targets):
|
||||||
|
t[:, 0] = j
|
||||||
|
targets = torch.cat([t[t[:, 5].nonzero()] for t in targets], 0).squeeze(1)
|
||||||
|
|
||||||
|
nT = len(targets)
|
||||||
if nT == 0: # if no targets continue
|
if nT == 0: # if no targets continue
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Plot images with bounding boxes
|
||||||
|
plot_images = False
|
||||||
|
if plot_images:
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
plt.figure(figsize=(10, 10))
|
||||||
|
for ip in range(batch_size):
|
||||||
|
labels = xywh2xyxy(targets[targets[:, 0] == ip, 2:6]).numpy() * img_size
|
||||||
|
plt.subplot(4, 4, ip + 1).imshow(imgs[ip].numpy().transpose(1, 2, 0))
|
||||||
|
plt.plot(labels[:, [0, 2, 2, 0, 0]].T, labels[:, [1, 1, 3, 3, 1]].T, '.-')
|
||||||
|
plt.axis('off')
|
||||||
|
|
||||||
# SGD burn-in
|
# SGD burn-in
|
||||||
if (epoch == 0) and (i <= n_burnin):
|
if (epoch == 0) and (i <= n_burnin):
|
||||||
lr = lr0 * (i / n_burnin) ** 4
|
lr = lr0 * (i / n_burnin) ** 4
|
||||||
|
@ -125,7 +131,7 @@ def train(
|
||||||
pred = model(imgs.to(device))
|
pred = model(imgs.to(device))
|
||||||
|
|
||||||
# Build targets
|
# Build targets
|
||||||
target_list = build_targets(model, targets, pred)
|
target_list = build_targets(model, targets.to(device), pred)
|
||||||
|
|
||||||
# Compute loss
|
# Compute loss
|
||||||
loss, loss_dict = compute_loss(pred, target_list)
|
loss, loss_dict = compute_loss(pred, target_list)
|
||||||
|
@ -139,9 +145,8 @@ def train(
|
||||||
optimizer.zero_grad()
|
optimizer.zero_grad()
|
||||||
|
|
||||||
# Running epoch-means of tracked metrics
|
# Running epoch-means of tracked metrics
|
||||||
ui += 1
|
|
||||||
for key, val in loss_dict.items():
|
for key, val in loss_dict.items():
|
||||||
rloss[key] = (rloss[key] * ui + val) / (ui + 1)
|
rloss[key] = (rloss[key] * i + val) / (i + 1)
|
||||||
|
|
||||||
s = ('%8s%12s' + '%10.3g' * 7) % (
|
s = ('%8s%12s' + '%10.3g' * 7) % (
|
||||||
'%g/%g' % (epoch, epochs - 1),
|
'%g/%g' % (epoch, epochs - 1),
|
||||||
|
@ -154,8 +159,8 @@ def train(
|
||||||
|
|
||||||
# Multi-Scale training (320 - 608 pixels) every 10 batches
|
# Multi-Scale training (320 - 608 pixels) every 10 batches
|
||||||
if multi_scale and (i + 1) % 10 == 0:
|
if multi_scale and (i + 1) % 10 == 0:
|
||||||
dataloader.img_size = random.choice(range(10, 20)) * 32
|
dataset.img_size = random.choice(range(10, 20)) * 32
|
||||||
print('multi_scale img_size = %g' % dataloader.img_size)
|
print('multi_scale img_size = %g' % dataset.img_size)
|
||||||
|
|
||||||
# Update best loss
|
# Update best loss
|
||||||
if rloss['total'] < best_loss:
|
if rloss['total'] < best_loss:
|
||||||
|
@ -198,6 +203,7 @@ if __name__ == '__main__':
|
||||||
parser.add_argument('--multi-scale', action='store_true', help='random image sizes per batch 320 - 608')
|
parser.add_argument('--multi-scale', action='store_true', help='random image sizes per batch 320 - 608')
|
||||||
parser.add_argument('--img-size', type=int, default=32 * 13, help='pixels')
|
parser.add_argument('--img-size', type=int, default=32 * 13, help='pixels')
|
||||||
parser.add_argument('--resume', action='store_true', help='resume training flag')
|
parser.add_argument('--resume', action='store_true', help='resume training flag')
|
||||||
|
parser.add_argument('--num-workers', type=int, default=4, help='number of Pytorch DataLoader workers')
|
||||||
opt = parser.parse_args()
|
opt = parser.parse_args()
|
||||||
print(opt, end='\n\n')
|
print(opt, end='\n\n')
|
||||||
|
|
||||||
|
@ -212,4 +218,5 @@ if __name__ == '__main__':
|
||||||
batch_size=opt.batch_size,
|
batch_size=opt.batch_size,
|
||||||
accumulate=opt.accumulate,
|
accumulate=opt.accumulate,
|
||||||
multi_scale=opt.multi_scale,
|
multi_scale=opt.multi_scale,
|
||||||
|
num_workers=opt.num_workers
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,8 +6,8 @@ import random
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import torch
|
import torch
|
||||||
|
from torch.utils.data import Dataset
|
||||||
|
|
||||||
# from torch.utils.data import Dataset
|
|
||||||
from utils.utils import xyxy2xywh
|
from utils.utils import xyxy2xywh
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,147 +89,105 @@ class LoadWebcam: # for inference
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
class LoadImagesAndLabels: # for training
|
class LoadImagesAndLabels(Dataset): # for training/testing
|
||||||
def __init__(self, path, batch_size=1, img_size=608, augment=False):
|
def __init__(self, path, img_size=416, augment=False):
|
||||||
with open(path, 'r') as file:
|
with open(path, 'r') as file:
|
||||||
self.img_files = file.read().splitlines()
|
self.img_files = file.read().splitlines()
|
||||||
self.img_files = list(filter(lambda x: len(x) > 0, self.img_files))
|
self.img_files = list(filter(lambda x: len(x) > 0, self.img_files))
|
||||||
|
assert len(self.img_files) > 0, 'No images found in %s' % path
|
||||||
self.nF = len(self.img_files) # number of image files
|
self.img_size = img_size
|
||||||
self.nB = math.ceil(self.nF / batch_size) # number of batches
|
self.augment = augment
|
||||||
assert self.nF > 0, 'No images found in %s' % path
|
|
||||||
|
|
||||||
self.label_files = [x.replace('images', 'labels').replace('.png', '.txt').replace('.jpg', '.txt')
|
self.label_files = [x.replace('images', 'labels').replace('.png', '.txt').replace('.jpg', '.txt')
|
||||||
for x in self.img_files]
|
for x in self.img_files]
|
||||||
|
|
||||||
self.batch_size = batch_size
|
def __len__(self):
|
||||||
self.img_size = img_size
|
return len(self.img_files)
|
||||||
self.augment = augment
|
|
||||||
iter(self)
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
self.count = -1
|
|
||||||
self.shuffled_vector = np.random.permutation(self.nF) if self.augment else np.arange(self.nF)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
imgs, labels0, img_paths, img_shapes = self.load_images(index, index + 1)
|
img_path = self.img_files[index]
|
||||||
labels0[:,0] = index % self.batch_size
|
label_path = self.label_files[index]
|
||||||
|
|
||||||
labels = torch.zeros(100, 6)
|
img = cv2.imread(img_path) # BGR
|
||||||
labels[:min(len(labels0), 100)] = labels0 # max 100 labels per image
|
assert img is not None, 'File Not Found ' + img_path
|
||||||
return imgs.squeeze(0), labels, img_paths, img_shapes
|
|
||||||
|
|
||||||
def __next__(self):
|
augment_hsv = True
|
||||||
self.count += 1 # batches
|
if self.augment and augment_hsv:
|
||||||
if self.count >= self.nB:
|
# SV augmentation by 50%
|
||||||
raise StopIteration
|
fraction = 0.50
|
||||||
|
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
||||||
|
S = img_hsv[:, :, 1].astype(np.float32)
|
||||||
|
V = img_hsv[:, :, 2].astype(np.float32)
|
||||||
|
|
||||||
ia = self.count * self.batch_size # start index
|
a = (random.random() * 2 - 1) * fraction + 1
|
||||||
ib = min(ia + self.batch_size, self.nF) # end index
|
S *= a
|
||||||
|
if a > 1:
|
||||||
|
np.clip(S, a_min=0, a_max=255, out=S)
|
||||||
|
|
||||||
return self.load_images(ia, ib)
|
a = (random.random() * 2 - 1) * fraction + 1
|
||||||
|
V *= a
|
||||||
|
if a > 1:
|
||||||
|
np.clip(V, a_min=0, a_max=255, out=V)
|
||||||
|
|
||||||
def load_images(self, ia, ib):
|
img_hsv[:, :, 1] = S.astype(np.uint8)
|
||||||
img_all, labels_all, img_paths, img_shapes = [], [], [], []
|
img_hsv[:, :, 2] = V.astype(np.uint8)
|
||||||
for index, files_index in enumerate(range(ia, ib)):
|
cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR, dst=img)
|
||||||
img_path = self.img_files[self.shuffled_vector[files_index]]
|
|
||||||
label_path = self.label_files[self.shuffled_vector[files_index]]
|
|
||||||
|
|
||||||
img = cv2.imread(img_path) # BGR
|
h, w, _ = img.shape
|
||||||
assert img is not None, 'File Not Found ' + img_path
|
img, ratio, padw, padh = letterbox(img, height=self.img_size)
|
||||||
|
|
||||||
augment_hsv = True
|
# Load labels
|
||||||
if self.augment and augment_hsv:
|
if os.path.isfile(label_path):
|
||||||
# SV augmentation by 50%
|
with open(label_path, 'r') as file:
|
||||||
fraction = 0.50
|
lines = file.read().splitlines()
|
||||||
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
|
||||||
S = img_hsv[:, :, 1].astype(np.float32)
|
|
||||||
V = img_hsv[:, :, 2].astype(np.float32)
|
|
||||||
|
|
||||||
a = (random.random() * 2 - 1) * fraction + 1
|
x = np.array([x.split() for x in lines], dtype=np.float32)
|
||||||
S *= a
|
if x.size is 0:
|
||||||
if a > 1:
|
# Empty labels file
|
||||||
np.clip(S, a_min=0, a_max=255, out=S)
|
|
||||||
|
|
||||||
a = (random.random() * 2 - 1) * fraction + 1
|
|
||||||
V *= a
|
|
||||||
if a > 1:
|
|
||||||
np.clip(V, a_min=0, a_max=255, out=V)
|
|
||||||
|
|
||||||
img_hsv[:, :, 1] = S.astype(np.uint8)
|
|
||||||
img_hsv[:, :, 2] = V.astype(np.uint8)
|
|
||||||
cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR, dst=img)
|
|
||||||
|
|
||||||
h, w, _ = img.shape
|
|
||||||
img, ratio, padw, padh = letterbox(img, height=self.img_size)
|
|
||||||
|
|
||||||
# Load labels
|
|
||||||
if os.path.isfile(label_path):
|
|
||||||
# labels0 = np.loadtxt(label_path, dtype=np.float32).reshape(-1, 5) # SLOWER
|
|
||||||
with open(label_path, 'r') as file:
|
|
||||||
lines = file.read().splitlines()
|
|
||||||
labels0 = np.array([x.split() for x in lines], dtype=np.float32)
|
|
||||||
|
|
||||||
# Normalized xywh to pixel xyxy format
|
|
||||||
labels = labels0.copy()
|
|
||||||
labels[:, 1] = ratio * w * (labels0[:, 1] - labels0[:, 3] / 2) + padw
|
|
||||||
labels[:, 2] = ratio * h * (labels0[:, 2] - labels0[:, 4] / 2) + padh
|
|
||||||
labels[:, 3] = ratio * w * (labels0[:, 1] + labels0[:, 3] / 2) + padw
|
|
||||||
labels[:, 4] = ratio * h * (labels0[:, 2] + labels0[:, 4] / 2) + padh
|
|
||||||
else:
|
|
||||||
labels = np.array([])
|
labels = np.array([])
|
||||||
|
else:
|
||||||
|
# Normalized xywh to pixel xyxy format
|
||||||
|
labels = x.copy()
|
||||||
|
labels[:, 1] = ratio * w * (x[:, 1] - x[:, 3] / 2) + padw
|
||||||
|
labels[:, 2] = ratio * h * (x[:, 2] - x[:, 4] / 2) + padh
|
||||||
|
labels[:, 3] = ratio * w * (x[:, 1] + x[:, 3] / 2) + padw
|
||||||
|
labels[:, 4] = ratio * h * (x[:, 2] + x[:, 4] / 2) + padh
|
||||||
|
else:
|
||||||
|
labels = np.array([])
|
||||||
|
|
||||||
# Augment image and labels
|
# Augment image and labels
|
||||||
if self.augment:
|
if self.augment:
|
||||||
img, labels, M = random_affine(img, labels, degrees=(-5, 5), translate=(0.10, 0.10), scale=(0.90, 1.10))
|
img, labels, M = random_affine(img, labels, degrees=(-5, 5), translate=(0.10, 0.10), scale=(0.90, 1.10))
|
||||||
|
|
||||||
plotFlag = False
|
nL = len(labels)
|
||||||
if plotFlag:
|
if nL > 0:
|
||||||
import matplotlib.pyplot as plt
|
# convert xyxy to xywh
|
||||||
plt.figure(figsize=(10, 10)) if index == 0 else None
|
labels[:, 1:5] = xyxy2xywh(labels[:, 1:5]) / self.img_size
|
||||||
plt.subplot(4, 4, index + 1).imshow(img[:, :, ::-1])
|
|
||||||
plt.plot(labels[:, [1, 3, 3, 1, 1]].T, labels[:, [2, 2, 4, 4, 2]].T, '.-')
|
|
||||||
plt.axis('off')
|
|
||||||
|
|
||||||
nL = len(labels)
|
if self.augment:
|
||||||
if nL > 0:
|
# random left-right flip
|
||||||
# convert xyxy to xywh
|
lr_flip = True
|
||||||
labels[:, 1:5] = xyxy2xywh(labels[:, 1:5].copy()) / self.img_size
|
if lr_flip & (random.random() > 0.5):
|
||||||
|
img = np.fliplr(img)
|
||||||
|
if nL > 0:
|
||||||
|
labels[:, 1] = 1 - labels[:, 1]
|
||||||
|
|
||||||
if self.augment:
|
# random up-down flip
|
||||||
# random left-right flip
|
ud_flip = False
|
||||||
lr_flip = True
|
if ud_flip & (random.random() > 0.5):
|
||||||
if lr_flip & (random.random() > 0.5):
|
img = np.flipud(img)
|
||||||
img = np.fliplr(img)
|
if nL > 0:
|
||||||
if nL > 0:
|
labels[:, 2] = 1 - labels[:, 2]
|
||||||
labels[:, 1] = 1 - labels[:, 1]
|
|
||||||
|
|
||||||
# random up-down flip
|
labels_out = np.zeros((100, 6), dtype=np.float32)
|
||||||
ud_flip = False
|
if nL > 0:
|
||||||
if ud_flip & (random.random() > 0.5):
|
labels_out[:nL, 1:] = labels # max 100 labels per image
|
||||||
img = np.flipud(img)
|
|
||||||
if nL > 0:
|
|
||||||
labels[:, 2] = 1 - labels[:, 2]
|
|
||||||
|
|
||||||
if nL > 0:
|
|
||||||
labels = np.concatenate((np.zeros((nL, 1), dtype='float32') + index, labels), 1)
|
|
||||||
labels_all.append(labels)
|
|
||||||
|
|
||||||
img_all.append(img)
|
|
||||||
img_paths.append(img_path)
|
|
||||||
img_shapes.append((h, w))
|
|
||||||
|
|
||||||
# Normalize
|
# Normalize
|
||||||
img_all = np.stack(img_all)[:, :, :, ::-1].transpose(0, 3, 1, 2) # list to np.array and BGR to RGB
|
img = img[:, :, ::-1].transpose(2, 0, 1) # list to np.array and BGR to RGB
|
||||||
img_all = np.ascontiguousarray(img_all, dtype=np.float32) # uint8 to float32
|
img = np.ascontiguousarray(img, dtype=np.float32) # uint8 to float32
|
||||||
img_all /= 255.0 # 0 - 255 to 0.0 - 1.0
|
img /= 255.0 # 0 - 255 to 0.0 - 1.0
|
||||||
|
|
||||||
labels_all = torch.from_numpy(np.concatenate(labels_all, 0))
|
return torch.from_numpy(img), torch.from_numpy(labels_out), img_path, (h, w)
|
||||||
return torch.from_numpy(img_all), labels_all, img_paths, img_shapes
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return self.nB # number of batches
|
|
||||||
|
|
||||||
|
|
||||||
def letterbox(img, height=416, color=(127.5, 127.5, 127.5)): # resize a rectangular image to a padded square
|
def letterbox(img, height=416, color=(127.5, 127.5, 127.5)): # resize a rectangular image to a padded square
|
||||||
|
|
28
utils/gcp.sh
28
utils/gcp.sh
|
@ -6,39 +6,31 @@ bash yolov3/data/get_coco_dataset.sh
|
||||||
sudo rm -rf cocoapi && git clone https://github.com/cocodataset/cocoapi && cd cocoapi/PythonAPI && make && cd ../.. && cp -r cocoapi/PythonAPI/pycocotools yolov3
|
sudo rm -rf cocoapi && git clone https://github.com/cocodataset/cocoapi && cd cocoapi/PythonAPI && make && cd ../.. && cp -r cocoapi/PythonAPI/pycocotools yolov3
|
||||||
sudo shutdown
|
sudo shutdown
|
||||||
|
|
||||||
# Start
|
# Train
|
||||||
sudo rm -rf yolov3 && git clone https://github.com/ultralytics/yolov3
|
sudo rm -rf yolov3 && git clone https://github.com/ultralytics/yolov3
|
||||||
cp -r weights yolov3
|
cp -r weights yolov3
|
||||||
cd yolov3 && python3 train.py --batch-size 26
|
cd yolov3 && python3 train.py --batch-size 16 --epochs 1
|
||||||
|
sudo shutdown
|
||||||
|
|
||||||
# Resume
|
# Resume
|
||||||
python3 train.py --resume
|
python3 train.py --resume
|
||||||
|
|
||||||
# Detect
|
# Detect
|
||||||
gsutil cp gs://ultralytics/yolov3.pt yolov3/weights
|
|
||||||
python3 detect.py
|
python3 detect.py
|
||||||
|
|
||||||
# Clone branch
|
# Clone a branch
|
||||||
sudo rm -rf yolov3 && git clone -b multi_gpu --depth 1 https://github.com/ultralytics/yolov3
|
sudo rm -rf yolov3 && git clone -b multi_gpu --depth 1 https://github.com/ultralytics/yolov3
|
||||||
cd yolov3 && python3 train.py --batch-size 26
|
|
||||||
|
|
||||||
sudo rm -rf yolov3 && git clone -b multigpu --depth 1 https://github.com/alexpolichroniadis/yolov3
|
|
||||||
cp coco.data yolov3/cfg
|
|
||||||
cd yolov3 && python3 train.py --batch-size 26
|
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
sudo rm -rf yolov3 && git clone https://github.com/ultralytics/yolov3
|
sudo rm -rf yolov3 && git clone https://github.com/ultralytics/yolov3
|
||||||
sudo rm -rf cocoapi && git clone https://github.com/cocodataset/cocoapi && cd cocoapi/PythonAPI && make && cd ../.. && cp -r cocoapi/PythonAPI/pycocotools yolov3
|
sudo rm -rf cocoapi && git clone https://github.com/cocodataset/cocoapi && cd cocoapi/PythonAPI && make && cd ../.. && cp -r cocoapi/PythonAPI/pycocotools yolov3
|
||||||
cd yolov3 && python3 test.py --save-json --conf-thres 0.005
|
cd yolov3 && python3 test.py --save-json --conf-thres 0.005
|
||||||
|
|
||||||
# Test Darknet
|
# Test Darknet training
|
||||||
python3 test.py --img_size 416 --weights ../darknet/backup/yolov3.backup
|
python3 test.py --img_size 416 --weights ../darknet/backup/yolov3.backup
|
||||||
|
|
||||||
# Download and Resume
|
# Download with wget
|
||||||
sudo rm -rf yolov3 && git clone https://github.com/ultralytics/yolov3 && cd yolov3
|
|
||||||
wget https://storage.googleapis.com/ultralytics/yolov3.pt -O weights/latest.pt
|
wget https://storage.googleapis.com/ultralytics/yolov3.pt -O weights/latest.pt
|
||||||
python3 train.py --img_size 416 --batch_size 16 --epochs 1 --resume
|
|
||||||
python3 test.py --img_size 416 --weights weights/latest.pt --conf_thres 0.5
|
|
||||||
|
|
||||||
# Copy latest.pt to bucket
|
# Copy latest.pt to bucket
|
||||||
gsutil cp yolov3/weights/latest.pt gs://ultralytics
|
gsutil cp yolov3/weights/latest.pt gs://ultralytics
|
||||||
|
@ -47,8 +39,8 @@ gsutil cp yolov3/weights/latest.pt gs://ultralytics
|
||||||
gsutil cp gs://ultralytics/latest.pt yolov3/weights/latest.pt
|
gsutil cp gs://ultralytics/latest.pt yolov3/weights/latest.pt
|
||||||
wget https://storage.googleapis.com/ultralytics/latest.pt
|
wget https://storage.googleapis.com/ultralytics/latest.pt
|
||||||
|
|
||||||
# Testing
|
# Trade Studies
|
||||||
sudo rm -rf yolov3 && git clone https://github.com/ultralytics/yolov3 && cd yolov3
|
sudo rm -rf yolov3 && git clone https://github.com/ultralytics/yolov3
|
||||||
python3 train.py --epochs 3 --var 64
|
cp -r weights yolov3
|
||||||
|
cd yolov3 && python3 train.py --batch-size 16 --epochs 1
|
||||||
sudo shutdown
|
sudo shutdown
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,9 @@ from utils import torch_utils
|
||||||
torch.set_printoptions(linewidth=1320, precision=5, profile='long')
|
torch.set_printoptions(linewidth=1320, precision=5, profile='long')
|
||||||
np.set_printoptions(linewidth=320, formatter={'float_kind': '{:11.5g}'.format}) # format short g, %precision=5
|
np.set_printoptions(linewidth=320, formatter={'float_kind': '{:11.5g}'.format}) # format short g, %precision=5
|
||||||
|
|
||||||
|
# Prevent OpenCV from multithreading (to use PyTorch DataLoader)
|
||||||
|
cv2.setNumThreads(0)
|
||||||
|
|
||||||
|
|
||||||
def float3(x): # format floats to 3 decimals
|
def float3(x): # format floats to 3 decimals
|
||||||
return float(format(x, '.3f'))
|
return float(format(x, '.3f'))
|
||||||
|
@ -37,10 +40,10 @@ def model_info(model):
|
||||||
# Plots a line-by-line description of a PyTorch model
|
# Plots a line-by-line description of a PyTorch model
|
||||||
n_p = sum(x.numel() for x in model.parameters()) # number parameters
|
n_p = sum(x.numel() for x in model.parameters()) # number parameters
|
||||||
n_g = sum(x.numel() for x in model.parameters() if x.requires_grad) # number gradients
|
n_g = sum(x.numel() for x in model.parameters() if x.requires_grad) # number gradients
|
||||||
print('\n%5s %38s %9s %12s %20s %12s %12s' % ('layer', 'name', 'gradient', 'parameters', 'shape', 'mu', 'sigma'))
|
print('\n%5s %40s %9s %12s %20s %10s %10s' % ('layer', 'name', 'gradient', 'parameters', 'shape', 'mu', 'sigma'))
|
||||||
for i, (name, p) in enumerate(model.named_parameters()):
|
for i, (name, p) in enumerate(model.named_parameters()):
|
||||||
name = name.replace('module_list.', '')
|
name = name.replace('module_list.', '')
|
||||||
print('%5g %38s %9s %12g %20s %12.3g %12.3g' % (
|
print('%5g %40s %9s %12g %20s %10.3g %10.3g' % (
|
||||||
i, name, p.requires_grad, p.numel(), list(p.shape), p.mean(), p.std()))
|
i, name, p.requires_grad, p.numel(), list(p.shape), p.mean(), p.std()))
|
||||||
print('Model Summary: %g layers, %g parameters, %g gradients' % (i + 1, n_p, n_g))
|
print('Model Summary: %g layers, %g parameters, %g gradients' % (i + 1, n_p, n_g))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue