This article was published as a part of the Data Science Blogathon.
# generate the data and make predictions with our two models n_classes = 3 X, y = make_classification(n_samples=10000, n_features=10, n_classes=n_classes, n_clusters_per_class=1, n_informative=10, n_redundant=0) y = y.astype(int) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=0) prediction_naive = np.random.randint(low=0, high=n_classes, size=len(y_test)) clf = LogisticRegression().fit(X_train, y_train) prediction = clf.predict(X_test)
fig, (ax1, ax2) = plt.subplots(1,2, figsize=(20,8)) plot_cm_standard(y_true=y_test, y_pred=prediction, title="Awesome Model", list_classes=[str(i) for i in range(n_classes)], normalize="prediction", ax=ax1) plot_cm_standard(y_true=y_test, y_pred=prediction_naive, title="Rolling Dice Model", list_classes=[str(i) for i in range(n_classes)], normalize="prediction", ax=ax2) plt.show()
Now, we enter the secret sauce: CM_Norm adjusts the colour-bar, such that its point of origin is equal to the accuracy expected for a random prediction. Essentially, the “naive-prediction accuracy” is our “point of origin” because a model which predicts worse than a coin-flip, is not a helpful model to begin with (hence the name: “coin-flip confusion-matrix”). In other words, we are interested in a models “excess performance”, rather than its “absolute” error rates. To give two examples: For 3 different classes, the “point of origin”, of the colour-bar, would be set at 1/3, or for 10 classes it would be set at 1/10.
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
import numpy as np
def plot_cm_standard(y_true, y_pred, list_classes: list, normalize: str, title: str=None, ax=None):
""" plot the standard confusion matrix!
:param y_true: np.array, the true values
:param y_pred: np.array, the predicted values
:param list_classes: list, of names of the classes
:param normalize: str, either None, prediction or true
:param title: str, title of the plot
"""
# color map and normalization
cmap = sns.diverging_palette(145, 325, s=200, as_cmap=True)
norm = CM_Norm(midpoint=1/len(list_classes), vmin=0, vmax=1)
# the confusion matrix
cm = confusion_matrix(y_true=y_true, y_pred=y_pred)
# use normalization?
if normalize == 'prediction':
cm = np.round(cm.astype('float') / cm.sum(axis=0)[np.newaxis, :], 2)
elif normalize == 'true':
cm = np.round(cm.astype('float') / cm.sum(axis=1)[:, np.newaxis], 2)
ax = sns.heatmap(cm, annot=True, cmap=cmap, square=True, annot_kws={'fontsize':18}, ax=ax, vmin=0, vmax=1)
# axis labels
ax.set_xticklabels(list_classes)
ax.set_yticklabels(list_classes)
# titles and labels
accuracy = np.round(accuracy_score(y_true=y_test, y_pred=y_pred), 2)
#compute accuracy
ax.set_title(title + f" (Acc.: {accuracy})")
ax.set_ylabel('True')
ax.set_xlabel('Prediction')
# layout
plt.grid(False)
plt.tight_layout()
class CM_Norm(plt.cm.colors.Normalize):
""" normalize the colorbar around a value
"""
def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False):
self.midpoint = midpoint
plt.cm.colors.Normalize.__init__(self, vmin, vmax, clip)
def __call__(self, value, clip=None):
x, y = [self.vmin, self.midpoint, self.vmax], [0, 0.5, 1]
return np.ma.masked_array(np.interp(value, x, y), np.isnan(value))
def plot_cm(y_true, y_pred, list_classes: list, normalize: str, title: str=None, ax=None):
""" plot the confusion matrix and normalize the values
:param y_true: np.array, the true values
:param y_pred: np.array, the predicted values
:param list_classes: list, of names of the classes
:param normalize: str, either None, prediction or true
:param title: str, title of the plot
"""
from sklearn.metrics import accuracy_score, confusion_matrix
# color map and normalization
cmap = sns.diverging_palette(145, 325, s=200, as_cmap=True)
norm = CM_Norm(midpoint=1/len(list_classes), vmin=0, vmax=1)
# the confusion matrix
cm = confusion_matrix(y_true=y_true, y_pred=y_pred)
# use normalization?
if normalize == 'prediction':
cm = np.round(cm.astype('float') / cm.sum(axis=0)[np.newaxis, :], 2)
elif normalize == 'true':
cm = np.round(cm.astype('float') / cm.sum(axis=1)[:, np.newaxis], 2)
ax = sns.heatmap(cm, annot=True, cmap=cmap, norm=norm, square=True, annot_kws={'fontsize':18}, ax=ax)
# axis labels
ax.set_xticklabels(list_classes)
ax.set_yticklabels(list_classes)
# titles and labels
accuracy = np.round(accuracy_score(y_true=y_test, y_pred=y_pred), 2)
#compute accuracy
ax.set_title(title + f" (Acc.: {accuracy})")
ax.set_ylabel('True')
ax.set_xlabel('Prediction')
# layout
plt.grid(False)
plt.tight_layout()
fig, (ax1, ax2) = plt.subplots(1,2, figsize=(20,8)) plot_cm(y_true=y_test, y_pred=prediction, title="Awesome Model", list_classes=[str(i) for i in range(n_classes)], normalize="prediction", ax=ax1) plot_cm(y_true=y_test, y_pred=prediction_naive, title="Rolling Dice Model", list_classes=[str(i) for i in range(n_classes)], normalize="prediction", ax=ax2) plt.show()
n_classes = 10 X, y = make_classification(n_samples=10000, n_features=10, n_classes=n_classes, n_clusters_per_class=1, n_informative=10, n_redundant=0) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=0) prediction_naive = np.random.randint(low=0, high=n_classes, size=len(y_test)) clf = LogisticRegression().fit(X_train, y_train) prediction = clf.predict(X_test)
fig, (ax1, ax2) = plt.subplots(1,2, figsize=(20,8)) plot_cm_standard(y_true=y_test, y_pred=prediction, title="Awesome Model", list_classes=[str(i) for i in range(n_classes)], normalize="prediction", ax=ax1) plot_cm_standard(y_true=y_test, y_pred=prediction_naive, title="Rolling Dice Model", list_classes=[str(i) for i in range(n_classes)], normalize="prediction", ax=ax2) fig, (ax1, ax2) = plt.subplots(1,2, figsize=(20,8)) plot_cm(y_true=y_test, y_pred=prediction, title="Our Awesome Model", list_classes=[str(i) for i in range(n_classes)], normalize="prediction", ax=ax1) plot_cm(y_true=y_test, y_pred=prediction_naive, title="Rolling Dice", list_classes=[str(i) for i in range(n_classes)], normalize="prediction", ax=ax2) plt.show()
I would like to share a few key takeaways from the article:
A. In machine learning, a confusion matrix is a table that is used to evaluate the performance of a classification model by comparing the predicted class labels to the actual class labels. It summarizes the number of correct and incorrect predictions made by the model for each class.
A. A 4×4 confusion matrix is a table with 4 rows and 4 columns that is commonly used to evaluate the performance of a multi-class classification model that has 4 classes. The rows represent the actual class labels, while the columns represent the predicted class labels. Each entry in the matrix represents the number of samples that belong to a particular actual class and were predicted to belong to a particular predicted class.
The confusion matrix is used to evaluate the performance of a classification model by checking how well it has predicted the class labels of the samples in the test dataset. It provides a way to visualize the performance of the model by summarizing the number of correct and incorrect predictions made by the model.
Thanks for reading! Hope you liked my article on confusion matrix!
The media shown in this article is not owned by Analytics Vidhya and is used at the Author’s discretion.