Skip to content
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

Merged
merged 10 commits into from Jul 29, 2022
334 changes: 292 additions & 42 deletions python/paddle/fluid/tests/unittests/test_pairwise_distance.py
Expand Up @@ -20,24 +20,64 @@
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={
Expand All @@ -46,69 +86,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()
Copy link
Member

@SigureMo SigureMo Jul 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

截图来自 PR-CI-APPROVAL

这里利用 _enable_legacy_dygraph 开启 legacy 动态图的原因是原来的 PairwiseDistance(class API)是包含 legacy 动态图代码的,如果不添加相关测试的话,Coverage CI 过不了,因此利用该 API 添加了相关测试,如果该逻辑不再推荐添加的话,我们会删去相关逻辑,并删去相关测试代码

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个到时可以让对应的同学 approve 就好。目前还不能删掉这个逻辑。

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好哒~明白~

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]]
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__":
Expand Down