diff --git a/hapi/model.py b/hapi/model.py index 4d27355603f111dfc637d68e7efa9695369b504b..50932fc46be1106d6a3fff267adae68e33ddc345 100644 --- a/hapi/model.py +++ b/hapi/model.py @@ -1135,7 +1135,7 @@ class Model(fluid.dygraph.Layer): test_data, batch_size=1, num_workers=0, - stack_outputs=True): + stack_outputs=False): """ FIXME: add more comments and usage Args: @@ -1183,20 +1183,34 @@ class Model(fluid.dygraph.Layer): loader = test_loader() outputs = [] - for data in tqdm.tqdm(loader): + count = 0 + for i, data in tqdm.tqdm(enumerate(loader)): data = flatten(data) - outputs.append(self.test(data[:len(self._inputs)])) + out = to_list(self.test(data[:len(self._inputs)])) + outputs.append(out) + count += out[0].shape[0] + + if test_loader is not None and self._adapter._nranks > 1 \ + and isinstance(test_loader, DataLoader) \ + and count > len(test_loader.dataset): + size = outputs[-1][0].shape[0] - (count - len(test_loader.dataset)) + outputs[-1] = [o[:size] for o in outputs[-1]] # NOTE: for lod tensor output, we should not stack outputs # for stacking may loss its detail info - outputs = list(zip(*outputs)) + if stack_outputs: - outputs = [np.stack(outs, axis=0) for outs in outputs] + stack_outs = [] + for i in range(len(outputs[0])): + split_outs = [] + for out in outputs: + split_outs.append(out[i]) + stack_outs.append(np.vstack(split_outs)) + + outputs = stack_outs self._test_dataloader = None - if test_loader is not None and self._adapter._nranks > 1 \ - and isinstance(test_loader, DataLoader): - outputs = [o[:len(test_loader.dataset)] for o in outputs] + return outputs def set_eval_data(self, eval_data): diff --git a/hapi/vision/models/darknet.py b/hapi/vision/models/darknet.py index 027cb4f2094e37ede347c090c5849cfcbf00611e..42f6a2e2dc485c1b0160f74cffe7a0ffd3f58957 100755 --- a/hapi/vision/models/darknet.py +++ b/hapi/vision/models/darknet.py @@ -12,11 +12,12 @@ #See the License for the specific language governing permissions and #limitations under the License. +import math import paddle.fluid as fluid from paddle.fluid.param_attr import ParamAttr from paddle.fluid.regularizer import L2Decay -from paddle.fluid.dygraph.nn import Conv2D, BatchNorm +from paddle.fluid.dygraph.nn import Conv2D, BatchNorm, Pool2D, Linear from hapi.model import Model from hapi.download import get_weights_path @@ -25,8 +26,8 @@ __all__ = ['DarkNet', 'ConvBNLayer', 'darknet53'] # {num_layers: (url, md5)} pretrain_infos = { - 53: ('https://paddlemodels.bj.bcebos.com/hapi/darknet53.pdparams', - '2506357a5c31e865785112fc614a487d') + 53: ('https://paddlemodels.bj.bcebos.com/hapi/darknet53.pdparams', + '2506357a5c31e865785112fc614a487d') } @@ -70,13 +71,9 @@ class ConvBNLayer(fluid.dygraph.Layer): out = fluid.layers.leaky_relu(x=out, alpha=0.1) return out + class DownSample(fluid.dygraph.Layer): - def __init__(self, - ch_in, - ch_out, - filter_size=3, - stride=2, - padding=1): + def __init__(self, ch_in, ch_out, filter_size=3, stride=2, padding=1): super(DownSample, self).__init__() @@ -87,46 +84,45 @@ class DownSample(fluid.dygraph.Layer): stride=stride, padding=padding) self.ch_out = ch_out + def forward(self, inputs): out = self.conv_bn_layer(inputs) return out + class BasicBlock(fluid.dygraph.Layer): def __init__(self, ch_in, ch_out): super(BasicBlock, self).__init__() self.conv1 = ConvBNLayer( - ch_in=ch_in, - ch_out=ch_out, - filter_size=1, - stride=1, - padding=0) + ch_in=ch_in, ch_out=ch_out, filter_size=1, stride=1, padding=0) self.conv2 = ConvBNLayer( ch_in=ch_out, - ch_out=ch_out*2, + ch_out=ch_out * 2, filter_size=3, stride=1, padding=1) + def forward(self, inputs): conv1 = self.conv1(inputs) conv2 = self.conv2(conv1) out = fluid.layers.elementwise_add(x=inputs, y=conv2, act=None) return out + class LayerWarp(fluid.dygraph.Layer): def __init__(self, ch_in, ch_out, count): - super(LayerWarp,self).__init__() + super(LayerWarp, self).__init__() self.basicblock0 = BasicBlock(ch_in, ch_out) self.res_out_list = [] - for i in range(1,count): + for i in range(1, count): res_out = self.add_sublayer("basic_block_%d" % (i), - BasicBlock( - ch_out*2, - ch_out)) + BasicBlock(ch_out * 2, ch_out)) self.res_out_list.append(res_out) self.ch_out = ch_out - def forward(self,inputs): + + def forward(self, inputs): y = self.basicblock0(inputs) for basic_block_i in self.res_out_list: y = basic_block_i(y) @@ -136,67 +132,88 @@ class LayerWarp(fluid.dygraph.Layer): DarkNet_cfg = {53: ([1, 2, 8, 8, 4])} -class DarkNet(fluid.dygraph.Layer): +class DarkNet(Model): """DarkNet model from `"YOLOv3: An Incremental Improvement" `_ Args: num_layers (int): layer number of DarkNet, only 53 supported currently, default: 53. - ch_in (int): channel number of input data, default 3. + num_classes (int): output dim of last fc layer. If num_classes <=0, last fc layer + will not be defined. Default: 1000. + with_pool (bool): use pool before the last fc layer or not. Default: True. + classifier_activation (str): activation for the last fc layer. Default: 'softmax'. """ - def __init__(self, num_layers=53, ch_in=3): + def __init__(self, + num_layers=53, + num_classes=1000, + with_pool=True, + classifier_activation='softmax'): super(DarkNet, self).__init__() assert num_layers in DarkNet_cfg.keys(), \ "only support num_layers in {} currently" \ .format(DarkNet_cfg.keys()) self.stages = DarkNet_cfg[num_layers] self.stages = self.stages[0:5] - + self.num_classes = 1000 + self.with_pool = True + ch_in = 3 self.conv0 = ConvBNLayer( - ch_in=ch_in, - ch_out=32, - filter_size=3, - stride=1, - padding=1) + ch_in=ch_in, ch_out=32, filter_size=3, stride=1, padding=1) - self.downsample0 = DownSample( - ch_in=32, - ch_out=32 * 2) + self.downsample0 = DownSample(ch_in=32, ch_out=32 * 2) self.darknet53_conv_block_list = [] self.downsample_list = [] - ch_in = [64,128,256,512,1024] + ch_in = [64, 128, 256, 512, 1024] for i, stage in enumerate(self.stages): - conv_block = self.add_sublayer( - "stage_%d" % (i), - LayerWarp( - int(ch_in[i]), - 32*(2**i), - stage)) + conv_block = self.add_sublayer("stage_%d" % (i), + LayerWarp( + int(ch_in[i]), 32 * (2**i), + stage)) self.darknet53_conv_block_list.append(conv_block) + for i in range(len(self.stages) - 1): downsample = self.add_sublayer( "stage_%d_downsample" % i, DownSample( - ch_in = 32*(2**(i+1)), - ch_out = 32*(2**(i+2)))) + ch_in=32 * (2**(i + 1)), ch_out=32 * (2**(i + 2)))) self.downsample_list.append(downsample) - def forward(self,inputs): - + if self.with_pool: + self.global_pool = Pool2D( + pool_size=7, pool_type='avg', global_pooling=True) + + if self.num_classes > 0: + stdv = 1.0 / math.sqrt(32 * (2**(i + 2))) + self.fc_input_dim = 32 * (2**(i + 2)) + + self.fc = Linear( + self.fc_input_dim, + num_classes, + act='softmax', + param_attr=fluid.param_attr.ParamAttr( + initializer=fluid.initializer.Uniform(-stdv, stdv))) + + def forward(self, inputs): + out = self.conv0(inputs) out = self.downsample0(out) - blocks = [] + for i, conv_block_i in enumerate(self.darknet53_conv_block_list): out = conv_block_i(out) - blocks.append(out) if i < len(self.stages) - 1: out = self.downsample_list[i](out) - return blocks[-1:-4:-1] + + if self.with_pool: + out = self.global_pool(out) + if self.num_classes > 0: + out = fluid.layers.reshape(out, shape=[-1, self.fc_input_dim]) + out = self.fc(out) + return out -def _darknet(num_layers=53, input_channels=3, pretrained=True): - model = DarkNet(num_layers, input_channels) +def _darknet(num_layers=53, pretrained=False, **kwargs): + model = DarkNet(num_layers, **kwargs) if pretrained: assert num_layers in pretrain_infos.keys(), \ "DarkNet{} do not have pretrained weights now, " \ @@ -208,7 +225,7 @@ def _darknet(num_layers=53, input_channels=3, pretrained=True): return model -def darknet53(input_channels=3, pretrained=True): +def darknet53(pretrained=False, **kwargs): """DarkNet 53-layer model Args: @@ -216,4 +233,4 @@ def darknet53(input_channels=3, pretrained=True): pretrained (bool): If True, returns a model pre-trained on ImageNet, default True. """ - return _darknet(53, input_channels, pretrained) + return _darknet(53, pretrained, **kwargs) diff --git a/hapi/vision/models/lenet.py b/hapi/vision/models/lenet.py new file mode 100644 index 0000000000000000000000000000000000000000..0f88bc91cb130f1432ecf29e6aae10755be1392d --- /dev/null +++ b/hapi/vision/models/lenet.py @@ -0,0 +1,58 @@ +# 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 paddle.fluid as fluid + +from paddle.fluid.dygraph.nn import Conv2D, BatchNorm, Pool2D, Linear +from paddle.fluid.dygraph.container import Sequential +from hapi.model import Model + +__all__ = ['LeNet'] + + +class LeNet(Model): + """LeNet model from + `"LeCun Y, Bottou L, Bengio Y, et al. Gradient-based learning applied to document recognition[J]. Proceedings of the IEEE, 1998, 86(11): 2278-2324.`_ + + Args: + num_classes (int): output dim of last fc layer. If num_classes <=0, last fc layer + will not be defined. Default: 10. + classifier_activation (str): activation for the last fc layer. Default: 'softmax'. + """ + + def __init__(self, num_classes=10, classifier_activation='softmax'): + super(LeNet, self).__init__() + self.num_classes = num_classes + self.features = Sequential( + Conv2D( + 1, 6, 3, stride=1, padding=1), + Pool2D(2, 'max', 2), + Conv2D( + 6, 16, 5, stride=1, padding=0), + Pool2D(2, 'max', 2)) + + if num_classes > 0: + self.fc = Sequential( + Linear(400, 120), + Linear(120, 84), + Linear( + 84, 10, act=classifier_activation)) + + def forward(self, inputs): + x = self.features(inputs) + + if self.num_classes > 0: + x = fluid.layers.flatten(x, 1) + x = self.fc(x) + return x diff --git a/mnist.py b/mnist.py index 39f323ac6454ed7dd06359017703401321428611..4e6240c2d5783b820a8f33f3d75064bd1d495693 100644 --- a/mnist.py +++ b/mnist.py @@ -24,10 +24,10 @@ import numpy as np from paddle import fluid from paddle.fluid.optimizer import Momentum from paddle.fluid.dygraph.nn import Conv2D, Pool2D, Linear -from vision.datasets import MNIST as MnistDataset +from hapi.datasets.mnist import MNIST as MnistDataset -from model import Model, CrossEntropy, Input, set_device -from metrics import Accuracy +from hapi.model import Model, CrossEntropy, Input, set_device +from hapi.metrics import Accuracy class SimpleImgConvPool(fluid.dygraph.Layer): diff --git a/tests/test_model.py b/tests/test_model.py index 7fe414c0c914b561cc78083f1fe89b0c79e77da2..3aea2d1353e2e414d35e9b6714bdb0985d1249c7 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -190,7 +190,8 @@ class TestModel(unittest.TestCase): eval_result = model.evaluate(val_dataset, batch_size=batch_size) - output = model.predict(test_dataset, batch_size=batch_size) + output = model.predict( + test_dataset, batch_size=batch_size, stack_outputs=True) np.testing.assert_equal(output[0].shape[0], len(test_dataset))