Skip to content

[OpenVINO Backend] : add support for numpy.nan_to_num #21186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

11happy
Copy link
Contributor

@11happy 11happy commented Apr 18, 2025

Overview:

Testing:

  • Currently passsing all tests with this approach but having issues with isinf approach . I have mentioned in detail in comments below.

CC:

@11happy
Copy link
Contributor Author

11happy commented Apr 18, 2025

current approach is passing all tests but when I use is_inf to create masks for neg_inf & pos_inf somehow i think it is not able to create those masks properly any insights on that is appreciated.

previous approach to create masks:

posinf_mask = ov_opset.is_inf(x,{'detect_negative': False,'detect_positive': True}).output(0)
neginf_mask = ov_opset.is_inf(x,{'detect_negative': True,'detect_positive': False}).output(0)

error faced(infs are not detected here I checked them ):
Screenshot from 2025-04-18 23-22-34

Thank you

@11happy
Copy link
Contributor Author

11happy commented Apr 18, 2025

as numpy documentation mentions to use dtype max I create new dictionaries is there any other approach to directly get dtype maxes & mins please let me know.
Thank you
image

@codecov-commenter
Copy link

codecov-commenter commented Apr 18, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 82.61%. Comparing base (50dae30) to head (1ed1b37).
Report is 18 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #21186      +/-   ##
==========================================
+ Coverage   82.59%   82.61%   +0.02%     
==========================================
  Files         564      564              
  Lines       54407    54474      +67     
  Branches     8449     8469      +20     
==========================================
+ Hits        44936    45003      +67     
+ Misses       7396     7395       -1     
- Partials     2075     2076       +1     
Flag Coverage Δ
keras 82.42% <100.00%> (+0.02%) ⬆️
keras-jax 63.71% <0.00%> (-0.10%) ⬇️
keras-numpy 58.83% <0.00%> (-0.10%) ⬇️
keras-openvino 32.99% <100.00%> (+0.04%) ⬆️
keras-tensorflow 64.11% <0.00%> (-0.11%) ⬇️
keras-torch 63.79% <0.00%> (-0.11%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Signed-off-by: 11happy <[email protected]>
@@ -96,7 +96,6 @@ NumpyOneInputOpsCorrectnessTest::test_median
NumpyOneInputOpsCorrectnessTest::test_meshgrid
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please also remove NumpyDtypeTest::test_nan

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 1038 to 1040
nan_vector = ov_opset.broadcast(
ov_opset.constant(nan, dtype), shape_x
).output(0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it can be just a scalar. select operation will broadcast it automatically. No need broadcast by yourself.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same comment for pos_inf and neg_inf:)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@rkazants
Copy link
Contributor

current approach is passing all tests but when I use is_inf to create masks for neg_inf & pos_inf somehow i think it is not able to create those masks properly any insights on that is appreciated.

previous approach to create masks:

posinf_mask = ov_opset.is_inf(x,{'detect_negative': False,'detect_positive': True}).output(0)
neginf_mask = ov_opset.is_inf(x,{'detect_negative': True,'detect_positive': False}).output(0)

error faced(infs are not detected here I checked them ): Screenshot from 2025-04-18 23-22-34

Thank you

Can you please create standalone code that demonstrates this issue and create issue in openvino github? It will be greatly to fix it on our side.

Best regards,
Roman

@11happy
Copy link
Contributor Author

11happy commented Apr 19, 2025

Can you please create standalone code that demonstrates this issue and create issue in openvino github? It will be greatly to fix it on our side.

Best regards, Roman

Sure I will create a new issue there , also I have correctec the code based on new tests & previous reviews.
Thank you

@11happy
Copy link
Contributor Author

11happy commented Apr 19, 2025

when I tried to reproduce the same error in a colab
image
now the is_inf are detecting it properly however still the error persists when I try to do it in numpy.nan_to_num implementation .. not sure why, but am debugging it , any insights on this is appreciated.
Thank you

@rkazants
Copy link
Contributor

when I tried to reproduce the same error in a colab image now the is_inf are detecting it properly however still the error persists when I try to do it in numpy.nan_to_num implementation .. not sure why, but am debugging it , any insights on this is appreciated. Thank you

try to create constant node instead of parameter, i.e. no params and one result node.

Signed-off-by: 11happy <[email protected]>
@11happy
Copy link
Contributor Author

11happy commented Apr 19, 2025

in is_inf we need to send the input by convert it to float inputs then only it works.

I first tried running the tests for is_inf by excluding it & saw they were also failing

def isinf(x):
    x = get_ov_output(x)
    return OpenVINOKerasTensor(ov_opset.is_inf(x).output(0)) 

correct usage would be & this change passes all tests same thing goes for is_finite I cheecked that as well.

def isinf(x):
    x = get_ov_output(x)
    return OpenVINOKerasTensor(ov_opset.is_inf(ov_opset.convert(x, Type.f32)).output(0)) 

also on colab it was always correctly workin also with constant node & single result with float dtype. but when I changed dtype to int they started failing & giving error
image

I suppose it is intentional to pass them as float
Thank you

Copy link
Contributor

@rkazants rkazants left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fchollet, looks good to me. Recommend to merge.

neginf if neginf is not None else DTYPES_MIN[dtype], dtype
).output(0)
posinf_mask = ov_opset.is_inf(
ov_opset.convert(x, Type.f32),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably it is a bug on OpenVINO side. Please create issue for non-f32 types in OpenVINO repo. For now, this WA absolutely works for us

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done , I have created the issue.
Thank you

x = get_ov_output(x)
dtype = x.get_element_type()
nan_val = ov_opset.constant(nan, dtype).output(0)
posinf_val = ov_opset.constant(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@11happy, let us do not call is_inf and is_nan for integer types. You can return input as is as I understand.
For floating point types you need to call but convert to fp32 is not needed.
Can you fix it please in this PR?

Copy link
Contributor Author

@11happy 11happy Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a check for int type & early retunr & removed the dtype conversion to float ,

if dtype.is_integral():
        return OpenVINOKerasTensor(x)

removed this ov_opset.convert(x, Type.f32)) conversion from is_inf, with these changes test is failing , it is not able to detect the infs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@11happy, please clarify what cases exactly are failing. For int types, it should pass because there is no inf and nan values.

Copy link
Contributor Author

@11happy 11happy Apr 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so the test case that is failing is previous one only

>       self.assertAllClose(
            knp.nan_to_num(x, nan=2, posinf=3, neginf=4), [1.0, 2.0, 3.0, 4.0]
        )

keras/src/ops/numpy_test.py:4806: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
keras/src/testing/test_case.py:48: in assertAllClose
    np.testing.assert_allclose(x1, x2, atol=atol, rtol=rtol, err_msg=msg)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<function assert_allclose.<locals>.compare at 0x722684c8ed40>, array([ 1.00000000e+00,  2.00000000e+00,  3.40282347e+38, -3.40282347e+38]), array([1., 2., 3., 4.]))
kwds = {'equal_nan': True, 'err_msg': 'None', 'header': 'Not equal to tolerance rtol=1e-06, atol=1e-06', 'verbose': True}

    @wraps(func)
    def inner(*args, **kwds):
        with self._recreate_cm():
>           return func(*args, **kwds)
E           AssertionError: 
E           Not equal to tolerance rtol=1e-06, atol=1e-06
E           None
E           Mismatched elements: 2 / 4 (50%)
E           Max absolute difference: 3.40282347e+38
E           Max relative difference: 1.13427449e+38
E            x: array([ 1.000000e+00,  2.000000e+00,  3.402823e+38, -3.402823e+38])
E            y: array([1., 2., 3., 4.])

/usr/lib/python3.12/contextlib.py:81: AssertionError

I tried to debug it , my observations:
this is failing for f64 dtype, here the test case with dtype f64 is failing , also in conversion logic if I intentionally convert it to f64 type instead of f32 the above test fails. here specifically it passes with f16,f32, but fails with f64.

I am also attaching a ss of PDB :
Screenshot from 2025-04-25 15-36-00

Copy link
Contributor Author

@11happy 11happy Apr 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have now a reproducible code on f64 dtype for the same error
I have posted that on the previous ticket openvinotoolkit/openvino#30264

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, can you please do the implementation as follows:

  1. return input as is for integer types
  2. use is_inf and is_nan mask for floating point types. In addition, use WA with convert for fp64, for other types it is not needed.
    Best regards,
    Roman

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have also added a comment referencing the issue

Signed-off-by: 11happy <[email protected]>
Copy link
Contributor

@rkazants rkazants left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fchollet, looks good to me. Recommend to merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Good First Issue][Keras 3 OpenVINO Backend]: Support numpy.nan_to_num operation
4 participants