test_imperative_resnet.py 12.9 KB
Newer Older
M
minqiyang 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
#
# 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 contextlib
import unittest
import numpy as np
import six

import paddle
import paddle.fluid as fluid
from paddle.fluid import core
from paddle.fluid.optimizer import SGDOptimizer
from paddle.fluid.imperative.nn import Conv2D, Pool2D, BatchNorm, FC
from paddle.fluid.imperative.base import to_variable
from test_imperative_base import new_program_scope

M
minqiyang 已提交
28
batch_size = 8
M
minqiyang 已提交
29 30 31 32 33 34
train_parameters = {
    "input_size": [3, 224, 224],
    "input_mean": [0.485, 0.456, 0.406],
    "input_std": [0.229, 0.224, 0.225],
    "learning_strategy": {
        "name": "piecewise_decay",
M
minqiyang 已提交
35
        "batch_size": batch_size,
M
minqiyang 已提交
36 37
        "epochs": [30, 60, 90],
        "steps": [0.1, 0.01, 0.001, 0.0001]
M
minqiyang 已提交
38
    },
M
minqiyang 已提交
39
    "batch_size": batch_size,
M
minqiyang 已提交
40 41
    "lr": 0.1,
    "total_images": 1281164,
M
minqiyang 已提交
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
}


def optimizer_setting(params):
    ls = params["learning_strategy"]
    if ls["name"] == "piecewise_decay":
        if "total_images" not in params:
            total_images = 1281167
        else:
            total_images = params["total_images"]
        batch_size = ls["batch_size"]
        step = int(total_images / batch_size + 1)

        bd = [step * e for e in ls["epochs"]]
        base_lr = params["lr"]
        lr = []
        lr = [base_lr * (0.1**i) for i in range(len(bd) + 1)]
M
minqiyang 已提交
59
        optimizer = fluid.optimizer.SGD(learning_rate=params["lr"])
M
minqiyang 已提交
60
        # TODO(minqiyang): Add learning rate scheduler support to imperative mode
M
minqiyang 已提交
61 62 63 64 65 66
        #  optimizer = fluid.optimizer.Momentum(
    #  learning_rate=params["lr"],
    #  learning_rate=fluid.layers.piecewise_decay(
    #  boundaries=bd, values=lr),
    #  momentum=0.9,
    #  regularization=fluid.regularizer.L2Decay(1e-4))
M
minqiyang 已提交
67 68 69 70 71

    return optimizer


class ConvBNLayer(fluid.imperative.Layer):
M
minqiyang 已提交
72 73 74 75 76 77 78
    def __init__(self,
                 num_channels,
                 num_filters,
                 filter_size,
                 stride=1,
                 groups=1,
                 act=None):
M
minqiyang 已提交
79 80 81
        super(ConvBNLayer, self).__init__()

        self._conv = Conv2D(
M
minqiyang 已提交
82 83 84 85 86
            num_channels=num_channels,
            num_filters=num_filters,
            filter_size=filter_size,
            stride=stride,
            padding=(filter_size - 1) // 2,
M
minqiyang 已提交
87 88 89 90 91 92 93 94 95 96 97 98 99 100
            groups=groups,
            act=None,
            bias_attr=None)

        self._batch_norm = BatchNorm(num_filters, act=act)

    def forward(self, inputs):
        y = self._conv(inputs)
        y = self._batch_norm(y)

        return y


class BottleneckBlock(fluid.imperative.Layer):
M
minqiyang 已提交
101
    def __init__(self, num_channels, num_filters, stride, shortcut=True):
M
minqiyang 已提交
102 103 104
        super(BottleneckBlock, self).__init__()

        self.conv0 = ConvBNLayer(
M
minqiyang 已提交
105 106 107 108
            num_channels=num_channels,
            num_filters=num_filters,
            filter_size=1,
            act='relu')
M
minqiyang 已提交
109
        self.conv1 = ConvBNLayer(
M
minqiyang 已提交
110 111 112 113 114
            num_channels=num_filters,
            num_filters=num_filters,
            filter_size=3,
            stride=stride,
            act='relu')
M
minqiyang 已提交
115
        self.conv2 = ConvBNLayer(
M
minqiyang 已提交
116 117 118 119
            num_channels=num_filters,
            num_filters=num_filters * 4,
            filter_size=1,
            act=None)
M
minqiyang 已提交
120

M
minqiyang 已提交
121
        if not shortcut:
M
minqiyang 已提交
122
            self.short = ConvBNLayer(
M
minqiyang 已提交
123 124 125 126
                num_channels=num_channels,
                num_filters=num_filters * 4,
                filter_size=1,
                stride=stride)
M
minqiyang 已提交
127 128 129

        self.shortcut = shortcut

M
minqiyang 已提交
130 131
        self._num_channels_out = num_filters * 4

M
minqiyang 已提交
132
    def forward(self, inputs):
M
minqiyang 已提交
133 134 135
        y = self.conv0(inputs)
        conv1 = self.conv1(y)
        conv2 = self.conv2(conv1)
M
minqiyang 已提交
136 137

        if self.shortcut:
M
minqiyang 已提交
138 139 140
            short = inputs
        else:
            short = self.short(inputs)
M
minqiyang 已提交
141

M
minqiyang 已提交
142
        return fluid.layers.elementwise_add(x=short, y=conv2, act='relu')
M
minqiyang 已提交
143 144 145 146


class ResNet(fluid.imperative.Layer):
    def __init__(self, layers=50, class_dim=1000):
M
minqiyang 已提交
147 148
        super(ResNet, self).__init__()

M
minqiyang 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161 162
        self.layers = layers
        supported_layers = [50, 101, 152]
        assert layers in supported_layers, \
            "supported layers are {} but input layer is {}".format(supported_layers, layers)

        if layers == 50:
            depth = [3, 4, 6, 3]
        elif layers == 101:
            depth = [3, 4, 23, 3]
        elif layers == 152:
            depth = [3, 8, 36, 3]
        num_filters = [64, 128, 256, 512]

        self.conv = ConvBNLayer(
M
minqiyang 已提交
163
            num_channels=3, num_filters=64, filter_size=7, stride=2, act='relu')
M
minqiyang 已提交
164 165 166 167
        self.pool2d_max = Pool2D(
            pool_size=3, pool_stride=2, pool_padding=1, pool_type='max')

        self.bottleneck_block_list = []
M
minqiyang 已提交
168
        num_channels = 64
M
minqiyang 已提交
169
        for block in range(len(depth)):
M
minqiyang 已提交
170
            shortcut = False
M
minqiyang 已提交
171 172
            for i in range(depth[block]):
                bottleneck_block = BottleneckBlock(
M
minqiyang 已提交
173
                    num_channels=num_channels,
M
minqiyang 已提交
174 175 176
                    num_filters=num_filters[block],
                    stride=2 if i == 0 and block != 0 else 1,
                    shortcut=shortcut)
M
minqiyang 已提交
177
                num_channels = bottleneck_block._num_channels_out
M
minqiyang 已提交
178
                self.bottleneck_block_list.append(bottleneck_block)
M
minqiyang 已提交
179
                shortcut = True
M
minqiyang 已提交
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197

        self.pool2d_avg = Pool2D(
            pool_size=7, pool_type='avg', global_pooling=True)

        import math
        stdv = 1.0 / math.sqrt(2048 * 1.0)

        self.out = FC(size=class_dim,
                      act='softmax',
                      param_attr=fluid.param_attr.ParamAttr(
                          initializer=fluid.initializer.Uniform(-stdv, stdv)))

    def forward(self, inputs):
        y = self.conv(inputs)
        y = self.pool2d_max(y)
        for bottleneck_block in self.bottleneck_block_list:
            y = bottleneck_block(y)
        y = self.pool2d_avg(y)
M
minqiyang 已提交
198
        y = self.out(y)
M
minqiyang 已提交
199 200 201 202
        return y


class TestImperativeResnet(unittest.TestCase):
M
minqiyang 已提交
203
    def test_resnet_gpu_float32(self):
M
minqiyang 已提交
204 205
        seed = 90

206
        batch_size = train_parameters["batch_size"]
M
minqiyang 已提交
207 208 209 210 211 212
        with fluid.imperative.guard():
            fluid.default_startup_program().random_seed = seed
            fluid.default_main_program().random_seed = seed

            resnet = ResNet()
            optimizer = optimizer_setting(train_parameters)
M
minqiyang 已提交
213 214 215
            np.random.seed(seed)
            import random
            random.seed = seed
M
minqiyang 已提交
216
            train_reader = paddle.batch(
M
minqiyang 已提交
217 218
                paddle.dataset.flowers.train(use_xmap=False),
                batch_size=batch_size)
M
minqiyang 已提交
219 220

            dy_param_init_value = {}
221 222 223 224
            for param in fluid.default_main_program().global_block(
            ).all_parameters():
                dy_param_init_value[param.name] = param._numpy()

M
minqiyang 已提交
225
            for batch_id, data in enumerate(train_reader()):
226
                if batch_id >= 1:
M
minqiyang 已提交
227 228
                    break

M
minqiyang 已提交
229
                dy_x_data = np.array(
M
minqiyang 已提交
230
                    [x[0].reshape(3, 224, 224) for x in data]).astype('float32')
M
minqiyang 已提交
231
                print('dy input shape', dy_x_data.shape)
M
minqiyang 已提交
232
                y_data = np.array([x[1] for x in data]).astype('int64').reshape(
233
                    batch_size, 1)
M
minqiyang 已提交
234

M
minqiyang 已提交
235
                img = to_variable(dy_x_data)
M
minqiyang 已提交
236 237 238
                label = to_variable(y_data)
                label._stop_gradient = True

M
minqiyang 已提交
239
                out = resnet(img)
M
minqiyang 已提交
240
                loss = fluid.layers.cross_entropy(input=out, label=label)
M
minqiyang 已提交
241
                avg_loss = fluid.layers.mean(x=loss)
M
minqiyang 已提交
242 243 244

                print('shapex ', avg_loss.shape)

M
minqiyang 已提交
245 246 247 248 249
                dy_out = avg_loss._numpy()

                if batch_id == 0:
                    for param in fluid.default_main_program().global_block(
                    ).all_parameters():
250 251
                        if param.name not in dy_param_init_value:
                            dy_param_init_value[param.name] = param._numpy()
M
minqiyang 已提交
252 253

                avg_loss._backward()
M
minqiyang 已提交
254 255 256 257 258 259 260 261 262
                dy_grad_value = {}
                for param in fluid.default_main_program().global_block(
                ).all_parameters():
                    if not param.stop_gradient:
                        np_array = np.array(param._ivar._grad_ivar().value()
                                            .get_tensor())
                        dy_grad_value[param.name + core.grad_var_suffix(
                        )] = np_array

M
minqiyang 已提交
263
                optimizer.minimize(avg_loss)
264

M
minqiyang 已提交
265 266 267 268 269
                dy_param_value = {}
                for param in fluid.default_main_program().global_block(
                ).all_parameters():
                    dy_param_value[param.name] = param._numpy()

270 271 272 273 274 275 276 277
        with new_program_scope():
            fluid.default_startup_program().random_seed = seed
            fluid.default_main_program().random_seed = seed

            exe = fluid.Executor(fluid.CUDAPlace(0))

            resnet = ResNet()
            optimizer = optimizer_setting(train_parameters)
M
minqiyang 已提交
278 279 280 281

            np.random.seed(seed)
            import random
            random.seed = seed
282
            train_reader = paddle.batch(
M
minqiyang 已提交
283 284
                paddle.dataset.flowers.train(use_xmap=False),
                batch_size=batch_size)
285 286 287 288 289 290 291 292 293

            img = fluid.layers.data(
                name='pixel', shape=[3, 224, 224], dtype='float32')
            label = fluid.layers.data(name='label', shape=[1], dtype='int64')
            out = resnet(img)
            loss = fluid.layers.cross_entropy(input=out, label=label)
            avg_loss = fluid.layers.mean(x=loss)
            optimizer.minimize(avg_loss)

M
minqiyang 已提交
294 295 296
            print('avg_loss shape', avg_loss.shape)
            print(fluid.default_main_program())

297 298 299
            # initialize params and fetch them
            static_param_init_value = {}
            static_param_name_list = []
M
minqiyang 已提交
300
            static_grad_name_list = []
301 302 303
            for param in fluid.default_startup_program().global_block(
            ).all_parameters():
                static_param_name_list.append(param.name)
M
minqiyang 已提交
304 305 306 307 308
            for param in fluid.default_main_program().global_block(
            ).all_parameters():
                if not param.stop_gradient:
                    static_grad_name_list.append(param.name +
                                                 core.grad_var_suffix())
309 310 311 312 313 314 315 316 317 318 319

            out = exe.run(fluid.default_startup_program(),
                          fetch_list=static_param_name_list)

            for i in range(len(static_param_name_list)):
                static_param_init_value[static_param_name_list[i]] = out[i]

            for batch_id, data in enumerate(train_reader()):
                if batch_id >= 1:
                    break

M
minqiyang 已提交
320
                static_x_data = np.array(
321 322 323 324
                    [x[0].reshape(3, 224, 224) for x in data]).astype('float32')
                y_data = np.array([x[1] for x in data]).astype('int64').reshape(
                    [batch_size, 1])

M
minqiyang 已提交
325
                fetch_list = [avg_loss.name]
326
                fetch_list.extend(static_param_name_list)
M
minqiyang 已提交
327
                fetch_list.extend(static_grad_name_list)
328
                out = exe.run(fluid.default_main_program(),
M
minqiyang 已提交
329
                              feed={"pixel": static_x_data,
330 331 332 333
                                    "label": y_data},
                              fetch_list=fetch_list)

                static_param_value = {}
M
minqiyang 已提交
334
                static_grad_value = {}
335
                static_out = out[0]
M
minqiyang 已提交
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
                param_start_pos = 1
                grad_start_pos = len(static_param_name_list) + param_start_pos
                for i in range(param_start_pos,
                               len(static_param_name_list) + param_start_pos):
                    static_param_value[static_param_name_list[
                        i - param_start_pos]] = out[i]
                for i in range(grad_start_pos,
                               len(static_grad_name_list) + grad_start_pos):
                    static_grad_value[static_grad_name_list[
                        i - grad_start_pos]] = out[i]

        self.assertTrue(np.allclose(static_out, dy_out))

        self.assertEqual(len(dy_param_init_value), len(static_param_init_value))
        for key, value in six.iteritems(static_param_init_value):
            self.assertTrue(np.allclose(value, dy_param_init_value[key]))
352

M
minqiyang 已提交
353 354 355 356 357
        self.assertEqual(len(dy_grad_value), len(static_grad_value))
        # TODO(minqiyang): find a way to align the gradient
        #  for key, value in six.iteritems(static_grad_value):
        #  self.assertTrue(
        #  np.allclose(value, dy_grad_value[key]))
358

M
minqiyang 已提交
359 360 361 362
        self.assertEqual(len(dy_param_value), len(static_param_value))
        #  for key, value in six.iteritems(static_param_value):

    #  self.assertTrue(np.allclose(value, dy_param_value[key]))
M
minqiyang 已提交
363 364 365 366


if __name__ == '__main__':
    unittest.main()