test_softmax_op.py 12.5 KB
Newer Older
1
#   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
D
dzhwinter 已提交
2
#
D
dzhwinter 已提交
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
D
dzhwinter 已提交
6
#
D
dzhwinter 已提交
7
#     http://www.apache.org/licenses/LICENSE-2.0
D
dzhwinter 已提交
8
#
D
dzhwinter 已提交
9 10 11 12 13 14
# 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.

Q
qijun 已提交
15 16
import unittest
import numpy as np
17
from op_test import OpTest, convert_float_to_uint16
18
import paddle.fluid.core as core
19
import paddle
20
import paddle.nn.functional as F
21 22

np.random.seed(10)
Q
qijun 已提交
23 24 25 26


def stable_softmax(x):
    """Compute the softmax of vector x in a numerically stable way."""
27 28
    # clip to shiftx, otherwise, when calc loss with
    # log(exp(shiftx)), may get log(0)=INF
29
    shiftx = (x - np.max(x)).clip(-64.0)
Q
qijun 已提交
30 31 32 33
    exps = np.exp(shiftx)
    return exps / np.sum(exps)


34 35 36 37 38 39 40 41 42
def ref_softmax(x, axis=None, dtype=None):
    x_t = x.copy()
    if dtype is not None:
        x_t = x_t.astype(dtype)
    if axis is None:
        axis = -1
    return np.apply_along_axis(stable_softmax, axis, x_t)


Q
qijun 已提交
43
class TestSoftmaxOp(OpTest):
F
fengjiayi 已提交
44 45 46
    def get_x_shape(self):
        return [10, 10]

D
dengkaipeng 已提交
47 48 49
    def get_axis(self):
        return -1

Q
qijun 已提交
50
    def setUp(self):
Q
fix bug  
qijun 已提交
51
        self.op_type = "softmax"
52
        self.use_cudnn = False
K
Kexin Zhao 已提交
53
        self.use_mkldnn = False
54 55
        # explicilty use float32 for ROCm, as MIOpen does not yet support float64
        self.dtype = np.float32 if core.is_compiled_with_rocm() else np.float64
K
Kexin Zhao 已提交
56
        self.init_kernel_type()
F
fengjiayi 已提交
57
        self.shape = self.get_x_shape()
D
dengkaipeng 已提交
58
        self.axis = self.get_axis()
F
fengjiayi 已提交
59

60
        np.random.seed(0)
F
fengjiayi 已提交
61
        x = np.random.uniform(0.1, 1, self.shape).astype(self.dtype)
D
dengkaipeng 已提交
62
        out = np.apply_along_axis(stable_softmax, self.axis, x)
K
Kexin Zhao 已提交
63 64 65

        self.inputs = {'X': OpTest.np_dtype_to_fluid_dtype(x)}
        self.outputs = {'Out': out}
66
        self.attrs = {
D
dengkaipeng 已提交
67
            'axis': self.axis,
68
            'use_cudnn': self.use_cudnn,
69
            'use_mkldnn': self.use_mkldnn,
70
        }
71

K
Kexin Zhao 已提交
72
    def init_kernel_type(self):
73
        pass
Q
qijun 已提交
74

Q
qijun 已提交
75
    def test_check_output(self):
76
        # TODO(wangzhongpu): support mkldnn op in dygraph mode
77 78
        if self.use_cudnn:
            place = core.CUDAPlace(0)
79
            self.check_output_with_place(
80 81
                place, atol=1e-5, check_dygraph=(self.use_mkldnn == False)
            )
82
        else:
83
            self.check_output(check_dygraph=(self.use_mkldnn == False))
Q
qijun 已提交
84

Q
qijun 已提交
85
    def test_check_grad(self):
86
        # TODO(wangzhongpu): support mkldnn op in dygraph mode
C
chengduo 已提交
87
        if self.use_cudnn or self.dtype == np.float16:
88
            place = core.CUDAPlace(0)
C
chengduo 已提交
89 90
            if core.is_float16_supported(place):
                self.check_grad_with_place(
91 92
                    place,
                    ["X"],
93 94
                    "Out",
                    max_relative_error=0.01,
95 96
                    check_dygraph=(self.use_mkldnn == False),
                )
97
        else:
98 99 100 101 102 103
            self.check_grad(
                ["X"],
                "Out",
                max_relative_error=0.01,
                check_dygraph=(self.use_mkldnn == False),
            )
104 105


F
fengjiayi 已提交
106 107 108 109 110
class TestSoftmaxOp2(TestSoftmaxOp):
    def get_x_shape(self):
        return [2, 3, 4, 5]


D
dengkaipeng 已提交
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
class TestSoftmaxOp3(TestSoftmaxOp):
    def get_x_shape(self):
        return [2, 3, 4, 5]

    def get_axis(self):
        return 0


class TestSoftmaxOp4(TestSoftmaxOp):
    def get_x_shape(self):
        return [2, 3, 4, 5]

    def get_axis(self):
        return 1


class TestSoftmaxOp5(TestSoftmaxOp):
    def get_x_shape(self):
        return [2, 3, 4, 5]

    def get_axis(self):
        return 2


135
class TestSoftmaxOp6(TestSoftmaxOp):
D
dengkaipeng 已提交
136 137 138 139 140 141 142
    def get_x_shape(self):
        return [2, 3, 4, 5]

    def get_axis(self):
        return 3


143 144 145
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
146
class TestSoftmaxCUDNNOp(TestSoftmaxOp):
K
Kexin Zhao 已提交
147 148 149 150
    def init_kernel_type(self):
        self.use_cudnn = True


151 152 153
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
F
fengjiayi 已提交
154 155 156 157 158
class TestSoftmaxCUDNNOp2(TestSoftmaxCUDNNOp):
    def get_x_shape(self):
        return [2, 3, 4, 5]


159 160 161
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
G
GaoWei8 已提交
162 163 164 165 166 167 168 169
class TestSoftmaxCUDNNOp3(TestSoftmaxCUDNNOp):
    def get_x_shape(self):
        return [2, 3, 4, 5]

    def get_axis(self):
        return 0


170 171 172
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
G
GaoWei8 已提交
173 174 175 176 177 178 179 180
class TestSoftmaxCUDNNOp4(TestSoftmaxCUDNNOp):
    def get_x_shape(self):
        return [2, 3, 4, 5]

    def get_axis(self):
        return 1


181 182 183
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
D
dengkaipeng 已提交
184
class TestSoftmaxCUDNNOp5(TestSoftmaxCUDNNOp):
D
dengkaipeng 已提交
185 186 187
    def get_x_shape(self):
        return [2, 3, 4, 5]

G
GaoWei8 已提交
188 189 190 191
    def get_axis(self):
        return 2


192 193 194
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
G
GaoWei8 已提交
195 196 197 198
class TestSoftmaxCUDNNOp6(TestSoftmaxCUDNNOp):
    def get_x_shape(self):
        return [2, 3, 4, 5]

D
dengkaipeng 已提交
199
    def get_axis(self):
200
        return 3
D
dengkaipeng 已提交
201 202


203 204 205
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
G
GaoWei8 已提交
206 207 208 209 210
class TestSoftmaxCUDNNOp7(TestSoftmaxCUDNNOp):
    def get_x_shape(self):
        return [2, 3, 4, 5, 6]


211 212 213
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
G
GaoWei8 已提交
214 215 216 217 218 219 220 221
class TestSoftmaxCUDNNOp8(TestSoftmaxCUDNNOp):
    def get_x_shape(self):
        return [2, 3, 4, 5, 6]

    def get_axis(self):
        return 0


222 223 224
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
G
GaoWei8 已提交
225 226 227 228 229 230 231 232
class TestSoftmaxCUDNNOp9(TestSoftmaxCUDNNOp):
    def get_x_shape(self):
        return [2, 3, 4, 5, 6]

    def get_axis(self):
        return 1


233 234 235
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
G
GaoWei8 已提交
236 237 238 239 240 241 242 243
class TestSoftmaxCUDNNOp10(TestSoftmaxCUDNNOp):
    def get_x_shape(self):
        return [2, 3, 4, 5, 6]

    def get_axis(self):
        return 2


244 245 246
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
G
GaoWei8 已提交
247 248 249 250 251 252 253 254
class TestSoftmaxCUDNNOp11(TestSoftmaxCUDNNOp):
    def get_x_shape(self):
        return [2, 3, 4, 5, 6]

    def get_axis(self):
        return 3


255 256 257
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
G
GaoWei8 已提交
258 259 260 261 262 263 264 265
class TestSoftmaxCUDNNOp12(TestSoftmaxCUDNNOp):
    def get_x_shape(self):
        return [2, 3, 4, 5, 6]

    def get_axis(self):
        return 4


266 267 268
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
269 270 271 272 273 274 275 276 277 278
class TestSoftmaxFP16Op(TestSoftmaxOp):
    def init_kernel_type(self):
        self.dtype = np.float16

    def test_check_output(self):
        if core.is_compiled_with_cuda():
            place = core.CUDAPlace(0)
            if core.is_float16_supported(place):
                self.check_output_with_place(place, atol=1e-3)

C
chengduo 已提交
279 280 281 282
    # FIXME: If the x_shape is [10, 10], gradient failed.
    def test_check_grad(self):
        pass

283

284 285 286
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
Z
zhupengyang 已提交
287
class TestSoftmaxFP16Op2(TestSoftmaxFP16Op):
F
fengjiayi 已提交
288
    def get_x_shape(self):
Z
zhupengyang 已提交
289
        return [2, 3, 4, 10]
290

F
fengjiayi 已提交
291

292 293 294
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
K
Kexin Zhao 已提交
295 296
class TestSoftmaxFP16CUDNNOp(TestSoftmaxOp):
    def init_kernel_type(self):
297
        self.use_cudnn = True
K
Kexin Zhao 已提交
298 299 300 301 302 303 304
        self.dtype = np.float16

    def test_check_output(self):
        if core.is_compiled_with_cuda():
            place = core.CUDAPlace(0)
            if core.is_float16_supported(place):
                self.check_output_with_place(place, atol=1e-3)
Q
Qiao Longfei 已提交
305 306


307 308 309
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
F
fengjiayi 已提交
310 311 312 313 314
class TestSoftmaxFP16CUDNNOp2(TestSoftmaxFP16CUDNNOp):
    def get_x_shape(self):
        return [2, 3, 4, 5]


315 316 317
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
class TestSoftmaxBF16Op(OpTest):
    def setUp(self):
        self.op_type = "softmax"
        self.use_cudnn = self.init_cudnn()
        self.use_mkldnn = False
        self.dtype = np.uint16
        self.shape = [10, 10]
        self.axis = -1

        np.random.seed(0)
        x = np.random.uniform(0.1, 1, self.shape).astype(np.float32)
        out = np.apply_along_axis(stable_softmax, self.axis, x)

        self.inputs = {
            'X': OpTest.np_dtype_to_fluid_dtype(convert_float_to_uint16(x))
        }
        self.outputs = {'Out': convert_float_to_uint16(out)}
        self.attrs = {
            'axis': self.axis,
            'use_cudnn': self.use_cudnn,
338
            'use_mkldnn': self.use_mkldnn,
339 340 341 342 343 344 345
        }

    def init_cudnn(self):
        return False

    def test_check_output(self):
        place = core.CUDAPlace(0)
346 347 348
        self.check_output_with_place(
            place, check_dygraph=(self.use_mkldnn == False)
        )
349 350 351

    def test_check_grad(self):
        place = core.CUDAPlace(0)
352 353 354 355 356 357 358
        self.check_grad_with_place(
            place,
            ["X"],
            "Out",
            numeric_grad_delta=0.05,
            check_dygraph=(self.use_mkldnn == False),
        )
359 360 361


@unittest.skipIf(
362 363
    not core.is_compiled_with_cuda()
    or core.cudnn_version() < 8100
364
    or paddle.device.cuda.get_device_capability()[0] < 8,
365
    "only support compiled with CUDA and cudnn version need larger than 8.1.0 and device's compute capability is at least 8.0",
366
)
367 368 369 370 371
class TestSoftmaxBF16CUDNNOp(TestSoftmaxBF16Op):
    def init_cudnn(self):
        return True


372
class TestSoftmaxAPI(unittest.TestCase):
373
    def setUp(self):
374 375 376 377 378 379
        self.place = (
            paddle.CUDAPlace(0)
            if core.is_compiled_with_cuda()
            else paddle.CPUPlace()
        )
        self.x_np = np.random.uniform(-1.0, 1.0, [2, 3, 4, 5]).astype('float32')
380
        self.out_ref = np.apply_along_axis(stable_softmax, -1, self.x_np)
381 382 383 384
        self.executed_api()

    def executed_api(self):
        self.softmax = F.softmax
385

386 387
    def test_static_check(self):
        with paddle.static.program_guard(paddle.static.Program()):
388
            x = paddle.fluid.data('X', self.x_np.shape, 'float32')
389
            out1 = self.softmax(x)
390 391
            m = paddle.nn.Softmax()
            out2 = m(x)
392
            exe = paddle.static.Executor(self.place)
393 394 395
            res = exe.run(feed={'X': self.x_np}, fetch_list=[out1, out2])
        out_ref = ref_softmax(self.x_np, axis=-1, dtype=None)
        for r in res:
396
            np.testing.assert_allclose(out_ref, r, rtol=1e-05)
397

398
    def test_dygraph_check(self):
399
        paddle.disable_static(self.place)
400

401
        x = paddle.to_tensor(self.x_np)
402 403
        out1 = self.softmax(x)
        x = paddle.to_tensor(self.x_np)
404 405 406 407
        m = paddle.nn.Softmax()
        out2 = m(x)
        out_ref = ref_softmax(self.x_np, axis=-1, dtype=None)
        for r in [out1, out2]:
408
            np.testing.assert_allclose(out_ref, r.numpy(), rtol=1e-05)
409

410 411
        out1 = self.softmax(x, axis=0)
        x = paddle.to_tensor(self.x_np)
412 413 414 415
        m = paddle.nn.Softmax(axis=0)
        out2 = m(x)
        out_ref = ref_softmax(self.x_np, axis=0, dtype=None)
        for r in [out1, out2]:
416
            np.testing.assert_allclose(out_ref, r.numpy(), rtol=1e-05)
417

418 419 420 421 422 423 424
        # explicilty use float32 for ROCm, as MIOpen does not yet support float64
        if core.is_compiled_with_rocm():
            out = self.softmax(x, dtype=np.float32)
            out_ref = ref_softmax(self.x_np, axis=-1, dtype=np.float32)
        else:
            out = self.softmax(x, dtype=np.float64)
            out_ref = ref_softmax(self.x_np, axis=-1, dtype=np.float64)
425
        np.testing.assert_allclose(out_ref, out.numpy(), rtol=1e-05)
426

427
        paddle.enable_static()
428 429

    def test_error(self):
430 431
        with paddle.static.program_guard(paddle.static.Program()):
            # The input type must be Variable.
432
            self.assertRaises(TypeError, self.softmax, 1)
433
            # The input dtype must be float16, float32, float64.
434 435 436
            x_int32 = paddle.fluid.data(
                name='x_int32', shape=[2, 3], dtype='int32'
            )
437
            self.assertRaises(TypeError, self.softmax, x_int32)
438
            # support the input dtype is float16
439 440 441
            x_fp16 = paddle.fluid.data(
                name='x_fp16', shape=[2, 3], dtype='float16'
            )
442 443 444 445 446 447
            self.softmax(x_fp16)


class TestSoftmaxInplaceAPI(TestSoftmaxAPI):
    def executed_api(self):
        self.softmax = F.softmax_
448 449


C
caoying03 已提交
450
if __name__ == "__main__":
Q
qijun 已提交
451
    unittest.main()