num_epochs = 800 for epoch in range(num_epochs): optimizer.zero_grad() outputs = model(x) loss = criterion(outputs, labels) loss.backward() optimizer.step() # Evaluation y_pred = model.predict(x_test).numpy() print("Accuracy: {0}".format(accuracy_score(y_test, y_pred))) # Select a data point whose prediction has to be explained x_orig = X_test[1, :] print("Prediction on x: {0}".format( model.predict(torch.from_numpy(np.array([x_orig]))))) # Whitelist of features we can use/change when computing the counterfactual features_whitelist = [0, 2] # Use the first and third feature only # Compute counterfactual print("\nCompute counterfactual ....") print( generate_counterfactual(model, x_orig, y_target=0, features_whitelist=features_whitelist, regularization="l1", C=0.1, optimizer="nelder-mead"))
def test_softmaxregression(): # Neural network - Softmax regression class Model(torch.nn.Module, ModelWithLoss): def __init__(self, input_size, num_classes): super(Model, self).__init__() self.linear = torch.nn.Linear(input_size, num_classes) self.softmax = torch.nn.Softmax(dim=0) def forward(self, x): return self.linear( x) # NOTE: Softmax is build into CrossEntropyLoss def predict_proba(self, x): return self.softmax(self.forward(x)) def predict(self, x, dim=1): return torch.argmax(self.forward(x), dim=dim) def get_loss(self, y_target, pred=None): return NegLogLikelihoodCost(input_to_output=self.predict_proba, y_target=y_target) # Load data X, y = load_iris(return_X_y=True) X = X.astype(np.dtype(np.float32)) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) # numpy -> torch tensor x = torch.from_numpy(X_train) labels = torch.from_numpy(y_train) x_test = torch.from_numpy(X_test) y_test = torch.from_numpy(y_test) # Create and fit model model = Model(4, 3) learning_rate = 0.001 momentum = 0.9 criterion = torch.nn.CrossEntropyLoss() optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum) num_epochs = 800 for epoch in range(num_epochs): optimizer.zero_grad() outputs = model(x) loss = criterion(outputs, labels) loss.backward() optimizer.step() # Evaluation y_pred = model.predict(x_test).detach().numpy() assert accuracy_score(y_test, y_pred) >= 0.8 # Select data point for explaining its prediction x_orig = X_test[1, :] assert model.predict(torch.from_numpy(np.array([x_orig ]))).detach().numpy() == 1 # Create weighted manhattan distance cost function md = np.median(X_train, axis=0) mad = np.median(np.abs(X_train - md), axis=0) regularization_mad = LMadCost(torch.from_numpy(x_orig), torch.from_numpy(mad)) # Compute counterfactual features_whitelist = None optimizer = "bfgs" optimizer_args = {"max_iter": 1000, "args": {"lr": 0.9, "momentum": 0.9}} x_cf, y_cf, delta = generate_counterfactual( model, x_orig, y_target=0, features_whitelist=features_whitelist, regularization="l2", C=0.001, optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False) assert y_cf == 0 assert model.predict(torch.from_numpy(np.array( [x_cf], dtype=np.float32))).detach().numpy() == 0 x_cf, y_cf, delta = generate_counterfactual( model, x_orig, y_target=0, features_whitelist=features_whitelist, regularization="l2", C=0.001, optimizer=MyOptimizer(), optimizer_args=optimizer_args, return_as_dict=False) assert y_cf == 0 assert model.predict(torch.from_numpy(np.array( [x_cf], dtype=np.float32))).detach().numpy() == 0 optimizer = "bfgs" optimizer_args = {"max_iter": 1000, "args": {"lr": 0.9, "momentum": 0.9}} x_cf, y_cf, delta = generate_counterfactual( model, x_orig, y_target=0, features_whitelist=features_whitelist, regularization=regularization_mad, C=0.001, optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False) assert y_cf == 0 assert model.predict(torch.from_numpy(np.array( [x_cf], dtype=np.float32))).detach().numpy() == 0 optimizer = "nelder-mead" x_cf, y_cf, delta = generate_counterfactual( model, x_orig, y_target=0, features_whitelist=features_whitelist, regularization="l1", C=0.001, optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False) assert y_cf == 0 assert model.predict(torch.from_numpy(np.array( [x_cf], dtype=np.float32))).detach().numpy() == 0 optimizer = torch.optim.SGD x_cf, y_cf, delta = generate_counterfactual( model, x_orig, y_target=0, features_whitelist=features_whitelist, regularization="l2", C=0.001, optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False) assert y_cf == 0 assert model.predict(torch.from_numpy(np.array( [x_cf], dtype=np.float32))).detach().numpy() == 0 optimizer = "bfgs" optimizer_args = {"max_iter": 1000, "args": {"lr": 0.9, "momentum": 0.9}} x_cf, y_cf, delta = generate_counterfactual( model, x_orig, y_target=0, features_whitelist=features_whitelist, regularization=None, optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False) assert y_cf == 0 assert model.predict(torch.from_numpy(np.array( [x_cf], dtype=np.float32))).detach().numpy() == 0 optimizer = torch.optim.SGD x_cf, y_cf, delta = generate_counterfactual( model, x_orig, y_target=0, features_whitelist=features_whitelist, regularization=None, optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False) assert y_cf == 0 assert model.predict(torch.from_numpy(np.array( [x_cf], dtype=np.float32))).detach().numpy() == 0 features_whitelist = [0, 2] optimizer = "bfgs" optimizer_args = {"max_iter": 1000, "args": {"lr": 0.9, "momentum": 0.9}} x_cf, y_cf, delta = generate_counterfactual( model, x_orig, y_target=0, features_whitelist=features_whitelist, regularization="l1", C=0.001, optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False) assert y_cf == 0 assert model.predict(torch.from_numpy(np.array( [x_cf], dtype=np.float32))).detach().numpy() == 0 assert all([ True if i in features_whitelist else delta[i] == 0. for i in range(x_orig.shape[0]) ]) optimizer = "nelder-mead" x_cf, y_cf, delta = generate_counterfactual( model, x_orig, y_target=0, features_whitelist=features_whitelist, regularization="l2", C=0.001, optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False) assert y_cf == 0 assert model.predict(torch.from_numpy(np.array( [x_cf], dtype=np.float32))).detach().numpy() == 0 assert all([ True if i in features_whitelist else delta[i] == 0. for i in range(x_orig.shape[0]) ]) optimizer = torch.optim.SGD x_cf, y_cf, delta = generate_counterfactual( model, x_orig, y_target=0, features_whitelist=features_whitelist, regularization="l2", C=0.0001, optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False) assert y_cf == 0 assert model.predict(torch.from_numpy(np.array( [x_cf], dtype=np.float32))).detach().numpy() == 0 assert all([ True if i in features_whitelist else delta[i] == 0. for i in range(x_orig.shape[0]) ]) optimizer = "bfgs" x_cf, y_cf, delta = generate_counterfactual( model, x_orig, y_target=0, features_whitelist=features_whitelist, regularization=None, optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False) assert y_cf == 0 assert model.predict(torch.from_numpy(np.array( [x_cf], dtype=np.float32))).detach().numpy() == 0 assert all([ True if i in features_whitelist else delta[i] == 0. for i in range(x_orig.shape[0]) ]) optimizer = torch.optim.SGD x_cf, y_cf, delta = generate_counterfactual( model, x_orig, y_target=0, features_whitelist=features_whitelist, regularization=None, optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False) assert y_cf == 0 assert model.predict(torch.from_numpy(np.array( [x_cf], dtype=np.float32))).detach().numpy() == 0 assert all([ True if i in features_whitelist else delta[i] == 0. for i in range(x_orig.shape[0]) ])
def test_linearregression(): # Neural network - Linear regression class Model(torch.nn.Module, ModelWithLoss): def __init__(self, input_size): super(Model, self).__init__() self.linear = torch.nn.Linear(input_size, 1) def forward(self, x): return self.linear(x) def predict(self, x, dim=None): # Note: In contrast to classification, the parameter `dim` is not used! return self.forward(x) def get_loss(self, y_target, pred=None): return SquaredError(self.predict, y_target) # Load data X, y = load_boston(True) X = X.astype(np.dtype(np.float32)) y = y.astype(np.dtype(np.float32)) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) # numpy -> torch tensor x = torch.from_numpy(X_train) labels = torch.from_numpy(y_train.reshape(y_train.shape[0], -1)) x_test = torch.from_numpy(X_test) y_test = torch.from_numpy(y_test) # Create and fit model model = Model(X_train.shape[1]) learning_rate = 0.1 criterion = torch.nn.MSELoss() optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate, weight_decay=0.01) num_epochs = 3000 for epoch in range(num_epochs): optimizer.zero_grad() outputs = model(x) loss = criterion(outputs, labels) loss.backward() optimizer.step() # Evaluation y_pred = model.predict(x_test).detach().numpy() assert r2_score(y_test, y_pred) >= 0.6 # Select data point for explaining its prediction x_orig = X_test[1:4][1,:] y_orig_pred = model.predict(torch.from_numpy(np.array([x_orig], dtype=np.float32))) assert y_orig_pred >= 16. and y_orig_pred < 20. # Compute counterfactual features_whitelist = None y_target = 30. y_target_done = lambda z: np.abs(z - y_target) < 6. optimizer = "bfgs" optimizer_args = {"max_iter": 1000, "args": {"lr": 0.1}} x_cf, y_cf, delta = generate_counterfactual(model, x_orig, y_target=y_target, features_whitelist=features_whitelist, regularization="l2", C=10., optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False, done=y_target_done) assert y_target_done(y_cf) assert y_target_done(model.predict(torch.from_numpy(np.array([x_cf], dtype=np.float32)))) optimizer = "nelder-mead" x_cf, y_cf, delta = generate_counterfactual(model, x_orig, y_target=y_target, features_whitelist=features_whitelist, regularization="l2", C=8., optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False, done=y_target_done) assert y_target_done(y_cf) assert y_target_done(model.predict(torch.from_numpy(np.array([x_cf], dtype=np.float32)))) optimizer = torch.optim.Adam x_cf, y_cf, delta = generate_counterfactual(model, x_orig, y_target=y_target, features_whitelist=features_whitelist, regularization="l2", C=10., optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False, done=y_target_done) assert y_target_done(y_cf) assert y_target_done(model.predict(torch.from_numpy(np.array([x_cf], dtype=np.float32)))) features_whitelist = features_whitelist = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] optimizer = "bfgs" optimizer_args = {"max_iter": 1000, "args": {"lr": 0.1}} x_cf, y_cf, delta = generate_counterfactual(model, x_orig, y_target=y_target, features_whitelist=features_whitelist, regularization="l2", C=10., optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False, done=y_target_done) assert y_target_done(y_cf) assert y_target_done(model.predict(torch.from_numpy(np.array([x_cf], dtype=np.float32)))) assert all([True if i in features_whitelist else delta[i] == 0. for i in range(x_orig.shape[0])]) optimizer = "nelder-mead" x_cf, y_cf, delta = generate_counterfactual(model, x_orig, y_target=y_target, features_whitelist=features_whitelist, regularization="l2", C=6., optimizer=optimizer, optimizer_args=optimizer_args, return_as_dict=False, done=y_target_done) assert y_target_done(y_cf) assert y_target_done(model.predict(torch.from_numpy(np.array([x_cf], dtype=np.float32)))) assert all([True if i in features_whitelist else delta[i] == 0. for i in range(x_orig.shape[0])])