From 6f718f5552de2470b18b4582a7954e3be09a6b5d Mon Sep 17 00:00:00 2001 From: NiccoloN Date: Thu, 9 Apr 2026 14:18:41 +0200 Subject: [PATCH] add validation tests for softmax, resize, split, gather --- validation/operations/README.md | 30 +++ .../gather_axis0_matrix_indices.onnx | Bin 0 -> 175 bytes .../operations/gather/axis1/gather_axis1.onnx | Bin 0 -> 137 bytes validation/operations/gen_tests.py | 203 +++++++++++++----- .../resize/nearest_2x/resize_nearest_2x.onnx | Bin 0 -> 257 bytes .../non_uniform/resize_non_uniform.onnx | Bin 0 -> 266 bytes .../resize/with_sizes/resize_with_sizes.onnx | Bin 0 -> 273 bytes .../3d_last_axis/softmax_3d_last_axis.onnx | Bin 0 -> 112 bytes .../softmax/basic/softmax_basic.onnx | Bin 0 -> 97 bytes .../channel_axis/softmax_channel_axis.onnx | Bin 0 -> 120 bytes .../operations/split/basic/split_basic.onnx | Bin 0 -> 160 bytes .../split_equal_three_way.onnx | Bin 0 -> 158 bytes 12 files changed, 183 insertions(+), 50 deletions(-) create mode 100644 validation/operations/gather/axis0_matrix_indices/gather_axis0_matrix_indices.onnx create mode 100644 validation/operations/gather/axis1/gather_axis1.onnx create mode 100644 validation/operations/resize/nearest_2x/resize_nearest_2x.onnx create mode 100644 validation/operations/resize/non_uniform/resize_non_uniform.onnx create mode 100644 validation/operations/resize/with_sizes/resize_with_sizes.onnx create mode 100644 validation/operations/softmax/3d_last_axis/softmax_3d_last_axis.onnx create mode 100644 validation/operations/softmax/basic/softmax_basic.onnx create mode 100644 validation/operations/softmax/channel_axis/softmax_channel_axis.onnx create mode 100644 validation/operations/split/basic/split_basic.onnx create mode 100644 validation/operations/split/equal_three_way/split_equal_three_way.onnx diff --git a/validation/operations/README.md b/validation/operations/README.md index 4870159..3b1d979 100644 --- a/validation/operations/README.md +++ b/validation/operations/README.md @@ -85,6 +85,36 @@ python3 validation/operations/gen_tests.py | 4D | `sigmoid/4d` | [2,3,4,4] | [2,3,4,4] | Standalone NCHW Sigmoid | | After Gemm | `sigmoid/after_gemm` | [4,64] | [4,32] | Gemm + bias, then Sigmoid | +## Softmax + +| Test | Directory | Input | Output | Axis | Notes | +|--------------|--------------------------|-------------|-------------|------|---------------------------------| +| Basic | `softmax/basic` | [3,5] | [3,5] | 1 | Row-wise softmax over features | +| 3D last axis | `softmax/3d_last_axis` | [2,3,4] | [2,3,4] | 2 | Last-dimension normalization | +| Channel axis | `softmax/channel_axis` | [1,3,2,2] | [1,3,2,2] | 1 | NCHW channel-wise softmax | + +## Resize + +| Test | Directory | Input | Output | Mode | Notes | +|---------------------|-------------------------|-----------|-----------|---------|-----------------------------------------| +| Nearest 2x | `resize/nearest_2x` | [1,1,2,3] | [1,1,4,6] | nearest | NCHW upsampling with scales [1,1,2,2] | +| Non-uniform scales | `resize/non_uniform` | [1,1,2,3] | [1,1,6,6] | nearest | Different height/width scaling factors | +| Explicit sizes | `resize/with_sizes` | [1,1,2,3] | [1,1,3,5] | nearest | Sizes input used instead of scales | + +## Split + +| Test | Directory | Input | Outputs | Axis | Notes | +|-----------------|---------------------------|-------|----------------------|------|-------------------------------------| +| Basic | `split/basic` | [2,6] | [2,2], [2,4] | 1 | Two-way split with explicit sizes | +| Equal three-way | `split/equal_three_way` | [2,6] | [2,2], [2,2], [2,2] | 1 | Optional split input omitted | + +## Gather + +| Test | Directory | Input | Indices | Output | Axis | Notes | +|----------------------|--------------------------------|-------|---------|----------|------|--------------------------------| +| Axis 1 | `gather/axis1` | [3,4] | [2] | [3,2] | 1 | Select two columns | +| Axis 0 matrix indices| `gather/axis0_matrix_indices` | [4,3] | [2,2] | [2,2,3] | 0 | Gather rows with 2D indices | + ## Add | Test | Directory | Input(s) | Output | Notes | diff --git a/validation/operations/gather/axis0_matrix_indices/gather_axis0_matrix_indices.onnx b/validation/operations/gather/axis0_matrix_indices/gather_axis0_matrix_indices.onnx new file mode 100644 index 0000000000000000000000000000000000000000..4119bada8b5155cf7be20206f4cc929bc4527922 GIT binary patch literal 175 zcmd=;$RYBcY>MXrN96MOi-E`N;5*~C}E(jLVR329E?I7TudA+ eK+K#Z&czrhB*Y~E6yyO4FhMc16AKrE051UhzZ{+b literal 0 HcmV?d00001 diff --git a/validation/operations/gather/axis1/gather_axis1.onnx b/validation/operations/gather/axis1/gather_axis1.onnx new file mode 100644 index 0000000000000000000000000000000000000000..c0d6ed43668f3cfb720b0d013f98b43ad9044a44 GIT binary patch literal 137 zcmdr7K{gp z7;4FLFbS|b!8CaZFhBtll#UVx8Y;xc#lyiU#KFbH!3@MKNkG9!G(jdO7A^(>UI3Wd B72^N^ literal 0 HcmV?d00001 diff --git a/validation/operations/gen_tests.py b/validation/operations/gen_tests.py index c146f8d..7e0309c 100644 --- a/validation/operations/gen_tests.py +++ b/validation/operations/gen_tests.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Generate ONNX test models for validating GEMM, Conv, Pooling, Relu, and ReduceMean implementations.""" +"""Generate ONNX test models for validating supported ONNX operations.""" import numpy as np import onnx @@ -473,6 +473,140 @@ def sigmoid_after_gemm(): save_model(model, "sigmoid/after_gemm", "sigmoid_after_gemm.onnx") +# --------------------------------------------------------------------------- +# Softmax tests +# --------------------------------------------------------------------------- + +def softmax_basic(): + """Softmax over the last dimension of a 2D tensor.""" + X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [3, 5]) + Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [3, 5]) + node = helper.make_node("Softmax", ["X"], ["Y"], axis=1) + graph = helper.make_graph([node], "softmax_basic", [X], [Y]) + model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)]) + save_model(model, "softmax/basic", "softmax_basic.onnx") + + +def softmax_3d_last_axis(): + """Softmax over the last axis of a 3D tensor.""" + X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [2, 3, 4]) + Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [2, 3, 4]) + node = helper.make_node("Softmax", ["X"], ["Y"], axis=2) + graph = helper.make_graph([node], "softmax_3d_last_axis", [X], [Y]) + model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)]) + save_model(model, "softmax/3d_last_axis", "softmax_3d_last_axis.onnx") + + +def softmax_channel_axis(): + """Softmax over the channel axis of an NCHW tensor.""" + X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [1, 3, 2, 2]) + Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [1, 3, 2, 2]) + node = helper.make_node("Softmax", ["X"], ["Y"], axis=1) + graph = helper.make_graph([node], "softmax_channel_axis", [X], [Y]) + model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)]) + save_model(model, "softmax/channel_axis", "softmax_channel_axis.onnx") + + +# --------------------------------------------------------------------------- +# Resize tests +# --------------------------------------------------------------------------- + +def resize_nearest_2x(): + """Resize an NCHW tensor with nearest-neighbor upsampling by a factor of 2.""" + X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [1, 1, 2, 3]) + Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [1, 1, 4, 6]) + roi = numpy_helper.from_array(np.asarray([], dtype=np.float32), name="roi") + scales = numpy_helper.from_array(np.asarray([1.0, 1.0, 2.0, 2.0], dtype=np.float32), name="scales") + node = helper.make_node( + "Resize", ["X", "roi", "scales"], ["Y"], + mode="nearest", coordinate_transformation_mode="asymmetric", nearest_mode="floor") + graph = helper.make_graph([node], "resize_nearest_2x", [X], [Y], initializer=[roi, scales]) + model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)]) + save_model(model, "resize/nearest_2x", "resize_nearest_2x.onnx") + + +def resize_nearest_non_uniform(): + """Resize an NCHW tensor with non-uniform nearest-neighbor scales.""" + X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [1, 1, 2, 3]) + Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [1, 1, 6, 6]) + roi = numpy_helper.from_array(np.asarray([], dtype=np.float32), name="roi") + scales = numpy_helper.from_array(np.asarray([1.0, 1.0, 3.0, 2.0], dtype=np.float32), name="scales") + node = helper.make_node( + "Resize", ["X", "roi", "scales"], ["Y"], + mode="nearest", coordinate_transformation_mode="asymmetric", nearest_mode="floor") + graph = helper.make_graph([node], "resize_nearest_non_uniform", [X], [Y], initializer=[roi, scales]) + model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)]) + save_model(model, "resize/non_uniform", "resize_non_uniform.onnx") + + +def resize_with_sizes(): + """Resize an NCHW tensor to explicit output sizes.""" + X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [1, 1, 2, 3]) + Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [1, 1, 3, 5]) + roi = numpy_helper.from_array(np.asarray([], dtype=np.float32), name="roi") + sizes = make_int64_initializer("sizes", [1, 1, 3, 5]) + node = helper.make_node( + "Resize", ["X", "roi", "", "sizes"], ["Y"], + mode="nearest", coordinate_transformation_mode="asymmetric", nearest_mode="floor") + graph = helper.make_graph([node], "resize_with_sizes", [X], [Y], initializer=[roi, sizes]) + model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)]) + save_model(model, "resize/with_sizes", "resize_with_sizes.onnx") + + +# --------------------------------------------------------------------------- +# Split tests +# --------------------------------------------------------------------------- + +def split_basic(): + """Split a 2D tensor into two outputs along the feature axis.""" + X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [2, 6]) + Y0 = helper.make_tensor_value_info("Y0", TensorProto.FLOAT, [2, 2]) + Y1 = helper.make_tensor_value_info("Y1", TensorProto.FLOAT, [2, 4]) + split = make_int64_initializer("split", [2, 4]) + node = helper.make_node("Split", ["X", "split"], ["Y0", "Y1"], axis=1) + graph = helper.make_graph([node], "split_basic", [X], [Y0, Y1], initializer=[split]) + model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)]) + save_model(model, "split/basic", "split_basic.onnx") + + +def split_equal_three_way(): + """Split a 2D tensor evenly into three outputs.""" + X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [2, 6]) + Y0 = helper.make_tensor_value_info("Y0", TensorProto.FLOAT, [2, 2]) + Y1 = helper.make_tensor_value_info("Y1", TensorProto.FLOAT, [2, 2]) + Y2 = helper.make_tensor_value_info("Y2", TensorProto.FLOAT, [2, 2]) + node = helper.make_node("Split", ["X"], ["Y0", "Y1", "Y2"], axis=1) + graph = helper.make_graph([node], "split_equal_three_way", [X], [Y0, Y1, Y2]) + model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)]) + save_model(model, "split/equal_three_way", "split_equal_three_way.onnx") + + +# --------------------------------------------------------------------------- +# Gather tests +# --------------------------------------------------------------------------- + +def gather_axis1(): + """Gather selected columns from a 2D tensor.""" + X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [3, 4]) + Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [3, 2]) + indices = make_int64_initializer("indices", [0, 2]) + node = helper.make_node("Gather", ["X", "indices"], ["Y"], axis=1) + graph = helper.make_graph([node], "gather_axis1", [X], [Y], initializer=[indices]) + model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)]) + save_model(model, "gather/axis1", "gather_axis1.onnx") + + +def gather_axis0_matrix_indices(): + """Gather rows using a 2D indices tensor.""" + X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [4, 3]) + Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [2, 2, 3]) + indices = make_int64_initializer("indices", [[0, 2], [3, 1]]) + node = helper.make_node("Gather", ["X", "indices"], ["Y"], axis=0) + graph = helper.make_graph([node], "gather_axis0_matrix_indices", [X], [Y], initializer=[indices]) + model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)]) + save_model(model, "gather/axis0_matrix_indices", "gather_axis0_matrix_indices.onnx") + + # --------------------------------------------------------------------------- # Add tests # --------------------------------------------------------------------------- @@ -599,55 +733,6 @@ def div_after_gemm(): save_model(model, "div/after_gemm", "div_after_gemm.onnx") -# --------------------------------------------------------------------------- -# ReduceMean tests -# --------------------------------------------------------------------------- - -def reducemean_basic(): - """ReduceMean over the feature dimension, preserving rank.""" - X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [4, 8]) - Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [4, 1]) - node = helper.make_node("ReduceMean", ["X"], ["Y"], axes=[1], keepdims=1) - graph = helper.make_graph([node], "reducemean_basic", [X], [Y]) - model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)]) - save_model(model, "reduce_mean/basic", "reduce_mean_basic.onnx") - - -def reducemean_keepdims_0(): - """ReduceMean over the feature dimension, dropping the reduced axis.""" - X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [4, 8]) - Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [4]) - node = helper.make_node("ReduceMean", ["X"], ["Y"], axes=[1], keepdims=0) - graph = helper.make_graph([node], "reducemean_keepdims_0", [X], [Y]) - model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)]) - save_model(model, "reduce_mean/keepdims_0", "reduce_mean_keepdims_0.onnx") - - -def reducemean_4d_spatial(): - """ReduceMean over H and W on an NCHW tensor.""" - X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [1, 3, 4, 4]) - Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [1, 3, 1, 1]) - node = helper.make_node("ReduceMean", ["X"], ["Y"], axes=[2, 3], keepdims=1) - graph = helper.make_graph([node], "reducemean_4d_spatial", [X], [Y]) - model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)]) - save_model(model, "reduce_mean/4d_spatial", "reduce_mean_4d_spatial.onnx") - - -def reducemean_after_conv(): - """Conv followed by ReduceMean over the spatial dimensions.""" - rng = np.random.default_rng(62) - X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [1, 3, 5, 5]) - Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [1, 2, 1, 1]) - W = numpy_helper.from_array(rng.uniform(-1, 1, (2, 3, 3, 3)).astype(np.float32), name="W") - B = numpy_helper.from_array(rng.uniform(-1, 1, (2,)).astype(np.float32), name="B") - conv = helper.make_node("Conv", ["X", "W", "B"], ["C"], - kernel_shape=[3, 3], strides=[1, 1], pads=[0, 0, 0, 0]) - reduce = helper.make_node("ReduceMean", ["C"], ["Y"], axes=[2, 3], keepdims=1) - graph = helper.make_graph([conv, reduce], "reducemean_after_conv", [X], [Y], initializer=[W, B]) - model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)]) - save_model(model, "reduce_mean/after_conv", "reduce_mean_after_conv.onnx") - - # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- @@ -699,6 +784,24 @@ if __name__ == "__main__": sigmoid_4d() sigmoid_after_gemm() + print("\nGenerating Split tests:") + split_basic() + split_equal_three_way() + + print("\nGenerating Softmax tests:") + softmax_basic() + softmax_3d_last_axis() + softmax_channel_axis() + + print("\nGenerating Resize tests:") + resize_nearest_2x() + resize_nearest_non_uniform() + resize_with_sizes() + + print("\nGenerating Gather tests:") + gather_axis1() + gather_axis0_matrix_indices() + print("\nGenerating Add tests:") add_basic() add_broadcast_row() diff --git a/validation/operations/resize/nearest_2x/resize_nearest_2x.onnx b/validation/operations/resize/nearest_2x/resize_nearest_2x.onnx new file mode 100644 index 0000000000000000000000000000000000000000..b4b8f5f94d07928212c1652fc5baa154ed1a7abf GIT binary patch literal 257 zcmZvWKMTSz5XCRH)^IA+AR-73j$H&t7sb)h$-0!-MhMshk__tPNAXiSdurVr9>=}k z{dv5AKMth`|PEZHfqZz`MPw6xY$+Q^`a;H2><>uMRaHAQVJHACsI zb*+Na<%30;!1!Z#jFL|I@b<>oYr5}B`*fz8M8tqb=uPp%i%Zt!kh0A?&la*NkEv;L pZBu~z>MK|S!1^$u>|%sN&P0kW&Kk9-Mn{7A9~cu%HZelle*qPjJAnWI literal 0 HcmV?d00001 diff --git a/validation/operations/resize/non_uniform/resize_non_uniform.onnx b/validation/operations/resize/non_uniform/resize_non_uniform.onnx new file mode 100644 index 0000000000000000000000000000000000000000..9003cee58e57d85aca252e8e4f4408de3d82802e GIT binary patch literal 266 zcmZvXu?oU45QY<5Yd9535D^83j$PbcERG$WtV@YCLck`F#KBHJif^h{TQ`TF#B|>tmXmbN tLQMJt^O%t5u2Yr~LbB5epWt(tG0I~YFxqQ?F*qYE{=f)fv=4#A(+{tbKh*#L literal 0 HcmV?d00001 diff --git a/validation/operations/resize/with_sizes/resize_with_sizes.onnx b/validation/operations/resize/with_sizes/resize_with_sizes.onnx new file mode 100644 index 0000000000000000000000000000000000000000..227b586bdc0e4236cdc9105bbf5ef4d55c4689e8 GIT binary patch literal 273 zcmZusJqyAx5KWp^!>Le%h#-hK7R24LV@D_J5~58pU>isRqVE0Jp=#kIcSh8<)TR6%x%kQ)&8QB~3z zb@^bDFbLc9u$IC|>zdpG#y#s=qVncvdWB7WiC@F-%3XQ8JQ-0Gx>fF*6XeBmpHNu}L_wa4`t*0sv7H6CwZr literal 0 HcmV?d00001 diff --git a/validation/operations/softmax/basic/softmax_basic.onnx b/validation/operations/softmax/basic/softmax_basic.onnx new file mode 100644 index 0000000000000000000000000000000000000000..689313b6c6c4e1a9ca2fdf54ed229fa9161e5054 GIT binary patch literal 97 zcmd8w;bM#sVvJN`56(|3$xW=#;^tyWtjH{uU|hh+B*a?`5sgnuEY3`h5(X*e dx>gF*6V|fp8K~G7^`h6AKrE051Ug$`hUd literal 0 HcmV?d00001 diff --git a/validation/operations/split/basic/split_basic.onnx b/validation/operations/split/basic/split_basic.onnx new file mode 100644 index 0000000000000000000000000000000000000000..22ddd19ee6fb6e889e49669af4f8e9225a3e65a5 GIT binary patch literal 160 zcmd{Hy2A{MP{)C;{rw|A#Sje_@u<* z%w#QD4kiJ1Cx|XD0VW0rV1d$6!ay^H__%mD7=<{vm^he#m@P?!3uHX1C=*!J5KWZD KiG_-T3DKx z6JL^1l$sh}o>&