train.py 9.84 KB
Newer Older
1
import _init_path
Shaoshuai Shi's avatar
Shaoshuai Shi committed
2
3
4
import argparse
import datetime
import glob
5
import os
Shaoshuai Shi's avatar
Shaoshuai Shi committed
6
7
8
from pathlib import Path
from test import repeat_eval_ckpt

9
10
11
import torch
import torch.nn as nn
from tensorboardX import SummaryWriter
Shaoshuai Shi's avatar
Shaoshuai Shi committed
12
13

from pcdet.config import cfg, cfg_from_list, cfg_from_yaml_file, log_config_to_file
14
15
from pcdet.datasets import build_dataloader
from pcdet.models import build_network, model_fn_decorator
Shaoshuai Shi's avatar
Shaoshuai Shi committed
16
from pcdet.utils import common_utils
17
18
19
20
from train_utils.optimization import build_optimizer, build_scheduler
from train_utils.train_utils import train_model


Gus-Guo's avatar
Gus-Guo committed
21
def parse_config():
22
23
24
    parser = argparse.ArgumentParser(description='arg parser')
    parser.add_argument('--cfg_file', type=str, default=None, help='specify the config for training')

25
26
    parser.add_argument('--batch_size', type=int, default=None, required=False, help='batch size for training')
    parser.add_argument('--epochs', type=int, default=None, required=False, help='number of epochs to train for')
27
    parser.add_argument('--workers', type=int, default=4, help='number of workers for dataloader')
28
29
30
31
32
33
34
35
36
37
    parser.add_argument('--extra_tag', type=str, default='default', help='extra tag for this experiment')
    parser.add_argument('--ckpt', type=str, default=None, help='checkpoint to start from')
    parser.add_argument('--pretrained_model', type=str, default=None, help='pretrained_model')
    parser.add_argument('--launcher', choices=['none', 'pytorch', 'slurm'], default='none')
    parser.add_argument('--tcp_port', type=int, default=18888, help='tcp port for distrbuted training')
    parser.add_argument('--sync_bn', action='store_true', default=False, help='whether to use sync bn')
    parser.add_argument('--fix_random_seed', action='store_true', default=False, help='')
    parser.add_argument('--ckpt_save_interval', type=int, default=1, help='number of training epochs')
    parser.add_argument('--local_rank', type=int, default=0, help='local rank for distributed training')
    parser.add_argument('--max_ckpt_save_num', type=int, default=30, help='max number of saved checkpoint')
38
    parser.add_argument('--merge_all_iters_to_one_epoch', action='store_true', default=False, help='')
39
40
41
42
43
    parser.add_argument('--set', dest='set_cfgs', default=None, nargs=argparse.REMAINDER,
                        help='set extra config keys if needed')

    parser.add_argument('--max_waiting_mins', type=int, default=0, help='max waiting minutes')
    parser.add_argument('--start_epoch', type=int, default=0, help='')
44
    parser.add_argument('--num_epochs_to_eval', type=int, default=0, help='number of checkpoints to be evaluated')
45
    parser.add_argument('--save_to_file', action='store_true', default=False, help='')
46
47
48
49
    
    parser.add_argument('--use_tqdm_to_record', action='store_true', default=False, help='if True, the intermediate losses will not be logged to file, only tqdm will be used')
    parser.add_argument('--logger_iter_interval', type=int, default=50, help='')
    parser.add_argument('--ckpt_save_time_interval', type=int, default=300, help='in terms of seconds')
50
    parser.add_argument('--wo_gpu_stat', action='store_true', help='')
jihan.yang's avatar
jihan.yang committed
51
    parser.add_argument('--use_amp', action='store_true', help='use mix precision training')
52
    
53
54
55
56
57
58

    args = parser.parse_args()

    cfg_from_yaml_file(args.cfg_file, cfg)
    cfg.TAG = Path(args.cfg_file).stem
    cfg.EXP_GROUP_PATH = '/'.join(args.cfg_file.split('/')[1:-1])  # remove 'cfgs' and 'xxxx.yaml'
jihan.yang's avatar
jihan.yang committed
59
60
    
    args.use_amp = args.use_amp or cfg.OPTIMIZATION.get('USE_AMP', False)
61
62
63
64
65
66
67
68

    if args.set_cfgs is not None:
        cfg_from_list(args.set_cfgs, cfg)

    return args, cfg


def main():
Gus-Guo's avatar
Gus-Guo committed
69
    args, cfg = parse_config()
70
71
    if args.launcher == 'none':
        dist_train = False
Shaoshuai Shi's avatar
Shaoshuai Shi committed
72
        total_gpus = 1
73
    else:
74
75
        total_gpus, cfg.LOCAL_RANK = getattr(common_utils, 'init_dist_%s' % args.launcher)(
            args.tcp_port, args.local_rank, backend='nccl'
76
77
        )
        dist_train = True
78

Shaoshuai Shi's avatar
Shaoshuai Shi committed
79
80
81
82
83
84
    if args.batch_size is None:
        args.batch_size = cfg.OPTIMIZATION.BATCH_SIZE_PER_GPU
    else:
        assert args.batch_size % total_gpus == 0, 'Batch size should match the number of gpus'
        args.batch_size = args.batch_size // total_gpus

85
86
    args.epochs = cfg.OPTIMIZATION.NUM_EPOCHS if args.epochs is None else args.epochs

87
    if args.fix_random_seed:
88
        common_utils.set_random_seed(666 + cfg.LOCAL_RANK)
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

    output_dir = cfg.ROOT_DIR / 'output' / cfg.EXP_GROUP_PATH / cfg.TAG / args.extra_tag
    ckpt_dir = output_dir / 'ckpt'
    output_dir.mkdir(parents=True, exist_ok=True)
    ckpt_dir.mkdir(parents=True, exist_ok=True)

    log_file = output_dir / ('log_train_%s.txt' % datetime.datetime.now().strftime('%Y%m%d-%H%M%S'))
    logger = common_utils.create_logger(log_file, rank=cfg.LOCAL_RANK)

    # log to file
    logger.info('**********************Start logging**********************')
    gpu_list = os.environ['CUDA_VISIBLE_DEVICES'] if 'CUDA_VISIBLE_DEVICES' in os.environ.keys() else 'ALL'
    logger.info('CUDA_VISIBLE_DEVICES=%s' % gpu_list)

    if dist_train:
        logger.info('total_batch_size: %d' % (total_gpus * args.batch_size))
    for key, val in vars(args).items():
        logger.info('{:16} {}'.format(key, val))
    log_config_to_file(cfg, logger=logger)
    if cfg.LOCAL_RANK == 0:
        os.system('cp %s %s' % (args.cfg_file, output_dir))

    tb_log = SummaryWriter(log_dir=str(output_dir / 'tensorboard')) if cfg.LOCAL_RANK == 0 else None

    # -----------------------create dataloader & network & optimizer---------------------------
    train_set, train_loader, train_sampler = build_dataloader(
        dataset_cfg=cfg.DATA_CONFIG,
        class_names=cfg.CLASS_NAMES,
        batch_size=args.batch_size,
        dist=dist_train, workers=args.workers,
        logger=logger,
120
121
        training=True,
        merge_all_iters_to_one_epoch=args.merge_all_iters_to_one_epoch,
122
123
        total_epochs=args.epochs,
        seed=666 if args.fix_random_seed else None
124
125
126
127
128
129
130
131
132
133
134
135
136
    )

    model = build_network(model_cfg=cfg.MODEL, num_class=len(cfg.CLASS_NAMES), dataset=train_set)
    if args.sync_bn:
        model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model)
    model.cuda()

    optimizer = build_optimizer(model, cfg.OPTIMIZATION)

    # load checkpoint if it is possible
    start_epoch = it = 0
    last_epoch = -1
    if args.pretrained_model is not None:
137
        model.load_params_from_file(filename=args.pretrained_model, to_cpu=dist_train, logger=logger)
138
139

    if args.ckpt is not None:
140
        it, start_epoch = model.load_params_with_optimizer(args.ckpt, to_cpu=dist_train, optimizer=optimizer, logger=logger)
141
142
        last_epoch = start_epoch + 1
    else:
143
144
        ckpt_list = glob.glob(str(ckpt_dir / '*.pth'))
              
145
146
        if len(ckpt_list) > 0:
            ckpt_list.sort(key=os.path.getmtime)
147
148
149
150
151
152
153
154
155
            while len(ckpt_list) > 0:
                try:
                    it, start_epoch = model.load_params_with_optimizer(
                        ckpt_list[-1], to_cpu=dist_train, optimizer=optimizer, logger=logger
                    )
                    last_epoch = start_epoch + 1
                    break
                except:
                    ckpt_list = ckpt_list[:-1]
156
157
158
159
160
161
162

    model.train()  # before wrap to DistributedDataParallel to support fixed some parameters
    if dist_train:
        model = nn.parallel.DistributedDataParallel(model, device_ids=[cfg.LOCAL_RANK % torch.cuda.device_count()])
    logger.info(model)

    lr_scheduler, lr_warmup_scheduler = build_scheduler(
163
        optimizer, total_iters_each_epoch=len(train_loader), total_epochs=args.epochs,
164
165
166
167
168
169
        last_epoch=last_epoch, optim_cfg=cfg.OPTIMIZATION
    )

    # -----------------------start training---------------------------
    logger.info('**********************Start training %s/%s(%s)**********************'
                % (cfg.EXP_GROUP_PATH, cfg.TAG, args.extra_tag))
170

171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
    train_model(
        model,
        optimizer,
        train_loader,
        model_func=model_fn_decorator(),
        lr_scheduler=lr_scheduler,
        optim_cfg=cfg.OPTIMIZATION,
        start_epoch=start_epoch,
        total_epochs=args.epochs,
        start_iter=it,
        rank=cfg.LOCAL_RANK,
        tb_log=tb_log,
        ckpt_save_dir=ckpt_dir,
        train_sampler=train_sampler,
        lr_warmup_scheduler=lr_warmup_scheduler,
        ckpt_save_interval=args.ckpt_save_interval,
        max_ckpt_save_num=args.max_ckpt_save_num,
188
189
190
191
        merge_all_iters_to_one_epoch=args.merge_all_iters_to_one_epoch, 
        logger=logger, 
        logger_iter_interval=args.logger_iter_interval,
        ckpt_save_time_interval=args.ckpt_save_time_interval,
192
        use_logger_to_record=not args.use_tqdm_to_record, 
jihan.yang's avatar
jihan.yang committed
193
194
        show_gpu_stat=not args.wo_gpu_stat,
        use_amp=args.use_amp
195
196
    )

197
198
199
    if hasattr(train_set, 'use_shared_memory') and train_set.use_shared_memory:
        train_set.clean_shared_memory()

200
201
202
    logger.info('**********************End training %s/%s(%s)**********************\n\n\n'
                % (cfg.EXP_GROUP_PATH, cfg.TAG, args.extra_tag))

203
204
205
206
207
208
209
210
211
212
    logger.info('**********************Start evaluation %s/%s(%s)**********************' %
                (cfg.EXP_GROUP_PATH, cfg.TAG, args.extra_tag))
    test_set, test_loader, sampler = build_dataloader(
        dataset_cfg=cfg.DATA_CONFIG,
        class_names=cfg.CLASS_NAMES,
        batch_size=args.batch_size,
        dist=dist_train, workers=args.workers, logger=logger, training=False
    )
    eval_output_dir = output_dir / 'eval' / 'eval_with_train'
    eval_output_dir.mkdir(parents=True, exist_ok=True)
213
    args.start_epoch = max(args.epochs - args.num_epochs_to_eval, 0)  # Only evaluate the last args.num_epochs_to_eval epochs
214
215
216
217
218
219
220
221
222

    repeat_eval_ckpt(
        model.module if dist_train else model,
        test_loader, args, eval_output_dir, logger, ckpt_dir,
        dist_test=dist_train
    )
    logger.info('**********************End evaluation %s/%s(%s)**********************' %
                (cfg.EXP_GROUP_PATH, cfg.TAG, args.extra_tag))

223
224
225

if __name__ == '__main__':
    main()