%matplotlib inline
%reload_ext autoreload
%autoreload 2
import argparse
import os
import shutil
import time
from fastai.transforms import *
from fastai.dataset import *
from fastai.fp16 import *
from fastai.conv_learner import *
from pathlib import *
import torch
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.parallel
import torch.backends.cudnn as cudnn
import torch.distributed as dist
import torch.optim
import torch.utils.data
import torch.utils.data.distributed
import torchvision.transforms as transforms
import torchvision.datasets as datasets
import models
import models.cifar10 as cifar10models
from distributed import DistributedDataParallel as DDP
# print(models.cifar10.__dict__)
model_names = sorted(name for name in models.__dict__
if name.islower() and not name.startswith("__")
and callable(models.__dict__[name]))
cifar10_names = sorted(name for name in cifar10models.__dict__
if name.islower() and not name.startswith("__")
and callable(cifar10models.__dict__[name]))
model_names = cifar10_names + model_names
print(model_names)
# Example usage: python run_fastai.py /home/paperspace/ILSVRC/Data/CLS-LOC/ -a resnext_50_32x4d --epochs 1 -j 4 -b 64 --fp16
parser = argparse.ArgumentParser(description='PyTorch Cifar10 Training')
parser.add_argument('data', metavar='DIR',
help='path to dataset')
parser.add_argument('--save-dir', type=str, default=Path.home()/'imagenet_training',
help='Directory to save logs and models.')
parser.add_argument('--arch', '-a', metavar='ARCH', default='resnet56',
choices=model_names,
help='model architecture: ' +
' | '.join(model_names) +
' (default: resnet56)')
parser.add_argument('-j', '--workers', default=7, type=int, metavar='N',
help='number of data loading workers (default: 4)')
parser.add_argument('--epochs', default=1, type=int, metavar='N',
help='number of total epochs to run')
parser.add_argument('--cycle-len', default=95, type=float, metavar='N',
help='Length of cycle to run')
# parser.add_argument('--start-epoch', default=0, type=int, metavar='N',
# help='manual epoch number (useful on restarts)')
parser.add_argument('-b', '--batch-size', default=512, type=int,
metavar='N', help='mini-batch size (default: 256)')
parser.add_argument('--lr', '--learning-rate', default=0.8, type=float,
metavar='LR', help='initial learning rate')
parser.add_argument('--momentum', default=0.9, type=float, metavar='M', help='momentum')
parser.add_argument('--weight-decay', '--wd', default=1e-4, type=float,
metavar='W', help='weight decay (default: 1e-4)')
# parser.add_argument('--print-freq', '-p', default=10, type=int,
# metavar='N', help='print frequency (default: 10)')
# parser.add_argument('--resume', default='', type=str, metavar='PATH',
# help='path to latest checkpoint (default: none)')
# parser.add_argument('-e', '--evaluate', dest='evaluate', action='store_true',
# help='evaluate model on validation set')
parser.add_argument('--pretrained', dest='pretrained', action='store_true', help='use pre-trained model')
parser.add_argument('--fp16', action='store_true', help='Run model fp16 mode.')
parser.add_argument('--use-tta', default=True, type=bool, help='Validate model with TTA at the end of traiing.')
parser.add_argument('--train-half', action='store_true', help='Train model on half images. TODO: allow custom epochs and LR')
parser.add_argument('--sz', default=32, type=int, help='Size of transformed image.')
# parser.add_argument('--decay-int', default=30, type=int, help='Decay LR by 10 every decay-int epochs')
parser.add_argument('--use-clr', default='10,13.68,0.95,0.85', type=str,
help='div,pct,max_mom,min_mom. Pass in a string delimited by commas. Ex: "20,2,0.95,0.85"')
parser.add_argument('--loss-scale', type=float, default=128,
help='Loss scaling, positive power of 2 values can improve fp16 convergence.')
parser.add_argument('--prof', dest='prof', action='store_true', help='Only run a few iters for profiling.')
parser.add_argument('--dist-url', default='file://sync.file', type=str,
help='url used to set up distributed training')
parser.add_argument('--dist-backend', default='nccl', type=str, help='distributed backend')
parser.add_argument('--world-size', default=1, type=int,
help='Number of GPUs to use. Can either be manually set ' +
'or automatically set by using \'python -m multiproc\'.')
parser.add_argument('--rank', default=0, type=int,
help='Used for multi-process training. Can either be manually set ' +
'or automatically set by using \'python -m multiproc\'.')
class TorchModelData(ModelData):
def __init__(self, path, trn_dl, val_dl, aug_dl=None):
super().__init__(path, trn_dl, val_dl)
self.aug_dl = aug_dl
def torch_loader(data_path, size):
# Data loading code
traindir = os.path.join(data_path, 'train')
valdir = os.path.join(data_path, 'test')
normalize = transforms.Normalize(mean=[0.4914 , 0.48216, 0.44653], std=[0.24703, 0.24349, 0.26159])
scale_size = 40
padding = int((scale_size - size) / 2)
train_tfms = transforms.Compose([
transforms.RandomCrop(size, padding=padding),
transforms.ColorJitter(.25,.25,.25),
transforms.RandomRotation(2),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
normalize,
])
train_dataset = datasets.ImageFolder(traindir, train_tfms)
train_sampler = (torch.utils.data.distributed.DistributedSampler(train_dataset)
if args.distributed else None)
train_loader = torch.utils.data.DataLoader(
train_dataset, batch_size=args.batch_size, shuffle=(train_sampler is None),
num_workers=args.workers, pin_memory=True, sampler=train_sampler)
val_tfms = transforms.Compose([
# transforms.Resize(int(size*1.14)),
# transforms.CenterCrop(size),
transforms.ToTensor(),
normalize,
])
val_loader = torch.utils.data.DataLoader(
datasets.ImageFolder(valdir, val_tfms),
batch_size=args.batch_size, shuffle=False,
num_workers=args.workers, pin_memory=True)
aug_loader = torch.utils.data.DataLoader(
datasets.ImageFolder(valdir, train_tfms),
batch_size=args.batch_size, shuffle=False,
num_workers=args.workers, pin_memory=True)
train_loader = DataPrefetcher(train_loader)
val_loader = DataPrefetcher(val_loader)
aug_loader = DataPrefetcher(aug_loader)
if args.prof:
train_loader.stop_after = 200
val_loader.stop_after = 0
data = TorchModelData(data_path, train_loader, val_loader, aug_loader)
return data, train_sampler
# Seems to speed up training by ~2%
class DataPrefetcher():
def __init__(self, loader, stop_after=None):
self.loader = loader
self.dataset = loader.dataset
self.stream = torch.cuda.Stream()
self.stop_after = stop_after
self.next_input = None
self.next_target = None
def __len__(self):
return len(self.loader)
def preload(self):
try:
self.next_input, self.next_target = next(self.loaditer)
except StopIteration:
self.next_input = None
self.next_target = None
return
with torch.cuda.stream(self.stream):
self.next_input = self.next_input.cuda(async=True)
self.next_target = self.next_target.cuda(async=True)
def __iter__(self):
count = 0
self.loaditer = iter(self.loader)
self.preload()
while self.next_input is not None:
torch.cuda.current_stream().wait_stream(self.stream)
input = self.next_input
target = self.next_target
self.preload()
count += 1
yield input, target
if type(self.stop_after) is int and (count > self.stop_after):
break
def top5(output, target):
"""Computes the precision@k for the specified values of k"""
top5 = 5
batch_size = target.size(0)
_, pred = output.topk(top5, 1, True, True)
pred = pred.t()
correct = pred.eq(target.view(1, -1).expand_as(pred))
correct_k = correct[:top5].view(-1).float().sum(0, keepdim=True)
return correct_k.mul_(1.0 / batch_size)
class ImagenetLoggingCallback(Callback):
def __init__(self, save_path, print_every=50):
super().__init__()
self.save_path=save_path
self.print_every=print_every
def on_train_begin(self):
self.batch = 0
self.epoch = 0
self.f = open(self.save_path, "a", 1)
self.log("\ton_train_begin")
def on_epoch_end(self, metrics):
log_str = f'\tEpoch:{self.epoch}\ttrn_loss:{self.last_loss}'
for (k,v) in zip(['val_loss', 'acc', 'top5', ''], metrics): log_str += f'\t{k}:{v}'
self.log(log_str)
self.epoch += 1
def on_batch_end(self, metrics):
self.last_loss = metrics
self.batch += 1
if self.batch % self.print_every == 0:
self.log(f'Epoch: {self.epoch} Batch: {self.batch} Metrics: {metrics}')
def on_train_end(self):
self.log("\ton_train_end")
self.f.close()
def log(self, string):
self.f.write(time.strftime("%Y-%m-%dT%H:%M:%S")+"\t"+string+"\n")
class DisbleTransformCallback(Callback):
def __init__(self, dataset, disable_at=120):
super().__init__()
self.dataset = dataset
self.disable_at = disable_at
def on_epoch_end(self, metrics):
log_str = f'\tEpoch:{self.epoch}\ttrn_loss:{self.last_loss}'
for (k,v) in zip(['val_loss', 'acc', 'top5', ''], metrics): log_str += f'\t{k}:{v}'
self.log(log_str)
self.epoch += 1
print('Disabling dataset transforms')
if self.epoch > disable_at:
dataset.transform = None
# Logging + saving models
def save_args(name, save_dir):
if (args.rank != 0) or not args.save_dir: return {}
log_dir = f'{save_dir}/training_logs'
os.makedirs(log_dir, exist_ok=True)
return {
'best_save_name': f'{name}_best_model',
'cycle_save_name': f'{name}',
'callbacks': [
ImagenetLoggingCallback(f'{log_dir}/{name}_log.txt')
]
}
def save_sched(sched, save_dir):
if (args.rank != 0) or not args.save_dir: return {}
log_dir = f'{save_dir}/training_logs'
sched.save_path = log_dir
sched.plot_loss()
sched.plot_lr()
def update_model_dir(learner, base_dir):
learner.tmp_path = f'{base_dir}/tmp'
os.makedirs(learner.tmp_path, exist_ok=True)
learner.models_path = f'{base_dir}/models'
os.makedirs(learner.models_path, exist_ok=True)
['preact_resnet18', 'preact_resnet34', 'preact_resnet50', 'resnet56', 'resnext29_16_64', 'resnext29_8_64', 'dpn107', 'dpn131', 'dpn68', 'dpn92', 'dpn98', 'inceptionresnetv2', 'inceptionresnetv2_conc', 'inceptionv4', 'load', 'load_block17', 'load_block35', 'load_block8', 'load_conv2d', 'load_conv2d_nobn', 'load_linear', 'load_mixed_4a_7a', 'load_mixed_5', 'load_mixed_5b', 'load_mixed_6', 'load_mixed_6a', 'load_mixed_7', 'load_mixed_7a', 'nasnetalarge', 'pre_resnet101', 'pre_resnet152', 'pre_resnet18', 'pre_resnet34', 'pre_resnet50', 'reduce', 'resnet101', 'resnet152', 'resnet18', 'resnet34', 'resnet50', 'resnext101', 'resnext152', 'resnext18', 'resnext34', 'resnext50', 'resnext_101_32x4d', 'resnext_101_64x4d', 'resnext_50_32x4d', 'se_resnet_101', 'se_resnet_152', 'se_resnet_18', 'se_resnet_34', 'se_resnet_50', 'se_resnet_50_conc', 'se_resnext_101', 'se_resnext_152', 'se_resnext_50', 'test', 'test_block17', 'test_block35', 'test_block8', 'test_conv2d', 'test_conv2d_nobn', 'test_mixed_4a_7a', 'test_mixed_5b', 'test_mixed_6a', 'test_mixed_7a', 'wrn_50_2f']
args_input = [
'/home/paperspace/imagenet-fast/fp16/data/cifar10',
'--save-dir', '/home/paperspace/data/cifar_training/test1',
'-a', 'preact_resnet50',
# '-j', '6',
# '--prof',
'-b', '512',
# '--sz', '32',
# '--loss-scale', '128',
'--fp16',
'--cycle-len', '60',
# '--epochs', '1',
'--use-clr', '20,18,0.95,0.85',
'--wd', '2e-4',
'--lr', '1',
# '--train-half' # With fp16, iterations are so fast this doesn't matter
]
# This is important for speed
cudnn.benchmark = True
global arg
args = parser.parse_args(args_input); args
if args.cycle_len > 1: args.cycle_len = int(args.cycle_len)
args.distributed = args.world_size > 1
args.gpu = 0
if args.distributed:
args.gpu = args.rank % torch.cuda.device_count()
if args.distributed:
torch.cuda.set_device(args.gpu)
dist.init_process_group(backend=args.dist_backend, init_method=args.dist_url,
world_size=args.world_size)
if args.fp16:
assert torch.backends.cudnn.enabled, "fp16 mode requires cudnn backend to be enabled."
# create model
model = cifar10models.__dict__[args.arch] if args.arch in cifar10_names else models.__dict__[args.arch]
if args.pretrained:
print("=> using pre-trained model '{}'".format(args.arch))
model = model(pretrained=True)
else:
print("=> creating model '{}'".format(args.arch))
model = model()
=> creating model 'preact_resnet50'
model = model.cuda()
if args.distributed:
model = DDP(model)
if args.train_half:
data, train_sampler = torch_loader(args.data, 16)
else:
data, train_sampler = torch_loader(args.data, args.sz)
learner = Learner.from_model_data(model, data)
# learner.crit = F.nll_loss
learner.crit = F.cross_entropy
learner.metrics = [accuracy]
if args.fp16: learner.half()
if args.prof:
args.epochs = 1
args.cycle_len=.01
if args.use_clr:
args.use_clr = tuple(map(float, args.use_clr.split(',')))
# x,y = next(iter(data.trn_dl))
# plt.imshow(np.transpose(x[50], (1, 2, 0)))
# %pdb off
learner.lr_find()
HBox(children=(IntProgress(value=0, description='Epoch', max=1), HTML(value='')))
15%|█▌ | 15/98 [00:04<00:22, 3.64it/s, loss=2.39] 16%|█▋ | 16/98 [00:04<00:22, 3.68it/s, loss=2.39]
Exception in thread Thread-4:
Traceback (most recent call last):
File "/home/paperspace/anaconda3/envs/fastai/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/home/paperspace/anaconda3/envs/fastai/lib/python3.6/site-packages/tqdm/_monitor.py", line 62, in run
for instance in self.tqdm_cls._instances:
File "/home/paperspace/anaconda3/envs/fastai/lib/python3.6/_weakrefset.py", line 60, in __iter__
for itemref in self.data:
RuntimeError: Set changed size during iteration
88%|████████▊ | 86/98 [00:20<00:02, 4.29it/s, loss=134]
learner.sched.plot()
# 128x128
if args.train_half:
save_dir = args.save_dir+'/128'
update_model_dir(learner, save_dir)
sargs = save_args('first_run_128', save_dir)
sargs['callbacks'] += [DisableTransformCallback(data.train_ds, 5)]
learner.fit(args.lr,args.epochs, cycle_len=45,
train_sampler=train_sampler,
wds=args.weight_decay,
use_clr_beta=args.use_clr,
loss_scale=args.loss_scale,
**sargs
)
save_sched(learner.sched, save_dir)
data, train_sampler = torch(args.data, args.sz)
learner.set_data(data)
# Full size
update_model_dir(learner, args.save_dir)
sargs = save_args('first_run', args.save_dir)
learner.fit(args.lr,args.epochs, cycle_len=args.cycle_len,
sampler=train_sampler,
wds=args.weight_decay,
use_clr_beta=args.use_clr,
loss_scale=args.loss_scale,
**sargs
)
save_sched(learner.sched, args.save_dir)
print('Finished!')
HBox(children=(IntProgress(value=0, description='Epoch', max=60), HTML(value='')))
epoch trn_loss val_loss accuracy
0 2.245936 2.228234 0.23
1 1.891597 1.706828 0.3638
2 1.662194 1.476923 0.4567
3 1.500541 1.338445 0.5125
4 1.360604 1.233304 0.5539
5 1.208304 1.131711 0.5935
6 1.082064 1.069637 0.6337
7 0.98461 0.94652 0.6699
8 0.900918 0.832646 0.7004
9 0.819869 1.045434 0.6457
10 0.746519 0.916172 0.6738
11 0.718537 0.801661 0.7106
12 0.669545 0.627794 0.7818
13 0.618797 0.65864 0.7723
14 0.588434 0.787638 0.7475
15 0.573386 0.609908 0.7905
16 0.561982 0.763521 0.7464
17 0.54299 0.78705 0.7349
18 0.532402 0.743404 0.7559
19 0.523774 0.90625 0.7077
20 0.508664 0.79797 0.7334
21 0.506003 0.618367 0.7907
22 0.494013 0.745909 0.7576
23 0.487272 0.847763 0.7234
24 0.491039 0.593619 0.8019
25 0.507369 0.544064 0.8138
26 0.47543 0.647273 0.7837
27 0.456015 0.698345 0.7719
28 0.440883 0.551565 0.819
29 0.431023 0.577162 0.8039
30 0.426791 0.565634 0.8108
31 0.407529 0.719702 0.7728
32 0.400397 0.710503 0.7707
33 0.382763 0.581605 0.8079
34 0.378888 0.50685 0.8294
35 0.374004 0.694361 0.7902
36 0.364203 0.547592 0.823
37 0.348122 0.455088 0.8509
38 0.333729 0.442609 0.8461
39 0.3234 0.490248 0.8326
40 0.29991 0.426351 0.8576
41 0.289124 0.386449 0.8707
42 0.276744 0.440993 0.8525
43 0.265305 0.365772 0.8756
44 0.237126 0.331618 0.8928
45 0.214655 0.319781 0.8932
46 0.194987 0.300269 0.8999
47 0.154052 0.27897 0.909
48 0.126818 0.2409 0.9236
49 0.094009 0.233152 0.9247
50 0.080817 0.225963 0.9304
51 0.069746 0.240966 0.9255
52 0.06146 0.215223 0.9333
53 0.055725 0.214275 0.9355
54 0.049614 0.217713 0.9331
55 0.041751 0.214561 0.9371
56 0.036472 0.214109 0.9367
57 0.032184 0.211613 0.9391
58 0.028103 0.206728 0.9411
59 0.024435 0.208003 0.9406
Finished!
learner.save('cifar10-resnext-aug-preact')
learner.sched.plot()
learner.lr_find()
learner.sched.plot()
leaner.fig
learner.fit(1e-5,1, cycle_len=15,
wds=args.weight_decay,
loss_scale=args.loss_scale,
**sargs
)
if args.use_tta:
log_preds,y = learner.TTA()
preds = np.mean(np.exp(log_preds),0)
acc = accuracy(torch.FloatTensor(preds),torch.LongTensor(y))
print('TTA acc:', acc)
with open(args.save_dir+'/tta_accuracy.txt', "a", 1) as f:
f.write(time.strftime("%Y-%m-%dT%H:%M:%S")+f"\tTTA accuracty: {acc}\n")
TTA acc: 0.2077
if args.use_tta:
log_preds,y = learner.TTA()
preds = np.mean(np.exp(log_preds),0)
acc = accuracy(torch.FloatTensor(preds),torch.LongTensor(y))
print('TTA acc:', acc)
with open(args.save_dir+'/tta_accuracy.txt', "a", 1) as f:
f.write(time.strftime("%Y-%m-%dT%H:%M:%S")+f"\tTTA accuracty: {acc}\n")
TTA acc: 0.2029