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

Changes in Dynamic Modules #1600

Merged
merged 9 commits into from
Feb 28, 2024
Merged

Conversation

AlbinSou
Copy link
Collaborator

I tried to implement the changes that would fix bugs described in Issue #1591 and also allow users to use Dynamic modules more easily outside of avalanche strategies.

To do so, I made several major changes.

  • Remove old avalanche_model_adaptation method and replace it with a new version which does things differently. Instead of iterating on all modules, it iterates only on childrens, and does so recursively in order to explore the whole hierarchy. It still iterates on the hierarchy of nn.Modules and catches DynamicModules to adapt them just like before.
  • Added a method adapt() to dynamicmodule that will call the new "avalanche_model_adaptation" on itself and recursively on it's submodules
  • Added an _auto_adapt attribute to all DynamicModules that triggers or not the adaptation inside the avalanche_model_adaptation recursion (defaults to True). In particular, this attribute does not prevent the user to call directly module.adapt() if he wants, however if the module needs to be adapted as a second level or more module inside this recursion, it will not get adapted. This is the part that fixes the bug described in IncrementalClassifiers inside MultiHeadClassifiers are adapted on all experiences #1591, since that way we can instantiate IncrementalClassifiers inside the MultiHeadClassifier that will not be automatically adapted but rather adapted manually by the MultiHeadClassifier only if the experience used for adaptation contains the task id related to this IncrementalClassifier.
  • Made a few clarification changes in IncrementalClassifier and Multiheadclassifier by replacing occurences of adaptation() implementation by train_adaptation and eval_adaptation, where if the eval_adaptation needs to be the same than train_adaptation I just call train_adaptation inside eval_adaptation (nested adaptation() override were a bit complex to understand and could lead to confusion)

For now tests do not pass, but some of them I am not sure what behaviour they were checking and if we really want this behaviour. I will also add some more tests to check that the behaviour in #1591 is solved (my local test says it is fixed for now).

@coveralls
Copy link

coveralls commented Feb 19, 2024

Pull Request Test Coverage Report for Build 8038239946

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 0 of 115 (100.0%) changed or added relevant lines in 6 files are covered.
  • 60 unchanged lines in 1 file lost coverage.
  • Overall coverage increased (+0.1%) to 64.167%

Files with Coverage Reduction New Missed Lines %
avalanche/training/supervised/lamaml_v2.py 60 0.0%
Totals Coverage Status
Change from base Build 7933962330: 0.1%
Covered Lines: 20155
Relevant Lines: 31410

💛 - Coveralls

@AlbinSou AlbinSou marked this pull request as ready for review February 19, 2024 13:04
avalanche/models/dynamic_modules.py Outdated Show resolved Hide resolved
avalanche/models/dynamic_modules.py Show resolved Hide resolved
tests/models/test_models.py Show resolved Hide resolved
tests/models/test_models.py Show resolved Hide resolved
@AntonioCarta
Copy link
Collaborator

Made a few clarification changes in IncrementalClassifier and Multiheadclassifier by replacing occurences of adaptation() implementation by train_adaptation and eval_adaptation, where if the eval_adaptation needs to be the same than train_adaptation I just call train_adaptation inside eval_adaptation (nested adaptation() override were a bit complex to understand and could lead to confusion)

I think we could also remove train_adaptation and eval_adaptation and have a single method. After, you can detect train/eval mode with a single if when necessary, so it seems to be adding unnecessary complexity in the common case.

@AlbinSou
Copy link
Collaborator Author

@AntonioCarta How would you like to manage the adapt() and adaptation() function ? I wanted to keep the name of the "adaptation" also to keep backward compatibility but now it's a bit confusing to have two methods adapt() and adaptation()

@AntonioCarta
Copy link
Collaborator

@AntonioCarta How would you like to manage the adapt() and adaptation() function ? I wanted to keep the name of the "adaptation" also to keep backward compatibility but now it's a bit confusing to have two methods adapt() and adaptation()

The advantage of the current solution is that it's backward compatible. If we don't want to rename the old method we could rename the new one as rec_adapt for recursive adaptation?

I was also thinking about a test case to check that the recursive calls don't break the #1591 fix:

model = ABasicDynamicModule(ABasicModule(MultiTaskClassifier(...)))
model.adapt(exp)  # here it's calling rec_adapt from the "base" layer, not the MultiTaskClassifier. It should still work.
# assert that wrong heads are not adapted

@AlbinSou
Copy link
Collaborator Author

AlbinSou commented Feb 20, 2024

@AntonioCarta How would you like to manage the adapt() and adaptation() function ? I wanted to keep the name of the "adaptation" also to keep backward compatibility but now it's a bit confusing to have two methods adapt() and adaptation()

The advantage of the current solution is that it's backward compatible. If we don't want to rename the old method we could rename the new one as rec_adapt for recursive adaptation?

I was also thinking about a test case to check that the recursive calls don't break the #1591 fix:

model = ABasicDynamicModule(ABasicModule(MultiTaskClassifier(...)))
model.adapt(exp)  # here it's calling rec_adapt from the "base" layer, not the MultiTaskClassifier. It should still work.
# assert that wrong heads are not adapted

I added a test for that at the end of test_models.DynamicModulesTest. Yes, maybe I will rename to make it clearer. Do you know how I can fix these failed Coveralls?

@AlbinSou
Copy link
Collaborator Author

Made a few clarification changes in IncrementalClassifier and Multiheadclassifier by replacing occurences of adaptation() implementation by train_adaptation and eval_adaptation, where if the eval_adaptation needs to be the same than train_adaptation I just call train_adaptation inside eval_adaptation (nested adaptation() override were a bit complex to understand and could lead to confusion)

I think we could also remove train_adaptation and eval_adaptation and have a single method. After, you can detect train/eval mode with a single if when necessary, so it seems to be adding unnecessary complexity in the common case.

Yes I agree, I don't remember why we included them in the first place

@AlbinSou
Copy link
Collaborator Author

AlbinSou commented Feb 23, 2024

@AntonioCarta On my side everything is ok. I was thinking maybe to include an example on how to use dynamic modules outside and inside of a strategy (call to recursive_adaptation and explanation on how avalanche_model_adaptation works). Another thing is that I changed the location of avalanche_model_adaptation for now it's inside dynamic_modules.py before it was in models/utils.py. For now I import it in utils so that ppl don't have to change their imports, but it's a bit weird. The reason I put it inside dynamic_modules is that this function is needed in recursive_adaptation() method so putting it to utils was leading to annoying recursive imports.

I also just noticed that NCM is still using eval_adaptation so I will need to change that. I don't know how this has not been caught by some tests.

@AntonioCarta
Copy link
Collaborator

Ok. I will wait to merge this after release to be on the safe side.

I was thinking maybe to include an example on how to use dynamic modules outside and inside of a strategy

we already have examples of that in the notebooks. Maybe you can just update them?

@AntonioCarta AntonioCarta merged commit a193d87 into ContinualAI:master Feb 28, 2024
12 checks passed
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.

IncrementalClassifiers inside MultiHeadClassifiers are adapted on all experiences
3 participants