Aggregate accuracy can hide whether a binary classifier misses positive cases or raises false alarms on negative cases. A confusion matrix separates those errors into true-negative, false-positive, false-negative, and true-positive counts for the same holdout set used by Keras evaluation.
The saved model, /x-test.npy features, and /y-test.npy labels must use the same preprocessing and sample order. The script loads the model without its training configuration, then compiles it with binary cross-entropy and accuracy so the evaluation settings remain explicit; projects that used another binary loss or metric should use that matching configuration.
The binary evaluation assumes integer labels 0 and 1 plus one sigmoid score per sample, with 0.5 as the class threshold. Rows represent true labels and columns represent predicted labels; a fixed labels=[0, 1] order lets the custom matrix be checked against scikit-learn without changing cell positions. Multiclass softmax output needs argmax(axis=1) and a larger matrix instead.
import keras import numpy as np from sklearn.metrics import confusion_matrix model = keras.saving.load_model("classifier.keras", compile=False) model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["accuracy"]) x_test = np.load("x-test.npy") y_test = np.load("y-test.npy").astype("int32").reshape(-1)
The filenames and evaluation configuration must match the saved classifier and its holdout data.
metrics = model.evaluate(x_test, y_test, verbose=0, return_dict=True) probabilities = model.predict(x_test, verbose=0).reshape(-1) y_pred = (probabilities >= 0.5).astype("int32")
matrix = np.zeros((2, 2), dtype="int32") np.add.at(matrix, (y_test, y_pred), 1) tn, fp, fn, tp = matrix.ravel()
reference_matrix = confusion_matrix(y_test, y_pred, labels=[0, 1]) matches_reference = np.array_equal(matrix, reference_matrix) print(f"loss: {metrics['loss']:.4f}") print(f"accuracy: {metrics['accuracy']:.4f}") print("confusion matrix rows=true columns=predicted labels=[0, 1]") print(matrix) print(f"tn={tn} fp={fp} fn={fn} tp={tp}") print(f"matches sklearn reference: {matches_reference}") if not matches_reference: raise RuntimeError("custom matrix differs from sklearn reference")
import keras import numpy as np from sklearn.metrics import confusion_matrix model = keras.saving.load_model("classifier.keras", compile=False) model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["accuracy"]) x_test = np.load("x-test.npy") y_test = np.load("y-test.npy").astype("int32").reshape(-1) metrics = model.evaluate(x_test, y_test, verbose=0, return_dict=True) probabilities = model.predict(x_test, verbose=0).reshape(-1) y_pred = (probabilities >= 0.5).astype("int32") matrix = np.zeros((2, 2), dtype="int32") np.add.at(matrix, (y_test, y_pred), 1) tn, fp, fn, tp = matrix.ravel() reference_matrix = confusion_matrix(y_test, y_pred, labels=[0, 1]) matches_reference = np.array_equal(matrix, reference_matrix) print(f"loss: {metrics['loss']:.4f}") print(f"accuracy: {metrics['accuracy']:.4f}") print("confusion matrix rows=true columns=predicted labels=[0, 1]") print(matrix) print(f"tn={tn} fp={fp} fn={fn} tp={tp}") print(f"matches sklearn reference: {matches_reference}") if not matches_reference: raise RuntimeError("custom matrix differs from sklearn reference")
$ python evaluate_confusion_matrix.py loss: 0.4488 accuracy: 0.6667 confusion matrix rows=true columns=predicted labels=[0, 1] [[2 1] [1 2]] tn=2 fp=1 fn=1 tp=2 matches sklearn reference: True
The displayed matrix contains two true negatives, one false positive, one false negative, and two true positives.
A False result raises an error because the custom accumulation no longer agrees with the independent reference implementation.