New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
【PaddlePaddle Hackathon 3 No.12】为 Paddle 新增 pairwise_distance #44161
Changes from 5 commits
1b7613f
515df56
2c64711
5da6d36
27ebf80
57cec0c
5bfe9de
504a3b6
a523910
34a2575
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,24 +20,65 @@ | |
import unittest | ||
|
||
|
||
def pairwise_distance(x, y, p=2.0, epsilon=1e-6, keepdim=False): | ||
return np.linalg.norm(x - y, ord=p, axis=1, keepdims=keepdim) | ||
def np_pairwise_distance(x, y, p=2.0, epsilon=1e-6, keepdim=False): | ||
|
||
distance = np.linalg.norm(x - y + epsilon, ord=p, axis=-1, keepdims=keepdim) | ||
# Paddle currently has not supported for 0-d Tensors, so even if keep_dim is False, | ||
# and neither x nor y is batched, a Tensor of shape (1, ) is returned | ||
if distance.ndim == 0: | ||
distance = np.expand_dims(distance, axis=0) | ||
return distance | ||
|
||
def test_static(x_np, y_np, p=2.0, epsilon=1e-6, keepdim=False): | ||
|
||
def call_pairwise_distance_layer(x, y, p=2., epsilon=1e-6, keepdim='False'): | ||
pairwise_distance = paddle.nn.PairwiseDistance(p=p, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
distance = pairwise_distance(x=x, y=y) | ||
return distance | ||
|
||
|
||
def call_pairwise_distance_functional(x, | ||
y, | ||
p=2., | ||
epsilon=1e-6, | ||
keepdim='False'): | ||
distance = paddle.nn.functional.pairwise_distance(x=x, | ||
y=y, | ||
p=p, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
return distance | ||
|
||
|
||
def test_static(place, | ||
x_np, | ||
y_np, | ||
p=2.0, | ||
epsilon=1e-6, | ||
keepdim=False, | ||
functional=False): | ||
prog = paddle.static.Program() | ||
startup_prog = paddle.static.Program() | ||
|
||
place = fluid.CUDAPlace( | ||
0) if paddle.fluid.core.is_compiled_with_cuda() else fluid.CPUPlace() | ||
|
||
paddle.enable_static() | ||
with paddle.static.program_guard(prog, startup_prog): | ||
x = paddle.fluid.data(name='x', shape=x_np.shape, dtype=x_np.dtype) | ||
y = paddle.fluid.data(name='y', shape=y_np.shape, dtype=x_np.dtype) | ||
dist = paddle.nn.layer.distance.PairwiseDistance(p=p, | ||
|
||
if functional: | ||
distance = call_pairwise_distance_functional(x=x, | ||
y=y, | ||
p=p, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
distance = dist(x, y) | ||
else: | ||
distance = call_pairwise_distance_layer(x=x, | ||
y=y, | ||
p=p, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
exe = paddle.static.Executor(place) | ||
static_ret = exe.run(prog, | ||
feed={ | ||
|
@@ -46,69 +87,279 @@ def test_static(x_np, y_np, p=2.0, epsilon=1e-6, keepdim=False): | |
}, | ||
fetch_list=[distance]) | ||
static_ret = static_ret[0] | ||
paddle.disable_static() | ||
return static_ret | ||
|
||
|
||
def test_dygraph(x_np, y_np, p=2.0, epsilon=1e-6, keepdim=False): | ||
paddle.disable_static() | ||
def test_dygraph(place, | ||
x_np, | ||
y_np, | ||
p=2.0, | ||
epsilon=1e-6, | ||
keepdim=False, | ||
functional=False): | ||
x = paddle.to_tensor(x_np) | ||
y = paddle.to_tensor(y_np) | ||
dist = paddle.nn.layer.distance.PairwiseDistance(p=p, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
distance = dist(x, y) | ||
dygraph_ret = distance.numpy() | ||
paddle.enable_static() | ||
if functional: | ||
dy_distance = call_pairwise_distance_functional(x=x, | ||
y=y, | ||
p=p, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
else: | ||
dy_distance = call_pairwise_distance_layer(x=x, | ||
y=y, | ||
p=p, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
dygraph_ret = dy_distance.numpy() | ||
return dygraph_ret | ||
|
||
|
||
def test_legacy_dygraph(place, | ||
x_np, | ||
y_np, | ||
p=2.0, | ||
epsilon=1e-6, | ||
keepdim=False, | ||
functional=False): | ||
paddle.fluid.framework._enable_legacy_dygraph() | ||
x = paddle.to_tensor(x_np) | ||
y = paddle.to_tensor(y_np) | ||
if functional: | ||
legacy_distance = call_pairwise_distance_functional(x=x, | ||
y=y, | ||
p=p, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
else: | ||
legacy_distance = call_pairwise_distance_layer(x=x, | ||
y=y, | ||
p=p, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
legacy_ret = legacy_distance.numpy() | ||
paddle.fluid.framework._disable_legacy_dygraph() | ||
return legacy_ret | ||
|
||
|
||
class TestPairwiseDistance(unittest.TestCase): | ||
|
||
def test_pairwise_distance(self): | ||
all_shape = [[100, 100], [4, 5, 6, 7]] | ||
epsilon = 1e-6 | ||
all_shape = [[5], [100, 100], [4, 5, 6, 7]] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
好的明白。
这里有一个同样的问题,原本的单测(fluid 下面的)也是对 undefined behavior( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 好的呀。 |
||
dtypes = ['float32', 'float64'] | ||
p_list = [-1, 0, 1, 2, np.inf, -np.inf] | ||
places = [paddle.CPUPlace()] | ||
if paddle.device.is_compiled_with_cuda(): | ||
places.append(paddle.CUDAPlace(0)) | ||
keeps = [False, True] | ||
for shape in all_shape: | ||
for dtype in dtypes: | ||
for keepdim in keeps: | ||
x_np = np.random.random(shape).astype(dtype) | ||
y_np = np.random.random(shape).astype(dtype) | ||
|
||
static_ret = test_static(x_np, y_np, keepdim=keepdim) | ||
dygraph_ret = test_dygraph(x_np, y_np, keepdim=keepdim) | ||
excepted_value = pairwise_distance(x_np, | ||
for place in places: | ||
for shape in all_shape: | ||
for dtype in dtypes: | ||
for p in p_list: | ||
for keepdim in keeps: | ||
x_np = np.random.random(shape).astype(dtype) | ||
y_np = np.random.random(shape).astype(dtype) | ||
|
||
static_ret = test_static(place, | ||
x_np, | ||
y_np, | ||
p, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
dygraph_ret = test_dygraph(place, | ||
x_np, | ||
y_np, | ||
p, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
legacy_ret = test_legacy_dygraph(place, | ||
x_np, | ||
y_np, | ||
p, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
excepted_value = np_pairwise_distance( | ||
x_np, y_np, p, epsilon=epsilon, keepdim=keepdim) | ||
|
||
self.assertEqual(static_ret.shape, | ||
excepted_value.shape) | ||
self.assertEqual(dygraph_ret.shape, | ||
excepted_value.shape) | ||
self.assertEqual(legacy_ret.shape, | ||
excepted_value.shape) | ||
|
||
self.assertTrue( | ||
np.allclose(static_ret, excepted_value)) | ||
self.assertTrue( | ||
np.allclose(dygraph_ret, excepted_value)) | ||
self.assertTrue( | ||
np.allclose(legacy_ret, excepted_value)) | ||
|
||
static_functional_ret = test_static(place, | ||
x_np, | ||
y_np, | ||
p, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
dygraph_functional_ret = test_dygraph( | ||
place, | ||
x_np, | ||
y_np, | ||
p, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
legacy_functional_ret = test_legacy_dygraph( | ||
place, | ||
x_np, | ||
y_np, | ||
p, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
|
||
self.assertTrue(np.allclose(static_ret, dygraph_ret)) | ||
self.assertTrue(np.allclose(static_ret, excepted_value)) | ||
self.assertTrue(np.allclose(dygraph_ret, excepted_value)) | ||
self.assertEqual(static_functional_ret.shape, | ||
excepted_value.shape) | ||
self.assertEqual(dygraph_functional_ret.shape, | ||
excepted_value.shape) | ||
self.assertEqual(legacy_functional_ret.shape, | ||
excepted_value.shape) | ||
|
||
def test_pairwise_distance_broadcast(self): | ||
self.assertTrue( | ||
np.allclose(static_functional_ret, | ||
excepted_value)) | ||
self.assertTrue( | ||
np.allclose(dygraph_functional_ret, | ||
excepted_value)) | ||
self.assertTrue( | ||
np.allclose(legacy_functional_ret, | ||
excepted_value)) | ||
|
||
def test_pairwise_distance_broadcast_1(self): | ||
shape_x = [100, 100] | ||
shape_y = [100, 1] | ||
epsilon = 1e-6 | ||
keepdim = False | ||
place = paddle.CPUPlace() | ||
x_np = np.random.random(shape_x).astype('float32') | ||
y_np = np.random.random(shape_y).astype('float32') | ||
static_ret = test_static(x_np, y_np, keepdim=keepdim) | ||
dygraph_ret = test_dygraph(x_np, y_np, keepdim=keepdim) | ||
excepted_value = pairwise_distance(x_np, y_np, keepdim=keepdim) | ||
self.assertTrue(np.allclose(static_ret, dygraph_ret)) | ||
static_ret = test_static(place=place, | ||
x_np=x_np, | ||
y_np=y_np, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
dygraph_ret = test_dygraph(place=place, | ||
x_np=x_np, | ||
y_np=y_np, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
legacy_ret = test_legacy_dygraph(place=place, | ||
x_np=x_np, | ||
y_np=y_np, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
excepted_value = np_pairwise_distance(x_np, | ||
y_np, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
|
||
self.assertEqual(static_ret.shape, excepted_value.shape) | ||
self.assertEqual(dygraph_ret.shape, excepted_value.shape) | ||
self.assertEqual(legacy_ret.shape, excepted_value.shape) | ||
|
||
self.assertTrue(np.allclose(static_ret, excepted_value)) | ||
self.assertTrue(np.allclose(dygraph_ret, excepted_value)) | ||
self.assertTrue(np.allclose(legacy_ret, excepted_value)) | ||
|
||
static_functional_ret = test_static(place=place, | ||
x_np=x_np, | ||
y_np=y_np, | ||
epsilon=epsilon, | ||
keepdim=keepdim, | ||
functional=True) | ||
dygraph_functional_ret = test_dygraph(place=place, | ||
x_np=x_np, | ||
y_np=y_np, | ||
epsilon=epsilon, | ||
keepdim=keepdim, | ||
functional=True) | ||
legacy_functional_ret = test_legacy_dygraph(place=place, | ||
x_np=x_np, | ||
y_np=y_np, | ||
epsilon=epsilon, | ||
keepdim=keepdim, | ||
functional=True) | ||
|
||
self.assertEqual(static_functional_ret.shape, excepted_value.shape) | ||
self.assertEqual(dygraph_functional_ret.shape, excepted_value.shape) | ||
self.assertEqual(legacy_functional_ret.shape, excepted_value.shape) | ||
|
||
def test_pairwise_distance_different_p(self): | ||
shape = [100, 100] | ||
self.assertTrue(np.allclose(static_functional_ret, excepted_value)) | ||
self.assertTrue(np.allclose(dygraph_functional_ret, excepted_value)) | ||
self.assertTrue(np.allclose(legacy_functional_ret, excepted_value)) | ||
|
||
def test_pairwise_distance_broadcast_2(self): | ||
shape_x = [100, 100] | ||
shape_y = [100] | ||
epsilon = 1e-6 | ||
keepdim = False | ||
p = 3.0 | ||
x_np = np.random.random(shape).astype('float32') | ||
y_np = np.random.random(shape).astype('float32') | ||
static_ret = test_static(x_np, y_np, p=p, keepdim=keepdim) | ||
dygraph_ret = test_dygraph(x_np, y_np, p=p, keepdim=keepdim) | ||
excepted_value = pairwise_distance(x_np, y_np, p=p, keepdim=keepdim) | ||
self.assertTrue(np.allclose(static_ret, dygraph_ret)) | ||
place = paddle.CPUPlace() | ||
x_np = np.random.random(shape_x).astype('float32') | ||
y_np = np.random.random(shape_y).astype('float32') | ||
static_ret = test_static(place=place, | ||
x_np=x_np, | ||
y_np=y_np, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
dygraph_ret = test_dygraph(place=place, | ||
x_np=x_np, | ||
y_np=y_np, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
legacy_ret = test_legacy_dygraph(place=place, | ||
x_np=x_np, | ||
y_np=y_np, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
excepted_value = np_pairwise_distance(x_np, | ||
y_np, | ||
epsilon=epsilon, | ||
keepdim=keepdim) | ||
|
||
self.assertEqual(static_ret.shape, excepted_value.shape) | ||
self.assertEqual(dygraph_ret.shape, excepted_value.shape) | ||
self.assertEqual(legacy_ret.shape, excepted_value.shape) | ||
|
||
self.assertTrue(np.allclose(static_ret, excepted_value)) | ||
self.assertTrue(np.allclose(dygraph_ret, excepted_value)) | ||
self.assertTrue(np.allclose(legacy_ret, excepted_value)) | ||
|
||
static_functional_ret = test_static(place=place, | ||
x_np=x_np, | ||
y_np=y_np, | ||
epsilon=epsilon, | ||
keepdim=keepdim, | ||
functional=True) | ||
dygraph_functional_ret = test_dygraph(place=place, | ||
x_np=x_np, | ||
y_np=y_np, | ||
epsilon=epsilon, | ||
keepdim=keepdim, | ||
functional=True) | ||
legacy_functional_ret = test_legacy_dygraph(place=place, | ||
x_np=x_np, | ||
y_np=y_np, | ||
epsilon=epsilon, | ||
keepdim=keepdim, | ||
functional=True) | ||
|
||
self.assertEqual(static_functional_ret.shape, excepted_value.shape) | ||
self.assertEqual(dygraph_functional_ret.shape, excepted_value.shape) | ||
self.assertEqual(legacy_functional_ret.shape, excepted_value.shape) | ||
|
||
self.assertTrue(np.allclose(static_functional_ret, excepted_value)) | ||
self.assertTrue(np.allclose(dygraph_functional_ret, excepted_value)) | ||
self.assertTrue(np.allclose(legacy_functional_ret, excepted_value)) | ||
|
||
|
||
if __name__ == "__main__": | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
截图来自 PR-CI-APPROVAL
这里利用 _enable_legacy_dygraph 开启 legacy 动态图的原因是原来的 PairwiseDistance(class API)是包含 legacy 动态图代码的,如果不添加相关测试的话,Coverage CI 过不了,因此利用该 API 添加了相关测试,如果该逻辑不再推荐添加的话,我们会删去相关逻辑,并删去相关测试代码
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个到时可以让对应的同学 approve 就好。目前还不能删掉这个逻辑。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
好哒~明白~