def describe_cf_instance(X, explanation, class_names, cat_vars_ohe=None, category_map=None, feature_names=None, eps=1e-2): print("Instance Outcomes and Probabilities") print("-" * 48) max_len = len(max(feature_names, key=len)) print('{}: {}\r\n{} {}'.format('original'.rjust(max_len), class_names[explanation.orig_class], " " * max_len, explanation.orig_proba[0])) if explanation.cf != None: print('{}: {}\r\n{} {}'.format('counterfactual'.rjust(max_len), class_names[explanation.cf['class']], " " * max_len, explanation.cf['proba'][0])) print("\r\nCategorical Feature Counterfactual Perturbations") print("-" * 48) X_orig_ord = ohe_to_ord(X, cat_vars_ohe)[0] try: X_cf_ord = ohe_to_ord(explanation.cf['X'], cat_vars_ohe)[0] except: X_cf_ord = ohe_to_ord(explanation.cf['X'].transpose(), cat_vars_ohe)[0].transpose() delta_cat = {} for _, (i, v) in enumerate(category_map.items()): cat_orig = v[int(X_orig_ord[0, i])] cat_cf = v[int(X_cf_ord[0, i])] if cat_orig != cat_cf: delta_cat[feature_names[i]] = [cat_orig, cat_cf] if delta_cat: for k, v in delta_cat.items(): print("\t{}: {} --> {}".format(k.rjust(max_len), v[0], v[1])) print("\r\nNumerical Feature Counterfactual Perturbations") print("-" * 48) num_idxs = [i for i in list(range(0,len(feature_names)))\ if i not in category_map.keys()] delta_num = X_cf_ord[0, num_idxs] - X_orig_ord[0, num_idxs] for i in range(delta_num.shape[0]): if np.abs(delta_num[i]) > eps: print("\t{}: {:.2f} --> {:.2f}".format( feature_names[i].rjust(max_len), X_orig_ord[0, i], X_cf_ord[0, i])) else: print("\tNO COUNTERFACTUALS")
def test_tf_keras_adult_explainer(disable_tf2, adult_data, tf_keras_adult_explainer, use_kdtree, k, d_type): model, cf = tf_keras_adult_explainer X_train = adult_data['preprocessor'].transform( adult_data['X_train']).toarray() # instance to be explained x = X_train[0].reshape(1, -1) pred_class = np.argmax(model.predict(x)) not_pred_class = np.argmin(model.predict(x)) # test fit cf.fit(X_train, d_type=d_type) # checked ragged tensor shape n_cat = len(list(cf.cat_vars_ord.keys())) max_key = max(cf.cat_vars_ord, key=cf.cat_vars_ord.get) max_cat = cf.cat_vars_ord[max_key] assert cf.d_abs_ragged.shape == (n_cat, max_cat) if use_kdtree: # k-d trees assert len(cf.kdtrees) == cf.classes # each class has a k-d tree n_by_class = 0 for c in range(cf.classes): n_by_class += cf.X_by_class[c].shape[0] assert n_by_class == X_train.shape[ 0] # all training instances are stored in the trees # test explanation explanation = cf.explain(x, k=k) if use_kdtree: assert cf.id_proto != pred_class assert np.argmax(model.predict( explanation.cf['X'])) == explanation.cf['class'] num_shape = (1, 12) assert explanation.cf['grads_num'].shape == explanation.cf[ 'grads_graph'].shape == num_shape assert explanation.meta.keys() == DEFAULT_META_CFP.keys() assert explanation.data.keys() == DEFAULT_DATA_CFP.keys() # test gradient shapes y = np.zeros((1, cf.classes)) np.put(y, pred_class, 1) cf.predict = cf.predict.predict # make model black box # convert instance to numerical space x_ord = ohe_to_ord(x, cf.cat_vars)[0] x_num = ord_to_num(x_ord, cf.d_abs) # check gradients grads = cf.get_gradients(x_num, y, num_shape[1:], cf.cat_vars_ord) assert grads.shape == num_shape
def test_mapping_fn(): # ohe_to_ord_shape assert mp.ohe_to_ord_shape(shape_ohe, cat_vars_ohe, is_ohe=True) == shape_ord # ohe_to_ord X_ohe_to_ord, cat_vars_ohe_to_ord = mp.ohe_to_ord(X_ohe, cat_vars_ohe) assert (X_ohe_to_ord == X_ord).all() and cat_vars_ohe_to_ord == cat_vars_ord # ord_to_ohe X_ord_to_ohe, cat_vars_ord_to_ohe = mp.ord_to_ohe(X_ord, cat_vars_ord) assert (X_ord_to_ohe == X_ohe).all() and cat_vars_ohe == cat_vars_ord_to_ohe # ord_to_num X_ord_to_num = mp.ord_to_num(X_ord, dist) assert (X_num == X_ord_to_num).all() # num_to_ord X_num_to_ord = mp.num_to_ord(X_num, dist) assert (X_ord == X_num_to_ord).all()