Skip to content

Commit

Permalink
Change logic in change factor calculation
Browse files Browse the repository at this point in the history
The current change tries to minimize the distance and increase the counterfactual score (therefore, lower distances and higher prediction scores are preferred)
  • Loading branch information
rmazzine committed Aug 12, 2022
1 parent 2c0c160 commit 1d2f921
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 3 deletions.
19 changes: 17 additions & 2 deletions cfnow/_fine_tune.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,25 @@ def _calculate_change_factor(c_cf, changes_back_factual, feat_distances, changes
# Now, calculate the probability for each different index
cf_back_factual_probs = mp1c(cf_back_factual)

prediction_dif = (c_cf_c - cf_back_factual_probs)

# Calculate how much a unitary change cause a change in probability
change_factor_feat = (c_cf_c - cf_back_factual_probs) / feat_distances[changes_back_original_idxs]
# The probability should be as negative as possible
# The distance (objective function) should be as close to zero as possible (as it cannot be negative)
# For a positive probability, we just multiply the prediction change by the distance
# For a negative probability, we just divide the prediction change by the distance
change_factor_feat = []
for f_pc, f_d in zip(prediction_dif, feat_distances[changes_back_original_idxs]):
if f_pc >= 0:
change_factor_feat.append(f_pc*f_d)
else:
# Avoid division by zero
if f_d == 0:
change_factor_feat.append(0)
else:
change_factor_feat.append(f_pc/f_d)

return change_factor_feat
return np.array(change_factor_feat)


def _generate_change_vectors(factual, factual_np, c_cf, _feat_idx_to_type, tabu_list,
Expand Down
4 changes: 4 additions & 0 deletions cfnow/_obj_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
In this module, we can specify the objective functions which the CF generator can use to optimize.
The optimizer will try to get the lowest value possible (minimization) of the objective function under the
condition the CF still a CF.
The requirements for a valid objective function are:
- Lower values should be better.
- It should not return negative values or zero. (obj > 0)
"""
import numpy as np

Expand Down
33 changes: 32 additions & 1 deletion tests/test__fine_tune.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,38 @@ def test__calculate_change_factor(self):
change_factor_feat = _calculate_change_factor(c_cf, changes_back_factual, feat_distances,
changes_back_original_idxs, mp1c, c_cf_c)

self.assertListEqual([0.1, 0.125, 0.25, 0.5], list([round(n, 3) for n in change_factor_feat]))
self.assertListEqual([0.1, 0.08, 0.04, 0.02], list([round(n, 3) for n in change_factor_feat]))
self.assertIsInstance(change_factor_feat, np.ndarray)

def test__calculate_change_factor_negative(self):
c_cf = np.array([1, 1, 1, 1])
changes_back_factual = np.array([[0.1, 0., 0., 0.], [0., 0.1, 0., 0.], [0., 0., 0.1, 0.], [0., 0., 0., 0.1]])
feat_distances = np.array([1, 0.8, 0.4, 0.2])
changes_back_original_idxs = [0, 1, 2, 3]
mp1c = MagicMock()
mp1c.return_value = np.array([0.7, 0.7, 0.5, 0.5])
c_cf_c = 0.6

change_factor_feat = _calculate_change_factor(c_cf, changes_back_factual, feat_distances,
changes_back_original_idxs, mp1c, c_cf_c)

self.assertListEqual([-0.1, round(-0.1/0.8, 3), 0.04, 0.02], list([round(n, 3) for n in change_factor_feat]))
self.assertIsInstance(change_factor_feat, np.ndarray)

def test__calculate_change_factor_negative_zero_distance(self):
# This should not happen as the objective function should not return zero values
c_cf = np.array([1, 1, 1, 1])
changes_back_factual = np.array([[0.1, 0., 0., 0.], [0., 0.1, 0., 0.], [0., 0., 0.1, 0.], [0., 0., 0., 0.1]])
feat_distances = np.array([1, 0.8, 0, 0.2])
changes_back_original_idxs = [0, 1, 2, 3]
mp1c = MagicMock()
mp1c.return_value = np.array([0.7, 0.7, 0.7, 0.5])
c_cf_c = 0.6

change_factor_feat = _calculate_change_factor(c_cf, changes_back_factual, feat_distances,
changes_back_original_idxs, mp1c, c_cf_c)

self.assertListEqual([-0.1, round(-0.1/0.8, 3), 0, 0.02], list([round(n, 3) for n in change_factor_feat]))
self.assertIsInstance(change_factor_feat, np.ndarray)

def test__generate_change_vectors_all_num_no_tabu(self):
Expand Down

0 comments on commit 1d2f921

Please sign in to comment.