Exact model converging on keras-tf but not on keras
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
If the "same" model converges in tf.keras but not in older standalone keras, the cause is usually not magic. It is almost always a difference in versions, defaults, random seeds, backend behavior, numerical precision, or data preprocessing. The fastest way to debug it is to stop treating the two environments as identical and compare them layer by layer, optimizer setting by optimizer setting, and batch by batch.
Start with the Most Likely Cause: They Are Not Actually the Same Stack
Historically, keras and tf.keras looked similar from the API level, but they were not always identical implementations. Even if the model code appears the same, the following can differ:
- optimizer defaults such as epsilon values or decay behavior
- layer implementations, especially recurrent layers and batch normalization
- random initialization paths
- backend graph execution details
- handling of channels ordering and data types
- CuDNN-accelerated kernels versus fallback kernels
So the first engineering move is not to tune blindly. It is to pin versions and print the environment clearly.
If you are truly comparing standalone keras against tf.keras, record both package versions and the backend version. Without that, "exact model" is not a meaningful claim.
Compare the Training Inputs First
A large share of convergence bugs are really data bugs. If one stack shuffles differently, casts to a different dtype, normalizes in a different place, or applies augmentation in a different order, training curves can diverge fast.
Useful checks include:
- verify input tensor shapes are identical
- verify label encoding is identical
- print batch mean and standard deviation in both runs
- verify training and validation splits are the same
- disable augmentation temporarily
A simple sanity check is to overfit on a tiny subset.
If one framework cannot overfit a tiny dataset and the other can, you likely have an optimizer, initialization, or numerical mismatch rather than a generalization problem.
Normalize the Optimizer and Initialization Settings
When convergence differs, I would compare these settings explicitly instead of relying on defaults:
- learning rate
- epsilon
- beta parameters for Adam-like optimizers
- gradient clipping
- kernel initializer
- bias initializer
- loss reduction mode
For example, define the optimizer manually rather than accepting library defaults:
Do the same for layer initializers if reproducibility matters. Default values have changed across releases, and subtle numeric shifts can matter in unstable models.
Watch for Backend-Specific Layers
Older keras plus a TensorFlow backend and modern tf.keras may treat some layers differently under the hood. Recurrent layers, convolutions on GPU, and batch normalization have historically been sensitive areas.
Batch normalization is especially worth testing. If your model is unstable, temporarily remove it or replace it with a simpler architecture. If convergence suddenly becomes consistent, you have narrowed the problem to a layer with training-mode state or numerical sensitivity.
Also check whether one environment is silently using float64 while the other uses float32, or whether mixed precision is enabled in only one run.
Use a Minimal Reproduction Instead of the Full Pipeline
The most productive debugging workflow is:
- fix seeds
- use one small dataset slice
- remove callbacks
- remove augmentation
- reduce the model to a few layers
- compare losses after each epoch
At that point, compare first-batch predictions and first gradient step if needed. That sounds tedious, but it is how you separate framework differences from hidden application differences.
If the numbers diverge on the first batch, the issue is in initialization, preprocessing, or forward/backward computation. If they diverge later, look at stateful layers, callbacks, and data order.
Common Pitfalls
The biggest mistake is saying two training runs are identical because the Python model definition matches. Identical source code does not imply identical optimizer defaults, kernels, or execution behavior.
Another mistake is debugging convergence before verifying that both environments see identical input data and labels.
People also leave too many moving parts enabled at once: augmentation, callbacks, learning-rate schedules, multiprocessing data loaders, and mixed precision. Strip the pipeline down first.
Finally, do not compare an old standalone keras package to a new TensorFlow release and expect them to behave the same. Version skew alone can explain the result.
Summary
- Different convergence between
kerasandtf.kerasusually comes from version or default-setting differences, not mystery behavior - Verify seeds, package versions, backend versions, and device placement before tuning
- Check that preprocessing, shuffling, labels, and dtypes are actually identical
- Define optimizer and initializer settings explicitly instead of relying on defaults
- Use a tiny overfitting test and a minimal reproduction to localize the mismatch
- Treat layer implementations such as batch normalization and recurrent layers as likely sources of divergence

