-
Notifications
You must be signed in to change notification settings - Fork 630
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
Conversation
Signed-off-by: ksztenderski <ksztenderski@nvidia.com>
Signed-off-by: ksztenderski <ksztenderski@nvidia.com>
Signed-off-by: ksztenderski <ksztenderski@nvidia.com>
!build |
CI MESSAGE: [5199224]: BUILD STARTED |
CI MESSAGE: [5199224]: BUILD PASSED |
There was a problem hiding this 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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
if op_schema.IsDeprecated() or op_name in _excluded_operators: | ||
return |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
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(): |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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>
!build |
CI MESSAGE: [5211570]: BUILD STARTED |
CI MESSAGE: [5211570]: BUILD PASSED |
Signed-off-by: ksztenderski <ksztenderski@nvidia.com>
!build |
CI MESSAGE: [5215548]: BUILD STARTED |
CI MESSAGE: [5215548]: BUILD FAILED |
CI MESSAGE: [5215548]: BUILD PASSED |
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 ofeager.rng_state
.Example usage:
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:
Checklist
Documentation
DALI team only
Requirements
REQ IDs: N/A
JIRA TASK: N/A