Saving only weights keeps the learned parameters without serializing the complete Keras model definition. This is useful when the architecture is recreated by code, when a checkpoint feeds transfer learning, or when only compatible layer weights should move between models.

In Keras 3, model.save_weights() writes an HDF5 weights file whose name should end in .weights.h5. The receiving model must have a compatible architecture and built variables before load_weights() can restore the parameters.

Use full-model .keras saving when the next process should reload the architecture, compile information, and optimizer state automatically. Use weights-only saving when that extra model metadata is not needed or the architecture is intentionally rebuilt.

Steps to save and load Keras model weights:

  1. Install Keras and the backend package.
    $ python -m pip install --upgrade keras jax
  2. Create save_load_weights.py.
    save_load_weights.py
    import os
    from pathlib import Path
     
    os.environ["KERAS_BACKEND"] = "jax"
     
    import keras
    import numpy as np
     
     
    def build_model(compile_model=True):
        model = keras.Sequential(
            [
                keras.Input(shape=(4,), name="features"),
                keras.layers.Dense(8, activation="relu", name="hidden"),
                keras.layers.Dense(1, activation="sigmoid", name="score"),
            ]
        )
        if compile_model:
            model.compile(
                optimizer=keras.optimizers.Adam(learning_rate=0.04),
                loss=keras.losses.BinaryCrossentropy(),
            )
        return model
     
     
    keras.utils.set_random_seed(29)
    weights_path = Path("risk-score.weights.h5")
    if weights_path.exists():
        weights_path.unlink()
     
    x_train = np.linspace(0.0, 1.0, 64, dtype="float32").reshape(16, 4)
    y_train = ((x_train[:, 1] + x_train[:, 3]) > 0.9).astype("float32")
    sample = np.array([[0.2, 0.7, 0.4, 0.8]], dtype="float32")
     
    model = build_model()
    model.fit(x_train, y_train, epochs=10, batch_size=4, verbose=0)
    before = model.predict(sample, verbose=0)
    model.save_weights(weights_path)
     
    clone = build_model(compile_model=False)
    clone(np.zeros((1, 4), dtype="float32"))
    clone.load_weights(weights_path)
    after = clone.predict(sample, verbose=0)
     
    print(f"backend: {keras.backend.backend()}")
    print(f"weights file: {weights_path}")
    print(f"weights file exists: {weights_path.exists()}")
    print(f"prediction before save: {before[0, 0]:.6f}")
    print(f"prediction after load: {after[0, 0]:.6f}")
    print(f"predictions match: {np.allclose(before, after, atol=1e-6)}")

    The clone is built once before loading weights so all layer variables exist. The architecture must stay compatible with the saved weights file.

  3. Run the script.
    $ python save_load_weights.py
    backend: jax
    weights file: risk-score.weights.h5
    weights file exists: True
    prediction before save: 0.655520
    prediction after load: 0.655520
    predictions match: True

    Confirm predictions match: True before using the weights file in another script or training job.

  4. Use the native .keras format instead when the architecture should travel with the weights.
    model.save("risk-score.keras")
    loaded_model = keras.models.load_model("risk-score.keras")