Repository: d-li14/face-attribute-prediction Branch: master Commit: 840e59aee39d Files: 25 Total size: 64.4 KB Directory structure: gitextract_wilfivay/ ├── .gitignore ├── LICENSE ├── README.md ├── celeba.py ├── main.py ├── models/ │ ├── __init__.py │ ├── mobilenetv1.py │ ├── mobilenetv2.py │ └── resnet.py └── utils/ ├── __init__.py ├── eval.py ├── logger.py ├── misc.py ├── progress/ │ ├── .gitignore │ ├── LICENSE │ ├── MANIFEST.in │ ├── README.rst │ ├── progress/ │ │ ├── __init__.py │ │ ├── bar.py │ │ ├── counter.py │ │ ├── helpers.py │ │ └── spinner.py │ ├── setup.py │ └── test_progress.py └── visualize.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ # checkpoints checkpoints/ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 Duo LI Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # face-attribute-prediction Face Attribute Prediction on [CelebA](http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html) benchmark with PyTorch Implemantation, heavily borrowed from [my MobileNetV2 implementation](https://github.com/d-li14/mobilenetv2.pytorch). ## Dependencies * Anaconda3 (Python 3.6+, with Numpy etc.) * PyTorch 0.4+ * tensorboard, tensorboardX ## Dataset [CelebA](http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html) dataset is a large-scale face dataset with attribute-based annotations. Cropped and aligned face regions are utilized as the training source. For the pre-processed data and specific split, please feel free to contact me: ## Features * Both ResNet and MobileNet as the backbone for scalability * Each of the 40 annotated attributes predicted with multi-head networks * Achieve ~92% average accuracy, comparative to state-of-the-art * Fast convergence (5~10 epochs) through finetuning the ImageNet pre-trained models ================================================ FILE: celeba.py ================================================ import torch import torch.utils.data as data from PIL import Image import os import os.path def pil_loader(path): # open path as file to avoid ResourceWarning (https://github.com/python-pillow/Pillow/issues/835) with open(path, 'rb') as f: img = Image.open(f) return img.convert('RGB') def accimage_loader(path): import accimage try: return accimage.Image(path) except IOError: # Potentially a decoding problem, fall back to PIL.Image return pil_loader(path) def default_loader(path): from torchvision import get_image_backend if get_image_backend() == 'accimage': return accimage_loader(path) else: return pil_loader(path) class CelebA(data.Dataset): def __init__(self, root, ann_file, transform=None, target_transform=None, loader=default_loader): images = [] targets = [] for line in open(os.path.join(root, ann_file), 'r'): sample = line.split() if len(sample) != 41: raise(RuntimeError("# Annotated face attributes of CelebA dataset should not be different from 40")) images.append(sample[0]) targets.append([int(i) for i in sample[1:]]) self.images = [os.path.join(root, 'img_align_celeba_png', img) for img in images] self.targets = targets self.transform = transform self.target_transform = target_transform self.loader = loader def __getitem__(self, index): path = self.images[index] sample = self.loader(path) target = self.targets[index] target = torch.LongTensor(target) if self.transform is not None: sample = self.transform(sample) if self.target_transform is not None: target = self.target_transform(target) return sample, target def __len__(self): return len(self.images) ================================================ FILE: main.py ================================================ ''' Training script for ImageNet Copyright (c) Wei YANG, 2017 ''' from __future__ import print_function import argparse import os import shutil import time import random import torch 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 from math import cos, pi from celeba import CelebA from utils import Bar, Logger, AverageMeter, accuracy, mkdir_p, savefig from tensorboardX import SummaryWriter model_names = sorted(name for name in models.__dict__ if name.islower() and not name.startswith("__") and callable(models.__dict__[name])) # Parse arguments parser = argparse.ArgumentParser(description='PyTorch ImageNet Training') parser.add_argument('-d', '--data', default='path to dataset', type=str) parser.add_argument('--arch', '-a', metavar='ARCH', default='resnet18', choices=model_names, help='model architecture: ' + ' | '.join(model_names) + ' (default: resnet18)') parser.add_argument('-j', '--workers', default=4, type=int, metavar='N', help='number of data loading workers (default: 4)') # Optimization options parser.add_argument('--epochs', default=90, type=int, metavar='N', help='number of total epochs to run') parser.add_argument('--start-epoch', default=0, type=int, metavar='N', help='manual epoch number (useful on restarts)') parser.add_argument('--train-batch', default=256, type=int, metavar='N', help='train batchsize (default: 256)') parser.add_argument('--test-batch', default=200, type=int, metavar='N', help='test batchsize (default: 200)') parser.add_argument('--lr', '--learning-rate', default=0.1, type=float, metavar='LR', help='initial learning rate') parser.add_argument('--lr-decay', type=str, default='step', help='mode for learning rate decay') parser.add_argument('--step', type=int, default=30, help='interval for learning rate decay in step mode') parser.add_argument('--schedule', type=int, nargs='+', default=[150, 225], help='decrease learning rate at these epochs.') parser.add_argument('--turning-point', type=int, default=100, help='epoch number from linear to exponential decay mode') parser.add_argument('--gamma', type=float, default=0.1, help='LR is multiplied by gamma on schedule.') 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)') # Checkpoints parser.add_argument('-c', '--checkpoint', default='checkpoints', type=str, metavar='PATH', help='path to save checkpoint (default: checkpoints)') parser.add_argument('--resume', default='', type=str, metavar='PATH', help='path to latest checkpoint (default: none)') # Architecture parser.add_argument('--cardinality', type=int, default=32, help='ResNeXt model cardinality (group).') parser.add_argument('--base-width', type=int, default=4, help='ResNeXt model base width (number of channels in each group).') parser.add_argument('--groups', type=int, default=3, help='ShuffleNet model groups') # Miscs parser.add_argument('--manual-seed', type=int, help='manual seed') 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('--world-size', default=1, type=int, help='number of distributed processes') parser.add_argument('--dist-url', default='tcp://224.66.41.62:23456', type=str, help='url used to set up distributed training') parser.add_argument('--dist-backend', default='gloo', type=str, help='distributed backend') # Device options parser.add_argument('--gpu-id', default='0', type=str, help='id(s) for CUDA_VISIBLE_DEVICES') best_prec1 = 0 def main(): global args, best_prec1 args = parser.parse_args() args.distributed = args.world_size > 1 if args.distributed: dist.init_process_group(backend=args.dist_backend, init_method=args.dist_url, world_size=args.world_size) # Use CUDA os.environ['CUDA_VISIBLE_DEVICES'] = args.gpu_id use_cuda = torch.cuda.is_available() # Random seed if args.manual_seed is None: args.manual_seed = random.randint(1, 10000) random.seed(args.manual_seed) torch.manual_seed(args.manual_seed) if use_cuda: torch.cuda.manual_seed_all(args.manual_seed) # create model if args.pretrained: print("=> using pre-trained model '{}'".format(args.arch)) model = models.__dict__[args.arch](pretrained=True) elif args.arch.startswith('resnext'): model = models.__dict__[args.arch]( baseWidth=args.base_width, cardinality=args.cardinality, ) elif args.arch.startswith('shufflenet'): model = models.__dict__[args.arch]( groups=args.groups ) else: print("=> creating model '{}'".format(args.arch)) model = models.__dict__[args.arch]() if not args.distributed: if args.arch.startswith('alexnet') or args.arch.startswith('vgg'): model.features = torch.nn.DataParallel(model.features) model.cuda() else: model = torch.nn.DataParallel(model).cuda() else: model.cuda() model = torch.nn.parallel.DistributedDataParallel(model) # define loss function (criterion) and optimizer criterion = nn.CrossEntropyLoss().cuda() optimizer = torch.optim.SGD(model.parameters(), args.lr, momentum=args.momentum, weight_decay=args.weight_decay) # optionally resume from a checkpoint title = 'CelebA-' + args.arch if not os.path.isdir(args.checkpoint): mkdir_p(args.checkpoint) if args.resume: if os.path.isfile(args.resume): print("=> loading checkpoint '{}'".format(args.resume)) checkpoint = torch.load(args.resume) args.start_epoch = checkpoint['epoch'] best_prec1 = checkpoint['best_prec1'] model.load_state_dict(checkpoint['state_dict']) optimizer.load_state_dict(checkpoint['optimizer']) print("=> loaded checkpoint '{}' (epoch {})" .format(args.resume, checkpoint['epoch'])) args.checkpoint = os.path.dirname(args.resume) logger = Logger(os.path.join(args.checkpoint, 'log.txt'), title=title, resume=True) else: print("=> no checkpoint found at '{}'".format(args.resume)) else: logger = Logger(os.path.join(args.checkpoint, 'log.txt'), title=title) logger.set_names(['Learning Rate', 'Train Loss', 'Valid Loss', 'Train Acc.', 'Valid Acc.']) cudnn.benchmark = True # Data loading code normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) train_dataset = CelebA( args.data, 'train_40_att_list.txt', transforms.Compose([ transforms.RandomHorizontalFlip(), transforms.ToTensor(), normalize, ])) if args.distributed: train_sampler = torch.utils.data.distributed.DistributedSampler(train_dataset) else: train_sampler = None train_loader = torch.utils.data.DataLoader( train_dataset, batch_size=args.train_batch, shuffle=(train_sampler is None), num_workers=args.workers, pin_memory=True, sampler=train_sampler) val_loader = torch.utils.data.DataLoader( CelebA(args.data, 'val_40_att_list.txt', transforms.Compose([ transforms.ToTensor(), normalize, ])), batch_size=args.test_batch, shuffle=False, num_workers=args.workers, pin_memory=True) test_loader = torch.utils.data.DataLoader( CelebA(args.data, 'test_40_att_list.txt', transforms.Compose([ transforms.ToTensor(), normalize, ])), batch_size=args.test_batch, shuffle=False, num_workers=args.workers, pin_memory=True) if args.evaluate: validate(test_loader, model, criterion) return # visualization writer = SummaryWriter(os.path.join(args.checkpoint, 'logs')) for epoch in range(args.start_epoch, args.epochs): if args.distributed: train_sampler.set_epoch(epoch) lr = adjust_learning_rate(optimizer, epoch) print('\nEpoch: [%d | %d] LR: %f' % (epoch + 1, args.epochs, lr)) # train for one epoch train_loss, train_acc = train(train_loader, model, criterion, optimizer, epoch) # evaluate on validation set val_loss, prec1 = validate(val_loader, model, criterion) # append logger file logger.append([lr, train_loss, val_loss, train_acc, prec1]) # tensorboardX writer.add_scalar('learning rate', lr, epoch + 1) writer.add_scalars('loss', {'train loss': train_loss, 'validation loss': val_loss}, epoch + 1) writer.add_scalars('accuracy', {'train accuracy': train_acc, 'validation accuracy': prec1}, epoch + 1) #for name, param in model.named_parameters(): # writer.add_histogram(name, param.clone().cpu().data.numpy(), epoch + 1) is_best = prec1 > best_prec1 best_prec1 = max(prec1, best_prec1) save_checkpoint({ 'epoch': epoch + 1, 'arch': args.arch, 'state_dict': model.state_dict(), 'best_prec1': best_prec1, 'optimizer' : optimizer.state_dict(), }, is_best, checkpoint=args.checkpoint) logger.close() logger.plot() savefig(os.path.join(args.checkpoint, 'log.eps')) writer.close() print('Best accuracy:') print(best_prec1) def train(train_loader, model, criterion, optimizer, epoch): bar = Bar('Processing', max=len(train_loader)) batch_time = AverageMeter() data_time = AverageMeter() losses = [AverageMeter() for _ in range(40)] top1 = [AverageMeter() for _ in range(40)] # switch to train mode model.train() end = time.time() for i, (input, target) in enumerate(train_loader): # measure data loading time data_time.update(time.time() - end) target = target.cuda(non_blocking=True) # compute output output = model(input) # measure accuracy and record loss loss = [] prec1 = [] for j in range(len(output)): loss.append(criterion(output[j], target[:, j])) prec1.append(accuracy(output[j], target[:, j], topk=(1,))) losses[j].update(loss[j].item(), input.size(0)) top1[j].update(prec1[j][0].item(), input.size(0)) losses_avg = [losses[k].avg for k in range(len(losses))] top1_avg = [top1[k].avg for k in range(len(top1))] loss_avg = sum(losses_avg) / len(losses_avg) prec1_avg = sum(top1_avg) / len(top1_avg) # compute gradient and do SGD step optimizer.zero_grad() loss_sum = sum(loss) loss_sum.backward() optimizer.step() # measure elapsed time batch_time.update(time.time() - end) end = time.time() # plot progress bar.suffix = '({batch}/{size}) Data: {data:.3f}s | Batch: {bt:.3f}s | Total: {total:} | ETA: {eta:} | Loss: {loss:.4f} | top1: {top1: .4f}'.format( batch=i + 1, size=len(train_loader), data=data_time.avg, bt=batch_time.avg, total=bar.elapsed_td, eta=bar.eta_td, loss=loss_avg, top1=prec1_avg, ) bar.next() bar.finish() return (loss_avg, prec1_avg) def validate(val_loader, model, criterion): bar = Bar('Processing', max=len(val_loader)) batch_time = AverageMeter() data_time = AverageMeter() losses = [AverageMeter() for _ in range(40)] top1 = [AverageMeter() for _ in range(40)] # switch to evaluate mode model.eval() with torch.no_grad(): end = time.time() for i, (input, target) in enumerate(val_loader): # measure data loading time data_time.update(time.time() - end) target = target.cuda(non_blocking=True) # compute output output = model(input) # measure accuracy and record loss loss = [] prec1 = [] for j in range(len(output)): loss.append(criterion(output[j], target[:, j])) prec1.append(accuracy(output[j], target[:, j], topk=(1,))) losses[j].update(loss[j].item(), input.size(0)) top1[j].update(prec1[j][0].item(), input.size(0)) losses_avg = [losses[k].avg for k in range(len(losses))] top1_avg = [top1[k].avg for k in range(len(top1))] loss_avg = sum(losses_avg) / len(losses_avg) prec1_avg = sum(top1_avg) / len(top1_avg) # measure elapsed time batch_time.update(time.time() - end) end = time.time() # plot progress bar.suffix = '({batch}/{size}) Data: {data:.3f}s | Batch: {bt:.3f}s | Total: {total:} | ETA: {eta:} | Loss: {loss:.4f} | top1: {top1: .4f}'.format( batch=i + 1, size=len(val_loader), data=data_time.avg, bt=batch_time.avg, total=bar.elapsed_td, eta=bar.eta_td, loss=loss_avg, top1=prec1_avg, ) bar.next() bar.finish() return (loss_avg, prec1_avg) def save_checkpoint(state, is_best, checkpoint='checkpoint', filename='checkpoint.pth.tar'): filepath = os.path.join(checkpoint, filename) torch.save(state, filepath) if is_best: shutil.copyfile(filepath, os.path.join(checkpoint, 'model_best.pth.tar')) def adjust_learning_rate(optimizer, epoch): lr = optimizer.param_groups[0]['lr'] """Sets the learning rate to the initial LR decayed by 10 following schedule""" if args.lr_decay == 'step': lr = args.lr * (args.gamma ** (epoch // args.step)) elif args.lr_decay == 'cos': lr = args.lr * (1 + cos(pi * epoch / args.epochs)) / 2 elif args.lr_decay == 'linear': lr = args.lr * (1 - epoch / args.epochs) elif args.lr_decay == 'linear2exp': if epoch < args.turning_point + 1: # learning rate decay as 95% at the turning point (1 / 95% = 1.0526) lr = args.lr * (1 - epoch / int(args.turning_point * 1.0526)) else: lr *= args.gamma elif args.lr_decay == 'schedule': if epoch in args.schedule: lr *= args.gamma else: raise ValueError('Unknown lr mode {}'.format(args.lr_decay)) for param_group in optimizer.param_groups: param_group['lr'] = lr return lr if __name__ == '__main__': main() ================================================ FILE: models/__init__.py ================================================ from __future__ import absolute_import from .resnet import * from .mobilenetv1 import * from .mobilenetv2 import * ================================================ FILE: models/mobilenetv1.py ================================================ """ Creates a MobileNet Model as defined in: Andrew G. H., Menglong Z., Bo C., Dmitry K., Weijun W., Tobias W., Marco A., Hartwig A. (2017). MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications arXiv preprint arXiv:1704.04861. import from https://github.com/marvis/pytorch-mobilenet """ import torch.nn as nn __all__ = ['mobilenetv1'] def conv_bn(inp, oup, stride): return nn.Sequential( nn.Conv2d(inp, oup, 3, stride, 1, bias=False), nn.BatchNorm2d(oup), nn.ReLU(inplace=True) ) def conv_dw(inp, oup, stride): return nn.Sequential( nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False), nn.BatchNorm2d(inp), nn.ReLU(inplace=True), nn.Conv2d(inp, oup, 1, 1, 0, bias=False), nn.BatchNorm2d(oup), nn.ReLU(inplace=True), ) class MobileNet(nn.Module): def __init__(self, num_classes=1000): super(MobileNet, self).__init__() self.features = nn.Sequential( conv_bn( 3, 32, 2), conv_dw( 32, 64, 1), conv_dw( 64, 128, 2), conv_dw(128, 128, 1), conv_dw(128, 256, 2), conv_dw(256, 256, 1), conv_dw(256, 512, 2), conv_dw(512, 512, 1), conv_dw(512, 512, 1), conv_dw(512, 512, 1), conv_dw(512, 512, 1), conv_dw(512, 512, 1), conv_dw(512, 1024, 2), conv_dw(1024, 1024, 1), nn.AvgPool2d(7), ) self.fc = nn.Linear(1024, num_classes) def forward(self, x): x = self.features(x) x = x.view(-1, 1024) x = self.fc(x) return x def mobilenetv1(**kwargs): """ Constructs a MobileNet V1 model """ return MobileNet(**kwargs) ================================================ FILE: models/mobilenetv2.py ================================================ """ Creates a MobileNetV2 Model as defined in: Mark Sandler, Andrew Howard, Menglong Zhu, Andrey Zhmoginov, Liang-Chieh Chen. (2018). MobileNetV2: Inverted Residuals and Linear Bottlenecks arXiv preprint arXiv:1801.04381. import from https://github.com/tonylins/pytorch-mobilenet-v2 """ import torch import torch.nn as nn import math import os.path __all__ = ['mobilenetv2'] def _make_divisible(v, divisor, min_value=None): """ This function is taken from the original tf repo. It ensures that all layers have a channel number that is divisible by 8 It can be seen here: https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py :param v: :param divisor: :param min_value: :return: """ if min_value is None: min_value = divisor new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) # Make sure that round down does not go down by more than 10%. if new_v < 0.9 * v: new_v += divisor return new_v def conv_3x3_bn(inp, oup, stride): return nn.Sequential( nn.Conv2d(inp, oup, 3, stride, 1, bias=False), nn.BatchNorm2d(oup), nn.ReLU6(inplace=True) ) def conv_1x1_bn(inp, oup): return nn.Sequential( nn.Conv2d(inp, oup, 1, 1, 0, bias=False), nn.BatchNorm2d(oup), nn.ReLU6(inplace=True) ) class InvertedResidual(nn.Module): def __init__(self, inp, oup, stride, expand_ratio): super(InvertedResidual, self).__init__() assert stride in [1, 2] hidden_dim = round(inp * expand_ratio) self.identity = stride == 1 and inp == oup if expand_ratio == 1: self.conv = nn.Sequential( # dw nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False), nn.BatchNorm2d(hidden_dim), nn.ReLU6(inplace=True), # pw-linear nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), nn.BatchNorm2d(oup), ) else: self.conv = nn.Sequential( # pw nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False), nn.BatchNorm2d(hidden_dim), nn.ReLU6(inplace=True), # dw nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False), nn.BatchNorm2d(hidden_dim), nn.ReLU6(inplace=True), # pw-linear nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), nn.BatchNorm2d(oup), ) def forward(self, x): if self.identity: return x + self.conv(x) else: return self.conv(x) class fc_block(nn.Module): def __init__(self, inplanes, planes, drop_rate=0.15): super(fc_block, self).__init__() self.fc = nn.Linear(inplanes, planes) self.bn = nn.BatchNorm1d(planes) if drop_rate > 0: self.dropout = nn.Dropout(drop_rate) self.relu = nn.ReLU(inplace=True) self.drop_rate = drop_rate def forward(self, x): x = self.fc(x) x = self.bn(x) if self.drop_rate > 0: x = self.dropout(x) x = self.relu(x) return x class MobileNetV2(nn.Module): def __init__(self, num_attributes=40, input_size=224, width_mult=1.): super(MobileNetV2, self).__init__() # setting of inverted residual blocks self.cfgs = [ # t, c, n, s [1, 16, 1, 1], [6, 24, 2, 2], [6, 32, 3, 2], [6, 64, 4, 2], [6, 96, 3, 1], [6, 160, 3, 2], [6, 320, 1, 1], ] # building first layer input_channel = _make_divisible(32 * width_mult, 8) layers = [conv_3x3_bn(3, input_channel, 2)] # building inverted residual blocks block = InvertedResidual for t, c, n, s in self.cfgs: output_channel = _make_divisible(c * width_mult, 8) layers.append(block(input_channel, output_channel, s, t)) input_channel = output_channel for i in range(1, n): layers.append(block(input_channel, output_channel, 1, t)) input_channel = output_channel self.features = nn.Sequential(*layers) # building last several layers output_channel = _make_divisible(1280 * width_mult, 8) if width_mult > 1.0 else 1280 self.conv = conv_1x1_bn(input_channel, output_channel) self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) self.stem = fc_block(output_channel, 512) for i in range(num_attributes): setattr(self, 'classifier' + str(i).zfill(2), nn.Sequential(fc_block(512, 256), nn.Linear(256, 2))) self.num_attributes = num_attributes self._initialize_weights() def forward(self, x): x = self.features(x) x = self.conv(x) x = self.avgpool(x) x = x.view(x.size(0), -1) x = self.stem(x) y = [] for i in range(self.num_attributes): classifier = getattr(self, 'classifier' + str(i).zfill(2)) y.append(classifier(x)) return y def _initialize_weights(self): for m in self.modules(): if isinstance(m, nn.Conv2d): n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels m.weight.data.normal_(0, math.sqrt(2. / n)) if m.bias is not None: m.bias.data.zero_() elif isinstance(m, nn.BatchNorm2d): m.weight.data.fill_(1) m.bias.data.zero_() elif isinstance(m, nn.Linear): n = m.weight.size(1) m.weight.data.normal_(0, 0.01) m.bias.data.zero_() def init_pretrained_weights(model, path=os.path.expanduser('~/.torch/models/mobilenetv2-0c6065bc.pth')): """ Initialize model with pretrained weights. Layers that don't match with pretrained layers in name or size are kept unchanged. """ #pretrain_dict = model_zoo.load_url(model_url) pretrain_dict = torch.load(path) model_dict = model.state_dict() pretrain_dict = {k: v for k, v in pretrain_dict.items() if k in model_dict and model_dict[k].size() == v.size()} model_dict.update(pretrain_dict) model.load_state_dict(model_dict) print("Initialized model with pretrained weights from {}".format(path)) def mobilenetv2(pretrained=True, **kwargs): """ Constructs a MobileNet V2 model """ model = MobileNetV2(**kwargs) if pretrained: init_pretrained_weights(model) return model ================================================ FILE: models/resnet.py ================================================ import torch.nn as nn import torch.utils.model_zoo as model_zoo __all__ = ['ResNet', 'resnet50'] model_urls = { 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', 'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth', 'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth', 'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth', 'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth', } def conv3x3(in_planes, out_planes, stride=1): """3x3 convolution with padding""" return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False) def conv1x1(in_planes, out_planes, stride=1): """1x1 convolution""" return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False) class BasicBlock(nn.Module): expansion = 1 def __init__(self, inplanes, planes, stride=1, downsample=None): super(BasicBlock, self).__init__() self.conv1 = conv3x3(inplanes, planes, stride) self.bn1 = nn.BatchNorm2d(planes) self.relu = nn.ReLU(inplace=True) self.conv2 = conv3x3(planes, planes) self.bn2 = nn.BatchNorm2d(planes) self.downsample = downsample self.stride = stride def forward(self, x): identity = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) if self.downsample is not None: identity = self.downsample(x) out += identity out = self.relu(out) return out class Bottleneck(nn.Module): expansion = 4 def __init__(self, inplanes, planes, stride=1, downsample=None): super(Bottleneck, self).__init__() self.conv1 = conv1x1(inplanes, planes) self.bn1 = nn.BatchNorm2d(planes) self.conv2 = conv3x3(planes, planes, stride) self.bn2 = nn.BatchNorm2d(planes) self.conv3 = conv1x1(planes, planes * self.expansion) self.bn3 = nn.BatchNorm2d(planes * self.expansion) self.relu = nn.ReLU(inplace=True) self.downsample = downsample self.stride = stride def forward(self, x): identity = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) out = self.relu(out) out = self.conv3(out) out = self.bn3(out) if self.downsample is not None: identity = self.downsample(x) out += identity out = self.relu(out) return out class fc_block(nn.Module): def __init__(self, inplanes, planes, drop_rate=0.15): super(fc_block, self).__init__() self.fc = nn.Linear(inplanes, planes) self.bn = nn.BatchNorm1d(planes) if drop_rate > 0: self.dropout = nn.Dropout(drop_rate) self.relu = nn.ReLU(inplace=True) self.drop_rate = drop_rate def forward(self, x): x = self.fc(x) x = self.bn(x) if self.drop_rate > 0: x = self.dropout(x) x = self.relu(x) return x class ResNet(nn.Module): def __init__(self, block, layers, num_attributes=40, zero_init_residual=False): super(ResNet, self).__init__() self.inplanes = 64 self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False) self.bn1 = nn.BatchNorm2d(64) self.relu = nn.ReLU(inplace=True) self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) self.layer1 = self._make_layer(block, 64, layers[0]) self.layer2 = self._make_layer(block, 128, layers[1], stride=2) self.layer3 = self._make_layer(block, 256, layers[2], stride=2) self.layer4 = self._make_layer(block, 512, layers[3], stride=2) self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) self.stem = fc_block(512 * block.expansion, 512) for i in range(num_attributes): setattr(self, 'classifier' + str(i).zfill(2), nn.Sequential(fc_block(512, 256), nn.Linear(256, 2))) self.num_attributes = num_attributes for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') elif isinstance(m, nn.BatchNorm2d): nn.init.constant_(m.weight, 1) nn.init.constant_(m.bias, 0) # Zero-initialize the last BN in each residual branch, # so that the residual branch starts with zeros, and each residual block behaves like an identity. # This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677 if zero_init_residual: for m in self.modules(): if isinstance(m, Bottleneck): nn.init.constant_(m.bn3.weight, 0) elif isinstance(m, BasicBlock): nn.init.constant_(m.bn2.weight, 0) def _make_layer(self, block, planes, blocks, stride=1): downsample = None if stride != 1 or self.inplanes != planes * block.expansion: downsample = nn.Sequential( conv1x1(self.inplanes, planes * block.expansion, stride), nn.BatchNorm2d(planes * block.expansion), ) layers = [] layers.append(block(self.inplanes, planes, stride, downsample)) self.inplanes = planes * block.expansion for _ in range(1, blocks): layers.append(block(self.inplanes, planes)) return nn.Sequential(*layers) def forward(self, x): x = self.conv1(x) x = self.bn1(x) x = self.relu(x) x = self.maxpool(x) x = self.layer1(x) x = self.layer2(x) x = self.layer3(x) x = self.layer4(x) x = self.avgpool(x) x = x.view(x.size(0), -1) x = self.stem(x) y = [] for i in range(self.num_attributes): classifier = getattr(self, 'classifier' + str(i).zfill(2)) y.append(classifier(x)) return y def resnet50(pretrained=True, **kwargs): """Constructs a ResNet-50 model. Args: pretrained (bool): If True, returns a model pre-trained on ImageNet """ model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs) if pretrained: init_pretrained_weights(model, model_urls['resnet50']) return model def init_pretrained_weights(model, model_url): """ Initialize model with pretrained weights. Layers that don't match with pretrained layers in name or size are kept unchanged. """ pretrain_dict = model_zoo.load_url(model_url) model_dict = model.state_dict() pretrain_dict = {k: v for k, v in pretrain_dict.items() if k in model_dict and model_dict[k].size() == v.size()} model_dict.update(pretrain_dict) model.load_state_dict(model_dict) print("Initialized model with pretrained weights from {}".format(model_url)) ================================================ FILE: utils/__init__.py ================================================ """Useful utils """ from .misc import * from .logger import * from .visualize import * from .eval import * # progress bar import os, sys sys.path.append(os.path.join(os.path.dirname(__file__), "progress")) from progress.bar import Bar as Bar ================================================ FILE: utils/eval.py ================================================ from __future__ import print_function, absolute_import import torch __all__ = ['accuracy'] def accuracy(output, target, topk=(1,)): """Computes the precision@k for the specified values of k""" with torch.no_grad(): maxk = max(topk) batch_size = target.size(0) _, pred = output.topk(maxk, 1, True, True) pred = pred.t() correct = pred.eq(target.view(1, -1).expand_as(pred)) res = [] for k in topk: correct_k = correct[:k].view(-1).float().sum(0) res.append(correct_k.mul_(100.0 / batch_size)) return res ================================================ FILE: utils/logger.py ================================================ # A simple torch style logger # (C) Wei YANG 2017 from __future__ import absolute_import import matplotlib.pyplot as plt import os import sys import numpy as np __all__ = ['Logger', 'LoggerMonitor', 'savefig'] def savefig(fname, dpi=None): dpi = 150 if dpi == None else dpi plt.savefig(fname, dpi=dpi) def plot_overlap(logger, names=None): names = logger.names if names == None else names numbers = logger.numbers for _, name in enumerate(names): x = np.arange(len(numbers[name])) plt.plot(x, np.asarray(numbers[name])) return [logger.title + '(' + name + ')' for name in names] class Logger(object): '''Save training process to log file with simple plot function.''' def __init__(self, fpath, title=None, resume=False): self.file = None self.resume = resume self.title = '' if title == None else title if fpath is not None: if resume: self.file = open(fpath, 'r') name = self.file.readline() self.names = name.rstrip().split('\t') self.numbers = {} for _, name in enumerate(self.names): self.numbers[name] = [] for numbers in self.file: numbers = numbers.rstrip().split('\t') for i in range(0, len(numbers)): self.numbers[self.names[i]].append(numbers[i]) self.file.close() self.file = open(fpath, 'a') else: self.file = open(fpath, 'w') def set_names(self, names): if self.resume: pass # initialize numbers as empty list self.numbers = {} self.names = names for _, name in enumerate(self.names): self.file.write(name) self.file.write('\t') self.numbers[name] = [] self.file.write('\n') self.file.flush() def append(self, numbers): assert len(self.names) == len(numbers), 'Numbers do not match names' for index, num in enumerate(numbers): self.file.write("{0:.6f}".format(num)) self.file.write('\t') self.numbers[self.names[index]].append(num) self.file.write('\n') self.file.flush() def plot(self, names=None): names = self.names if names == None else names numbers = self.numbers for _, name in enumerate(names): x = np.arange(len(numbers[name])) plt.plot(x, np.asarray(numbers[name])) plt.legend([self.title + '(' + name + ')' for name in names]) plt.grid(True) def close(self): if self.file is not None: self.file.close() class LoggerMonitor(object): '''Load and visualize multiple logs.''' def __init__ (self, paths): '''paths is a distionary with {name:filepath} pair''' self.loggers = [] for title, path in paths.items(): logger = Logger(path, title=title, resume=True) self.loggers.append(logger) def plot(self, names=None): plt.figure() plt.subplot(121) legend_text = [] for logger in self.loggers: legend_text += plot_overlap(logger, names) plt.legend(legend_text, bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) plt.grid(True) if __name__ == '__main__': # # Example # logger = Logger('test.txt') # logger.set_names(['Train loss', 'Valid loss','Test loss']) # length = 100 # t = np.arange(length) # train_loss = np.exp(-t / 10.0) + np.random.rand(length) * 0.1 # valid_loss = np.exp(-t / 10.0) + np.random.rand(length) * 0.1 # test_loss = np.exp(-t / 10.0) + np.random.rand(length) * 0.1 # for i in range(0, length): # logger.append([train_loss[i], valid_loss[i], test_loss[i]]) # logger.plot() # Example: logger monitor paths = { 'resadvnet20':'/home/wyang/code/pytorch-classification/checkpoint/cifar10/resadvnet20/log.txt', 'resadvnet32':'/home/wyang/code/pytorch-classification/checkpoint/cifar10/resadvnet32/log.txt', 'resadvnet44':'/home/wyang/code/pytorch-classification/checkpoint/cifar10/resadvnet44/log.txt', } field = ['Valid Acc.'] monitor = LoggerMonitor(paths) monitor.plot(names=field) savefig('test.eps') ================================================ FILE: utils/misc.py ================================================ '''Some helper functions for PyTorch, including: - get_mean_and_std: calculate the mean and std value of dataset. - msr_init: net parameter initialization. - progress_bar: progress bar mimic xlua.progress. ''' import errno import os import sys import time import math import torch.nn as nn import torch.nn.init as init from torch.autograd import Variable __all__ = ['get_mean_and_std', 'init_params', 'mkdir_p', 'AverageMeter'] def get_mean_and_std(dataset): '''Compute the mean and std value of dataset.''' dataloader = trainloader = torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=True, num_workers=2) mean = torch.zeros(3) std = torch.zeros(3) print('==> Computing mean and std..') for inputs, targets in dataloader: for i in range(3): mean[i] += inputs[:,i,:,:].mean() std[i] += inputs[:,i,:,:].std() mean.div_(len(dataset)) std.div_(len(dataset)) return mean, std def init_params(net): '''Init layer parameters.''' for m in net.modules(): if isinstance(m, nn.Conv2d): init.kaiming_normal(m.weight, mode='fan_out') if m.bias: init.constant(m.bias, 0) elif isinstance(m, nn.BatchNorm2d): init.constant(m.weight, 1) init.constant(m.bias, 0) elif isinstance(m, nn.Linear): init.normal(m.weight, std=1e-3) if m.bias: init.constant(m.bias, 0) def mkdir_p(path): '''make dir if not exist''' try: os.makedirs(path) except OSError as exc: # Python >2.5 if exc.errno == errno.EEXIST and os.path.isdir(path): pass else: raise class AverageMeter(object): """Computes and stores the average and current value Imported from https://github.com/pytorch/examples/blob/master/imagenet/main.py#L247-L262 """ def __init__(self): self.reset() def reset(self): self.val = 0 self.avg = 0 self.sum = 0 self.count = 0 def update(self, val, n=1): self.val = val self.sum += val * n self.count += n self.avg = self.sum / self.count ================================================ FILE: utils/progress/.gitignore ================================================ *.pyc *.egg-info build/ dist/ ================================================ FILE: utils/progress/LICENSE ================================================ # Copyright (c) 2012 Giorgos Verigakis # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ================================================ FILE: utils/progress/MANIFEST.in ================================================ include README.rst LICENSE ================================================ FILE: utils/progress/README.rst ================================================ Easy progress reporting for Python ================================== |pypi| |demo| .. |pypi| image:: https://img.shields.io/pypi/v/progress.svg .. |demo| image:: https://raw.github.com/verigak/progress/master/demo.gif :alt: Demo Bars ---- There are 7 progress bars to choose from: - ``Bar`` - ``ChargingBar`` - ``FillingSquaresBar`` - ``FillingCirclesBar`` - ``IncrementalBar`` - ``PixelBar`` - ``ShadyBar`` To use them, just call ``next`` to advance and ``finish`` to finish: .. code-block:: python from progress.bar import Bar bar = Bar('Processing', max=20) for i in range(20): # Do some work bar.next() bar.finish() The result will be a bar like the following: :: Processing |############# | 42/100 To simplify the common case where the work is done in an iterator, you can use the ``iter`` method: .. code-block:: python for i in Bar('Processing').iter(it): # Do some work Progress bars are very customizable, you can change their width, their fill character, their suffix and more: .. code-block:: python bar = Bar('Loading', fill='@', suffix='%(percent)d%%') This will produce a bar like the following: :: Loading |@@@@@@@@@@@@@ | 42% You can use a number of template arguments in ``message`` and ``suffix``: ========== ================================ Name Value ========== ================================ index current value max maximum value remaining max - index progress index / max percent progress * 100 avg simple moving average time per item (in seconds) elapsed elapsed time in seconds elapsed_td elapsed as a timedelta (useful for printing as a string) eta avg * remaining eta_td eta as a timedelta (useful for printing as a string) ========== ================================ Instead of passing all configuration options on instatiation, you can create your custom subclass: .. code-block:: python class FancyBar(Bar): message = 'Loading' fill = '*' suffix = '%(percent).1f%% - %(eta)ds' You can also override any of the arguments or create your own: .. code-block:: python class SlowBar(Bar): suffix = '%(remaining_hours)d hours remaining' @property def remaining_hours(self): return self.eta // 3600 Spinners ======== For actions with an unknown number of steps you can use a spinner: .. code-block:: python from progress.spinner import Spinner spinner = Spinner('Loading ') while state != 'FINISHED': # Do some work spinner.next() There are 5 predefined spinners: - ``Spinner`` - ``PieSpinner`` - ``MoonSpinner`` - ``LineSpinner`` - ``PixelSpinner`` Other ===== There are a number of other classes available too, please check the source or subclass one of them to create your own. License ======= progress is licensed under ISC ================================================ FILE: utils/progress/progress/__init__.py ================================================ # Copyright (c) 2012 Giorgos Verigakis # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from __future__ import division from collections import deque from datetime import timedelta from math import ceil from sys import stderr from time import time __version__ = '1.3' class Infinite(object): file = stderr sma_window = 10 # Simple Moving Average window def __init__(self, *args, **kwargs): self.index = 0 self.start_ts = time() self.avg = 0 self._ts = self.start_ts self._xput = deque(maxlen=self.sma_window) for key, val in kwargs.items(): setattr(self, key, val) def __getitem__(self, key): if key.startswith('_'): return None return getattr(self, key, None) @property def elapsed(self): return int(time() - self.start_ts) @property def elapsed_td(self): return timedelta(seconds=self.elapsed) def update_avg(self, n, dt): if n > 0: self._xput.append(dt / n) self.avg = sum(self._xput) / len(self._xput) def update(self): pass def start(self): pass def finish(self): pass def next(self, n=1): now = time() dt = now - self._ts self.update_avg(n, dt) self._ts = now self.index = self.index + n self.update() def iter(self, it): try: for x in it: yield x self.next() finally: self.finish() class Progress(Infinite): def __init__(self, *args, **kwargs): super(Progress, self).__init__(*args, **kwargs) self.max = kwargs.get('max', 100) @property def eta(self): return int(ceil(self.avg * self.remaining)) @property def eta_td(self): return timedelta(seconds=self.eta) @property def percent(self): return self.progress * 100 @property def progress(self): return min(1, self.index / self.max) @property def remaining(self): return max(self.max - self.index, 0) def start(self): self.update() def goto(self, index): incr = index - self.index self.next(incr) def iter(self, it): try: self.max = len(it) except TypeError: pass try: for x in it: yield x self.next() finally: self.finish() ================================================ FILE: utils/progress/progress/bar.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) 2012 Giorgos Verigakis # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from __future__ import unicode_literals from . import Progress from .helpers import WritelnMixin class Bar(WritelnMixin, Progress): width = 32 message = '' suffix = '%(index)d/%(max)d' bar_prefix = ' |' bar_suffix = '| ' empty_fill = ' ' fill = '#' hide_cursor = True def update(self): filled_length = int(self.width * self.progress) empty_length = self.width - filled_length message = self.message % self bar = self.fill * filled_length empty = self.empty_fill * empty_length suffix = self.suffix % self line = ''.join([message, self.bar_prefix, bar, empty, self.bar_suffix, suffix]) self.writeln(line) class ChargingBar(Bar): suffix = '%(percent)d%%' bar_prefix = ' ' bar_suffix = ' ' empty_fill = '∙' fill = '█' class FillingSquaresBar(ChargingBar): empty_fill = '▢' fill = '▣' class FillingCirclesBar(ChargingBar): empty_fill = '◯' fill = '◉' class IncrementalBar(Bar): phases = (' ', '▏', '▎', '▍', '▌', '▋', '▊', '▉', '█') def update(self): nphases = len(self.phases) filled_len = self.width * self.progress nfull = int(filled_len) # Number of full chars phase = int((filled_len - nfull) * nphases) # Phase of last char nempty = self.width - nfull # Number of empty chars message = self.message % self bar = self.phases[-1] * nfull current = self.phases[phase] if phase > 0 else '' empty = self.empty_fill * max(0, nempty - len(current)) suffix = self.suffix % self line = ''.join([message, self.bar_prefix, bar, current, empty, self.bar_suffix, suffix]) self.writeln(line) class PixelBar(IncrementalBar): phases = ('⡀', '⡄', '⡆', '⡇', '⣇', '⣧', '⣷', '⣿') class ShadyBar(IncrementalBar): phases = (' ', '░', '▒', '▓', '█') ================================================ FILE: utils/progress/progress/counter.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) 2012 Giorgos Verigakis # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from __future__ import unicode_literals from . import Infinite, Progress from .helpers import WriteMixin class Counter(WriteMixin, Infinite): message = '' hide_cursor = True def update(self): self.write(str(self.index)) class Countdown(WriteMixin, Progress): hide_cursor = True def update(self): self.write(str(self.remaining)) class Stack(WriteMixin, Progress): phases = (' ', '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█') hide_cursor = True def update(self): nphases = len(self.phases) i = min(nphases - 1, int(self.progress * nphases)) self.write(self.phases[i]) class Pie(Stack): phases = ('○', '◔', '◑', '◕', '●') ================================================ FILE: utils/progress/progress/helpers.py ================================================ # Copyright (c) 2012 Giorgos Verigakis # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from __future__ import print_function HIDE_CURSOR = '\x1b[?25l' SHOW_CURSOR = '\x1b[?25h' class WriteMixin(object): hide_cursor = False def __init__(self, message=None, **kwargs): super(WriteMixin, self).__init__(**kwargs) self._width = 0 if message: self.message = message if self.file.isatty(): if self.hide_cursor: print(HIDE_CURSOR, end='', file=self.file) print(self.message, end='', file=self.file) self.file.flush() def write(self, s): if self.file.isatty(): b = '\b' * self._width c = s.ljust(self._width) print(b + c, end='', file=self.file) self._width = max(self._width, len(s)) self.file.flush() def finish(self): if self.file.isatty() and self.hide_cursor: print(SHOW_CURSOR, end='', file=self.file) class WritelnMixin(object): hide_cursor = False def __init__(self, message=None, **kwargs): super(WritelnMixin, self).__init__(**kwargs) if message: self.message = message if self.file.isatty() and self.hide_cursor: print(HIDE_CURSOR, end='', file=self.file) def clearln(self): if self.file.isatty(): print('\r\x1b[K', end='', file=self.file) def writeln(self, line): if self.file.isatty(): self.clearln() print(line, end='', file=self.file) self.file.flush() def finish(self): if self.file.isatty(): print(file=self.file) if self.hide_cursor: print(SHOW_CURSOR, end='', file=self.file) from signal import signal, SIGINT from sys import exit class SigIntMixin(object): """Registers a signal handler that calls finish on SIGINT""" def __init__(self, *args, **kwargs): super(SigIntMixin, self).__init__(*args, **kwargs) signal(SIGINT, self._sigint_handler) def _sigint_handler(self, signum, frame): self.finish() exit(0) ================================================ FILE: utils/progress/progress/spinner.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) 2012 Giorgos Verigakis # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from __future__ import unicode_literals from . import Infinite from .helpers import WriteMixin class Spinner(WriteMixin, Infinite): message = '' phases = ('-', '\\', '|', '/') hide_cursor = True def update(self): i = self.index % len(self.phases) self.write(self.phases[i]) class PieSpinner(Spinner): phases = ['◷', '◶', '◵', '◴'] class MoonSpinner(Spinner): phases = ['◑', '◒', '◐', '◓'] class LineSpinner(Spinner): phases = ['⎺', '⎻', '⎼', '⎽', '⎼', '⎻'] class PixelSpinner(Spinner): phases = ['⣾','⣷', '⣯', '⣟', '⡿', '⢿', '⣻', '⣽'] ================================================ FILE: utils/progress/setup.py ================================================ #!/usr/bin/env python from setuptools import setup import progress setup( name='progress', version=progress.__version__, description='Easy to use progress bars', long_description=open('README.rst').read(), author='Giorgos Verigakis', author_email='verigak@gmail.com', url='http://github.com/verigak/progress/', license='ISC', packages=['progress'], classifiers=[ 'Environment :: Console', 'Intended Audience :: Developers', 'License :: OSI Approved :: ISC License (ISCL)', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', ] ) ================================================ FILE: utils/progress/test_progress.py ================================================ #!/usr/bin/env python from __future__ import print_function import random import time from progress.bar import (Bar, ChargingBar, FillingSquaresBar, FillingCirclesBar, IncrementalBar, PixelBar, ShadyBar) from progress.spinner import (Spinner, PieSpinner, MoonSpinner, LineSpinner, PixelSpinner) from progress.counter import Counter, Countdown, Stack, Pie def sleep(): t = 0.01 t += t * random.uniform(-0.1, 0.1) # Add some variance time.sleep(t) for bar_cls in (Bar, ChargingBar, FillingSquaresBar, FillingCirclesBar): suffix = '%(index)d/%(max)d [%(elapsed)d / %(eta)d / %(eta_td)s]' bar = bar_cls(bar_cls.__name__, suffix=suffix) for i in bar.iter(range(200)): sleep() for bar_cls in (IncrementalBar, PixelBar, ShadyBar): suffix = '%(percent)d%% [%(elapsed_td)s / %(eta)d / %(eta_td)s]' bar = bar_cls(bar_cls.__name__, suffix=suffix) for i in bar.iter(range(200)): sleep() for spin in (Spinner, PieSpinner, MoonSpinner, LineSpinner, PixelSpinner): for i in spin(spin.__name__ + ' ').iter(range(100)): sleep() print() for singleton in (Counter, Countdown, Stack, Pie): for i in singleton(singleton.__name__ + ' ').iter(range(100)): sleep() print() bar = IncrementalBar('Random', suffix='%(index)d') for i in range(100): bar.goto(random.randint(0, 100)) sleep() bar.finish() ================================================ FILE: utils/visualize.py ================================================ import matplotlib.pyplot as plt import torch import torch.nn as nn import torchvision import torchvision.transforms as transforms import numpy as np from .misc import * __all__ = ['make_image', 'show_batch', 'show_mask', 'show_mask_single'] # functions to show an image def make_image(img, mean=(0,0,0), std=(1,1,1)): for i in range(0, 3): img[i] = img[i] * std[i] + mean[i] # unnormalize npimg = img.numpy() return np.transpose(npimg, (1, 2, 0)) def gauss(x,a,b,c): return torch.exp(-torch.pow(torch.add(x,-b),2).div(2*c*c)).mul(a) def colorize(x): ''' Converts a one-channel grayscale image to a color heatmap image ''' if x.dim() == 2: torch.unsqueeze(x, 0, out=x) if x.dim() == 3: cl = torch.zeros([3, x.size(1), x.size(2)]) cl[0] = gauss(x,.5,.6,.2) + gauss(x,1,.8,.3) cl[1] = gauss(x,1,.5,.3) cl[2] = gauss(x,1,.2,.3) cl[cl.gt(1)] = 1 elif x.dim() == 4: cl = torch.zeros([x.size(0), 3, x.size(2), x.size(3)]) cl[:,0,:,:] = gauss(x,.5,.6,.2) + gauss(x,1,.8,.3) cl[:,1,:,:] = gauss(x,1,.5,.3) cl[:,2,:,:] = gauss(x,1,.2,.3) return cl def show_batch(images, Mean=(2, 2, 2), Std=(0.5,0.5,0.5)): images = make_image(torchvision.utils.make_grid(images), Mean, Std) plt.imshow(images) plt.show() def show_mask_single(images, mask, Mean=(2, 2, 2), Std=(0.5,0.5,0.5)): im_size = images.size(2) # save for adding mask im_data = images.clone() for i in range(0, 3): im_data[:,i,:,:] = im_data[:,i,:,:] * Std[i] + Mean[i] # unnormalize images = make_image(torchvision.utils.make_grid(images), Mean, Std) plt.subplot(2, 1, 1) plt.imshow(images) plt.axis('off') # for b in range(mask.size(0)): # mask[b] = (mask[b] - mask[b].min())/(mask[b].max() - mask[b].min()) mask_size = mask.size(2) # print('Max %f Min %f' % (mask.max(), mask.min())) mask = (upsampling(mask, scale_factor=im_size/mask_size)) # mask = colorize(upsampling(mask, scale_factor=im_size/mask_size)) # for c in range(3): # mask[:,c,:,:] = (mask[:,c,:,:] - Mean[c])/Std[c] # print(mask.size()) mask = make_image(torchvision.utils.make_grid(0.3*im_data+0.7*mask.expand_as(im_data))) # mask = make_image(torchvision.utils.make_grid(0.3*im_data+0.7*mask), Mean, Std) plt.subplot(2, 1, 2) plt.imshow(mask) plt.axis('off') def show_mask(images, masklist, Mean=(2, 2, 2), Std=(0.5,0.5,0.5)): im_size = images.size(2) # save for adding mask im_data = images.clone() for i in range(0, 3): im_data[:,i,:,:] = im_data[:,i,:,:] * Std[i] + Mean[i] # unnormalize images = make_image(torchvision.utils.make_grid(images), Mean, Std) plt.subplot(1+len(masklist), 1, 1) plt.imshow(images) plt.axis('off') for i in range(len(masklist)): mask = masklist[i].data.cpu() # for b in range(mask.size(0)): # mask[b] = (mask[b] - mask[b].min())/(mask[b].max() - mask[b].min()) mask_size = mask.size(2) # print('Max %f Min %f' % (mask.max(), mask.min())) mask = (upsampling(mask, scale_factor=im_size/mask_size)) # mask = colorize(upsampling(mask, scale_factor=im_size/mask_size)) # for c in range(3): # mask[:,c,:,:] = (mask[:,c,:,:] - Mean[c])/Std[c] # print(mask.size()) mask = make_image(torchvision.utils.make_grid(0.3*im_data+0.7*mask.expand_as(im_data))) # mask = make_image(torchvision.utils.make_grid(0.3*im_data+0.7*mask), Mean, Std) plt.subplot(1+len(masklist), 1, i+2) plt.imshow(mask) plt.axis('off') # x = torch.zeros(1, 3, 3) # out = colorize(x) # out_im = make_image(out) # plt.imshow(out_im) # plt.show()