Skip to content

Commit

Permalink
Add tests for computation of plausible counterfactuals
Browse files Browse the repository at this point in the history
  • Loading branch information
andreArtelt committed Jun 3, 2020
1 parent 12f2782 commit b8e3947
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 2 deletions.
85 changes: 84 additions & 1 deletion tests/sklearn/test_sklearn_decisiontree.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,95 @@
sys.path.insert(0,'..')

import numpy as np
import random
random.seed(424242)
import sklearn
from sklearn.datasets import load_iris, load_boston
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KernelDensity
from sklearn.mixture import GaussianMixture
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor
from sklearn.utils import shuffle

from ceml.sklearn import generate_counterfactual
from ceml.sklearn.decisiontree import decisiontree_generate_counterfactual
from ceml.sklearn.plausibility import prepare_computation_of_plausible_counterfactuals


def test_plausible_counterfactual():
# Load data
X, y = load_iris(return_X_y=True)

X, y = shuffle(X, y, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=4242)

# Choose target labels
y_test_target = []
labels = np.unique(y)
for i in range(X_test.shape[0]):
y_test_target.append(random.choice(list(filter(lambda l: l != y_test[i], labels))))
y_test_target = np.array(y_test_target)

# Reduce dimensionality
X_train_orig = np.copy(X_train)
X_test_orig = np.copy(X_test)
projection_matrix = None
projection_mean_sub = None

# Fit classifier
model = DecisionTreeClassifier(max_depth=7, random_state=42)
model.fit(X_train, y_train)

# For each class, fit density estimators
density_estimators = {}
kernel_density_estimators = {}
labels = np.unique(y)
for label in labels:
# Get all samples with the 'correct' label
idx = y_train == label
X_ = X_train[idx, :]

# Optimize hyperparameters
cv = GridSearchCV(estimator=KernelDensity(), param_grid={'bandwidth': np.arange(0.1, 10.0, 0.05)}, n_jobs=-1, cv=5)
cv.fit(X_)
bandwidth = cv.best_params_["bandwidth"]

cv = GridSearchCV(estimator=GaussianMixture(covariance_type='full'), param_grid={'n_components': range(2, 10)}, n_jobs=-1, cv=5)
cv.fit(X_)
n_components = cv.best_params_["n_components"]

# Build density estimators
kde = KernelDensity(bandwidth=bandwidth)
kde.fit(X_)

de = GaussianMixture(n_components=n_components, covariance_type='full', random_state=42)
de.fit(X_)

density_estimators[label] = de
kernel_density_estimators[label] = kde

# Build dictionary for ceml
plausibility_stuff = prepare_computation_of_plausible_counterfactuals(X_train_orig, y_train, density_estimators, projection_mean_sub, projection_matrix)

# Compute and plot counterfactual with vs. without density constraints
i = 0

x_orig = X_test[i,:]
x_orig_orig = X_test_orig[i,:]
y_orig = y_test[i]
y_target = y_test_target[i]

assert model.predict([x_orig]) == y_orig # Model already predicts target label!

# Compute plausible counterfactual
x_cf_plausible = decisiontree_generate_counterfactual(model, x_orig_orig, y_target, plausibility=plausibility_stuff)
print("Predictec label of plausible countrefactual: {0}".format(model.predict([x_cf_plausible])))
assert model.predict([x_cf_plausible]) == y_target

# Compute closest counterfactual
plausibility_stuff["use_density_constraints"] = False
x_cf = decisiontree_generate_counterfactual(model, x_orig_orig, y_target, plausibility=plausibility_stuff)
assert model.predict([x_cf]) == y_target


def test_decisiontree_classifier():
Expand Down
85 changes: 84 additions & 1 deletion tests/sklearn/test_sklearn_softmaxregression.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,21 @@

import numpy as np
np.random.seed(42)
import random
random.seed(424242)
from sklearn.utils import shuffle
import pytest
import sklearn
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KernelDensity
from sklearn.mixture import GaussianMixture
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.linear_model import LogisticRegression
from scipy.optimize import minimize

from ceml.sklearn import generate_counterfactual
from ceml.sklearn.softmaxregression import softmaxregression_generate_counterfactual
from ceml.sklearn.plausibility import prepare_computation_of_plausible_counterfactuals
from ceml.optim import Optimizer
from ceml.backend.jax.costfunctions import LMadCost

Expand Down Expand Up @@ -42,6 +49,82 @@ def __call__(self):
return np.array(optimum["x"])


def test_plausible_counterfactual():
# Load data
X, y = load_iris(return_X_y=True)

X, y = shuffle(X, y, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=4242)

# Choose target labels
y_test_target = []
labels = np.unique(y)
for i in range(X_test.shape[0]):
y_test_target.append(random.choice(list(filter(lambda l: l != y_test[i], labels))))
y_test_target = np.array(y_test_target)

# Reduce dimensionality
X_train_orig = np.copy(X_train)
X_test_orig = np.copy(X_test)
projection_matrix = None
projection_mean_sub = None

# Fit classifier
model = LogisticRegression(multi_class="multinomial", solver="lbfgs", random_state=42)
model.fit(X_train, y_train)

# For each class, fit density estimators
density_estimators = {}
kernel_density_estimators = {}
labels = np.unique(y)
for label in labels:
# Get all samples with the 'correct' label
idx = y_train == label
X_ = X_train[idx, :]

# Optimize hyperparameters
cv = GridSearchCV(estimator=KernelDensity(), param_grid={'bandwidth': np.arange(0.1, 10.0, 0.05)}, n_jobs=-1, cv=5)
cv.fit(X_)
bandwidth = cv.best_params_["bandwidth"]

cv = GridSearchCV(estimator=GaussianMixture(covariance_type='full'), param_grid={'n_components': range(2, 10)}, n_jobs=-1, cv=5)
cv.fit(X_)
n_components = cv.best_params_["n_components"]

# Build density estimators
kde = KernelDensity(bandwidth=bandwidth)
kde.fit(X_)

de = GaussianMixture(n_components=n_components, covariance_type='full', random_state=42)
de.fit(X_)

density_estimators[label] = de
kernel_density_estimators[label] = kde

# Build dictionary for ceml
plausibility_stuff = prepare_computation_of_plausible_counterfactuals(X_train_orig, y_train, density_estimators, projection_mean_sub, projection_matrix)

# Compute and plot counterfactual with vs. without density constraints
i = 0

x_orig = X_test[i,:]
x_orig_orig = X_test_orig[i,:]
y_orig = y_test[i]
y_target = y_test_target[i]

assert model.predict([x_orig]) == y_orig # Model already predicts target label!

# Compute plausible counterfactual
x_cf_plausible = softmaxregression_generate_counterfactual(model, x_orig_orig, y_target, plausibility=plausibility_stuff)
print("Predictec label of plausible countrefactual: {0}".format(model.predict([x_cf_plausible])))
assert model.predict([x_cf_plausible]) == y_target

# Compute closest counterfactual
plausibility_stuff["use_density_constraints"] = False
x_cf = softmaxregression_generate_counterfactual(model, x_orig_orig, y_target, plausibility=plausibility_stuff)
assert model.predict([x_cf]) == y_target


def test_softmaxregression():
# Load data
X, y = load_iris(True)
Expand Down

0 comments on commit b8e3947

Please sign in to comment.