Skip to content

Commit b2a814c

Browse files
author
Arthur Douillard
committed
[icarl] Huge ugly commit, reached icarl paper performances.
1 parent 9688a80 commit b2a814c

26 files changed

+1699
-588
lines changed

inclearn/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from inclearn import data, factory, models, resnet, utils, results_utils
1+
from inclearn import parser, train

inclearn/__main__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66

77
if args["seed_range"] is not None:
88
args["seed"] = list(range(args["seed_range"][0], args["seed_range"][1] + 1))
9+
print("Seed range", args["seed"])
910

1011
train(args)

inclearn/convnet/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import cifar_resnet, densenet, my_resnet, resnet

inclearn/convnet/cifar_resnet.py

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
''' Incremental-Classifier Learning
2+
Authors : Khurram Javed, Muhammad Talha Paracha
3+
Maintainer : Khurram Javed
4+
Lab : TUKL-SEECS R&D Lab
5+
Email : [email protected] '''
6+
7+
import math
8+
9+
import torch
10+
import torch.nn as nn
11+
import torch.nn.functional as F
12+
from torch.nn import init
13+
14+
15+
class DownsampleA(nn.Module):
16+
def __init__(self, nIn, nOut, stride):
17+
super(DownsampleA, self).__init__()
18+
assert stride == 2
19+
self.avg = nn.AvgPool2d(kernel_size=1, stride=stride)
20+
21+
def forward(self, x):
22+
x = self.avg(x)
23+
return torch.cat((x, x.mul(0)), 1)
24+
25+
26+
class ResNetBasicblock(nn.Module):
27+
expansion = 1
28+
"""
29+
RexNet basicblock (https://github.com/facebook/fb.resnet.torch/blob/master/models/resnet.lua)
30+
"""
31+
32+
def __init__(self, inplanes, planes, stride=1, downsample=None):
33+
super(ResNetBasicblock, self).__init__()
34+
35+
self.conv_a = nn.Conv2d(inplanes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
36+
self.bn_a = nn.BatchNorm2d(planes)
37+
38+
self.conv_b = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
39+
self.bn_b = nn.BatchNorm2d(planes)
40+
41+
self.downsample = downsample
42+
self.featureSize = 64
43+
44+
def forward(self, x):
45+
residual = x
46+
47+
basicblock = self.conv_a(x)
48+
basicblock = self.bn_a(basicblock)
49+
basicblock = F.relu(basicblock, inplace=True)
50+
51+
basicblock = self.conv_b(basicblock)
52+
basicblock = self.bn_b(basicblock)
53+
54+
if self.downsample is not None:
55+
residual = self.downsample(x)
56+
57+
return F.relu(residual + basicblock, inplace=True)
58+
59+
60+
class CifarResNet(nn.Module):
61+
"""
62+
ResNet optimized for the Cifar Dataset, as specified in
63+
https://arxiv.org/abs/1512.03385.pdf
64+
"""
65+
66+
def __init__(self, block, depth, num_classes, channels=3):
67+
""" Constructor
68+
Args:
69+
depth: number of layers.
70+
num_classes: number of classes
71+
base_width: base width
72+
"""
73+
super(CifarResNet, self).__init__()
74+
75+
self.featureSize = 64
76+
# Model type specifies number of layers for CIFAR-10 and CIFAR-100 model
77+
assert (depth - 2) % 6 == 0, 'depth should be one of 20, 32, 44, 56, 110'
78+
layer_blocks = (depth - 2) // 6
79+
80+
self.num_classes = num_classes
81+
82+
self.conv_1_3x3 = nn.Conv2d(channels, 16, kernel_size=3, stride=1, padding=1, bias=False)
83+
self.bn_1 = nn.BatchNorm2d(16)
84+
85+
self.inplanes = 16
86+
self.stage_1 = self._make_layer(block, 16, layer_blocks, 1)
87+
self.stage_2 = self._make_layer(block, 32, layer_blocks, 2)
88+
self.stage_3 = self._make_layer(block, 64, layer_blocks, 2)
89+
self.avgpool = nn.AvgPool2d(8)
90+
self.out_dim = 64 * block.expansion
91+
92+
for m in self.modules():
93+
if isinstance(m, nn.Conv2d):
94+
n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
95+
m.weight.data.normal_(0, math.sqrt(2. / n))
96+
# m.bias.data.zero_()
97+
elif isinstance(m, nn.BatchNorm2d):
98+
m.weight.data.fill_(1)
99+
m.bias.data.zero_()
100+
elif isinstance(m, nn.Linear):
101+
init.kaiming_normal(m.weight)
102+
m.bias.data.zero_()
103+
104+
def _make_layer(self, block, planes, blocks, stride=1):
105+
downsample = None
106+
if stride != 1 or self.inplanes != planes * block.expansion:
107+
downsample = DownsampleA(self.inplanes, planes * block.expansion, stride)
108+
109+
layers = []
110+
layers.append(block(self.inplanes, planes, stride, downsample))
111+
self.inplanes = planes * block.expansion
112+
for i in range(1, blocks):
113+
layers.append(block(self.inplanes, planes))
114+
115+
return nn.Sequential(*layers)
116+
117+
def forward(self, x, feature=False, T=1, labels=False, scale=None, keep=None):
118+
119+
x = self.conv_1_3x3(x)
120+
x = F.relu(self.bn_1(x), inplace=True)
121+
x = self.stage_1(x)
122+
x = self.stage_2(x)
123+
x = self.stage_3(x)
124+
x = self.avgpool(x)
125+
x = x.view(x.size(0), -1)
126+
return x
127+
128+
def forwardFeature(self, x):
129+
pass
130+
131+
132+
def resnet20(num_classes=10):
133+
"""Constructs a ResNet-20 model for CIFAR-10 (by default)
134+
Args:
135+
num_classes (uint): number of classes
136+
"""
137+
model = CifarResNet(ResNetBasicblock, 20, num_classes)
138+
return model
139+
140+
141+
def resnet10mnist(num_classes=10):
142+
"""Constructs a ResNet-20 model for CIFAR-10 (by default)
143+
Args:
144+
num_classes (uint): number of classes
145+
"""
146+
model = CifarResNet(ResNetBasicblock, 10, num_classes, 1)
147+
return model
148+
149+
150+
def resnet20mnist(num_classes=10):
151+
"""Constructs a ResNet-20 model for CIFAR-10 (by default)
152+
Args:
153+
num_classes (uint): number of classes
154+
"""
155+
model = CifarResNet(ResNetBasicblock, 20, num_classes, 1)
156+
return model
157+
158+
159+
def resnet32mnist(num_classes=10, channels=1):
160+
model = CifarResNet(ResNetBasicblock, 32, num_classes, channels)
161+
return model
162+
163+
164+
def resnet32(num_classes=10):
165+
"""Constructs a ResNet-32 model for CIFAR-10 (by default)
166+
Args:
167+
num_classes (uint): number of classes
168+
"""
169+
model = CifarResNet(ResNetBasicblock, 32, num_classes)
170+
return model
171+
172+
173+
def resnet44(num_classes=10):
174+
"""Constructs a ResNet-44 model for CIFAR-10 (by default)
175+
Args:
176+
num_classes (uint): number of classes
177+
"""
178+
model = CifarResNet(ResNetBasicblock, 44, num_classes)
179+
return model
180+
181+
182+
def resnet56(num_classes=10):
183+
"""Constructs a ResNet-56 model for CIFAR-10 (by default)
184+
Args:
185+
num_classes (uint): number of classes
186+
"""
187+
model = CifarResNet(ResNetBasicblock, 56, num_classes)
188+
return model
189+
190+
191+
def resnet110(num_classes=10):
192+
"""Constructs a ResNet-110 model for CIFAR-10 (by default)
193+
Args:
194+
num_classes (uint): number of classes
195+
"""
196+
model = CifarResNet(ResNetBasicblock, 110, num_classes)
197+
return model

inclearn/convnet/densenet.py

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import re
2+
from collections import OrderedDict
3+
4+
import torch
5+
import torch.nn as nn
6+
import torch.nn.functional as F
7+
8+
__all__ = ['DenseNet', 'densenet121', 'densenet169', 'densenet201', 'densenet161']
9+
10+
model_urls = {
11+
'densenet121': 'https://download.pytorch.org/models/densenet121-a639ec97.pth',
12+
'densenet169': 'https://download.pytorch.org/models/densenet169-b2777c0a.pth',
13+
'densenet201': 'https://download.pytorch.org/models/densenet201-c1103571.pth',
14+
'densenet161': 'https://download.pytorch.org/models/densenet161-8d451a50.pth',
15+
}
16+
17+
18+
class _DenseLayer(nn.Sequential):
19+
def __init__(self, num_input_features, growth_rate, bn_size, drop_rate):
20+
super(_DenseLayer, self).__init__()
21+
self.add_module('norm1', nn.BatchNorm2d(num_input_features)),
22+
self.add_module('relu1', nn.ReLU(inplace=True)),
23+
self.add_module('conv1', nn.Conv2d(num_input_features, bn_size *
24+
growth_rate, kernel_size=1, stride=1,
25+
bias=False)),
26+
self.add_module('norm2', nn.BatchNorm2d(bn_size * growth_rate)),
27+
self.add_module('relu2', nn.ReLU(inplace=True)),
28+
self.add_module('conv2', nn.Conv2d(bn_size * growth_rate, growth_rate,
29+
kernel_size=3, stride=1, padding=1,
30+
bias=False)),
31+
self.drop_rate = drop_rate
32+
33+
def forward(self, x):
34+
new_features = super(_DenseLayer, self).forward(x)
35+
if self.drop_rate > 0:
36+
new_features = F.dropout(new_features, p=self.drop_rate,
37+
training=self.training)
38+
return torch.cat([x, new_features], 1)
39+
40+
41+
class _DenseBlock(nn.Sequential):
42+
def __init__(self, num_layers, num_input_features, bn_size, growth_rate, drop_rate):
43+
super(_DenseBlock, self).__init__()
44+
for i in range(num_layers):
45+
layer = _DenseLayer(num_input_features + i * growth_rate, growth_rate,
46+
bn_size, drop_rate)
47+
self.add_module('denselayer%d' % (i + 1), layer)
48+
49+
50+
class _Transition(nn.Sequential):
51+
def __init__(self, num_input_features, num_output_features):
52+
super(_Transition, self).__init__()
53+
self.add_module('norm', nn.BatchNorm2d(num_input_features))
54+
self.add_module('relu', nn.ReLU(inplace=True))
55+
self.add_module('conv', nn.Conv2d(num_input_features, num_output_features,
56+
kernel_size=1, stride=1, bias=False))
57+
self.add_module('pool', nn.AvgPool2d(kernel_size=2, stride=2))
58+
59+
60+
class DenseNet(nn.Module):
61+
r"""Densenet-BC model class, based on
62+
`"Densely Connected Convolutional Networks" <https://arxiv.org/pdf/1608.06993.pdf>`_
63+
64+
Args:
65+
growth_rate (int) - how many filters to add each layer (`k` in paper)
66+
block_config (list of 4 ints) - how many layers in each pooling block
67+
num_init_features (int) - the number of filters to learn in the first convolution layer
68+
bn_size (int) - multiplicative factor for number of bottle neck layers
69+
(i.e. bn_size * k features in the bottleneck layer)
70+
drop_rate (float) - dropout rate after each dense layer
71+
num_classes (int) - number of classification classes
72+
"""
73+
74+
def __init__(self, growth_rate=32, block_config=(6, 12, 24, 16),
75+
num_init_features=64, bn_size=4, drop_rate=0, num_classes=1000,
76+
**kwargs):
77+
78+
super(DenseNet, self).__init__()
79+
80+
# First convolution
81+
self.features = nn.Sequential(OrderedDict([
82+
('conv0', nn.Conv2d(3, num_init_features, kernel_size=7, stride=2,
83+
padding=3, bias=False)),
84+
('norm0', nn.BatchNorm2d(num_init_features)),
85+
('relu0', nn.ReLU(inplace=True)),
86+
('pool0', nn.MaxPool2d(kernel_size=3, stride=2, padding=1)),
87+
]))
88+
89+
# Each denseblock
90+
num_features = num_init_features
91+
for i, num_layers in enumerate(block_config):
92+
block = _DenseBlock(num_layers=num_layers, num_input_features=num_features,
93+
bn_size=bn_size, growth_rate=growth_rate,
94+
drop_rate=drop_rate)
95+
self.features.add_module('denseblock%d' % (i + 1), block)
96+
num_features = num_features + num_layers * growth_rate
97+
if i != len(block_config) - 1:
98+
trans = _Transition(num_input_features=num_features,
99+
num_output_features=num_features // 2)
100+
self.features.add_module('transition%d' % (i + 1), trans)
101+
num_features = num_features // 2
102+
103+
# Final batch norm
104+
self.features.add_module('norm5', nn.BatchNorm2d(num_features))
105+
106+
self.out_dim = num_features
107+
108+
# Official init from torch repo.
109+
for m in self.modules():
110+
if isinstance(m, nn.Conv2d):
111+
nn.init.kaiming_normal_(m.weight)
112+
elif isinstance(m, nn.BatchNorm2d):
113+
nn.init.constant_(m.weight, 1)
114+
nn.init.constant_(m.bias, 0)
115+
elif isinstance(m, nn.Linear):
116+
nn.init.constant_(m.bias, 0)
117+
118+
def forward(self, x):
119+
features = self.features(x)
120+
out = F.relu(features, inplace=True)
121+
out = F.adaptive_avg_pool2d(out, (1, 1)).view(features.size(0), -1)
122+
return out
123+
124+
125+
def _load_state_dict(model, model_url, progress):
126+
pass
127+
128+
def _densenet(arch, growth_rate, block_config, num_init_features, pretrained, progress,
129+
**kwargs):
130+
model = DenseNet(growth_rate, block_config, num_init_features, **kwargs)
131+
if pretrained:
132+
_load_state_dict(model, model_urls[arch], progress)
133+
return model
134+
135+
136+
def densenet121(pretrained=False, progress=True, **kwargs):
137+
r"""Densenet-121 model from
138+
`"Densely Connected Convolutional Networks" <https://arxiv.org/pdf/1608.06993.pdf>`_
139+
140+
Args:
141+
pretrained (bool): If True, returns a model pre-trained on ImageNet
142+
progress (bool): If True, displays a progress bar of the download to stderr
143+
"""
144+
return _densenet('densenet121', 32, (6, 12, 24, 16), 64, pretrained, progress,
145+
**kwargs)
146+
147+
148+
def densenet161(pretrained=False, progress=True, **kwargs):
149+
r"""Densenet-161 model from
150+
`"Densely Connected Convolutional Networks" <https://arxiv.org/pdf/1608.06993.pdf>`_
151+
152+
Args:
153+
pretrained (bool): If True, returns a model pre-trained on ImageNet
154+
progress (bool): If True, displays a progress bar of the download to stderr
155+
"""
156+
return _densenet('densenet161', 48, (6, 12, 36, 24), 96, pretrained, progress,
157+
**kwargs)
158+
159+
160+
def densenet169(pretrained=False, progress=True, **kwargs):
161+
r"""Densenet-169 model from
162+
`"Densely Connected Convolutional Networks" <https://arxiv.org/pdf/1608.06993.pdf>`_
163+
164+
Args:
165+
pretrained (bool): If True, returns a model pre-trained on ImageNet
166+
progress (bool): If True, displays a progress bar of the download to stderr
167+
"""
168+
return _densenet('densenet169', 32, (6, 12, 32, 32), 64, pretrained, progress,
169+
**kwargs)
170+
171+
172+
def densenet201(pretrained=False, progress=True, **kwargs):
173+
r"""Densenet-201 model from
174+
`"Densely Connected Convolutional Networks" <https://arxiv.org/pdf/1608.06993.pdf>`_
175+
176+
Args:
177+
pretrained (bool): If True, returns a model pre-trained on ImageNet
178+
progress (bool): If True, displays a progress bar of the download to stderr
179+
"""
180+
return _densenet('densenet201', 32, (6, 12, 48, 32), 64, pretrained, progress,
181+
**kwargs)

0 commit comments

Comments
 (0)