chore(core): update launch backend to subprocess (#158)
Browse files- tools/train.py +3 -3
- yolox/core/launch.py +11 -9
- yolox/data/data_augment.py +11 -43
- yolox/data/datasets/mosaicdetection.py +1 -1
- yolox/utils/boxes.py +9 -1
- yolox/utils/metric.py +7 -0
- yolox/utils/setup_env.py +2 -0
tools/train.py
CHANGED
@@ -12,7 +12,7 @@ import torch.backends.cudnn as cudnn
|
|
12 |
|
13 |
from yolox.core import Trainer, launch
|
14 |
from yolox.exp import get_exp
|
15 |
-
from yolox.utils import configure_nccl
|
16 |
|
17 |
|
18 |
def make_parser():
|
@@ -106,8 +106,8 @@ if __name__ == "__main__":
|
|
106 |
exp = get_exp(args.exp_file, args.name)
|
107 |
exp.merge(args.opts)
|
108 |
|
109 |
-
num_gpu =
|
110 |
-
assert num_gpu <=
|
111 |
|
112 |
dist_url = "auto" if args.dist_url is None else args.dist_url
|
113 |
launch(
|
|
|
12 |
|
13 |
from yolox.core import Trainer, launch
|
14 |
from yolox.exp import get_exp
|
15 |
+
from yolox.utils import configure_nccl, get_num_devices
|
16 |
|
17 |
|
18 |
def make_parser():
|
|
|
106 |
exp = get_exp(args.exp_file, args.name)
|
107 |
exp.merge(args.opts)
|
108 |
|
109 |
+
num_gpu = get_num_devices() if args.devices is None else args.devices
|
110 |
+
assert num_gpu <= get_num_devices()
|
111 |
|
112 |
dist_url = "auto" if args.dist_url is None else args.dist_url
|
113 |
launch(
|
yolox/core/launch.py
CHANGED
@@ -55,15 +55,17 @@ def launch(
|
|
55 |
port = _find_free_port()
|
56 |
dist_url = f"tcp://127.0.0.1:{port}"
|
57 |
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
|
|
|
|
67 |
else:
|
68 |
main_func(*args)
|
69 |
|
|
|
55 |
port = _find_free_port()
|
56 |
dist_url = f"tcp://127.0.0.1:{port}"
|
57 |
|
58 |
+
processes = []
|
59 |
+
for rank in range(num_gpus_per_machine):
|
60 |
+
p = mp.Process(
|
61 |
+
target=_distributed_worker,
|
62 |
+
args=(
|
63 |
+
rank, main_func, world_size, num_gpus_per_machine,
|
64 |
+
machine_rank, backend, dist_url, args))
|
65 |
+
p.start()
|
66 |
+
processes.append(p)
|
67 |
+
for p in processes:
|
68 |
+
p.join()
|
69 |
else:
|
70 |
main_func(*args)
|
71 |
|
yolox/data/data_augment.py
CHANGED
@@ -4,9 +4,6 @@
|
|
4 |
"""
|
5 |
Data augmentation functionality. Passed as callable transformations to
|
6 |
Dataset classes.
|
7 |
-
|
8 |
-
The data augmentation procedures were interpreted from @weiliu89's SSD paper
|
9 |
-
http://arxiv.org/abs/1512.02325
|
10 |
"""
|
11 |
|
12 |
import math
|
@@ -17,6 +14,8 @@ import numpy as np
|
|
17 |
|
18 |
import torch
|
19 |
|
|
|
|
|
20 |
|
21 |
def augment_hsv(img, hgain=0.015, sgain=0.7, vgain=0.4):
|
22 |
r = np.random.uniform(-1, 1, 3) * [hgain, sgain, vgain] + 1 # random gains
|
@@ -197,20 +196,11 @@ class TrainTransform:
|
|
197 |
def __call__(self, image, targets, input_dim):
|
198 |
boxes = targets[:, :4].copy()
|
199 |
labels = targets[:, 4].copy()
|
200 |
-
if targets.shape[1] > 5:
|
201 |
-
mixup = True
|
202 |
-
ratios = targets[:, -1].copy()
|
203 |
-
ratios_o = targets[:, -1].copy()
|
204 |
-
else:
|
205 |
-
mixup = False
|
206 |
-
ratios = None
|
207 |
-
ratios_o = None
|
208 |
-
lshape = 6 if mixup else 5
|
209 |
if len(boxes) == 0:
|
210 |
-
targets = np.zeros((self.max_labels,
|
211 |
image, r_o = preproc(image, input_dim, self.means, self.std)
|
212 |
image = np.ascontiguousarray(image, dtype=np.float32)
|
213 |
-
return image, targets
|
214 |
|
215 |
image_o = image.copy()
|
216 |
targets_o = targets.copy()
|
@@ -218,58 +208,36 @@ class TrainTransform:
|
|
218 |
boxes_o = targets_o[:, :4]
|
219 |
labels_o = targets_o[:, 4]
|
220 |
# bbox_o: [xyxy] to [c_x,c_y,w,h]
|
221 |
-
|
222 |
-
b_y_o = (boxes_o[:, 3] + boxes_o[:, 1]) * 0.5
|
223 |
-
b_w_o = (boxes_o[:, 2] - boxes_o[:, 0]) * 1.0
|
224 |
-
b_h_o = (boxes_o[:, 3] - boxes_o[:, 1]) * 1.0
|
225 |
-
boxes_o[:, 0] = b_x_o
|
226 |
-
boxes_o[:, 1] = b_y_o
|
227 |
-
boxes_o[:, 2] = b_w_o
|
228 |
-
boxes_o[:, 3] = b_h_o
|
229 |
|
230 |
image_t = _distort(image)
|
231 |
image_t, boxes = _mirror(image_t, boxes)
|
232 |
height, width, _ = image_t.shape
|
233 |
image_t, r_ = preproc(image_t, input_dim, self.means, self.std)
|
234 |
-
boxes = boxes
|
235 |
# boxes [xyxy] 2 [cx,cy,w,h]
|
236 |
-
b_x = (boxes[:, 2] + boxes[:, 0]) * 0.5
|
237 |
-
b_y = (boxes[:, 3] + boxes[:, 1]) * 0.5
|
238 |
-
b_w = (boxes[:, 2] - boxes[:, 0]) * 1.0
|
239 |
-
b_h = (boxes[:, 3] - boxes[:, 1]) * 1.0
|
240 |
-
boxes[:, 0] = b_x
|
241 |
-
boxes[:, 1] = b_y
|
242 |
-
boxes[:, 2] = b_w
|
243 |
-
boxes[:, 3] = b_h
|
244 |
|
245 |
boxes *= r_
|
246 |
|
247 |
mask_b = np.minimum(boxes[:, 2], boxes[:, 3]) > 8
|
248 |
boxes_t = boxes[mask_b]
|
249 |
-
labels_t = labels[mask_b]
|
250 |
-
if mixup:
|
251 |
-
ratios_t = ratios[mask_b].copy()
|
252 |
|
253 |
if len(boxes_t) == 0:
|
254 |
image_t, r_o = preproc(image_o, input_dim, self.means, self.std)
|
255 |
boxes_o *= r_o
|
256 |
boxes_t = boxes_o
|
257 |
labels_t = labels_o
|
258 |
-
ratios_t = ratios_o
|
259 |
|
260 |
labels_t = np.expand_dims(labels_t, 1)
|
261 |
-
|
262 |
-
|
263 |
-
targets_t = np.hstack((labels_t, boxes_t, ratios_t))
|
264 |
-
else:
|
265 |
-
targets_t = np.hstack((labels_t, boxes_t))
|
266 |
-
padded_labels = np.zeros((self.max_labels, lshape))
|
267 |
padded_labels[range(len(targets_t))[: self.max_labels]] = targets_t[
|
268 |
: self.max_labels
|
269 |
]
|
270 |
padded_labels = np.ascontiguousarray(padded_labels, dtype=np.float32)
|
271 |
image_t = np.ascontiguousarray(image_t, dtype=np.float32)
|
272 |
-
return image_t, padded_labels
|
273 |
|
274 |
|
275 |
class ValTransform:
|
@@ -298,4 +266,4 @@ class ValTransform:
|
|
298 |
# assume input is cv2 img for now
|
299 |
def __call__(self, img, res, input_size):
|
300 |
img, _ = preproc(img, input_size, self.means, self.std, self.swap)
|
301 |
-
return torch.
|
|
|
4 |
"""
|
5 |
Data augmentation functionality. Passed as callable transformations to
|
6 |
Dataset classes.
|
|
|
|
|
|
|
7 |
"""
|
8 |
|
9 |
import math
|
|
|
14 |
|
15 |
import torch
|
16 |
|
17 |
+
from yolox.utils import xyxy2cxcywh
|
18 |
+
|
19 |
|
20 |
def augment_hsv(img, hgain=0.015, sgain=0.7, vgain=0.4):
|
21 |
r = np.random.uniform(-1, 1, 3) * [hgain, sgain, vgain] + 1 # random gains
|
|
|
196 |
def __call__(self, image, targets, input_dim):
|
197 |
boxes = targets[:, :4].copy()
|
198 |
labels = targets[:, 4].copy()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
199 |
if len(boxes) == 0:
|
200 |
+
targets = np.zeros((self.max_labels, 5), dtype=np.float32)
|
201 |
image, r_o = preproc(image, input_dim, self.means, self.std)
|
202 |
image = np.ascontiguousarray(image, dtype=np.float32)
|
203 |
+
return torch.as_tensor(image), torch.as_tensor(targets)
|
204 |
|
205 |
image_o = image.copy()
|
206 |
targets_o = targets.copy()
|
|
|
208 |
boxes_o = targets_o[:, :4]
|
209 |
labels_o = targets_o[:, 4]
|
210 |
# bbox_o: [xyxy] to [c_x,c_y,w,h]
|
211 |
+
boxes_o = xyxy2cxcywh(boxes_o)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
|
213 |
image_t = _distort(image)
|
214 |
image_t, boxes = _mirror(image_t, boxes)
|
215 |
height, width, _ = image_t.shape
|
216 |
image_t, r_ = preproc(image_t, input_dim, self.means, self.std)
|
217 |
+
boxes = xyxy2cxcywh(boxes)
|
218 |
# boxes [xyxy] 2 [cx,cy,w,h]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
219 |
|
220 |
boxes *= r_
|
221 |
|
222 |
mask_b = np.minimum(boxes[:, 2], boxes[:, 3]) > 8
|
223 |
boxes_t = boxes[mask_b]
|
224 |
+
labels_t = labels[mask_b]
|
|
|
|
|
225 |
|
226 |
if len(boxes_t) == 0:
|
227 |
image_t, r_o = preproc(image_o, input_dim, self.means, self.std)
|
228 |
boxes_o *= r_o
|
229 |
boxes_t = boxes_o
|
230 |
labels_t = labels_o
|
|
|
231 |
|
232 |
labels_t = np.expand_dims(labels_t, 1)
|
233 |
+
targets_t = np.hstack((labels_t, boxes_t))
|
234 |
+
padded_labels = np.zeros((self.max_labels, 5))
|
|
|
|
|
|
|
|
|
235 |
padded_labels[range(len(targets_t))[: self.max_labels]] = targets_t[
|
236 |
: self.max_labels
|
237 |
]
|
238 |
padded_labels = np.ascontiguousarray(padded_labels, dtype=np.float32)
|
239 |
image_t = np.ascontiguousarray(image_t, dtype=np.float32)
|
240 |
+
return torch.as_tensor(image_t), torch.as_tensor(padded_labels)
|
241 |
|
242 |
|
243 |
class ValTransform:
|
|
|
266 |
# assume input is cv2 img for now
|
267 |
def __call__(self, img, res, input_size):
|
268 |
img, _ = preproc(img, input_size, self.means, self.std, self.swap)
|
269 |
+
return torch.tensor_as(img), torch.zeros(1, 5)
|
yolox/data/datasets/mosaicdetection.py
CHANGED
@@ -220,4 +220,4 @@ class MosaicDetection(Dataset):
|
|
220 |
origin_img = origin_img.astype(np.float32)
|
221 |
origin_img = 0.5 * origin_img + 0.5 * padded_cropped_img.astype(np.float32)
|
222 |
|
223 |
-
return origin_img
|
|
|
220 |
origin_img = origin_img.astype(np.float32)
|
221 |
origin_img = 0.5 * origin_img + 0.5 * padded_cropped_img.astype(np.float32)
|
222 |
|
223 |
+
return origin_img, origin_labels
|
yolox/utils/boxes.py
CHANGED
@@ -9,7 +9,7 @@ import torchvision
|
|
9 |
|
10 |
__all__ = [
|
11 |
"filter_box", "postprocess", "bboxes_iou", "matrix_iou",
|
12 |
-
"adjust_box_anns", "xyxy2xywh",
|
13 |
]
|
14 |
|
15 |
|
@@ -113,3 +113,11 @@ def xyxy2xywh(bboxes):
|
|
113 |
bboxes[:, 2] = bboxes[:, 2] - bboxes[:, 0]
|
114 |
bboxes[:, 3] = bboxes[:, 3] - bboxes[:, 1]
|
115 |
return bboxes
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
|
10 |
__all__ = [
|
11 |
"filter_box", "postprocess", "bboxes_iou", "matrix_iou",
|
12 |
+
"adjust_box_anns", "xyxy2xywh", "xyxy2cxcywh",
|
13 |
]
|
14 |
|
15 |
|
|
|
113 |
bboxes[:, 2] = bboxes[:, 2] - bboxes[:, 0]
|
114 |
bboxes[:, 3] = bboxes[:, 3] - bboxes[:, 1]
|
115 |
return bboxes
|
116 |
+
|
117 |
+
|
118 |
+
def xyxy2cxcywh(bboxes):
|
119 |
+
bboxes[:, 2] = bboxes[:, 2] - bboxes[:, 0]
|
120 |
+
bboxes[:, 3] = bboxes[:, 3] - bboxes[:, 1]
|
121 |
+
bboxes[:, 0] = bboxes[:, 0] + bboxes[:, 2] * 0.5
|
122 |
+
bboxes[:, 1] = bboxes[:, 1] + bboxes[:, 3] * 0.5
|
123 |
+
return bboxes
|
yolox/utils/metric.py
CHANGED
@@ -13,12 +13,19 @@ import torch
|
|
13 |
__all__ = [
|
14 |
"AverageMeter",
|
15 |
"MeterBuffer",
|
|
|
16 |
"get_total_and_free_memory_in_Mb",
|
17 |
"occupy_mem",
|
18 |
"gpu_mem_usage",
|
19 |
]
|
20 |
|
21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
def get_total_and_free_memory_in_Mb(cuda_device):
|
23 |
devices_info_str = os.popen(
|
24 |
"nvidia-smi --query-gpu=memory.total,memory.used --format=csv,nounits,noheader"
|
|
|
13 |
__all__ = [
|
14 |
"AverageMeter",
|
15 |
"MeterBuffer",
|
16 |
+
"get_num_devices",
|
17 |
"get_total_and_free_memory_in_Mb",
|
18 |
"occupy_mem",
|
19 |
"gpu_mem_usage",
|
20 |
]
|
21 |
|
22 |
|
23 |
+
def get_num_devices():
|
24 |
+
devices_list_info = os.popen("nvidia-smi -L")
|
25 |
+
devices_list_info = devices_list_info.read().strip().split("\n")
|
26 |
+
return len(devices_list_info)
|
27 |
+
|
28 |
+
|
29 |
def get_total_and_free_memory_in_Mb(cuda_device):
|
30 |
devices_info_str = os.popen(
|
31 |
"nvidia-smi --query-gpu=memory.total,memory.used --format=csv,nounits,noheader"
|
yolox/utils/setup_env.py
CHANGED
@@ -48,3 +48,5 @@ def configure_module(ulimit_value=8192):
|
|
48 |
except Exception:
|
49 |
# cv2 version mismatch might rasie exceptions.
|
50 |
pass
|
|
|
|
|
|
48 |
except Exception:
|
49 |
# cv2 version mismatch might rasie exceptions.
|
50 |
pass
|
51 |
+
|
52 |
+
os.environ["OMP_NUM_THREADS"] = str(1)
|