From 2944c22b73337dc05a431cc383e281820e6dce19 Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Thu, 30 Oct 2025 22:21:40 -0400 Subject: [PATCH 01/26] support for celu, one hot, avg pooling --- keras/src/backend/openvino/nn.py | 50 ++++++++++++++++++++++++++++---- keras/src/ops/nn_test.py | 6 ++-- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index 2c025825ed82..9ac09a5aa5fa 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -2,6 +2,7 @@ from openvino import Type from keras.src import backend +from keras.src.backend.openvino.core import OPENVINO_DTYPES from keras.src.backend.openvino.core import OpenVINOKerasTensor from keras.src.backend.openvino.core import get_ov_output @@ -16,6 +17,21 @@ def relu6(x): return OpenVINOKerasTensor(ov_opset.clamp(x, 0.0, 6.0).output(0)) +def celu(x, alpha=1.0): + x = get_ov_output(x) + const_zero = get_ov_output(0.0, x.get_element_type()) + const_alpha = get_ov_output(alpha, x.get_element_type()) + const_one = get_ov_output(1.0, x.get_element_type()) + foo = ov_opset.exp(ov_opset.divide(x, const_alpha)).output(0) + foobar = ov_opset.multiply(const_alpha, ov_opset.subtract(foo, const_one)) + + celu_x = ov_opset.add( + ov_opset.maximum(x, const_zero).output(0), + ov_opset.minimum(foobar, const_zero).output(0), + ) + return OpenVINOKerasTensor(celu_x.output(0)) + + def sigmoid(x): x = get_ov_output(x) return OpenVINOKerasTensor(ov_opset.sigmoid(x).output(0)) @@ -140,9 +156,27 @@ def average_pool( padding="valid", data_format=None, ): - raise NotImplementedError( - "`average_pool` is not supported with openvino backend" - ) + data_format = backend.standardize_data_format(data_format) + inputs = get_ov_output(inputs) + + num_spatial_dims = inputs.get_partial_shape().rank.get_length() - 2 + if isinstance(pool_size, int): + pool_size = [pool_size] * num_spatial_dims + + strides = _adjust_strides_dilation(strides, num_spatial_dims) + pad_mode, pads_begin, pads_end = _adjust_padding(padding) + inputs = _adjust_input(inputs, num_spatial_dims, data_format) + avg_pooled = ov_opset.avg_pool( + inputs, + kernel_shape=pool_size, + strides=strides, + auto_pad=pad_mode, + exclude_pad=True, + pads_begin=pads_begin, + pads_end=pads_end, + ).output(0) + avg_pooled = _adjust_outputs(avg_pooled, num_spatial_dims, data_format) + return OpenVINOKerasTensor(avg_pooled) def _adjust_strides_dilation( @@ -374,9 +408,13 @@ def conv_transpose( def one_hot(x, num_classes, axis=-1, dtype=None, sparse=False): - raise NotImplementedError( - "`one_hot` is not supported with openvino backend" - ) + one_hot_encoded = ov_opset.one_hot( + x, depth=num_classes, axis=axis, on_value=1, off_value=0 + ).output(0) + if dtype is not None: + dtype = OPENVINO_DTYPES[dtype] + one_hot_encoded = ov_opset.convert(one_hot_encoded, dtype).output(0) + return OpenVINOKerasTensor(one_hot_encoded) def multi_hot(x, num_classes, axis=-1, dtype=None, sparse=False): diff --git a/keras/src/ops/nn_test.py b/keras/src/ops/nn_test.py index f4718c495337..969683d88ecc 100644 --- a/keras/src/ops/nn_test.py +++ b/keras/src/ops/nn_test.py @@ -2439,9 +2439,11 @@ def test_dot_product_attention( mask = mask[None, None, ...] mask = np.tile(mask, (2, 4, 1, 1)) if bias is not None: - if backend.backend() == "torch": + backend_ = backend.backend() + if backend_ in ("torch", "openvino"): self.skipTest( - "torch does not support `bias` with `dot_product_attention`" + f"{backend_} does not support `bias` " + f"with `dot_product_attention`" ) bias = np.arange(math.prod(bias_shape), dtype=float).reshape( bias_shape From 2b09bc3bef610b4b83c804d2e92f810dbe64237e Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Thu, 30 Oct 2025 22:35:30 -0400 Subject: [PATCH 02/26] ctc loss --- keras/src/backend/openvino/nn.py | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index 9ac09a5aa5fa..35bc42466cfb 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -144,9 +144,27 @@ def max_pool( padding="valid", data_format=None, ): - raise NotImplementedError( - "`max_pool` is not supported with openvino backend" - ) + data_format = backend.standardize_data_format(data_format) + inputs = get_ov_output(inputs) + + num_spatial_dims = inputs.get_partial_shape().rank.get_length() - 2 + if isinstance(pool_size, int): + pool_size = [pool_size] * num_spatial_dims + + strides = _adjust_strides_dilation(strides, num_spatial_dims) + pad_mode, pads_begin, pads_end = _adjust_padding(padding) + inputs = _adjust_input(inputs, num_spatial_dims, data_format) + avg_pooled = ov_opset.max_pool( + inputs, + kernel_shape=pool_size, + strides=strides, + auto_pad=pad_mode, + exclude_pad=True, + pads_begin=pads_begin, + pads_end=pads_end, + ).output(0) + avg_pooled = _adjust_outputs(avg_pooled, num_spatial_dims, data_format) + return OpenVINOKerasTensor(avg_pooled) def average_pool( @@ -503,9 +521,11 @@ def batch_normalization( def ctc_loss(target, output, target_length, output_length, mask_index=0): - raise NotImplementedError( - "`ctc_loss` is not supported with openvino backend" + output = log_softmax(output, axis=-1) + ctc_loss_ = ov_opset.ctc_loss( + output, output_length, target, target_length, blank_index=mask_index ) + return OpenVINOKerasTensor(ctc_loss_.output(0)) def ctc_decode( From a37a7f4dad6a43ee44bea862edefdad63293ad83 Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Thu, 30 Oct 2025 22:37:39 -0400 Subject: [PATCH 03/26] revert test change --- keras/src/ops/nn_test.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/keras/src/ops/nn_test.py b/keras/src/ops/nn_test.py index 969683d88ecc..f4718c495337 100644 --- a/keras/src/ops/nn_test.py +++ b/keras/src/ops/nn_test.py @@ -2439,11 +2439,9 @@ def test_dot_product_attention( mask = mask[None, None, ...] mask = np.tile(mask, (2, 4, 1, 1)) if bias is not None: - backend_ = backend.backend() - if backend_ in ("torch", "openvino"): + if backend.backend() == "torch": self.skipTest( - f"{backend_} does not support `bias` " - f"with `dot_product_attention`" + "torch does not support `bias` with `dot_product_attention`" ) bias = np.arange(math.prod(bias_shape), dtype=float).reshape( bias_shape From 2d6527ec21b3282d350c1c1198714661e47273f1 Mon Sep 17 00:00:00 2001 From: Danny <33044223+danielenricocahall@users.noreply.github.com> Date: Thu, 30 Oct 2025 22:50:15 -0400 Subject: [PATCH 04/26] Update keras/src/backend/openvino/nn.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- keras/src/backend/openvino/nn.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index 35bc42466cfb..02f67d0118b4 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -22,12 +22,12 @@ def celu(x, alpha=1.0): const_zero = get_ov_output(0.0, x.get_element_type()) const_alpha = get_ov_output(alpha, x.get_element_type()) const_one = get_ov_output(1.0, x.get_element_type()) - foo = ov_opset.exp(ov_opset.divide(x, const_alpha)).output(0) - foobar = ov_opset.multiply(const_alpha, ov_opset.subtract(foo, const_one)) + exp_x_div_alpha = ov_opset.exp(ov_opset.divide(x, const_alpha)).output(0) + negative_branch = ov_opset.multiply(const_alpha, ov_opset.subtract(exp_x_div_alpha, const_one)) celu_x = ov_opset.add( ov_opset.maximum(x, const_zero).output(0), - ov_opset.minimum(foobar, const_zero).output(0), + ov_opset.minimum(negative_branch, const_zero).output(0), ) return OpenVINOKerasTensor(celu_x.output(0)) From b9d76181b346368a8061cbec398520959e0d011a Mon Sep 17 00:00:00 2001 From: Danny <33044223+danielenricocahall@users.noreply.github.com> Date: Thu, 30 Oct 2025 22:50:24 -0400 Subject: [PATCH 05/26] Update keras/src/backend/openvino/nn.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- keras/src/backend/openvino/nn.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index 02f67d0118b4..bdeb4ca11022 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -154,7 +154,7 @@ def max_pool( strides = _adjust_strides_dilation(strides, num_spatial_dims) pad_mode, pads_begin, pads_end = _adjust_padding(padding) inputs = _adjust_input(inputs, num_spatial_dims, data_format) - avg_pooled = ov_opset.max_pool( + max_pooled = ov_opset.max_pool( inputs, kernel_shape=pool_size, strides=strides, @@ -163,8 +163,8 @@ def max_pool( pads_begin=pads_begin, pads_end=pads_end, ).output(0) - avg_pooled = _adjust_outputs(avg_pooled, num_spatial_dims, data_format) - return OpenVINOKerasTensor(avg_pooled) + max_pooled = _adjust_outputs(max_pooled, num_spatial_dims, data_format) + return OpenVINOKerasTensor(max_pooled) def average_pool( From a54b884cca8474a124615404a1eef28ae41a2514 Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Thu, 30 Oct 2025 22:52:46 -0400 Subject: [PATCH 06/26] fix one hot with sparse check --- keras/src/backend/openvino/nn.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index bdeb4ca11022..456884bc9d3e 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -23,7 +23,9 @@ def celu(x, alpha=1.0): const_alpha = get_ov_output(alpha, x.get_element_type()) const_one = get_ov_output(1.0, x.get_element_type()) exp_x_div_alpha = ov_opset.exp(ov_opset.divide(x, const_alpha)).output(0) - negative_branch = ov_opset.multiply(const_alpha, ov_opset.subtract(exp_x_div_alpha, const_one)) + negative_branch = ov_opset.multiply( + const_alpha, ov_opset.subtract(exp_x_div_alpha, const_one) + ) celu_x = ov_opset.add( ov_opset.maximum(x, const_zero).output(0), @@ -426,12 +428,16 @@ def conv_transpose( def one_hot(x, num_classes, axis=-1, dtype=None, sparse=False): + if sparse: + raise ValueError("`sparse=True` is not supported with openvino backend") + ov_dtype = OPENVINO_DTYPES[dtype] one_hot_encoded = ov_opset.one_hot( - x, depth=num_classes, axis=axis, on_value=1, off_value=0 + x, + depth=num_classes, + axis=axis, + on_value=ov_opset.constant(1, ov_dtype), + off_value=ov_opset.constant(0, ov_dtype), ).output(0) - if dtype is not None: - dtype = OPENVINO_DTYPES[dtype] - one_hot_encoded = ov_opset.convert(one_hot_encoded, dtype).output(0) return OpenVINOKerasTensor(one_hot_encoded) @@ -521,7 +527,10 @@ def batch_normalization( def ctc_loss(target, output, target_length, output_length, mask_index=0): - output = log_softmax(output, axis=-1) + target = get_ov_output(target) + output = get_ov_output(output) + target_length = get_ov_output(target_length) + output_length = get_ov_output(output_length) ctc_loss_ = ov_opset.ctc_loss( output, output_length, target, target_length, blank_index=mask_index ) From aa4ff5e15267dea7b84fa729f0597aeb82aa01f4 Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Fri, 31 Oct 2025 08:25:15 -0400 Subject: [PATCH 07/26] simplify pooling --- keras/src/backend/openvino/nn.py | 47 ++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index 456884bc9d3e..8d2dbb11a45c 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -146,32 +146,37 @@ def max_pool( padding="valid", data_format=None, ): - data_format = backend.standardize_data_format(data_format) - inputs = get_ov_output(inputs) + return _pool( + inputs, + pool_size, + ov_opset.max_pool, + strides=strides, + padding=padding, + data_format=data_format, + ) - num_spatial_dims = inputs.get_partial_shape().rank.get_length() - 2 - if isinstance(pool_size, int): - pool_size = [pool_size] * num_spatial_dims - strides = _adjust_strides_dilation(strides, num_spatial_dims) - pad_mode, pads_begin, pads_end = _adjust_padding(padding) - inputs = _adjust_input(inputs, num_spatial_dims, data_format) - max_pooled = ov_opset.max_pool( +def average_pool( + inputs, + pool_size, + strides=None, + padding="valid", + data_format=None, +): + return _pool( inputs, - kernel_shape=pool_size, + pool_size, + ov_opset.avg_pool, strides=strides, - auto_pad=pad_mode, - exclude_pad=True, - pads_begin=pads_begin, - pads_end=pads_end, - ).output(0) - max_pooled = _adjust_outputs(max_pooled, num_spatial_dims, data_format) - return OpenVINOKerasTensor(max_pooled) + padding=padding, + data_format=data_format, + ) -def average_pool( +def _pool( inputs, pool_size, + pooling_func, strides=None, padding="valid", data_format=None, @@ -186,7 +191,7 @@ def average_pool( strides = _adjust_strides_dilation(strides, num_spatial_dims) pad_mode, pads_begin, pads_end = _adjust_padding(padding) inputs = _adjust_input(inputs, num_spatial_dims, data_format) - avg_pooled = ov_opset.avg_pool( + pooled = pooling_func( inputs, kernel_shape=pool_size, strides=strides, @@ -195,8 +200,8 @@ def average_pool( pads_begin=pads_begin, pads_end=pads_end, ).output(0) - avg_pooled = _adjust_outputs(avg_pooled, num_spatial_dims, data_format) - return OpenVINOKerasTensor(avg_pooled) + adjusted_pooled = _adjust_outputs(pooled, num_spatial_dims, data_format) + return OpenVINOKerasTensor(adjusted_pooled) def _adjust_strides_dilation( From 5421b36aa11154ea9ff97ec70dfe247b07ecd67e Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Fri, 31 Oct 2025 08:25:51 -0400 Subject: [PATCH 08/26] handle dtype of one_hot --- keras/src/backend/openvino/nn.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index 8d2dbb11a45c..e064f025022a 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -435,6 +435,8 @@ def conv_transpose( def one_hot(x, num_classes, axis=-1, dtype=None, sparse=False): if sparse: raise ValueError("`sparse=True` is not supported with openvino backend") + if dtype is None: + dtype = backend.floatx() ov_dtype = OPENVINO_DTYPES[dtype] one_hot_encoded = ov_opset.one_hot( x, From 2548b3325d2d1cc373d72177e1eeb54a2691a38e Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Fri, 31 Oct 2025 08:32:15 -0400 Subject: [PATCH 09/26] use swish op for silu --- keras/src/backend/openvino/nn.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index e064f025022a..de603daeeed3 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -56,9 +56,7 @@ def softsign(x): def silu(x): x = get_ov_output(x) - return OpenVINOKerasTensor( - ov_opset.multiply(x, ov_opset.sigmoid(x)).output(0) - ) + return OpenVINOKerasTensor(ov_opset.swish(x).output(0)) def log_sigmoid(x): From 80dcb9c012d082d018c9a4a85a774db2dc4a3de4 Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Fri, 31 Oct 2025 08:37:12 -0400 Subject: [PATCH 10/26] support for log_sigmoid --- keras/src/backend/openvino/nn.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index de603daeeed3..69e648eb20c2 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -60,8 +60,10 @@ def silu(x): def log_sigmoid(x): - raise NotImplementedError( - "`log_sigmoid` is not supported with openvino backend" + x = get_ov_output(x) + neg_x = ov_opset.negative(x) + return OpenVINOKerasTensor( + ov_opset.negative(ov_opset.softplus(neg_x)).output(0) ) From 467f41874365b0a7a1b1ea1e2aee9e2db2667b52 Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Fri, 31 Oct 2025 09:19:41 -0400 Subject: [PATCH 11/26] address gemini feedback --- keras/src/backend/openvino/nn.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index 69e648eb20c2..1f7b3be863e7 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -188,12 +188,15 @@ def _pool( if isinstance(pool_size, int): pool_size = [pool_size] * num_spatial_dims + if strides is None: + strides = pool_size + strides = _adjust_strides_dilation(strides, num_spatial_dims) pad_mode, pads_begin, pads_end = _adjust_padding(padding) inputs = _adjust_input(inputs, num_spatial_dims, data_format) pooled = pooling_func( inputs, - kernel_shape=pool_size, + kernel=pool_size, strides=strides, auto_pad=pad_mode, exclude_pad=True, @@ -435,15 +438,18 @@ def conv_transpose( def one_hot(x, num_classes, axis=-1, dtype=None, sparse=False): if sparse: raise ValueError("`sparse=True` is not supported with openvino backend") + x = get_ov_output(x) if dtype is None: dtype = backend.floatx() ov_dtype = OPENVINO_DTYPES[dtype] + on_value = get_ov_output(1, ov_dtype) + off_value = get_ov_output(0, ov_dtype) one_hot_encoded = ov_opset.one_hot( x, depth=num_classes, axis=axis, - on_value=ov_opset.constant(1, ov_dtype), - off_value=ov_opset.constant(0, ov_dtype), + on_value=on_value, + off_value=off_value, ).output(0) return OpenVINOKerasTensor(one_hot_encoded) From e5e5c0bdf3e48774a09916fbab8971a0fcb2bd11 Mon Sep 17 00:00:00 2001 From: Danny <33044223+danielenricocahall@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:26:00 -0400 Subject: [PATCH 12/26] Update keras/src/backend/openvino/nn.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- keras/src/backend/openvino/nn.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index 1f7b3be863e7..acffddccc82d 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -194,15 +194,16 @@ def _pool( strides = _adjust_strides_dilation(strides, num_spatial_dims) pad_mode, pads_begin, pads_end = _adjust_padding(padding) inputs = _adjust_input(inputs, num_spatial_dims, data_format) - pooled = pooling_func( - inputs, - kernel=pool_size, - strides=strides, - auto_pad=pad_mode, - exclude_pad=True, - pads_begin=pads_begin, - pads_end=pads_end, - ).output(0) + pool_kwargs = { + "kernel": pool_size, + "strides": strides, + "auto_pad": pad_mode, + "pads_begin": pads_begin, + "pads_end": pads_end, + } + if pooling_func == ov_opset.avg_pool: + pool_kwargs["exclude_pad"] = True + pooled = pooling_func(inputs, **pool_kwargs).output(0) adjusted_pooled = _adjust_outputs(pooled, num_spatial_dims, data_format) return OpenVINOKerasTensor(adjusted_pooled) From 357f5e9bbf38bcd7d72434c650e69e4843837730 Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Fri, 31 Oct 2025 11:22:55 -0400 Subject: [PATCH 13/26] fix consolidated pool function --- keras/src/backend/openvino/nn.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index acffddccc82d..56d8052fdb2c 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -147,12 +147,7 @@ def max_pool( data_format=None, ): return _pool( - inputs, - pool_size, - ov_opset.max_pool, - strides=strides, - padding=padding, - data_format=data_format, + inputs, pool_size, ov_opset.max_pool, strides, padding, data_format ) @@ -167,9 +162,10 @@ def average_pool( inputs, pool_size, ov_opset.avg_pool, - strides=strides, - padding=padding, - data_format=data_format, + strides, + padding, + data_format, + exclude_pad=True, ) @@ -180,6 +176,7 @@ def _pool( strides=None, padding="valid", data_format=None, + **kwargs, ): data_format = backend.standardize_data_format(data_format) inputs = get_ov_output(inputs) @@ -195,14 +192,13 @@ def _pool( pad_mode, pads_begin, pads_end = _adjust_padding(padding) inputs = _adjust_input(inputs, num_spatial_dims, data_format) pool_kwargs = { - "kernel": pool_size, + "kernel_shape": pool_size, "strides": strides, "auto_pad": pad_mode, "pads_begin": pads_begin, "pads_end": pads_end, + **kwargs, } - if pooling_func == ov_opset.avg_pool: - pool_kwargs["exclude_pad"] = True pooled = pooling_func(inputs, **pool_kwargs).output(0) adjusted_pooled = _adjust_outputs(pooled, num_spatial_dims, data_format) return OpenVINOKerasTensor(adjusted_pooled) From a32fc571d4fa8334deb05c967623d4640a5feded Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Wed, 5 Nov 2025 22:01:27 -0500 Subject: [PATCH 14/26] enable testing --- .../openvino/excluded_concrete_tests.txt | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/keras/src/backend/openvino/excluded_concrete_tests.txt b/keras/src/backend/openvino/excluded_concrete_tests.txt index d75a9a234d13..5ae0a24a6412 100644 --- a/keras/src/backend/openvino/excluded_concrete_tests.txt +++ b/keras/src/backend/openvino/excluded_concrete_tests.txt @@ -276,3 +276,57 @@ TestMathErrors::test_stft_invalid_window TestMathErrors::test_stft_invalid_window_shape LinalgOpsCorrectnessTest::test_cholesky LinalgOpsCorrectnessTest::test_cholesky_inverse +NNOpsDynamicShapeTest::test_binary_crossentropy +NNOpsDynamicShapeTest::test_categorical_crossentropy +NNOpsDynamicShapeTest::test_multi_hot_dtype_ +NNOpsCorrectnessTest::test_conv_transpose_ +NNOpsCorrectnessTest::test_ctc_decode +NNOpsCorrectnessTest::test_dot_product_attention_ +NNOpsCorrectnessTest::test_multi_hot_ +NNOpsCorrectnessTest::test_leaky_relu +NNOpsCorrectnessTest::test_binary_crossentropy +NNOpsCorrectnessTest::test_categorical_crossentropy +NNOpsCorrectnessTest::test_log_softmax_correctness_with_axis_tuple +NNOpsCorrectnessTest::test_softmax_correctness_with_axis_tuple +NNOpsCorrectnessTest::test_separable_conv_ +NNOpsCorrectnessTest::test_elu +NNOpsCorrectnessTest::test_glu +NNOpsCorrectnessTest::test_hard_shrink +NNOpsCorrectnessTest::test_hard_sigmoid +NNOpsCorrectnessTest::test_hard_silu +NNOpsCorrectnessTest::test_hard_tanh +NNOpsCorrectnessTest::test_moments +NNOpsCorrectnessTest::test_normalize +NNOpsCorrectnessTest::test_polar_corectness +NNOpsCorrectnessTest::test_psnr +NNOpsCorrectnessTest::test_selu +NNOpsCorrectnessTest::test_soft_shrink +NNOpsCorrectnessTest::test_sparse_ +NNOpsCorrectnessTest::test_squareplus +NNOpsCorrectnessTest::test_tanh_shrink +NNOpsCorrectnessTest::test_threshold +NNOpsCorrectnessTest::test_sparsemax + + + +NNOpsDtypeTest::test_squareplus +NNOpsDtypeTest::test_tanh +NNOpsDtypeTest::test_soft_shrink +NNOpsDtypeTest::test_hard_shrink +NNOpsDtypeTest::test_ctc_decode +NNOpsDtypeTest::test_glu_ +NNOpsDtypeTest::test_hard_tanh_ +NNOpsDtypeTest::test_polar_ +NNOpsDtypeTest::test_sparse_sigmoid_ +NNOpsDtypeTest::test_threshold_ +NNOpsDtypeTest::test_dot_product_attention_ +NNOpsDtypeTest::test_sparse_plus_ + +NNOpsDynamicShapeTest::test_elu +NNOpsDynamicShapeTest::test_glu +NNOpsDynamicShapeTest::test_selu +NNOpsDynamicShapeTest::test_silu +NNOpsDynamicShapeTest::test_tanh + +NNOpsBehaviorTest::test_invalid_strategy_ctc_decode +NNOpsBehaviorTest::test_logit_recovery_binary_crossentropy From 4805c2e1cd5c9c985249ecba5b686afeba729e16 Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Wed, 5 Nov 2025 22:01:50 -0500 Subject: [PATCH 15/26] fix max_pool call --- keras/src/backend/openvino/nn.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index 56d8052fdb2c..48408560d4a4 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -146,8 +146,18 @@ def max_pool( padding="valid", data_format=None, ): + num_spatial_dims = ( + get_ov_output(inputs).get_partial_shape().rank.get_length() - 2 + ) + kwargs = {"dilations": [1] * num_spatial_dims} # required for ov max_pool return _pool( - inputs, pool_size, ov_opset.max_pool, strides, padding, data_format + inputs, + pool_size, + ov_opset.max_pool, + strides, + padding, + data_format, + **kwargs, ) From 618cf53228240a8c7ebcbaaa85c8d5398819fb8a Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Wed, 5 Nov 2025 22:05:29 -0500 Subject: [PATCH 16/26] fix dtype for ctc_loss --- keras/src/backend/openvino/nn.py | 1 + 1 file changed, 1 insertion(+) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index 48408560d4a4..eafd57dd76fa 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -554,6 +554,7 @@ def ctc_loss(target, output, target_length, output_length, mask_index=0): ctc_loss_ = ov_opset.ctc_loss( output, output_length, target, target_length, blank_index=mask_index ) + ctc_loss_ = ov_opset.convert(ctc_loss_, OPENVINO_DTYPES[backend.floatx()]) return OpenVINOKerasTensor(ctc_loss_.output(0)) From 51152eec180b96235022c70479b5a4c779889d2d Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Wed, 5 Nov 2025 22:07:10 -0500 Subject: [PATCH 17/26] fix dtype for swish --- keras/src/backend/openvino/nn.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index eafd57dd76fa..6180343fa18e 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -56,7 +56,8 @@ def softsign(x): def silu(x): x = get_ov_output(x) - return OpenVINOKerasTensor(ov_opset.swish(x).output(0)) + beta = get_ov_output(1.0, x.get_element_type()) + return OpenVINOKerasTensor(ov_opset.swish(x, beta=beta).output(0)) def log_sigmoid(x): From fb133d263c4247716eb5441bcbba78a18dc156c8 Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Wed, 5 Nov 2025 22:19:08 -0500 Subject: [PATCH 18/26] permit nn test to be run --- keras/src/backend/openvino/excluded_concrete_tests.txt | 6 +----- keras/src/backend/openvino/excluded_tests.txt | 1 - keras/src/ops/nn_test.py | 9 +++++++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/keras/src/backend/openvino/excluded_concrete_tests.txt b/keras/src/backend/openvino/excluded_concrete_tests.txt index 5ae0a24a6412..3b59acd487ba 100644 --- a/keras/src/backend/openvino/excluded_concrete_tests.txt +++ b/keras/src/backend/openvino/excluded_concrete_tests.txt @@ -306,9 +306,7 @@ NNOpsCorrectnessTest::test_squareplus NNOpsCorrectnessTest::test_tanh_shrink NNOpsCorrectnessTest::test_threshold NNOpsCorrectnessTest::test_sparsemax - - - +NNOpsCorrectnessTest::test_rms_normalization_10.0 NNOpsDtypeTest::test_squareplus NNOpsDtypeTest::test_tanh NNOpsDtypeTest::test_soft_shrink @@ -321,12 +319,10 @@ NNOpsDtypeTest::test_sparse_sigmoid_ NNOpsDtypeTest::test_threshold_ NNOpsDtypeTest::test_dot_product_attention_ NNOpsDtypeTest::test_sparse_plus_ - NNOpsDynamicShapeTest::test_elu NNOpsDynamicShapeTest::test_glu NNOpsDynamicShapeTest::test_selu NNOpsDynamicShapeTest::test_silu NNOpsDynamicShapeTest::test_tanh - NNOpsBehaviorTest::test_invalid_strategy_ctc_decode NNOpsBehaviorTest::test_logit_recovery_binary_crossentropy diff --git a/keras/src/backend/openvino/excluded_tests.txt b/keras/src/backend/openvino/excluded_tests.txt index b68bc4c2dbc5..bb18ad798a6a 100644 --- a/keras/src/backend/openvino/excluded_tests.txt +++ b/keras/src/backend/openvino/excluded_tests.txt @@ -30,7 +30,6 @@ keras/src/metrics keras/src/models keras/src/ops/image_test.py keras/src/ops/linalg_test.py -keras/src/ops/nn_test.py keras/src/optimizers keras/src/quantizers keras/src/random/seed_generator_test.py diff --git a/keras/src/ops/nn_test.py b/keras/src/ops/nn_test.py index f4718c495337..eeb04aa59dfc 100644 --- a/keras/src/ops/nn_test.py +++ b/keras/src/ops/nn_test.py @@ -1324,6 +1324,15 @@ def test_polar(self): class NNOpsCorrectnessTest(testing.TestCase): + def assertAllClose(self, x1, x2, atol=1e-6, rtol=1e-6, msg=None): + if backend.backend() == "openvino": + # OpenVINO seems to use lower precision for some operations, + # or employs some different algorithms that wind up with + # slightly different results. To address this, we relax + # the tolerances for OpenVINO backend. + atol = 1e-3 + super().assertAllClose(x1, x2, atol=atol, rtol=rtol, msg=msg) + def test_relu(self): x = np.array([-1, 0, 1, 2, 3], dtype=np.float32) self.assertAllClose(knn.relu(x), [0, 0, 1, 2, 3]) From c74515993ae58de2c65769302b84b1e2e18fd065 Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Wed, 5 Nov 2025 22:36:31 -0500 Subject: [PATCH 19/26] support for more activation functions, enabling tests that should stay enabled --- .../openvino/excluded_concrete_tests.txt | 19 +------ keras/src/backend/openvino/nn.py | 50 +++++++++++++++++++ 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/keras/src/backend/openvino/excluded_concrete_tests.txt b/keras/src/backend/openvino/excluded_concrete_tests.txt index 3b59acd487ba..43d608b7849b 100644 --- a/keras/src/backend/openvino/excluded_concrete_tests.txt +++ b/keras/src/backend/openvino/excluded_concrete_tests.txt @@ -283,46 +283,31 @@ NNOpsCorrectnessTest::test_conv_transpose_ NNOpsCorrectnessTest::test_ctc_decode NNOpsCorrectnessTest::test_dot_product_attention_ NNOpsCorrectnessTest::test_multi_hot_ -NNOpsCorrectnessTest::test_leaky_relu NNOpsCorrectnessTest::test_binary_crossentropy NNOpsCorrectnessTest::test_categorical_crossentropy NNOpsCorrectnessTest::test_log_softmax_correctness_with_axis_tuple NNOpsCorrectnessTest::test_softmax_correctness_with_axis_tuple NNOpsCorrectnessTest::test_separable_conv_ -NNOpsCorrectnessTest::test_elu NNOpsCorrectnessTest::test_glu -NNOpsCorrectnessTest::test_hard_shrink -NNOpsCorrectnessTest::test_hard_sigmoid -NNOpsCorrectnessTest::test_hard_silu -NNOpsCorrectnessTest::test_hard_tanh NNOpsCorrectnessTest::test_moments NNOpsCorrectnessTest::test_normalize NNOpsCorrectnessTest::test_polar_corectness NNOpsCorrectnessTest::test_psnr NNOpsCorrectnessTest::test_selu -NNOpsCorrectnessTest::test_soft_shrink -NNOpsCorrectnessTest::test_sparse_ +NNOpsCorrectnessTest::test_sparse_plus +NNOpsCorrectnessTest::test_sparse_categorical_crossentropy NNOpsCorrectnessTest::test_squareplus -NNOpsCorrectnessTest::test_tanh_shrink NNOpsCorrectnessTest::test_threshold NNOpsCorrectnessTest::test_sparsemax NNOpsCorrectnessTest::test_rms_normalization_10.0 NNOpsDtypeTest::test_squareplus -NNOpsDtypeTest::test_tanh -NNOpsDtypeTest::test_soft_shrink -NNOpsDtypeTest::test_hard_shrink NNOpsDtypeTest::test_ctc_decode NNOpsDtypeTest::test_glu_ -NNOpsDtypeTest::test_hard_tanh_ NNOpsDtypeTest::test_polar_ -NNOpsDtypeTest::test_sparse_sigmoid_ NNOpsDtypeTest::test_threshold_ NNOpsDtypeTest::test_dot_product_attention_ NNOpsDtypeTest::test_sparse_plus_ -NNOpsDynamicShapeTest::test_elu NNOpsDynamicShapeTest::test_glu NNOpsDynamicShapeTest::test_selu -NNOpsDynamicShapeTest::test_silu -NNOpsDynamicShapeTest::test_tanh NNOpsBehaviorTest::test_invalid_strategy_ctc_decode NNOpsBehaviorTest::test_logit_recovery_binary_crossentropy diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index 6180343fa18e..ac7a331c6413 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -44,6 +44,42 @@ def tanh(x): return OpenVINOKerasTensor(ov_opset.tanh(x).output(0)) +def tanh_shrink(x): + x = get_ov_output(x) + return OpenVINOKerasTensor(ov_opset.subtract(x, ov_opset.tanh(x)).output(0)) + + +def hard_tanh(x): + x = get_ov_output(x) + return OpenVINOKerasTensor(ov_opset.clamp(x, -1.0, 1.0).output(0)) + + +def soft_shrink(x, threshold=0.5): + x = get_ov_output(x) + et = x.get_element_type() + thr = get_ov_output(threshold, et) + zero = get_ov_output(0.0, et) + abs_x = ov_opset.abs(x) + sub = ov_opset.subtract(abs_x, thr) + shrunk = ov_opset.maximum(sub, zero) + sign = ov_opset.sign(x) + out = ov_opset.multiply(sign, shrunk) + return OpenVINOKerasTensor(out.output(0)) + + +def hard_shrink(x, threshold=0.5): + x = get_ov_output(x) + et = x.get_element_type() + + thr = get_ov_output(threshold, et) + zero = get_ov_output(0.0, et) + + cond = ov_opset.greater(ov_opset.abs(x), thr) + + out = ov_opset.select(cond, x, zero) + return OpenVINOKerasTensor(out.output(0)) + + def softplus(x): x = get_ov_output(x) return OpenVINOKerasTensor(ov_opset.softplus(x).output(0)) @@ -77,6 +113,20 @@ def leaky_relu(x, negative_slope=0.2): return OpenVINOKerasTensor(leaky_relu) +def sparse_sigmoid(x): + x = get_ov_output(x) + et = x.get_element_type() + + one = get_ov_output(1.0, et) + neg_one = get_ov_output(-1.0, et) + half = get_ov_output(0.5, et) + + y = ov_opset.minimum(ov_opset.maximum(x, neg_one), one) + + out = ov_opset.multiply(half, ov_opset.add(y, one)) + return OpenVINOKerasTensor(out.output(0)) + + def hard_sigmoid(x): x = get_ov_output(x) alpha = get_ov_output(1.0 / 6.0, x.get_element_type()) From 6397f92ec599817a5179103798a48d24ed364b8b Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Thu, 6 Nov 2025 17:45:45 -0500 Subject: [PATCH 20/26] support squareplus and sparse_plus --- .../openvino/excluded_concrete_tests.txt | 4 -- keras/src/backend/openvino/nn.py | 41 +++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/keras/src/backend/openvino/excluded_concrete_tests.txt b/keras/src/backend/openvino/excluded_concrete_tests.txt index 43d608b7849b..08d59f1ad1bb 100644 --- a/keras/src/backend/openvino/excluded_concrete_tests.txt +++ b/keras/src/backend/openvino/excluded_concrete_tests.txt @@ -294,19 +294,15 @@ NNOpsCorrectnessTest::test_normalize NNOpsCorrectnessTest::test_polar_corectness NNOpsCorrectnessTest::test_psnr NNOpsCorrectnessTest::test_selu -NNOpsCorrectnessTest::test_sparse_plus NNOpsCorrectnessTest::test_sparse_categorical_crossentropy -NNOpsCorrectnessTest::test_squareplus NNOpsCorrectnessTest::test_threshold NNOpsCorrectnessTest::test_sparsemax NNOpsCorrectnessTest::test_rms_normalization_10.0 -NNOpsDtypeTest::test_squareplus NNOpsDtypeTest::test_ctc_decode NNOpsDtypeTest::test_glu_ NNOpsDtypeTest::test_polar_ NNOpsDtypeTest::test_threshold_ NNOpsDtypeTest::test_dot_product_attention_ -NNOpsDtypeTest::test_sparse_plus_ NNOpsDynamicShapeTest::test_glu NNOpsDynamicShapeTest::test_selu NNOpsBehaviorTest::test_invalid_strategy_ctc_decode diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index ac7a331c6413..084f4c8f27f5 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -190,6 +190,47 @@ def log_softmax(x, axis=-1): return OpenVINOKerasTensor(ov_opset.log_softmax(x, axis).output(0)) +def squareplus(x, b=4): + x = get_ov_output(x) + et = x.get_element_type() + + b = get_ov_output(b, et) + two = get_ov_output(2.0, et) + + x_squared = ov_opset.multiply(x, x) + inside = ov_opset.add(x_squared, b) + root = ov_opset.sqrt(inside) + summed = ov_opset.add(x, root) + + out = ov_opset.divide(summed, two) + + return OpenVINOKerasTensor(out.output(0)) + + +def sparse_plus(x): + x = get_ov_output(x) + et = x.get_element_type() + + one = get_ov_output(1.0, et) + neg_one = get_ov_output(-1.0, et) + zero = get_ov_output(0.0, et) + quarter = get_ov_output(0.25, et) + + x_plus_1 = ov_opset.add(x, one) + quad = ov_opset.multiply(quarter, ov_opset.multiply(x_plus_1, x_plus_1)) + + leq_than_neg_one = ov_opset.less_equal(x, neg_one) + less_than_one = ov_opset.less(x, one) + + out = ov_opset.select( + leq_than_neg_one, + zero, + ov_opset.select(less_than_one, quad, x), + ) + + return OpenVINOKerasTensor(out.output(0)) + + def max_pool( inputs, pool_size, From 38eca85218eff192ea07a3b368825f58a8233697 Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Thu, 6 Nov 2025 17:46:23 -0500 Subject: [PATCH 21/26] enable selu test --- keras/src/backend/openvino/excluded_concrete_tests.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/keras/src/backend/openvino/excluded_concrete_tests.txt b/keras/src/backend/openvino/excluded_concrete_tests.txt index 08d59f1ad1bb..560a35ee47c2 100644 --- a/keras/src/backend/openvino/excluded_concrete_tests.txt +++ b/keras/src/backend/openvino/excluded_concrete_tests.txt @@ -293,7 +293,6 @@ NNOpsCorrectnessTest::test_moments NNOpsCorrectnessTest::test_normalize NNOpsCorrectnessTest::test_polar_corectness NNOpsCorrectnessTest::test_psnr -NNOpsCorrectnessTest::test_selu NNOpsCorrectnessTest::test_sparse_categorical_crossentropy NNOpsCorrectnessTest::test_threshold NNOpsCorrectnessTest::test_sparsemax From fdb2d0a2396614101d414ff78852c73d3460f073 Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Thu, 6 Nov 2025 17:48:03 -0500 Subject: [PATCH 22/26] support threshold --- .../backend/openvino/excluded_concrete_tests.txt | 2 -- keras/src/backend/openvino/nn.py | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/keras/src/backend/openvino/excluded_concrete_tests.txt b/keras/src/backend/openvino/excluded_concrete_tests.txt index 560a35ee47c2..eb51032e1db3 100644 --- a/keras/src/backend/openvino/excluded_concrete_tests.txt +++ b/keras/src/backend/openvino/excluded_concrete_tests.txt @@ -294,13 +294,11 @@ NNOpsCorrectnessTest::test_normalize NNOpsCorrectnessTest::test_polar_corectness NNOpsCorrectnessTest::test_psnr NNOpsCorrectnessTest::test_sparse_categorical_crossentropy -NNOpsCorrectnessTest::test_threshold NNOpsCorrectnessTest::test_sparsemax NNOpsCorrectnessTest::test_rms_normalization_10.0 NNOpsDtypeTest::test_ctc_decode NNOpsDtypeTest::test_glu_ NNOpsDtypeTest::test_polar_ -NNOpsDtypeTest::test_threshold_ NNOpsDtypeTest::test_dot_product_attention_ NNOpsDynamicShapeTest::test_glu NNOpsDynamicShapeTest::test_selu diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index 084f4c8f27f5..cb867a667046 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -231,6 +231,20 @@ def sparse_plus(x): return OpenVINOKerasTensor(out.output(0)) +def threshold(x, threshold, default_value): + x = get_ov_output(x) + et = x.get_element_type() + + thr = get_ov_output(threshold, et) + dv = get_ov_output(default_value, et) + + cond = ov_opset.greater(x, thr) + + out = ov_opset.select(cond, x, dv) + + return OpenVINOKerasTensor(out.output(0)) + + def max_pool( inputs, pool_size, From a99358040b56ea49c5a1693dc9c34ac5c127746d Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Thu, 6 Nov 2025 17:48:26 -0500 Subject: [PATCH 23/26] selu test --- keras/src/backend/openvino/excluded_concrete_tests.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/keras/src/backend/openvino/excluded_concrete_tests.txt b/keras/src/backend/openvino/excluded_concrete_tests.txt index eb51032e1db3..abdd70805657 100644 --- a/keras/src/backend/openvino/excluded_concrete_tests.txt +++ b/keras/src/backend/openvino/excluded_concrete_tests.txt @@ -301,6 +301,5 @@ NNOpsDtypeTest::test_glu_ NNOpsDtypeTest::test_polar_ NNOpsDtypeTest::test_dot_product_attention_ NNOpsDynamicShapeTest::test_glu -NNOpsDynamicShapeTest::test_selu NNOpsBehaviorTest::test_invalid_strategy_ctc_decode NNOpsBehaviorTest::test_logit_recovery_binary_crossentropy From 764e4fb39c42e81a5865a6831fb54dddcbae52e1 Mon Sep 17 00:00:00 2001 From: "daniel.cahall" Date: Mon, 10 Nov 2025 20:48:12 -0500 Subject: [PATCH 24/26] support scaled dot product attention --- .../openvino/excluded_concrete_tests.txt | 2 - keras/src/backend/openvino/nn.py | 42 ++++++++++++++++++- keras/src/ops/nn_test.py | 2 +- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/keras/src/backend/openvino/excluded_concrete_tests.txt b/keras/src/backend/openvino/excluded_concrete_tests.txt index d1462158b512..f090a18bb8e8 100644 --- a/keras/src/backend/openvino/excluded_concrete_tests.txt +++ b/keras/src/backend/openvino/excluded_concrete_tests.txt @@ -264,7 +264,6 @@ NNOpsDynamicShapeTest::test_categorical_crossentropy NNOpsDynamicShapeTest::test_multi_hot_dtype_ NNOpsCorrectnessTest::test_conv_transpose_ NNOpsCorrectnessTest::test_ctc_decode -NNOpsCorrectnessTest::test_dot_product_attention_ NNOpsCorrectnessTest::test_multi_hot_ NNOpsCorrectnessTest::test_binary_crossentropy NNOpsCorrectnessTest::test_categorical_crossentropy @@ -282,7 +281,6 @@ NNOpsCorrectnessTest::test_rms_normalization_10.0 NNOpsDtypeTest::test_ctc_decode NNOpsDtypeTest::test_glu_ NNOpsDtypeTest::test_polar_ -NNOpsDtypeTest::test_dot_product_attention_ NNOpsDynamicShapeTest::test_glu NNOpsBehaviorTest::test_invalid_strategy_ctc_decode NNOpsBehaviorTest::test_logit_recovery_binary_crossentropy diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index cb867a667046..cd88b2f4da34 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -693,9 +693,47 @@ def dot_product_attention( flash_attention=None, attn_logits_soft_cap=None, ): - raise NotImplementedError( - "`dot_product_attention` is not supported with openvino backend" + if bias is not None: + raise NotImplementedError( + "`dot_product_attention` with `bias` is not supported " + "with openvino backend" + ) + if flash_attention is not None: + raise NotImplementedError( + "`dot_product_attention` with `flash_attention` is not supported " + "with openvino backend" + ) + if attn_logits_soft_cap is not None: + raise NotImplementedError( + "`dot_product_attention` with `attn_logits_soft_cap` is not " + "supported with openvino backend" + ) + query = get_ov_output(query) + key = get_ov_output(key) + value = get_ov_output(value) + if query.get_element_type() != key.get_element_type(): + ov_type = OPENVINO_DTYPES[backend.floatx()] + query = ov_opset.convert(query, ov_type) + key = ov_opset.convert(key, ov_type) + if value.get_element_type() != query.get_element_type(): + ov_type = OPENVINO_DTYPES[backend.floatx()] + value = ov_opset.convert(value, ov_type) + axes_const = ov_opset.constant([0, 2, 1, 3], Type.i32).output(0) + + query = ov_opset.transpose(query, axes_const) + key = ov_opset.transpose(key, axes_const) + value = ov_opset.transpose(value, axes_const) + mask = get_ov_output(mask) if mask is not None else None + scale = ( + get_ov_output(scale, query.get_element_type()) + if scale is not None + else None + ) + dpa = ov_opset.scaled_dot_product_attention( + query, key, value, attention_mask=mask, scale=scale, causal=is_causal ) + dpa = ov_opset.transpose(dpa, axes_const) + return OpenVINOKerasTensor(dpa.output(0)) def unfold(input, kernel_size, dilation=1, padding=0, stride=1): diff --git a/keras/src/ops/nn_test.py b/keras/src/ops/nn_test.py index eeb04aa59dfc..859b6c75f004 100644 --- a/keras/src/ops/nn_test.py +++ b/keras/src/ops/nn_test.py @@ -2448,7 +2448,7 @@ def test_dot_product_attention( mask = mask[None, None, ...] mask = np.tile(mask, (2, 4, 1, 1)) if bias is not None: - if backend.backend() == "torch": + if backend.backend() in ("torch", "openvino"): self.skipTest( "torch does not support `bias` with `dot_product_attention`" ) From 93fd96db186140b2472dbc2eef7ad075f1bfcb8b Mon Sep 17 00:00:00 2001 From: Danny <33044223+danielenricocahall@users.noreply.github.com> Date: Mon, 10 Nov 2025 20:54:20 -0500 Subject: [PATCH 25/26] Update keras/src/backend/openvino/nn.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- keras/src/backend/openvino/nn.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/keras/src/backend/openvino/nn.py b/keras/src/backend/openvino/nn.py index cd88b2f4da34..e6639e7eea0b 100644 --- a/keras/src/backend/openvino/nn.py +++ b/keras/src/backend/openvino/nn.py @@ -713,11 +713,10 @@ def dot_product_attention( value = get_ov_output(value) if query.get_element_type() != key.get_element_type(): ov_type = OPENVINO_DTYPES[backend.floatx()] - query = ov_opset.convert(query, ov_type) - key = ov_opset.convert(key, ov_type) + query = ov_opset.convert(query, ov_type).output(0) + key = ov_opset.convert(key, ov_type).output(0) if value.get_element_type() != query.get_element_type(): - ov_type = OPENVINO_DTYPES[backend.floatx()] - value = ov_opset.convert(value, ov_type) + value = ov_opset.convert(value, query.get_element_type()).output(0) axes_const = ov_opset.constant([0, 2, 1, 3], Type.i32).output(0) query = ov_opset.transpose(query, axes_const) From c7b59f04f1e4def1e82278d2a81e6f443bc8c436 Mon Sep 17 00:00:00 2001 From: Danny <33044223+danielenricocahall@users.noreply.github.com> Date: Mon, 10 Nov 2025 20:54:35 -0500 Subject: [PATCH 26/26] Update keras/src/ops/nn_test.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- keras/src/ops/nn_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keras/src/ops/nn_test.py b/keras/src/ops/nn_test.py index 859b6c75f004..5b16c276c32f 100644 --- a/keras/src/ops/nn_test.py +++ b/keras/src/ops/nn_test.py @@ -2450,7 +2450,7 @@ def test_dot_product_attention( if bias is not None: if backend.backend() in ("torch", "openvino"): self.skipTest( - "torch does not support `bias` with `dot_product_attention`" + "torch and openvino do not support `bias` with `dot_product_attention`" ) bias = np.arange(math.prod(bias_shape), dtype=float).reshape( bias_shape