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

Point optimizer to tf.keras.optimizer.legacy.Optimizer to be compatib… #2706

Merged
merged 14 commits into from
Jun 19, 2022

Conversation

chenmoneygithub
Copy link
Contributor

Description

Brief Description of the PR:
Keras has made a new version of optimizer, check the link here, and in a future TF/Keras release, tf.keras.optimizers.XXX will point to the new optimizer, and old optimizers will continue to be supported under legacy namespace until further notice. To avoid code breakage, this PR is replacing tf.keras.optimizer.Optimizer with tf.keras.optimizer.legacy.Optimizer.

Fixes # (issue)

Type of change

Checklist:

  • I've properly formatted my code according to the guidelines
    • By running Black + Flake8
    • By running pre-commit hooks
  • This PR addresses an already submitted issue for TensorFlow Addons
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • This PR contains modifications to C++ custom-ops

How Has This Been Tested?

No new test cases are required, just making sure the current test is not broken due to the change.

@bot-of-gabrieldemarmiesse

@rehanguha @lokhande-vishnu @szutenberg @juntang-zhuang @RaphaelMeudec @PhilJd @manzilz @junjiek @CyberZHG @pkan2 @hyang0129

You are owners of some files modified in this pull request.
Would you kindly review the changes whenever you have the time to?
Thank you very much.

@manzilz
Copy link
Contributor

manzilz commented May 19, 2022

changes to yogi.py: LGTM

@juntang-zhuang
Copy link
Contributor

changes to adabelief.py LGTM!

Copy link
Contributor

@rehanguha rehanguha left a comment

Choose a reason for hiding this comment

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

tensorflow_addons/optimizers/cocob.py changes LGTM.

@bhack
Copy link
Contributor

bhack commented May 19, 2022

@chenmoneygithub To understand this a little bit:

/cc @seanpmorgan

@chenmoneygithub
Copy link
Contributor Author

@bhack Thanks for your questions! Re your questions:

  • Yes, after this PR addon optimizers will be based on tf.keras.optimizers.legacy.Optimizer, which is an alias to current tf.keras.optimizers.Optimizer. We will later check how to make them compatible with the new optimizer, which would need some refactor work.
  • Do you mean AdamW? We are supporting AdamW in Keras now because it seems to be very popular.
  • V2 optimizer is what people use now. I apologize the the misleading name, but basically V2 optimizers were made at the same time as TF2, so V2 means it supports TF2. The experimental optimizer is made to replace all those V2 optimizers.

Hope this would answer your question!

@bhack
Copy link
Contributor

bhack commented May 19, 2022

Yes, thanks for the confirmation.

We will later check how to make them compatible with the new optimizer, which would need some refactor work.

I have some doubt on this point as it really depends on the Keras, Keras-CV, Keras-NLP roadmaps and how these will overlap with the existing "legacy" components here.

If a refactor it is needed probably it is better to evaluate if the single optimizer will be absorbed, with the new inheritance, in any of these Keras-* repos.

@chenmoneygithub
Copy link
Contributor Author

chenmoneygithub commented May 19, 2022

@bhack Yes, I have exactly the same thought - new optimizer-based implementation should be done in Keras repo rather than doing in-place code changes to addon.optimizers.

@bhack
Copy link
Contributor

bhack commented May 19, 2022

Ok.
For this PR you need to handle a bit all the conditionals as we are going to support and produce wheels for the last 3 TF releases.

@chenmoneygithub
Copy link
Contributor Author

Got it, will reflect it in the code.

@seanpmorgan
Copy link
Member

Hi @chenmoneygithub , thanks for the PR and keeping us in the loop. As you can see from our CI this appears to be a significant breaking change. TF Addons supports 3 TensorFlow/Keras versions during each release, so implementing this change would require some work arounds on our end. I'm wondering if this is implemented as intended on the Keras side?

Has legacy.optimizer been available / has the new optimizer been part of experimental for a few releases? Typically Keras is quite good at backwards compatibility.

cc @fchollet

@seanpmorgan
Copy link
Member

I didn't see an RFC in keras governance or anything on this, so I suspect more downstream consumers (other than TFA) will be surprised.

@bhack
Copy link
Contributor

bhack commented May 23, 2022

Also isn't not explained what these legacy optimizers are in the official documentation:
https://www.tensorflow.org/api_docs/python/tf/keras/optimizers/legacy

@chenmoneygithub
Copy link
Contributor Author

@seanpmorgan yes, your concern is very reasonable.

For some background context: In this release (TF/Keras 2.9), our goal is to push out the experimental optimizer, mainly for internal adoptions. Making experimental optimizer the default one will happen in a later release (2.10 or 2.11), and old optimizer deprecation would happen in a much later release (we have not decided which one yet!), but we made this legacy optimizer, which is a mirror of tf.keras.optimizers.XXX to prepare for the future deprecation.

For the question about breakage: the actions failed due to symbol missing, not functionality. I added the version control, which means only use legacy namespace for version after 2.8. We don't want to generate workloads for OSS contributors, so we prefer to handle the implementation based on our side.

@bhack Yea, this is something we will fix. The page should be more clear.

@bhack
Copy link
Contributor

bhack commented May 23, 2022

For some background context: In this release (TF/Keras 2.9), our goal is to push out the experimental optimizer, mainly for internal adoptions. Making experimental optimizer the default one will happen in a later release (2.10 or 2.11), and old optimizer deprecation would happen in a much later release (we have not decided which one yet!), but we made this legacy optimizer, which is a mirror of tf.keras.optimizers.XXX to prepare for the future deprecation.

I suppose that @seanpmorgan meant that he didn't find any RFC in:
https://github.com/keras-team/governance
https://github.com/tensorflow/community/pulls

Do you meany it will land there before the official deprecation?

I added the version control, which means only use legacy namespace for version after 2.8. We don't want to generate workloads for OSS contributors, so we prefer to handle the implementation based on our side.

If you have added this in Keras directly and you will not do a patch release we need to wait for the next stable to cherry pick this PR as we switch TF/Keras versions every new TF stable release/rc.

@chenmoneygithub
Copy link
Contributor Author

@bhack Thanks!

Yes, we will make public notice before taking migration/deprecation actions, now we are focusing on internal optimizer adoptions.

Yea at this moment it's hard to edit the page.

@bhack
Copy link
Contributor

bhack commented May 23, 2022

Yea at this moment it's hard to edit the page.

What do you mean? I was mentioning about our dependency on a stable TF release in TFA master instead of TF nightly.

@chenmoneygithub
Copy link
Contributor Author

@bhack Sorry I misread your previous comment, I thought you mean the documentation of legacy.optimizer.

Re you previous comment - I don't think cherrypicking is required? I am saying there are two options here:

  1. addons contributors change the code to implement optimizers based on tf.keras.optimizers.experimental.XXX.

  2. We move addons optimizers into main Keras by reimplementing them based on tf.keras.optimizers.experimental.XXX. e.g., tf.keras.optimizers.lamb.

  3. is actually the better option, but it puts on workloads on contributors.

@bhack
Copy link
Contributor

bhack commented May 23, 2022

I added the version control.

I was talking about this. Can you point me on this version control?

@chenmoneygithub
Copy link
Contributor Author

@bhack My previous push failed due to some merge conflict, I will ping you once I get the latest code uploaded.

@bhack
Copy link
Contributor

bhack commented May 23, 2022

My previous push failed due to some merge conflict, I will ping you once I get the latest code uploaded.

Ok thanks.

More in general:

We move addons optimizers into main Keras by reimplementing them based on tf.keras.optimizers.experimental.XXX. e.g., tf.keras.optimizers.lamb.

It could be ok for us but you need to consider:

  1. A little bit of deprecation margin https://github.com/tensorflow/addons#periodic-evaluation-of-subpackages
  2. I suppose you will eventually fix the Keras.io repository yourself for TFA depending tutorial/notebooks in case of breaking optimizers API
  3. We are a small project but we have accumulated over time a little bit of downstream dependencies:
    immagine

@bhack
Copy link
Contributor

bhack commented May 23, 2022

Ok, now I see that we are back on the original idea to conditional control the namespace downstream (in TFA directly).

@seanpmorgan
Copy link
Member

Ok, now I see that we are back on the original idea to conditional control the namespace downstream (in TFA directly).

Yes, this is what I was afraid of. This PR is sub-optimal because it makes downstream consumers inherit the debt of a quickly implemented public api change to Keras.

@chenmoneygithub @fchollet is there anywhere publicly facing from the Keras perspective where we can discuss this? TFA is not the only one impacted by this.

cc @joanafilipa

@chenmoneygithub
Copy link
Contributor Author

@bhack I just updated the code with the version control part.

Yes, I am aware of the large user group of tf addons, and never want to break their workflows. On Keras it remains unclear to us that when we can completely remove the code of the old optimizer, personally I feel it is a hard task. If you are curious on why we are making the new optimizer even though we are not removing the old code in the short term, briefly we feel the old optimizer's logic and layout are bad, so we replace things like slot variables and fused ops with more understandable code, and split out the distributed training part to a separate class.

@bhack
Copy link
Contributor

bhack commented May 23, 2022

Yes, I am aware of the large user group of tf addons, and never want to break their workflows. On Keras it remains unclear to us that when we can completely remove the code of the old optimizer, personally I feel it is a hard task. If you are curious on why we are making the new optimizer even though we are not removing the old code in the short term, briefly we feel the old optimizer's logic and layout are bad, so we replace things like slot variables and fused ops with more understandable code, and split out the distributed training part to a separate class.

Personally I agree with Sean's vision.
As these are public symbols it seems to me a little bit too late to present an RFC in Keras/governance or in TF/community only when effectively we want to remove these symbols (than at the RFC time they will be already legacy).

For a top namespace change like tf.keras.* I suppose that an early RFC is required to involve the community, SIGs, and more in general downstream projects.

This time it went like this, probably for a good reasons (I don't know) but I would not feel like promoting this process in general for our community / ecosystem.

@chenmoneygithub
Copy link
Contributor Author

We discussed about RFC when we kicked off the project, and did not go for it because main changes happen on internal logic, while the public API mostly remains identical.

Our rollout journey would be hidden for main users, basically the tf.keras.optimizers namespace will point to tf.keras.optimizers.experimental in a future release (this info is available in the release note of TF 2.9), and users will switch to new optimizer silently. In the window of 2.9, we will mainly work with internal teams to fix unseen errors during testing, and later on we will publish a public notice for required actions. Addons optimizer is the most special one, it is written based on the Keras optimizer, and itself has a large user group, so I am prioritizing this action now.

@bhack
Copy link
Contributor

bhack commented May 23, 2022

I could understand this only eventually for end-users but not for derived/ecosystem projects.
Do you meant that using Keras public API for creating derived objects in a third party library and in this case a TF ecosystem library is not a supported use case anymore?

I don't think that TFA is a special case for derived projects.

@chenmoneygithub
Copy link
Contributor Author

@bhack Refactored the code, please take another look when you are free, thanks!

@@ -14,6 +14,7 @@
# ==============================================================================
"""Additional optimizers that conform to Keras API."""

from tensorflow_addons.optimizers.constants import BASE_OPTIMIZER_CLASS
Copy link
Contributor

Choose a reason for hiding this comment

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

this breaks alphabetic order

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This order matters, if this line does not go before other optimizers it creates a cyclic importing.

raise TypeError(
"optimizer is not an object of tf.keras.optimizers.Optimizer"
)
if tf.__version__[:3] <= "2.8":
Copy link
Contributor

Choose a reason for hiding this comment

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

why do we need this condition? What if we would write (tf.keras.optimizers.Optimizer, BASE_OPTIMIZER_CLASS)?

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 did this for the error message to be accurate. but rethinking about it, I am doing (tf.keras.optimizers.Optimizer, BASE_OPTIMIZER_CLASS) check and imply that after 2.9 you should expect tf.keras.optimizers.legacy.Optimizer.

@@ -0,0 +1,5 @@
import tensorflow as tf
Copy link
Contributor

Choose a reason for hiding this comment

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

missing copyright header

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

@@ -0,0 +1,5 @@
import tensorflow as tf

BASE_OPTIMIZER_CLASS = tf.keras.optimizers.legacy.Optimizer
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not if ... else? It will crash if tf.keras.optimizers.legacy doesn't exist (TF 2.8), right?

Are you sure it has to be CAPITAL_LETTER?

I'd rename it to KerasLegacyOptimizer (or KERAS_LEGACY_OPTIMIZER - I'm not an expert in conventions 😄 ) and rename the file to keras.py.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! I changed the check condition.

For the naming, yea, we probably want to do camel case since it is a class itself. But I don't want to imply Legacy in the name, which is not yet 100% correct - we still use tf.keras.optimizers.Optimizer in many places. I am renaming to BaseOptimizerClass, wdyt?

Copy link
Contributor

Choose a reason for hiding this comment

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

@chenmoneygithub - I expect that after refactor in keras is done, we will be gradually switching back to tf.keras.optimizers.Optimizer. BaseOptimizerClass will be confusing then because it'll be name for the legacy class.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sg, renamed to KerasLegacyOptimizer.

import tensorflow as tf

BASE_OPTIMIZER_CLASS = tf.keras.optimizers.legacy.Optimizer
if tf.__version__[:3] <= "2.8":
Copy link
Contributor

Choose a reason for hiding this comment

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

As I wrote previously:

>>> tf.__version__
'2.10.0-dev20220531'
>>> tf.__version__[:3]
'2.1'
>>> tf.__version__[:3] > "2.8"
False

Please fix this condition (also in other files) or just check if tf.keras.optimizers.legacy exists, maybe code would be cleaner then...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch! done

@@ -16,11 +16,12 @@
import tensorflow as tf
from tensorflow_addons.utils import types
Copy link
Contributor

Choose a reason for hiding this comment

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

isn't required to update types.Optimizer?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch! done

@@ -17,12 +17,13 @@
import tensorflow as tf
from tensorflow_addons.utils.types import FloatTensorLike

from tensorflow_addons.optimizers import BASE_OPTIMIZER_CLASS
Copy link
Contributor

Choose a reason for hiding this comment

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

this line should be added after line 17

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

@@ -401,13 +401,17 @@ def test_var_list_with_exclude_list_sgdw(dtype):
)


if tf.__version__[:3] > "2.8":
Copy link
Member

Choose a reason for hiding this comment

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

Use from packaging.version import Version

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks! I am switching to check if "optimizers.legacy" exists to keep consistency.

@@ -27,8 +27,14 @@
from typing import Union, Callable


if tf.__version__[:3] > "2.8":
Copy link
Member

@fsx950223 fsx950223 Jun 3, 2022

Choose a reason for hiding this comment

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

Use from packaging.version import Version

@@ -261,10 +261,17 @@ def _do_use_weight_decay(self, var):
return var.ref() in self._decay_var_list


optimizer_class = Union[
Copy link
Member

Choose a reason for hiding this comment

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

Why not BaseOptimizerClass?

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 am renaming this to keras_legacy_optimizer to keep aligned with changes suggested by Michal.

Optimizer = Union[tf.keras.optimizers.Optimizer, str]
if importlib.util.find_spec("tensorflow.keras.optimizers.legacy") is not None:
Optimizer = Union[
tf.keras.optimizers.Optimizer, tf.keras.optimizers.legacy.Optimizer, str
Copy link
Member

Choose a reason for hiding this comment

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

Same as above

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This I am using the name "Optimizer" to keep it unchanged.

@chenmoneygithub
Copy link
Contributor Author

Hi there, sorry for not updating this for a while. I have been terribly sick for the last a few days, will update this PR when I can work. Thanks!

@bhack bhack requested a review from fsx950223 June 10, 2022 19:23
@fsx950223
Copy link
Member

Please check ci/cd.

@chenmoneygithub
Copy link
Contributor Author

@fsx950223 I checked the failure log, and it seems to ask for type annotation from source Keras optimizer code not changed I made to addons. Can I ask what should we do here?

@chenmoneygithub
Copy link
Contributor Author

Hi maintainers, the current error message is:

'keras.optimizers.optimizer_v2.optimizer_v2.Optimizer.__init__' has not complete type annotations in its signature (it's missing the type hint for 'name'). We would like this functions to be typed.If you are not familiar with adding type hints in functions, you can look at functions already typed inthe codebase. 

Which does not look like a new issue imported by this PR. Could anyone help check and give some suggestions on how we should handle it? https://github.com/tensorflow/addons/runs/6836413149?check_suite_focus=true

I would like to have this PR submitted soon to avoid potential breakage of addons.optimizers in the next TF release.

@bhack
Copy link
Contributor

bhack commented Jun 18, 2022

@chenmoneygithub
Copy link
Contributor Author

@bhack Thanks! But the error is pointing to main Keras repo rather than some classes I am changing in addons repo. Is there any way to exempt the check?

@bhack
Copy link
Contributor

bhack commented Jun 18, 2022

@boring-cyborg boring-cyborg bot added the test-cases Related to Addons tests label Jun 18, 2022
@fsx950223 fsx950223 merged commit 339159f into tensorflow:master Jun 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
optimizers test-cases Related to Addons tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants