diff --git a/hub_module/modules/image/classification/googlenet_imagenet/module.py b/hub_module/modules/image/classification/googlenet_imagenet/module.py new file mode 100644 index 0000000000000000000000000000000000000000..08b60fea5b7a1c2c1eb87cec3ce29cec66bc5f30 --- /dev/null +++ b/hub_module/modules/image/classification/googlenet_imagenet/module.py @@ -0,0 +1,179 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F +from paddle.nn import Conv2d, BatchNorm, Linear, Dropout +from paddle.nn import AdaptiveAvgPool2d, MaxPool2d, AvgPool2d +from paddle.nn.initializer import Uniform +from paddlehub.module.module import moduleinfo +from paddlehub.module.cv_module import ImageClassifierModule + + +def xavier(channels: int, filter_size: int, name: str): + """Initialize the weights by uniform distribution.""" + stdv = (3.0 / (filter_size**2 * channels))**0.5 + param_attr = ParamAttr(initializer=Uniform(-stdv, stdv), name=name + "_weights") + return param_attr + + +class ConvLayer(nn.Layer): + """Basic conv2d layer.""" + def __init__(self, + num_channels: int, + num_filters: int, + filter_size: int, + stride: int = 1, + groups: int = 1, + name: str = None): + super(ConvLayer, self).__init__() + + self._conv = Conv2d(in_channels=num_channels, + out_channels=num_filters, + kernel_size=filter_size, + stride=stride, + padding=(filter_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False) + + def forward(self, inputs: paddle.Tensor): + y = self._conv(inputs) + return y + + +class Inception(nn.Layer): + """Inception block.""" + def __init__(self, + input_channels: int, + output_channels: int, + filter1: int, + filter3R: int, + filter3: int, + filter5R: int, + filter5: int, + proj: int, + name: str = None): + super(Inception, self).__init__() + + self._conv1 = ConvLayer(input_channels, filter1, 1, name="inception_" + name + "_1x1") + self._conv3r = ConvLayer(input_channels, filter3R, 1, name="inception_" + name + "_3x3_reduce") + self._conv3 = ConvLayer(filter3R, filter3, 3, name="inception_" + name + "_3x3") + self._conv5r = ConvLayer(input_channels, filter5R, 1, name="inception_" + name + "_5x5_reduce") + self._conv5 = ConvLayer(filter5R, filter5, 5, name="inception_" + name + "_5x5") + self._pool = MaxPool2d(kernel_size=3, stride=1, padding=1) + + self._convprj = ConvLayer(input_channels, proj, 1, name="inception_" + name + "_3x3_proj") + + def forward(self, inputs: paddle.Tensor): + conv1 = self._conv1(inputs) + + conv3r = self._conv3r(inputs) + conv3 = self._conv3(conv3r) + + conv5r = self._conv5r(inputs) + conv5 = self._conv5(conv5r) + + pool = self._pool(inputs) + convprj = self._convprj(pool) + + cat = paddle.concat([conv1, conv3, conv5, convprj], axis=1) + cat = F.relu(cat) + return cat + + +@moduleinfo(name="googlenet_imagenet", + type="CV/classification", + author="paddlepaddle", + author_email="", + summary="GoogleNet_imagenet is a classification model, " + "this module is trained with Imagenet dataset.", + version="1.1.0", + meta=ImageClassifierModule) +class GoogleNet(nn.Layer): + """GoogleNet model""" + def __init__(self, class_dim: int = 1000, load_checkpoint: str = None): + super(GoogleNet, self).__init__() + self._conv = ConvLayer(3, 64, 7, 2, name="conv1") + self._pool = MaxPool2d(kernel_size=3, stride=2) + self._conv_1 = ConvLayer(64, 64, 1, name="conv2_1x1") + self._conv_2 = ConvLayer(64, 192, 3, name="conv2_3x3") + + self._ince3a = Inception(192, 192, 64, 96, 128, 16, 32, 32, name="ince3a") + self._ince3b = Inception(256, 256, 128, 128, 192, 32, 96, 64, name="ince3b") + + self._ince4a = Inception(480, 480, 192, 96, 208, 16, 48, 64, name="ince4a") + self._ince4b = Inception(512, 512, 160, 112, 224, 24, 64, 64, name="ince4b") + self._ince4c = Inception(512, 512, 128, 128, 256, 24, 64, 64, name="ince4c") + self._ince4d = Inception(512, 512, 112, 144, 288, 32, 64, 64, name="ince4d") + self._ince4e = Inception(528, 528, 256, 160, 320, 32, 128, 128, name="ince4e") + + self._ince5a = Inception(832, 832, 256, 160, 320, 32, 128, 128, name="ince5a") + self._ince5b = Inception(832, 832, 384, 192, 384, 48, 128, 128, name="ince5b") + + self._pool_5 = AvgPool2d(kernel_size=7, stride=7) + + self._drop = Dropout(p=0.4, mode="downscale_in_infer") + self._fc_out = Linear(1024, + class_dim, + weight_attr=xavier(1024, 1, "out"), + bias_attr=ParamAttr(name="out_offset")) + + if load_checkpoint is not None: + model_dict = paddle.load(load_checkpoint)[0] + self.set_dict(model_dict) + print("load custom checkpoint success") + + else: + checkpoint = os.path.join(self.directory, 'googlenet_imagenet.pdparams') + if not os.path.exists(checkpoint): + os.system( + 'wget https://paddlehub.bj.bcebos.com/dygraph/image_classification/googlenet_imagenet.pdparams -O' + + checkpoint) + model_dict = paddle.load(checkpoint)[0] + self.set_dict(model_dict) + print("load pretrained checkpoint success") + + def forward(self, inputs: paddle.Tensor): + x = self._conv(inputs) + x = self._pool(x) + x = self._conv_1(x) + x = self._conv_2(x) + x = self._pool(x) + + x = self._ince3a(x) + x = self._ince3b(x) + x = self._pool(x) + + ince4a = self._ince4a(x) + x = self._ince4b(ince4a) + x = self._ince4c(x) + ince4d = self._ince4d(x) + x = self._ince4e(ince4d) + x = self._pool(x) + + x = self._ince5a(x) + ince5b = self._ince5b(x) + + x = self._pool_5(ince5b) + x = self._drop(x) + x = paddle.squeeze(x, axis=[2, 3]) + out = self._fc_out(x) + out = F.softmax(out) + + return out diff --git a/hub_module/modules/image/classification/inceptionv4_imagenet/module.py b/hub_module/modules/image/classification/inceptionv4_imagenet/module.py new file mode 100644 index 0000000000000000000000000000000000000000..e926cfe36e62fa274453ec91d0356f455bd0ac79 --- /dev/null +++ b/hub_module/modules/image/classification/inceptionv4_imagenet/module.py @@ -0,0 +1,343 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import math + +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F +from paddle.nn import Conv2d, BatchNorm, Linear, Dropout +from paddle.nn import AdaptiveAvgPool2d, MaxPool2d, AvgPool2d +from paddle.nn.initializer import Uniform +from paddlehub.module.module import moduleinfo +from paddlehub.module.cv_module import ImageClassifierModule + + +class ConvBNLayer(nn.Layer): + """Basic conv bn layer.""" + def __init__(self, + num_channels: int, + num_filters: int, + filter_size: int, + stride: int = 1, + padding: int = 0, + groups: int = 1, + act: str = 'relu', + name: str = None): + super(ConvBNLayer, self).__init__() + + self._conv = Conv2d(in_channels=num_channels, + out_channels=num_filters, + kernel_size=filter_size, + stride=stride, + padding=padding, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False) + bn_name = name + "_bn" + self._batch_norm = BatchNorm(num_filters, + act=act, + param_attr=ParamAttr(name=bn_name + "_scale"), + bias_attr=ParamAttr(name=bn_name + "_offset"), + moving_mean_name=bn_name + '_mean', + moving_variance_name=bn_name + '_variance') + + def forward(self, inputs: paddle.Tensor): + y = self._conv(inputs) + y = self._batch_norm(y) + return y + + +class InceptionStem(nn.Layer): + """InceptionV4 stem module.""" + def __init__(self): + super(InceptionStem, self).__init__() + self._conv_1 = ConvBNLayer(3, 32, 3, stride=2, act="relu", name="conv1_3x3_s2") + self._conv_2 = ConvBNLayer(32, 32, 3, act="relu", name="conv2_3x3_s1") + self._conv_3 = ConvBNLayer(32, 64, 3, padding=1, act="relu", name="conv3_3x3_s1") + self._pool = MaxPool2d(kernel_size=3, stride=2, padding=0) + self._conv2 = ConvBNLayer(64, 96, 3, stride=2, act="relu", name="inception_stem1_3x3_s2") + self._conv1_1 = ConvBNLayer(160, 64, 1, act="relu", name="inception_stem2_3x3_reduce") + self._conv1_2 = ConvBNLayer(64, 96, 3, act="relu", name="inception_stem2_3x3") + self._conv2_1 = ConvBNLayer(160, 64, 1, act="relu", name="inception_stem2_1x7_reduce") + self._conv2_2 = ConvBNLayer(64, 64, (7, 1), padding=(3, 0), act="relu", name="inception_stem2_1x7") + self._conv2_3 = ConvBNLayer(64, 64, (1, 7), padding=(0, 3), act="relu", name="inception_stem2_7x1") + self._conv2_4 = ConvBNLayer(64, 96, 3, act="relu", name="inception_stem2_3x3_2") + self._conv3 = ConvBNLayer(192, 192, 3, stride=2, act="relu", name="inception_stem3_3x3_s2") + + def forward(self, inputs: paddle.Tensor): + conv = self._conv_1(inputs) + conv = self._conv_2(conv) + conv = self._conv_3(conv) + + pool1 = self._pool(conv) + conv2 = self._conv2(conv) + concat = paddle.concat([pool1, conv2], axis=1) + + conv1 = self._conv1_1(concat) + conv1 = self._conv1_2(conv1) + + conv2 = self._conv2_1(concat) + conv2 = self._conv2_2(conv2) + conv2 = self._conv2_3(conv2) + conv2 = self._conv2_4(conv2) + + concat = paddle.concat([conv1, conv2], axis=1) + + conv1 = self._conv3(concat) + pool1 = self._pool(concat) + + concat = paddle.concat([conv1, pool1], axis=1) + return concat + + +class InceptionA(nn.Layer): + """InceptionA module for InceptionV4.""" + def __init__(self, name: str): + super(InceptionA, self).__init__() + self._pool = AvgPool2d(kernel_size=3, stride=1, padding=1) + self._conv1 = ConvBNLayer(384, 96, 1, act="relu", name="inception_a" + name + "_1x1") + self._conv2 = ConvBNLayer(384, 96, 1, act="relu", name="inception_a" + name + "_1x1_2") + self._conv3_1 = ConvBNLayer(384, 64, 1, act="relu", name="inception_a" + name + "_3x3_reduce") + self._conv3_2 = ConvBNLayer(64, 96, 3, padding=1, act="relu", name="inception_a" + name + "_3x3") + self._conv4_1 = ConvBNLayer(384, 64, 1, act="relu", name="inception_a" + name + "_3x3_2_reduce") + self._conv4_2 = ConvBNLayer(64, 96, 3, padding=1, act="relu", name="inception_a" + name + "_3x3_2") + self._conv4_3 = ConvBNLayer(96, 96, 3, padding=1, act="relu", name="inception_a" + name + "_3x3_3") + + def forward(self, inputs: paddle.Tensor): + pool1 = self._pool(inputs) + conv1 = self._conv1(pool1) + + conv2 = self._conv2(inputs) + + conv3 = self._conv3_1(inputs) + conv3 = self._conv3_2(conv3) + + conv4 = self._conv4_1(inputs) + conv4 = self._conv4_2(conv4) + conv4 = self._conv4_3(conv4) + + concat = paddle.concat([conv1, conv2, conv3, conv4], axis=1) + return concat + + +class ReductionA(nn.Layer): + """ReductionA module for InceptionV4.""" + def __init__(self): + super(ReductionA, self).__init__() + self._pool = MaxPool2d(kernel_size=3, stride=2, padding=0) + self._conv2 = ConvBNLayer(384, 384, 3, stride=2, act="relu", name="reduction_a_3x3") + self._conv3_1 = ConvBNLayer(384, 192, 1, act="relu", name="reduction_a_3x3_2_reduce") + self._conv3_2 = ConvBNLayer(192, 224, 3, padding=1, act="relu", name="reduction_a_3x3_2") + self._conv3_3 = ConvBNLayer(224, 256, 3, stride=2, act="relu", name="reduction_a_3x3_3") + + def forward(self, inputs: paddle.Tensor): + pool1 = self._pool(inputs) + conv2 = self._conv2(inputs) + conv3 = self._conv3_1(inputs) + conv3 = self._conv3_2(conv3) + conv3 = self._conv3_3(conv3) + concat = paddle.concat([pool1, conv2, conv3], axis=1) + return concat + + +class InceptionB(nn.Layer): + """InceptionB module for InceptionV4.""" + def __init__(self, name: str = None): + super(InceptionB, self).__init__() + self._pool = AvgPool2d(kernel_size=3, stride=1, padding=1) + self._conv1 = ConvBNLayer(1024, 128, 1, act="relu", name="inception_b" + name + "_1x1") + self._conv2 = ConvBNLayer(1024, 384, 1, act="relu", name="inception_b" + name + "_1x1_2") + self._conv3_1 = ConvBNLayer(1024, 192, 1, act="relu", name="inception_b" + name + "_1x7_reduce") + self._conv3_2 = ConvBNLayer(192, 224, (1, 7), padding=(0, 3), act="relu", name="inception_b" + name + "_1x7") + self._conv3_3 = ConvBNLayer(224, 256, (7, 1), padding=(3, 0), act="relu", name="inception_b" + name + "_7x1") + self._conv4_1 = ConvBNLayer(1024, 192, 1, act="relu", name="inception_b" + name + "_7x1_2_reduce") + self._conv4_2 = ConvBNLayer(192, 192, (1, 7), padding=(0, 3), act="relu", name="inception_b" + name + "_1x7_2") + self._conv4_3 = ConvBNLayer(192, 224, (7, 1), padding=(3, 0), act="relu", name="inception_b" + name + "_7x1_2") + self._conv4_4 = ConvBNLayer(224, 224, (1, 7), padding=(0, 3), act="relu", name="inception_b" + name + "_1x7_3") + self._conv4_5 = ConvBNLayer(224, 256, (7, 1), padding=(3, 0), act="relu", name="inception_b" + name + "_7x1_3") + + def forward(self, inputs: paddle.Tensor): + pool1 = self._pool(inputs) + conv1 = self._conv1(pool1) + + conv2 = self._conv2(inputs) + + conv3 = self._conv3_1(inputs) + conv3 = self._conv3_2(conv3) + conv3 = self._conv3_3(conv3) + + conv4 = self._conv4_1(inputs) + conv4 = self._conv4_2(conv4) + conv4 = self._conv4_3(conv4) + conv4 = self._conv4_4(conv4) + conv4 = self._conv4_5(conv4) + + concat = paddle.concat([conv1, conv2, conv3, conv4], axis=1) + return concat + + +class ReductionB(nn.Layer): + """ReductionB module for InceptionV4.""" + def __init__(self): + super(ReductionB, self).__init__() + self._pool = MaxPool2d(kernel_size=3, stride=2, padding=0) + self._conv2_1 = ConvBNLayer(1024, 192, 1, act="relu", name="reduction_b_3x3_reduce") + self._conv2_2 = ConvBNLayer(192, 192, 3, stride=2, act="relu", name="reduction_b_3x3") + self._conv3_1 = ConvBNLayer(1024, 256, 1, act="relu", name="reduction_b_1x7_reduce") + self._conv3_2 = ConvBNLayer(256, 256, (1, 7), padding=(0, 3), act="relu", name="reduction_b_1x7") + self._conv3_3 = ConvBNLayer(256, 320, (7, 1), padding=(3, 0), act="relu", name="reduction_b_7x1") + self._conv3_4 = ConvBNLayer(320, 320, 3, stride=2, act="relu", name="reduction_b_3x3_2") + + def forward(self, inputs: paddle.Tensor): + pool1 = self._pool(inputs) + + conv2 = self._conv2_1(inputs) + conv2 = self._conv2_2(conv2) + + conv3 = self._conv3_1(inputs) + conv3 = self._conv3_2(conv3) + conv3 = self._conv3_3(conv3) + conv3 = self._conv3_4(conv3) + + concat = paddle.concat([pool1, conv2, conv3], axis=1) + + return concat + + +class InceptionC(nn.Layer): + """InceptionC module for InceptionV4.""" + def __init__(self, name: str = None): + super(InceptionC, self).__init__() + self._pool = AvgPool2d(kernel_size=3, stride=1, padding=1) + self._conv1 = ConvBNLayer(1536, 256, 1, act="relu", name="inception_c" + name + "_1x1") + self._conv2 = ConvBNLayer(1536, 256, 1, act="relu", name="inception_c" + name + "_1x1_2") + self._conv3_0 = ConvBNLayer(1536, 384, 1, act="relu", name="inception_c" + name + "_1x1_3") + self._conv3_1 = ConvBNLayer(384, 256, (1, 3), padding=(0, 1), act="relu", name="inception_c" + name + "_1x3") + self._conv3_2 = ConvBNLayer(384, 256, (3, 1), padding=(1, 0), act="relu", name="inception_c" + name + "_3x1") + self._conv4_0 = ConvBNLayer(1536, 384, 1, act="relu", name="inception_c" + name + "_1x1_4") + self._conv4_00 = ConvBNLayer(384, 448, (1, 3), padding=(0, 1), act="relu", name="inception_c" + name + "_1x3_2") + self._conv4_000 = ConvBNLayer(448, + 512, (3, 1), + padding=(1, 0), + act="relu", + name="inception_c" + name + "_3x1_2") + self._conv4_1 = ConvBNLayer(512, 256, (1, 3), padding=(0, 1), act="relu", name="inception_c" + name + "_1x3_3") + self._conv4_2 = ConvBNLayer(512, 256, (3, 1), padding=(1, 0), act="relu", name="inception_c" + name + "_3x1_3") + + def forward(self, inputs: paddle.Tensor): + pool1 = self._pool(inputs) + conv1 = self._conv1(pool1) + + conv2 = self._conv2(inputs) + + conv3 = self._conv3_0(inputs) + conv3_1 = self._conv3_1(conv3) + conv3_2 = self._conv3_2(conv3) + + conv4 = self._conv4_0(inputs) + conv4 = self._conv4_00(conv4) + conv4 = self._conv4_000(conv4) + conv4_1 = self._conv4_1(conv4) + conv4_2 = self._conv4_2(conv4) + + concat = paddle.concat([conv1, conv2, conv3_1, conv3_2, conv4_1, conv4_2], axis=1) + + return concat + + +@moduleinfo(name="inceptionv4_imagenet", + type="CV/classification", + author="paddlepaddle", + author_email="", + summary="InceptionV4_imagenet is a classification model, " + "this module is trained with Imagenet dataset.", + version="1.1.0", + meta=ImageClassifierModule) +class InceptionV4(nn.Layer): + """InceptionV4 model.""" + def __init__(self, class_dim: int = 1000, load_checkpoint: str = None): + super(InceptionV4, self).__init__() + self._inception_stem = InceptionStem() + + self._inceptionA_1 = InceptionA(name="1") + self._inceptionA_2 = InceptionA(name="2") + self._inceptionA_3 = InceptionA(name="3") + self._inceptionA_4 = InceptionA(name="4") + self._reductionA = ReductionA() + + self._inceptionB_1 = InceptionB(name="1") + self._inceptionB_2 = InceptionB(name="2") + self._inceptionB_3 = InceptionB(name="3") + self._inceptionB_4 = InceptionB(name="4") + self._inceptionB_5 = InceptionB(name="5") + self._inceptionB_6 = InceptionB(name="6") + self._inceptionB_7 = InceptionB(name="7") + self._reductionB = ReductionB() + + self._inceptionC_1 = InceptionC(name="1") + self._inceptionC_2 = InceptionC(name="2") + self._inceptionC_3 = InceptionC(name="3") + + self.avg_pool = AdaptiveAvgPool2d(1) + self._drop = Dropout(p=0.2, mode="downscale_in_infer") + stdv = 1.0 / math.sqrt(1536 * 1.0) + self.out = Linear(1536, + class_dim, + weight_attr=ParamAttr(initializer=Uniform(-stdv, stdv), name="final_fc_weights"), + bias_attr=ParamAttr(name="final_fc_offset")) + + if load_checkpoint is not None: + model_dict = paddle.load(load_checkpoint)[0] + self.set_dict(model_dict) + print("load custom checkpoint success") + + else: + checkpoint = os.path.join(self.directory, 'inceptionv4_imagenet.pdparams') + if not os.path.exists(checkpoint): + os.system( + 'wget https://paddlehub.bj.bcebos.com/dygraph/image_classification/inceptionv4_imagenet.pdparams -O' + + checkpoint) + model_dict = paddle.load(checkpoint)[0] + self.set_dict(model_dict) + print("load pretrained checkpoint success") + + def forward(self, inputs): + x = self._inception_stem(inputs) + + x = self._inceptionA_1(x) + x = self._inceptionA_2(x) + x = self._inceptionA_3(x) + x = self._inceptionA_4(x) + x = self._reductionA(x) + + x = self._inceptionB_1(x) + x = self._inceptionB_2(x) + x = self._inceptionB_3(x) + x = self._inceptionB_4(x) + x = self._inceptionB_5(x) + x = self._inceptionB_6(x) + x = self._inceptionB_7(x) + x = self._reductionB(x) + + x = self._inceptionC_1(x) + x = self._inceptionC_2(x) + x = self._inceptionC_3(x) + + x = self.avg_pool(x) + x = paddle.squeeze(x, axis=[2, 3]) + x = self._drop(x) + x = self.out(x) + return x diff --git a/hub_module/modules/image/classification/xception41_imagenet/module.py b/hub_module/modules/image/classification/xception41_imagenet/module.py new file mode 100644 index 0000000000000000000000000000000000000000..087a8944b9498ff9949f1ec2242ce349624edf86 --- /dev/null +++ b/hub_module/modules/image/classification/xception41_imagenet/module.py @@ -0,0 +1,312 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys +import math + +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F +from paddle.nn import Conv2d, BatchNorm, Linear, Dropout +from paddle.nn import AdaptiveAvgPool2d, MaxPool2d, AvgPool2d +from paddle.nn.initializer import Uniform +from paddlehub.module.module import moduleinfo +from paddlehub.module.cv_module import ImageClassifierModule + + +class ConvBNLayer(nn.Layer): + """Basic conv bn layer.""" + def __init__(self, + num_channels: int, + num_filters: int, + filter_size: int, + stride: int = 1, + groups: int = 1, + act: str = None, + name: str = None): + super(ConvBNLayer, self).__init__() + + self._conv = Conv2d(in_channels=num_channels, + out_channels=num_filters, + kernel_size=filter_size, + stride=stride, + padding=(filter_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False) + bn_name = "bn_" + name + self._batch_norm = BatchNorm(num_filters, + act=act, + param_attr=ParamAttr(name=bn_name + "_scale"), + bias_attr=ParamAttr(name=bn_name + "_offset"), + moving_mean_name=bn_name + '_mean', + moving_variance_name=bn_name + '_variance') + + def forward(self, inputs: paddle.Tensor): + y = self._conv(inputs) + y = self._batch_norm(y) + return y + + +class SeparableConv(nn.Layer): + """Basic separable conv layer, it contains pointwise conv and depthwise conv.""" + def __init__(self, input_channels: int, output_channels: int, stride: int = 1, name: str = None): + super(SeparableConv, self).__init__() + + self._pointwise_conv = ConvBNLayer(input_channels, output_channels, 1, name=name + "_sep") + self._depthwise_conv = ConvBNLayer(output_channels, + output_channels, + 3, + stride=stride, + groups=output_channels, + name=name + "_dw") + + def forward(self, inputs: paddle.Tensor): + x = self._pointwise_conv(inputs) + x = self._depthwise_conv(x) + return x + + +class EntryFlowBottleneckBlock(nn.Layer): + """Basic entry flow bottleneck block for Xception.""" + def __init__(self, + input_channels: int, + output_channels: int, + stride: int = 2, + name: str = None, + relu_first: bool = False): + super(EntryFlowBottleneckBlock, self).__init__() + self.relu_first = relu_first + + self._short = Conv2d(in_channels=input_channels, + out_channels=output_channels, + kernel_size=1, + stride=stride, + padding=0, + weight_attr=ParamAttr(name + "_branch1_weights"), + bias_attr=False) + self._conv1 = SeparableConv(input_channels, output_channels, stride=1, name=name + "_branch2a_weights") + self._conv2 = SeparableConv(output_channels, output_channels, stride=1, name=name + "_branch2b_weights") + self._pool = MaxPool2d(kernel_size=3, stride=stride, padding=1) + + def forward(self, inputs: paddle.Tensor): + conv0 = inputs + short = self._short(inputs) + if self.relu_first: + conv0 = F.relu(conv0) + conv1 = self._conv1(conv0) + conv2 = F.relu(conv1) + conv2 = self._conv2(conv2) + pool = self._pool(conv2) + return paddle.elementwise_add(x=short, y=pool) + + +class EntryFlow(nn.Layer): + """Entry flow for Xception.""" + def __init__(self, block_num: int = 3): + super(EntryFlow, self).__init__() + + name = "entry_flow" + self.block_num = block_num + self._conv1 = ConvBNLayer(3, 32, 3, stride=2, act="relu", name=name + "_conv1") + self._conv2 = ConvBNLayer(32, 64, 3, act="relu", name=name + "_conv2") + if block_num == 3: + self._conv_0 = EntryFlowBottleneckBlock(64, 128, stride=2, name=name + "_0", relu_first=False) + self._conv_1 = EntryFlowBottleneckBlock(128, 256, stride=2, name=name + "_1", relu_first=True) + self._conv_2 = EntryFlowBottleneckBlock(256, 728, stride=2, name=name + "_2", relu_first=True) + elif block_num == 5: + self._conv_0 = EntryFlowBottleneckBlock(64, 128, stride=2, name=name + "_0", relu_first=False) + self._conv_1 = EntryFlowBottleneckBlock(128, 256, stride=1, name=name + "_1", relu_first=True) + self._conv_2 = EntryFlowBottleneckBlock(256, 256, stride=2, name=name + "_2", relu_first=True) + self._conv_3 = EntryFlowBottleneckBlock(256, 728, stride=1, name=name + "_3", relu_first=True) + self._conv_4 = EntryFlowBottleneckBlock(728, 728, stride=2, name=name + "_4", relu_first=True) + else: + sys.exit(-1) + + def forward(self, inputs: paddle.Tensor): + x = self._conv1(inputs) + x = self._conv2(x) + + if self.block_num == 3: + x = self._conv_0(x) + x = self._conv_1(x) + x = self._conv_2(x) + elif self.block_num == 5: + x = self._conv_0(x) + x = self._conv_1(x) + x = self._conv_2(x) + x = self._conv_3(x) + x = self._conv_4(x) + return x + + +class MiddleFlowBottleneckBlock(nn.Layer): + """Basic middle flow bottleneck block for Xception.""" + def __init__(self, input_channels: int, output_channels: int, name: str): + super(MiddleFlowBottleneckBlock, self).__init__() + + self._conv_0 = SeparableConv(input_channels, output_channels, stride=1, name=name + "_branch2a_weights") + self._conv_1 = SeparableConv(output_channels, output_channels, stride=1, name=name + "_branch2b_weights") + self._conv_2 = SeparableConv(output_channels, output_channels, stride=1, name=name + "_branch2c_weights") + + def forward(self, inputs: paddle.Tensor): + conv0 = F.relu(inputs) + conv0 = self._conv_0(conv0) + conv1 = F.relu(conv0) + conv1 = self._conv_1(conv1) + conv2 = F.relu(conv1) + conv2 = self._conv_2(conv2) + return paddle.elementwise_add(x=inputs, y=conv2) + + +class MiddleFlow(nn.Layer): + """Middle flow for Xception.""" + def __init__(self, block_num: int = 8): + super(MiddleFlow, self).__init__() + + self.block_num = block_num + self._conv_0 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_0") + self._conv_1 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_1") + self._conv_2 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_2") + self._conv_3 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_3") + self._conv_4 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_4") + self._conv_5 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_5") + self._conv_6 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_6") + self._conv_7 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_7") + if block_num == 16: + self._conv_8 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_8") + self._conv_9 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_9") + self._conv_10 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_10") + self._conv_11 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_11") + self._conv_12 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_12") + self._conv_13 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_13") + self._conv_14 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_14") + self._conv_15 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_15") + + def forward(self, inputs: paddle.Tensor): + x = self._conv_0(inputs) + x = self._conv_1(x) + x = self._conv_2(x) + x = self._conv_3(x) + x = self._conv_4(x) + x = self._conv_5(x) + x = self._conv_6(x) + x = self._conv_7(x) + if self.block_num == 16: + x = self._conv_8(x) + x = self._conv_9(x) + x = self._conv_10(x) + x = self._conv_11(x) + x = self._conv_12(x) + x = self._conv_13(x) + x = self._conv_14(x) + x = self._conv_15(x) + return x + + +class ExitFlowBottleneckBlock(nn.Layer): + """Basic exit flow bottleneck block for Xception.""" + def __init__(self, input_channels: int, output_channels1: int, output_channels2: int, name: str): + super(ExitFlowBottleneckBlock, self).__init__() + + self._short = Conv2d(in_channels=input_channels, + out_channels=output_channels2, + kernel_size=1, + stride=2, + padding=0, + weight_attr=ParamAttr(name + "_branch1_weights"), + bias_attr=False) + self._conv_1 = SeparableConv(input_channels, output_channels1, stride=1, name=name + "_branch2a_weights") + self._conv_2 = SeparableConv(output_channels1, output_channels2, stride=1, name=name + "_branch2b_weights") + self._pool = MaxPool2d(kernel_size=3, stride=2, padding=1) + + def forward(self, inputs: paddle.Tensor): + short = self._short(inputs) + conv0 = F.relu(inputs) + conv1 = self._conv_1(conv0) + conv2 = F.relu(conv1) + conv2 = self._conv_2(conv2) + pool = self._pool(conv2) + return paddle.elementwise_add(x=short, y=pool) + + +class ExitFlow(nn.Layer): + """Exit flow for Xception.""" + def __init__(self, class_dim: int): + super(ExitFlow, self).__init__() + + name = "exit_flow" + + self._conv_0 = ExitFlowBottleneckBlock(728, 728, 1024, name=name + "_1") + self._conv_1 = SeparableConv(1024, 1536, stride=1, name=name + "_2") + self._conv_2 = SeparableConv(1536, 2048, stride=1, name=name + "_3") + self._pool = AdaptiveAvgPool2d(1) + stdv = 1.0 / math.sqrt(2048 * 1.0) + self._out = Linear(2048, + class_dim, + weight_attr=ParamAttr(name="fc_weights", initializer=Uniform(-stdv, stdv)), + bias_attr=ParamAttr(name="fc_offset")) + + def forward(self, inputs: paddle.Tensor): + conv0 = self._conv_0(inputs) + conv1 = self._conv_1(conv0) + conv1 = F.relu(conv1) + conv2 = self._conv_2(conv1) + conv2 = F.relu(conv2) + pool = self._pool(conv2) + pool = paddle.reshape(pool, [0, -1]) + out = self._out(pool) + return out + + +@moduleinfo(name="xception41_imagenet", + type="CV/classification", + author="paddlepaddle", + author_email="", + summary="Xception41_imagenet is a classification model, " + "this module is trained with Imagenet dataset.", + version="1.1.0", + meta=ImageClassifierModule) +class Xception41(nn.Layer): + """Xception41 model.""" + def __init__(self, class_dim: int = 1000, load_checkpoint: str = None): + super(Xception41, self).__init__() + self.entry_flow_block_num = 3 + self.middle_flow_block_num = 8 + self._entry_flow = EntryFlow(self.entry_flow_block_num) + self._middle_flow = MiddleFlow(self.middle_flow_block_num) + self._exit_flow = ExitFlow(class_dim) + + if load_checkpoint is not None: + model_dict = paddle.load(load_checkpoint)[0] + self.set_dict(model_dict) + print("load custom checkpoint success") + + else: + checkpoint = os.path.join(self.directory, 'xception41_imagenet.pdparams') + if not os.path.exists(checkpoint): + os.system( + 'wget https://paddlehub.bj.bcebos.com/dygraph/image_classification/xception41_imagenet.pdparams -O' + + checkpoint) + model_dict = paddle.load(checkpoint)[0] + self.set_dict(model_dict) + print("load pretrained checkpoint success") + + def forward(self, inputs: paddle.Tensor): + x = self._entry_flow(inputs) + x = self._middle_flow(x) + x = self._exit_flow(x) + return x diff --git a/hub_module/modules/image/classification/xception65_imagenet/module.py b/hub_module/modules/image/classification/xception65_imagenet/module.py new file mode 100644 index 0000000000000000000000000000000000000000..54822055239d80fe904e14bee16cfaf4dd1e57b3 --- /dev/null +++ b/hub_module/modules/image/classification/xception65_imagenet/module.py @@ -0,0 +1,311 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys +import math + +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F +from paddle.nn import Conv2d, BatchNorm, Linear, Dropout +from paddle.nn import AdaptiveAvgPool2d, MaxPool2d, AvgPool2d +from paddle.nn.initializer import Uniform +from paddlehub.module.module import moduleinfo +from paddlehub.module.cv_module import ImageClassifierModule + + +class ConvBNLayer(nn.Layer): + """Basic conv bn layer.""" + def __init__(self, + num_channels: int, + num_filters: int, + filter_size: int, + stride: int = 1, + groups: int = 1, + act: str = None, + name: str = None): + super(ConvBNLayer, self).__init__() + + self._conv = Conv2d(in_channels=num_channels, + out_channels=num_filters, + kernel_size=filter_size, + stride=stride, + padding=(filter_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False) + bn_name = "bn_" + name + self._batch_norm = BatchNorm(num_filters, + act=act, + param_attr=ParamAttr(name=bn_name + "_scale"), + bias_attr=ParamAttr(name=bn_name + "_offset"), + moving_mean_name=bn_name + '_mean', + moving_variance_name=bn_name + '_variance') + + def forward(self, inputs: paddle.Tensor): + y = self._conv(inputs) + y = self._batch_norm(y) + return y + + +class SeparableConv(nn.Layer): + """Basic separable conv layer, it contains pointwise conv and depthwise conv.""" + def __init__(self, input_channels: int, output_channels: int, stride: int = 1, name: str = None): + super(SeparableConv, self).__init__() + + self._pointwise_conv = ConvBNLayer(input_channels, output_channels, 1, name=name + "_sep") + self._depthwise_conv = ConvBNLayer(output_channels, + output_channels, + 3, + stride=stride, + groups=output_channels, + name=name + "_dw") + + def forward(self, inputs: paddle.Tensor): + x = self._pointwise_conv(inputs) + x = self._depthwise_conv(x) + return x + + +class EntryFlowBottleneckBlock(nn.Layer): + """Basic entry flow bottleneck block for Xception.""" + def __init__(self, + input_channels: int, + output_channels: int, + stride: int = 2, + name: str = None, + relu_first: bool = False): + super(EntryFlowBottleneckBlock, self).__init__() + self.relu_first = relu_first + + self._short = Conv2d(in_channels=input_channels, + out_channels=output_channels, + kernel_size=1, + stride=stride, + padding=0, + weight_attr=ParamAttr(name + "_branch1_weights"), + bias_attr=False) + self._conv1 = SeparableConv(input_channels, output_channels, stride=1, name=name + "_branch2a_weights") + self._conv2 = SeparableConv(output_channels, output_channels, stride=1, name=name + "_branch2b_weights") + self._pool = MaxPool2d(kernel_size=3, stride=stride, padding=1) + + def forward(self, inputs: paddle.Tensor): + conv0 = inputs + short = self._short(inputs) + if self.relu_first: + conv0 = F.relu(conv0) + conv1 = self._conv1(conv0) + conv2 = F.relu(conv1) + conv2 = self._conv2(conv2) + pool = self._pool(conv2) + return paddle.elementwise_add(x=short, y=pool) + + +class EntryFlow(nn.Layer): + """Entry flow for Xception.""" + def __init__(self, block_num: int = 3): + super(EntryFlow, self).__init__() + + name = "entry_flow" + self.block_num = block_num + self._conv1 = ConvBNLayer(3, 32, 3, stride=2, act="relu", name=name + "_conv1") + self._conv2 = ConvBNLayer(32, 64, 3, act="relu", name=name + "_conv2") + if block_num == 3: + self._conv_0 = EntryFlowBottleneckBlock(64, 128, stride=2, name=name + "_0", relu_first=False) + self._conv_1 = EntryFlowBottleneckBlock(128, 256, stride=2, name=name + "_1", relu_first=True) + self._conv_2 = EntryFlowBottleneckBlock(256, 728, stride=2, name=name + "_2", relu_first=True) + elif block_num == 5: + self._conv_0 = EntryFlowBottleneckBlock(64, 128, stride=2, name=name + "_0", relu_first=False) + self._conv_1 = EntryFlowBottleneckBlock(128, 256, stride=1, name=name + "_1", relu_first=True) + self._conv_2 = EntryFlowBottleneckBlock(256, 256, stride=2, name=name + "_2", relu_first=True) + self._conv_3 = EntryFlowBottleneckBlock(256, 728, stride=1, name=name + "_3", relu_first=True) + self._conv_4 = EntryFlowBottleneckBlock(728, 728, stride=2, name=name + "_4", relu_first=True) + else: + sys.exit(-1) + + def forward(self, inputs: paddle.Tensor): + x = self._conv1(inputs) + x = self._conv2(x) + + if self.block_num == 3: + x = self._conv_0(x) + x = self._conv_1(x) + x = self._conv_2(x) + elif self.block_num == 5: + x = self._conv_0(x) + x = self._conv_1(x) + x = self._conv_2(x) + x = self._conv_3(x) + x = self._conv_4(x) + return x + + +class MiddleFlowBottleneckBlock(nn.Layer): + """Basic middle flow bottleneck block for Xception.""" + def __init__(self, input_channels: int, output_channels: int, name: str): + super(MiddleFlowBottleneckBlock, self).__init__() + + self._conv_0 = SeparableConv(input_channels, output_channels, stride=1, name=name + "_branch2a_weights") + self._conv_1 = SeparableConv(output_channels, output_channels, stride=1, name=name + "_branch2b_weights") + self._conv_2 = SeparableConv(output_channels, output_channels, stride=1, name=name + "_branch2c_weights") + + def forward(self, inputs: paddle.Tensor): + conv0 = F.relu(inputs) + conv0 = self._conv_0(conv0) + conv1 = F.relu(conv0) + conv1 = self._conv_1(conv1) + conv2 = F.relu(conv1) + conv2 = self._conv_2(conv2) + return paddle.elementwise_add(x=inputs, y=conv2) + + +class MiddleFlow(nn.Layer): + """Middle flow for Xception.""" + def __init__(self, block_num: int = 8): + super(MiddleFlow, self).__init__() + + self.block_num = block_num + self._conv_0 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_0") + self._conv_1 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_1") + self._conv_2 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_2") + self._conv_3 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_3") + self._conv_4 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_4") + self._conv_5 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_5") + self._conv_6 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_6") + self._conv_7 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_7") + if block_num == 16: + self._conv_8 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_8") + self._conv_9 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_9") + self._conv_10 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_10") + self._conv_11 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_11") + self._conv_12 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_12") + self._conv_13 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_13") + self._conv_14 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_14") + self._conv_15 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_15") + + def forward(self, inputs: paddle.Tensor): + x = self._conv_0(inputs) + x = self._conv_1(x) + x = self._conv_2(x) + x = self._conv_3(x) + x = self._conv_4(x) + x = self._conv_5(x) + x = self._conv_6(x) + x = self._conv_7(x) + if self.block_num == 16: + x = self._conv_8(x) + x = self._conv_9(x) + x = self._conv_10(x) + x = self._conv_11(x) + x = self._conv_12(x) + x = self._conv_13(x) + x = self._conv_14(x) + x = self._conv_15(x) + return x + + +class ExitFlowBottleneckBlock(nn.Layer): + """Basic exit flow bottleneck block for Xception.""" + def __init__(self, input_channels, output_channels1, output_channels2, name): + super(ExitFlowBottleneckBlock, self).__init__() + + self._short = Conv2d(in_channels=input_channels, + out_channels=output_channels2, + kernel_size=1, + stride=2, + padding=0, + weight_attr=ParamAttr(name + "_branch1_weights"), + bias_attr=False) + self._conv_1 = SeparableConv(input_channels, output_channels1, stride=1, name=name + "_branch2a_weights") + self._conv_2 = SeparableConv(output_channels1, output_channels2, stride=1, name=name + "_branch2b_weights") + self._pool = MaxPool2d(kernel_size=3, stride=2, padding=1) + + def forward(self, inputs: paddle.Tensor): + short = self._short(inputs) + conv0 = F.relu(inputs) + conv1 = self._conv_1(conv0) + conv2 = F.relu(conv1) + conv2 = self._conv_2(conv2) + pool = self._pool(conv2) + return paddle.elementwise_add(x=short, y=pool) + + +class ExitFlow(nn.Layer): + """Exit flow for Xception.""" + def __init__(self, class_dim): + super(ExitFlow, self).__init__() + + name = "exit_flow" + + self._conv_0 = ExitFlowBottleneckBlock(728, 728, 1024, name=name + "_1") + self._conv_1 = SeparableConv(1024, 1536, stride=1, name=name + "_2") + self._conv_2 = SeparableConv(1536, 2048, stride=1, name=name + "_3") + self._pool = AdaptiveAvgPool2d(1) + stdv = 1.0 / math.sqrt(2048 * 1.0) + self._out = Linear(2048, + class_dim, + weight_attr=ParamAttr(name="fc_weights", initializer=Uniform(-stdv, stdv)), + bias_attr=ParamAttr(name="fc_offset")) + + def forward(self, inputs: paddle.Tensor): + conv0 = self._conv_0(inputs) + conv1 = self._conv_1(conv0) + conv1 = F.relu(conv1) + conv2 = self._conv_2(conv1) + conv2 = F.relu(conv2) + pool = self._pool(conv2) + pool = paddle.reshape(pool, [0, -1]) + out = self._out(pool) + return out + + +@moduleinfo(name="xception65_imagenet", + type="CV/classification", + author="paddlepaddle", + author_email="", + summary="Xception65_imagenet is a classification model, " + "this module is trained with Imagenet dataset.", + version="1.1.0", + meta=ImageClassifierModule) +class Xception65(nn.Layer): + def __init__(self, class_dim=1000, load_checkpoint: str = None): + super(Xception65, self).__init__() + self.entry_flow_block_num = 3 + self.middle_flow_block_num = 16 + self._entry_flow = EntryFlow(self.entry_flow_block_num) + self._middle_flow = MiddleFlow(self.middle_flow_block_num) + self._exit_flow = ExitFlow(class_dim) + + if load_checkpoint is not None: + model_dict = paddle.load(load_checkpoint)[0] + self.set_dict(model_dict) + print("load custom checkpoint success") + + else: + checkpoint = os.path.join(self.directory, 'xception65_imagenet.pdparams') + if not os.path.exists(checkpoint): + os.system( + 'wget https://paddlehub.bj.bcebos.com/dygraph/image_classification/xception65_imagenet.pdparams -O' + + checkpoint) + model_dict = paddle.load(checkpoint)[0] + self.set_dict(model_dict) + print("load pretrained checkpoint success") + + def forward(self, inputs): + x = self._entry_flow(inputs) + x = self._middle_flow(x) + x = self._exit_flow(x) + return x diff --git a/hub_module/modules/image/classification/xception71_imagenet/module.py b/hub_module/modules/image/classification/xception71_imagenet/module.py new file mode 100644 index 0000000000000000000000000000000000000000..41cc0e0e9ad17fb0685f0be3434f7c04667c2fcc --- /dev/null +++ b/hub_module/modules/image/classification/xception71_imagenet/module.py @@ -0,0 +1,310 @@ +# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys +import math + +import paddle +from paddle import ParamAttr +import paddle.nn as nn +import paddle.nn.functional as F +from paddle.nn import Conv2d, BatchNorm, Linear, Dropout +from paddle.nn import AdaptiveAvgPool2d, MaxPool2d, AvgPool2d +from paddle.nn.initializer import Uniform +from paddlehub.module.module import moduleinfo +from paddlehub.module.cv_module import ImageClassifierModule + + +class ConvBNLayer(nn.Layer): + """Basic conv bn layer.""" + def __init__(self, + num_channels: int, + num_filters: int, + filter_size: int, + stride: int = 1, + groups: int = 1, + act: str = None, + name: str = None): + super(ConvBNLayer, self).__init__() + + self._conv = Conv2d(in_channels=num_channels, + out_channels=num_filters, + kernel_size=filter_size, + stride=stride, + padding=(filter_size - 1) // 2, + groups=groups, + weight_attr=ParamAttr(name=name + "_weights"), + bias_attr=False) + bn_name = "bn_" + name + self._batch_norm = BatchNorm(num_filters, + act=act, + param_attr=ParamAttr(name=bn_name + "_scale"), + bias_attr=ParamAttr(name=bn_name + "_offset"), + moving_mean_name=bn_name + '_mean', + moving_variance_name=bn_name + '_variance') + + def forward(self, inputs: paddle.Tensor): + y = self._conv(inputs) + y = self._batch_norm(y) + return y + + +class SeparableConv(nn.Layer): + """Basic separable conv layer, it contains pointwise conv and depthwise conv.""" + def __init__(self, input_channels: int, output_channels: int, stride: int = 1, name: str = None): + super(SeparableConv, self).__init__() + + self._pointwise_conv = ConvBNLayer(input_channels, output_channels, 1, name=name + "_sep") + self._depthwise_conv = ConvBNLayer(output_channels, + output_channels, + 3, + stride=stride, + groups=output_channels, + name=name + "_dw") + + def forward(self, inputs: paddle.Tensor): + x = self._pointwise_conv(inputs) + x = self._depthwise_conv(x) + return x + + +class EntryFlowBottleneckBlock(nn.Layer): + """Basic entry flow bottleneck block for Xception.""" + def __init__(self, + input_channels: int, + output_channels: int, + stride: int = 2, + name: str = None, + relu_first: bool = False): + super(EntryFlowBottleneckBlock, self).__init__() + self.relu_first = relu_first + + self._short = Conv2d(in_channels=input_channels, + out_channels=output_channels, + kernel_size=1, + stride=stride, + padding=0, + weight_attr=ParamAttr(name + "_branch1_weights"), + bias_attr=False) + self._conv1 = SeparableConv(input_channels, output_channels, stride=1, name=name + "_branch2a_weights") + self._conv2 = SeparableConv(output_channels, output_channels, stride=1, name=name + "_branch2b_weights") + self._pool = MaxPool2d(kernel_size=3, stride=stride, padding=1) + + def forward(self, inputs: paddle.Tensor): + conv0 = inputs + short = self._short(inputs) + if self.relu_first: + conv0 = F.relu(conv0) + conv1 = self._conv1(conv0) + conv2 = F.relu(conv1) + conv2 = self._conv2(conv2) + pool = self._pool(conv2) + return paddle.elementwise_add(x=short, y=pool) + + +class EntryFlow(nn.Layer): + """Entry flow for Xception.""" + def __init__(self, block_num: int = 3): + super(EntryFlow, self).__init__() + + name = "entry_flow" + self.block_num = block_num + self._conv1 = ConvBNLayer(3, 32, 3, stride=2, act="relu", name=name + "_conv1") + self._conv2 = ConvBNLayer(32, 64, 3, act="relu", name=name + "_conv2") + if block_num == 3: + self._conv_0 = EntryFlowBottleneckBlock(64, 128, stride=2, name=name + "_0", relu_first=False) + self._conv_1 = EntryFlowBottleneckBlock(128, 256, stride=2, name=name + "_1", relu_first=True) + self._conv_2 = EntryFlowBottleneckBlock(256, 728, stride=2, name=name + "_2", relu_first=True) + elif block_num == 5: + self._conv_0 = EntryFlowBottleneckBlock(64, 128, stride=2, name=name + "_0", relu_first=False) + self._conv_1 = EntryFlowBottleneckBlock(128, 256, stride=1, name=name + "_1", relu_first=True) + self._conv_2 = EntryFlowBottleneckBlock(256, 256, stride=2, name=name + "_2", relu_first=True) + self._conv_3 = EntryFlowBottleneckBlock(256, 728, stride=1, name=name + "_3", relu_first=True) + self._conv_4 = EntryFlowBottleneckBlock(728, 728, stride=2, name=name + "_4", relu_first=True) + else: + sys.exit(-1) + + def forward(self, inputs: paddle.Tensor): + x = self._conv1(inputs) + x = self._conv2(x) + + if self.block_num == 3: + x = self._conv_0(x) + x = self._conv_1(x) + x = self._conv_2(x) + elif self.block_num == 5: + x = self._conv_0(x) + x = self._conv_1(x) + x = self._conv_2(x) + x = self._conv_3(x) + x = self._conv_4(x) + return x + + +class MiddleFlowBottleneckBlock(nn.Layer): + """Basic middle flow bottleneck block for Xception.""" + def __init__(self, input_channels: int, output_channels: int, name: str): + super(MiddleFlowBottleneckBlock, self).__init__() + + self._conv_0 = SeparableConv(input_channels, output_channels, stride=1, name=name + "_branch2a_weights") + self._conv_1 = SeparableConv(output_channels, output_channels, stride=1, name=name + "_branch2b_weights") + self._conv_2 = SeparableConv(output_channels, output_channels, stride=1, name=name + "_branch2c_weights") + + def forward(self, inputs: paddle.Tensor): + conv0 = F.relu(inputs) + conv0 = self._conv_0(conv0) + conv1 = F.relu(conv0) + conv1 = self._conv_1(conv1) + conv2 = F.relu(conv1) + conv2 = self._conv_2(conv2) + return paddle.elementwise_add(x=inputs, y=conv2) + + +class MiddleFlow(nn.Layer): + """Middle flow for Xception.""" + def __init__(self, block_num: int = 8): + super(MiddleFlow, self).__init__() + + self.block_num = block_num + self._conv_0 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_0") + self._conv_1 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_1") + self._conv_2 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_2") + self._conv_3 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_3") + self._conv_4 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_4") + self._conv_5 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_5") + self._conv_6 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_6") + self._conv_7 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_7") + if block_num == 16: + self._conv_8 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_8") + self._conv_9 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_9") + self._conv_10 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_10") + self._conv_11 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_11") + self._conv_12 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_12") + self._conv_13 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_13") + self._conv_14 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_14") + self._conv_15 = MiddleFlowBottleneckBlock(728, 728, name="middle_flow_15") + + def forward(self, inputs: paddle.Tensor): + x = self._conv_0(inputs) + x = self._conv_1(x) + x = self._conv_2(x) + x = self._conv_3(x) + x = self._conv_4(x) + x = self._conv_5(x) + x = self._conv_6(x) + x = self._conv_7(x) + if self.block_num == 16: + x = self._conv_8(x) + x = self._conv_9(x) + x = self._conv_10(x) + x = self._conv_11(x) + x = self._conv_12(x) + x = self._conv_13(x) + x = self._conv_14(x) + x = self._conv_15(x) + return x + + +class ExitFlowBottleneckBlock(nn.Layer): + """Basic exit flow bottleneck block for Xception.""" + def __init__(self, input_channels: int, output_channels1: int, output_channels2: int, name: str): + super(ExitFlowBottleneckBlock, self).__init__() + + self._short = Conv2d(in_channels=input_channels, + out_channels=output_channels2, + kernel_size=1, + stride=2, + padding=0, + weight_attr=ParamAttr(name + "_branch1_weights"), + bias_attr=False) + self._conv_1 = SeparableConv(input_channels, output_channels1, stride=1, name=name + "_branch2a_weights") + self._conv_2 = SeparableConv(output_channels1, output_channels2, stride=1, name=name + "_branch2b_weights") + self._pool = MaxPool2d(kernel_size=3, stride=2, padding=1) + + def forward(self, inputs: paddle.Tensor): + short = self._short(inputs) + conv0 = F.relu(inputs) + conv1 = self._conv_1(conv0) + conv2 = F.relu(conv1) + conv2 = self._conv_2(conv2) + pool = self._pool(conv2) + return paddle.elementwise_add(x=short, y=pool) + + +class ExitFlow(nn.Layer): + def __init__(self, class_dim: int): + super(ExitFlow, self).__init__() + + name = "exit_flow" + + self._conv_0 = ExitFlowBottleneckBlock(728, 728, 1024, name=name + "_1") + self._conv_1 = SeparableConv(1024, 1536, stride=1, name=name + "_2") + self._conv_2 = SeparableConv(1536, 2048, stride=1, name=name + "_3") + self._pool = AdaptiveAvgPool2d(1) + stdv = 1.0 / math.sqrt(2048 * 1.0) + self._out = Linear(2048, + class_dim, + weight_attr=ParamAttr(name="fc_weights", initializer=Uniform(-stdv, stdv)), + bias_attr=ParamAttr(name="fc_offset")) + + def forward(self, inputs: paddle.Tensor): + conv0 = self._conv_0(inputs) + conv1 = self._conv_1(conv0) + conv1 = F.relu(conv1) + conv2 = self._conv_2(conv1) + conv2 = F.relu(conv2) + pool = self._pool(conv2) + pool = paddle.reshape(pool, [0, -1]) + out = self._out(pool) + return out + + +@moduleinfo(name="xception71_imagenet", + type="CV/classification", + author="paddlepaddle", + author_email="", + summary="Xception71_imagenet is a classification model, " + "this module is trained with Imagenet dataset.", + version="1.1.0", + meta=ImageClassifierModule) +class Xception71(nn.Layer): + def __init__(self, class_dim=1000, load_checkpoint: str = None): + super(Xception71, self).__init__() + self.entry_flow_block_num = 5 + self.middle_flow_block_num = 16 + self._entry_flow = EntryFlow(self.entry_flow_block_num) + self._middle_flow = MiddleFlow(self.middle_flow_block_num) + self._exit_flow = ExitFlow(class_dim) + + if load_checkpoint is not None: + model_dict = paddle.load(load_checkpoint)[0] + self.set_dict(model_dict) + print("load custom checkpoint success") + + else: + checkpoint = os.path.join(self.directory, 'xception71_imagenet.pdparams') + if not os.path.exists(checkpoint): + os.system( + 'wget https://paddlehub.bj.bcebos.com/dygraph/image_classification/xception71_imagenet.pdparams -O' + + checkpoint) + model_dict = paddle.load(checkpoint)[0] + self.set_dict(model_dict) + print("load pretrained checkpoint success") + + def forward(self, inputs): + x = self._entry_flow(inputs) + x = self._middle_flow(x) + x = self._exit_flow(x) + return x