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

[JIT] Add API for ignoring arbitrary module attributes #45262

Closed

Conversation

SplitInfinity
Copy link

@SplitInfinity SplitInfinity commented Sep 24, 2020

Stack from ghstack:

Summary
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named ignored_attributes containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
forward, methods used by forward or by @exported methods. They
are, however, copied to the RecursiveScriptModule wrapper and can be
used by @ignored methods and regular Python code.

Test Plan
This commit adds unit tests to TestScriptPy3 to test this new API.

Differential Revision: D23971882

**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

[ghstack-poisoned]
SplitInfinity pushed a commit that referenced this pull request Sep 24, 2020
**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

ghstack-source-id: b9fcadc0f56ffe932250905f1c31743ef9de9a0c
Pull Request resolved: #45262
@facebook-github-bot facebook-github-bot added the oncall: jit Add this issue/PR to JIT oncall triage queue label Sep 24, 2020
@dr-ci
Copy link

dr-ci bot commented Sep 24, 2020

💊 CI failures summary and remediations

As of commit ded8d28 (more details on the Dr. CI page):


Commit ded8d28 was recently pushed. Waiting for builds...


This comment was automatically generated by Dr. CI (expand for details).Follow this link to opt-out of these comments for your Pull Requests.

Please report bugs/suggestions on the GitHub issue tracker or post in the (internal) Dr. CI Users group.

See how this bot performed.

This comment has been revised 68 times.

**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

[ghstack-poisoned]
**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

[ghstack-poisoned]
SplitInfinity pushed a commit that referenced this pull request Sep 24, 2020
**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

ghstack-source-id: 7f7e38a6ab760e5f7cf1195dbca468eb80d5d6a4
Pull Request resolved: #45262
Copy link
Contributor

@eellison eellison left a comment

Choose a reason for hiding this comment

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

Cool! This is definitely needed as we're adding compilation of a few types of module attributes that we didn't previously (properties, enums, class types).

I guess there's some question about __unused__ vs __ignored__ - I think ignore'd makes since here bc we're attaching it to the model but maybe not.

We could potentially allow attribute access in scripted methods but probably not worth the effort for this PR.

Bikeshedding: wdyt of __jit_ignored__attributes__ ? __constants__ doesnt explicitly mention jit, but I think maybe it'd be clearer if it did.

ping @suo for another person to look at API.

@eellison eellison requested a review from suo September 24, 2020 22:32
**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

[ghstack-poisoned]
SplitInfinity pushed a commit that referenced this pull request Sep 25, 2020
**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

ghstack-source-id: 62642931355508cc7e76ff6b15e1225e8bfa0494
Pull Request resolved: #45262
@SplitInfinity
Copy link
Author

I like __jit_ignored_attributes__, but we'd have to change __ignored_properties__ to __jit_ignored_properties__ too. Not saying that's a problem but just something to keep in mind (so that I/we remember to do it).

**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

[ghstack-poisoned]
@eellison
Copy link
Contributor

We haven’t done a release yet so I think it’s good to get the best api even if we have to switch it once. We can also let the old api still work if we want

Meghan Lele added 5 commits September 26, 2020 11:09
**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

[ghstack-poisoned]
**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

[ghstack-poisoned]
**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

[ghstack-poisoned]
**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

[ghstack-poisoned]
**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

[ghstack-poisoned]
Copy link
Contributor

@eellison eellison left a comment

Choose a reason for hiding this comment

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

Conventionally, unused means that the function can be used in a script'd function and will error if it's being invoked but the model can still be exported. ignore means we retain the python function, it can be invoked in the module, but will error if export'd. Here we're retaining python attributes, but not allowing them to be invoked and still the model can be exported.

It might make more sense for this to be unused instead of ignored, although on the other hand we could just as easily extend __jit_ignored_attributes__ to allow accessing them in scripted functions.

I don't think this should block landing just something to think about.

@SplitInfinity
Copy link
Author

Conventionally, unused means that the function can be used in a script'd function and will error if it's being invoked but the model can still be exported. ignore means we retain the python function, it can be invoked in the module, but will error if export'd. Here we're retaining python attributes, but not allowing them to be invoked and still the model can be exported.

It might make more sense for this to be unused instead of ignored, although on the other hand we could just as easily extend __jit_ignored_attributes__ to allow accessing them in scripted functions.

I don't think this should block landing just something to think about.

Hm, but the ignored attributes can be used in ignored functions, so I think that means they can also be used in regular Python code too (I can add a test for that).

@codecov
Copy link

codecov bot commented Sep 29, 2020

Codecov Report

❗ No coverage uploaded for pull request base (gh/splitinfinity/53/base@6403714). Click here to learn what that means.
The diff coverage is n/a.

Impacted file tree graph

@@                     Coverage Diff                     @@
##             gh/splitinfinity/53/base   #45262   +/-   ##
===========================================================
  Coverage                            ?   68.20%           
===========================================================
  Files                               ?      410           
  Lines                               ?    53257           
  Branches                            ?        0           
===========================================================
  Hits                                ?    36323           
  Misses                              ?    16934           
  Partials                            ?        0           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6403714...ded8d28. Read the comment docs.

suo
suo previously requested changes Sep 29, 2020
@@ -123,6 +125,10 @@ def infer_type(name, item):
added_names = set()

for name, item in nn_module._parameters.items():
if name in user_annotated_ignored_attributes:
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we need to check user_annotated_ignored_attributes for each present attribute, we can just add it all to the concrete type right away, e.g.

concrete_type_builder.add_ignored_attributes(user_annotated_ignored_attributes)

The only difference would be that ignored attributes would be added even if they aren't present in the module object, but that won't make a difference in practice I think.

Copy link
Author

Choose a reason for hiding this comment

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

But we still need to skip adding attributes if they are ignored, right? So instead of

if name in user_annotated_ignored_attributes:
    concrete_type_builder.add_ignored_attribute(name)
    continue

we'd have

if name in user_annotated_ignored_attributes:
    continue

or

if {existing_condition} and name not in user_annotated_ignored_attributes:
     concrete_type_builder.add_xxx(...)

@@ -104,6 +104,8 @@ def infer_concrete_type_builder(nn_module, share_types=True):
concrete_type_builder.set_module_list()

class_annotations = getattr(nn_module, '__annotations__', {})
# Get user-annotated ignored attributes.
user_annotated_ignored_attributes = getattr(nn_module, "__jit_ignored_attributes__", set())
Copy link
Member

Choose a reason for hiding this comment

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

__torchscript_ignored__? JIT is a little overloaded.

Copy link
Author

Choose a reason for hiding this comment

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

:'( I just landed a change to name the equivalent class attribute for unused properties __jit_unused_properties__. I'm good with either, but I just want to make sure they're consistent. So I can either keep this or change both to __torchscript_ignored_xxx__.

@@ -186,6 +186,10 @@ c10::optional<std::string> ConcreteModuleType::findFailedAttribute(
return c10::nullopt;
}

bool ConcreteModuleType::isIgnoredAttribute(const std::string& name) const {
Copy link
Member

Choose a reason for hiding this comment

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

You need to edit ConcreteModuleTypeBuilder::equals so that this field gets used to disambiguate instances of a concrete type.

Copy link
Author

Choose a reason for hiding this comment

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

I added this but it's hard to test because ignoring an attribute changes the list of attributes on the ConcreteModuleTypeBuilder instance. The equality check between ConcreteModuleTypeBuilder instances (also ConcreteModuleType and Builder instances) fails when when an attribute is ignored even if there is no ignoredAttributes_ == other.ignoredAttributes_ condition added to ConcreteModuleTypeBuilder::equals because attributes_ == other.attributes_ is false .

I'm not saying we shouldn't have it, but I can't prove that it works.

Copy link
Contributor

Choose a reason for hiding this comment

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

As far as I understand, ConcreteMuduleType::equals is to disambiguate between different instantiations of an nn.Module which can result in a different type. __jit_ignored_attributes__ is a class-level attribute, I don't see how it could differ between different instantiations.

Copy link
Author

Choose a reason for hiding this comment

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

That's my understanding too, but as per one of my tests, the class attribute can be edited between invocations to torch.jit.script. I don't think it's a realistic use case, but it is possible. But as I mentioned in my earlier comment, unintended type sharing does not happen because modifying that class attribute changes which attributes are included/excluded in ConcreteModuleTypeBuilder.

Copy link
Member

Choose a reason for hiding this comment

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

It can be edited, or we can set it in the body of the __init__, e.g.

def __init__(self, ignored):
    self.__ignored__ = ignored

Both happen with some degree of regularity for other elements of ConcreteModuleType. But good point that it changes the available attributes, so checking ignored attrs is somewhat redundant. I think we should do it still for consistency, since all other attributes of ConcreteModuleType should participate in equality.

@@ -678,6 +678,56 @@ def attr(self):
with self.assertRaisesRegex(torch.nn.modules.module.ModuleAttributeError, "has no attribute"):
scripted_mod.ignored_attr

def test_ignoring_module_attributes(self):
Copy link
Member

Choose a reason for hiding this comment

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

  1. can we add tests to test_type_sharing.py to make sure we are not improperly sharing types with different ignored attributes sets?
  2. I think we should stop adding to test_jit_py3.py. This file originally existed because it let us use py3 syntax, but that no longer has a purpose now that we are py3 only.

Copy link
Author

Choose a reason for hiding this comment

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

  1. I added tests to test_type_sharing.py to test that types are shared if ignoring attrs make them equal and not shared if the ignored attributes list is modified and the types of modules before and after the modification should be different.
  2. Where should I move them?

Copy link
Member

Choose a reason for hiding this comment

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

For 2: not sure, I wish we had the tests that exercise "compilation customization" APIs, but they have not yet been factored out of test_jit.py.

**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

Differential Revision: [D23971882](https://our.internmc.facebook.com/intern/diff/D23971882)

[ghstack-poisoned]
**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

Differential Revision: [D23971882](https://our.internmc.facebook.com/intern/diff/D23971882)

[ghstack-poisoned]
Copy link
Contributor

@eellison eellison left a comment

Choose a reason for hiding this comment

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

Changes LGTM, bikeshedding was addressed, and this is improvement to the codebase so accepting. It'd also be good to land the API change for the upcoming release to reduce API churn so any remaining outstanding comments can be addressed in a follow up

@@ -186,6 +186,10 @@ c10::optional<std::string> ConcreteModuleType::findFailedAttribute(
return c10::nullopt;
}

bool ConcreteModuleType::isIgnoredAttribute(const std::string& name) const {
Copy link
Contributor

Choose a reason for hiding this comment

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

As far as I understand, ConcreteMuduleType::equals is to disambiguate between different instantiations of an nn.Module which can result in a different type. __jit_ignored_attributes__ is a class-level attribute, I don't see how it could differ between different instantiations.

**Summary**
This commit adds an API for ignoring arbitrary module attributes during
scripting. A class attribute named `ignored_attributes` containing names
of attributes to ignore can be added to the class of the instance being
scripted. Attributes ignored in this fashion cannot be used in
`forward`, methods used by `forward` or by `@exported` methods. They
are, however, copied to the `RecursiveScriptModule` wrapper and can be
used by `@ignored` methods and regular Python code.

**Test Plan**
This commit adds unit tests to `TestScriptPy3` to test this new API.

Differential Revision: [D23971882](https://our.internmc.facebook.com/intern/diff/D23971882)

[ghstack-poisoned]
@facebook-github-bot
Copy link
Contributor

@SplitInfinity merged this pull request in 4fdba30.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Merged oncall: jit Add this issue/PR to JIT oncall triage queue
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants