-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
Fix local variables being collected into module_arguments dict #2048
Conversation
Codecov Report
@@ Coverage Diff @@
## master #2048 +/- ##
======================================
Coverage 86% 86%
======================================
Files 75 75
Lines 4705 4709 +4
======================================
+ Hits 4064 4068 +4
Misses 641 641 |
kindly asking @yukw777 to also have a look at this since you were involved in the hparams PR. |
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.
`some questions, but otherwise looks good to me :)
frame = inspect.currentframe() | ||
|
||
frame_args = _collect_init_args(frame.f_back, []) | ||
child = _get_latest_child(frame) |
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.
Wasn't there a reason with inheritance for 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.
I tested it and I found that child is always the specific type, i.e. "self", even if self.auto_collect() is called in a super class.
So in a concrete example:
class A(object):
def __init__(self, *args, **kwargs):
print(self)
class B(A):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
print(self)
B() # prints twice <__main__.B object at 0x0000021540C94288>
so really it does not matter where we collect "self", it will always be the child (leaf node) in the inheritance tree.
The previous inheritance tests still pass.
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.
The child = _get_latest_child(frame)
does not break anything, it's just redudant as far as I can tell.
varargs_identifier = spec.varargs # by convention this is named "*args" | ||
kwargs_identifier = spec.varkw # by convention this is named "**kwargs" | ||
exclude_argnames = ( | ||
varargs_identifier, kwargs_identifier, self_identifier, '__class__', 'frame', 'frame_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.
why also ignore *args
and **kwargs
?
Shouldn't we collect them as well and use them for reinstantiation/loading? Otherwise the module might be instantiated with different params which could cause incompatibilities with module and loaded checkpoint.
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.
Because if we have this class
class B(object):
def __init__(self, *args, **kwargs):
super().__init__()
something = kwargs.get('something')
self.auto_collect_arguments()
and we call it like this
B(something=1, other=2)
then we want to module arguments to be
dict(something=1, other=2)
and not
dict(args=[], kwargs={something=1, other=2})
It was like this before, this has not changed. The code that I added there just makes it so that the name *args and **kwargs is not hardcoded, but can be named whatever the user wants.
The Pyhton inspection magic makes it possible to determine which is which.
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.
That's the explanation why the ignore, and the second part of your question:
The args inside **kwargs are still collected, see this line
local_args.update(local_args.get('kwargs', {}))
the same as here #2039 (comment) |
I don't have any huge concerns about this. I do worry |
by a long way you mean it's not ready yet? |
This pull request is now in conflict... :( |
@awaelchli @Borda good effort on tackling this! I propose the final API for init interactions is as follows: case 1User explicitly says what they want to save. class LitModel(...):
def __init__(self, conf):
self.save_hyperparameters(conf) Case 2:User wants to save all the init stuff. class LitModel(...):
def __init__(self, arg1, arg2, arg3):
# manually
self.save_hyperparameters(arg_name=arg1, arg_name=arg2, arg_name=arg3)
# equivalent automatic
self.save_hyperparameters() Case 3:They want to save ONLY some of the init stuff class LitModel(...):
def __init__(self, arg1, arg2, arg3):
# manually
self.save_hyperparameters(arg_name=arg2) Special cases:
def __init__(self, hparams):
# manually
self.save_hyperparameters(hparams)
def __init__(self, some_dict):
# manually
self.save_hyperparameters(some_dict)
def __init__(self, conf):
# manually
self.save_hyperparameters(conf)
def __init__(self, some_random_alternative_to_config):
# manually
self.save_hyperparameters(some_random_alternative_to_config) @PyTorchLightning/core-contributors |
@awaelchli nah i just meant that as a general observation, not specific to this PR, which looks good to me! |
8dc4650
to
79a6023
Compare
the API changes will be applied in #2047 |
* do not include local vars in auto collection * add test * add test for model with "self" renamed to "obj" * skip decorator * changelog * changelog * update docs * remove obsolete child collection * generalize **args, **kwargs names * docs * also update varargs passed in * Revert "also update varargs passed in" This reverts commit 3d7a30d. * update test
Before submitting
What does this PR do?
Fixes issues 6 and 7 in #1937
The new tests that I added fail on master, proofing that the this PR actually fixes the issue.
I will follow up with another PR that will add even more tests for things like multiple inheritance etc.