fastbook 14 Implement ResNet
Modified the one is the book so that it would be easier to read
from fastai.vision.all import *
def get_data(url, presize, resize):
path = untar_data(url)
dls = DataBlock(
blocks=(ImageBlock, CategoryBlock),
get_items=get_image_files,
splitter=GrandparentSplitter(valid_name='val'),
get_y=parent_label,
item_tfms=Resize(presize),
batch_tfms=[
*aug_transforms(min_scale=0.5, size=resize),
Normalize.from_stats(*imagenet_stats),
],
).dataloaders(path, bs=128)
return dls
dls = get_data(URLs.IMAGENETTE_160, 160, 128)
def _conv_block(ni,nf,stride):
return nn.Sequential(
ConvLayer(ni, nf, stride=stride),
ConvLayer(nf, nf, act_cls=None, norm_type=NormType.BatchZero))
class ResBlock(Module):
def __init__(self, ni, nf, stride=1):
self.convs = _conv_block(ni,nf,stride)
self.idconv = noop if ni==nf else ConvLayer(ni, nf, 1, act_cls=None)
self.pool = noop if stride==1 else nn.AvgPool2d(2, ceil_mode=True)
def forward(self, x):
return F.relu(self.convs(x) + self.idconv(self.pool(x)))
def _resnet_stem(*sizes):
return [
ConvLayer(sizes[i], sizes[i+1], 3, stride = 2 if i==0 else 1)
for i in range(len(sizes)-1)
] + [nn.MaxPool2d(kernel_size=3, stride=2, padding=1)]
def _resnet_stem(*sizes):
# Conv Macro only within this function
def __C(i, s):
return ConvLayer(sizes[i], sizes[i+1], 3, stride=s)
l = [__C(i=0, s=2)]
l += [__C(i=i, s=1) for i in range(1, len(sizes)-1)]
l += [nn.MaxPool2d(kernel_size=3, stride=2, padding=1)]
return l
#_resnet_stem(3,32,32,64)
Modified the above '_resnet_stem' to read a little bit easier, where we deal with the 1st iteration and the rest of the others separately since only the 1st time has differenet params. The 1st iter is done out side of the loop and then the loop will do the rest.
class ResNet(nn.Sequential):
def __init__(self, n_out, layers, expansion=1):
stem = _resnet_stem(3,32,32,64)
self.block_szs = [64, 64, 128, 256, 512]
for i in range(1,5): self.block_szs[i] *= expansion
blocks = [self._make_layer(*o) for o in enumerate(layers)]
super().__init__(*stem, *blocks,
nn.AdaptiveAvgPool2d(1), Flatten(),
nn.Linear(self.block_szs[-1], n_out))
def _make_layer(self, idx, n_layers):
stride = 1 if idx==0 else 2
ch_in,ch_out = self.block_szs[idx:idx+2]
return nn.Sequential(*[
ResBlock(ch_in if i==0 else ch_out, ch_out, stride if i==0 else 1)
for i in range(n_layers)
])
class ResNet(nn.Sequential):
def __init__(self, n_out, layers, expansion=1):
self.block_szs = [64, 64, 128, 256, 512]
for i in range(1,5): self.block_szs[i] *= expansion
l = _resnet_stem(3,32,32,64)
l += [self._make_layer(layers[0], *self.block_szs[:2], 1)] # Do 0th
l += [self._make_layer(layers[i], *self.block_szs[i:i+2], 2) # Do the rest
for i in range(1, len(layers))]
# Appending the Head part
l += [
nn.AdaptiveAvgPool2d(1),
Flatten(),
nn.Linear(self.block_szs[-1], n_out)
]
super().__init__(*l)
def _make_layer(self, n_layers, ch_in, ch_out, stride):
l = [ResBlock(ch_in, ch_out, stride)] # Do 0th
l += [ResBlock(ch_out, ch_out, 1) # Do the rest
for i in range(1, n_layers)]
return nn.Sequential(*l)
learn = Learner(dls, ResNet(dls.c, [2,2,2,2]), loss_func=nn.CrossEntropyLoss(),
metrics=accuracy).to_fp16()
learn.fit_one_cycle(5, 3e-3)
dls = get_data(URLs.IMAGENETTE_320, presize=320, resize=224)
def _conv_block(ni,nf,stride):
return nn.Sequential(
ConvLayer(ni, nf//4, 1),
ConvLayer(nf//4, nf//4, stride=stride),
ConvLayer(nf//4, nf, 1, act_cls=None, norm_type=NormType.BatchZero))
learn = Learner(dls, ResNet(dls.c, [3,4,6,3], 4), loss_func=nn.CrossEntropyLoss(),
metrics=accuracy).to_fp16()
learn.fit_one_cycle(20, 3e-3)