test_mnist.py 10.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
#   Copyright (c) 2020 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.

15 16
import os
import tempfile
17
import unittest
18
from time import time
19

20
import numpy as np
21
from predictor_utils import PredictorTools
22

23
import paddle
24
from paddle import fluid
25
from paddle.fluid.dygraph import to_variable
26 27
from paddle.fluid.dygraph.base import switch_to_static_graph
from paddle.fluid.optimizer import AdamOptimizer
28
from paddle.jit.translated_layer import INFER_MODEL_SUFFIX, INFER_PARAMS_SUFFIX
29
from paddle.nn import Linear
30

31
SEED = 2020
32

33 34 35
if paddle.fluid.is_compiled_with_cuda():
    paddle.fluid.set_flags({'FLAGS_cudnn_deterministic': True})

36

37
class SimpleImgConvPool(paddle.nn.Layer):
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
    def __init__(
        self,
        num_channels,
        num_filters,
        filter_size,
        pool_size,
        pool_stride,
        pool_padding=0,
        pool_type='max',
        global_pooling=False,
        conv_stride=1,
        conv_padding=0,
        conv_dilation=1,
        conv_groups=1,
        act=None,
        use_cudnn=True,
        param_attr=None,
        bias_attr=None,
    ):
57
        super().__init__()
58

59 60 61 62
        self._conv2d = paddle.nn.Conv2D(
            in_channels=num_channels,
            out_channels=num_filters,
            kernel_size=filter_size,
63 64 65 66
            stride=conv_stride,
            padding=conv_padding,
            dilation=conv_dilation,
            groups=conv_groups,
67
            weight_attr=None,
68 69 70
            bias_attr=None,
        )

W
wangzhen38 已提交
71 72 73 74
        self._pool2d = paddle.nn.MaxPool2D(
            kernel_size=pool_size,
            stride=pool_stride,
            padding=pool_padding,
75
        )
76 77 78 79 80 81 82

    def forward(self, inputs):
        x = self._conv2d(inputs)
        x = self._pool2d(x)
        return x


83
class MNIST(paddle.nn.Layer):
84
    def __init__(self):
85
        super().__init__()
86

87 88 89
        self._simple_img_conv_pool_1 = SimpleImgConvPool(
            1, 20, 5, 2, 2, act="relu"
        )
90

91 92 93
        self._simple_img_conv_pool_2 = SimpleImgConvPool(
            20, 50, 5, 2, 2, act="relu"
        )
94 95 96

        self.pool_2_shape = 50 * 4 * 4
        SIZE = 10
97 98 99 100
        scale = (2.0 / (self.pool_2_shape**2 * SIZE)) ** 0.5
        self._fc = Linear(
            self.pool_2_shape,
            10,
101 102
            weight_attr=paddle.ParamAttr(
                initializer=paddle.nn.initializer.Normal(mean=0.0, std=scale)
103 104
            ),
        )
105 106 107 108

    def forward(self, inputs, label=None):
        x = self.inference(inputs)
        if label is not None:
109
            acc = paddle.static.accuracy(input=x, label=label)
110 111 112
            loss = paddle.nn.functional.cross_entropy(
                x, label, reduction='none', use_softmax=False
            )
113
            avg_loss = paddle.mean(loss)
114

115 116 117
            return x, acc, avg_loss
        else:
            return x
118 119 120 121

    def inference(self, inputs):
        x = self._simple_img_conv_pool_1(inputs)
        x = self._simple_img_conv_pool_2(x)
122
        x = paddle.reshape(x, shape=[-1, self.pool_2_shape])
123
        x = self._fc(x)
124
        x = paddle.nn.functional.softmax(x)
125 126 127 128 129 130 131
        return x


class TestMNIST(unittest.TestCase):
    def setUp(self):
        self.epoch_num = 1
        self.batch_size = 64
132 133 134 135 136 137 138 139 140 141
        self.place = (
            fluid.CUDAPlace(0)
            if fluid.is_compiled_with_cuda()
            else fluid.CPUPlace()
        )
        self.train_reader = paddle.batch(
            paddle.dataset.mnist.train(),
            batch_size=self.batch_size,
            drop_last=True,
        )
142 143 144 145
        self.temp_dir = tempfile.TemporaryDirectory()

    def tearDown(self):
        self.temp_dir.cleanup()
146 147


A
Aurelius84 已提交
148
class TestMNISTWithToStatic(TestMNIST):
149
    """
150 151 152
    Tests model if doesn't change the layers while decorated
    by `dygraph_to_static_output`. In this case, everything should
    still works if model is trained in dygraph mode.
153 154
    """

155 156 157 158 159 160
    def train_static(self):
        return self.train(to_static=True)

    def train_dygraph(self):
        return self.train(to_static=False)

A
Aurelius84 已提交
161
    def test_mnist_to_static(self):
162 163
        dygraph_loss = self.train_dygraph()
        static_loss = self.train_static()
164 165 166 167 168
        np.testing.assert_allclose(
            dygraph_loss,
            static_loss,
            rtol=1e-05,
            err_msg='dygraph is {}\n static_res is \n{}'.format(
169 170 171
                dygraph_loss, static_loss
            ),
        )
172

173 174 175 176 177 178 179
    def test_mnist_declarative_cpu_vs_mkldnn(self):
        dygraph_loss_cpu = self.train_dygraph()
        fluid.set_flags({'FLAGS_use_mkldnn': True})
        try:
            dygraph_loss_mkldnn = self.train_dygraph()
        finally:
            fluid.set_flags({'FLAGS_use_mkldnn': False})
180 181 182 183 184
        np.testing.assert_allclose(
            dygraph_loss_cpu,
            dygraph_loss_mkldnn,
            rtol=1e-05,
            err_msg='cpu dygraph is {}\n mkldnn dygraph is \n{}'.format(
185 186 187
                dygraph_loss_cpu, dygraph_loss_mkldnn
            ),
        )
188

189 190 191 192 193 194
    def train(self, to_static=False):

        loss_data = []
        with fluid.dygraph.guard(self.place):
            fluid.default_main_program().random_seed = SEED
            fluid.default_startup_program().random_seed = SEED
195
            mnist = MNIST()
196 197
            if to_static:
                mnist = paddle.jit.to_static(mnist)
198 199 200
            adam = AdamOptimizer(
                learning_rate=0.001, parameter_list=mnist.parameters()
            )
201

202 203 204
            for epoch in range(self.epoch_num):
                start = time()
                for batch_id, data in enumerate(self.train_reader()):
205 206 207 208 209 210 211 212
                    dy_x_data = np.array(
                        [x[0].reshape(1, 28, 28) for x in data]
                    ).astype('float32')
                    y_data = (
                        np.array([x[1] for x in data])
                        .astype('int64')
                        .reshape(-1, 1)
                    )
213 214 215 216 217 218 219 220 221

                    img = to_variable(dy_x_data)
                    label = to_variable(y_data)

                    label.stop_gradient = True
                    prediction, acc, avg_loss = mnist(img, label=label)
                    avg_loss.backward()

                    adam.minimize(avg_loss)
222
                    loss_data.append(float(avg_loss))
223 224 225 226
                    # save checkpoint
                    mnist.clear_gradients()
                    if batch_id % 10 == 0:
                        print(
227 228 229 230 231 232 233 234
                            "Loss at epoch {} step {}: loss: {:}, acc: {}, cost: {}".format(
                                epoch,
                                batch_id,
                                avg_loss.numpy(),
                                acc.numpy(),
                                time() - start,
                            )
                        )
235 236 237 238
                        start = time()
                    if batch_id == 50:
                        mnist.eval()
                        prediction, acc, avg_loss = mnist(img, label)
239
                        loss_data.append(float(avg_loss))
240
                        # new save load check
241 242 243
                        self.check_jit_save_load(
                            mnist, [dy_x_data], [img], to_static, prediction
                        )
244
                        break
245
        return loss_data
246

247 248
    def check_jit_save_load(self, model, inputs, input_spec, to_static, gt_out):
        if to_static:
249
            infer_model_path = os.path.join(
250 251
                self.temp_dir.name, 'test_mnist_inference_model_by_jit_save'
            )
252 253
            model_save_dir = os.path.join(self.temp_dir.name, 'inference')
            model_save_prefix = os.path.join(model_save_dir, 'mnist')
254 255
            model_filename = "mnist" + INFER_MODEL_SUFFIX
            params_filename = "mnist" + INFER_PARAMS_SUFFIX
256
            paddle.jit.save(
257 258 259 260 261
                layer=model,
                path=model_save_prefix,
                input_spec=input_spec,
                output_spec=[gt_out],
            )
262
            # load in static graph mode
263
            static_infer_out = self.jit_load_and_run_inference_static(
264 265 266 267 268
                model_save_dir, model_filename, params_filename, inputs
            )
            np.testing.assert_allclose(
                gt_out.numpy(), static_infer_out, rtol=1e-05
            )
269 270
            # load in dygraph mode
            dygraph_infer_out = self.jit_load_and_run_inference_dygraph(
271 272 273 274 275
                model_save_prefix, inputs
            )
            np.testing.assert_allclose(
                gt_out.numpy(), dygraph_infer_out, rtol=1e-05
            )
276
            # load in Paddle-Inference
277 278 279 280 281 282 283 284
            predictor_infer_out = (
                self.predictor_load_and_run_inference_analysis(
                    model_save_dir, model_filename, params_filename, inputs
                )
            )
            np.testing.assert_allclose(
                gt_out.numpy(), predictor_infer_out, rtol=1e-05
            )
285 286

    @switch_to_static_graph
287 288 289
    def jit_load_and_run_inference_static(
        self, model_path, model_filename, params_filename, inputs
    ):
290
        paddle.enable_static()
291
        exe = fluid.Executor(self.place)
292 293 294 295 296 297 298 299 300 301
        [
            inference_program,
            feed_target_names,
            fetch_targets,
        ] = fluid.io.load_inference_model(
            dirname=model_path,
            executor=exe,
            model_filename=model_filename,
            params_filename=params_filename,
        )
302
        assert len(inputs) == len(feed_target_names)
303 304 305 306 307
        results = exe.run(
            inference_program,
            feed=dict(zip(feed_target_names, inputs)),
            fetch_list=fetch_targets,
        )
308 309 310 311

        return np.array(results[0])

    def jit_load_and_run_inference_dygraph(self, model_path, inputs):
312
        infer_net = paddle.jit.load(model_path)
313 314 315
        pred = infer_net(inputs[0])
        return pred.numpy()

316 317 318 319 320 321 322
    def predictor_load_and_run_inference_analysis(
        self, model_path, model_filename, params_filename, inputs
    ):
        output = PredictorTools(
            model_path, model_filename, params_filename, inputs
        )
        (out,) = output()
323 324
        return out

325 326 327

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