Skip to content

Commit 8714a49

Browse files
Merge branch 'master' into openvino-nn-functions
2 parents a993580 + 4d30a7f commit 8714a49

File tree

24 files changed

+344
-106
lines changed

24 files changed

+344
-106
lines changed

.github/workflows/nightly.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ jobs:
6969
PYTHON: ${{ matrix.python-version }}
7070
KERAS_BACKEND: ${{ matrix.backend }}
7171
steps:
72-
- uses: actions/checkout@v4
72+
- uses: actions/checkout@v5
7373
- name: Set up Python
74-
uses: actions/setup-python@v5
74+
uses: actions/setup-python@v6
7575
with:
7676
python-version: ${{ matrix.python-version }}
7777
- name: Get pip cache dir

.github/workflows/scorecard.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,14 @@ jobs:
4848
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
4949
# format to the repository Actions tab.
5050
- name: "Upload artifact"
51-
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
51+
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
5252
with:
5353
name: SARIF file
5454
path: results.sarif
5555
retention-days: 5
5656

5757
# Upload the results to GitHub's code scanning dashboard.
5858
- name: "Upload to code-scanning"
59-
uses: github/codeql-action/upload-sarif@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.29.5
59+
uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5
6060
with:
6161
sarif_file: results.sarif

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Contributions can be made in a variety of ways, including coding, enriching docu
44

55

66
## Current items open for contributions
7-
At [this link](https://github.com/keras-team/keras/issues/18442), you'll find a list of items where you help is needed!
7+
At [this link](https://github.com/keras-team/keras/issues/18442), you'll find a list of items where your help is needed!
88

99

1010
## How to contribute code

keras/src/backend/jax/nn.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ def conv(
355355
feature_group_count = channels // kernel_in_channels
356356
kernel = convert_to_tensor(kernel)
357357
inputs = convert_to_tensor(inputs, dtype=kernel.dtype)
358-
return jax.lax.conv_general_dilated(
358+
result = jax.lax.conv_general_dilated(
359359
inputs,
360360
kernel,
361361
strides,
@@ -364,6 +364,14 @@ def conv(
364364
dimension_numbers=dimension_numbers,
365365
feature_group_count=feature_group_count,
366366
)
367+
if result.size == 0:
368+
raise ValueError(
369+
"The convolution operation resulted in an empty output. "
370+
"This can happen if the input is too small for the given "
371+
"kernel size, strides, dilation rate, and padding mode. "
372+
"Please check the input shape and convolution parameters."
373+
)
374+
return result
367375

368376

369377
def depthwise_conv(

keras/src/backend/jax/numpy.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,8 @@ def take(x, indices, axis=None):
11961196

11971197

11981198
def take_along_axis(x, indices, axis=None):
1199+
x = convert_to_tensor(x)
1200+
indices = convert_to_tensor(indices, sparse=False)
11991201
return jnp.take_along_axis(x, indices, axis=axis)
12001202

12011203

keras/src/backend/numpy/nn.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ def conv(
404404
f"kernel in_channels {kernel_in_channels}. "
405405
)
406406
feature_group_count = channels // kernel_in_channels
407-
return np.array(
407+
result = np.array(
408408
jax.lax.conv_general_dilated(
409409
inputs,
410410
kernel if is_tensor(kernel) else kernel.numpy(),
@@ -415,6 +415,14 @@ def conv(
415415
feature_group_count=feature_group_count,
416416
)
417417
)
418+
if result.size == 0:
419+
raise ValueError(
420+
"The convolution operation resulted in an empty output. "
421+
"This can happen if the input is too small for the given "
422+
"kernel size, strides, dilation rate, and padding mode. "
423+
"Please check the input shape and convolution parameters."
424+
)
425+
return result
418426

419427

420428
def depthwise_conv(

keras/src/backend/openvino/excluded_concrete_tests.txt

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ NumpyDtypeTest::test_correlate
1919
NumpyDtypeTest::test_cross
2020
NumpyDtypeTest::test_cumprod
2121
NumpyDtypeTest::test_diag
22-
NumpyDtypeTest::test_digitize
2322
NumpyDtypeTest::test_einsum
2423
NumpyDtypeTest::test_exp2
2524
NumpyDtypeTest::test_flip
@@ -76,13 +75,6 @@ NumpyOneInputOpsCorrectnessTest::test_floor_divide
7675
NumpyOneInputOpsCorrectnessTest::test_imag
7776
NumpyOneInputOpsCorrectnessTest::test_isreal
7877
NumpyOneInputOpsCorrectnessTest::test_logaddexp2
79-
NumpyOneInputOpsCorrectnessTest::test_pad_float16_constant_2
80-
NumpyOneInputOpsCorrectnessTest::test_pad_float32_constant_2
81-
NumpyOneInputOpsCorrectnessTest::test_pad_float64_constant_2
82-
NumpyOneInputOpsCorrectnessTest::test_pad_int16_constant_2
83-
NumpyOneInputOpsCorrectnessTest::test_pad_int8_constant_2
84-
NumpyOneInputOpsCorrectnessTest::test_pad_uint8_constant_2
85-
NumpyOneInputOpsCorrectnessTest::test_pad_int32_constant_2
8678
NumpyOneInputOpsCorrectnessTest::test_real
8779
NumpyOneInputOpsCorrectnessTest::test_reshape
8880
NumpyOneInputOpsCorrectnessTest::test_roll
@@ -92,15 +84,12 @@ NumpyOneInputOpsCorrectnessTest::test_select
9284
NumpyOneInputOpsCorrectnessTest::test_signbit
9385
NumpyOneInputOpsCorrectnessTest::test_size
9486
NumpyOneInputOpsCorrectnessTest::test_slogdet
95-
NumpyOneInputOpsCorrectnessTest::test_std
9687
NumpyOneInputOpsCorrectnessTest::test_swapaxes
9788
NumpyOneInputOpsCorrectnessTest::test_tile
9889
NumpyOneInputOpsCorrectnessTest::test_trace
99-
NumpyOneInputOpsCorrectnessTest::test_transpose
10090
NumpyOneInputOpsCorrectnessTest::test_trapezoid
10191
NumpyOneInputOpsCorrectnessTest::test_trunc
10292
NumpyOneInputOpsCorrectnessTest::test_unravel_index
103-
NumpyOneInputOpsCorrectnessTest::test_var
10493
NumpyOneInputOpsCorrectnessTest::test_vectorize
10594
NumpyOneInputOpsCorrectnessTest::test_vstack
10695
NumpyOneInputOpsCorrectnessTest::test_view
@@ -110,11 +99,9 @@ NumpyTwoInputOpsCorrectnessTest::test_bitwise_or
11099
NumpyTwoInputOpsCorrectnessTest::test_bitwise_right_shift
111100
NumpyTwoInputOpsCorrectnessTest::test_bitwise_xor
112101
NumpyTwoInputOpsCorrectnessTest::test_cross
113-
NumpyTwoInputOpsCorrectnessTest::test_digitize
114102
NumpyTwoInputOpsCorrectnessTest::test_divide_no_nan
115103
NumpyTwoInputOpsCorrectnessTest::test_einsum
116104
NumpyTwoInputOpsCorrectnessTest::test_gcd
117-
NumpyTwoInputOpsCorrectnessTest::test_heaviside
118105
NumpyTwoInputOpsCorrectnessTest::test_hypot
119106
NumpyTwoInputOpsCorrectnessTest::test_inner
120107
NumpyTwoInputOpsCorrectnessTest::test_isin
@@ -130,23 +117,19 @@ NumpyOneInputOpsDynamicShapeTest::test_cbrt
130117
NumpyOneInputOpsDynamicShapeTest::test_corrcoef
131118
NumpyOneInputOpsDynamicShapeTest::test_hamming
132119
NumpyOneInputOpsDynamicShapeTest::test_hanning
133-
NumpyOneInputOpsDynamicShapeTest::test_isposinf
134120
NumpyOneInputOpsDynamicShapeTest::test_isreal
135121
NumpyOneInputOpsDynamicShapeTest::test_kaiser
136122
NumpyOneInputOpsDynamicShapeTest::test_view
137123
NumpyOneInputOpsStaticShapeTest::test_angle
138124
NumpyOneInputOpsStaticShapeTest::test_cbrt
139-
NumpyOneInputOpsStaticShapeTest::test_isposinf
140125
NumpyOneInputOpsStaticShapeTest::test_isreal
141126
NumpyOneInputOpsStaticShapeTest::test_view
142127
NumpyTwoInputOpsDynamicShapeTest::test_gcd
143-
NumpyTwoInputOpsDynamicShapeTest::test_heaviside
144128
NumpyTwoInputOpsDynamicShapeTest::test_hypot
145129
NumpyTwoInputOpsDynamicShapeTest::test_isin
146130
NumpyTwoInputOpsDynamicShapeTest::test_kron
147131
NumpyTwoInputOpsDynamicShapeTest::test_lcm
148132
NumpyTwoInputOpsStaticShapeTest::test_gcd
149-
NumpyTwoInputOpsStaticShapeTest::test_heaviside
150133
NumpyTwoInputOpsStaticShapeTest::test_hypot
151134
NumpyTwoInputOpsStaticShapeTest::test_isin
152135
NumpyTwoInputOpsStaticShapeTest::test_kron

keras/src/backend/openvino/numpy.py

Lines changed: 62 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -557,9 +557,21 @@ def hamming(x):
557557

558558

559559
def heaviside(x1, x2):
560-
raise NotImplementedError(
561-
"`heaviside` is not supported with openvino backend"
562-
)
560+
x1 = get_ov_output(x1)
561+
x_type = x1.get_element_type()
562+
x2 = get_ov_output(x2, x_type)
563+
564+
zero_scalar = ov_opset.constant(0, x_type).output(0)
565+
one_scalar = ov_opset.constant(1, x_type).output(0)
566+
567+
neg = ov_opset.less(x1, zero_scalar).output(0)
568+
pos = ov_opset.greater(x1, zero_scalar).output(0)
569+
eq = ov_opset.equal(x1, zero_scalar).output(0)
570+
571+
x = ov_opset.select(neg, zero_scalar, x1).output(0)
572+
x = ov_opset.select(pos, one_scalar, x).output(0)
573+
x = ov_opset.select(eq, x2, x).output(0)
574+
return OpenVINOKerasTensor(x)
563575

564576

565577
def kaiser(x, beta):
@@ -699,15 +711,9 @@ def count_nonzero(x, axis=None):
699711
zero_constant = ov_opset.convert_like(zero_constant, x)
700712
x = ov_opset.not_equal(x, zero_constant).output(0)
701713
x = ov_opset.convert(x, Type.i32).output(0)
702-
if axis is None:
703-
flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
704-
x = ov_opset.reshape(x, flatten_shape, False).output(0)
705-
axis = 0
706-
if isinstance(axis, tuple):
707-
axis = list(axis)
708-
if axis == []:
714+
x, axis = _resolve_axis(x, axis)
715+
if not axis:
709716
return OpenVINOKerasTensor(x)
710-
axis = ov_opset.constant(axis, Type.i32).output(0)
711717
return OpenVINOKerasTensor(ov_opset.reduce_sum(x, axis, False).output(0))
712718

713719

@@ -726,11 +732,7 @@ def cumsum(x, axis=None, dtype=None):
726732
if dtype is not None:
727733
ov_type = OPENVINO_DTYPES[standardize_dtype(dtype)]
728734
x = ov_opset.convert(x, ov_type).output(0)
729-
if axis is None:
730-
flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
731-
x = ov_opset.reshape(x, flatten_shape, False).output(0)
732-
axis = 0
733-
axis = ov_opset.constant(axis, Type.i32).output(0)
735+
x, axis = _resolve_axis(x, axis)
734736
if x.get_element_type() == Type.boolean:
735737
x = ov_opset.convert(x, Type.i32).output(0)
736738
return OpenVINOKerasTensor(ov_opset.cumsum(x, axis).output(0))
@@ -841,9 +843,30 @@ def diff(a, n=1, axis=-1):
841843

842844

843845
def digitize(x, bins):
844-
raise NotImplementedError(
845-
"`digitize` is not supported with openvino backend"
846-
)
846+
x_node = get_ov_output(x)
847+
848+
if isinstance(bins, OpenVINOKerasTensor):
849+
bins_node = get_ov_output(bins)
850+
else:
851+
bins_np = np.asarray(bins)
852+
if bins_np.ndim != 1:
853+
raise ValueError("`bins` must be 1-D array-like")
854+
bins_node = ov_opset.constant(bins_np).output(0)
855+
856+
x_node, bins_node = _align_operand_types(x_node, bins_node, "digitize()")
857+
858+
if x_node.get_element_type() == Type.boolean:
859+
x_node = ov_opset.convert(x_node, Type.f32).output(0)
860+
bins_node = ov_opset.convert(bins_node, Type.f32).output(0)
861+
862+
result = ov_opset.bucketize(
863+
x_node,
864+
bins_node,
865+
output_type=Type.i32,
866+
with_right_bound=False,
867+
).output(0)
868+
869+
return OpenVINOKerasTensor(result)
847870

848871

849872
def dot(x1, x2):
@@ -1765,7 +1788,9 @@ def pad(x, pad_width, mode="constant", constant_values=None):
17651788
"`pad` operation supports only scalar pad value "
17661789
"in constant mode by openvino backend"
17671790
)
1768-
pad_value = constant_values
1791+
pad_value = ov_opset.constant(
1792+
constant_values, x.get_element_type()
1793+
).output(0)
17691794

17701795
# split pad_width into two tensors pads_begin and pads_end
17711796
pads_begin = []
@@ -2061,22 +2086,9 @@ def stack(x, axis=0):
20612086

20622087

20632088
def std(x, axis=None, keepdims=False):
2064-
x = get_ov_output(x)
2065-
if axis is None:
2066-
flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
2067-
x = ov_opset.reshape(x, flatten_shape, False).output(0)
2068-
axis = 0
2069-
axis = ov_opset.constant(axis, Type.i32).output(0)
2070-
# The variance is computed using $Var = E[|x|^2] - |E[x]|^2$, It is faster
2071-
# but less numerically stable.
2072-
mean = ov_opset.reduce_mean(x, axis, keepdims).output(0)
2073-
const_two = ov_opset.constant(2, x.get_element_type()).output(0)
2074-
squared_x = ov_opset.power(x, const_two).output(0)
2075-
squared_mean = ov_opset.power(mean, const_two).output(0)
2076-
squared_x_mean = ov_opset.reduce_mean(squared_x, axis, keepdims)
2077-
variance = ov_opset.subtract(squared_x_mean, squared_mean).output(0)
2078-
std_var = OpenVINOKerasTensor(ov_opset.sqrt(variance).output(0))
2079-
return std_var
2089+
var_x = var(x, axis, keepdims)
2090+
std_dev = ov_opset.sqrt(var_x).output(0)
2091+
return OpenVINOKerasTensor(std_dev)
20802092

20812093

20822094
def swapaxes(x, axis1, axis2):
@@ -2441,18 +2453,26 @@ def trapezoid(y, x=None, dx=1.0, axis=-1):
24412453

24422454
def var(x, axis=None, keepdims=False):
24432455
x = get_ov_output(x)
2456+
x_type = x.get_element_type()
2457+
x, axis = _resolve_axis(x, axis)
2458+
2459+
work_dtype = Type.f64 if x_type.is_integral() else x.get_element_type()
2460+
if x_type.is_integral():
2461+
x = ov_opset.convert(x, work_dtype).output(0)
24442462
if axis is None:
2445-
flatten_shape = ov_opset.constant([-1], Type.i32).output(0)
2446-
x = ov_opset.reshape(x, flatten_shape, False).output(0)
2447-
axis = 0
2448-
axis = ov_opset.constant(axis, Type.i32).output(0)
2463+
const_zero = ov_opset.constant(0, dtype=work_dtype).output(0)
2464+
return OpenVINOKerasTensor(
2465+
ov_opset.broadcast(const_zero, ov_opset.shape_of(x)).output(0)
2466+
)
24492467
# The variance is computed using $Var = E[|x|^2] - |E[x]|^2$, It is faster
24502468
# but less numerically stable.
24512469
mean = ov_opset.reduce_mean(x, axis, keepdims).output(0)
2452-
const_two = ov_opset.constant(2, x.get_element_type()).output(0)
2470+
const_two = ov_opset.constant(2, work_dtype).output(0)
2471+
24532472
squared_x = ov_opset.power(x, const_two).output(0)
24542473
squared_mean = ov_opset.power(mean, const_two).output(0)
2455-
squared_x_mean = ov_opset.reduce_mean(squared_x, axis, keepdims)
2474+
2475+
squared_x_mean = ov_opset.reduce_mean(squared_x, axis, keepdims).output(0)
24562476
variance = OpenVINOKerasTensor(
24572477
ov_opset.subtract(squared_x_mean, squared_mean).output(0)
24582478
)

keras/src/backend/tensorflow/nn.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,14 +310,28 @@ def conv(
310310
):
311311
def _conv():
312312
tf_data_format = _convert_data_format(data_format, len(inputs.shape))
313-
return tf.nn.convolution(
313+
result = tf.nn.convolution(
314314
inputs,
315315
kernel,
316316
strides,
317317
padding.upper(),
318318
data_format=tf_data_format,
319319
dilations=dilation_rate,
320320
)
321+
result_shape = result.shape
322+
if (
323+
result_shape.is_fully_defined()
324+
and math.prod(result_shape.as_list()) == 0
325+
):
326+
raise ValueError(
327+
"The convolution operation resulted in an empty output. "
328+
"Output shape:"
329+
f" {result_shape}. This can happen if the input is too small "
330+
"for the given kernel size, strides, dilation rate, and "
331+
"padding mode. Please check the input shape and convolution "
332+
"parameters."
333+
)
334+
return result
321335

322336
# Certain ops are are broken in Tensorflow on CPU only.
323337
# We can work around by compiling the op with XLA.

0 commit comments

Comments
 (0)