diff --git a/python/paddle/tests/test_vision_models.py b/python/paddle/tests/test_vision_models.py index a25a8f373c29c4..109ad512884f3c 100644 --- a/python/paddle/tests/test_vision_models.py +++ b/python/paddle/tests/test_vision_models.py @@ -81,7 +81,19 @@ def test_lenet(self): x = np.array(np.random.random((2, 1, 28, 28)), dtype=np.float32) lenet.predict_batch(x) - + + def test_densenet121(self): + self.models_infer('densenet121') + + def test_densenet161(self): + self.models_infer('densenet161') + + def test_densenet169(self): + self.models_infer('densenet169') + + def test_densenet201(self): + self.models_infer('densenet201') + if __name__ == '__main__': unittest.main() diff --git a/python/paddle/vision/__init__.py b/python/paddle/vision/__init__.py index 76393865ded04a..d7372ef6e39566 100644 --- a/python/paddle/vision/__init__.py +++ b/python/paddle/vision/__init__.py @@ -28,6 +28,11 @@ from .datasets import Cifar10 # noqa: F401 from .datasets import Cifar100 # noqa: F401 from .datasets import VOC2012 # noqa: F401 +from .models import DenseNet # noqa: F401 +from .models import densenet121 # noqa: F401 +from .models import densenet161 # noqa: F401 +from .models import densenet169 # noqa: F401 +from .models import densenet201 # noqa: F401 from .models import ResNet # noqa: F401 from .models import resnet18 # noqa: F401 from .models import resnet34 # noqa: F401 diff --git a/python/paddle/vision/models/__init__.py b/python/paddle/vision/models/__init__.py index d38f3b1722ee8c..ed11bd0b9c51e2 100644 --- a/python/paddle/vision/models/__init__.py +++ b/python/paddle/vision/models/__init__.py @@ -28,8 +28,18 @@ from .vgg import vgg16 # noqa: F401 from .vgg import vgg19 # noqa: F401 from .lenet import LeNet # noqa: F401 +from .densenet import DenseNet # noqa: F401 +from .densenet import densenet121 +from .densenet import densenet161 +from .densenet import densenet169 +from .densenet import densenet201 __all__ = [ #noqa + 'DenseNet', + 'densenet121', + 'densenet161', + 'densenet169', + 'densenet201', 'ResNet', 'resnet18', 'resnet34', diff --git a/python/paddle/vision/models/densenet.py b/python/paddle/vision/models/densenet.py new file mode 100644 index 00000000000000..9f1f449ba7ca63 --- /dev/null +++ b/python/paddle/vision/models/densenet.py @@ -0,0 +1,251 @@ +import re +import paddle +import paddle.nn as nn +import paddle.nn.functional as F +from collections import OrderedDict +from paddle import Tensor +from typing import Any, List, Tuple +from paddle.utils.download import get_weights_path_from_url + +__all__ = ['DenseNet', 'densenet121', 'densenet169', 'densenet201', 'densenet161'] + +model_urls = { + 'densenet121': 'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/DenseNet121_pretrained.pdparams', + 'densenet169': 'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/DenseNet169_pretrained.pdparams', + 'densenet201': 'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/DenseNet201_pretrained.pdparams', + 'densenet161': 'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/DenseNet161_pretrained.pdparams', +} + + +class _DenseLayer(nn.Layer): + def __init__( + self, + num_input_features: int, + growth_rate: int, + bn_size: int, + drop_rate: float, + memory_efficient: bool = False + ) -> None: + super(_DenseLayer, self).__init__() + self.num_input_features = num_input_features + self.norm1 = nn.BatchNorm2D(num_input_features) + self.relu1 = nn.ReLU() + self.conv1 = nn.Conv2D(num_input_features, bn_size * + growth_rate, kernel_size=1, stride=1) + self.norm2 = nn.BatchNorm2D(bn_size * growth_rate) + self.relu2 = nn.ReLU() + self.conv2 = nn.Conv2D(bn_size * growth_rate, growth_rate, + kernel_size=3, stride=1, padding=1) + self.drop_rate = float(drop_rate) + self.memory_efficient = memory_efficient + + + def forward(self, input) -> Tensor: # noqa: F811 + + if isinstance(input, Tensor): + input = [input] + input = paddle.concat(input, 1) + + # print(self.num_input_features) + # print(input.shape) + bottleneck_output = self.conv1(self.relu1(self.norm1(input))) + new_features = self.conv2(self.relu2(self.norm2(bottleneck_output))) + if self.drop_rate > 0: + new_features = F.dropout(new_features, p=self.drop_rate, + training=self.training) + return new_features + + +class _DenseBlock(nn.LayerDict): + + def __init__( + self, + num_layers: int, + num_input_features: int, + bn_size: int, + growth_rate: int, + drop_rate: float, + memory_efficient: bool = False + ) -> None: + super(_DenseBlock, self).__init__() + + for i in range(num_layers): + layer = _DenseLayer( + num_input_features + i * growth_rate, + growth_rate=growth_rate, + bn_size=bn_size, + drop_rate=drop_rate, + memory_efficient=memory_efficient, + ) + self.update([('denselayer%d' % (i + 1), layer)]) + + + def forward(self, init_features: Tensor) -> Tensor: + features = [init_features] + for name, layer in self.items(): + new_features = layer(features) + # print("DenseLayer Passed") + features.append(new_features) + return paddle.concat(features, 1) + + +class _Transition(nn.Layer): + def __init__(self, num_input_features: int, num_output_features: int) -> None: + super(_Transition, self).__init__() + self.seq = nn.Sequential( + ('norm', nn.BatchNorm2D(num_input_features)), + ('relu', nn.ReLU()), + ('conv', nn.Conv2D(num_input_features, num_output_features, + kernel_size=1, stride=1)), + ('pool', nn.AvgPool2D(kernel_size=2, stride=2)) + ) + def forward(self,x): + return self.seq(x) + + +class DenseNet(nn.Layer): + r"""Densenet-BC model class, based on + `"Densely Connected Convolutional Networks" `_. + Args: + growth_rate (int) - how many filters to add each layer (`k` in paper) + block_config (list of 4 ints) - how many layers in each pooling block + num_init_features (int) - the number of filters to learn in the first convolution layer + bn_size (int) - multiplicative factor for number of bottle neck layers + (i.e. bn_size * k features in the bottleneck layer) + drop_rate (float) - dropout rate after each dense layer + num_classes (int) - number of classification classes + memory_efficient (bool) - If True, uses checkpointing. Much more memory efficient, + but slower. Default: *False*. See `"paper" `_. + """ + + def __init__( + self, + growth_rate: int = 32, + block_config: Tuple[int, int, int, int] = (6, 12, 24, 16), + num_init_features: int = 64, + bn_size: int = 4, + drop_rate: float = 0, + num_classes: int = 1000, + memory_efficient: bool = False + ) -> None: + + super(DenseNet, self).__init__() + + # First convolution + self.features = nn.Sequential( + ('conv0', nn.Conv2D(3, num_init_features, kernel_size=7, stride=2, + padding=3)), + ('norm0', nn.BatchNorm2D(num_init_features)), + ('relu0', nn.ReLU()), + ('pool0', nn.MaxPool2D(kernel_size=3, stride=2, padding=1)) + ) + + # Each denseblock + num_features = num_init_features + for i, num_layers in enumerate(block_config): + block = _DenseBlock( + num_layers=num_layers, + num_input_features=num_features, + bn_size=bn_size, + growth_rate=growth_rate, + drop_rate=drop_rate, + memory_efficient=memory_efficient + ) + self.features.add_sublayer('denseblock%d' % (i + 1), block) + num_features = num_features + num_layers * growth_rate + if i != len(block_config) - 1: + trans = _Transition(num_input_features=num_features, + num_output_features=num_features // 2) + self.features.add_sublayer('transition%d' % (i + 1), trans) + num_features = num_features // 2 + + # Final batch norm + self.features.add_sublayer('norm5', nn.BatchNorm2D(num_features)) + + # Linear layer + self.classifier = nn.Linear(num_features, num_classes) + + + + def forward(self, x: Tensor) -> Tensor: + features = self.features(x) + out = F.relu(features, inplace=True) + out = F.adaptive_avg_pool2d(out, (1, 1)) + out = paddle.flatten(out, 1) + out = self.classifier(out) + return out + +def _densenet( + arch: str, + growth_rate: int, + block_config: Tuple[int, int, int, int], + num_init_features: int, + pretrained: bool, + progress: bool, + **kwargs: Any +) -> DenseNet: + model = DenseNet(growth_rate, block_config, num_init_features, **kwargs) + + if pretrained: + assert arch in model_urls, "{} model do not have a pretrained model now, you should set pretrained=False".format( + arch) + weight_path = get_weights_path_from_url(model_urls[arch]) + param = paddle.load(weight_path) + model.set_dict(param) + + return model + +def densenet121(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> DenseNet: + r"""Densenet-121 model from + `"Densely Connected Convolutional Networks" `_. + The required minimum input size of the model is 29x29. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + memory_efficient (bool) - If True, uses checkpointing. Much more memory efficient, + but slower. Default: *False*. See `"paper" `_. + """ + return _densenet('densenet121', 32, (6, 12, 24, 16), 64, pretrained, progress, + **kwargs) + + +def densenet161(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> DenseNet: + r"""Densenet-161 model from + `"Densely Connected Convolutional Networks" `_. + The required minimum input size of the model is 29x29. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + memory_efficient (bool) - If True, uses checkpointing. Much more memory efficient, + but slower. Default: *False*. See `"paper" `_. + """ + return _densenet('densenet161', 48, (6, 12, 36, 24), 96, pretrained, progress, + **kwargs) + + +def densenet169(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> DenseNet: + r"""Densenet-169 model from + `"Densely Connected Convolutional Networks" `_. + The required minimum input size of the model is 29x29. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + memory_efficient (bool) - If True, uses checkpointing. Much more memory efficient, + but slower. Default: *False*. See `"paper" `_. + """ + return _densenet('densenet169', 32, (6, 12, 32, 32), 64, pretrained, progress, + **kwargs) + + +def densenet201(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> DenseNet: + r"""Densenet-201 model from + `"Densely Connected Convolutional Networks" `_. + The required minimum input size of the model is 29x29. + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + memory_efficient (bool) - If True, uses checkpointing. Much more memory efficient, + but slower. Default: *False*. See `"paper" `_. + """ + return _densenet('densenet201', 32, (6, 12, 48, 32), 64, pretrained, progress, + **kwargs)