Skip to content

Commit 9edea54

Browse files
[Accuracy diff No. 163] Fix accuracy (output type) diff for paddle.clip API (#74719)
* fix(math.py): fix output type diff for clip kernel * fix(math.py): fix output type diff for clip kernel
1 parent 80f1123 commit 9edea54

File tree

2 files changed

+175
-8
lines changed

2 files changed

+175
-8
lines changed

python/paddle/tensor/math.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3823,7 +3823,8 @@ def clip(
38233823
name (str|None, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.
38243824
38253825
Returns:
3826-
Tensor: A Tensor with the same data type and data shape as input.
3826+
Tensor: A Tensor with the same data shape as input. If either min or max is a floating-point value/Tensor, the output tensor will have a data type of ``float32``. Otherwise, the output tensor will inherit the same data type as the input.
3827+
38273828
38283829
Examples:
38293830
.. code-block:: python
@@ -3859,14 +3860,33 @@ def clip(
38593860
else:
38603861
min_ = float(np.finfo(np.float32).min)
38613862
max_ = float(np.finfo(np.float32).max)
3863+
min = min_ if min is None else min
3864+
max = max_ if max is None else max
38623865

3863-
if in_dynamic_or_pir_mode():
3864-
if isinstance(min, Variable):
3865-
min = min.item(0)
3866-
if isinstance(max, Variable):
3867-
max = max.item(0)
3868-
min = min_ if min is None else min
3869-
max = max_ if max is None else max
3866+
if in_dynamic_mode():
3867+
if x_dtype in ['paddle.int32', 'paddle.int64']:
3868+
if isinstance(min, paddle.Tensor):
3869+
min = min.item(0)
3870+
if isinstance(max, paddle.Tensor):
3871+
max = max.item(0)
3872+
if isinstance(min, float) or isinstance(max, float):
3873+
x = paddle.cast(x, paddle.float32)
3874+
return _C_ops.clip(x, min, max)
3875+
elif in_pir_mode():
3876+
if x_dtype in ['paddle.int32', 'paddle.int64']:
3877+
if (
3878+
isinstance(min, float)
3879+
or isinstance(max, float)
3880+
or (
3881+
isinstance(min, paddle.pir.Value)
3882+
and min.dtype in [paddle.float32, paddle.float64]
3883+
)
3884+
or (
3885+
isinstance(max, paddle.pir.Value)
3886+
and max.dtype in [paddle.float32, paddle.float64]
3887+
)
3888+
):
3889+
x = paddle.cast(x, paddle.float32)
38703890
return _C_ops.clip(x, min, max)
38713891
else:
38723892
if min is not None:

test/legacy_test/test_clip_op.py

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,153 @@ def test_errors(self):
487487
paddle.disable_static()
488488

489489

490+
class TestClipAPI_Int(unittest.TestCase):
491+
def _executed_api(self, x, min=None, max=None):
492+
return paddle.clip(x, min, max)
493+
494+
def test_clip(self):
495+
paddle.enable_static()
496+
data_shape = [1, 9, 9, 4]
497+
data = np.random.random(data_shape).astype('int32')
498+
place = (
499+
base.CUDAPlace(0)
500+
if base.core.is_compiled_with_cuda()
501+
else base.CPUPlace()
502+
)
503+
exe = base.Executor(place)
504+
505+
main = paddle.static.Program()
506+
startup = paddle.static.Program()
507+
with paddle.static.program_guard(main, startup):
508+
images = paddle.static.data(
509+
name='image', shape=data_shape, dtype='int32'
510+
)
511+
min = paddle.static.data(name='min', shape=[1], dtype='float32')
512+
max = paddle.static.data(name='max', shape=[1], dtype='float32')
513+
out_1 = self._executed_api(images, min=min, max=max)
514+
out_2 = self._executed_api(images, min=2.2, max=8.9)
515+
out_3 = self._executed_api(images, min=3.3)
516+
out_4 = self._executed_api(images, max=4.7)
517+
out_5 = self._executed_api(images, min=min)
518+
out_6 = self._executed_api(images, max=max)
519+
out_7 = self._executed_api(images, max=-1.0)
520+
out_8 = self._executed_api(images)
521+
out_9 = self._executed_api(
522+
paddle.cast(images, 'int32'), min=2.2, max=8.9
523+
)
524+
out_10 = self._executed_api(
525+
paddle.cast(images * 10, 'int32'), min=2.8, max=8.8
526+
)
527+
out_11 = self._executed_api(
528+
paddle.cast(images * 10, 'int64'), min=2.8, max=8.8
529+
)
530+
531+
(
532+
res1,
533+
res2,
534+
res3,
535+
res4,
536+
res5,
537+
res6,
538+
res7,
539+
res8,
540+
res9,
541+
res10,
542+
res11,
543+
) = exe.run(
544+
main,
545+
feed={
546+
"image": data,
547+
"min": np.array([2.2]).astype('float32'),
548+
"max": np.array([8.8]).astype('float32'),
549+
},
550+
fetch_list=[
551+
out_1,
552+
out_2,
553+
out_3,
554+
out_4,
555+
out_5,
556+
out_6,
557+
out_7,
558+
out_8,
559+
out_9,
560+
out_10,
561+
out_11,
562+
],
563+
)
564+
565+
np.testing.assert_allclose(res1, data.clip(2.2, 8.8), rtol=1e-05)
566+
np.testing.assert_allclose(res2, data.clip(2.2, 8.9), rtol=1e-05)
567+
np.testing.assert_allclose(res3, data.clip(min=3.3), rtol=1e-05)
568+
np.testing.assert_allclose(res4, data.clip(max=4.7), rtol=1e-05)
569+
np.testing.assert_allclose(res5, data.clip(min=2.2), rtol=1e-05)
570+
np.testing.assert_allclose(res6, data.clip(max=8.8), rtol=1e-05)
571+
np.testing.assert_allclose(res7, data.clip(max=-1.0), rtol=1e-05)
572+
np.testing.assert_allclose(res8, data, rtol=1e-05)
573+
np.testing.assert_allclose(
574+
res9, data.astype(np.int32).clip(2.2, 8.9), rtol=1e-05
575+
)
576+
np.testing.assert_allclose(
577+
res10, (data * 10).astype(np.int32).clip(2.8, 8.8), rtol=1e-05
578+
)
579+
np.testing.assert_allclose(
580+
res11, (data * 10).astype(np.int64).clip(2.8, 8.8), rtol=1e-05
581+
)
582+
paddle.disable_static()
583+
584+
def test_clip_dygraph(self):
585+
paddle.disable_static()
586+
place = (
587+
base.CUDAPlace(0)
588+
if base.core.is_compiled_with_cuda()
589+
else base.CPUPlace()
590+
)
591+
paddle.disable_static(place)
592+
data_shape = [1, 9, 9, 4]
593+
data = np.random.random(data_shape).astype('int32')
594+
images = paddle.to_tensor(data, dtype='int32')
595+
v_min = paddle.to_tensor(np.array([2.2], dtype=np.float32))
596+
v_max = paddle.to_tensor(np.array([8.8], dtype=np.float32))
597+
598+
out_1 = self._executed_api(images, min=2.2, max=8.8)
599+
images = paddle.to_tensor(data, dtype='int32')
600+
out_2 = self._executed_api(images, min=2.2, max=8.9)
601+
images = paddle.to_tensor(data, dtype='int32')
602+
out_3 = self._executed_api(images, min=v_min, max=v_max)
603+
604+
out_4 = self._executed_api(
605+
paddle.cast(images * 10, 'int32'), min=2.2, max=8.8
606+
)
607+
out_5 = self._executed_api(
608+
paddle.cast(images * 10, 'int64'), min=2.2, max=8.8
609+
)
610+
# test with numpy.generic
611+
out_6 = self._executed_api(images, min=np.abs(2.2), max=np.abs(8.8))
612+
613+
np.testing.assert_allclose(
614+
out_1.numpy(), data.clip(2.2, 8.8), rtol=1e-05
615+
)
616+
np.testing.assert_allclose(
617+
out_2.numpy(), data.clip(2.2, 8.9), rtol=1e-05
618+
)
619+
np.testing.assert_allclose(
620+
out_3.numpy(), data.clip(2.2, 8.8), rtol=1e-05
621+
)
622+
np.testing.assert_allclose(
623+
out_4.numpy(),
624+
(data * 10).astype(np.int32).clip(2.2, 8.8),
625+
rtol=1e-05,
626+
)
627+
np.testing.assert_allclose(
628+
out_5.numpy(),
629+
(data * 10).astype(np.int64).clip(2.2, 8.8),
630+
rtol=1e-05,
631+
)
632+
np.testing.assert_allclose(
633+
out_6.numpy(), data.clip(2.2, 8.8), rtol=1e-05
634+
)
635+
636+
490637
class TestClipOpFp16(unittest.TestCase):
491638
def test_fp16(self):
492639
if base.core.is_compiled_with_cuda():

0 commit comments

Comments
 (0)