Skip to content

[Bug][Relax][ONNX] NonMaxSuppression crashes on 1-D single-element scalar inputs (NumPy 2.x) #19693

@wuyii8941

Description

@wuyii8941

Expected behavior

ONNX NonMaxSuppression declares max_output_boxes_per_class, iou_threshold, and score_threshold as scalar tensors. By long-standing convention (and ORT's behavior), both 0-D scalars (shape []) and 1-D single-element tensors (shape [1]) are accepted. Many ONNX exporters emit shape [1].

Actual behavior

The TVM frontend crashes during conversion:

TypeError: only 0-dimensional arrays can be converted to Python scalars

This is a NumPy 2.x stricter cast — calling int(np.array([3])) now raises TypeError, whereas NumPy 1.x silently accepted it.

Reproduction

import numpy as np
import onnx
from onnx import helper, TensorProto, numpy_helper
import onnxruntime as ort
from tvm.relax.frontend.onnx import from_onnx

boxes = helper.make_tensor_value_info("boxes", TensorProto.FLOAT, [1, 5, 4])
scores = helper.make_tensor_value_info("scores", TensorProto.FLOAT, [1, 1, 5])
Y = onnx.ValueInfoProto(); Y.name = "selected"
inits = [
    numpy_helper.from_array(np.array([3], dtype=np.int64), "max_output"),     # shape [1]
    numpy_helper.from_array(np.array([0.5], dtype=np.float32), "iou"),         # shape [1]
    numpy_helper.from_array(np.array([0.0], dtype=np.float32), "score_thr"),   # shape [1]
]
node = helper.make_node("NonMaxSuppression",
                        ["boxes", "scores", "max_output", "iou", "score_thr"],
                        ["selected"])
g = helper.make_graph([node], "g", [boxes, scores], [Y], initializer=inits)
m = helper.make_model(g, opset_imports=[helper.make_opsetid("", 18)])

boxes_v = np.array([[[0., 0., 1., 1.],
                     [0., 0.1, 1., 1.1],
                     [0., -0.1, 1., 0.9],
                     [0., 10., 1., 11.],
                     [0., 10.1, 1., 11.1]]], dtype=np.float32)
scores_v = np.array([[[0.9, 0.75, 0.6, 0.95, 0.5]]], dtype=np.float32)

print("ORT:", ort.InferenceSession(m.SerializeToString()).run(None, {"boxes": boxes_v, "scores": scores_v})[0])
# ORT: [[0 0 3] [0 0 0]]

inf = onnx.shape_inference.infer_shapes(m)
mod = from_onnx(inf)  # TypeError: only 0-dimensional arrays can be converted to Python scalars

Root cause

python/tvm/relax/frontend/onnx/onnx_frontend.py, NonMaxSuppression._impl_v10:

if max_output_boxes_per_class is not None and isinstance(max_output_boxes_per_class, relax.Constant):
    max_output_boxes_per_class = int(max_output_boxes_per_class.data.numpy())   # NumPy 2.x: TypeError on shape [1]
...
if iou_threshold is not None and isinstance(iou_threshold, relax.Constant):
    iou_threshold = float(iou_threshold.data.numpy())                            # same
...
if score_threshold is not None and isinstance(score_threshold, relax.Constant):
    score_threshold = float(score_threshold.data.numpy())                        # same

int() / float() on a NumPy ndarray no longer auto-flattens; this raises TypeError on any non-0-D tensor.

Suggested fix

Use .item() (which accepts both 0-D and 1-element tensors of any rank):

max_output_boxes_per_class = int(max_output_boxes_per_class.data.numpy().item())
iou_threshold = float(iou_threshold.data.numpy().item())
score_threshold = float(score_threshold.data.numpy().item())

The same pattern (int(constant.data.numpy()) / float(...)) appears in several other converters and should be audited; e.g., TopK had a similar problem fixed by #19573.

Impact

Any ONNX model that emits NMS thresholds as shape-[1] tensors fails to import. ONNX exporters from torchvision, MMDetection, YOLO-family object-detection pipelines commonly produce shape-[1] here.

Unrelated to (but distinct from) #19544 which addresses the max_output_boxes_per_class = 0 semantics.

Environment

  • TVM: latest main (commit b172d5e)
  • Python: 3.11
  • NumPy: 2.4.4
  • ONNX Runtime: 1.24.4

cc @KJlaccHoeUM9l @junrushao

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triagePRs or issues that need to be investigated by maintainers to find the right assignees to address ittype: bug

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions