From 1cb5b9ce5817f0aaafa46f0e782bdea9816d2323 Mon Sep 17 00:00:00 2001 From: Fabien Hertschuh <1091026+hertschuh@users.noreply.github.com> Date: Fri, 17 Jan 2025 13:49:36 -0800 Subject: [PATCH] Fix memory leaks in `model.evaluate`. The history is only used in `model.fit`, no need to create it for `evaluate` and `predict`. The history is attached to the model and therefore lives for as long as the model is around. The executor used in `CallbackList` was never shut down, causing it to keep a thread around, which in turn had thread locals that were leaked. --- keras/src/backend/jax/trainer.py | 2 -- keras/src/backend/numpy/trainer.py | 2 -- keras/src/backend/tensorflow/trainer.py | 2 -- keras/src/backend/torch/trainer.py | 2 -- keras/src/callbacks/callback_list.py | 4 ++++ 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/keras/src/backend/jax/trainer.py b/keras/src/backend/jax/trainer.py index c127f7f83344..9ee3c78956f0 100644 --- a/keras/src/backend/jax/trainer.py +++ b/keras/src/backend/jax/trainer.py @@ -547,7 +547,6 @@ def evaluate( if not isinstance(callbacks, callbacks_module.CallbackList): callbacks = callbacks_module.CallbackList( callbacks, - add_history=True, add_progbar=verbose != 0, verbose=verbose, epochs=1, @@ -642,7 +641,6 @@ def predict( if not isinstance(callbacks, callbacks_module.CallbackList): callbacks = callbacks_module.CallbackList( callbacks, - add_history=True, add_progbar=verbose != 0, verbose=verbose, epochs=1, diff --git a/keras/src/backend/numpy/trainer.py b/keras/src/backend/numpy/trainer.py index 404a41244d5c..80494a540be9 100644 --- a/keras/src/backend/numpy/trainer.py +++ b/keras/src/backend/numpy/trainer.py @@ -185,7 +185,6 @@ def predict( if not isinstance(callbacks, callbacks_module.CallbackList): callbacks = callbacks_module.CallbackList( callbacks, - add_history=True, add_progbar=verbose != 0, verbose=verbose, epochs=1, @@ -265,7 +264,6 @@ def evaluate( if not isinstance(callbacks, callbacks_module.CallbackList): callbacks = callbacks_module.CallbackList( callbacks, - add_history=True, add_progbar=verbose != 0, verbose=verbose, epochs=1, diff --git a/keras/src/backend/tensorflow/trainer.py b/keras/src/backend/tensorflow/trainer.py index e4f999dcd787..769f3b32419a 100644 --- a/keras/src/backend/tensorflow/trainer.py +++ b/keras/src/backend/tensorflow/trainer.py @@ -465,7 +465,6 @@ def evaluate( if not isinstance(callbacks, callbacks_module.CallbackList): callbacks = callbacks_module.CallbackList( callbacks, - add_history=True, add_progbar=verbose != 0, verbose=verbose, epochs=1, @@ -510,7 +509,6 @@ def predict( if not isinstance(callbacks, callbacks_module.CallbackList): callbacks = callbacks_module.CallbackList( callbacks, - add_history=True, add_progbar=verbose != 0, verbose=verbose, epochs=1, diff --git a/keras/src/backend/torch/trainer.py b/keras/src/backend/torch/trainer.py index 0eba8aa50ab1..04ef0a2318d1 100644 --- a/keras/src/backend/torch/trainer.py +++ b/keras/src/backend/torch/trainer.py @@ -353,7 +353,6 @@ def evaluate( if not isinstance(callbacks, callbacks_module.CallbackList): callbacks = callbacks_module.CallbackList( callbacks, - add_history=True, add_progbar=verbose != 0, verbose=verbose, epochs=1, @@ -399,7 +398,6 @@ def predict( if not isinstance(callbacks, callbacks_module.CallbackList): callbacks = callbacks_module.CallbackList( callbacks, - add_history=True, add_progbar=verbose != 0, verbose=verbose, epochs=1, diff --git a/keras/src/callbacks/callback_list.py b/keras/src/callbacks/callback_list.py index ea70052c0245..acd0712d204a 100644 --- a/keras/src/callbacks/callback_list.py +++ b/keras/src/callbacks/callback_list.py @@ -241,3 +241,7 @@ def on_predict_end(self, logs=None): logs = python_utils.pythonify_logs(logs) for callback in self.callbacks: callback.on_predict_end(logs) + + def __del__(self): + if self._executor is not None: + self._executor.shutdown(cancel_futures=True)