未验证 提交 f0f2e2f9 编写于 作者: J JYChen 提交者: GitHub

Add notes and more cases for quantile unittest. (#41191)

* add notes for quantile UT

* Supoort quantile in static-mode and add UT
上级 3b7b8528
...@@ -20,46 +20,59 @@ import paddle ...@@ -20,46 +20,59 @@ import paddle
class TestQuantile(unittest.TestCase): class TestQuantile(unittest.TestCase):
"""
This class is used for numerical precision testing. If there is
a corresponding numpy API, the precision comparison can be performed directly.
Otherwise, it needs to be verified by numpy implementated function.
"""
def setUp(self): def setUp(self):
np.random.seed(678) np.random.seed(678)
self.input_data = np.random.rand(6, 7, 8, 9, 10) self.input_data = np.random.rand(6, 7, 8, 9, 10)
# Test correctness when q and axis are set.
def test_quantile_single_q(self): def test_quantile_single_q(self):
x = paddle.to_tensor(self.input_data) x = paddle.to_tensor(self.input_data)
paddle_res = paddle.quantile(x, q=0.5, axis=2) paddle_res = paddle.quantile(x, q=0.5, axis=2)
np_res = np.quantile(self.input_data, q=0.5, axis=2) np_res = np.quantile(self.input_data, q=0.5, axis=2)
self.assertTrue(np.allclose(paddle_res.numpy(), np_res)) self.assertTrue(np.allclose(paddle_res.numpy(), np_res))
# Test correctness for default axis.
def test_quantile_with_no_axis(self): def test_quantile_with_no_axis(self):
x = paddle.to_tensor(self.input_data) x = paddle.to_tensor(self.input_data)
paddle_res = paddle.quantile(x, q=0.35) paddle_res = paddle.quantile(x, q=0.35)
np_res = np.quantile(self.input_data, q=0.35) np_res = np.quantile(self.input_data, q=0.35)
self.assertTrue(np.allclose(paddle_res.numpy(), np_res)) self.assertTrue(np.allclose(paddle_res.numpy(), np_res))
# Test correctness for multiple axis.
def test_quantile_with_multi_axis(self): def test_quantile_with_multi_axis(self):
x = paddle.to_tensor(self.input_data) x = paddle.to_tensor(self.input_data)
paddle_res = paddle.quantile(x, q=0.75, axis=[0, 2, 3]) paddle_res = paddle.quantile(x, q=0.75, axis=[0, 2, 3])
np_res = np.quantile(self.input_data, q=0.75, axis=[0, 2, 3]) np_res = np.quantile(self.input_data, q=0.75, axis=[0, 2, 3])
self.assertTrue(np.allclose(paddle_res.numpy(), np_res)) self.assertTrue(np.allclose(paddle_res.numpy(), np_res))
# Test correctness when keepdim is set.
def test_quantile_with_keepdim(self): def test_quantile_with_keepdim(self):
x = paddle.to_tensor(self.input_data) x = paddle.to_tensor(self.input_data)
paddle_res = paddle.quantile(x, q=0.35, axis=4, keepdim=True) paddle_res = paddle.quantile(x, q=0.35, axis=4, keepdim=True)
np_res = np.quantile(self.input_data, q=0.35, axis=4, keepdims=True) np_res = np.quantile(self.input_data, q=0.35, axis=4, keepdims=True)
self.assertTrue(np.allclose(paddle_res.numpy(), np_res)) self.assertTrue(np.allclose(paddle_res.numpy(), np_res))
# Test correctness when all parameters are set.
def test_quantile_with_keepdim_and_multiple_axis(self): def test_quantile_with_keepdim_and_multiple_axis(self):
x = paddle.to_tensor(self.input_data) x = paddle.to_tensor(self.input_data)
paddle_res = paddle.quantile(x, q=0.1, axis=[1, 4], keepdim=True) paddle_res = paddle.quantile(x, q=0.1, axis=[1, 4], keepdim=True)
np_res = np.quantile(self.input_data, q=0.1, axis=[1, 4], keepdims=True) np_res = np.quantile(self.input_data, q=0.1, axis=[1, 4], keepdims=True)
self.assertTrue(np.allclose(paddle_res.numpy(), np_res)) self.assertTrue(np.allclose(paddle_res.numpy(), np_res))
# Test correctness when q = 0.
def test_quantile_with_boundary_q(self): def test_quantile_with_boundary_q(self):
x = paddle.to_tensor(self.input_data) x = paddle.to_tensor(self.input_data)
paddle_res = paddle.quantile(x, q=0, axis=3) paddle_res = paddle.quantile(x, q=0, axis=3)
np_res = np.quantile(self.input_data, q=0, axis=3) np_res = np.quantile(self.input_data, q=0, axis=3)
self.assertTrue(np.allclose(paddle_res.numpy(), np_res)) self.assertTrue(np.allclose(paddle_res.numpy(), np_res))
# Test correctness when input includes NaN.
def test_quantile_include_NaN(self): def test_quantile_include_NaN(self):
input_data = np.random.randn(2, 3, 4) input_data = np.random.randn(2, 3, 4)
input_data[0, 1, 1] = np.nan input_data[0, 1, 1] = np.nan
...@@ -69,6 +82,10 @@ class TestQuantile(unittest.TestCase): ...@@ -69,6 +82,10 @@ class TestQuantile(unittest.TestCase):
class TestQuantileMuitlpleQ(unittest.TestCase): class TestQuantileMuitlpleQ(unittest.TestCase):
"""
This class is used to test multiple input of q.
"""
def setUp(self): def setUp(self):
np.random.seed(678) np.random.seed(678)
self.input_data = np.random.rand(10, 3, 4, 5, 4) self.input_data = np.random.rand(10, 3, 4, 5, 4)
...@@ -95,56 +112,125 @@ class TestQuantileMuitlpleQ(unittest.TestCase): ...@@ -95,56 +112,125 @@ class TestQuantileMuitlpleQ(unittest.TestCase):
class TestQuantileError(unittest.TestCase): class TestQuantileError(unittest.TestCase):
"""
This class is used to test that exceptions are thrown correctly.
Validity of all parameter values and types should be considered.
"""
def setUp(self): def setUp(self):
self.x = paddle.randn((2, 3, 4)) self.x = paddle.randn((2, 3, 4))
def test_errors(self): def test_errors(self):
# Test error when q > 1
def test_q_range_error_1(): def test_q_range_error_1():
paddle_res = paddle.quantile(self.x, q=1.5) paddle_res = paddle.quantile(self.x, q=1.5)
self.assertRaises(ValueError, test_q_range_error_1) self.assertRaises(ValueError, test_q_range_error_1)
# Test error when q < 0
def test_q_range_error_2(): def test_q_range_error_2():
paddle_res = paddle.quantile(self.x, q=[0.2, -0.3]) paddle_res = paddle.quantile(self.x, q=[0.2, -0.3])
self.assertRaises(ValueError, test_q_range_error_2) self.assertRaises(ValueError, test_q_range_error_2)
# Test error with no valid q
def test_q_range_error_3(): def test_q_range_error_3():
paddle_res = paddle.quantile(self.x, q=[]) paddle_res = paddle.quantile(self.x, q=[])
self.assertRaises(ValueError, test_q_range_error_3) self.assertRaises(ValueError, test_q_range_error_3)
# Test error when x is not Tensor
def test_x_type_error(): def test_x_type_error():
x = [1, 3, 4] x = [1, 3, 4]
paddle_res = paddle.quantile(x, q=0.9) paddle_res = paddle.quantile(x, q=0.9)
self.assertRaises(TypeError, test_x_type_error) self.assertRaises(TypeError, test_x_type_error)
# Test error when scalar axis is not int
def test_axis_type_error_1(): def test_axis_type_error_1():
paddle_res = paddle.quantile(self.x, q=0.4, axis=0.4) paddle_res = paddle.quantile(self.x, q=0.4, axis=0.4)
self.assertRaises(ValueError, test_axis_type_error_1) self.assertRaises(ValueError, test_axis_type_error_1)
# Test error when axis in List is not int
def test_axis_type_error_2(): def test_axis_type_error_2():
paddle_res = paddle.quantile(self.x, q=0.4, axis=[1, 0.4]) paddle_res = paddle.quantile(self.x, q=0.4, axis=[1, 0.4])
self.assertRaises(ValueError, test_axis_type_error_2) self.assertRaises(ValueError, test_axis_type_error_2)
# Test error when axis not in [-D, D)
def test_axis_value_error_1(): def test_axis_value_error_1():
paddle_res = paddle.quantile(self.x, q=0.4, axis=10) paddle_res = paddle.quantile(self.x, q=0.4, axis=10)
self.assertRaises(ValueError, test_axis_value_error_1) self.assertRaises(ValueError, test_axis_value_error_1)
# Test error when axis not in [-D, D)
def test_axis_value_error_2(): def test_axis_value_error_2():
paddle_res = paddle.quantile(self.x, q=0.4, axis=[1, -10]) paddle_res = paddle.quantile(self.x, q=0.4, axis=[1, -10])
self.assertRaises(ValueError, test_axis_value_error_2) self.assertRaises(ValueError, test_axis_value_error_2)
# Test error with no valid axis
def test_axis_value_error_3(): def test_axis_value_error_3():
paddle_res = paddle.quantile(self.x, q=0.4, axis=[]) paddle_res = paddle.quantile(self.x, q=0.4, axis=[])
self.assertRaises(ValueError, test_axis_value_error_3) self.assertRaises(ValueError, test_axis_value_error_3)
class TestQuantileRuntime(unittest.TestCase):
"""
This class is used to test the API could run correctly with
different devices, different data types, and dygraph/static mode.
"""
def setUp(self):
np.random.seed(678)
self.input_data = np.random.rand(6, 7, 8, 9, 10)
self.dtypes = ['float32', 'float64']
self.devices = ['cpu']
if paddle.device.is_compiled_with_cuda():
self.devices.append('gpu')
def test_dygraph(self):
paddle.disable_static()
for device in self.devices:
# Check different devices
paddle.set_device(device)
for dtype in self.dtypes:
# Check different dtypes
np_input_data = self.input_data.astype(dtype)
x = paddle.to_tensor(np_input_data, dtype=dtype)
paddle_res = paddle.quantile(x, q=0.5, axis=2)
np_res = np.quantile(np_input_data, q=0.5, axis=2)
self.assertTrue(np.allclose(paddle_res.numpy(), np_res))
def test_static(self):
paddle.enable_static()
for device in self.devices:
x = paddle.static.data(
name="x", shape=self.input_data.shape, dtype=paddle.float32)
x_fp64 = paddle.static.data(
name="x_fp64",
shape=self.input_data.shape,
dtype=paddle.float64)
results = paddle.quantile(x, q=0.5, axis=2)
np_input_data = self.input_data.astype('float32')
results_fp64 = paddle.quantile(x_fp64, q=0.5, axis=2)
np_input_data_fp64 = self.input_data.astype('float64')
exe = paddle.static.Executor(device)
paddle_res, paddle_res_fp64 = exe.run(
paddle.static.default_main_program(),
feed={"x": np_input_data,
"x_fp64": np_input_data_fp64},
fetch_list=[results, results_fp64])
np_res = np.quantile(np_input_data, q=0.5, axis=2)
np_res_fp64 = np.quantile(np_input_data_fp64, q=0.5, axis=2)
self.assertTrue(
np.allclose(paddle_res, np_res) and np.allclose(paddle_res_fp64,
np_res_fp64))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -387,7 +387,7 @@ def quantile(x, q, axis=None, keepdim=False): ...@@ -387,7 +387,7 @@ def quantile(x, q, axis=None, keepdim=False):
if not isinstance(x, Variable): if not isinstance(x, Variable):
raise TypeError("input x should be a Tensor.") raise TypeError("input x should be a Tensor.")
dims = len(x.shape) dims = len(x.shape)
out_shape = x.shape out_shape = list(x.shape)
if axis is None: if axis is None:
x = paddle.flatten(x) x = paddle.flatten(x)
axis = 0 axis = 0
...@@ -433,16 +433,15 @@ def quantile(x, q, axis=None, keepdim=False): ...@@ -433,16 +433,15 @@ def quantile(x, q, axis=None, keepdim=False):
indices.append(q_num * (x.shape[axis] - 1)) indices.append(q_num * (x.shape[axis] - 1))
else: else:
raise TypeError("Type of q should be int, float, list or tuple.") raise TypeError("Type of q should be int, float, list or tuple.")
indices = paddle.to_tensor(indices).astype(paddle.float32)
sorted_tensor = paddle.sort(x, axis) sorted_tensor = paddle.sort(x, axis)
indices_below = paddle.floor(indices).astype(paddle.int32) indices_tensor = paddle.assign(indices).astype(paddle.float32)
indices_upper = paddle.ceil(indices).astype(paddle.int32) indices_below = paddle.floor(indices_tensor).astype(paddle.int32)
indices_upper = paddle.ceil(indices_tensor).astype(paddle.int32)
outputs = [] outputs = []
def expand_dim(indices, sorted_tensor_shape, axis): def expand_dim(indices, sorted_tensor_shape, axis):
assert axis < len(list(sorted_tensor_shape)) assert axis < len(list(sorted_tensor_shape))
expanded_shape = [1] * len(list(sorted_tensor_shape)) expanded_shape = [1] * len(list(sorted_tensor_shape))
expanded_shape[axis] = len(indices)
expanded_shape = tuple(expanded_shape) expanded_shape = tuple(expanded_shape)
indices = indices.reshape(expanded_shape) indices = indices.reshape(expanded_shape)
return indices return indices
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册