@@ -0,0 +1,53 @@ | |||
[loggers] | |||
keys=root,TRAIN,TEST,EVAL | |||
[handlers] | |||
keys=consoleHandler,fileHandler | |||
[formatters] | |||
keys=simpleFormatter | |||
[logger_root] | |||
level=INFO | |||
handlers=consoleHandler,fileHandler | |||
[logger_TRAIN] | |||
level=INFO | |||
handlers=consoleHandler,fileHandler | |||
qualname=TRAIN | |||
propagate=0 | |||
[logger_VAL] | |||
level=INFO | |||
handlers=consoleHandler,fileHandler | |||
qualname=VAL | |||
propagate=0 | |||
[logger_TEST] | |||
level=INFO | |||
handlers=consoleHandler,fileHandler | |||
qualname=TEST | |||
propagate=0 | |||
[logger_EVAL] | |||
level=INFO | |||
handlers=consoleHandler,fileHandler | |||
qualname=simpleExample | |||
propagate=0 | |||
[handler_consoleHandler] | |||
class=StreamHandler | |||
level=INFO | |||
formatter=simpleFormatter | |||
args=(sys.stdout,) | |||
[handler_fileHandler] | |||
class = FileHandler | |||
level=INFO | |||
formatter=simpleFormatter | |||
args=('/output/log/log.txt','a') | |||
[formatter_simpleFormatter] | |||
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s | |||
datefmt=%Y-%m-%d %H:%M:%S |
@@ -0,0 +1,25 @@ | |||
# file location | |||
# ROOT_DIR="/home/xujiahong/VeRi_try/openI" | |||
ROOT_DIR="" | |||
# output dir | |||
CODE_ROOT_DIR = ROOT_DIR + "/code" | |||
# CODE_ROOT_DIR = '/home/xujiahong/openI_benchmark/BoT_person_reID' | |||
# config dir | |||
CONFIG_PATH = CODE_ROOT_DIR + "/config/re_ID.yaml" | |||
# output dir | |||
OUTPUT_RESULT_DIR= "/output/result/" | |||
EVAL_RESULT_PATH = "/output/log/eval_result.json" | |||
FULL_INFO_PATH = "/output/log/full_info.json" | |||
LOGGER_PATH = "/output/log/log.txt" #与logger.conf 中的值保持一致 | |||
# upload API | |||
UPLOAD_EVAL_RESULT_API = "http://192.168.202.90:6134/api/benchmark-result/" | |||
DEBUG_MODE = True | |||
REID_TYPE = {1: "vehicle reid", 2:"image-based person reid", 3:"person search", 4:"video-based person reid"} | |||
ALG_TYPE = "Re-ID" | |||
USER_INFO_PATH = "/benchmark/taskInfo" | |||
@@ -0,0 +1,35 @@ | |||
# scene: type of re-ID | |||
# four types in total: | |||
# 1. vehicle reid | |||
# 2. image_based person reid | |||
# 3. person search | |||
# 4. video-based person reid | |||
scene: 2 | |||
attr_name: | |||
attr_value: | |||
input: | |||
dataset: | |||
# data_dir: /root/small_market1501 | |||
# data_dir: /dataset/small_market1501 | |||
data_dir: /dataset/benchmark_vehicle_reID | |||
# data_dir: /dataset/1_Market1501 | |||
config: | |||
adam: True | |||
batchsize: 128 | |||
droprate: 0.2 | |||
erasing_p: 0 | |||
h: 256 | |||
w: 256 | |||
inputsize: 256 | |||
lr: 0.00035 | |||
name: Res50 | |||
nclass: 2304 #class number of benchmark_person_reID | |||
num_epochs: 1 | |||
pool: avg | |||
stride: 1 | |||
warm_epoch: 0 | |||
test: | |||
flip_test: True |
@@ -0,0 +1,3 @@ | |||
from .label_smooth import * | |||
from .model_train import * | |||
from .triplet import * |
@@ -0,0 +1,33 @@ | |||
from __future__ import print_function, absolute_import | |||
import torch | |||
from torch import nn | |||
class LSR_loss(nn.Module): | |||
def __init__(self, e=0.1): | |||
super().__init__() | |||
self.log_softmax = nn.LogSoftmax(dim=1) | |||
self.e = e | |||
def _one_hot(self, labels, classes, value=1): | |||
one_hot = torch.zeros(labels.size(0), classes) | |||
# labels and value_added size must match | |||
labels = labels.view(labels.size(0), -1) | |||
value_added = torch.Tensor(labels.size(0), 1).fill_(value) | |||
value_added = value_added.to(labels.device) | |||
one_hot = one_hot.to(labels.device) | |||
one_hot.scatter_add_(1, labels, value_added) | |||
return one_hot | |||
def _smooth_label(self, target, length, smooth_factor): | |||
one_hot = self._one_hot(target, length, value=1 - smooth_factor) | |||
one_hot += smooth_factor / length | |||
return one_hot.to(target.device) | |||
def forward(self, x, target): | |||
smoothed_target = self._smooth_label(target, x.size(1), self.e) | |||
x = self.log_softmax(x) | |||
loss = torch.sum(- x * smoothed_target, dim=1) | |||
return torch.mean(loss) |
@@ -0,0 +1,157 @@ | |||
import argparse | |||
import torch | |||
import torch.nn as nn | |||
from torch.nn import init | |||
from torchvision import models | |||
from torch.autograd import Variable | |||
#from efficientnet_pytorch import EfficientNet | |||
#import pretrainedmodels | |||
from torch.nn import functional as F | |||
###################################################################### | |||
def weights_init_kaiming(m): | |||
classname = m.__class__.__name__ | |||
# print(classname) | |||
if classname.find('Conv') != -1: | |||
init.kaiming_normal_(m.weight.data, a=0, mode='fan_in') # For old pytorch, you may use kaiming_normal. | |||
elif classname.find('Linear') != -1: | |||
init.kaiming_normal_(m.weight.data, a=0, mode='fan_out') | |||
init.constant_(m.bias.data, 0.0) | |||
elif classname.find('BatchNorm1d') != -1: | |||
init.normal_(m.weight.data, 1.0, 0.02) | |||
init.constant_(m.bias.data, 0.0) | |||
def weights_init_classifier(m): | |||
classname = m.__class__.__name__ | |||
if classname.find('Linear') != -1: | |||
init.normal_(m.weight.data, std=0.001) | |||
init.constant_(m.bias.data, 0.0) | |||
def fix_relu(m): | |||
classname = m.__class__.__name__ | |||
if classname.find('ReLU') != -1: | |||
m.inplace=True | |||
# Defines the new fc layer and classification layer | |||
# |--Linear--|--bn--|--relu--|--Linear--| | |||
class ClassBlock(nn.Module): | |||
''' | |||
xjh_notes: | |||
----------------------- | |||
Defines the linear layers after the adp avg pooling layer in the backbone | |||
----------------------- | |||
Parameters: | |||
linear - if add a fc layer of size [input_dim, num_bottleneck] | |||
bnorm - if add a bn layer | |||
relu - if add a leakyReLU layer | |||
droprate - if droprate > 0, using dropout in add_block | |||
''' | |||
def __init__(self, input_dim, class_num, droprate, relu=True, bnorm=True, num_bottleneck=2048, linear=True, return_f = False): | |||
super(ClassBlock, self).__init__() | |||
self.return_f = return_f | |||
add_block = [] | |||
if linear: | |||
add_block += [nn.Linear(input_dim, num_bottleneck)] | |||
else: | |||
num_bottleneck = input_dim | |||
if bnorm: | |||
add_block += [nn.BatchNorm1d(num_bottleneck)] | |||
if relu: | |||
add_block += [nn.LeakyReLU(0.1)] | |||
if droprate>0: | |||
add_block += [nn.Dropout(p=droprate)] | |||
add_block = nn.Sequential(*add_block) | |||
add_block.apply(weights_init_kaiming) | |||
classifier = [] | |||
classifier += [nn.Linear(num_bottleneck, class_num)] | |||
classifier = nn.Sequential(*classifier) | |||
classifier.apply(weights_init_classifier) | |||
self.add_block = add_block | |||
self.classifier = classifier | |||
def forward(self, x): | |||
f = x | |||
x = self.add_block(x) | |||
x = self.classifier(x) | |||
if self.return_f: | |||
return x,f | |||
else: | |||
return x | |||
# Define the ResNet50-based Model | |||
class ft_net(nn.Module): | |||
def __init__(self, class_num, droprate=0.5, stride=2, init_model=None, pool='avg', return_f=True): | |||
self.return_f=return_f | |||
super(ft_net, self).__init__() | |||
model_ft = models.resnet50(pretrained=True) | |||
# avg pooling to global pooling | |||
if stride == 1: | |||
model_ft.layer4[0].downsample[0].stride = (1,1) | |||
model_ft.layer4[0].conv2.stride = (1,1) | |||
self.pool = pool | |||
if pool =='avg+max': | |||
model_ft.avgpool2 = nn.AdaptiveAvgPool2d((1,1)) | |||
model_ft.maxpool2 = nn.AdaptiveMaxPool2d((1,1)) | |||
self.model = model_ft | |||
self.classifier= ClassBlock(2048, class_num, droprate, return_f=self.return_f, linear=False) | |||
elif pool=='avg': | |||
model_ft.avgpool = nn.AdaptiveAvgPool2d((1,1)) | |||
self.model = model_ft | |||
self.classifier= ClassBlock(2048, class_num, droprate, return_f=self.return_f, linear=False) | |||
if init_model!=None: | |||
self.flag = True | |||
self.model = init_model.model | |||
self.pool = init_model.pool | |||
self.classifier.add_block = init_model.classifier.add_block | |||
# avg pooling to global pooling | |||
def forward(self, x): | |||
x = self.model.conv1(x) | |||
x = self.model.bn1(x) | |||
x = self.model.relu(x) | |||
x = self.model.maxpool(x) | |||
x = self.model.layer1(x) | |||
x = self.model.layer2(x) | |||
x = self.model.layer3(x) | |||
x = self.model.layer4(x) | |||
if self.pool == 'avg+max': | |||
x1 = self.model.avgpool2(x) | |||
x2 = self.model.maxpool2(x) | |||
x = torch.cat((x1,x2), dim = 1) | |||
x = x.view(x.size(0), x.size(1)) | |||
elif self.pool == 'avg': | |||
x = self.model.avgpool(x) | |||
x = x.view(x.size(0), x.size(1)) | |||
output = self.classifier(x) #if return_f: return (fc, feature) else return fc | |||
return output | |||
''' | |||
# debug model structure | |||
# Run this code with: | |||
python model.py | |||
''' | |||
if __name__ == '__main__': | |||
# Here I left a simple forward function. | |||
# Test the model, before you train it. | |||
''' | |||
net = CPB(751) | |||
#net = ft_net_SE(751) | |||
print(net) | |||
input = Variable(torch.FloatTensor(4, 3, 320, 320)) | |||
output = net(input) | |||
print('net output size:') | |||
#print(output.shape) | |||
''' | |||
xjh_net = ft_net(333) | |||
print(xjh_net) | |||
input = Variable(torch.FloatTensor(2,3,384,384)) | |||
output = xjh_net(input) | |||
print(output.shape) |
@@ -0,0 +1,170 @@ | |||
from __future__ import absolute_import | |||
import torch | |||
from torch import nn | |||
def normalize(x, axis=-1): | |||
"""Normalizing to unit length along the specified dimension. | |||
Args: | |||
x: pytorch Variable | |||
Returns: | |||
x: pytorch Variable, same shape as input | |||
""" | |||
x = 1. * x / (torch.norm(x, 2, axis, keepdim=True).expand_as(x) + 1e-12) | |||
return x | |||
def euclidean_dist(x, y): | |||
""" | |||
Args: | |||
x: pytorch Variable, with shape [m, d] | |||
y: pytorch Variable, with shape [n, d] | |||
Returns: | |||
dist: pytorch Variable, with shape [m, n] | |||
""" | |||
m, n = x.size(0), y.size(0) | |||
xx = torch.pow(x, 2).sum(1, keepdim=True).expand(m, n) | |||
yy = torch.pow(y, 2).sum(1, keepdim=True).expand(n, m).t() | |||
dist = xx + yy | |||
# dist.addmm_(1, -2, x, y.t()) | |||
dist.addmm_( x, y.t(), beta = 1, alpha = -2) | |||
dist = dist.clamp(min=1e-12).sqrt() # for numerical stability | |||
return dist | |||
def hard_example_mining(dist_mat, labels, return_inds=False): | |||
"""For each anchor, find the hardest positive and negative sample. | |||
Args: | |||
dist_mat: pytorch Variable, pair wise distance between samples, shape [N, N] | |||
labels: pytorch LongTensor, with shape [N] | |||
return_inds: whether to return the indices. Save time if `False`(?) | |||
Returns: | |||
dist_ap: pytorch Variable, distance(anchor, positive); shape [N] | |||
dist_an: pytorch Variable, distance(anchor, negative); shape [N] | |||
p_inds: pytorch LongTensor, with shape [N]; | |||
indices of selected hard positive samples; 0 <= p_inds[i] <= N - 1 | |||
n_inds: pytorch LongTensor, with shape [N]; | |||
indices of selected hard negative samples; 0 <= n_inds[i] <= N - 1 | |||
NOTE: Only consider the case in which all labels have same num of samples, | |||
thus we can cope with all anchors in parallel. | |||
""" | |||
assert len(dist_mat.size()) == 2 | |||
assert dist_mat.size(0) == dist_mat.size(1) | |||
N = dist_mat.size(0) | |||
# shape [N, N] | |||
is_pos = labels.expand(N, N).eq(labels.expand(N, N).t()) | |||
is_neg = labels.expand(N, N).ne(labels.expand(N, N).t()) | |||
''' | |||
# `dist_ap` means distance(anchor, positive) | |||
# both `dist_ap` and `relative_p_inds` with shape [N, 1] | |||
dist_ap, relative_p_inds = torch.max(dist_mat[is_pos].contiguous().view(N, -1), 1, keepdim=True) | |||
# `dist_an` means distance(anchor, negative) | |||
# both `dist_an` and `relative_n_inds` with shape [N, 1] | |||
dist_an, relative_n_inds = torch.min(dist_mat[is_neg].contiguous().view(N, -1), 1, keepdim=True) | |||
# shape [N] | |||
dist_ap = dist_ap.squeeze(1) | |||
dist_an = dist_an.squeeze(1) | |||
''' | |||
dist_ap, relative_p_inds = torch.max(dist_mat.masked_fill(is_neg, 0), 1, keepdim=True) | |||
dist_an, relative_n_inds = torch.min(dist_mat.masked_fill(is_pos, float("inf")), 1, keepdim=True) | |||
# dist_ap1, dist_an1 = [], [] | |||
# for i in range(N): | |||
# dist_ap1.append(dist_mat[i][is_pos[i]].max().unsqueeze(0)) | |||
# dist_an1.append(dist_mat[i][is_pos[i] == 0].min().unsqueeze(0)) | |||
# dist_ap1, dist_an1 = torch.tensor(dist_ap1), torch.tensor(dist_an1) | |||
if return_inds: | |||
# shape [N, N] | |||
ind = (labels.new().resize_as_(labels).copy_(torch.arange(0, N).long()).unsqueeze(0).expand(N, N)) | |||
# shape [N, 1] | |||
p_inds = torch.gather(ind[is_pos].contiguous().view(N, -1), 1, relative_p_inds.data) | |||
n_inds = torch.gather(ind[is_neg].contiguous().view(N, -1), 1, relative_n_inds.data) | |||
# shape [N] | |||
p_inds = p_inds.squeeze(1) | |||
n_inds = n_inds.squeeze(1) | |||
return dist_ap, dist_an, p_inds, n_inds | |||
return dist_ap, dist_an | |||
class TripletLoss(nn.Module): | |||
def __init__(self, margin=None): | |||
super(TripletLoss, self).__init__() | |||
self.margin = margin | |||
if self.margin is not None: | |||
self.ranking_loss = nn.MarginRankingLoss(margin=margin) | |||
else: | |||
self.ranking_loss = nn.SoftMarginLoss() | |||
def forward(self, global_feat, labels, normalize_feature=False): | |||
if normalize_feature: | |||
global_feat = normalize(global_feat, axis=-1) | |||
# shape [N, N] | |||
dist_mat = euclidean_dist(global_feat, global_feat) | |||
dist_ap, dist_an = hard_example_mining(dist_mat, labels) | |||
y = dist_an.new().resize_as_(dist_an).fill_(1) | |||
if self.margin is not None: | |||
loss = self.ranking_loss(dist_an, dist_ap, y) | |||
else: | |||
loss = self.ranking_loss(dist_an - dist_ap, y) | |||
prec = (dist_an.data > dist_ap.data).sum().float() / y.size(0) | |||
return loss, prec, dist_ap, dist_an | |||
class Center_loss(nn.Module): | |||
"""Center loss. | |||
Reference: | |||
Wen et al. A Discriminative Feature Learning Approach for Deep Face Recognition. ECCV 2016. | |||
Args: | |||
num_classes (int): number of classes. | |||
feat_dim (int): feature dimension. | |||
""" | |||
def __init__(self, num_classes=751, feat_dim=2048, use_gpu=True): | |||
super(Center_loss, self).__init__() | |||
self.num_classes = num_classes | |||
self.feat_dim = feat_dim | |||
self.use_gpu = use_gpu | |||
if self.use_gpu: | |||
self.centers = nn.Parameter(torch.randn(self.num_classes, self.feat_dim).cuda()) | |||
else: | |||
self.centers = nn.Parameter(torch.randn(self.num_classes, self.feat_dim)) | |||
def forward(self, x, labels): | |||
""" | |||
Args: | |||
x: feature matrix with shape (batch_size, feat_dim). | |||
labels: ground truth labels with shape (num_classes). | |||
""" | |||
assert x.size(0) == labels.size(0), "features.size(0) is not equal to labels.size(0)" | |||
batch_size = x.size(0) | |||
distmat = torch.pow(x, 2).sum(dim=1, keepdim=True).expand(batch_size, self.num_classes) + \ | |||
torch.pow(self.centers, 2).sum(dim=1, keepdim=True).expand(self.num_classes, batch_size).t() | |||
distmat.addmm_(1, -2, x, self.centers.t()) | |||
classes = torch.arange(self.num_classes).long() | |||
if self.use_gpu: classes = classes.cuda() | |||
labels = labels.unsqueeze(1).expand(batch_size, self.num_classes) | |||
mask = labels.eq(classes.expand(batch_size, self.num_classes)) | |||
dist = distmat * mask.float() | |||
loss = dist.clamp(min=1e-12, max=1e+12).sum() / batch_size | |||
#dist = [] | |||
#for i in range(batch_size): | |||
# value = distmat[i][mask[i]] | |||
# value = value.clamp(min=1e-12, max=1e+12) # for numerical stability | |||
# dist.append(value) | |||
#dist = torch.cat(dist) | |||
#loss = dist.mean() | |||
return loss | |||
@@ -0,0 +1,56 @@ | |||
# CIFAR-10图像识别项目实战 | |||
本实战项目是一个CIFAR-10的图像分类任务,基于CIFAR10的数据集和【PyTorch】,通过在云脑环境调试和训练模型,最终评估模型的准确率。 | |||
## 文件资料说明 | |||
- 《Case1》文件夹,是基于云脑1算力资源(CPU/GPU)进行任务调试与训练的代码文件 | |||
- 《Case2》文件夹,是基于云脑2算力资源(Ascend NPU)进行任务调试与训练的代码文件 | |||
- 《教程》文件夹,是本次项目实战的详细教程 | |||
- CIFAR.zip,是本次项目使用的数据集 | |||
## 前置条件 | |||
- Python 3.6+ | |||
- PyTorch 1.0+ | |||
## 云脑1(CPU/GPU)模型调试与训练 | |||
``` | |||
ls | |||
#(相应代码放在/code下,相应数据集放在/dataset下) | |||
cd /code/case1 | |||
python main.py | |||
``` | |||
<div align="center"> | |||
<img src= ./case1/img/train.png width=100%> | |||
</div> | |||
<br> | |||
## 云脑2(Ascend NPU)模型调试 | |||
``` | |||
#克隆代码,在代码页的HTTPS点击复制链接,在此处!git clone后面粘贴链接 | |||
!git clone | |||
#解压数据集 | |||
!unzip cifar.zip | |||
#运行代码 | |||
!python OpenI_test/case2/train.py --dataset_path ./cifar-10-batches-bin/ | |||
``` | |||
## 云脑2(Ascend NPU)模型训练 | |||
在新建训练任务中,输入指定文件为“case2/train.py”,数据集指定为cifar.zip,点击提交即可 | |||
## 部分模型准确率训练结果: | |||
| Model | Acc. | | |||
| ----------------- | ----------- | | |||
| [VGG16](https://arxiv.org/abs/1409.1556) | 92.64% | | |||
| [DLA](https://arxiv.org/pdf/1707.06484.pdf) | 95.47% | | |||
@@ -0,0 +1,21 @@ | |||
MIT License | |||
Copyright (c) 2017 liukuang | |||
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. |
@@ -0,0 +1,154 @@ | |||
'''Train CIFAR10 with PyTorch.''' | |||
import torch | |||
import torch.nn as nn | |||
import torch.optim as optim | |||
import torch.nn.functional as F | |||
import torch.backends.cudnn as cudnn | |||
import torchvision | |||
import torchvision.transforms as transforms | |||
import os | |||
import argparse | |||
from models import * | |||
from utils import progress_bar | |||
parser = argparse.ArgumentParser(description='PyTorch CIFAR10 Training') | |||
parser.add_argument('--lr', default=0.1, type=float, help='learning rate') | |||
parser.add_argument('--resume', '-r', action='store_true', | |||
help='resume from checkpoint') | |||
args = parser.parse_args() | |||
device = 'cuda' if torch.cuda.is_available() else 'cpu' | |||
best_acc = 0 # best test accuracy | |||
start_epoch = 0 # start from epoch 0 or last checkpoint epoch | |||
# Data | |||
print('==> Preparing data..') | |||
transform_train = transforms.Compose([ | |||
transforms.RandomCrop(32, padding=4), | |||
transforms.RandomHorizontalFlip(), | |||
transforms.ToTensor(), | |||
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)), | |||
]) | |||
transform_test = transforms.Compose([ | |||
transforms.ToTensor(), | |||
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)), | |||
]) | |||
trainset = torchvision.datasets.CIFAR10( | |||
root='/dataset/', train=True, download=False, transform=transform_train) | |||
trainloader = torch.utils.data.DataLoader( | |||
trainset, batch_size=128, shuffle=True, num_workers=2) | |||
testset = torchvision.datasets.CIFAR10( | |||
root='/dataset/', train=False, download=False, transform=transform_test) | |||
testloader = torch.utils.data.DataLoader( | |||
testset, batch_size=100, shuffle=False, num_workers=2) | |||
classes = ('plane', 'car', 'bird', 'cat', 'deer', | |||
'dog', 'frog', 'horse', 'ship', 'truck') | |||
# Model | |||
print('==> Building model..') | |||
# net = VGG('VGG19') | |||
# net = ResNet18() | |||
# net = PreActResNet18() | |||
# net = GoogLeNet() | |||
# net = DenseNet121() | |||
# net = ResNeXt29_2x64d() | |||
# net = MobileNet() | |||
# net = MobileNetV2() | |||
# net = DPN92() | |||
# net = ShuffleNetG2() | |||
# net = SENet18() | |||
# net = ShuffleNetV2(1) | |||
# net = EfficientNetB0() | |||
# net = RegNetX_200MF() | |||
net = SimpleDLA() | |||
net = net.to(device) | |||
if device == 'cuda': | |||
net = torch.nn.DataParallel(net) | |||
cudnn.benchmark = True | |||
if args.resume: | |||
# Load checkpoint. | |||
print('==> Resuming from checkpoint..') | |||
assert os.path.isdir('checkpoint'), 'Error: no checkpoint directory found!' | |||
checkpoint = torch.load('./checkpoint/ckpt.pth') | |||
net.load_state_dict(checkpoint['net']) | |||
best_acc = checkpoint['acc'] | |||
start_epoch = checkpoint['epoch'] | |||
criterion = nn.CrossEntropyLoss() | |||
optimizer = optim.SGD(net.parameters(), lr=args.lr, | |||
momentum=0.9, weight_decay=5e-4) | |||
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=200) | |||
# Training | |||
def train(epoch): | |||
print('\nEpoch: %d' % epoch) | |||
net.train() | |||
train_loss = 0 | |||
correct = 0 | |||
total = 0 | |||
for batch_idx, (inputs, targets) in enumerate(trainloader): | |||
inputs, targets = inputs.to(device), targets.to(device) | |||
optimizer.zero_grad() | |||
outputs = net(inputs) | |||
loss = criterion(outputs, targets) | |||
loss.backward() | |||
optimizer.step() | |||
train_loss += loss.item() | |||
_, predicted = outputs.max(1) | |||
total += targets.size(0) | |||
correct += predicted.eq(targets).sum().item() | |||
progress_bar(batch_idx, len(trainloader), 'Loss: %.3f | Acc: %.3f%% (%d/%d)' | |||
% (train_loss/(batch_idx+1), 100.*correct/total, correct, total)) | |||
def test(epoch): | |||
global best_acc | |||
net.eval() | |||
test_loss = 0 | |||
correct = 0 | |||
total = 0 | |||
with torch.no_grad(): | |||
for batch_idx, (inputs, targets) in enumerate(testloader): | |||
inputs, targets = inputs.to(device), targets.to(device) | |||
outputs = net(inputs) | |||
loss = criterion(outputs, targets) | |||
test_loss += loss.item() | |||
_, predicted = outputs.max(1) | |||
total += targets.size(0) | |||
correct += predicted.eq(targets).sum().item() | |||
progress_bar(batch_idx, len(testloader), 'Loss: %.3f | Acc: %.3f%% (%d/%d)' | |||
% (test_loss/(batch_idx+1), 100.*correct/total, correct, total)) | |||
# Save checkpoint. | |||
acc = 100.*correct/total | |||
if acc > best_acc: | |||
print('Saving..') | |||
state = { | |||
'net': net.state_dict(), | |||
'acc': acc, | |||
'epoch': epoch, | |||
} | |||
if not os.path.isdir('checkpoint'): | |||
os.mkdir('checkpoint') | |||
torch.save(state, './checkpoint/ckpt.pth') | |||
best_acc = acc | |||
for epoch in range(start_epoch, start_epoch+200): | |||
train(epoch) | |||
test(epoch) | |||
scheduler.step() |
@@ -0,0 +1,18 @@ | |||
from .vgg import * | |||
from .dpn import * | |||
from .lenet import * | |||
from .senet import * | |||
from .pnasnet import * | |||
from .densenet import * | |||
from .googlenet import * | |||
from .shufflenet import * | |||
from .shufflenetv2 import * | |||
from .resnet import * | |||
from .resnext import * | |||
from .preact_resnet import * | |||
from .mobilenet import * | |||
from .mobilenetv2 import * | |||
from .efficientnet import * | |||
from .regnet import * | |||
from .dla_simple import * | |||
from .dla import * |
@@ -0,0 +1,107 @@ | |||
'''DenseNet in PyTorch.''' | |||
import math | |||
import torch | |||
import torch.nn as nn | |||
import torch.nn.functional as F | |||
class Bottleneck(nn.Module): | |||
def __init__(self, in_planes, growth_rate): | |||
super(Bottleneck, self).__init__() | |||
self.bn1 = nn.BatchNorm2d(in_planes) | |||
self.conv1 = nn.Conv2d(in_planes, 4*growth_rate, kernel_size=1, bias=False) | |||
self.bn2 = nn.BatchNorm2d(4*growth_rate) | |||
self.conv2 = nn.Conv2d(4*growth_rate, growth_rate, kernel_size=3, padding=1, bias=False) | |||
def forward(self, x): | |||
out = self.conv1(F.relu(self.bn1(x))) | |||
out = self.conv2(F.relu(self.bn2(out))) | |||
out = torch.cat([out,x], 1) | |||
return out | |||
class Transition(nn.Module): | |||
def __init__(self, in_planes, out_planes): | |||
super(Transition, self).__init__() | |||
self.bn = nn.BatchNorm2d(in_planes) | |||
self.conv = nn.Conv2d(in_planes, out_planes, kernel_size=1, bias=False) | |||
def forward(self, x): | |||
out = self.conv(F.relu(self.bn(x))) | |||
out = F.avg_pool2d(out, 2) | |||
return out | |||
class DenseNet(nn.Module): | |||
def __init__(self, block, nblocks, growth_rate=12, reduction=0.5, num_classes=10): | |||
super(DenseNet, self).__init__() | |||
self.growth_rate = growth_rate | |||
num_planes = 2*growth_rate | |||
self.conv1 = nn.Conv2d(3, num_planes, kernel_size=3, padding=1, bias=False) | |||
self.dense1 = self._make_dense_layers(block, num_planes, nblocks[0]) | |||
num_planes += nblocks[0]*growth_rate | |||
out_planes = int(math.floor(num_planes*reduction)) | |||
self.trans1 = Transition(num_planes, out_planes) | |||
num_planes = out_planes | |||
self.dense2 = self._make_dense_layers(block, num_planes, nblocks[1]) | |||
num_planes += nblocks[1]*growth_rate | |||
out_planes = int(math.floor(num_planes*reduction)) | |||
self.trans2 = Transition(num_planes, out_planes) | |||
num_planes = out_planes | |||
self.dense3 = self._make_dense_layers(block, num_planes, nblocks[2]) | |||
num_planes += nblocks[2]*growth_rate | |||
out_planes = int(math.floor(num_planes*reduction)) | |||
self.trans3 = Transition(num_planes, out_planes) | |||
num_planes = out_planes | |||
self.dense4 = self._make_dense_layers(block, num_planes, nblocks[3]) | |||
num_planes += nblocks[3]*growth_rate | |||
self.bn = nn.BatchNorm2d(num_planes) | |||
self.linear = nn.Linear(num_planes, num_classes) | |||
def _make_dense_layers(self, block, in_planes, nblock): | |||
layers = [] | |||
for i in range(nblock): | |||
layers.append(block(in_planes, self.growth_rate)) | |||
in_planes += self.growth_rate | |||
return nn.Sequential(*layers) | |||
def forward(self, x): | |||
out = self.conv1(x) | |||
out = self.trans1(self.dense1(out)) | |||
out = self.trans2(self.dense2(out)) | |||
out = self.trans3(self.dense3(out)) | |||
out = self.dense4(out) | |||
out = F.avg_pool2d(F.relu(self.bn(out)), 4) | |||
out = out.view(out.size(0), -1) | |||
out = self.linear(out) | |||
return out | |||
def DenseNet121(): | |||
return DenseNet(Bottleneck, [6,12,24,16], growth_rate=32) | |||
def DenseNet169(): | |||
return DenseNet(Bottleneck, [6,12,32,32], growth_rate=32) | |||
def DenseNet201(): | |||
return DenseNet(Bottleneck, [6,12,48,32], growth_rate=32) | |||
def DenseNet161(): | |||
return DenseNet(Bottleneck, [6,12,36,24], growth_rate=48) | |||
def densenet_cifar(): | |||
return DenseNet(Bottleneck, [6,12,24,16], growth_rate=12) | |||
def test(): | |||
net = densenet_cifar() | |||
x = torch.randn(1,3,32,32) | |||
y = net(x) | |||
print(y) | |||
# test() |
@@ -0,0 +1,135 @@ | |||
'''DLA in PyTorch. | |||
Reference: | |||
Deep Layer Aggregation. https://arxiv.org/abs/1707.06484 | |||
''' | |||
import torch | |||
import torch.nn as nn | |||
import torch.nn.functional as F | |||
class BasicBlock(nn.Module): | |||
expansion = 1 | |||
def __init__(self, in_planes, planes, stride=1): | |||
super(BasicBlock, self).__init__() | |||
self.conv1 = nn.Conv2d( | |||
in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False) | |||
self.bn1 = nn.BatchNorm2d(planes) | |||
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, | |||
stride=1, padding=1, bias=False) | |||
self.bn2 = nn.BatchNorm2d(planes) | |||
self.shortcut = nn.Sequential() | |||
if stride != 1 or in_planes != self.expansion*planes: | |||
self.shortcut = nn.Sequential( | |||
nn.Conv2d(in_planes, self.expansion*planes, | |||
kernel_size=1, stride=stride, bias=False), | |||
nn.BatchNorm2d(self.expansion*planes) | |||
) | |||
def forward(self, x): | |||
out = F.relu(self.bn1(self.conv1(x))) | |||
out = self.bn2(self.conv2(out)) | |||
out += self.shortcut(x) | |||
out = F.relu(out) | |||
return out | |||
class Root(nn.Module): | |||
def __init__(self, in_channels, out_channels, kernel_size=1): | |||
super(Root, self).__init__() | |||
self.conv = nn.Conv2d( | |||
in_channels, out_channels, kernel_size, | |||
stride=1, padding=(kernel_size - 1) // 2, bias=False) | |||
self.bn = nn.BatchNorm2d(out_channels) | |||
def forward(self, xs): | |||
x = torch.cat(xs, 1) | |||
out = F.relu(self.bn(self.conv(x))) | |||
return out | |||
class Tree(nn.Module): | |||
def __init__(self, block, in_channels, out_channels, level=1, stride=1): | |||
super(Tree, self).__init__() | |||
self.level = level | |||
if level == 1: | |||
self.root = Root(2*out_channels, out_channels) | |||
self.left_node = block(in_channels, out_channels, stride=stride) | |||
self.right_node = block(out_channels, out_channels, stride=1) | |||
else: | |||
self.root = Root((level+2)*out_channels, out_channels) | |||
for i in reversed(range(1, level)): | |||
subtree = Tree(block, in_channels, out_channels, | |||
level=i, stride=stride) | |||
self.__setattr__('level_%d' % i, subtree) | |||
self.prev_root = block(in_channels, out_channels, stride=stride) | |||
self.left_node = block(out_channels, out_channels, stride=1) | |||
self.right_node = block(out_channels, out_channels, stride=1) | |||
def forward(self, x): | |||
xs = [self.prev_root(x)] if self.level > 1 else [] | |||
for i in reversed(range(1, self.level)): | |||
level_i = self.__getattr__('level_%d' % i) | |||
x = level_i(x) | |||
xs.append(x) | |||
x = self.left_node(x) | |||
xs.append(x) | |||
x = self.right_node(x) | |||
xs.append(x) | |||
out = self.root(xs) | |||
return out | |||
class DLA(nn.Module): | |||
def __init__(self, block=BasicBlock, num_classes=10): | |||
super(DLA, self).__init__() | |||
self.base = nn.Sequential( | |||
nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False), | |||
nn.BatchNorm2d(16), | |||
nn.ReLU(True) | |||
) | |||
self.layer1 = nn.Sequential( | |||
nn.Conv2d(16, 16, kernel_size=3, stride=1, padding=1, bias=False), | |||
nn.BatchNorm2d(16), | |||
nn.ReLU(True) | |||
) | |||
self.layer2 = nn.Sequential( | |||
nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1, bias=False), | |||
nn.BatchNorm2d(32), | |||
nn.ReLU(True) | |||
) | |||
self.layer3 = Tree(block, 32, 64, level=1, stride=1) | |||
self.layer4 = Tree(block, 64, 128, level=2, stride=2) | |||
self.layer5 = Tree(block, 128, 256, level=2, stride=2) | |||
self.layer6 = Tree(block, 256, 512, level=1, stride=2) | |||
self.linear = nn.Linear(512, num_classes) | |||
def forward(self, x): | |||
out = self.base(x) | |||
out = self.layer1(out) | |||
out = self.layer2(out) | |||
out = self.layer3(out) | |||
out = self.layer4(out) | |||
out = self.layer5(out) | |||
out = self.layer6(out) | |||
out = F.avg_pool2d(out, 4) | |||
out = out.view(out.size(0), -1) | |||
out = self.linear(out) | |||
return out | |||
def test(): | |||
net = DLA() | |||
print(net) | |||
x = torch.randn(1, 3, 32, 32) | |||
y = net(x) | |||
print(y.size()) | |||
if __name__ == '__main__': | |||
test() |
@@ -0,0 +1,128 @@ | |||
'''Simplified version of DLA in PyTorch. | |||
Note this implementation is not identical to the original paper version. | |||
But it seems works fine. | |||
See dla.py for the original paper version. | |||
Reference: | |||
Deep Layer Aggregation. https://arxiv.org/abs/1707.06484 | |||
''' | |||
import torch | |||
import torch.nn as nn | |||
import torch.nn.functional as F | |||
class BasicBlock(nn.Module): | |||
expansion = 1 | |||
def __init__(self, in_planes, planes, stride=1): | |||
super(BasicBlock, self).__init__() | |||
self.conv1 = nn.Conv2d( | |||
in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False) | |||
self.bn1 = nn.BatchNorm2d(planes) | |||
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, | |||
stride=1, padding=1, bias=False) | |||
self.bn2 = nn.BatchNorm2d(planes) | |||
self.shortcut = nn.Sequential() | |||
if stride != 1 or in_planes != self.expansion*planes: | |||
self.shortcut = nn.Sequential( | |||
nn.Conv2d(in_planes, self.expansion*planes, | |||
kernel_size=1, stride=stride, bias=False), | |||
nn.BatchNorm2d(self.expansion*planes) | |||
) | |||
def forward(self, x): | |||
out = F.relu(self.bn1(self.conv1(x))) | |||
out = self.bn2(self.conv2(out)) | |||
out += self.shortcut(x) | |||
out = F.relu(out) | |||
return out | |||
class Root(nn.Module): | |||
def __init__(self, in_channels, out_channels, kernel_size=1): | |||
super(Root, self).__init__() | |||
self.conv = nn.Conv2d( | |||
in_channels, out_channels, kernel_size, | |||
stride=1, padding=(kernel_size - 1) // 2, bias=False) | |||
self.bn = nn.BatchNorm2d(out_channels) | |||
def forward(self, xs): | |||
x = torch.cat(xs, 1) | |||
out = F.relu(self.bn(self.conv(x))) | |||
return out | |||
class Tree(nn.Module): | |||
def __init__(self, block, in_channels, out_channels, level=1, stride=1): | |||
super(Tree, self).__init__() | |||
self.root = Root(2*out_channels, out_channels) | |||
if level == 1: | |||
self.left_tree = block(in_channels, out_channels, stride=stride) | |||
self.right_tree = block(out_channels, out_channels, stride=1) | |||
else: | |||
self.left_tree = Tree(block, in_channels, | |||
out_channels, level=level-1, stride=stride) | |||
self.right_tree = Tree(block, out_channels, | |||
out_channels, level=level-1, stride=1) | |||
def forward(self, x): | |||
out1 = self.left_tree(x) | |||
out2 = self.right_tree(out1) | |||
out = self.root([out1, out2]) | |||
return out | |||
class SimpleDLA(nn.Module): | |||
def __init__(self, block=BasicBlock, num_classes=10): | |||
super(SimpleDLA, self).__init__() | |||
self.base = nn.Sequential( | |||
nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False), | |||
nn.BatchNorm2d(16), | |||
nn.ReLU(True) | |||
) | |||
self.layer1 = nn.Sequential( | |||
nn.Conv2d(16, 16, kernel_size=3, stride=1, padding=1, bias=False), | |||
nn.BatchNorm2d(16), | |||
nn.ReLU(True) | |||
) | |||
self.layer2 = nn.Sequential( | |||
nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1, bias=False), | |||
nn.BatchNorm2d(32), | |||
nn.ReLU(True) | |||
) | |||
self.layer3 = Tree(block, 32, 64, level=1, stride=1) | |||
self.layer4 = Tree(block, 64, 128, level=2, stride=2) | |||
self.layer5 = Tree(block, 128, 256, level=2, stride=2) | |||
self.layer6 = Tree(block, 256, 512, level=1, stride=2) | |||
self.linear = nn.Linear(512, num_classes) | |||
def forward(self, x): | |||
out = self.base(x) | |||
out = self.layer1(out) | |||
out = self.layer2(out) | |||
out = self.layer3(out) | |||
out = self.layer4(out) | |||
out = self.layer5(out) | |||
out = self.layer6(out) | |||
out = F.avg_pool2d(out, 4) | |||
out = out.view(out.size(0), -1) | |||
out = self.linear(out) | |||
return out | |||
def test(): | |||
net = SimpleDLA() | |||
print(net) | |||
x = torch.randn(1, 3, 32, 32) | |||
y = net(x) | |||
print(y.size()) | |||
if __name__ == '__main__': | |||
test() |
@@ -0,0 +1,98 @@ | |||
'''Dual Path Networks in PyTorch.''' | |||
import torch | |||
import torch.nn as nn | |||
import torch.nn.functional as F | |||
class Bottleneck(nn.Module): | |||
def __init__(self, last_planes, in_planes, out_planes, dense_depth, stride, first_layer): | |||
super(Bottleneck, self).__init__() | |||
self.out_planes = out_planes | |||
self.dense_depth = dense_depth | |||
self.conv1 = nn.Conv2d(last_planes, in_planes, kernel_size=1, bias=False) | |||
self.bn1 = nn.BatchNorm2d(in_planes) | |||
self.conv2 = nn.Conv2d(in_planes, in_planes, kernel_size=3, stride=stride, padding=1, groups=32, bias=False) | |||
self.bn2 = nn.BatchNorm2d(in_planes) | |||
self.conv3 = nn.Conv2d(in_planes, out_planes+dense_depth, kernel_size=1, bias=False) | |||
self.bn3 = nn.BatchNorm2d(out_planes+dense_depth) | |||
self.shortcut = nn.Sequential() | |||
if first_layer: | |||
self.shortcut = nn.Sequential( | |||
nn.Conv2d(last_planes, out_planes+dense_depth, kernel_size=1, stride=stride, bias=False), | |||
nn.BatchNorm2d(out_planes+dense_depth) | |||
) | |||
def forward(self, x): | |||
out = F.relu(self.bn1(self.conv1(x))) | |||
out = F.relu(self.bn2(self.conv2(out))) | |||
out = self.bn3(self.conv3(out)) | |||
x = self.shortcut(x) | |||
d = self.out_planes | |||
out = torch.cat([x[:,:d,:,:]+out[:,:d,:,:], x[:,d:,:,:], out[:,d:,:,:]], 1) | |||
out = F.relu(out) | |||
return out | |||
class DPN(nn.Module): | |||
def __init__(self, cfg): | |||
super(DPN, self).__init__() | |||
in_planes, out_planes = cfg['in_planes'], cfg['out_planes'] | |||
num_blocks, dense_depth = cfg['num_blocks'], cfg['dense_depth'] | |||
self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False) | |||
self.bn1 = nn.BatchNorm2d(64) | |||
self.last_planes = 64 | |||
self.layer1 = self._make_layer(in_planes[0], out_planes[0], num_blocks[0], dense_depth[0], stride=1) | |||
self.layer2 = self._make_layer(in_planes[1], out_planes[1], num_blocks[1], dense_depth[1], stride=2) | |||
self.layer3 = self._make_layer(in_planes[2], out_planes[2], num_blocks[2], dense_depth[2], stride=2) | |||
self.layer4 = self._make_layer(in_planes[3], out_planes[3], num_blocks[3], dense_depth[3], stride=2) | |||
self.linear = nn.Linear(out_planes[3]+(num_blocks[3]+1)*dense_depth[3], 10) | |||
def _make_layer(self, in_planes, out_planes, num_blocks, dense_depth, stride): | |||
strides = [stride] + [1]*(num_blocks-1) | |||
layers = [] | |||
for i,stride in enumerate(strides): | |||
layers.append(Bottleneck(self.last_planes, in_planes, out_planes, dense_depth, stride, i==0)) | |||
self.last_planes = out_planes + (i+2) * dense_depth | |||
return nn.Sequential(*layers) | |||
def forward(self, x): | |||
out = F.relu(self.bn1(self.conv1(x))) | |||
out = self.layer1(out) | |||
out = self.layer2(out) | |||
out = self.layer3(out) | |||
out = self.layer4(out) | |||
out = F.avg_pool2d(out, 4) | |||
out = out.view(out.size(0), -1) | |||
out = self.linear(out) | |||
return out | |||
def DPN26(): | |||
cfg = { | |||
'in_planes': (96,192,384,768), | |||
'out_planes': (256,512,1024,2048), | |||
'num_blocks': (2,2,2,2), | |||
'dense_depth': (16,32,24,128) | |||
} | |||
return DPN(cfg) | |||
def DPN92(): | |||
cfg = { | |||
'in_planes': (96,192,384,768), | |||
'out_planes': (256,512,1024,2048), | |||
'num_blocks': (3,4,20,3), | |||
'dense_depth': (16,32,24,128) | |||
} | |||
return DPN(cfg) | |||
def test(): | |||
net = DPN92() | |||
x = torch.randn(1,3,32,32) | |||
y = net(x) | |||
print(y) | |||
# test() |
@@ -0,0 +1,175 @@ | |||
'''EfficientNet in PyTorch. | |||
Paper: "EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks". | |||
Reference: https://github.com/keras-team/keras-applications/blob/master/keras_applications/efficientnet.py | |||
''' | |||
import torch | |||
import torch.nn as nn | |||
import torch.nn.functional as F | |||
def swish(x): | |||
return x * x.sigmoid() | |||
def drop_connect(x, drop_ratio): | |||
keep_ratio = 1.0 - drop_ratio | |||
mask = torch.empty([x.shape[0], 1, 1, 1], dtype=x.dtype, device=x.device) | |||
mask.bernoulli_(keep_ratio) | |||
x.div_(keep_ratio) | |||
x.mul_(mask) | |||
return x | |||
class SE(nn.Module): | |||
'''Squeeze-and-Excitation block with Swish.''' | |||
def __init__(self, in_channels, se_channels): | |||
super(SE, self).__init__() | |||
self.se1 = nn.Conv2d(in_channels, se_channels, | |||
kernel_size=1, bias=True) | |||
self.se2 = nn.Conv2d(se_channels, in_channels, | |||
kernel_size=1, bias=True) | |||
def forward(self, x): | |||
out = F.adaptive_avg_pool2d(x, (1, 1)) | |||
out = swish(self.se1(out)) | |||
out = self.se2(out).sigmoid() | |||
out = x * out | |||
return out | |||
class Block(nn.Module): | |||
'''expansion + depthwise + pointwise + squeeze-excitation''' | |||
def __init__(self, | |||
in_channels, | |||
out_channels, | |||
kernel_size, | |||
stride, | |||
expand_ratio=1, | |||
se_ratio=0., | |||
drop_rate=0.): | |||
super(Block, self).__init__() | |||
self.stride = stride | |||
self.drop_rate = drop_rate | |||
self.expand_ratio = expand_ratio | |||
# Expansion | |||
channels = expand_ratio * in_channels | |||
self.conv1 = nn.Conv2d(in_channels, | |||
channels, | |||
kernel_size=1, | |||
stride=1, | |||
padding=0, | |||
bias=False) | |||
self.bn1 = nn.BatchNorm2d(channels) | |||
# Depthwise conv | |||
self.conv2 = nn.Conv2d(channels, | |||
channels, | |||
kernel_size=kernel_size, | |||
stride=stride, | |||
padding=(1 if kernel_size == 3 else 2), | |||
groups=channels, | |||
bias=False) | |||
self.bn2 = nn.BatchNorm2d(channels) | |||
# SE layers | |||
se_channels = int(in_channels * se_ratio) | |||
self.se = SE(channels, se_channels) | |||
# Output | |||
self.conv3 = nn.Conv2d(channels, | |||
out_channels, | |||
kernel_size=1, | |||
stride=1, | |||
padding=0, | |||
bias=False) | |||
self.bn3 = nn.BatchNorm2d(out_channels) | |||
# Skip connection if in and out shapes are the same (MV-V2 style) | |||
self.has_skip = (stride == 1) and (in_channels == out_channels) | |||
def forward(self, x): | |||
out = x if self.expand_ratio == 1 else swish(self.bn1(self.conv1(x))) | |||
out = swish(self.bn2(self.conv2(out))) | |||
out = self.se(out) | |||
out = self.bn3(self.conv3(out)) | |||
if self.has_skip: | |||
if self.training and self.drop_rate > 0: | |||
out = drop_connect(out, self.drop_rate) | |||
out = out + x | |||
return out | |||
class EfficientNet(nn.Module): | |||
def __init__(self, cfg, num_classes=10): | |||
super(EfficientNet, self).__init__() | |||
self.cfg = cfg | |||
self.conv1 = nn.Conv2d(3, | |||
32, | |||
kernel_size=3, | |||
stride=1, | |||
padding=1, | |||
bias=False) | |||
self.bn1 = nn.BatchNorm2d(32) | |||
self.layers = self._make_layers(in_channels=32) | |||
self.linear = nn.Linear(cfg['out_channels'][-1], num_classes) | |||
def _make_layers(self, in_channels): | |||
layers = [] | |||
cfg = [self.cfg[k] for k in ['expansion', 'out_channels', 'num_blocks', 'kernel_size', | |||
'stride']] | |||
b = 0 | |||
blocks = sum(self.cfg['num_blocks']) | |||
for expansion, out_channels, num_blocks, kernel_size, stride in zip(*cfg): | |||
strides = [stride] + [1] * (num_blocks - 1) | |||
for stride in strides: | |||
drop_rate = self.cfg['drop_connect_rate'] * b / blocks | |||
layers.append( | |||
Block(in_channels, | |||
out_channels, | |||
kernel_size, | |||
stride, | |||
expansion, | |||
se_ratio=0.25, | |||
drop_rate=drop_rate)) | |||
in_channels = out_channels | |||
return nn.Sequential(*layers) | |||
def forward(self, x): | |||
out = swish(self.bn1(self.conv1(x))) | |||
out = self.layers(out) | |||
out = F.adaptive_avg_pool2d(out, 1) | |||
out = out.view(out.size(0), -1) | |||
dropout_rate = self.cfg['dropout_rate'] | |||
if self.training and dropout_rate > 0: | |||
out = F.dropout(out, p=dropout_rate) | |||
out = self.linear(out) | |||
return out | |||
def EfficientNetB0(): | |||
cfg = { | |||
'num_blocks': [1, 2, 2, 3, 3, 4, 1], | |||
'expansion': [1, 6, 6, 6, 6, 6, 6], | |||
'out_channels': [16, 24, 40, 80, 112, 192, 320], | |||
'kernel_size': [3, 3, 5, 3, 5, 5, 3], | |||
'stride': [1, 2, 2, 2, 1, 2, 1], | |||
'dropout_rate': 0.2, | |||
'drop_connect_rate': 0.2, | |||
} | |||
return EfficientNet(cfg) | |||
def test(): | |||
net = EfficientNetB0() | |||
x = torch.randn(2, 3, 32, 32) | |||
y = net(x) | |||
print(y.shape) | |||
if __name__ == '__main__': | |||
test() |
@@ -0,0 +1,107 @@ | |||
'''GoogLeNet with PyTorch.''' | |||
import torch | |||
import torch.nn as nn | |||
import torch.nn.functional as F | |||
class Inception(nn.Module): | |||
def __init__(self, in_planes, n1x1, n3x3red, n3x3, n5x5red, n5x5, pool_planes): | |||
super(Inception, self).__init__() | |||
# 1x1 conv branch | |||
self.b1 = nn.Sequential( | |||
nn.Conv2d(in_planes, n1x1, kernel_size=1), | |||
nn.BatchNorm2d(n1x1), | |||
nn.ReLU(True), | |||
) | |||
# 1x1 conv -> 3x3 conv branch | |||
self.b2 = nn.Sequential( | |||
nn.Conv2d(in_planes, n3x3red, kernel_size=1), | |||
nn.BatchNorm2d(n3x3red), | |||
nn.ReLU(True), | |||
nn.Conv2d(n3x3red, n3x3, kernel_size=3, padding=1), | |||
nn.BatchNorm2d(n3x3), | |||
nn.ReLU(True), | |||
) | |||
# 1x1 conv -> 5x5 conv branch | |||
self.b3 = nn.Sequential( | |||
nn.Conv2d(in_planes, n5x5red, kernel_size=1), | |||
nn.BatchNorm2d(n5x5red), | |||
nn.ReLU(True), | |||
nn.Conv2d(n5x5red, n5x5, kernel_size=3, padding=1), | |||
nn.BatchNorm2d(n5x5), | |||
nn.ReLU(True), | |||
nn.Conv2d(n5x5, n5x5, kernel_size=3, padding=1), | |||
nn.BatchNorm2d(n5x5), | |||
nn.ReLU(True), | |||
) | |||
# 3x3 pool -> 1x1 conv branch | |||
self.b4 = nn.Sequential( | |||
nn.MaxPool2d(3, stride=1, padding=1), | |||
nn.Conv2d(in_planes, pool_planes, kernel_size=1), | |||
nn.BatchNorm2d(pool_planes), | |||
nn.ReLU(True), | |||
) | |||
def forward(self, x): | |||
y1 = self.b1(x) | |||
y2 = self.b2(x) | |||
y3 = self.b3(x) | |||
y4 = self.b4(x) | |||
return torch.cat([y1,y2,y3,y4], 1) | |||
class GoogLeNet(nn.Module): | |||
def __init__(self): | |||
super(GoogLeNet, self).__init__() | |||
self.pre_layers = nn.Sequential( | |||
nn.Conv2d(3, 192, kernel_size=3, padding=1), | |||
nn.BatchNorm2d(192), | |||
nn.ReLU(True), | |||
) | |||
self.a3 = Inception(192, 64, 96, 128, 16, 32, 32) | |||
self.b3 = Inception(256, 128, 128, 192, 32, 96, 64) | |||
self.maxpool = nn.MaxPool2d(3, stride=2, padding=1) | |||
self.a4 = Inception(480, 192, 96, 208, 16, 48, 64) | |||
self.b4 = Inception(512, 160, 112, 224, 24, 64, 64) | |||
self.c4 = Inception(512, 128, 128, 256, 24, 64, 64) | |||
self.d4 = Inception(512, 112, 144, 288, 32, 64, 64) | |||
self.e4 = Inception(528, 256, 160, 320, 32, 128, 128) | |||
self.a5 = Inception(832, 256, 160, 320, 32, 128, 128) | |||
self.b5 = Inception(832, 384, 192, 384, 48, 128, 128) | |||
self.avgpool = nn.AvgPool2d(8, stride=1) | |||
self.linear = nn.Linear(1024, 10) | |||
def forward(self, x): | |||
out = self.pre_layers(x) | |||
out = self.a3(out) | |||
out = self.b3(out) | |||
out = self.maxpool(out) | |||
out = self.a4(out) | |||
out = self.b4(out) | |||
out = self.c4(out) | |||
out = self.d4(out) | |||
out = self.e4(out) | |||
out = self.maxpool(out) | |||
out = self.a5(out) | |||
out = self.b5(out) | |||
out = self.avgpool(out) | |||
out = out.view(out.size(0), -1) | |||
out = self.linear(out) | |||
return out | |||
def test(): | |||
net = GoogLeNet() | |||
x = torch.randn(1,3,32,32) | |||
y = net(x) | |||
print(y.size()) | |||
# test() |
@@ -0,0 +1,23 @@ | |||
'''LeNet in PyTorch.''' | |||
import torch.nn as nn | |||
import torch.nn.functional as F | |||
class LeNet(nn.Module): | |||
def __init__(self): | |||
super(LeNet, self).__init__() | |||
self.conv1 = nn.Conv2d(3, 6, 5) | |||
self.conv2 = nn.Conv2d(6, 16, 5) | |||
self.fc1 = nn.Linear(16*5*5, 120) | |||
self.fc2 = nn.Linear(120, 84) | |||
self.fc3 = nn.Linear(84, 10) | |||
def forward(self, x): | |||
out = F.relu(self.conv1(x)) | |||
out = F.max_pool2d(out, 2) | |||
out = F.relu(self.conv2(out)) | |||
out = F.max_pool2d(out, 2) | |||
out = out.view(out.size(0), -1) | |||
out = F.relu(self.fc1(out)) | |||
out = F.relu(self.fc2(out)) | |||
out = self.fc3(out) | |||
return out |
@@ -0,0 +1,61 @@ | |||
'''MobileNet in PyTorch. | |||
See the paper "MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications" | |||
for more details. | |||
''' | |||
import torch | |||
import torch.nn as nn | |||
import torch.nn.functional as F | |||
class Block(nn.Module): | |||
'''Depthwise conv + Pointwise conv''' | |||
def __init__(self, in_planes, out_planes, stride=1): | |||
super(Block, self).__init__() | |||
self.conv1 = nn.Conv2d(in_planes, in_planes, kernel_size=3, stride=stride, padding=1, groups=in_planes, bias=False) | |||
self.bn1 = nn.BatchNorm2d(in_planes) | |||
self.conv2 = nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=1, padding=0, bias=False) | |||
self.bn2 = nn.BatchNorm2d(out_planes) | |||
def forward(self, x): | |||
out = F.relu(self.bn1(self.conv1(x))) | |||
out = F.relu(self.bn2(self.conv2(out))) | |||
return out | |||
class MobileNet(nn.Module): | |||
# (128,2) means conv planes=128, conv stride=2, by default conv stride=1 | |||
cfg = [64, (128,2), 128, (256,2), 256, (512,2), 512, 512, 512, 512, 512, (1024,2), 1024] | |||
def __init__(self, num_classes=10): | |||
super(MobileNet, self).__init__() | |||
self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1, bias=False) | |||
self.bn1 = nn.BatchNorm2d(32) | |||
self.layers = self._make_layers(in_planes=32) | |||
self.linear = nn.Linear(1024, num_classes) | |||
def _make_layers(self, in_planes): | |||
layers = [] | |||
for x in self.cfg: | |||
out_planes = x if isinstance(x, int) else x[0] | |||
stride = 1 if isinstance(x, int) else x[1] | |||
layers.append(Block(in_planes, out_planes, stride)) | |||
in_planes = out_planes | |||
return nn.Sequential(*layers) | |||
def forward(self, x): | |||
out = F.relu(self.bn1(self.conv1(x))) | |||
out = self.layers(out) | |||
out = F.avg_pool2d(out, 2) | |||
out = out.view(out.size(0), -1) | |||
out = self.linear(out) | |||
return out | |||
def test(): | |||
net = MobileNet() | |||
x = torch.randn(1,3,32,32) | |||
y = net(x) | |||
print(y.size()) | |||
# test() |
@@ -0,0 +1,86 @@ | |||
'''MobileNetV2 in PyTorch. | |||
See the paper "Inverted Residuals and Linear Bottlenecks: | |||
Mobile Networks for Classification, Detection and Segmentation" for more details. | |||
''' | |||
import torch | |||
import torch.nn as nn | |||
import torch.nn.functional as F | |||
class Block(nn.Module): | |||
'''expand + depthwise + pointwise''' | |||
def __init__(self, in_planes, out_planes, expansion, stride): | |||
super(Block, self).__init__() | |||
self.stride = stride | |||
planes = expansion * in_planes | |||
self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=1, stride=1, padding=0, bias=False) | |||
self.bn1 = nn.BatchNorm2d(planes) | |||
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, groups=planes, bias=False) | |||
self.bn2 = nn.BatchNorm2d(planes) | |||
self.conv3 = nn.Conv2d(planes, out_planes, kernel_size=1, stride=1, padding=0, bias=False) | |||
self.bn3 = nn.BatchNorm2d(out_planes) | |||
self.shortcut = nn.Sequential() | |||
if stride == 1 and in_planes != out_planes: | |||
self.shortcut = nn.Sequential( | |||
nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=1, padding=0, bias=False), | |||
nn.BatchNorm2d(out_planes), | |||
) | |||
def forward(self, x): | |||
out = F.relu(self.bn1(self.conv1(x))) | |||
out = F.relu(self.bn2(self.conv2(out))) | |||
out = self.bn3(self.conv3(out)) | |||
out = out + self.shortcut(x) if self.stride==1 else out | |||
return out | |||
class MobileNetV2(nn.Module): | |||
# (expansion, out_planes, num_blocks, stride) | |||
cfg = [(1, 16, 1, 1), | |||
(6, 24, 2, 1), # NOTE: change stride 2 -> 1 for CIFAR10 | |||
(6, 32, 3, 2), | |||
(6, 64, 4, 2), | |||
(6, 96, 3, 1), | |||
(6, 160, 3, 2), | |||
(6, 320, 1, 1)] | |||
def __init__(self, num_classes=10): | |||
super(MobileNetV2, self).__init__() | |||
# NOTE: change conv1 stride 2 -> 1 for CIFAR10 | |||
self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1, bias=False) | |||
self.bn1 = nn.BatchNorm2d(32) | |||
self.layers = self._make_layers(in_planes=32) | |||
self.conv2 = nn.Conv2d(320, 1280, kernel_size=1, stride=1, padding=0, bias=False) | |||
self.bn2 = nn.BatchNorm2d(1280) | |||
self.linear = nn.Linear(1280, num_classes) | |||
def _make_layers(self, in_planes): | |||
layers = [] | |||
for expansion, out_planes, num_blocks, stride in self.cfg: | |||
strides = [stride] + [1]*(num_blocks-1) | |||
for stride in strides: | |||
layers.append(Block(in_planes, out_planes, expansion, stride)) | |||
in_planes = out_planes | |||
return nn.Sequential(*layers) | |||
def forward(self, x): | |||
out = F.relu(self.bn1(self.conv1(x))) | |||
out = self.layers(out) | |||
out = F.relu(self.bn2(self.conv2(out))) | |||
# NOTE: change pooling kernel_size 7 -> 4 for CIFAR10 | |||
out = F.avg_pool2d(out, 4) | |||
out = out.view(out.size(0), -1) | |||
out = self.linear(out) | |||
return out | |||
def test(): | |||
net = MobileNetV2() | |||
x = torch.randn(2,3,32,32) | |||
y = net(x) | |||
print(y.size()) | |||
# test() |
@@ -0,0 +1,125 @@ | |||
'''PNASNet in PyTorch. | |||
Paper: Progressive Neural Architecture Search | |||
''' | |||
import torch | |||
import torch.nn as nn | |||
import torch.nn.functional as F | |||
class SepConv(nn.Module): | |||
'''Separable Convolution.''' | |||
def __init__(self, in_planes, out_planes, kernel_size, stride): | |||
super(SepConv, self).__init__() | |||
self.conv1 = nn.Conv2d(in_planes, out_planes, | |||
kernel_size, stride, | |||
padding=(kernel_size-1)//2, | |||
bias=False, groups=in_planes) | |||
self.bn1 = nn.BatchNorm2d(out_planes) | |||
def forward(self, x): | |||
return self.bn1(self.conv1(x)) | |||
class CellA(nn.Module): | |||
def __init__(self, in_planes, out_planes, stride=1): | |||
super(CellA, self).__init__() | |||
self.stride = stride | |||
self.sep_conv1 = SepConv(in_planes, out_planes, kernel_size=7, stride=stride) | |||
if stride==2: | |||
self.conv1 = nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=1, padding=0, bias=False) | |||
self.bn1 = nn.BatchNorm2d(out_planes) | |||
def forward(self, x): | |||
y1 = self.sep_conv1(x) | |||
y2 = F.max_pool2d(x, kernel_size=3, stride=self.stride, padding=1) | |||
if self.stride==2: | |||
y2 = self.bn1(self.conv1(y2)) | |||
return F.relu(y1+y2) | |||
class CellB(nn.Module): | |||
def __init__(self, in_planes, out_planes, stride=1): | |||
super(CellB, self).__init__() | |||
self.stride = stride | |||
# Left branch | |||
self.sep_conv1 = SepConv(in_planes, out_planes, kernel_size=7, stride=stride) | |||
self.sep_conv2 = SepConv(in_planes, out_planes, kernel_size=3, stride=stride) | |||
# Right branch | |||
self.sep_conv3 = SepConv(in_planes, out_planes, kernel_size=5, stride=stride) | |||
if stride==2: | |||
self.conv1 = nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=1, padding=0, bias=False) | |||
self.bn1 = nn.BatchNorm2d(out_planes) | |||
# Reduce channels | |||
self.conv2 = nn.Conv2d(2*out_planes, out_planes, kernel_size=1, stride=1, padding=0, bias=False) | |||
self.bn2 = nn.BatchNorm2d(out_planes) | |||
def forward(self, x): | |||
# Left branch | |||
y1 = self.sep_conv1(x) | |||
y2 = self.sep_conv2(x) | |||
# Right branch | |||
y3 = F.max_pool2d(x, kernel_size=3, stride=self.stride, padding=1) | |||
if self.stride==2: | |||
y3 = self.bn1(self.conv1(y3)) | |||
y4 = self.sep_conv3(x) | |||
# Concat & reduce channels | |||
b1 = F.relu(y1+y2) | |||
b2 = F.relu(y3+y4) | |||
y = torch.cat([b1,b2], 1) | |||
return F.relu(self.bn2(self.conv2(y))) | |||
class PNASNet(nn.Module): | |||
def __init__(self, cell_type, num_cells, num_planes): | |||
super(PNASNet, self).__init__() | |||
self.in_planes = num_planes | |||
self.cell_type = cell_type | |||
self.conv1 = nn.Conv2d(3, num_planes, kernel_size=3, stride=1, padding=1, bias=False) | |||
self.bn1 = nn.BatchNorm2d(num_planes) | |||
self.layer1 = self._make_layer(num_planes, num_cells=6) | |||
self.layer2 = self._downsample(num_planes*2) | |||
self.layer3 = self._make_layer(num_planes*2, num_cells=6) | |||
self.layer4 = self._downsample(num_planes*4) | |||
self.layer5 = self._make_layer(num_planes*4, num_cells=6) | |||
self.linear = nn.Linear(num_planes*4, 10) | |||
def _make_layer(self, planes, num_cells): | |||
layers = [] | |||
for _ in range(num_cells): | |||
layers.append(self.cell_type(self.in_planes, planes, stride=1)) | |||
self.in_planes = planes | |||
return nn.Sequential(*layers) | |||
def _downsample(self, planes): | |||
layer = self.cell_type(self.in_planes, planes, stride=2) | |||
self.in_planes = planes | |||
return layer | |||
def forward(self, x): | |||
out = F.relu(self.bn1(self.conv1(x))) | |||
out = self.layer1(out) | |||
out = self.layer2(out) | |||
out = self.layer3(out) | |||
out = self.layer4(out) | |||
out = self.layer5(out) | |||
out = F.avg_pool2d(out, 8) | |||
out = self.linear(out.view(out.size(0), -1)) | |||
return out | |||
def PNASNetA(): | |||
return PNASNet(CellA, num_cells=6, num_planes=44) | |||
def PNASNetB(): | |||
return PNASNet(CellB, num_cells=6, num_planes=32) | |||
def test(): | |||
net = PNASNetB() | |||
x = torch.randn(1,3,32,32) | |||
y = net(x) | |||
print(y) | |||
# test() |
@@ -0,0 +1,118 @@ | |||
'''Pre-activation ResNet in PyTorch. | |||
Reference: | |||
[1] Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun | |||
Identity Mappings in Deep Residual Networks. arXiv:1603.05027 | |||
''' | |||
import torch | |||
import torch.nn as nn | |||
import torch.nn.functional as F | |||
class PreActBlock(nn.Module): | |||
'''Pre-activation version of the BasicBlock.''' | |||
expansion = 1 | |||
def __init__(self, in_planes, planes, stride=1): | |||
super(PreActBlock, self).__init__() | |||
self.bn1 = nn.BatchNorm2d(in_planes) | |||
self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False) | |||
self.bn2 = nn.BatchNorm2d(planes) | |||
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False) | |||
if stride != 1 or in_planes != self.expansion*planes: | |||
self.shortcut = nn.Sequential( | |||
nn.Conv2d(in_planes, self.expansion*planes, kernel_size=1, stride=stride, bias=False) | |||
) | |||
def forward(self, x): | |||
out = F.relu(self.bn1(x)) | |||
shortcut = self.shortcut(out) if hasattr(self, 'shortcut') else x | |||
out = self.conv1(out) | |||
out = self.conv2(F.relu(self.bn2(out))) | |||
out += shortcut | |||
return out | |||
class PreActBottleneck(nn.Module): | |||
'''Pre-activation version of the original Bottleneck module.''' | |||
expansion = 4 | |||
def __init__(self, in_planes, planes, stride=1): | |||
super(PreActBottleneck, self).__init__() | |||
self.bn1 = nn.BatchNorm2d(in_planes) | |||
self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=1, bias=False) | |||
self.bn2 = nn.BatchNorm2d(planes) | |||
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False) | |||
self.bn3 = nn.BatchNorm2d(planes) | |||
self.conv3 = nn.Conv2d(planes, self.expansion*planes, kernel_size=1, bias=False) | |||
if stride != 1 or in_planes != self.expansion*planes: | |||
self.shortcut = nn.Sequential( | |||
nn.Conv2d(in_planes, self.expansion*planes, kernel_size=1, stride=stride, bias=False) | |||
) | |||
def forward(self, x): | |||
out = F.relu(self.bn1(x)) | |||
shortcut = self.shortcut(out) if hasattr(self, 'shortcut') else x | |||
out = self.conv1(out) | |||
out = self.conv2(F.relu(self.bn2(out))) | |||
out = self.conv3(F.relu(self.bn3(out))) | |||
out += shortcut | |||
return out | |||
class PreActResNet(nn.Module): | |||
def __init__(self, block, num_blocks, num_classes=10): | |||
super(PreActResNet, self).__init__() | |||
self.in_planes = 64 | |||
self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False) | |||
self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1) | |||
self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2) | |||
self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2) | |||
self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2) | |||
self.linear = nn.Linear(512*block.expansion, num_classes) | |||
def _make_layer(self, block, planes, num_blocks, stride): | |||
strides = [stride] + [1]*(num_blocks-1) | |||
layers = [] | |||
for stride in strides: | |||
layers.append(block(self.in_planes, planes, stride)) | |||
self.in_planes = planes * block.expansion | |||
return nn.Sequential(*layers) | |||
def forward(self, x): | |||
out = self.conv1(x) | |||
out = self.layer1(out) | |||
out = self.layer2(out) | |||
out = self.layer3(out) | |||
out = self.layer4(out) | |||
out = F.avg_pool2d(out, 4) | |||
out = out.view(out.size(0), -1) | |||
out = self.linear(out) | |||
return out | |||
def PreActResNet18(): | |||
return PreActResNet(PreActBlock, [2,2,2,2]) | |||
def PreActResNet34(): | |||
return PreActResNet(PreActBlock, [3,4,6,3]) | |||
def PreActResNet50(): | |||
return PreActResNet(PreActBottleneck, [3,4,6,3]) | |||
def PreActResNet101(): | |||
return PreActResNet(PreActBottleneck, [3,4,23,3]) | |||
def PreActResNet152(): | |||
return PreActResNet(PreActBottleneck, [3,8,36,3]) | |||
def test(): | |||
net = PreActResNet18() | |||
y = net((torch.randn(1,3,32,32))) | |||
print(y.size()) | |||
# test() |
@@ -0,0 +1,155 @@ | |||
'''RegNet in PyTorch. | |||
Paper: "Designing Network Design Spaces". | |||
Reference: https://github.com/keras-team/keras-applications/blob/master/keras_applications/efficientnet.py | |||
''' | |||
import torch | |||
import torch.nn as nn | |||
import torch.nn.functional as F | |||
class SE(nn.Module): | |||
'''Squeeze-and-Excitation block.''' | |||
def __init__(self, in_planes, se_planes): | |||
super(SE, self).__init__() | |||
self.se1 = nn.Conv2d(in_planes, se_planes, kernel_size=1, bias=True) | |||
self.se2 = nn.Conv2d(se_planes, in_planes, kernel_size=1, bias=True) | |||
def forward(self, x): | |||
out = F.adaptive_avg_pool2d(x, (1, 1)) | |||
out = F.relu(self.se1(out)) | |||
out = self.se2(out).sigmoid() | |||
out = x * out | |||
return out | |||
class Block(nn.Module): | |||
def __init__(self, w_in, w_out, stride, group_width, bottleneck_ratio, se_ratio): | |||
super(Block, self).__init__() | |||
# 1x1 | |||
w_b = int(round(w_out * bottleneck_ratio)) | |||
self.conv1 = nn.Conv2d(w_in, w_b, kernel_size=1, bias=False) | |||
self.bn1 = nn.BatchNorm2d(w_b) | |||
# 3x3 | |||
num_groups = w_b // group_width | |||
self.conv2 = nn.Conv2d(w_b, w_b, kernel_size=3, | |||
stride=stride, padding=1, groups=num_groups, bias=False) | |||
self.bn2 = nn.BatchNorm2d(w_b) | |||
# se | |||
self.with_se = se_ratio > 0 | |||
if self.with_se: | |||
w_se = int(round(w_in * se_ratio)) | |||
self.se = SE(w_b, w_se) | |||
# 1x1 | |||
self.conv3 = nn.Conv2d(w_b, w_out, kernel_size=1, bias=False) | |||