This is an automated email from the ASF dual-hosted git repository.

zhic pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new 4c13ae9  [Torch] Fix PyTorch NMS conversion for negative scores (#7137)
4c13ae9 is described below

commit 4c13ae9d17d1709ed7a777ce1bb72212e8d2559d
Author: masahi <[email protected]>
AuthorDate: Sat Dec 26 09:13:29 2020 +0900

    [Torch] Fix PyTorch NMS conversion for negative scores (#7137)
    
    * Fix pytorch nms conversion for negative scores
    
    * updated mask rcnn test to verify outputs and also run cuda target
    
    * set rpn_post_nms_top_n_test to 200
    
    * fix parameter name
    
    * dump output box information
    
    * simplifying
---
 python/tvm/relay/frontend/pytorch.py               | 14 +++--
 tests/python/frontend/pytorch/test_forward.py      |  4 +-
 .../frontend/pytorch/test_object_detection.py      | 69 ++++++++++------------
 3 files changed, 42 insertions(+), 45 deletions(-)

diff --git a/python/tvm/relay/frontend/pytorch.py 
b/python/tvm/relay/frontend/pytorch.py
index c75bd2d..94ee928 100644
--- a/python/tvm/relay/frontend/pytorch.py
+++ b/python/tvm/relay/frontend/pytorch.py
@@ -1857,16 +1857,18 @@ class PyTorchOpConverter:
         scores = inputs[1]
         iou_threshold = inputs[2]
 
+        num_boxes = _op.shape_of(scores)
+
+        # TVM NMS assumes score > 0
+        scores = scores - _op.min(scores) + _op.const(1.0)
         # Generate data with shape (1, num_anchors, 5)
         scores = AttrCvt(op_name="expand_dims", extras={"axis": -1, 
"num_newaxis": 1})([scores], {})
-
-        # Prepare input data for get_valid_counts
         data = _op.concatenate([scores, boxes], -1)
         data = _op.expand_dims(data, 0, 1)
-        # Leverage get_valid_counts to sort the data and clear invalid boxes
-        ct, data, indices = get_relay_op("get_valid_counts")(
-            data, score_threshold=-1.0, id_index=-1, score_index=0
-        )
+        # PyTorch NMS doesn't have score_threshold, so no need to run 
get_valid_count
+        indices = _op.transform.arange(_op.squeeze(num_boxes), dtype="int32")
+        indices = _op.expand_dims(indices, 0, 1)
+        ct = num_boxes
 
         # Perform Non-Maximum Suppression,
         # PyTorch NMS doesn't have parameter top_k and max_output_size
diff --git a/tests/python/frontend/pytorch/test_forward.py 
b/tests/python/frontend/pytorch/test_forward.py
index 74d9c78..04f08b9 100644
--- a/tests/python/frontend/pytorch/test_forward.py
+++ b/tests/python/frontend/pytorch/test_forward.py
@@ -1675,10 +1675,10 @@ def test_forward_nms():
         boxes = torch.rand(num_boxes, box_len, dtype=torch.float) * 0.5
         boxes[:, 2] += boxes[:, 0]
         boxes[:, 3] += boxes[:, 1]
-        scores = torch.rand(num_boxes, dtype=torch.float)
+        scores = torch.from_numpy(np.random.uniform(-1, 1, 
size=(num_boxes,)).astype(np.float32))
         return boxes, scores
 
-    targets = ["llvm"]  # dynamic nms does not work on gpu
+    targets = ["llvm", "cuda"]
 
     for num_boxes, iou_thres in [(10, 0.3), (100, 0.5), (500, 0.9)]:
         in_boxes, in_scores = _gen_rand_inputs(num_boxes)
diff --git a/tests/python/frontend/pytorch/test_object_detection.py 
b/tests/python/frontend/pytorch/test_object_detection.py
index f519749..e4545ec 100644
--- a/tests/python/frontend/pytorch/test_object_detection.py
+++ b/tests/python/frontend/pytorch/test_object_detection.py
@@ -23,6 +23,7 @@ import cv2
 
 import tvm
 
+import tvm.testing
 from tvm import relay
 from tvm.runtime.vm import VirtualMachine
 from tvm.contrib.download import download
@@ -70,7 +71,7 @@ def generate_jit_model(index):
     ]
 
     model_func = model_funcs[index]
-    model = TraceWrapper(model_func(pretrained=True))
+    model = TraceWrapper(model_func(pretrained=True, 
rpn_pre_nms_top_n_test=200))
 
     model.eval()
     inp = torch.Tensor(np.random.uniform(0.0, 250.0, size=(1, 3, in_size, 
in_size)))
@@ -94,46 +95,40 @@ def test_detection_models():
     download(img_url, img)
 
     input_shape = (1, 3, in_size, in_size)
-    target = "llvm"
+
     input_name = "input0"
     shape_list = [(input_name, input_shape)]
-    score_threshold = 0.9
 
     scripted_model = generate_jit_model(1)
     mod, params = relay.frontend.from_pytorch(scripted_model, shape_list)
 
-    with tvm.transform.PassContext(opt_level=3, 
disabled_pass=["FoldScaleAxis"]):
-        vm_exec = relay.vm.compile(mod, target=target, params=params)
-
-    ctx = tvm.cpu()
-    vm = VirtualMachine(vm_exec, ctx)
     data = process_image(img)
-    pt_res = scripted_model(data)
-    data = data.detach().numpy()
-    vm.set_input("main", **{input_name: data})
-    tvm_res = vm.run()
-
-    # Note: due to accumulated numerical error, we can't directly compare 
results
-    # with pytorch output. Some boxes might have a quite tiny difference in 
score
-    # and the order can become different. We just measure how many valid boxes
-    # there are for input image.
-    pt_scores = pt_res[1].detach().numpy().tolist()
-    tvm_scores = tvm_res[1].asnumpy().tolist()
-    num_pt_valid_scores = num_tvm_valid_scores = 0
-
-    for score in pt_scores:
-        if score >= score_threshold:
-            num_pt_valid_scores += 1
-        else:
-            break
-
-    for score in tvm_scores:
-        if score >= score_threshold:
-            num_tvm_valid_scores += 1
-        else:
-            break
-
-    assert num_pt_valid_scores == num_tvm_valid_scores, (
-        "Output mismatch: Under score threshold {}, Pytorch has {} valid "
-        "boxes while TVM has {}.".format(score_threshold, num_pt_valid_scores, 
num_tvm_valid_scores)
-    )
+    data_np = data.detach().numpy()
+
+    with torch.no_grad():
+        pt_res = scripted_model(data)
+
+    for target in ["llvm", "cuda"]:
+        with tvm.transform.PassContext(opt_level=3):
+            vm_exec = relay.vm.compile(mod, target=target, params=params)
+
+        ctx = tvm.context(target, 0)
+        vm = VirtualMachine(vm_exec, ctx)
+
+        vm.set_input("main", **{input_name: data_np})
+        tvm_res = vm.run()
+
+        # Bounding boxes
+        tvm.testing.assert_allclose(
+            pt_res[0].cpu().numpy(), tvm_res[0].asnumpy(), rtol=1e-5, atol=1e-5
+        )
+        # Scores
+        tvm.testing.assert_allclose(
+            pt_res[1].cpu().numpy(), tvm_res[1].asnumpy(), rtol=1e-5, atol=1e-5
+        )
+        # Class ids
+        np.testing.assert_equal(pt_res[2].cpu().numpy(), tvm_res[2].asnumpy())
+
+        score_threshold = 0.9
+        print("Num boxes:", pt_res[0].cpu().numpy().shape[0])
+        print("Num valid boxes:", np.sum(pt_res[1].cpu().numpy() >= 
score_threshold))

Reply via email to