a b/HTNet/image-modality/train.py
1
import datetime
2
import os
3
import time
4
5
import torch
6
import torch.utils.data
7
from torch import nn
8
from torchvision import models, transforms
9
import utils
10
11
def train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, print_freq):
12
    model.train()
13
    metric_logger = utils.MetricLogger(delimiter="  ")
14
    metric_logger.add_meter('lr', utils.SmoothedValue(window_size=1, fmt='{value}'))
15
    metric_logger.add_meter('img/s', utils.SmoothedValue(window_size=10, fmt='{value}'))
16
17
    header = 'Epoch: [{}]'.format(epoch)
18
    for image, target in metric_logger.log_every(data_loader, print_freq, header):
19
        start_time = time.time()
20
        image, target = image.to(device), target.to(device)
21
        output = model(image)
22
        loss = criterion(output, target)
23
24
        optimizer.zero_grad()
25
        loss.backward()
26
        optimizer.step()
27
28
        acc1, acc5 = utils.accuracy(output, target, topk=(1, 2))
29
        batch_size = image.shape[0]
30
        metric_logger.update(loss=loss.item(), lr=optimizer.param_groups[0]["lr"])
31
        metric_logger.meters['acc1'].update(acc1.item(), n=batch_size)
32
        metric_logger.meters['acc2'].update(acc5.item(), n=batch_size)
33
        metric_logger.meters['img/s'].update(batch_size / (time.time() - start_time))
34
35
36
def evaluate(model, criterion, data_loader, device, print_freq=100):
37
    model.eval()
38
    metric_logger = utils.MetricLogger(delimiter="  ")
39
    header = 'Test:'
40
    with torch.no_grad():
41
        for image, target in metric_logger.log_every(data_loader, print_freq, header):
42
            image = image.to(device, non_blocking=True)
43
            target = target.to(device, non_blocking=True)
44
            output = model(image)
45
            loss = criterion(output, target)
46
47
            acc1, acc5 = utils.accuracy(output, target, topk=(1, 2))
48
            # FIXME need to take into account that the datasets
49
            # could have been padded in distributed setup
50
            batch_size = image.shape[0]
51
            metric_logger.update(loss=loss.item())
52
            metric_logger.meters['acc1'].update(acc1.item(), n=batch_size)
53
            metric_logger.meters['acc2'].update(acc5.item(), n=batch_size)
54
    # gather the stats from all processes
55
    metric_logger.synchronize_between_processes()
56
57
    print(' * Acc@1 {top1.global_avg:.3f} Acc@2 {top5.global_avg:.3f}'
58
          .format(top1=metric_logger.acc1, top5=metric_logger.acc2))
59
    return metric_logger.acc1.global_avg
60
61
62
def _get_cache_path(filepath):
63
    import hashlib
64
    h = hashlib.sha1(filepath.encode()).hexdigest()
65
    cache_path = os.path.join("~", ".torch", "vision", "datasets", "imagefolder", h[:10] + ".pt")
66
    cache_path = os.path.expanduser(cache_path)
67
    return cache_path
68
69
70
def load_data(traindir, valdir, cache_dataset, distributed):
71
    # Data loading code
72
    print("Loading data")
73
    normalize = transforms.Normalize(mean=[0.168, 0.174, 0.182],
74
                                     std =[0.159, 0.160, 0.162])
75
76
    print("Loading training data")
77
    st = time.time()
78
    cache_path = _get_cache_path(traindir)
79
    if cache_dataset and os.path.exists(cache_path):
80
        # Attention, as the transforms are also cached!
81
        print("Loading dataset_train from {}".format(cache_path))
82
        dataset, _ = torch.load(cache_path)
83
    else:
84
        dataset = utils.CSVDataset(
85
            traindir,
86
            transforms.Compose([
87
                transforms.RandomResizedCrop(224),
88
                transforms.RandomHorizontalFlip(),
89
                transforms.RandomRotation(degrees=180),
90
                transforms.ColorJitter(brightness=0.4, contrast=0.4, saturation=0.4, hue=0.0),
91
                transforms.ToTensor(),
92
                normalize,
93
            ]))
94
        if cache_dataset:
95
            print("Saving dataset_train to {}".format(cache_path))
96
            utils.mkdir(os.path.dirname(cache_path))
97
            utils.save_on_master((dataset, traindir), cache_path)
98
    print("Took", time.time() - st)
99
100
    print("Loading validation data")
101
    cache_path = _get_cache_path(valdir)
102
    if cache_dataset and os.path.exists(cache_path):
103
        # Attention, as the transforms are also cached!
104
        print("Loading dataset_test from {}".format(cache_path))
105
        dataset_test, _ = torch.load(cache_path)
106
    else:
107
        dataset_test = utils.CSVDataset(
108
            valdir,
109
            transforms.Compose([
110
                transforms.Resize(256),
111
                transforms.CenterCrop(224),
112
                transforms.ToTensor(),
113
                normalize,
114
            ]))
115
        if cache_dataset:
116
            print("Saving dataset_test to {}".format(cache_path))
117
            utils.mkdir(os.path.dirname(cache_path))
118
            utils.save_on_master((dataset_test, valdir), cache_path)
119
120
    print("Creating data loaders")
121
    if distributed:
122
        train_sampler = torch.utils.data.distributed.DistributedSampler(dataset)
123
        test_sampler = torch.utils.data.distributed.DistributedSampler(dataset_test)
124
    else:
125
        train_sampler = torch.utils.data.RandomSampler(dataset)
126
        test_sampler = torch.utils.data.SequentialSampler(dataset_test)
127
128
    return dataset, dataset_test, train_sampler, test_sampler
129
130
131
def main(args):
132
133
    if args.output_dir:
134
        utils.mkdir(args.output_dir)
135
136
    utils.init_distributed_mode(args)
137
    print(args)
138
139
    device = torch.device(args.device)
140
141
    torch.backends.cudnn.benchmark = True
142
143
    train_dir = args.train_file
144
    val_dir = args.val_file
145
    dataset, dataset_test, train_sampler, test_sampler = load_data(train_dir, val_dir,
146
                                                                   args.cache_dataset, args.distributed)
147
    data_loader = torch.utils.data.DataLoader(
148
        dataset, batch_size=args.batch_size,
149
        sampler=train_sampler, num_workers=args.workers, pin_memory=True)
150
151
    data_loader_test = torch.utils.data.DataLoader(
152
        dataset_test, batch_size=args.batch_size,
153
        sampler=test_sampler, num_workers=args.workers, pin_memory=True)
154
155
    print("Creating model")
156
    model = models.resnet152(num_classes=2)
157
    Ca_vs_control_checkpoint = "/media/storage1/project/deep_learning/ultrasound_tjmuch/research2.0/resnet152/model_89.pth"
158
    if os.path.exists(Ca_vs_control_checkpoint):
159
        checkpoint = torch.load(Ca_vs_control_checkpoint, map_location='cpu')
160
        model.load_state_dict(checkpoint['model'])
161
        
162
    model.to(device)
163
    if args.distributed and args.sync_bn:
164
        model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model)
165
166
    criterion = nn.CrossEntropyLoss()
167
168
    optimizer = torch.optim.SGD(
169
        model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.weight_decay)
170
171
    lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=args.lr_step_size, gamma=args.lr_gamma)
172
173
    model_without_ddp = model
174
    if args.distributed:
175
        model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu])
176
        model_without_ddp = model.module
177
178
    if args.resume:
179
        checkpoint = torch.load(args.resume, map_location='cpu')
180
        model_without_ddp.load_state_dict(checkpoint['model'])
181
        optimizer.load_state_dict(checkpoint['optimizer'])
182
        lr_scheduler.load_state_dict(checkpoint['lr_scheduler'])
183
        args.start_epoch = checkpoint['epoch'] + 1
184
185
    if args.test_only:
186
        evaluate(model, criterion, data_loader_test, device=device)
187
        return
188
189
    print("Start training")
190
    start_time = time.time()
191
    for epoch in range(args.start_epoch, args.epochs):
192
        if args.distributed:
193
            train_sampler.set_epoch(epoch)
194
        train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, args.print_freq)
195
        lr_scheduler.step()
196
        evaluate(model, criterion, data_loader_test, device=device)
197
        if args.output_dir:
198
            checkpoint = {
199
                'model': model_without_ddp.state_dict(),
200
                'optimizer': optimizer.state_dict(),
201
                'lr_scheduler': lr_scheduler.state_dict(),
202
                'epoch': epoch,
203
                'args': args}
204
            utils.save_on_master(
205
                checkpoint,
206
                os.path.join(args.output_dir, 'model_{}.pth'.format(epoch)))
207
            utils.save_on_master(
208
                checkpoint,
209
                os.path.join(args.output_dir, 'checkpoint.pth'))
210
211
    total_time = time.time() - start_time
212
    total_time_str = str(datetime.timedelta(seconds=int(total_time)))
213
    print('Training time {}'.format(total_time_str))
214
215
216
def parse_args():
217
    import argparse
218
    parser = argparse.ArgumentParser(description='PyTorch Classification Training')
219
220
    parser.add_argument('--train-file', help='training set')
221
    parser.add_argument('--val-file', help='validation set')
222
    parser.add_argument('--num-classes', help='number of classes for the objective task', type=int)
223
    parser.add_argument(
224
        "--focal-loss", help="Use focal loss",action="store_true")
225
    
226
    parser.add_argument('--device', default='cuda', help='device')
227
    parser.add_argument('-b', '--batch-size', default=32, type=int)
228
    parser.add_argument('--epochs', default=90, type=int, metavar='N',
229
                        help='number of total epochs to run')
230
    parser.add_argument('-j', '--workers', default=16, type=int, metavar='N',
231
                        help='number of data loading workers (default: 16)')
232
    parser.add_argument('--lr', default=0.1, type=float, help='initial learning rate')
233
    parser.add_argument('--momentum', default=0.9, type=float, metavar='M',
234
                        help='momentum')
235
    parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float,
236
                        metavar='W', help='weight decay (default: 1e-4)',
237
                        dest='weight_decay')
238
    parser.add_argument('--lr-step-size', default=30, type=int, help='decrease lr every step-size epochs')
239
    parser.add_argument('--lr-gamma', default=0.1, type=float, help='decrease lr by a factor of lr-gamma')
240
    parser.add_argument('--print-freq', default=10, type=int, help='print frequency')
241
    parser.add_argument('--output-dir', default='.', help='path where to save')
242
    parser.add_argument('--resume', default='', help='resume from checkpoint')
243
    parser.add_argument('--start-epoch', default=0, type=int, metavar='N',
244
                        help='start epoch')
245
    parser.add_argument(
246
        "--cache-dataset",
247
        dest="cache_dataset",
248
        help="Cache the datasets for quicker initialization. It also serializes the transforms",
249
        action="store_true",
250
    )
251
    parser.add_argument(
252
        "--sync-bn",
253
        dest="sync_bn",
254
        help="Use sync batch norm",
255
        action="store_true",
256
    )
257
    parser.add_argument(
258
        "--test-only",
259
        dest="test_only",
260
        help="Only test the model",
261
        action="store_true",
262
    )
263
    parser.add_argument(
264
        "--pretrained",
265
        dest="pretrained",
266
        help="Use pre-trained models from the modelzoo",
267
        action="store_true",
268
    )
269
270
    # distributed training parameters
271
    parser.add_argument('--world-size', default=1, type=int,
272
                        help='number of distributed processes')
273
    parser.add_argument('--dist-url', default='env://', help='url used to set up distributed training')
274
275
    args = parser.parse_args()
276
277
    return args
278
279
280
if __name__ == "__main__":
281
    args = parse_args()
282
    main(args)