test_deform_conv2d.py 22.3 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 17 18 19
import unittest
from unittest import TestCase

import numpy as np

20 21 22 23 24 25
import paddle
import paddle.nn.initializer as I


class TestDeformConv2D(TestCase):
    batch_size = 4
26
    spatial_shape = (5, 5)
27 28 29
    dtype = "float32"

    def setUp(self):
30
        self.in_channels = 2
31 32 33 34 35
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [0, 0]
        self.stride = [1, 1]
        self.dilation = [1, 1]
36
        self.deformable_groups = 1
37 38 39 40
        self.groups = 1
        self.no_bias = True

    def prepare(self):
41 42
        np.random.seed(1)
        paddle.seed(1)
43
        if isinstance(self.kernel_size, int):
44
            filter_shape = (self.kernel_size,) * 2
45 46 47 48 49
        else:
            filter_shape = tuple(self.kernel_size)
        self.filter_shape = filter_shape

        self.weight = np.random.uniform(
50 51 52 53
            -1,
            1,
            (self.out_channels, self.in_channels // self.groups) + filter_shape,
        ).astype(self.dtype)
54
        if not self.no_bias:
55 56 57
            self.bias = np.random.uniform(-1, 1, (self.out_channels,)).astype(
                self.dtype
            )
58

59 60 61 62 63 64
        def out_size(
            in_size, pad_size, dilation_size, kernel_size, stride_size
        ):
            return (
                in_size + 2 * pad_size - (dilation_size * (kernel_size - 1) + 1)
            ) / stride_size + 1
65 66

        out_h = int(
67 68 69 70 71 72 73 74
            out_size(
                self.spatial_shape[0],
                self.padding[0],
                self.dilation[0],
                self.kernel_size[0],
                self.stride[0],
            )
        )
75
        out_w = int(
76 77 78 79 80 81 82 83
            out_size(
                self.spatial_shape[1],
                self.padding[1],
                self.dilation[1],
                self.kernel_size[1],
                self.stride[1],
            )
        )
84 85
        out_shape = (out_h, out_w)

86 87 88 89
        self.input_shape = (
            self.batch_size,
            self.in_channels,
        ) + self.spatial_shape
90

91 92 93 94
        self.offset_shape = (
            self.batch_size,
            self.deformable_groups * 2 * filter_shape[0] * filter_shape[1],
        ) + out_shape
95

96 97 98 99
        self.mask_shape = (
            self.batch_size,
            self.deformable_groups * filter_shape[0] * filter_shape[1],
        ) + out_shape
100

101 102 103
        self.input = np.random.uniform(-1, 1, self.input_shape).astype(
            self.dtype
        )
104

105 106 107
        self.offset = np.random.uniform(-1, 1, self.offset_shape).astype(
            self.dtype
        )
108 109 110 111 112 113 114 115

        self.mask = np.random.uniform(-1, 1, self.mask_shape).astype(self.dtype)

    def static_graph_case_dcn(self):
        main = paddle.static.Program()
        start = paddle.static.Program()
        paddle.enable_static()
        with paddle.static.program_guard(main, start):
116 117 118
            x = paddle.static.data(
                "input", (-1, self.in_channels, -1, -1), dtype=self.dtype
            )
119
            offset = paddle.static.data(
120 121 122 123 124 125 126 127 128 129 130 131
                "offset",
                (
                    -1,
                    self.deformable_groups
                    * 2
                    * self.filter_shape[0]
                    * self.filter_shape[1],
                    -1,
                    -1,
                ),
                dtype=self.dtype,
            )
132
            mask = paddle.static.data(
133 134 135 136 137 138 139 140 141 142 143
                "mask",
                (
                    -1,
                    self.deformable_groups
                    * self.filter_shape[0]
                    * self.filter_shape[1],
                    -1,
                    -1,
                ),
                dtype=self.dtype,
            )
144

145
            y_v1 = paddle.static.nn.common.deformable_conv(
146 147 148 149 150 151 152 153 154
                input=x,
                offset=offset,
                mask=None,
                num_filters=self.out_channels,
                filter_size=self.filter_shape,
                stride=self.stride,
                padding=self.padding,
                dilation=self.dilation,
                groups=self.groups,
155
                deformable_groups=self.deformable_groups,
156 157 158
                im2col_step=1,
                param_attr=I.Assign(self.weight),
                bias_attr=False if self.no_bias else I.Assign(self.bias),
159 160
                modulated=False,
            )
161

162
            y_v2 = paddle.static.nn.common.deformable_conv(
163 164 165 166 167 168 169 170 171
                input=x,
                offset=offset,
                mask=mask,
                num_filters=self.out_channels,
                filter_size=self.filter_shape,
                stride=self.stride,
                padding=self.padding,
                dilation=self.dilation,
                groups=self.groups,
172
                deformable_groups=self.deformable_groups,
173 174
                im2col_step=1,
                param_attr=I.Assign(self.weight),
175 176
                bias_attr=False if self.no_bias else I.Assign(self.bias),
            )
177 178 179

        exe = paddle.static.Executor(self.place)
        exe.run(start)
180 181 182 183 184 185 186 187 188
        out_v1, out_v2 = exe.run(
            main,
            feed={
                "input": self.input,
                "offset": self.offset,
                "mask": self.mask,
            },
            fetch_list=[y_v1, y_v2],
        )
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
        return out_v1, out_v2

    def dygraph_case_dcn(self):
        paddle.disable_static()
        x = paddle.to_tensor(self.input)
        offset = paddle.to_tensor(self.offset)
        mask = paddle.to_tensor(self.mask)

        bias = None if self.no_bias else paddle.to_tensor(self.bias)

        deform_conv2d = paddle.vision.ops.DeformConv2D(
            in_channels=self.in_channels,
            out_channels=self.out_channels,
            kernel_size=self.kernel_size,
            stride=self.stride,
            padding=self.padding,
            dilation=self.dilation,
206
            deformable_groups=self.deformable_groups,
207 208
            groups=self.groups,
            weight_attr=I.Assign(self.weight),
209 210
            bias_attr=False if self.no_bias else I.Assign(self.bias),
        )
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237

        y_v1 = deform_conv2d(x, offset)
        y_v2 = deform_conv2d(x, offset, mask)

        out_v1 = y_v1.numpy()
        out_v2 = y_v2.numpy()

        return out_v1, out_v2

    def _test_identity(self):
        self.prepare()
        static_dcn_v1, static_dcn_v2 = self.static_graph_case_dcn()
        dy_dcn_v1, dy_dcn_v2 = self.dygraph_case_dcn()
        np.testing.assert_array_almost_equal(static_dcn_v1, dy_dcn_v1)
        np.testing.assert_array_almost_equal(static_dcn_v2, dy_dcn_v2)

    def test_identity(self):
        self.place = paddle.CPUPlace()
        self._test_identity()

        if paddle.is_compiled_with_cuda():
            self.place = paddle.CUDAPlace(0)
            self._test_identity()


class TestDeformConv2DFunctional(TestCase):
    batch_size = 4
238
    spatial_shape = (5, 5)
239 240 241
    dtype = "float32"

    def setUp(self):
242
        self.in_channels = 2
243 244 245 246 247
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [0, 0]
        self.stride = [1, 1]
        self.dilation = [1, 1]
248
        self.deformable_groups = 1
249 250 251 252
        self.groups = 1
        self.no_bias = True

    def prepare(self):
253 254
        np.random.seed(1)
        paddle.seed(1)
255
        if isinstance(self.kernel_size, int):
256
            filter_shape = (self.kernel_size,) * 2
257 258 259 260 261
        else:
            filter_shape = tuple(self.kernel_size)
        self.filter_shape = filter_shape

        self.weight = np.random.uniform(
262 263 264 265
            -1,
            1,
            (self.out_channels, self.in_channels // self.groups) + filter_shape,
        ).astype(self.dtype)
266
        if not self.no_bias:
267 268 269
            self.bias = np.random.uniform(-1, 1, (self.out_channels,)).astype(
                self.dtype
            )
270

271 272 273 274 275 276
        def out_size(
            in_size, pad_size, dilation_size, kernel_size, stride_size
        ):
            return (
                in_size + 2 * pad_size - (dilation_size * (kernel_size - 1) + 1)
            ) / stride_size + 1
277 278

        out_h = int(
279 280 281 282 283 284 285 286
            out_size(
                self.spatial_shape[0],
                self.padding[0],
                self.dilation[0],
                self.kernel_size[0],
                self.stride[0],
            )
        )
287
        out_w = int(
288 289 290 291 292 293 294 295
            out_size(
                self.spatial_shape[1],
                self.padding[1],
                self.dilation[1],
                self.kernel_size[1],
                self.stride[1],
            )
        )
296 297
        out_shape = (out_h, out_w)

298 299 300 301
        self.input_shape = (
            self.batch_size,
            self.in_channels,
        ) + self.spatial_shape
302

303 304 305 306
        self.offset_shape = (
            self.batch_size,
            self.deformable_groups * 2 * filter_shape[0] * filter_shape[1],
        ) + out_shape
307

308 309 310 311
        self.mask_shape = (
            self.batch_size,
            self.deformable_groups * filter_shape[0] * filter_shape[1],
        ) + out_shape
312

313 314 315
        self.input = np.random.uniform(-1, 1, self.input_shape).astype(
            self.dtype
        )
316

317 318 319
        self.offset = np.random.uniform(-1, 1, self.offset_shape).astype(
            self.dtype
        )
320 321 322 323 324 325 326 327

        self.mask = np.random.uniform(-1, 1, self.mask_shape).astype(self.dtype)

    def static_graph_case_dcn(self):
        main = paddle.static.Program()
        start = paddle.static.Program()
        paddle.enable_static()
        with paddle.static.program_guard(main, start):
328 329 330
            x = paddle.static.data(
                "input", (-1, self.in_channels, -1, -1), dtype=self.dtype
            )
331
            offset = paddle.static.data(
332 333 334 335 336 337 338 339 340 341 342 343
                "offset",
                (
                    -1,
                    self.deformable_groups
                    * 2
                    * self.filter_shape[0]
                    * self.filter_shape[1],
                    -1,
                    -1,
                ),
                dtype=self.dtype,
            )
344
            mask = paddle.static.data(
345 346 347 348 349 350 351 352 353 354 355
                "mask",
                (
                    -1,
                    self.deformable_groups
                    * self.filter_shape[0]
                    * self.filter_shape[1],
                    -1,
                    -1,
                ),
                dtype=self.dtype,
            )
356

357
            y_v1 = paddle.static.nn.common.deformable_conv(
358 359 360 361 362 363 364 365 366
                input=x,
                offset=offset,
                mask=None,
                num_filters=self.out_channels,
                filter_size=self.filter_shape,
                stride=self.stride,
                padding=self.padding,
                dilation=self.dilation,
                groups=self.groups,
367
                deformable_groups=self.deformable_groups,
368 369 370
                im2col_step=1,
                param_attr=I.Assign(self.weight),
                bias_attr=False if self.no_bias else I.Assign(self.bias),
371 372
                modulated=False,
            )
373

374
            y_v2 = paddle.static.nn.common.deformable_conv(
375 376 377 378 379 380 381 382 383
                input=x,
                offset=offset,
                mask=mask,
                num_filters=self.out_channels,
                filter_size=self.filter_shape,
                stride=self.stride,
                padding=self.padding,
                dilation=self.dilation,
                groups=self.groups,
384
                deformable_groups=self.deformable_groups,
385 386
                im2col_step=1,
                param_attr=I.Assign(self.weight),
387 388
                bias_attr=False if self.no_bias else I.Assign(self.bias),
            )
389 390 391

        exe = paddle.static.Executor(self.place)
        exe.run(start)
392 393 394 395 396 397 398 399 400
        out_v1, out_v2 = exe.run(
            main,
            feed={
                "input": self.input,
                "offset": self.offset,
                "mask": self.mask,
            },
            fetch_list=[y_v1, y_v2],
        )
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
        return out_v1, out_v2

    def dygraph_case_dcn(self):
        paddle.disable_static()
        x = paddle.to_tensor(self.input)
        offset = paddle.to_tensor(self.offset)
        mask = paddle.to_tensor(self.mask)
        weight = paddle.to_tensor(self.weight)
        bias = None if self.no_bias else paddle.to_tensor(self.bias)

        y_v1 = paddle.vision.ops.deform_conv2d(
            x=x,
            offset=offset,
            weight=weight,
            bias=bias,
            stride=self.stride,
            padding=self.padding,
            dilation=self.dilation,
419
            deformable_groups=self.deformable_groups,
420 421
            groups=self.groups,
        )
422 423 424 425 426 427 428 429 430 431

        y_v2 = paddle.vision.ops.deform_conv2d(
            x=x,
            offset=offset,
            mask=mask,
            weight=weight,
            bias=bias,
            stride=self.stride,
            padding=self.padding,
            dilation=self.dilation,
432
            deformable_groups=self.deformable_groups,
433 434
            groups=self.groups,
        )
435 436 437 438 439 440 441 442 443 444 445

        out_v1 = y_v1.numpy()
        out_v2 = y_v2.numpy()

        return out_v1, out_v2

    def new_api_static_graph_case_dcn(self):
        main = paddle.static.Program()
        start = paddle.static.Program()
        paddle.enable_static()
        with paddle.static.program_guard(main, start):
446 447 448
            x = paddle.static.data(
                "input", (-1, self.in_channels, -1, -1), dtype=self.dtype
            )
449
            offset = paddle.static.data(
450 451 452 453 454 455 456 457 458 459 460 461
                "offset",
                (
                    -1,
                    self.deformable_groups
                    * 2
                    * self.filter_shape[0]
                    * self.filter_shape[1],
                    -1,
                    -1,
                ),
                dtype=self.dtype,
            )
462
            mask = paddle.static.data(
463 464 465 466 467 468 469 470 471 472 473
                "mask",
                (
                    -1,
                    self.deformable_groups
                    * self.filter_shape[0]
                    * self.filter_shape[1],
                    -1,
                    -1,
                ),
                dtype=self.dtype,
            )
474

475 476 477
            weight = paddle.static.data(
                "weight", list(self.weight.shape), dtype=self.dtype
            )
478 479 480 481 482 483 484 485 486 487 488 489

            if not self.no_bias:
                bias = paddle.static.data("bias", [-1], dtype=self.dtype)

            y_v1 = paddle.vision.ops.deform_conv2d(
                x=x,
                offset=offset,
                weight=weight,
                bias=None if self.no_bias else bias,
                stride=self.stride,
                padding=self.padding,
                dilation=self.dilation,
490
                deformable_groups=self.deformable_groups,
491 492
                groups=self.groups,
            )
493 494 495 496 497 498 499 500 501 502

            y_v2 = paddle.vision.ops.deform_conv2d(
                x=x,
                offset=offset,
                mask=mask,
                weight=weight,
                bias=None if self.no_bias else bias,
                stride=self.stride,
                padding=self.padding,
                dilation=self.dilation,
503
                deformable_groups=self.deformable_groups,
504 505
                groups=self.groups,
            )
506 507 508 509 510 511 512

        exe = paddle.static.Executor(self.place)
        exe.run(start)
        feed_dict = {
            "input": self.input,
            "offset": self.offset,
            "mask": self.mask,
513
            "weight": self.weight,
514 515 516 517 518 519 520 521 522 523 524
        }
        if not self.no_bias:
            feed_dict["bias"] = self.bias

        out_v1, out_v2 = exe.run(main, feed=feed_dict, fetch_list=[y_v1, y_v2])
        return out_v1, out_v2

    def _test_identity(self):
        self.prepare()
        static_dcn_v1, static_dcn_v2 = self.static_graph_case_dcn()
        dy_dcn_v1, dy_dcn_v2 = self.dygraph_case_dcn()
525 526 527 528
        (
            new_static_dcn_v1,
            new_static_dcn_v2,
        ) = self.new_api_static_graph_case_dcn()
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
        np.testing.assert_array_almost_equal(static_dcn_v1, dy_dcn_v1)
        np.testing.assert_array_almost_equal(static_dcn_v2, dy_dcn_v2)
        np.testing.assert_array_almost_equal(static_dcn_v1, new_static_dcn_v1)
        np.testing.assert_array_almost_equal(static_dcn_v2, new_static_dcn_v2)

    def test_identity(self):
        self.place = paddle.CPUPlace()
        self._test_identity()

        if paddle.is_compiled_with_cuda():
            self.place = paddle.CUDAPlace(0)
            self._test_identity()


# testcases for DeformConv2D
class TestDeformConv2DWithPadding(TestDeformConv2D):
    def setUp(self):
        self.in_channels = 3
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [2, 2]
        self.stride = [1, 1]
        self.dilation = [1, 1]
552
        self.deformable_groups = 1
553 554 555 556 557 558 559 560 561 562 563 564
        self.groups = 1
        self.no_bias = True


class TestDeformConv2DWithBias(TestDeformConv2D):
    def setUp(self):
        self.in_channels = 3
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [2, 2]
        self.stride = [1, 1]
        self.dilation = [1, 1]
565
        self.deformable_groups = 1
566 567 568 569 570 571 572 573 574 575 576 577
        self.groups = 1
        self.no_bias = False


class TestDeformConv2DWithAsynPadding(TestDeformConv2D):
    def setUp(self):
        self.in_channels = 3
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [1, 2]
        self.stride = [1, 1]
        self.dilation = [1, 1]
578
        self.deformable_groups = 1
579 580 581 582 583 584 585 586 587 588 589 590
        self.groups = 1
        self.no_bias = False


class TestDeformConv2DWithDilation(TestDeformConv2D):
    def setUp(self):
        self.in_channels = 3
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [1, 1]
        self.stride = [1, 1]
        self.dilation = [3, 3]
591
        self.deformable_groups = 1
592 593 594 595 596 597 598 599 600 601 602 603
        self.groups = 1
        self.no_bias = False


class TestDeformConv2DWithStride(TestDeformConv2D):
    def setUp(self):
        self.in_channels = 3
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [1, 1]
        self.stride = [2, 2]
        self.dilation = [1, 1]
604 605 606 607 608 609 610 611 612 613 614 615 616 617
        self.deformable_groups = 1
        self.groups = 1
        self.no_bias = False


class TestDeformConv2DWithDeformable_Groups(TestDeformConv2D):
    def setUp(self):
        self.in_channels = 5
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [1, 1]
        self.stride = [1, 1]
        self.dilation = [1, 1]
        self.deformable_groups = 5
618 619 620 621 622 623 624 625 626 627 628 629
        self.groups = 1
        self.no_bias = False


class TestDeformConv2DWithGroups(TestDeformConv2D):
    def setUp(self):
        self.in_channels = 5
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [1, 1]
        self.stride = [1, 1]
        self.dilation = [1, 1]
630
        self.deformable_groups = 1
631 632 633 634 635 636 637 638 639 640 641 642 643
        self.groups = 5
        self.no_bias = False


# testcases for deform_conv2d
class TestDeformConv2DFunctionalWithPadding(TestDeformConv2DFunctional):
    def setUp(self):
        self.in_channels = 3
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [2, 2]
        self.stride = [1, 1]
        self.dilation = [1, 1]
644
        self.deformable_groups = 1
645 646 647 648 649 650 651 652 653 654 655 656
        self.groups = 1
        self.no_bias = True


class TestDeformConv2DFunctionalWithBias(TestDeformConv2DFunctional):
    def setUp(self):
        self.in_channels = 3
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [2, 2]
        self.stride = [1, 1]
        self.dilation = [1, 1]
657
        self.deformable_groups = 1
658 659 660 661 662 663 664 665 666 667 668 669
        self.groups = 1
        self.no_bias = False


class TestDeformConv2DFunctionalWithAsynPadding(TestDeformConv2DFunctional):
    def setUp(self):
        self.in_channels = 3
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [1, 2]
        self.stride = [1, 1]
        self.dilation = [1, 1]
670
        self.deformable_groups = 1
671 672 673 674 675 676 677 678 679 680 681 682
        self.groups = 1
        self.no_bias = False


class TestDeformConv2DFunctionalWithDilation(TestDeformConv2DFunctional):
    def setUp(self):
        self.in_channels = 3
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [1, 1]
        self.stride = [1, 1]
        self.dilation = [3, 3]
683
        self.deformable_groups = 1
684 685 686 687 688 689 690 691 692 693 694 695
        self.groups = 1
        self.no_bias = False


class TestDeformConv2DFunctionalWithStride(TestDeformConv2DFunctional):
    def setUp(self):
        self.in_channels = 3
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [1, 1]
        self.stride = [2, 2]
        self.dilation = [1, 1]
696 697 698 699 700
        self.deformable_groups = 1
        self.groups = 1
        self.no_bias = False


701 702 703
class TestDeformConv2DFunctionalWithDeformable_Groups(
    TestDeformConv2DFunctional
):
704 705 706 707 708 709 710 711
    def setUp(self):
        self.in_channels = 5
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [1, 1]
        self.stride = [1, 1]
        self.dilation = [1, 1]
        self.deformable_groups = 5
712 713 714 715 716 717 718 719 720 721 722 723
        self.groups = 1
        self.no_bias = False


class TestDeformConv2DFunctionalWithGroups(TestDeformConv2DFunctional):
    def setUp(self):
        self.in_channels = 5
        self.out_channels = 5
        self.kernel_size = [3, 3]
        self.padding = [1, 1]
        self.stride = [1, 1]
        self.dilation = [1, 1]
724
        self.deformable_groups = 1
725 726 727 728
        self.groups = 5
        self.no_bias = False


729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
class TestDeformConv2DError(unittest.TestCase):
    def test_input_error(self):
        def test_input_rank_error():
            paddle.enable_static()
            x = paddle.static.data(name='error_x_1', shape=[0], dtype='float32')
            offset = paddle.static.data(
                name='error_offset_1', shape=[0], dtype='float32'
            )
            mask = paddle.static.data(
                name='error_mask_1', shape=[0, 0, 0], dtype='float32'
            )
            out = paddle.static.nn.deform_conv2d(
                x, offset, mask, 0, 0, deformable_groups=0
            )

        self.assertRaises(ValueError, test_input_rank_error)


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