SWHL commited on
Commit
78a233c
·
1 Parent(s): 6855814

Add custom training sample code and dataset (#256)

Browse files

* add train custom dataset code
* update custom docs
* fix(evaluator): fix n_samples=0 bug for the len of dataloader is 1

docs/train_custom_data.md CHANGED
@@ -35,6 +35,8 @@ ln -s /path/to/your/VOCdevkit ./datasets/VOCdevkit
35
  ```
36
  * The path "VOCdevkit" will be used in your exp file described in next section. Specifically, in `get_data_loader` and `get_eval_loader` function.
37
 
 
 
38
  ## 2. Create your Exp file to control everything
39
  We put everything involved in a model to one single Exp file, including model setting, training setting, and testing setting.
40
 
@@ -59,6 +61,8 @@ Besides, you should also overwrite the `dataset` and `evaluator`, prepared befor
59
 
60
  Please see [get_data_loader](../exps/example/yolox_voc/yolox_voc_s.py#L20), [get_eval_loader](../exps/example/yolox_voc/yolox_voc_s.py#L82), and [get_evaluator](../exps/example/yolox_voc/yolox_voc_s.py#L113) for more details.
61
 
 
 
62
  ## 3. Train
63
  Except special cases, we always recommend to use our [COCO pretrained weights](../README.md) for initializing the model.
64
 
@@ -72,6 +76,12 @@ or take the `YOLOX-S` VOC training for example:
72
  python tools/train.py -f exps/example/yolox_voc/yolox_voc_s.py -d 8 -b 64 --fp16 -o -c /path/to/yolox_s.pth.tar
73
  ```
74
 
 
 
 
 
 
 
75
  (Don't worry for the different shape of detection head between the pretrained weights and your own model, we will handle it)
76
 
77
  ## 4. Tips for Best Training Results
 
35
  ```
36
  * The path "VOCdevkit" will be used in your exp file described in next section. Specifically, in `get_data_loader` and `get_eval_loader` function.
37
 
38
+ ✧✧✧ You can download the mini-coco128 dataset by the [link](https://drive.google.com/file/d/16N3u36ycNd70m23IM7vMuRQXejAJY9Fs/view?usp=sharing), and then unzip it to the `datasets` directory. The dataset has been converted from YOLO format to COCO format, and can be used directly as a dataset for testing whether the train environment can be runned successfully.
39
+
40
  ## 2. Create your Exp file to control everything
41
  We put everything involved in a model to one single Exp file, including model setting, training setting, and testing setting.
42
 
 
61
 
62
  Please see [get_data_loader](../exps/example/yolox_voc/yolox_voc_s.py#L20), [get_eval_loader](../exps/example/yolox_voc/yolox_voc_s.py#L82), and [get_evaluator](../exps/example/yolox_voc/yolox_voc_s.py#L113) for more details.
63
 
64
+ ✧✧✧ You can also see the `exps/example/custom` directory for more details.
65
+
66
  ## 3. Train
67
  Except special cases, we always recommend to use our [COCO pretrained weights](../README.md) for initializing the model.
68
 
 
76
  python tools/train.py -f exps/example/yolox_voc/yolox_voc_s.py -d 8 -b 64 --fp16 -o -c /path/to/yolox_s.pth.tar
77
  ```
78
 
79
+ ✧✧✧ For example:
80
+ - If you download the [mini-coco128](https://drive.google.com/file/d/16N3u36ycNd70m23IM7vMuRQXejAJY9Fs/view?usp=sharing) and unzip it to the `datasets`, you can direct run the following training code.
81
+ ```bash
82
+ python tools/train.py -f exps/example/custom/yolox_s.py -d 8 -b 64 --fp16 -o -c /path/to/yolox_s.pth.tar
83
+ ```
84
+
85
  (Don't worry for the different shape of detection head between the pretrained weights and your own model, we will handle it)
86
 
87
  ## 4. Tips for Best Training Results
exps/example/custom/nano.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding:utf-8 -*-
3
+ # Copyright (c) Megvii, Inc. and its affiliates.
4
+
5
+ import os
6
+ import torch.nn as nn
7
+
8
+ from yolox.exp import Exp as MyExp
9
+
10
+
11
+ class Exp(MyExp):
12
+ def __init__(self):
13
+ super(Exp, self).__init__()
14
+ self.depth = 0.33
15
+ self.width = 0.25
16
+ self.scale = (0.5, 1.5)
17
+ self.random_size = (10, 20)
18
+ self.test_size = (416, 416)
19
+ self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]
20
+ self.enable_mixup = False
21
+
22
+ # Define yourself dataset path
23
+ self.data_dir = "datasets/coco128"
24
+ self.train_ann = "instances_train2017.json"
25
+ self.val_ann = "instances_val2017.json"
26
+
27
+ self.num_classes = 71
28
+
29
+ def get_model(self, sublinear=False):
30
+
31
+ def init_yolo(M):
32
+ for m in M.modules():
33
+ if isinstance(m, nn.BatchNorm2d):
34
+ m.eps = 1e-3
35
+ m.momentum = 0.03
36
+ if "model" not in self.__dict__:
37
+ from yolox.models import YOLOX, YOLOPAFPN, YOLOXHead
38
+ in_channels = [256, 512, 1024]
39
+ # NANO model use depthwise = True, which is main difference.
40
+ backbone = YOLOPAFPN(self.depth, self.width, in_channels=in_channels, depthwise=True)
41
+ head = YOLOXHead(self.num_classes, self.width, in_channels=in_channels, depthwise=True)
42
+ self.model = YOLOX(backbone, head)
43
+
44
+ self.model.apply(init_yolo)
45
+ self.model.head.initialize_biases(1e-2)
46
+ return self.model
exps/example/custom/yolox_s.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding:utf-8 -*-
3
+ # Copyright (c) Megvii, Inc. and its affiliates.
4
+ import os
5
+ from pathlib import Path
6
+
7
+ from yolox.exp import Exp as MyExp
8
+
9
+
10
+ class Exp(MyExp):
11
+ def __init__(self):
12
+ super(Exp, self).__init__()
13
+ self.depth = 0.33
14
+ self.width = 0.50
15
+ self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]
16
+
17
+ # Define yourself dataset path
18
+ self.data_dir = "datasets/coco128"
19
+ self.train_ann = "instances_train2017.json"
20
+ self.val_ann = "instances_val2017.json"
21
+
22
+ self.num_classes = 71
23
+
24
+ self.max_epoch = 300
25
+ self.data_num_workers = 4
26
+ self.eval_interval = 1
yolox/evaluators/coco_evaluator.py CHANGED
@@ -83,7 +83,7 @@ class COCOEvaluator:
83
 
84
  inference_time = 0
85
  nms_time = 0
86
- n_samples = len(self.dataloader) - 1
87
 
88
  if trt_file is not None:
89
  from torch2trt import TRTModule
 
83
 
84
  inference_time = 0
85
  nms_time = 0
86
+ n_samples = max(len(self.dataloader) - 1, 1)
87
 
88
  if trt_file is not None:
89
  from torch2trt import TRTModule
yolox/exp/yolox_base.py CHANGED
@@ -2,17 +2,18 @@
2
  # -*- coding:utf-8 -*-
3
  # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4
 
 
 
 
5
  import torch
6
  import torch.distributed as dist
7
  import torch.nn as nn
8
 
9
- import os
10
- import random
11
-
12
  from .base_exp import BaseExp
13
 
14
 
15
  class Exp(BaseExp):
 
16
  def __init__(self):
17
  super().__init__()
18
 
@@ -26,6 +27,7 @@ class Exp(BaseExp):
26
  self.data_num_workers = 4
27
  self.input_size = (640, 640)
28
  self.random_size = (14, 26)
 
29
  self.train_ann = "instances_train2017.json"
30
  self.val_ann = "instances_val2017.json"
31
 
@@ -60,7 +62,7 @@ class Exp(BaseExp):
60
  self.nmsthre = 0.65
61
 
62
  def get_model(self):
63
- from yolox.models import YOLOPAFPN, YOLOX, YOLOXHead
64
 
65
  def init_yolo(M):
66
  for m in M.modules():
@@ -81,15 +83,15 @@ class Exp(BaseExp):
81
  def get_data_loader(self, batch_size, is_distributed, no_aug=False):
82
  from yolox.data import (
83
  COCODataset,
 
 
84
  DataLoader,
85
  InfiniteSampler,
86
  MosaicDetection,
87
- TrainTransform,
88
- YoloBatchSampler
89
  )
90
 
91
  dataset = COCODataset(
92
- data_dir=None,
93
  json_file=self.train_ann,
94
  img_size=self.input_size,
95
  preproc=TrainTransform(
@@ -121,7 +123,9 @@ class Exp(BaseExp):
121
  if is_distributed:
122
  batch_size = batch_size // dist.get_world_size()
123
 
124
- sampler = InfiniteSampler(len(self.dataset), seed=self.seed if self.seed else 0)
 
 
125
 
126
  batch_sampler = YoloBatchSampler(
127
  sampler=sampler,
@@ -141,7 +145,7 @@ class Exp(BaseExp):
141
  tensor = torch.LongTensor(2).cuda()
142
 
143
  if rank == 0:
144
- size_factor = self.input_size[1] * 1.0 / self.input_size[0]
145
  size = random.randint(*self.random_size)
146
  size = (int(32 * size), 32 * int(size * size_factor))
147
  tensor[0] = size[0]
@@ -186,7 +190,6 @@ class Exp(BaseExp):
186
 
187
  def get_lr_scheduler(self, lr, iters_per_epoch):
188
  from yolox.utils import LRScheduler
189
-
190
  scheduler = LRScheduler(
191
  self.scheduler,
192
  lr,
@@ -203,7 +206,7 @@ class Exp(BaseExp):
203
  from yolox.data import COCODataset, ValTransform
204
 
205
  valdataset = COCODataset(
206
- data_dir=None,
207
  json_file=self.val_ann if not testdev else "image_info_test-dev2017.json",
208
  name="val2017" if not testdev else "test2017",
209
  img_size=self.test_size,
 
2
  # -*- coding:utf-8 -*-
3
  # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4
 
5
+ import os
6
+ import random
7
+
8
  import torch
9
  import torch.distributed as dist
10
  import torch.nn as nn
11
 
 
 
 
12
  from .base_exp import BaseExp
13
 
14
 
15
  class Exp(BaseExp):
16
+
17
  def __init__(self):
18
  super().__init__()
19
 
 
27
  self.data_num_workers = 4
28
  self.input_size = (640, 640)
29
  self.random_size = (14, 26)
30
+ self.data_dir = None
31
  self.train_ann = "instances_train2017.json"
32
  self.val_ann = "instances_val2017.json"
33
 
 
62
  self.nmsthre = 0.65
63
 
64
  def get_model(self):
65
+ from yolox.models import YOLOX, YOLOPAFPN, YOLOXHead
66
 
67
  def init_yolo(M):
68
  for m in M.modules():
 
83
  def get_data_loader(self, batch_size, is_distributed, no_aug=False):
84
  from yolox.data import (
85
  COCODataset,
86
+ TrainTransform,
87
+ YoloBatchSampler,
88
  DataLoader,
89
  InfiniteSampler,
90
  MosaicDetection,
 
 
91
  )
92
 
93
  dataset = COCODataset(
94
+ data_dir=self.data_dir,
95
  json_file=self.train_ann,
96
  img_size=self.input_size,
97
  preproc=TrainTransform(
 
123
  if is_distributed:
124
  batch_size = batch_size // dist.get_world_size()
125
 
126
+ sampler = InfiniteSampler(
127
+ len(self.dataset), seed=self.seed if self.seed else 0
128
+ )
129
 
130
  batch_sampler = YoloBatchSampler(
131
  sampler=sampler,
 
145
  tensor = torch.LongTensor(2).cuda()
146
 
147
  if rank == 0:
148
+ size_factor = self.input_size[1] * 1. / self.input_size[0]
149
  size = random.randint(*self.random_size)
150
  size = (int(32 * size), 32 * int(size * size_factor))
151
  tensor[0] = size[0]
 
190
 
191
  def get_lr_scheduler(self, lr, iters_per_epoch):
192
  from yolox.utils import LRScheduler
 
193
  scheduler = LRScheduler(
194
  self.scheduler,
195
  lr,
 
206
  from yolox.data import COCODataset, ValTransform
207
 
208
  valdataset = COCODataset(
209
+ data_dir=self.data_dir,
210
  json_file=self.val_ann if not testdev else "image_info_test-dev2017.json",
211
  name="val2017" if not testdev else "test2017",
212
  img_size=self.test_size,