├── .gitignore ├── LICENSE ├── README.md ├── main.py ├── pretrainedmodels ├── __init__.py ├── imagenet │ ├── __init__.py │ ├── config.py │ ├── transforms.py │ └── utils.py ├── models │ ├── __init__.py │ └── resnet.py └── version.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | db.sqlite3 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # Jupyter Notebook 76 | .ipynb_checkpoints 77 | 78 | # pyenv 79 | .python-version 80 | 81 | # celery beat schedule file 82 | celerybeat-schedule 83 | 84 | # SageMath parsed files 85 | *.sage.py 86 | 87 | # Environments 88 | .env 89 | .venv 90 | env/ 91 | venv/ 92 | ENV/ 93 | env.bak/ 94 | venv.bak/ 95 | 96 | # Spyder project settings 97 | .spyderproject 98 | .spyproject 99 | 100 | # Rope project settings 101 | .ropeproject 102 | 103 | # mkdocs documentation 104 | /site 105 | 106 | # mypy 107 | .mypy_cache/ 108 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Sogou-MM 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pretrained CNN models for Pytorch (Work in progress) 2 | 3 | The goal of this repo is: 4 | 5 | + We provide pre-trained CNN models to help bootstrap computer vision applications. (Res50 Top1 larger than 77 or even better) 6 | 7 | ## Models 8 | 9 | | Model | Top 1/5 (ours) | Top 1/5 (Paper) | Model | 10 | | :------------: | :------------: | :-------------: | :---------------: | 11 | | Resnet 50 | 78.83/94.23 | 75.3/92.2 | [Model](https://drive.google.com/file/d/1ruQq4noE8Y50hocgJXJ9spDNNtX6ujrG/view?usp=sharing)| 12 | 13 | 14 | ## Evaluation 15 | 16 | ```shell 17 | python main.py VAL_DIR -e --load_ckpt res50.pth 18 | ``` 19 | 20 | ## News 21 | 22 | + 24/12/2018 Add Resnet50 23 | + 29/09/2018 First Init -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import random 4 | import shutil 5 | import time 6 | import warnings 7 | import sys 8 | 9 | import torch 10 | import torch.nn as nn 11 | import torch.nn.parallel 12 | import torch.backends.cudnn as cudnn 13 | import torch.distributed as dist 14 | import torch.optim 15 | import torch.multiprocessing as mp 16 | import torch.utils.data 17 | import torch.utils.data.distributed 18 | import torchvision.transforms as transforms 19 | import torchvision.datasets as datasets 20 | import pretrainedmodels.models as models 21 | 22 | model_names = sorted(name for name in models.__dict__ 23 | if name.islower() and not name.startswith("__") 24 | and callable(models.__dict__[name])) 25 | 26 | parser = argparse.ArgumentParser(description='PyTorch ImageNet Training') 27 | parser.add_argument('data', metavar='DIR', 28 | help='path to dataset') 29 | parser.add_argument('-a', '--arch', metavar='ARCH', default='resnet50', 30 | choices=model_names, 31 | help='model architecture: ' + 32 | ' | '.join(model_names) + 33 | ' (default: resnet18)') 34 | parser.add_argument('-j', '--workers', default=4, type=int, metavar='N', 35 | help='number of data loading workers (default: 4)') 36 | parser.add_argument('--epochs', default=90, type=int, metavar='N', 37 | help='number of total epochs to run') 38 | parser.add_argument('--start-epoch', default=0, type=int, metavar='N', 39 | help='manual epoch number (useful on restarts)') 40 | parser.add_argument('-b', '--batch-size', default=256, type=int, 41 | metavar='N', 42 | help='mini-batch size (default: 256), this is the total ' 43 | 'batch size of all GPUs on the current node when ' 44 | 'using Data Parallel or Distributed Data Parallel') 45 | parser.add_argument('--lr', '--learning-rate', default=0.1, type=float, 46 | metavar='LR', help='initial learning rate', dest='lr') 47 | parser.add_argument('--momentum', default=0.9, type=float, metavar='M', 48 | help='momentum') 49 | parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float, 50 | metavar='W', help='weight decay (default: 1e-4)', 51 | dest='weight_decay') 52 | parser.add_argument('-p', '--print-freq', default=10, type=int, 53 | metavar='N', help='print frequency (default: 10)') 54 | parser.add_argument('--load-ckpt', default='', type=str, metavar='PATH', 55 | help='path to latest checkpoint (default: none)') 56 | parser.add_argument('-e', '--evaluate', dest='evaluate', action='store_true', 57 | help='evaluate model on validation set') 58 | parser.add_argument('--pretrained', dest='pretrained', action='store_true', 59 | help='use pre-trained model') 60 | parser.add_argument('--world-size', default=-1, type=int, 61 | help='number of nodes for distributed training') 62 | parser.add_argument('--rank', default=-1, type=int, 63 | help='node rank for distributed training') 64 | parser.add_argument('--dist-url', default='tcp://224.66.41.62:23456', type=str, 65 | help='url used to set up distributed training') 66 | parser.add_argument('--dist-backend', default='nccl', type=str, 67 | help='distributed backend') 68 | parser.add_argument('--seed', default=None, type=int, 69 | help='seed for initializing training. ') 70 | parser.add_argument('--gpu', default=None, type=int, 71 | help='GPU id to use.') 72 | parser.add_argument('--multiprocessing-distributed', action='store_true', 73 | help='Use multi-processing distributed training to launch ' 74 | 'N processes per node, which has N GPUs. This is the ' 75 | 'fastest way to use PyTorch for either single node or ' 76 | 'multi node data parallel training') 77 | 78 | best_acc1 = 0 79 | 80 | 81 | def main(): 82 | args = parser.parse_args() 83 | 84 | if args.seed is not None: 85 | random.seed(args.seed) 86 | torch.manual_seed(args.seed) 87 | cudnn.deterministic = True 88 | warnings.warn('You have chosen to seed training. ' 89 | 'This will turn on the CUDNN deterministic setting, ' 90 | 'which can slow down your training considerably! ' 91 | 'You may see unexpected behavior when restarting ' 92 | 'from checkpoints.') 93 | 94 | if args.gpu is not None: 95 | warnings.warn('You have chosen a specific GPU. This will completely ' 96 | 'disable data parallelism.') 97 | 98 | if args.dist_url == "env://" and args.world_size == -1: 99 | args.world_size = int(os.environ["WORLD_SIZE"]) 100 | 101 | args.distributed = args.world_size > 1 or args.multiprocessing_distributed 102 | 103 | ngpus_per_node = torch.cuda.device_count() 104 | if args.multiprocessing_distributed: 105 | # Since we have ngpus_per_node processes per node, the total world_size 106 | # needs to be adjusted accordingly 107 | args.world_size = ngpus_per_node * args.world_size 108 | # Use torch.multiprocessing.spawn to launch distributed processes: the 109 | # main_worker process function 110 | mp.spawn(main_worker, nprocs=ngpus_per_node, args=(ngpus_per_node, args)) 111 | else: 112 | # Simply call main_worker function 113 | main_worker(args.gpu, ngpus_per_node, args) 114 | 115 | 116 | def main_worker(gpu, ngpus_per_node, args): 117 | global best_acc1 118 | args.gpu = gpu 119 | 120 | if args.gpu is not None: 121 | print("Use GPU: {} for training".format(args.gpu)) 122 | 123 | if args.distributed: 124 | if args.dist_url == "env://" and args.rank == -1: 125 | args.rank = int(os.environ["RANK"]) 126 | if args.multiprocessing_distributed: 127 | # For multiprocessing distributed training, rank needs to be the 128 | # global rank among all the processes 129 | args.rank = args.rank * ngpus_per_node + gpu 130 | dist.init_process_group(backend=args.dist_backend, init_method=args.dist_url, 131 | world_size=args.world_size, rank=args.rank) 132 | # create model 133 | if args.pretrained: 134 | print("=> using pre-trained model '{}'".format(args.arch)) 135 | model = models.__dict__[args.arch](pretrained=True) 136 | else: 137 | print("=> creating model '{}'".format(args.arch)) 138 | model = models.__dict__[args.arch]() 139 | 140 | optimizer = torch.optim.SGD(model.parameters(), args.lr, 141 | momentum=args.momentum, 142 | weight_decay=args.weight_decay) 143 | 144 | if args.load_ckpt: 145 | if os.path.isfile(args.load_ckpt): 146 | print("=> loading checkpoint '{}'".format(args.load_ckpt)) 147 | checkpoint = torch.load(args.load_ckpt) 148 | model.load_state_dict(checkpoint['model']) 149 | optimizer.load_state_dict(checkpoint['optimizer']) 150 | 151 | else: 152 | print("=> no checkpoint found at '{}'".format(args.load_ckpt)) 153 | 154 | if args.distributed: 155 | # For multiprocessing distributed, DistributedDataParallel constructor 156 | # should always set the single device scope, otherwise, 157 | # DistributedDataParallel will use all available devices. 158 | if args.gpu is not None: 159 | torch.cuda.set_device(args.gpu) 160 | model.cuda(args.gpu) 161 | # When using a single GPU per process and per 162 | # DistributedDataParallel, we need to divide the batch size 163 | # ourselves based on the total number of GPUs we have 164 | args.batch_size = int(args.batch_size / ngpus_per_node) 165 | model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) 166 | else: 167 | model.cuda() 168 | # DistributedDataParallel will divide and allocate batch_size to all 169 | # available GPUs if device_ids are not set 170 | model = torch.nn.parallel.DistributedDataParallel(model) 171 | elif args.gpu is not None: 172 | torch.cuda.set_device(args.gpu) 173 | model = model.cuda(args.gpu) 174 | else: 175 | # DataParallel will divide and allocate batch_size to all available GPUs 176 | if args.arch.startswith('alexnet') or args.arch.startswith('vgg'): 177 | model.features = torch.nn.DataParallel(model.features) 178 | model.cuda() 179 | else: 180 | model = torch.nn.DataParallel(model).cuda() 181 | 182 | # define loss function (criterion) and optimizer 183 | criterion = nn.CrossEntropyLoss().cuda(args.gpu) 184 | 185 | cudnn.benchmark = True 186 | 187 | # Data loading code 188 | valdir = os.path.join(args.data, 'val') 189 | normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], 190 | std=[0.229, 0.224, 0.225]) 191 | 192 | # val_loader = torch.utils.data.DataLoader( 193 | # datasets.ImageFolder(valdir, transforms.Compose([ 194 | # transforms.Resize(256), 195 | # transforms.CenterCrop(224), 196 | # transforms.ToTensor(), 197 | # normalize, 198 | # ])), 199 | # batch_size=args.batch_size, shuffle=False, 200 | # num_workers=args.workers, pin_memory=True) 201 | 202 | val_dataset = \ 203 | datasets.ImageFolder(valdir, 204 | transform=transforms.Compose([ 205 | transforms.Resize(256), 206 | transforms.CenterCrop(224), 207 | transforms.ToTensor(), 208 | transforms.Normalize(mean=[0.485, 0.456, 0.406], 209 | std=[0.229, 0.224, 0.225]) 210 | ])) 211 | 212 | val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=args.batch_size, shuffle=False, 213 | num_workers=args.workers, pin_memory=True) 214 | 215 | if args.evaluate: 216 | validate(val_loader, model, criterion, args) 217 | return 218 | 219 | 220 | def validate(val_loader, model, criterion, args): 221 | batch_time = AverageMeter() 222 | losses = AverageMeter() 223 | top1 = AverageMeter() 224 | top5 = AverageMeter() 225 | 226 | # switch to evaluate mode 227 | model.eval() 228 | 229 | with torch.no_grad(): 230 | end = time.time() 231 | for i, (input, target) in enumerate(val_loader): 232 | if args.gpu is not None: 233 | input = input.cuda(args.gpu, non_blocking=True) 234 | target = target.cuda(args.gpu, non_blocking=True) 235 | 236 | # compute output 237 | output = model(input) 238 | loss = criterion(output, target) 239 | 240 | # measure accuracy and record loss 241 | acc1, acc5 = accuracy(output, target, topk=(1, 5)) 242 | losses.update(loss.item(), input.size(0)) 243 | top1.update(acc1[0], input.size(0)) 244 | top5.update(acc5[0], input.size(0)) 245 | 246 | # measure elapsed time 247 | batch_time.update(time.time() - end) 248 | end = time.time() 249 | 250 | if i % args.print_freq == 0: 251 | print('Test: [{0}/{1}]\t' 252 | 'Time {batch_time.val:.3f} ({batch_time.avg:.3f})\t' 253 | 'Loss {loss.val:.4f} ({loss.avg:.4f})\t' 254 | 'Acc@1 {top1.val:.3f} ({top1.avg:.3f})\t' 255 | 'Acc@5 {top5.val:.3f} ({top5.avg:.3f})'.format( 256 | i, len(val_loader), batch_time=batch_time, loss=losses, 257 | top1=top1, top5=top5)) 258 | 259 | print(' * Acc@1 {top1.avg:.3f} Acc@5 {top5.avg:.3f}' 260 | .format(top1=top1, top5=top5)) 261 | 262 | return top1.avg 263 | 264 | class AverageMeter(object): 265 | """Computes and stores the average and current value""" 266 | def __init__(self): 267 | self.reset() 268 | 269 | def reset(self): 270 | self.val = 0 271 | self.avg = 0 272 | self.sum = 0 273 | self.count = 0 274 | 275 | def update(self, val, n=1): 276 | self.val = val 277 | self.sum += val * n 278 | self.count += n 279 | self.avg = self.sum / self.count 280 | 281 | def accuracy(output, target, topk=(1,)): 282 | """Computes the accuracy over the k top predictions for the specified values of k""" 283 | with torch.no_grad(): 284 | maxk = max(topk) 285 | batch_size = target.size(0) 286 | 287 | _, pred = output.topk(maxk, 1, True, True) 288 | pred = pred.t() 289 | correct = pred.eq(target.view(1, -1).expand_as(pred)) 290 | 291 | res = [] 292 | for k in topk: 293 | correct_k = correct[:k].view(-1).float().sum(0, keepdim=True) 294 | res.append(correct_k.mul_(100.0 / batch_size)) 295 | return res 296 | 297 | 298 | if __name__ == '__main__': 299 | main() -------------------------------------------------------------------------------- /pretrainedmodels/__init__.py: -------------------------------------------------------------------------------- 1 | from .version import __version__ 2 | -------------------------------------------------------------------------------- /pretrainedmodels/imagenet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lbin/pretrainedmodels.pytorch/24efb031ad7a9fa2b209286c1fc7ace29a3a8a62/pretrainedmodels/imagenet/__init__.py -------------------------------------------------------------------------------- /pretrainedmodels/imagenet/config.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | DEFAULTS = { 4 | "network": { 5 | "arch": "resnet101", 6 | "activation": "relu", # supported: relu, leaky_relu, elu, none 7 | "leaky_relu_slope": 0.01, 8 | "input_3x3": False, 9 | "bn_mode": "standard", # supported: standard, inplace, sync 10 | "classes": 1000, 11 | "dilation": 1, 12 | "weight_gain_multiplier": 1, # note: this is ignored if weight_init == kaiming_* 13 | "weight_init": "xavier_normal", # supported: xavier_[normal,uniform], kaiming_[normal,uniform], orthogonal 14 | "devices": [0, 1, 2, 3] # default: 4 gpus 15 | }, 16 | "optimizer": { 17 | "batch_size": 256, 18 | "type": "SGD", # supported: SGD, Adam 19 | "momentum": 0.9, 20 | "weight_decay": 1e-4, 21 | "clip": 1., 22 | "learning_rate": 0.1, 23 | "classifier_lr": -1., # If -1 use same learning rate as the rest of the network 24 | "nesterov": False, 25 | "schedule": { 26 | "type": "constant", # supported: constant, step, multistep, exponential, linear 27 | "mode": "epoch", # supported: epoch, step 28 | "epochs": 10, 29 | "params": {} 30 | } 31 | }, 32 | "input": { 33 | "scale_train": -1, # If -1 do not scale 34 | "crop_train": 224, 35 | "color_jitter_train": False, 36 | "lighting_train": False, 37 | "scale_val": 256, # If -1 do not scale 38 | "crop_val": 224, 39 | "mean": [0.485, 0.456, 0.406], 40 | "std": [0.229, 0.224, 0.225] 41 | } 42 | } 43 | 44 | 45 | def _merge(src, dst): 46 | for k, v in src.items(): 47 | if k in dst: 48 | if isinstance(v, dict): 49 | _merge(src[k], dst[k]) 50 | else: 51 | dst[k] = v 52 | 53 | 54 | def load_config(config_file, defaults=DEFAULTS): 55 | with open(config_file, "r") as fd: 56 | config = json.load(fd) 57 | _merge(defaults, config) 58 | return config 59 | -------------------------------------------------------------------------------- /pretrainedmodels/imagenet/transforms.py: -------------------------------------------------------------------------------- 1 | from random import sample 2 | 3 | import torch 4 | 5 | # Default augmentation values compatible with ImageNet data augmentation pipeline 6 | _DEFAULT_ALPHASTD = 0.1 7 | _DEFAULT_EIGVAL = torch.Tensor([0.2175, 0.0188, 0.0045]) 8 | _DEFAULT_EIGVEC = torch.Tensor( 9 | [[-0.5675, 0.7192, 0.4009], [-0.5808, -0.0045, -0.8140], [-0.5836, -0.6948, 0.4203]]) 10 | _DEFAULT_BCS = [0.4, 0.4, 0.4] 11 | 12 | 13 | def _grayscale(img): 14 | alpha = torch.Tensor([0.299, 0.587, 0.114]) 15 | return (alpha.view(3, 1, 1) * img).sum(0, keepdim=True) 16 | 17 | 18 | def _blend(img1, img2, alpha): 19 | return img1 * alpha + (1 - alpha) * img2 20 | 21 | 22 | class Lighting(object): 23 | def __init__(self, alphastd=_DEFAULT_ALPHASTD, eigval=_DEFAULT_EIGVAL, eigvec=_DEFAULT_EIGVEC): 24 | self._alphastd = alphastd 25 | self._eigval = eigval 26 | self._eigvec = eigvec 27 | 28 | def __call__(self, img): 29 | if self._alphastd == 0.: 30 | return img 31 | 32 | alpha = torch.normal(torch.zeros(3), self._alphastd) 33 | rgb = (self._eigvec * alpha * self._eigval).sum(dim=1) 34 | return img + rgb.view(3, 1, 1) 35 | 36 | 37 | class Saturation(object): 38 | def __init__(self, var): 39 | self._var = var 40 | 41 | def __call__(self, img): 42 | gs = _grayscale(img) 43 | alpha = torch.FloatTensor(1).uniform_(-self._var, self._var) + 1.0 44 | return _blend(img, gs, alpha) 45 | 46 | 47 | class Brightness(object): 48 | def __init__(self, var): 49 | self._var = var 50 | 51 | def __call__(self, img): 52 | gs = torch.zeros(img.size()) 53 | alpha = torch.FloatTensor(1).uniform_(-self._var, self._var) + 1.0 54 | return _blend(img, gs, alpha) 55 | 56 | 57 | class Contrast(object): 58 | def __init__(self, var): 59 | self._var = var 60 | 61 | def __call__(self, img): 62 | gs = _grayscale(img) 63 | gs = torch.FloatTensor(1, 1, 1).fill_(gs.mean()) 64 | alpha = torch.FloatTensor(1).uniform_(-self._var, self._var) + 1.0 65 | return _blend(img, gs, alpha) 66 | 67 | 68 | class ColorJitter(object): 69 | def __init__(self, saturation=_DEFAULT_BCS[0], brightness=_DEFAULT_BCS[1], contrast=_DEFAULT_BCS[2]): 70 | self._transforms = [] 71 | if saturation is not None: 72 | self._transforms.append(Saturation(saturation)) 73 | if brightness is not None: 74 | self._transforms.append(Brightness(brightness)) 75 | if contrast is not None: 76 | self._transforms.append(Contrast(contrast)) 77 | 78 | def __call__(self, img): 79 | if len(self._transforms) == 0: 80 | return img 81 | 82 | for t in sample(self._transforms, len(self._transforms)): 83 | img = t(img) 84 | return img 85 | -------------------------------------------------------------------------------- /pretrainedmodels/imagenet/utils.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | from math import pi, cos 3 | 4 | import torch.nn as nn 5 | import torch.optim as optim 6 | import torch.optim.lr_scheduler as lr_scheduler 7 | import torchvision.transforms as transforms 8 | 9 | from .transforms import ColorJitter, Lighting 10 | 11 | 12 | def create_optimizer(optimizer_config, model, niters=0): 13 | """Creates optimizer and schedule from configuration 14 | 15 | Parameters 16 | ---------- 17 | optimizer_config : dict 18 | Dictionary containing the configuration options for the optimizer. 19 | model : Model 20 | The network model. 21 | niters: int 22 | A epoch include niters times iters. 23 | Returns 24 | ------- 25 | optimizer : Optimizer 26 | The optimizer. 27 | scheduler : LRScheduler 28 | The learning rate scheduler. 29 | """ 30 | if optimizer_config["classifier_lr"] != -1: 31 | # Separate classifier parameters from all others 32 | net_params = [] 33 | classifier_params = [] 34 | for k, v in model.named_parameters(): 35 | if k.find("fc") != -1 or k.find("last") != -1: 36 | classifier_params.append(v) 37 | else: 38 | net_params.append(v) 39 | params = [ 40 | {"params": net_params}, 41 | {"params": classifier_params, 42 | "lr": optimizer_config["classifier_lr"]}, 43 | ] 44 | else: 45 | params = model.parameters() 46 | 47 | # params = filter(lambda p: p.requires_grad, params) 48 | if optimizer_config["freeze"] == 1 and optimizer_config["classifier_lr"] != -1: 49 | params = classifier_params 50 | lr = optimizer_config["classifier_lr"] 51 | # print("Using freeze!") 52 | else: 53 | lr = optimizer_config["learning_rate"] 54 | # print("Not using freeze!") 55 | 56 | if optimizer_config["type"] == "SGD": 57 | optimizer = optim.SGD(params, 58 | lr=lr, 59 | momentum=optimizer_config["momentum"], 60 | weight_decay=optimizer_config["weight_decay"], 61 | nesterov=optimizer_config["nesterov"]) 62 | elif optimizer_config["type"] == "Adam": 63 | optimizer = optim.Adam(params, 64 | lr=optimizer_config["learning_rate"], 65 | weight_decay=optimizer_config["weight_decay"]) 66 | else: 67 | raise KeyError("unrecognized optimizer {}".format( 68 | optimizer_config["type"])) 69 | 70 | if optimizer_config["schedule"]["mode"] == "step": 71 | if optimizer_config["schedule"]["type"] == "linear": 72 | def linear_lr(it): 73 | return it * optimizer_config["schedule"]["params"]["alpha"] + optimizer_config["schedule"]["params"][ 74 | "beta"] 75 | scheduler = lr_scheduler.LambdaLR(optimizer, linear_lr) 76 | 77 | elif optimizer_config["schedule"]["type"] == "warmup_step": 78 | def linear_lr(it): 79 | it += 1 80 | epoch = it // niters 81 | if epoch == 0: 82 | return 0.1 / optimizer_config["learning_rate"] 83 | elif epoch >= 1 and epoch <= 5: 84 | it = it - niters 85 | return (0.1 + (optimizer_config["learning_rate"] - 0.1) * 86 | (it / (5 * niters))) / optimizer_config["learning_rate"] 87 | else: 88 | return 0.1 ** (epoch // 30) 89 | 90 | scheduler = lr_scheduler.LambdaLR(optimizer, linear_lr) 91 | 92 | elif optimizer_config["schedule"]["type"] == "warmup_liner": 93 | def warmup_liner_lr(it): 94 | it += 1 95 | epoch = it // niters 96 | if epoch == 0: 97 | return 0.1 / optimizer_config["learning_rate"] 98 | elif epoch >= 1 and epoch <= 5: 99 | it = it - niters 100 | return (0.1 + (optimizer_config["learning_rate"] - 0.1) * 101 | (it / (5 * niters))) / optimizer_config["learning_rate"] 102 | else: 103 | it = it - 6 * niters 104 | return 1 - (it / (niters * (optimizer_config["schedule"]["epochs"] - 6))) 105 | 106 | scheduler = lr_scheduler.LambdaLR(optimizer, warmup_liner_lr) 107 | 108 | elif optimizer_config["schedule"]["type"] == "warmup_cos": 109 | def linear_lr(it): 110 | it += 1 111 | epoch = it // niters 112 | if epoch == 0: 113 | return 0.1 / optimizer_config["learning_rate"] 114 | elif epoch >= 1 and epoch <= 5: 115 | it = it - niters 116 | return (0.1 + (optimizer_config["learning_rate"] - 0.1) * 117 | (it / (5 * niters))) / optimizer_config["learning_rate"] 118 | else: 119 | it = it - 6 * niters 120 | return (1 + cos(pi * (it / (niters * (optimizer_config["schedule"]["epochs"] - 6))))) / 2 121 | 122 | scheduler = lr_scheduler.LambdaLR(optimizer, linear_lr) 123 | 124 | else: 125 | if optimizer_config["schedule"]["type"] == "step": 126 | scheduler = lr_scheduler.StepLR( 127 | optimizer, **optimizer_config["schedule"]["params"]) 128 | elif optimizer_config["schedule"]["type"] == "multistep": 129 | scheduler = lr_scheduler.MultiStepLR( 130 | optimizer, **optimizer_config["schedule"]["params"]) 131 | elif optimizer_config["schedule"]["type"] == "exponential": 132 | scheduler = lr_scheduler.ExponentialLR( 133 | optimizer, **optimizer_config["schedule"]["params"]) 134 | elif optimizer_config["schedule"]["type"] == "constant": 135 | scheduler = lr_scheduler.LambdaLR(optimizer, lambda epoch: 1.0) 136 | elif optimizer_config["schedule"]["type"] == "warmup": 137 | def warmup_lr(it): 138 | if it >= 0 and it <= 5: 139 | return ((optimizer_config["learning_rate"] - 0.1) * it + 0.5) / ( 140 | 5 * optimizer_config["learning_rate"]) 141 | else: 142 | return 0.1 ** (it // 30) 143 | 144 | scheduler = lr_scheduler.LambdaLR(optimizer, warmup_lr) 145 | elif optimizer_config["schedule"]["type"] == "warmup_liner": 146 | def warmup_liner_lr(it): 147 | if it >= 0 and it <= 5: 148 | return ((optimizer_config["learning_rate"] - 0.1) * it + 0.5) / ( 149 | 5 * optimizer_config["learning_rate"]) 150 | else: 151 | return 1 - (it / (optimizer_config["schedule"]["epochs"] - 5)) 152 | 153 | scheduler = lr_scheduler.LambdaLR(optimizer, warmup_liner_lr) 154 | 155 | return optimizer, scheduler 156 | 157 | 158 | def create_transforms(input_config): 159 | """Create transforms from configuration 160 | 161 | Parameters 162 | ---------- 163 | input_config : dict 164 | Dictionary containing the configuration options for input pre-processing. 165 | 166 | Returns 167 | ------- 168 | train_transforms : list 169 | List of transforms to be applied to the input during training. 170 | val_transforms : list 171 | List of transforms to be applied to the input during validation. 172 | """ 173 | normalize = transforms.Normalize( 174 | mean=input_config["mean"], std=input_config["std"]) 175 | 176 | train_transforms = [] 177 | if input_config["scale_train"] != -1: 178 | train_transforms.append(transforms.Resize(input_config["scale_train"])) 179 | # https://github.com/pytorch/examples/issues/355 180 | train_transforms += [ 181 | transforms.RandomResizedCrop( 182 | input_config["crop_train"]), 183 | transforms.RandomHorizontalFlip(), 184 | transforms.ToTensor() 185 | ] 186 | if input_config["color_jitter_train"]: 187 | train_transforms.append(ColorJitter()) 188 | if input_config["lighting_train"]: 189 | train_transforms.append(Lighting()) 190 | train_transforms.append(normalize) 191 | 192 | val_transforms = [] 193 | if input_config["scale_val"] != -1: 194 | val_transforms.append(transforms.Resize(input_config["scale_val"])) 195 | val_transforms += [ 196 | transforms.CenterCrop(input_config["crop_val"]), 197 | transforms.ToTensor(), 198 | normalize, 199 | ] 200 | 201 | return train_transforms, val_transforms 202 | 203 | 204 | def get_model_params(network_config): 205 | """Convert a configuration to actual model parameters 206 | 207 | Parameters 208 | ---------- 209 | network_config : dict 210 | Dictionary containing the configuration options for the network. 211 | 212 | Returns 213 | ------- 214 | model_params : dict 215 | Dictionary containing the actual parameters to be passed to the `net_*` functions in `models`. 216 | """ 217 | model_params = {} 218 | 219 | # model_params["classes"] = network_config["classes"] 220 | 221 | return model_params 222 | 223 | 224 | class MultiCropEnsemble(nn.Module): 225 | def __init__(self, module, cropsize, act=nn.functional.softmax, flipping=True): 226 | super(MultiCropEnsemble, self).__init__() 227 | self.cropsize = cropsize 228 | self.flipping = flipping 229 | self.internal_module = module 230 | self.act = act 231 | 232 | # Naive code 233 | def forward(self, x): 234 | # H, W >= cropsize 235 | assert(x.size()[2] >= self.cropsize) 236 | assert(x.size()[3] >= self.cropsize) 237 | 238 | cs = self.cropsize 239 | x1 = 0 240 | x2 = x.size()[2] - self.cropsize 241 | cx = x.size()[2] // 2 - self.cropsize // 2 242 | y1 = 0 243 | y2 = x.size()[3] - self.cropsize 244 | cy = x.size()[3] // 2 - self.cropsize // 2 245 | 246 | def get_output(x): return self.act(self.internal_module.forward(x)) 247 | 248 | _y = get_output(x[:, :, x1: x1 + cs, y1: y1 + cs]) 249 | _y = get_output(x[:, :, x1: x1 + cs, y2: y2 + cs]) + _y 250 | _y = get_output(x[:, :, x2: x2 + cs, y1: y1 + cs]) + _y 251 | _y = get_output(x[:, :, x2: x2 + cs, y2: y2 + cs]) + _y 252 | _y = get_output(x[:, :, cx: cx + cs, cy: cy + cs]) + _y 253 | 254 | if self.flipping == True: 255 | # Naive flipping 256 | 257 | # Bring back to cpu 258 | arr = (x.data).cpu().numpy() 259 | arr = arr[:, :, :, :: -1] # Flip 260 | x.data = type(x.data)(np.ascontiguousarray(arr)) # Store 261 | 262 | _y = get_output(x[:, :, x1: x1 + cs, y1: y1 + cs]) + _y 263 | _y = get_output(x[:, :, x1: x1 + cs, y2: y2 + cs]) + _y 264 | _y = get_output(x[:, :, x2: x2 + cs, y1: y1 + cs]) + _y 265 | _y = get_output(x[:, :, x2: x2 + cs, y2: y2 + cs]) + _y 266 | _y = get_output(x[:, :, cx: cx + cs, cy: cy + cs]) + _y 267 | 268 | _y = _y / 10.0 269 | else: 270 | _y = _y / 5.0 271 | 272 | return _y 273 | -------------------------------------------------------------------------------- /pretrainedmodels/models/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division, absolute_import 2 | from .resnet import * 3 | -------------------------------------------------------------------------------- /pretrainedmodels/models/resnet.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | import torch 3 | import math 4 | import torch.utils.model_zoo as model_zoo 5 | 6 | __all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', 7 | 'resnet152'] 8 | 9 | 10 | model_urls = { 11 | 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', 12 | 'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth', 13 | 'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth', 14 | 'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth', 15 | 'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth', 16 | } 17 | 18 | 19 | def conv3x3(in_planes, out_planes, stride=1): 20 | """3x3 convolution with padding""" 21 | return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, 22 | padding=1, bias=False) 23 | 24 | 25 | class BasicBlock(nn.Module): 26 | expansion = 1 27 | 28 | def __init__(self, inplanes, planes, stride=1, downsample=None): 29 | super(BasicBlock, self).__init__() 30 | self.conv1 = conv3x3(inplanes, planes, stride) 31 | self.bn1 = nn.BatchNorm2d(planes) 32 | # self.relu = nn.LeakyReLU(inplace=True) 33 | self.relu = nn.ReLU(inplace=True) 34 | self.conv2 = conv3x3(planes, planes) 35 | self.bn2 = nn.BatchNorm2d(planes) 36 | self.downsample = downsample 37 | self.stride = stride 38 | 39 | def forward(self, x): 40 | residual = x 41 | 42 | out = self.conv1(x) 43 | out = self.bn1(out) 44 | out = self.relu(out) 45 | 46 | out = self.conv2(out) 47 | out = self.bn2(out) 48 | 49 | if self.downsample is not None: 50 | residual = self.downsample(x) 51 | 52 | out += residual 53 | out = self.relu(out) 54 | 55 | return out 56 | 57 | 58 | class Bottleneck(nn.Module): 59 | expansion = 4 60 | 61 | def __init__(self, inplanes, planes, stride=1, downsample=None,dilation=1): 62 | super(Bottleneck, self).__init__() 63 | self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) 64 | self.bn1 = nn.BatchNorm2d(planes) 65 | self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, 66 | padding=dilation,dilation=dilation,bias=False) 67 | self.bn2 = nn.BatchNorm2d(planes) 68 | self.conv3 = nn.Conv2d(planes, planes * self.expansion, kernel_size=1, bias=False) 69 | self.bn3 = nn.BatchNorm2d(planes * self.expansion) 70 | self.relu = nn.ReLU(inplace=True) 71 | # self.relu = nn.LeakyReLU(inplace=True) 72 | self.downsample = downsample 73 | self.stride = stride 74 | 75 | nn.init.constant_(self.bn1.weight,1) 76 | nn.init.constant_(self.bn2.weight,1) 77 | nn.init.constant_(self.bn3.weight,0) 78 | def forward(self, x): 79 | residual = x 80 | 81 | out = self.conv1(x) 82 | out = self.bn1(out) 83 | out = self.relu(out) 84 | 85 | out = self.conv2(out) 86 | out = self.bn2(out) 87 | out = self.relu(out) 88 | 89 | out = self.conv3(out) 90 | out = self.bn3(out) 91 | 92 | if self.downsample is not None: 93 | residual = self.downsample(x) 94 | 95 | out += residual 96 | out = self.relu(out) 97 | 98 | return out 99 | 100 | 101 | class ResNet(nn.Module): 102 | 103 | def __init__(self, block, layers, num_classes=1000,dilation=True): 104 | self.inplanes = 64 105 | super(ResNet, self).__init__() 106 | #�?x7的卷积换�?�?x3�? 107 | # self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, 108 | # bias=False) 109 | self.conv1 = nn.Conv2d(3,32,kernel_size=3,stride=2,padding=1,bias=False) 110 | self.bn1 = nn.BatchNorm2d(32) 111 | self.conv2 = nn.Conv2d(32,32,kernel_size=3,stride=1,padding=1,bias=False) 112 | self.bn2 = nn.BatchNorm2d(32) 113 | self.conv3 = nn.Conv2d(32,64,kernel_size=3,stride=1,padding=1,bias=False) 114 | self.bn3 = nn.BatchNorm2d(64) 115 | # self.bn1 = nn.BatchNorm2d(64) 116 | self.relu = nn.ReLU(inplace=True) 117 | # self.relu = nn.LeakyReLU(inplace=True) 118 | self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) 119 | self.layer1 = self._make_layer(block, 64, layers[0]) 120 | self.layer2 = self._make_layer(block, 128, layers[1], stride=2) 121 | if dilation: 122 | self.layer3 = self._make_layer(block, 256, layers[2], stride=1, dilation=2) 123 | self.layer4 = self._make_layer(block, 512, layers[3], stride=1, dilation=4) 124 | else: 125 | self.layer3 = self._make_layer(block, 256, layers[2], stride=2) 126 | self.layer4 = self._make_layer(block, 512, layers[3], stride=2) 127 | # self.avgpool = nn.AvgPool2d(7, stride=1) 128 | self.avgpool = nn.AdaptiveAvgPool2d(output_size=1) 129 | # self.maxpool2 = nn.MaxPool2d(7,stride=1) 130 | self.fc = nn.Linear(512 * block.expansion, num_classes) 131 | 132 | nn.init.constant_(self.bn1.weight,1) 133 | nn.init.constant_(self.bn1.bias,0) 134 | nn.init.constant_(self.bn2.weight, 1) 135 | nn.init.constant_(self.bn2.bias, 0) 136 | nn.init.constant_(self.bn3.weight, 1) 137 | nn.init.constant_(self.bn3.bias, 0) 138 | for m in self.modules(): 139 | if isinstance(m, nn.Conv2d): 140 | nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') 141 | elif isinstance(m, nn.BatchNorm2d): 142 | # nn.init.constant_(m.weight, 1) 143 | nn.init.constant_(m.bias, 0) 144 | # print(m.weight) 145 | def _make_layer(self, block, planes, blocks, stride=1,dilation=1): 146 | #在downsample加入pooling 147 | downsample = None 148 | if stride != 1 or self.inplanes != planes * block.expansion: 149 | self.bn = nn.BatchNorm2d(planes * block.expansion) 150 | if dilation==1: 151 | downsample = nn.Sequential( 152 | nn.AvgPool2d(kernel_size=1,stride=1,ceil_mode=True,count_include_pad=False), 153 | nn.Conv2d(self.inplanes, planes * block.expansion, 154 | kernel_size=1, stride=stride, bias=False), 155 | self.bn, 156 | ) 157 | else: 158 | downsample = nn.Sequential( 159 | nn.AvgPool2d(kernel_size=stride,stride=stride,ceil_mode=True,count_include_pad=False), 160 | nn.Conv2d(self.inplanes,planes*block.expansion, 161 | kernel_size=1,stride=stride,bias=False), 162 | self.bn 163 | ) 164 | nn.init.constant_(self.bn.weight,1) 165 | 166 | layers = [] 167 | # layers.append(block(self.inplanes, planes, stride, downsample)) 168 | if dilation in (1,2): 169 | layers.append(block(self.inplanes,planes,stride,dilation=1,downsample=downsample)) 170 | elif dilation == 4: 171 | layers.append(block(self.inplanes,planes,stride,dilation=2,downsample=downsample)) 172 | else: 173 | raise RuntimeError("=> unknown dilation size: {}".format(dilation)) 174 | self.inplanes = planes * block.expansion 175 | for i in range(1, blocks): 176 | layers.append(block(self.inplanes, planes,dilation=dilation)) 177 | 178 | return nn.Sequential(*layers) 179 | 180 | def forward(self, x): 181 | x = self.conv1(x) 182 | x = self.bn1(x) 183 | x = self.relu(x) 184 | x = self.conv2(x) 185 | x = self.bn2(x) 186 | x = self.relu(x) 187 | x = self.conv3(x) 188 | x = self.bn3(x) 189 | x = self.relu(x) 190 | x = self.maxpool(x) 191 | 192 | x = self.layer1(x) 193 | x = self.layer2(x) 194 | x = self.layer3(x) 195 | x = self.layer4(x) 196 | 197 | x = self.avgpool(x) 198 | # x = self.maxpool2(x) 199 | x = x.view(x.size(0), -1) 200 | x = self.fc(x) 201 | 202 | return x 203 | 204 | 205 | def resnet18(pretrained=False, **kwargs): 206 | """Constructs a ResNet-18 model. 207 | 208 | Args: 209 | pretrained (bool): If True, returns a model pre-trained on ImageNet 210 | """ 211 | model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs) 212 | if pretrained: 213 | model.load_state_dict(model_zoo.load_url(model_urls['resnet18'])) 214 | return model 215 | 216 | 217 | def resnet34(pretrained=False, **kwargs): 218 | """Constructs a ResNet-34 model. 219 | 220 | Args: 221 | pretrained (bool): If True, returns a model pre-trained on ImageNet 222 | """ 223 | model = ResNet(BasicBlock, [3, 4, 6, 3], **kwargs) 224 | if pretrained: 225 | model.load_state_dict(model_zoo.load_url(model_urls['resnet34'])) 226 | return model 227 | 228 | 229 | def resnet50(pretrained=False, **kwargs): 230 | """Constructs a ResNet-50 model. 231 | 232 | Args: 233 | pretrained (bool): If True, returns a model pre-trained on ImageNet 234 | """ 235 | model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs) 236 | if pretrained: 237 | model.load_state_dict(model_zoo.load_url(model_urls['resnet50'])) 238 | return model 239 | 240 | 241 | def resnet101(pretrained=False, **kwargs): 242 | """Constructs a ResNet-101 model. 243 | 244 | Args: 245 | pretrained (bool): If True, returns a model pre-trained on ImageNet 246 | """ 247 | model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs) 248 | if pretrained: 249 | model.load_state_dict(model_zoo.load_url(model_urls['resnet101'])) 250 | return model 251 | 252 | 253 | def resnet152(pretrained=False, **kwargs): 254 | """Constructs a ResNet-152 model. 255 | 256 | Args: 257 | pretrained (bool): If True, returns a model pre-trained on ImageNet 258 | """ 259 | model = ResNet(Bottleneck, [3, 8, 36, 3], **kwargs) 260 | if pretrained: 261 | model.load_state_dict(model_zoo.load_url(model_urls['resnet152'])) 262 | return model -------------------------------------------------------------------------------- /pretrainedmodels/version.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division, absolute_import 2 | __version__ = '0.1.0' 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lbin/pretrainedmodels.pytorch/24efb031ad7a9fa2b209286c1fc7ace29a3a8a62/requirements.txt --------------------------------------------------------------------------------