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

jrmccluskey pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git


The following commit(s) were added to refs/heads/master by this push:
     new 320e1007729 [Experimental] Update ONNX extras (#40821)
320e1007729 is described below

commit 320e1007729c63a67c0fc59f52dd7b04f53987f1
Author: Jack McCluskey <[email protected]>
AuthorDate: Thu Oct 29 09:58:12 2026 -0400

    [Experimental] Update ONNX extras (#40821)
    
    * Update ONNX extras
    
    * Trigger postcommits
    
    * Fix py311-ml onnx bounds
    
    * add onnxscript to tox environment
    
    * Add onnxscript to ml_base
---
 .../beam_PostCommit_Python_Dependency.json         |  2 +-
 .../ml/inference/onnx_inference_test.py            | 61 ++++++++++++++--------
 sdks/python/setup.py                               | 20 +++----
 sdks/python/test-suites/tox/py311/build.gradle     | 14 +++--
 sdks/python/tox.ini                                | 35 ++++++++-----
 5 files changed, 82 insertions(+), 50 deletions(-)

diff --git a/.github/trigger_files/beam_PostCommit_Python_Dependency.json 
b/.github/trigger_files/beam_PostCommit_Python_Dependency.json
index a5413bd5db9..8f085d87884 100644
--- a/.github/trigger_files/beam_PostCommit_Python_Dependency.json
+++ b/.github/trigger_files/beam_PostCommit_Python_Dependency.json
@@ -1,4 +1,4 @@
 {
     "comment": "Modify this file in a trivial way to cause this test suite to 
run",
-    "modification": 10
+    "modification": 11
   }
diff --git a/sdks/python/apache_beam/ml/inference/onnx_inference_test.py 
b/sdks/python/apache_beam/ml/inference/onnx_inference_test.py
index c301f98169f..e573a516345 100644
--- a/sdks/python/apache_beam/ml/inference/onnx_inference_test.py
+++ b/sdks/python/apache_beam/ml/inference/onnx_inference_test.py
@@ -31,10 +31,6 @@ from apache_beam.testing.test_pipeline import TestPipeline
 from apache_beam.testing.util import assert_that
 from apache_beam.testing.util import equal_to
 
-if bool(1):  # lint doesn't like an unconditional `raise`.
-  raise unittest.SkipTest(
-      'TODO: fix https://github.com/apache/beam/issues/31254')
-
 # Protect against environments where onnx and pytorch library is not available.
 # pylint: disable=wrong-import-order, wrong-import-position, ungrouped-imports
 try:
@@ -77,6 +73,28 @@ class PytorchLinearRegression(torch.nn.Module):
     return out
 
 
+def _build_tf_linear_model(params):
+  # Keras 3 (tensorflow>=2.16) no longer accepts a `weights` argument when
+  # constructing layers, so build the model and then set the weights.
+  input_dim = params[0].shape[0]
+  linear_layer = layers.Dense(units=1)
+  linear_model = tf.keras.Sequential(
+      [tf.keras.Input(shape=(input_dim, )), linear_layer])
+  linear_layer.set_weights(params)
+  return linear_model
+
+
+def _export_tf_model_to_onnx(model, input_signature, path):
+  # tf2onnx.convert.from_keras does not support Keras 3 models
+  # (tensorflow>=2.16), so convert a tf.function wrapping the model instead.
+  @tf.function(input_signature=input_signature)
+  def model_fn(x):
+    return model(x)
+
+  tf2onnx.convert.from_function(
+      model_fn, input_signature=input_signature, opset=13, output_path=path)
+
+
 class TestDataAndModel():
   def get_one_feature_samples(self):
     return [
@@ -121,9 +139,7 @@ class TestDataAndModel():
     params = [
         np.array([[2.0]], dtype="float32"), np.array([0.5], dtype="float32")
     ]
-    linear_layer = layers.Dense(units=1, weights=params)
-    linear_model = tf.keras.Sequential([linear_layer])
-    return linear_model
+    return _build_tf_linear_model(params)
 
   def get_sklearn_one_feature_model(self):
     x = [[0], [1]]
@@ -140,10 +156,11 @@ class TestDataAndModel():
     return model
 
   def get_tf_two_feature_model(self):
-    params = [np.array([[2.0], [3]]), np.array([0.5], dtype="float32")]
-    linear_layer = layers.Dense(units=1, weights=params)
-    linear_model = tf.keras.Sequential([linear_layer])
-    return linear_model
+    params = [
+        np.array([[2.0], [3]], dtype="float32"),
+        np.array([0.5], dtype="float32")
+    ]
+    return _build_tf_linear_model(params)
 
   def get_sklearn_two_feature_model(self):
     x = [[1, 5], [3, 2], [1, 0]]
@@ -211,7 +228,7 @@ class OnnxPytorchRunInferenceTest(OnnxTestBase):
         dummy_input,  # model input
         path,  # where to save the model
         export_params=True,  # store the trained parameter weights
-        opset_version=10,  # the ONNX version
+        opset_version=17,  # the ONNX version
         do_constant_folding=True,  # whether to execute constant-
         # folding for optimization
         input_names=['input'],  # model's input names
@@ -266,10 +283,7 @@ class OnnxTensorflowRunInferenceTest(OnnxTestBase):
 
     path = os.path.join(self.tmpdir, 'my_onnx_tf_path')
     spec = (tf.TensorSpec((None, 1), tf.float32, name="input"), )
-    _, _ = tf2onnx.convert.from_keras(linear_model,
-    input_signature=spec,
-    opset=13,
-    output_path=path)
+    _export_tf_model_to_onnx(linear_model, spec, path)
 
     inference_runner = TestOnnxModelHandler(path)
     inference_session = ort.InferenceSession(
@@ -316,7 +330,7 @@ class OnnxPytorchRunInferencePipelineTest(OnnxTestBase):
         dummy_input,  # model input
         path,  # where to save the model
         export_params=True,  # store the trained parameter weights
-        opset_version=10,  # the ONNX version
+        opset_version=17,  # the ONNX version
         do_constant_folding=True,  # whether to execute constant
         # folding for optimization
         input_names=['input'],  # odel's input names
@@ -352,6 +366,7 @@ class OnnxPytorchRunInferencePipelineTest(OnnxTestBase):
       self.exportModelToOnnx(model, path)
       model_handler = OnnxModelHandlerNumpy(
           model_uri=path, env_vars={'FOO': 'bar'})
+      os.environ.pop('FOO', None)
       self.assertFalse('FOO' in os.environ)
       _ = (
           pipeline
@@ -360,8 +375,12 @@ class OnnxPytorchRunInferencePipelineTest(OnnxTestBase):
           | RunInference(model_handler))
       pipeline.run()
       self.assertTrue('FOO' in os.environ)
-      self.assertTrue('bar'.equals(os.environ['FOO']))
+      self.assertEqual(os.environ['FOO'], 'bar')
 
+  @unittest.skip(
+      'large_model=True does not work with the default ONNX inference fn: '
+      'InferenceSession.get_inputs() returns onnxruntime NodeArg objects, '
+      'which cannot be pickled back through the multi_process_shared proxy.')
   def test_model_handler_large_model(self):
     with TestPipeline() as pipeline:
 
@@ -384,6 +403,7 @@ class OnnxPytorchRunInferencePipelineTest(OnnxTestBase):
           env_vars={'FOO': 'bar'},
           inference_fn=onxx_numpy_inference_fn,
           large_model=True)
+      os.environ.pop('FOO', None)
       self.assertFalse('FOO' in os.environ)
       _ = (
           pipeline
@@ -427,8 +447,7 @@ class OnnxPytorchRunInferencePipelineTest(OnnxTestBase):
 class OnnxTensorflowRunInferencePipelineTest(OnnxTestBase):
   def exportModelToOnnx(self, model, path):
     spec = (tf.TensorSpec((None, 2), tf.float32, name="input"), )
-    _, _ = tf2onnx.convert.from_keras(model,
-    input_signature=spec, opset=13, output_path=path)
+    _export_tf_model_to_onnx(model, spec, path)
 
   def test_pipeline_local_model_simple(self):
     with TestPipeline() as pipeline:
@@ -518,7 +537,7 @@ class OnnxSklearnRunInferencePipelineTest(OnnxTestBase):
           equal_to(expected_predictions, equals_fn=_compare_prediction_result))
 
   def test_invalid_input_type(self):
-    with self.assertRaisesRegex(Exception, "InvalidArgument"):
+    with self.assertRaisesRegex(Exception, "Got invalid dimensions for input"):
       with TestPipeline() as pipeline:
         examples = [np.array([1], dtype="float32")]
         path = os.path.join(self.tmpdir, 'my_onnx_sklearn_path')
diff --git a/sdks/python/setup.py b/sdks/python/setup.py
index 98d5b631f13..ed39cd76ba7 100644
--- a/sdks/python/setup.py
+++ b/sdks/python/setup.py
@@ -183,10 +183,8 @@ xgboost_dependency = [
 ml_base_core = [
     'embeddings>=0.0.4',  # 0.0.3 crashes setuptools
     'onnxruntime',
-    # onnx 1.12–1.13 cap protobuf in ways that trigger huge backtracking with
-    # Beam[gcp]+ml_test; pip can fall back to onnx 1.11 sdist which needs 
cmake.
-    # 1.14.1+ matches tf2onnx>=1.16 and ships manylinux wheels for py3.10.
     'onnx>=1.14.1,<2',
+    'onnxscript>=0.5.0,<1',
     'langchain',
     'sentence-transformers>=2.2.2',
     'skl2onnx',
@@ -703,12 +701,16 @@ if __name__ == '__main__':
           ],
           'tfrecord': ['crcmod>=1.7,<2.0'],
           'onnx': [
-              'onnxruntime==1.13.1',
-              'torch==1.13.1',
-              'tensorflow==2.11.0',
-              'tf2onnx==1.17.0',
-              'skl2onnx==1.13',
-              'transformers==4.25.1',  # tensorflow transient dep
+              # Keep in line with the py311-onnx-* environments in tox.ini.
+              'onnx>=1.16.0,<2',
+              'onnxruntime>=1.19.0,<2',
+              # Converters for producing ONNX models from scikit-learn,
+              # TensorFlow and PyTorch models.
+              'skl2onnx>=1.17.0,<2',
+              'tf2onnx>=1.17.0,<1.18',
+              'tensorflow>=2.18.0,<2.22',
+              'torch>=2.3.0,<2.12.0',
+              'onnxscript>=0.5.0,<1',
               'absl-py>=0.12.0'
           ],
           'xgboost': xgboost_dependency,
diff --git a/sdks/python/test-suites/tox/py311/build.gradle 
b/sdks/python/test-suites/tox/py311/build.gradle
index 580b6fd7fba..27f7904896d 100644
--- a/sdks/python/test-suites/tox/py311/build.gradle
+++ b/sdks/python/test-suites/tox/py311/build.gradle
@@ -111,11 +111,15 @@ toxTask "testPy311tft-121", "py311-tft-121", "${posargs}"
 test.dependsOn "testPy311tft-121"
 postCommitPyDep.dependsOn "testPy311tft-121"
 
-// TODO(https://github.com/apache/beam/issues/25796) - uncomment onnx tox task 
once onnx supports protobuf 4.x.x
-// Create a test task for each minor version of onnx
-// toxTask "testPy311onnx-113", "py311-onnx-113", "${posargs}"
-// test.dependsOn "testPy311onnx-113"
-// postCommitPyDep.dependsOn "testPy311onnx-113"
+// Create a test task for the lower and upper bounds of supported onnx.
+// Keep in sync with [testenv:py311-onnx-...] in tox.ini.
+toxTask "testPy311onnx-119", "py311-onnx-119", "${posargs}"
+test.dependsOn "testPy311onnx-119"
+postCommitPyDep.dependsOn "testPy311onnx-119"
+
+toxTask "testPy311onnx-latest", "py311-onnx-latest", "${posargs}"
+test.dependsOn "testPy311onnx-latest"
+postCommitPyDep.dependsOn "testPy311onnx-latest"
 
 // Create a test task for the lower and upper bounds of supported tensorflow.
 // Keep in sync with [testenv:py311-tensorflow-...] in tox.ini.
diff --git a/sdks/python/tox.ini b/sdks/python/tox.ini
index 80897289733..ac583f3b6c7 100644
--- a/sdks/python/tox.ini
+++ b/sdks/python/tox.ini
@@ -111,7 +111,8 @@ commands =
 deps =
   pip==25.0.1
   accelerate>=1.6.0
-  onnx<1.19.0
+  onnx<2.0.0
+  onnxscript>=0.5.0,<1
 setenv =
 extras = test,gcp,dataframe,p311_ml_test
 commands =
@@ -420,25 +421,31 @@ commands =
   # Allow exit code 5 (no tests run) so that we can run this command safely on 
arbitrary subdirectories.
   /bin/sh -c 'pytest -o junit_suite_name={envname} 
--junitxml=pytest_{envname}.xml -n 6 -m uses_pytorch {posargs}; ret=$?; [ $ret 
= 5 ] && exit 0 || exit $ret'
 
-# TODO(https://github.com/apache/beam/issues/25796) - uncomment onnx tox task 
in tox/py311/build.gradle once onnx supports protobuf 4.x.x
-[testenv:py311-onnx-113]
-# TODO(https://github.com/apache/beam/issues/25443)
-# apparently tox has problem when substitution key has single value. Change 
back to -onnx-{113,...}
-# when multiple onnx versions are tested.
+[testenv:py311-onnx-{119,latest}]
+# Keep in line with the onnx extra in setup.py. tensorflow is pinned to 2.21 in
+# both environments because tensorflow<2.20 cannot be installed alongside the
+# gcp extra (protobuf<6 vs protobuf>=6.33.5).
 deps =
-  onnxruntime==1.13.1
-  pandas==1.5.2
-  torch==1.13.1
-  tensorflow==2.11.0
-  tf2onnx==1.13.0
-  skl2onnx==1.13
-  transformers==4.25.1
+  # Lower bound of the supported range.
+  119: onnxruntime>=1.19.0,<1.20
+  119: onnx>=1.16.0,<1.17
+  119: skl2onnx>=1.17.0,<1.18
+  119: torch>=2.3.0,<2.4.0
+  latest: onnxruntime>=1.31.0,<2
+  latest: onnx>=1.23.0,<2
+  latest: skl2onnx>=1.20.0,<2
+  latest: torch>=2.11.0,<2.12.0
+  tf2onnx>=1.17.0,<1.18
+  tensorflow>=2.21.0,<2.22
+  # torch>=2.9 exports to ONNX through torch.export, which requires onnxscript.
+  onnxscript>=0.5.0,<1
 extras = test,gcp
 commands =
   # Log onnx version for debugging
   /bin/sh -c "pip freeze | grep -E onnx"
   # Run all ONNX unit tests
-  pytest -o junit_suite_name={envname} --junitxml=pytest_{envname}.xml -n 6 -m 
uses_onnx {posargs}
+  # Allow exit code 5 (no tests run) so that we can run this command safely on 
arbitrary subdirectories.
+  /bin/sh -c 'pytest -o junit_suite_name={envname} 
--junitxml=pytest_{envname}.xml -n 6 -m uses_onnx {posargs}; ret=$?; [ $ret = 5 
] && exit 0 || exit $ret'
 
 [testenv:py311-tensorflow-{216,221}]
 # Keep in line with the tensorflow extra in setup.py.

Reply via email to