from __future__ import division
import numpy as np
import chainer
import chainer.functions as F
from chainer import initializers
import chainer.links as L
from chainercv.links import Conv2DBNActiv
from chainercv.links.model.resnet.resblock import ResBlock
from chainercv.links import PickableSequentialChain
from chainercv import utils
# RGB order
# This is channel wise mean of mean image distributed at
# https://github.com/KaimingHe/deep-residual-networks
_imagenet_mean = np.array(
[123.15163084, 115.90288257, 103.0626238],
dtype=np.float32)[:, np.newaxis, np.newaxis]
[docs]class ResNet(PickableSequentialChain):
"""Base class for ResNet architecture.
This is a pickable sequential link.
The network can choose output layers from set of all
intermediate layers.
The attribute :obj:`pick` is the names of the layers that are going
to be picked by :meth:`__call__`.
The attribute :obj:`layer_names` is the names of all layers
that can be picked.
Examples:
>>> model = ResNet50()
# By default, __call__ returns a probability score (after Softmax).
>>> prob = model(imgs)
>>> model.pick = 'res5'
# This is layer res5
>>> res5 = model(imgs)
>>> model.pick = ['res5', 'fc6']
>>> # These are layers res5 and fc6.
>>> res5, fc6 = model(imgs)
.. seealso::
:class:`chainercv.links.model.PickableSequentialChain`
When :obj:`pretrained_model` is the path of a pre-trained chainer model
serialized as a :obj:`.npz` file in the constructor, this chain model
automatically initializes all the parameters with it.
When a string in the prespecified set is provided, a pretrained model is
loaded from weights distributed on the Internet.
The list of pretrained models supported are as follows:
* :obj:`imagenet`: Loads weights trained with ImageNet and distributed \
at `Model Zoo \
<https://github.com/BVLC/caffe/wiki/Model-Zoo>`_.
This is only supported when :obj:`arch=='he'`.
Args:
n_layer (int): The number of layers.
n_class (int): The number of classes. If :obj:`None`,
the default values are used.
If a supported pretrained model is used,
the number of classes used to train the pretrained model
is used. Otherwise, the number of classes in ILSVRC 2012 dataset
is used.
pretrained_model (string): The destination of the pre-trained
chainer model serialized as a :obj:`.npz` file.
If this is one of the strings described
above, it automatically loads weights stored under a directory
:obj:`$CHAINER_DATASET_ROOT/pfnet/chainercv/models/`,
where :obj:`$CHAINER_DATASET_ROOT` is set as
:obj:`$HOME/.chainer/dataset` unless you specify another value
by modifying the environment variable.
mean (numpy.ndarray): A mean value. If :obj:`None`,
the default values are used.
If a supported pretrained model is used,
the mean value used to train the pretrained model is used.
Otherwise, the mean value calculated from ILSVRC 2012 dataset
is used.
initialW (callable): Initializer for the weights of
convolution kernels.
fc_kwargs (dict): Keyword arguments passed to initialize
the :class:`chainer.links.Linear`.
arch (string): If :obj:`fb`, use Facebook ResNet
architecture. When :obj:`he`, use the architecture presented
by `the original ResNet paper \
<https://arxiv.org/pdf/1512.03385.pdf>`_.
This option changes where to apply strided convolution.
The default value is :obj:`fb`.
"""
_blocks = {
50: [3, 4, 6, 3],
101: [3, 4, 23, 3],
152: [3, 8, 36, 3]
}
_models = {
'fb': {
50: {},
101: {},
152: {}
},
'he': {
50: {
'imagenet': {
'param': {'n_class': 1000, 'mean': _imagenet_mean},
'overwritable': {'mean'},
'url': 'https://chainercv-models.preferred.jp/'
'resnet50_imagenet_converted_2018_03_07.npz'
},
},
101: {
'imagenet': {
'param': {'n_class': 1000, 'mean': _imagenet_mean},
'overwritable': {'mean'},
'url': 'https://chainercv-models.preferred.jp/'
'resnet101_imagenet_converted_2018_03_07.npz'
},
},
152: {
'imagenet': {
'param': {'n_class': 1000, 'mean': _imagenet_mean},
'overwritable': {'mean'},
'url': 'https://chainercv-models.preferred.jp/'
'resnet152_imagenet_converted_2018_03_07.npz'
},
}
}
}
def __init__(self, n_layer,
n_class=None,
pretrained_model=None,
mean=None, initialW=None, fc_kwargs={}, arch='fb'):
if arch == 'fb':
if pretrained_model == 'imagenet':
raise ValueError(
'Pretrained weights for Facebook ResNet models '
'are not supported. Please set arch to \'he\'.')
stride_first = False
conv1_no_bias = True
elif arch == 'he':
stride_first = True
# Kaiming He uses bias only for ResNet50
conv1_no_bias = n_layer != 50
else:
raise ValueError('arch is expected to be one of [\'he\', \'fb\']')
blocks = self._blocks[n_layer]
param, path = utils.prepare_pretrained_model(
{'n_class': n_class, 'mean': mean},
pretrained_model, self._models[arch][n_layer],
{'n_class': 1000, 'mean': _imagenet_mean})
self.mean = param['mean']
if initialW is None:
initialW = initializers.HeNormal(scale=1., fan_option='fan_out')
if 'initialW' not in fc_kwargs:
fc_kwargs['initialW'] = initializers.Normal(scale=0.01)
if pretrained_model:
# As a sampling process is time-consuming,
# we employ a zero initializer for faster computation.
initialW = initializers.constant.Zero()
fc_kwargs['initialW'] = initializers.constant.Zero()
kwargs = {'initialW': initialW, 'stride_first': stride_first}
super(ResNet, self).__init__()
with self.init_scope():
self.conv1 = Conv2DBNActiv(None, 64, 7, 2, 3, nobias=conv1_no_bias,
initialW=initialW)
self.pool1 = lambda x: F.max_pooling_2d(x, ksize=3, stride=2)
self.res2 = ResBlock(blocks[0], None, 64, 256, 1, **kwargs)
self.res3 = ResBlock(blocks[1], None, 128, 512, 2, **kwargs)
self.res4 = ResBlock(blocks[2], None, 256, 1024, 2, **kwargs)
self.res5 = ResBlock(blocks[3], None, 512, 2048, 2, **kwargs)
self.pool5 = lambda x: F.average(x, axis=(2, 3))
self.fc6 = L.Linear(None, param['n_class'], **fc_kwargs)
self.prob = F.softmax
if path:
chainer.serializers.load_npz(path, self)
[docs]class ResNet50(ResNet):
"""ResNet-50 Network.
Please consult the documentation for :class:`ResNet`.
.. seealso::
:class:`chainercv.links.model.resnet.ResNet`
"""
def __init__(self, n_class=None, pretrained_model=None,
mean=None, initialW=None, fc_kwargs={}, arch='fb'):
super(ResNet50, self).__init__(
50, n_class, pretrained_model,
mean, initialW, fc_kwargs, arch)
[docs]class ResNet101(ResNet):
"""ResNet-101 Network.
Please consult the documentation for :class:`ResNet`.
.. seealso::
:class:`chainercv.links.model.resnet.ResNet`
"""
def __init__(self, n_class=None, pretrained_model=None,
mean=None, initialW=None, fc_kwargs={}, arch='fb'):
super(ResNet101, self).__init__(
101, n_class, pretrained_model,
mean, initialW, fc_kwargs, arch)
[docs]class ResNet152(ResNet):
"""ResNet-152 Network.
Please consult the documentation for :class:`ResNet`.
.. seealso::
:class:`chainercv.links.model.resnet.ResNet`
"""
def __init__(self, n_class=None, pretrained_model=None,
mean=None, initialW=None, fc_kwargs={}, arch='fb'):
super(ResNet152, self).__init__(
152, n_class, pretrained_model,
mean, initialW, fc_kwargs, arch)