test_imperative_triple_grad.py 10.9 KB
Newer Older
W
Weilong Wu 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# Copyright (c) 2021 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 unittest
from unittest import TestCase
17

W
Weilong Wu 已提交
18
import numpy as np
19 20 21 22

import paddle
import paddle.fluid as fluid
from paddle.fluid.wrapped_decorator import wrap_decorator
W
Weilong Wu 已提交
23 24 25 26


def _dygraph_guard_(func):
    def __impl__(*args, **kwargs):
J
Jiabin Yang 已提交
27
        if fluid._non_static_mode():
W
Weilong Wu 已提交
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
            return func(*args, **kwargs)
        else:
            with fluid.dygraph.guard():
                return func(*args, **kwargs)

    return __impl__


dygraph_guard = wrap_decorator(_dygraph_guard_)


def random_var(size, low=-1, high=1, dtype='float32'):
    np.random.seed(2021)
    x_np = np.random.uniform(low=low, high=high, size=size).astype(dtype)
    return fluid.dygraph.to_variable(x_np)


45 46 47
class TestDygraphTripleGradMatmul(TestCase):
    def test_matmul_triple_grad(self):
        input_numpy = np.ones([3, 3]) * 2
48 49 50
        x = paddle.to_tensor(input_numpy, stop_gradient=False, dtype='float32')
        y = paddle.to_tensor(input_numpy, stop_gradient=False, dtype='float32')
        out = paddle.matmul(x, y, False, False)
51

52 53 54 55 56 57
        new_out_g = paddle.to_tensor(
            np.ones([3, 3]), stop_gradient=False, dtype='float32'
        )
        new_x_g, new_y_g = paddle.grad(
            [out], [x, y], [new_out_g], retain_graph=True, create_graph=True
        )
58

59 60 61 62 63 64 65 66 67 68 69 70 71
        new_x_g_g = paddle.to_tensor(
            np.ones([3, 3]), stop_gradient=False, dtype='float32'
        )
        new_y_g_g = paddle.to_tensor(
            np.ones([3, 3]), stop_gradient=False, dtype='float32'
        )
        new_a, new_b, new_c = paddle.grad(
            [new_x_g, new_y_g],
            [x, y, new_out_g],
            [new_x_g_g, new_y_g_g],
            retain_graph=True,
            create_graph=True,
        )
72

73
        new_a.backward()
74

75 76
        out_ref = np.ones([3, 3]) * 12.0
        np.testing.assert_array_equal(out.numpy(), out_ref)
77

78 79 80 81
        new_x_g_ref = np.ones([3, 3]) * 6.0
        new_y_g_ref = np.ones([3, 3]) * 6.0
        np.testing.assert_array_equal(new_x_g.numpy(), new_x_g_ref)
        np.testing.assert_array_equal(new_y_g.numpy(), new_y_g_ref)
82

83 84 85
        new_a_ref = np.ones([3, 3]) * 3.0
        new_b_ref = np.ones([3, 3]) * 3.0
        new_c_ref = np.ones([3, 3]) * 12.0
86

87 88 89
        np.testing.assert_array_equal(new_a.numpy(), new_a_ref)
        np.testing.assert_array_equal(new_b.numpy(), new_b_ref)
        np.testing.assert_array_equal(new_c.numpy(), new_c_ref)
90

91 92
        x_grad_ref = np.ones([3, 3]) * 0.0
        np.testing.assert_array_equal(x.grad.numpy(), x_grad_ref)
93

94 95
        y_grad_ref = np.ones([3, 3]) * 0.0
        np.testing.assert_array_equal(y.grad.numpy(), y_grad_ref)
96

97 98
        new_out_g_ref = np.ones([3, 3]) * 3.0
        np.testing.assert_array_equal(new_out_g.grad.numpy(), new_out_g_ref)
99

100 101 102 103
        new_x_g_g_ref = np.ones([3, 3]) * 0.0
        new_y_g_g_ref = np.ones([3, 3]) * 3.0
        np.testing.assert_array_equal(new_x_g_g.grad.numpy(), new_x_g_g_ref)
        np.testing.assert_array_equal(new_y_g_g.grad.numpy(), new_y_g_g_ref)
104 105


W
Weilong Wu 已提交
106 107 108 109 110
class TestDygraphTripleGrad(TestCase):
    def setUp(self):
        self.sort_sum_gradient = False
        self.shape = [5, 5]

111 112 113 114 115 116 117 118 119 120
    def grad(
        self,
        outputs,
        inputs,
        grad_outputs=None,
        no_grad_vars=None,
        retain_graph=None,
        create_graph=False,
        allow_unused=False,
    ):
W
Weilong Wu 已提交
121
        fluid.set_flags({'FLAGS_sort_sum_gradient': self.sort_sum_gradient})
122 123 124 125 126 127 128 129 130
        return fluid.dygraph.grad(
            outputs=outputs,
            inputs=inputs,
            grad_outputs=grad_outputs,
            no_grad_vars=no_grad_vars,
            retain_graph=retain_graph,
            create_graph=create_graph,
            allow_unused=allow_unused,
        )
W
Weilong Wu 已提交
131 132

    @dygraph_guard
133
    def func_exception(self):
W
Weilong Wu 已提交
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
        with self.assertRaises(AssertionError):
            self.grad(None, None)

        shape = self.shape

        with self.assertRaises(AssertionError):
            self.grad(1, random_var(shape))

        with self.assertRaises(AssertionError):
            self.grad(random_var(shape), 1)

        with self.assertRaises(AssertionError):
            self.grad([1], [random_var(shape)])

        with self.assertRaises(AssertionError):
            self.grad([random_var(shape)], [1])

        with self.assertRaises(AssertionError):
152 153 154 155 156
            self.grad(
                [random_var(shape), random_var(shape)],
                [random_var(shape)],
                [random_var(shape)],
            )
W
Weilong Wu 已提交
157 158

        with self.assertRaises(AssertionError):
159 160 161
            self.grad(
                [random_var(shape)], [random_var(shape)], no_grad_vars=[1]
            )
W
Weilong Wu 已提交
162 163 164 165 166

        with self.assertRaises(AssertionError):
            self.grad([random_var(shape)], [random_var(shape)], no_grad_vars=1)

    @dygraph_guard
167
    def func_example_with_gradient_and_create_graph(self):
W
Weilong Wu 已提交
168 169 170 171 172 173 174 175 176 177 178 179 180
        x = random_var(self.shape)
        x_np = x.numpy()
        x.stop_gradient = False

        y = random_var(self.shape)
        y_np = y.numpy()
        y.stop_gradient = False

        z = random_var(self.shape)
        z_np = z.numpy()
        numel = z_np.size
        z.stop_gradient = False

181
        out = paddle.nn.functional.sigmoid(paddle.matmul(x, y) + z)
W
Weilong Wu 已提交
182 183
        out_np = out.numpy()

184
        (dx_actual,) = self.grad([out], [x], create_graph=True)
W
Weilong Wu 已提交
185 186
        # Theoritical result based on math calculation
        dout = np.ones(self.shape).astype('float32')
187 188 189
        dx_expected = np.matmul(
            dout * out_np * (1 - out_np), np.transpose(y_np)
        )
190
        np.testing.assert_allclose(dx_actual.numpy(), dx_expected, rtol=1e-05)
W
Weilong Wu 已提交
191

192
        (ddx_actual,) = self.grad([dx_actual], [x], create_graph=True)
W
Weilong Wu 已提交
193 194 195
        # Theoritical result based on math calculation
        DDY = np.zeros(self.shape).astype('float32')
        DDX = np.ones(self.shape).astype('float32')
196 197 198
        double_grad_tmp1 = np.matmul(
            dout * out_np * (1 - out_np), np.transpose(DDY)
        )
W
Weilong Wu 已提交
199 200
        double_grad_tmp2 = np.matmul(DDX, y_np) + np.matmul(x_np, DDY)
        double_grad_tmp3 = (
201 202 203 204 205
            (1 - 2 * out_np) * dout * double_grad_tmp2 * out_np * (1 - out_np)
        )
        ddx_expected = double_grad_tmp1 + np.matmul(
            double_grad_tmp3, np.transpose(y_np)
        )
206
        np.testing.assert_allclose(ddx_actual.numpy(), ddx_expected, rtol=1e-05)
W
Weilong Wu 已提交
207 208 209 210 211

        # Theoritical result based on math calculation
        d_ddout = np.zeros(self.shape).astype('float32')
        tmp0 = np.matmul(DDX, y_np) + np.matmul(x_np, DDY)
        tmp1 = (1 - 2 * out_np) * ((1 - 2 * out_np) * dout * tmp0 * tmp0)
212 213 214 215 216 217 218
        tmp2 = (
            tmp0 * (1 - 2 * out_np) * d_ddout
            - 2 * dout * (1 - out_np) * out_np * tmp0 * tmp0
        )
        dddx_expected = np.matmul(
            ((tmp1 + tmp2) * out_np * (1 - out_np)), np.transpose(y_np)
        )
W
Weilong Wu 已提交
219 220 221

        ddx_actual.backward()
        dddx_grad_actual = x.gradient()
222
        np.testing.assert_allclose(dddx_grad_actual, dddx_expected, rtol=1e-05)
W
Weilong Wu 已提交
223

224
    def test_all_cases(self):
225
        fluid.set_flags({"FLAGS_retain_grad_for_all_tensor": True})
226 227
        self.func_exception()
        self.func_example_with_gradient_and_create_graph()
228
        fluid.set_flags({"FLAGS_retain_grad_for_all_tensor": False})
229

W
Weilong Wu 已提交
230

231 232 233 234 235 236 237
class TestDygraphTripleGradBradcastCase(TestCase):
    def setUp(self):
        self.sort_sum_gradient = False
        self.x_shape = [3, 2, 2]
        self.y_shape = [1, 2, 2]
        self.z_shape = [2, 2]

238 239 240 241 242 243 244 245 246 247
    def grad(
        self,
        outputs,
        inputs,
        grad_outputs=None,
        no_grad_vars=None,
        retain_graph=None,
        create_graph=False,
        allow_unused=False,
    ):
248
        fluid.set_flags({'FLAGS_sort_sum_gradient': self.sort_sum_gradient})
249 250 251 252 253 254 255 256 257
        return fluid.dygraph.grad(
            outputs=outputs,
            inputs=inputs,
            grad_outputs=grad_outputs,
            no_grad_vars=no_grad_vars,
            retain_graph=retain_graph,
            create_graph=create_graph,
            allow_unused=allow_unused,
        )
258 259

    @dygraph_guard
260
    def func_example_with_gradient_and_create_graph(self):
261 262 263 264 265 266 267 268 269 270 271 272 273
        x = random_var(self.x_shape)
        x_np = x.numpy()
        x.stop_gradient = False

        y = random_var(self.y_shape)
        y_np = y.numpy()
        y.stop_gradient = False

        z = random_var(self.z_shape)
        z_np = z.numpy()
        numel = z_np.size
        z.stop_gradient = False

274
        out = paddle.nn.functional.sigmoid(paddle.matmul(x, y) + z)
275 276
        out_np = out.numpy()

277
        (dx_actual,) = self.grad([out], [x], create_graph=True)
278 279
        # Theoritical result based on math calculation
        dout = np.ones(self.x_shape).astype('float32')
280 281 282
        dx_expected = np.matmul(
            dout * out_np * (1 - out_np), np.transpose(y_np, axes=(0, 2, 1))
        )
283
        np.testing.assert_allclose(dx_actual.numpy(), dx_expected, rtol=1e-05)
284

285
        (ddx_actual,) = self.grad([dx_actual], [x], create_graph=True)
286 287 288
        # Theoritical result based on math calculation
        DDY = np.zeros(self.y_shape).astype('float32')
        DDX = np.ones(self.x_shape).astype('float32')
289 290 291
        double_grad_tmp1 = np.matmul(
            dout * out_np * (1 - out_np), np.transpose(DDY, axes=(0, 2, 1))
        )
292 293
        double_grad_tmp2 = np.matmul(DDX, y_np) + np.matmul(x_np, DDY)
        double_grad_tmp3 = (
294 295
            (1 - 2 * out_np) * dout * double_grad_tmp2 * out_np * (1 - out_np)
        )
296
        ddx_expected = double_grad_tmp1 + np.matmul(
297 298
            double_grad_tmp3, np.transpose(y_np, axes=(0, 2, 1))
        )
299
        np.testing.assert_allclose(ddx_actual.numpy(), ddx_expected, rtol=1e-05)
300 301 302 303 304

        # Theoritical result based on math calculation
        d_ddout = np.zeros(self.x_shape).astype('float32')
        tmp0 = np.matmul(DDX, y_np) + np.matmul(x_np, DDY)
        tmp1 = (1 - 2 * out_np) * ((1 - 2 * out_np) * dout * tmp0 * tmp0)
305 306 307 308 309 310 311 312
        tmp2 = (
            tmp0 * (1 - 2 * out_np) * d_ddout
            - 2 * dout * (1 - out_np) * out_np * tmp0 * tmp0
        )
        dddx_expected = np.matmul(
            ((tmp1 + tmp2) * out_np * (1 - out_np)),
            np.transpose(y_np, axes=(0, 2, 1)),
        )
313 314 315

        ddx_actual.backward()
        dddx_grad_actual = x.gradient()
316
        np.testing.assert_allclose(dddx_grad_actual, dddx_expected, rtol=1e-05)
317

318
    def test_all_cases(self):
319
        fluid.set_flags({"FLAGS_retain_grad_for_all_tensor": True})
320
        self.func_example_with_gradient_and_create_graph()
321
        fluid.set_flags({"FLAGS_retain_grad_for_all_tensor": False})
322

323

W
Weilong Wu 已提交
324 325
if __name__ == '__main__':
    unittest.main()