Skip to content

Commit 9e4bd2e

Browse files
bottlerfacebook-github-bot
authored andcommitted
chamfer test consistency
Summary: Modify test_chamfer for more robustness. Avoid empty pointclouds, including where point_reduction is mean, for which we currently return nan (*), and so that we aren't looking at an empty gradient. Make sure we aren't using padding as points in the homogenous cases in the tests, which will lead to a tie between closest points and therefore a potential instability in the gradient - see pytorch/pytorch#35699. (*) This doesn't attempt to fix the nan. Reviewed By: nikhilaravi, gkioxari Differential Revision: D21157322 fbshipit-source-id: a609e84e25a24379c8928ff645d587552526e4af
1 parent faf0885 commit 9e4bd2e

File tree

2 files changed

+28
-20
lines changed

2 files changed

+28
-20
lines changed

tests/common_testing.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,8 @@ def assertClose(
123123
input, other, rtol=rtol, atol=atol, equal_nan=equal_nan
124124
)
125125

126+
if not close and msg is None:
127+
max_diff = backend.abs(input - other).max()
128+
self.fail(f"Not close. max diff {max_diff}.")
129+
126130
self.assertTrue(close, msg)

tests/test_chamfer.py

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from common_testing import TestCaseMixin
1010
from pytorch3d.loss import chamfer_distance
1111
from pytorch3d.structures.pointclouds import Pointclouds
12-
from pytorch3d.structures.utils import list_to_padded
1312

1413

1514
# Output of init_pointclouds
@@ -24,38 +23,39 @@ def setUp(self) -> None:
2423
torch.manual_seed(1)
2524

2625
@staticmethod
27-
def init_pointclouds(N, P1, P2, device, requires_grad: bool = True):
26+
def init_pointclouds(
27+
N, P1, P2, device, requires_grad: bool = True, allow_empty: bool = True
28+
):
2829
"""
2930
Create 2 pointclouds object and associated padded points/normals tensors by
3031
starting from lists. The clouds and tensors have the same data. The
3132
leaf nodes for the clouds are a list of tensors. The padded tensor can be
3233
used directly as a leaf node.
3334
"""
34-
p1_lengths = torch.randint(P1, size=(N,), dtype=torch.int64, device=device)
35-
p2_lengths = torch.randint(P2, size=(N,), dtype=torch.int64, device=device)
35+
low = 0 if allow_empty else 1
36+
p1_lengths = torch.randint(low, P1, size=(N,), dtype=torch.int64, device=device)
37+
p2_lengths = torch.randint(low, P2, size=(N,), dtype=torch.int64, device=device)
3638
weights = torch.rand((N,), dtype=torch.float32, device=device)
3739

3840
# list of points and normals tensors
41+
p1 = torch.rand((N, P1, 3), dtype=torch.float32, device=device)
42+
p2 = torch.rand((N, P2, 3), dtype=torch.float32, device=device)
43+
n1 = torch.rand((N, P1, 3), dtype=torch.float32, device=device)
44+
n2 = torch.rand((N, P2, 3), dtype=torch.float32, device=device)
45+
n1 /= n1.norm(dim=-1, p=2, keepdim=True)
46+
n2 /= n2.norm(dim=-1, p=2, keepdim=True)
47+
3948
p1_list = []
4049
p2_list = []
4150
n1_list = []
4251
n2_list = []
4352
for i in range(N):
4453
l1 = p1_lengths[i]
4554
l2 = p2_lengths[i]
46-
p1_list.append(torch.rand((l1, 3), dtype=torch.float32, device=device))
47-
p2_list.append(torch.rand((l2, 3), dtype=torch.float32, device=device))
48-
n1_list.append(torch.rand((l1, 3), dtype=torch.float32, device=device))
49-
n2_list.append(torch.rand((l2, 3), dtype=torch.float32, device=device))
50-
51-
n1_list = [n / n.norm(dim=-1, p=2, keepdim=True) for n in n1_list]
52-
n2_list = [n / n.norm(dim=-1, p=2, keepdim=True) for n in n2_list]
53-
54-
# Clone the lists and initialize padded tensors.
55-
p1 = list_to_padded([p.clone() for p in p1_list])
56-
p2 = list_to_padded([p.clone() for p in p2_list])
57-
n1 = list_to_padded([p.clone() for p in n1_list])
58-
n2 = list_to_padded([p.clone() for p in n2_list])
55+
p1_list.append(p1[i, :l1].clone())
56+
p2_list.append(p2[i, :l2].clone())
57+
n1_list.append(n1[i, :l1].clone())
58+
n2_list.append(n2[i, :l2].clone())
5959

6060
# Set requires_grad for all tensors in the lists and
6161
# padded tensors.
@@ -313,7 +313,9 @@ def test_chamfer_pointcloud_object_withnormals(self):
313313

314314
# Reinitialize all the tensors so that the
315315
# backward pass can be computed.
316-
points_normals = TestChamfer.init_pointclouds(N, P1, P2, device)
316+
points_normals = TestChamfer.init_pointclouds(
317+
N, P1, P2, device, allow_empty=False
318+
)
317319

318320
# Chamfer with pointclouds as input.
319321
cham_cloud, norm_cloud = chamfer_distance(
@@ -371,7 +373,9 @@ def test_chamfer_pointcloud_object_nonormals(self):
371373

372374
# Reinitialize all the tensors so that the
373375
# backward pass can be computed.
374-
points_normals = TestChamfer.init_pointclouds(N, P1, P2, device)
376+
points_normals = TestChamfer.init_pointclouds(
377+
N, P1, P2, device, allow_empty=False
378+
)
375379

376380
# Chamfer with pointclouds as input.
377381
cham_cloud, _ = chamfer_distance(
@@ -560,7 +564,7 @@ def _check_grad_by_type(self, x1, x2, lengths=None):
560564

561565
# List of tensors vs padded tensor
562566
for i in range(len(x1)):
563-
self.assertClose(x1[i].grad, x2.grad[i, : lengths[i]])
567+
self.assertClose(x1[i].grad, x2.grad[i, : lengths[i]], atol=1e-7)
564568
self.assertTrue(x2.grad[i, lengths[i] :].sum().item() == 0.0)
565569
elif all(torch.is_tensor(p) for p in [x1, x2]):
566570
# Two tensors

0 commit comments

Comments
 (0)