Skip to content
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

Add eager mode stateful operators #4016

Merged
merged 6 commits into from
Jun 30, 2022

Conversation

ksztenderski
Copy link
Contributor

@ksztenderski ksztenderski commented Jun 28, 2022

Category:

New feature (non-breaking change which adds functionality)

Description:

Add experimental exposure of eager.rng_state. All operators that are dependent on a state (excluding readers) are methods of eager.rng_state.
Example usage:

import nvidia.dali.experimental.eager as eager

eager_state = eager.rng_state(seed=27)
tl = TensorListCPU(np.zeros((8, 320, 320, 3), dtype=np.uint8))
out = rng.noise.gaussian(tl)
print(out)

Additionally adds a function for exposing eager operators as objects if we ever want to switch to the ops-like API.

Additional information:

Affected modules and functionalities:

Eager mode

Key points relevant for the review:

Tests:

  • Existing tests apply
  • New tests added
    • Python tests
    • GTests
    • Benchmark
    • Other
  • N/A

Checklist

Documentation

  • Existing documentation applies
  • Documentation updated
    • Docstring
    • Doxygen
    • RST
    • Jupyter
    • Other
  • N/A

DALI team only

Requirements

  • Implements new requirements
  • Affects existing requirements
  • N/A

REQ IDs: N/A

JIRA TASK: N/A

Signed-off-by: ksztenderski <ksztenderski@nvidia.com>
Signed-off-by: ksztenderski <ksztenderski@nvidia.com>
Signed-off-by: ksztenderski <ksztenderski@nvidia.com>
@ksztenderski
Copy link
Contributor Author

!build

@dali-automaton
Copy link
Collaborator

CI MESSAGE: [5199224]: BUILD STARTED

@dali-automaton
Copy link
Collaborator

CI MESSAGE: [5199224]: BUILD PASSED

Copy link
Contributor

@klecki klecki left a comment

Choose a reason for hiding this comment

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

Mostly nitpick and docstring requests. Otherwise looks ok.

if not isinstance(out_eager, (tuple, list)):
out_eager = (out_eager,)
def prep_stateful_operators(op_path):
seed = rng.integers(2048)
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add a comment that we are replicating the seed that would be used in the eager op internal for the basline op in the pipeline.

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

@@ -131,14 +131,14 @@ def eager_source(self, i, layout='HWC'):
return get_tl(np.array(self.fn_source(i)), layout)
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess for the rng_state we should probably get a test that interleaves operations on the same op with different scalar arguments (so two different instances) and maybe two different rng_state objects (same/distinct seed) and check if they have equal/not equal results as we expect.

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 755 to 756
if op_schema.IsDeprecated() or op_name in _excluded_operators:
return
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should start with the simple checks that return us out of this function, so we get them out of the way - I would start with this.

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 747 to 751
last_module = eager.rng_state
for cur_module_name in submodule:
# If nonexistent registers rng_state's submodule.
cur_module = last_module._submodule(cur_module_name)
last_module = cur_module
Copy link
Contributor

Choose a reason for hiding this comment

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

Just a suggestion, feel free to ignore, but maybe we should have
get_stateful_target_module and get_stateless_target_module or something, and than we can have a neat if/else chain that checks the kind of op, prepares the wrapper and the target module to insert the wrapper into?

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

return backend_op


def _eager_op_object_factory(op_class, op_name):
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this could be grouped and marked with the _expose part as unused and with the purpose described. Maybe make it a section with a "block" comment, or a file?

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 grouped it together, and added comments about it being unused. I don't know about moving it to a separate file, I think it's fine like this.


class rng_state(_create_module_class()):
""" Manager class for stateful operators. Methods of this class correspond to the appropriate
functions in the fn API, they are created by :func:`_wrap_stateful` and are added dynamically.
Copy link
Contributor

Choose a reason for hiding this comment

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

they are created by :func:_wrap_stateful and are added dynamically. - this part should probably be a comment, and the docstring for the class or for the __init__ could add some info about the seeds, and some rough sketch of what this state promises.

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

key = op_name + _desc_call_args(inputs, call_args) + str(sorted(init_args.items()))

if key not in self._operator_cache:
seed = self._seed_generator.integers(2**32)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the tricky part here, can you add a comment why do we create seeds this way and what do we cache (that key - so the scalar arguments and the input dim/type maps to a distinct seed).

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

@klecki klecki assigned awolant and unassigned mzient Jun 29, 2022
Fixes mixed device eager operators and gpu arithmetic operators.

Signed-off-by: ksztenderski <ksztenderski@nvidia.com>
@@ -549,7 +550,7 @@ def __init__(self, exec_func, **kwargs):
import numpy as np
seed = kwargs.get('seed', -1)
if seed < 0:
seed = np.random.randint(0, 2**32)
seed = np.random.randint(sys.maxsize)
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, wouldn't this be platform dependent? Maybe used iinfo and fixed sized type?

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 set a fixed value at (1 << 31) - 1

@@ -212,7 +212,11 @@ def _arithm_op(name, *inputs):
categories_idxs, inputs, integers, reals = _ops._group_inputs(
inputs, edge_type=(_tensors.TensorListCPU, _tensors.TensorListGPU))
input_desc = _ops._generate_input_desc(categories_idxs, integers, reals)
device = _ops._choose_device(inputs)

if any(isinstance(input, _tensors.TensorListGPU) for input in inputs):
Copy link
Contributor

Choose a reason for hiding this comment

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

Here the inputs are already normalized to TensorListCPU/GPU, correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes

@@ -647,6 +668,13 @@ def _desc_call_args(inputs, args):
[(key, value.dtype, value.layout(), len(value[0].shape())) for key, value in args.items()]))


def _gen_cache_key(op_name, inputs, init_args, call_args):
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

assert np.allclose(out_1_2.as_tensor(), out_2_2.as_tensor())


def test_objective_eager_resize():
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add a comment that this tests the hidden functionality of exposing the Eager Ops classes.

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

Signed-off-by: ksztenderski <ksztenderski@nvidia.com>
@ksztenderski
Copy link
Contributor Author

!build

@dali-automaton
Copy link
Collaborator

CI MESSAGE: [5211570]: BUILD STARTED

@dali-automaton
Copy link
Collaborator

CI MESSAGE: [5211570]: BUILD PASSED

Signed-off-by: ksztenderski <ksztenderski@nvidia.com>
@ksztenderski
Copy link
Contributor Author

!build

@dali-automaton
Copy link
Collaborator

CI MESSAGE: [5215548]: BUILD STARTED

@dali-automaton
Copy link
Collaborator

CI MESSAGE: [5215548]: BUILD FAILED

@dali-automaton
Copy link
Collaborator

CI MESSAGE: [5215548]: BUILD PASSED

@ksztenderski ksztenderski merged commit 752e770 into NVIDIA:main Jun 30, 2022
@JanuszL JanuszL mentioned this pull request Jan 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants