test_custom_relu_op_setup.py 13.1 KB
Newer Older
1
# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
2
#
3 4 5
# 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
6
#
7
#     http://www.apache.org/licenses/LICENSE-2.0
8
#
9 10 11 12 13 14 15 16
# 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 site
17
import sys
18
import unittest
19 20 21

import numpy as np

22
import paddle
23
from paddle import static
24 25
from paddle.utils.cpp_extension.extension_utils import run_cmd
from paddle.vision.transforms import Compose, Normalize
26 27


28
def custom_relu_dynamic(func, device, dtype, np_x, use_func=True):
29 30
    paddle.set_device(device)

31
    t = paddle.to_tensor(np_x, dtype=dtype)
32 33
    t.stop_gradient = False

34
    out = func(t) if use_func else paddle.nn.functional.relu(t)
35 36 37 38
    out.stop_gradient = False

    out.backward()

39 40 41 42
    if t.grad is None:
        return out.numpy(), t.grad
    else:
        return out.numpy(), t.grad.numpy()
43 44


45 46 47
def custom_relu_static(
    func, device, dtype, np_x, use_func=True, test_infer=False
):
48 49 50 51 52 53 54
    paddle.enable_static()
    paddle.set_device(device)

    with static.scope_guard(static.Scope()):
        with static.program_guard(static.Program()):
            x = static.data(name='X', shape=[None, 8], dtype=dtype)
            x.stop_gradient = False
55
            out = func(x) if use_func else paddle.nn.functional.relu(x)
56 57 58 59
            static.append_backward(out)

            exe = static.Executor()
            exe.run(static.default_startup_program())
60
            # in static graph mode, x data has been covered by out
61 62 63 64 65
            out_v = exe.run(
                static.default_main_program(),
                feed={'X': np_x},
                fetch_list=[out.name],
            )
66

67
    paddle.disable_static()
68 69 70
    return out_v


71 72 73 74 75 76
def custom_relu_static_inference(func, device, np_data, np_label, path_prefix):
    paddle.set_device(device)

    with static.scope_guard(static.Scope()):
        with static.program_guard(static.Program()):
            # simple module
77 78 79
            data = static.data(
                name='data', shape=[None, 1, 28, 28], dtype='float32'
            )
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
            label = static.data(name='label', shape=[None, 1], dtype='int64')

            hidden = static.nn.fc(data, size=128)
            hidden = func(hidden)
            hidden = static.nn.fc(hidden, size=128)
            predict = static.nn.fc(hidden, size=10, activation='softmax')
            loss = paddle.nn.functional.cross_entropy(input=hidden, label=label)
            avg_loss = paddle.mean(loss)

            opt = paddle.optimizer.SGD(learning_rate=0.1)
            opt.minimize(avg_loss)

            # run start up model
            exe = static.Executor()
            exe.run(static.default_startup_program())

            # train
            for i in range(4):
98 99 100 101 102
                avg_loss_v = exe.run(
                    static.default_main_program(),
                    feed={'data': np_data, 'label': np_label},
                    fetch_list=[avg_loss],
                )
103 104 105 106 107

            # save inference model
            static.save_inference_model(path_prefix, [data], [predict], exe)

            # get train predict value
108 109 110 111 112
            predict_v = exe.run(
                static.default_main_program(),
                feed={'data': np_data, 'label': np_label},
                fetch_list=[predict],
            )
113 114 115 116

    return predict_v


117 118 119 120
def custom_relu_double_grad_dynamic(func, device, dtype, np_x, use_func=True):
    paddle.set_device(device)

    t = paddle.to_tensor(np_x, dtype=dtype, stop_gradient=False)
姜永久 已提交
121
    t.retain_grads()
122 123

    out = func(t) if use_func else paddle.nn.functional.relu(t)
姜永久 已提交
124
    out.retain_grads()
125
    dx = paddle.grad(
126 127 128 129 130
        outputs=out,
        inputs=t,
        grad_outputs=paddle.ones_like(t),
        create_graph=True,
        retain_graph=True,
131
    )
132

133 134 135 136 137 138
    ddout = paddle.grad(
        outputs=dx[0],
        inputs=out.grad,
        grad_outputs=paddle.ones_like(t),
        create_graph=False,
    )
139

140 141
    assert ddout[0].numpy() is not None
    return dx[0].numpy(), ddout[0].numpy()
142 143


144 145 146 147
class TestNewCustomOpSetUpInstall(unittest.TestCase):
    def setUp(self):
        cur_dir = os.path.dirname(os.path.abspath(__file__))
        # compile, install the custom op egg into site-packages under background
148
        if os.name == 'nt':
149
            cmd = 'cd /d {} && python custom_relu_setup.py install'.format(
150 151
                cur_dir
            )
152
        else:
153
            cmd = 'cd {} && {} custom_relu_setup.py install'.format(
154 155
                cur_dir, sys.executable
            )
156 157 158 159 160 161 162
        run_cmd(cmd)

        # NOTE(Aurelius84): Normally, it's no need to add following codes for users.
        # But we simulate to pip install in current process, so interpreter don't snap
        # sys.path has been updated. So we update it manually.

        # See: https://stackoverflow.com/questions/56974185/import-runtime-installed-module-using-pip-in-python-3
163 164 165 166 167
        if os.name == 'nt':
            # NOTE(zhouwei25): getsitepackages on windows will return a list: [python install dir, site packages dir]
            site_dir = site.getsitepackages()[1]
        else:
            site_dir = site.getsitepackages()[0]
168
        custom_egg_path = [
169
            x for x in os.listdir(site_dir) if 'custom_relu_module_setup' in x
170
        ]
171 172 173
        assert len(custom_egg_path) == 1, "Matched egg number is %d." % len(
            custom_egg_path
        )
174 175 176
        sys.path.append(os.path.join(site_dir, custom_egg_path[0]))

        # usage: import the package directly
177
        import custom_relu_module_setup
178

179 180 181
        # `custom_relu_dup` is same as `custom_relu_dup`
        self.custom_ops = [
            custom_relu_module_setup.custom_relu,
182
            custom_relu_module_setup.custom_relu_dup,
183
        ]
184 185

        self.dtypes = ['float32', 'float64']
186 187 188 189 190
        if paddle.is_compiled_with_cuda():
            self.dtypes.append('float16')
        self.devices = ['cpu']
        if paddle.is_compiled_with_cuda():
            self.devices.append('gpu')
191

192 193 194 195 196
        # config seed
        SEED = 2021
        paddle.seed(SEED)
        paddle.framework.random._manual_program_seed(SEED)

197 198 199
    def test_static(self):
        for device in self.devices:
            for dtype in self.dtypes:
200 201
                if device == 'cpu' and dtype == 'float16':
                    continue
202
                x = np.random.uniform(-1, 1, [4, 8]).astype(dtype)
203
                for custom_op in self.custom_ops:
204
                    out = custom_relu_static(custom_op, device, dtype, x)
205 206 207
                    pd_out = custom_relu_static(
                        custom_op, device, dtype, x, False
                    )
208 209 210
                    np.testing.assert_array_equal(
                        out,
                        pd_out,
211 212 213 214
                        err_msg='custom op out: {},\n paddle api out: {}'.format(
                            out, pd_out
                        ),
                    )
215

216
    def test_dynamic(self):
217 218
        for device in self.devices:
            for dtype in self.dtypes:
219 220
                if device == 'cpu' and dtype == 'float16':
                    continue
221
                x = np.random.uniform(-1, 1, [4, 8]).astype(dtype)
222
                for custom_op in self.custom_ops:
223 224 225
                    out, x_grad = custom_relu_dynamic(
                        custom_op, device, dtype, x
                    )
226
                    pd_out, pd_x_grad = custom_relu_dynamic(
227 228
                        custom_op, device, dtype, x, False
                    )
229 230 231
                    np.testing.assert_array_equal(
                        out,
                        pd_out,
232 233 234 235
                        err_msg='custom op out: {},\n paddle api out: {}'.format(
                            out, pd_out
                        ),
                    )
236 237 238
                    np.testing.assert_array_equal(
                        x_grad,
                        pd_x_grad,
239 240 241 242
                        err_msg='custom op x grad: {},\n paddle api x grad: {}'.format(
                            x_grad, pd_x_grad
                        ),
                    )
243

244 245 246 247 248 249
    def test_static_save_and_load_inference_model(self):
        paddle.enable_static()
        np_data = np.random.random((1, 1, 28, 28)).astype("float32")
        np_label = np.random.random((1, 1)).astype("int64")
        path_prefix = "custom_op_inference/custom_relu"
        for device in self.devices:
250 251 252
            predict = custom_relu_static_inference(
                self.custom_ops[0], device, np_data, np_label, path_prefix
            )
253 254 255
            # load inference model
            with static.scope_guard(static.Scope()):
                exe = static.Executor()
256 257 258 259 260 261 262 263 264 265
                [
                    inference_program,
                    feed_target_names,
                    fetch_targets,
                ] = static.load_inference_model(path_prefix, exe)
                predict_infer = exe.run(
                    inference_program,
                    feed={feed_target_names[0]: np_data},
                    fetch_list=fetch_targets,
                )
266 267 268
                np.testing.assert_array_equal(
                    predict,
                    predict_infer,
269 270 271 272
                    err_msg='custom op predict: {},\n custom op infer predict: {}'.format(
                        predict, predict_infer
                    ),
                )
273 274
        paddle.disable_static()

275 276 277 278 279
    def test_static_save_and_run_inference_predictor(self):
        paddle.enable_static()
        np_data = np.random.random((1, 1, 28, 28)).astype("float32")
        np_label = np.random.random((1, 1)).astype("int64")
        path_prefix = "custom_op_inference/custom_relu"
280
        from paddle.inference import Config, create_predictor
281

282
        for device in self.devices:
283 284 285
            predict = custom_relu_static_inference(
                self.custom_ops[0], device, np_data, np_label, path_prefix
            )
286
            # load inference model
287 288 289
            config = Config(
                path_prefix + ".pdmodel", path_prefix + ".pdiparams"
            )
290
            predictor = create_predictor(config)
291
            input_tensor = predictor.get_input_handle(
292 293
                predictor.get_input_names()[0]
            )
294 295 296 297
            input_tensor.reshape(np_data.shape)
            input_tensor.copy_from_cpu(np_data.copy())
            predictor.run()
            output_tensor = predictor.get_output_handle(
298 299
                predictor.get_output_names()[0]
            )
300 301
            predict_infer = output_tensor.copy_to_cpu()
            self.assertTrue(
302
                np.isclose(predict, predict_infer, rtol=5e-5).any(),
303
                "custom op predict: {},\n custom op infer predict: {}".format(
304 305 306
                    predict, predict_infer
                ),
            )
307 308
        paddle.disable_static()

309
    def test_double_grad_dynamic(self):
310 311 312 313 314 315
        for device in self.devices:
            for dtype in self.dtypes:
                if device == 'cpu' and dtype == 'float16':
                    continue
                x = np.random.uniform(-1, 1, [4, 8]).astype(dtype)
                out, dx_grad = custom_relu_double_grad_dynamic(
316 317
                    self.custom_ops[0], device, dtype, x
                )
318
                pd_out, pd_dx_grad = custom_relu_double_grad_dynamic(
319 320
                    self.custom_ops[0], device, dtype, x, False
                )
321 322 323 324
                np.testing.assert_array_equal(
                    out,
                    pd_out,
                    err_msg='custom op out: {},\n paddle api out: {}'.format(
325 326 327
                        out, pd_out
                    ),
                )
328 329 330
                np.testing.assert_array_equal(
                    dx_grad,
                    pd_dx_grad,
331 332 333 334
                    err_msg='custom op dx grad: {},\n paddle api dx grad: {}'.format(
                        dx_grad, pd_dx_grad
                    ),
                )
335

336 337 338 339 340
    def test_with_dataloader(self):
        for device in self.devices:
            paddle.set_device(device)
            # data loader
            transform = Compose(
341 342 343 344 345 346 347 348 349 350 351 352
                [Normalize(mean=[127.5], std=[127.5], data_format='CHW')]
            )
            train_dataset = paddle.vision.datasets.MNIST(
                mode='train', transform=transform
            )
            train_loader = paddle.io.DataLoader(
                train_dataset,
                batch_size=64,
                shuffle=True,
                drop_last=True,
                num_workers=0,
            )
353 354

            for batch_id, (image, _) in enumerate(train_loader()):
355
                image = paddle.to_tensor(image)
356 357
                out = self.custom_ops[0](image)
                pd_out = paddle.nn.functional.relu(image)
358 359 360 361
                np.testing.assert_array_equal(
                    out,
                    pd_out,
                    err_msg='custom op out: {},\n paddle api out: {}'.format(
362 363 364
                        out, pd_out
                    ),
                )
365 366 367 368

                if batch_id == 5:
                    break

369 370 371

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