Deep learning / Activation functions and loss
Binary Cross-Entropy Limitations for Imbalanced Datasets
Contents
- Binary Cross-Entropy: Challenges with Imbalanced Datasets
- Understanding Binary Cross-Entropy
- The Problem with Imbalanced Datasets
- Visualizing the Problem
- Why Binary Cross-Entropy Struggles
- Alternative 1: Weighted Binary Cross-Entropy
- Alternative 2: Focal Loss
- Alternative 3: Oversampling
- Alternative 4: Undersampling
- Real-Life Example: Medical Diagnosis
- Real-Life Example: Anomaly Detection in Network Traffic
- Comparing Different Approaches
- Conclusion and Best Practices
- Additional Resources
Binary Cross-Entropy: Challenges with Imbalanced Datasets
Binary cross-entropy is a popular loss function for binary classification tasks. However, it can be problematic when dealing with imbalanced datasets. This presentation will explore why and propose alternative approaches.
import numpy as np
import matplotlib.pyplot as plt
# Create an imbalanced dataset
np.random.seed(42)
class_0 = np.random.normal(0, 1, (1000, 2))
class_1 = np.random.normal(3, 1, (100, 2))
plt.scatter(class_0[:, 0], class_0[:, 1], label='Class 0')
plt.scatter(class_1[:, 0], class_1[:, 1], label='Class 1')
plt.title('Imbalanced Dataset')
plt.legend()
plt.show()
Understanding Binary Cross-Entropy
Binary cross-entropy measures the performance of a classification model whose output is a probability value between 0 and 1. It increases as the predicted probability diverges from the actual label.
def binary_cross_entropy(y_true, y_pred):
return -np.mean(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))
# Example
y_true = np.array([1, 0, 1, 1, 0])
y_pred = np.array([0.9, 0.1, 0.8, 0.7, 0.2])
loss = binary_cross_entropy(y_true, y_pred)
print(f"Binary Cross-Entropy Loss: {loss:.4f}")
The Problem with Imbalanced Datasets
In imbalanced datasets, the model may achieve high accuracy by simply predicting the majority class, leading to poor performance on the minority class.
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, f1_score
# Prepare imbalanced dataset
X = np.vstack((class_0, class_1))
y = np.hstack((np.zeros(1000), np.ones(100)))
# Split the data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Train a logistic regression model
model = LogisticRegression()
model.fit(X_train, y_train)
# Predict and evaluate
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
print(f"Accuracy: {accuracy:.4f}")
print(f"F1 Score: {f1:.4f}")
Visualizing the Problem
Let's visualize how the model performs on our imbalanced dataset. We'll plot the decision boundary and see how it affects both classes.
def plot_decision_boundary(model, X, y):
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1),
np.arange(y_min, y_max, 0.1))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, alpha=0.4)
plt.scatter(X[:, 0], X[:, 1], c=y, alpha=0.8)
plt.title("Decision Boundary")
plt.show()
plot_decision_boundary(model, X, y)
Why Binary Cross-Entropy Struggles
Binary cross-entropy treats all misclassifications equally, regardless of class imbalance. This can lead to the model focusing more on the majority class, potentially ignoring the minority class.
# Calculate class weights
class_weights = {0: 1 / np.sum(y == 0), 1: 1 / np.sum(y == 1)}
# Print class weights
print("Class weights:")
print(f"Class 0: {class_weights[0]:.4f}")
print(f"Class 1: {class_weights[1]:.4f}")
# Calculate weighted binary cross-entropy
def weighted_binary_cross_entropy(y_true, y_pred, class_weights):
return -np.mean(class_weights[1] * y_true * np.log(y_pred) +
class_weights[0] * (1 - y_true) * np.log(1 - y_pred))
# Example
y_true = np.array([1, 0, 1, 1, 0])
y_pred = np.array([0.9, 0.1, 0.8, 0.7, 0.2])
weighted_loss = weighted_binary_cross_entropy(y_true, y_pred, class_weights)
print(f"Weighted Binary Cross-Entropy Loss: {weighted_loss:.4f}")
Alternative 1: Weighted Binary Cross-Entropy
One solution is to use weighted binary cross-entropy, which assigns higher weights to the minority class.
from sklearn.utils.class_weight import compute_class_weight
# Compute class weights
class_weights = compute_class_weight('balanced', classes=np.unique(y), y=y)
sample_weights = np.where(y == 0, class_weights[0], class_weights[1])
# Train a weighted logistic regression model
weighted_model = LogisticRegression(class_weight='balanced')
weighted_model.fit(X_train, y_train)
# Predict and evaluate
y_pred_weighted = weighted_model.predict(X_test)
accuracy_weighted = accuracy_score(y_test, y_pred_weighted)
f1_weighted = f1_score(y_test, y_pred_weighted)
print(f"Weighted Accuracy: {accuracy_weighted:.4f}")
print(f"Weighted F1 Score: {f1_weighted:.4f}")
# Visualize the new decision boundary
plot_decision_boundary(weighted_model, X, y)
Alternative 2: Focal Loss
Focal loss is another alternative that addresses class imbalance by down-weighting easy examples and focusing on hard ones.
def focal_loss(y_true, y_pred, gamma=2.0):
pt = np.where(y_true == 1, y_pred, 1 - y_pred)
return -np.mean((1 - pt) ** gamma * np.log(pt))
# Example
y_true = np.array([1, 0, 1, 1, 0])
y_pred = np.array([0.9, 0.1, 0.8, 0.7, 0.2])
focal_loss_value = focal_loss(y_true, y_pred)
print(f"Focal Loss: {focal_loss_value:.4f}")
Alternative 3: Oversampling
Oversampling involves increasing the number of samples in the minority class to balance the dataset.
from imblearn.over_sampling import SMOTE
# Apply SMOTE
smote = SMOTE(random_state=42)
X_resampled, y_resampled = smote.fit_resample(X, y)
# Train a model on the resampled data
oversampled_model = LogisticRegression()
oversampled_model.fit(X_resampled, y_resampled)
# Predict and evaluate
y_pred_oversampled = oversampled_model.predict(X_test)
accuracy_oversampled = accuracy_score(y_test, y_pred_oversampled)
f1_oversampled = f1_score(y_test, y_pred_oversampled)
print(f"Oversampled Accuracy: {accuracy_oversampled:.4f}")
print(f"Oversampled F1 Score: {f1_oversampled:.4f}")
# Visualize the new decision boundary
plot_decision_boundary(oversampled_model, X, y)
Alternative 4: Undersampling
Undersampling involves reducing the number of samples in the majority class to balance the dataset.
from imblearn.under_sampling import RandomUnderSampler
# Apply Random Undersampling
rus = RandomUnderSampler(random_state=42)
X_resampled, y_resampled = rus.fit_resample(X, y)
# Train a model on the resampled data
undersampled_model = LogisticRegression()
undersampled_model.fit(X_resampled, y_resampled)
# Predict and evaluate
y_pred_undersampled = undersampled_model.predict(X_test)
accuracy_undersampled = accuracy_score(y_test, y_pred_undersampled)
f1_undersampled = f1_score(y_test, y_pred_undersampled)
print(f"Undersampled Accuracy: {accuracy_undersampled:.4f}")
print(f"Undersampled F1 Score: {f1_undersampled:.4f}")
# Visualize the new decision boundary
plot_decision_boundary(undersampled_model, X, y)
Real-Life Example: Medical Diagnosis
In medical diagnosis, imbalanced datasets are common. For instance, in detecting a rare disease, the majority of samples will be negative. Using binary cross-entropy might lead to a model that always predicts "no disease," missing crucial positive cases.
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
# Simulate a medical dataset
np.random.seed(42)
n_samples = 1000
n_features = 5
X = np.random.randn(n_samples, n_features)
y = np.random.choice([0, 1], size=n_samples, p=[0.95, 0.05]) # 5% disease prevalence
# Split the data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Train a standard model
model = LogisticRegression()
model.fit(X_train, y_train)
# Predict and evaluate
y_pred = model.predict(X_test)
print("Standard Model Results:")
print(classification_report(y_test, y_pred))
# Train a weighted model
weighted_model = LogisticRegression(class_weight='balanced')
weighted_model.fit(X_train, y_train)
# Predict and evaluate
y_pred_weighted = weighted_model.predict(X_test)
print("\nWeighted Model Results:")
print(classification_report(y_test, y_pred_weighted))
Real-Life Example: Anomaly Detection in Network Traffic
In network security, detecting malicious traffic is crucial. However, malicious packets are often rare compared to normal traffic, creating an imbalanced dataset.
from sklearn.ensemble import IsolationForest
# Simulate network traffic data
np.random.seed(42)
n_samples = 10000
n_features = 10
X = np.random.randn(n_samples, n_features)
y = np.ones(n_samples) # 1 for normal traffic
# Add anomalies (1% of the data)
n_anomalies = int(0.01 * n_samples)
anomaly_indices = np.random.choice(n_samples, n_anomalies, replace=False)
X[anomaly_indices] = np.random.uniform(low=10, high=20, size=(n_anomalies, n_features))
y[anomaly_indices] = -1 # -1 for anomalies
# Split the data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Train an Isolation Forest model
clf = IsolationForest(contamination=0.01, random_state=42)
clf.fit(X_train)
# Predict and evaluate
y_pred = clf.predict(X_test)
print("Isolation Forest Results:")
print(classification_report(y_test, y_pred))
# Visualize results (for 2 features)
plt.figure(figsize=(10, 6))
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_pred, cmap='viridis')
plt.title("Anomaly Detection in Network Traffic")
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
plt.colorbar(label='Prediction')
plt.show()
Comparing Different Approaches
Let's compare the performance of different approaches on our imbalanced dataset.
from sklearn.metrics import precision_recall_fscore_support
def evaluate_model(y_true, y_pred, model_name):
precision, recall, f1, _ = precision_recall_fscore_support(y_true, y_pred, average='binary')
print(f"{model_name}:")
print(f"Precision: {precision:.4f}")
print(f"Recall: {recall:.4f}")
print(f"F1 Score: {f1:.4f}\n")
# Evaluate standard model
evaluate_model(y_test, y_pred, "Standard Model")
# Evaluate weighted model
evaluate_model(y_test, y_pred_weighted, "Weighted Model")
# Evaluate oversampled model
evaluate_model(y_test, y_pred_oversampled, "Oversampled Model")
# Evaluate undersampled model
evaluate_model(y_test, y_pred_undersampled, "Undersampled Model")
# Visualize results
models = ['Standard', 'Weighted', 'Oversampled', 'Undersampled']
f1_scores = [f1_score(y_test, y_pred),
f1_score(y_test, y_pred_weighted),
f1_score(y_test, y_pred_oversampled),
f1_score(y_test, y_pred_undersampled)]
plt.bar(models, f1_scores)
plt.title('F1 Scores of Different Models')
plt.ylabel('F1 Score')
plt.show()
Conclusion and Best Practices
When dealing with imbalanced datasets:
- Always consider the nature of your problem and the costs associated with different types of errors.
- Use appropriate evaluation metrics like precision, recall, and F1 score instead of just accuracy.
- Try different approaches: weighted loss functions, resampling techniques, or algorithms designed for imbalanced data.
- Collect more data for the minority class if possible.
- Consider ensemble methods that combine multiple approaches.
Remember, there's no one-size-fits-all solution. The best approach depends on your specific dataset and problem.
Additional Resources
For more information on handling imbalanced datasets, consider these peer-reviewed articles:
- "Learning from Imbalanced Data" by He et al. (2009) ArXiv: https://arxiv.org/abs/1505.01658
- "A Survey of Predictive Modelling under Imbalanced Distributions" by Branco et al. (2016) ArXiv: https://arxiv.org/abs/1505.01658
- "SMOTE: Synthetic Minority Over-sampling Technique" by Chawla et al. (2002) Journal of Artificial Intelligence Research
These resources provide in-depth discussions on various techniques for handling imbalanced datasets and their applications in different domains.