-
Notifications
You must be signed in to change notification settings - Fork 23
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
Feat: Upgrade tensorflow
to 2.16.1
and keras
to 3.4.0
#1385
Feat: Upgrade tensorflow
to 2.16.1
and keras
to 3.4.0
#1385
Conversation
To accommodate the upgrade, next changes to the code were made: * Since `keras 3.*` now clones the input `regressor` that is provided to the `BaseEstimator` into a `regressor_` attribute, and ONLY updates the `regressor_` one after fitting, metadata extraction in `gordo.builder.build_model.ModelBuilder` was changed to seek the `regressor_` in the `steps`. * Accessing `keras.optimizers` has changed in `keras 3.*` to having to use the `keras.optimizers.get` method and providing a `class_name` and `config` to deserialize. Relevant for lstm and feedforward autoencoders. * Model config now requires for `tensorflow.keras.models.Sequential` to define `input_shape` in its layers, otherwise model is not properly built after compiling and prior to fitting. Relevant for `KerasRawModelRegressor`. `KerasBaseEstimator` underwent the most changes. * We now need to run the `__init__` of the KerasRegressor with the expected `kwargs` for proper initialisation of the object, but the `kwargs` will always take precedence for `fit`, `predict` and `compile`, so they are mostly for making `keras` happy. * Saving model for pickling was changed ƒrom `h5` format, which is now considered legacy to `keras` native zip format, and the file is now stored to a temporary file and read into a buffer instead of using the `h5py` library, since `save_model` and `load_model` now exclusively expect an actual file instead of an io buffer. * Current implementation of model preparation for fit depends on the `__call__` method, which is no longer used in `keras 3.*`, and was changed to `_prepare_model` to replicate the same behaviour right before we call our own `fit` since it requires the `X` and `Y` to be present to set the `n_features` and `n_features_out`. * `History` is no longer returned from calling `fit` and must be extracted from under `model.history`. * Manually stored history now resides in `self._history` instead of `self.history` to avoid attribute name clash. Adjustments to tests were made: * `input_shape` now resides under `input.shape` in `model.layers`. * `optimizer_kwargs.lr` is now `optimizer_kwargs.learning_rate`
gordo/builder/build_model.py
Outdated
if key.endswith( | ||
"_" | ||
): # keras3 clones the regressor into regressor_ and never updates original regressor |
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.
if key.endswith( | |
"_" | |
): # keras3 clones the regressor into regressor_ and never updates original regressor | |
if key == "regressor": # keras3 clones the regressor into regressor_ and never updates original regressor | |
continue |
endwith is too broad to fix one back-compatibility issue
gordo/machine/model/models.py
Outdated
*self._fit_kwargs, | ||
*self._predict_kwargs, | ||
*self._compile_kwargs, |
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.
*self._fit_kwargs, | |
*self._predict_kwargs, | |
*self._compile_kwargs, | |
*KerasRegressor._fit_kwargs, | |
*KerasRegressor._predict_kwargs, | |
*KerasRegressor._compile_kwargs, |
in case of self not clear what an exact source of these constants
gordo/machine/model/models.py
Outdated
@@ -301,25 +313,25 @@ def get_params(self, **params): | |||
Parameters used in this estimator | |||
""" | |||
params = super().get_params(**params) | |||
params.pop("build_fn", None) | |||
params.pop("model", None) |
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.
params.pop("model", None) |
unnecessary call
input_shape: | ||
- 4 |
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.
input_shape: | |
- 4 | |
input_shape: [4] |
just suggestion
input_shape: | ||
- 1 |
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.
input_shape: | |
- 1 |
@@ -88,8 +88,9 @@ class (e.x. Adam(lr=0.01,beta_1=0.9, beta_2=0.999)). If no arguments are | |||
|
|||
# Instantiate optimizer with kwargs | |||
if isinstance(optimizer, str): | |||
Optim = getattr(keras.optimizers, optimizer) | |||
optimizer = Optim(**optimizer_kwargs) |
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 this previous approach still works though. It's what we have implemented in aion and runs fine. But I'm ok with changing it.
import tensorflow.keras.models | ||
from tensorflow.keras.models import load_model, save_model | ||
from tensorflow.keras.preprocessing.sequence import pad_sequences, TimeseriesGenerator | ||
from tensorflow.keras.wrappers.scikit_learn import KerasRegressor as BaseWrapper | ||
from tensorflow.keras.callbacks import History | ||
from scikeras.wrappers import KerasRegressor |
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.
Interesting I wasn't aware about this. Good you found this out.
*KerasRegressor._predict_kwargs, | ||
*KerasRegressor._compile_kwargs, | ||
} | ||
KerasRegressor.__init__( |
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 indeed a bit strange, but oh well, as long as it works. Fortunately we won't need this wrapping business in aion.
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.
Wow quite a lot of tricky changes to make, good work @RollerKnobster in finding these out. It's hard to have an opinion if they will work or not but once we have a pre-release of gordo-anomaly I will use my local dieta to test it
To accommodate the upgrade, next changes to the code were made:
tensorflow
no longer has keras wrappers, which are now used fromscikeras
library, recommended for use by them. They have largely the same interface, but slightly different innerworkings, which were accomodated for.keras 3.*
now clones the inputregressor
that is provided to theBaseEstimator
into aregressor_
attribute, and ONLY updates theregressor_
one after fitting, metadata extraction ingordo.builder.build_model.ModelBuilder
was changed to seek theregressor_
in thesteps
.keras.optimizers
has changed inkeras 3.*
to having to use thekeras.optimizers.get
method and providing aclass_name
andconfig
to deserialize. Relevant for lstm and feedforward autoencoders.tensorflow.keras.models.Sequential
to defineinput_shape
in its layers, otherwise model is not properly built after compiling and prior to fitting. Relevant forKerasRawModelRegressor
.KerasBaseEstimator
underwent the most changes.__init__
of the KerasRegressor with the expectedkwargs
for proper initialisation of the object, but thekwargs
will always take precedence forfit
,predict
andcompile
, so they are mostly for makingkeras
happy.h5
format, which is now considered legacy tokeras
native zip format, and the file is now stored to a temporary file and read into a buffer instead of using theh5py
library, sincesave_model
andload_model
now exclusively expect an actual file instead of an io buffer.__call__
method, which is no longer used inkeras 3.*
, and was changed to_prepare_model
to replicate the same behaviour right before we call our ownfit
since it requires theX
andY
to be present to set then_features
andn_features_out
.History
is no longer returned from callingfit
and must be extracted from undermodel.history
.self._history
instead ofself.history
to avoid attribute name clash.Adjustments to tests were made:
input_shape
now resides underinput.shape
inmodel.layers
.optimizer_kwargs.lr
is nowoptimizer_kwargs.learning_rate