Skip to content

Commit

Permalink
updates to notebook
Browse files Browse the repository at this point in the history
  • Loading branch information
swag2198 committed Jul 8, 2024
1 parent c5ed0d4 commit d80bce1
Showing 1 changed file with 64 additions and 40 deletions.
104 changes: 64 additions & 40 deletions examples/ecertify/certification_example_fico.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
"# End to end example on certifying local explanations using `Ecertify`\n",
"\n",
"In this notebook, we demonstrate how to _certify_ a local explanation for a prediction of a classification \n",
"model. Here we choose the popular tabular dataset FICO HELOC, and LIME and SHAP explainers for certification.\n",
"model. Here we choose the popular tabular dataset [FICO HELOC](https://github.com/Trusted-AI/AIX360/blob/master/examples/tutorials/HELOC.ipynb), and use local explainers such as [LIME](https://github.com/marcotcr/lime) and [SHAP](https://github.com/shap/shap) for certification. We also comment\n",
"on how the explainers can be compared on the basis of their (found) certification widths ($w$) at the end.\n",
"The cells below describe steps needed to perform certification in detail:\n",
"- obtaining a trained model on the dataset\n",
"- selecting an instance of interest and computing its explanation\n",
"- defining the quality criterion (here 1 - mean absolute error, i.e., the fidelity) to assess the degree to which this explanation is applicable to other instances\n",
"- decide the fidelity threshold $\\theta$\n",
"- certify the explanation, i.e., find the largest hypercube around the original instance where the computed explanation has sufficiently high fidelity $\\ge \\theta$"
"\n",
"1. obtaining a trained model on the dataset, here we use `GradientBoostingClassifier` from the sklearn library\n",
"2. selecting an instance of interest and computing its explanation\n",
"3. defining the quality criterion (here we use `1 - mean absolute error`, i.e., the fidelity as mentioned in the paper) to assess the degree to which the computed explanation is _applicable_ to other instances\n",
"4. decide the fidelity threshold $\\theta$, this is another user configurable option\n",
"5. certify the explanation, i.e., find the largest hypercube around the original instance where the computed explanation has sufficiently high fidelity $\\ge \\theta$"
]
},
{
Expand Down Expand Up @@ -42,18 +44,20 @@
"import numpy as np; np.set_printoptions(suppress=True)\n",
"import pandas as pd\n",
"\n",
"\n",
"# sklearn utilities\n",
"from sklearn.model_selection import train_test_split\n",
"from sklearn.ensemble import GradientBoostingClassifier, GradientBoostingRegressor\n",
"from sklearn.metrics import f1_score, accuracy_score, precision_score, recall_score, r2_score\n",
"\n",
"\n",
"\n",
"# explainers\n",
"import shap, lime\n",
"from lime.lime_tabular import LimeTabularExplainer\n",
"\n",
"# our code\n",
"from aix360.algorithms.ecertify.utils import load_fico_dataset, compute_lime_explainer, compute_shap_explainer"
"from aix360.algorithms.ecertify.utils import load_fico_dataset, compute_lime_explainer, compute_shap_explainer\n",
"# from algorithms.ecertify.utils import load_fico_dataset, compute_lime_explainer, compute_shap_explainer"
]
},
{
Expand Down Expand Up @@ -107,12 +111,21 @@
"id": "f8a8cb86",
"metadata": {},
"source": [
"## Prepare the callables for querying the model and the explanation during certification"
"## Prepare the callables for querying the model and the explanation during certification\n",
"The next cell prepares few callables: `_bb()` and `_e()` to query the `model` and the explanation. Note that\n",
"we are assuming we have obtained a functional form of the computed explainer which can be _applied_ to another\n",
"instance. In case of LIME, the explanation is a linear function with weights/coefficients set as the feature\n",
"importance values in the explanation. For KernelSHAP, a similar linear function is used as well. We apply the\n",
"function on an instance and get the correct class' (the original instance's class for which the explanation \n",
"was computed) probability.\n",
"\n",
"Later when we have the `model` and the `expl_func` ready, we can wrap these functions(`_bb()` and `_e()`) with\n",
"the `partial` _functool_ to hide the second argument (e.g., `expl_func` in `_e()`) and only pass the first argument `x`."
]
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 7,
"id": "dba4d6dc",
"metadata": {},
"outputs": [],
Expand All @@ -122,6 +135,7 @@
"def _bb(x, model, label_x0=0, is_regression=False):\n",
" \"\"\"\n",
" x: single 1d numpy array of shape (d, )\n",
" label_x0: if classification, we need to take the correct class' probability\n",
" \"\"\"\n",
" x = [x]\n",
"\n",
Expand All @@ -146,12 +160,13 @@
"id": "c2fe7c55",
"metadata": {},
"source": [
"## Choose a random sample for finding explanation and certification"
"## Choose a random sample for finding explanation and certification\n",
"Here we choose one prototype that we also discussed in the paper."
]
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 8,
"id": "0c1cbae2",
"metadata": {},
"outputs": [
Expand Down Expand Up @@ -183,7 +198,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 9,
"id": "0f832a6c",
"metadata": {},
"outputs": [
Expand Down Expand Up @@ -335,7 +350,7 @@
"PercentTradesWBalance 81.0"
]
},
"execution_count": 6,
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
Expand All @@ -347,12 +362,12 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 11,
"id": "e5ab5b5c",
"metadata": {},
"outputs": [],
"source": [
"# prepare the blackbox function for querying the model\n",
"# prepare the blackbox function for querying the model, partial magic\n",
"bb = partial(_bb, model=model, label_x0=label_x0, is_regression=False)"
]
},
Expand All @@ -364,13 +379,15 @@
"## Check fidelity of explanations on the point `x0` itself\n",
"\n",
"Note that LIME explanations can have fidelity less than 1.0 on that instance, i.e., the linear/affine function \n",
"approximated the model at $x_0$ need not pass through the model's prediction for $x_0$. But for KernelSHAP, \n",
"the approximating linear function always passes through model's prediction."
"approximated the model at $x_0$ need not pass through the model's predicted probability for $x_0$. But for \n",
"KernelSHAP, the approximating linear function always passes through model's predicted probability (this is\n",
"also known as the efficiency criterion for shapley values, i.e., the sum of values should be equal to the \n",
"total value)."
]
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 12,
"id": "1af94b94",
"metadata": {},
"outputs": [
Expand Down Expand Up @@ -427,22 +444,30 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 16,
"id": "f63775d5",
"metadata": {},
"outputs": [],
"source": [
"from aix360.algorithms.ecertify.ecertify import CertifyExplanation"
]
},
{
"cell_type": "markdown",
"id": "7ba1e370",
"metadata": {},
"source": [
"First we certify for the LIME explanation, we are computing again since in the last cell the variables were\n",
"overwritten by the SHAP explanation."
]
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 17,
"id": "1940a4f3",
"metadata": {},
"outputs": [],
"source": [
"# do it for LIME explanation\n",
"func, expl = compute_lime_explainer(x_train, model, x0, num_features=len(x0))\n",
"e = partial(_e, expl_func=func)\n",
"f = lambda x: 1 - abs(bb(x) - e(x)) # fidelity function (specific to this explanation)"
Expand All @@ -458,34 +483,34 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 22,
"id": "47bc1168",
"metadata": {},
"outputs": [],
"source": [
"# Inputs to Certify()\n",
"theta = 0.75 # user desired fidelity threshold \n",
"theta = 0.75 # user desired fidelity threshold \n",
"lb = 0; ub = 1 # init hypercube of size 1\n",
"Q = 10000 # query budget related arguments\n",
"Z = 10 # number of halving/doubling iterations during certification (so total queries expensed = Z*Q)\n",
"sigma0 = 0.1 # sigma for gaussians used in unifI and adaptI strategies\n",
"NUMRUNS = 10 # consider running for more iterations here for reduced error\n",
"Q = 10000 # query budget related arguments\n",
"Z = 10 # number of halving/doubling iterations during certification (so total queries expensed = Z*Q)\n",
"sigma0 = 0.1 # sigma for gaussians used in unifI and adaptI strategies\n",
"NUMRUNS = 10 # consider running for more iterations here for reduced error\n",
"\n",
"certifier = CertifyExplanation(theta=theta, Q=Q, Z=Z, lb=lb, ub=ub, sigma0=sigma0, numruns=NUMRUNS)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": 23,
"id": "4d988077",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Time per run: 5.247 s\n",
"Found w: 0.3178 ± 0.142338\n"
"Time per run: 6.844 s\n",
"Found w: 0.4298 ± 0.047384\n"
]
}
],
Expand All @@ -502,13 +527,12 @@
"metadata": {},
"source": [
"## Certification of SHAP\n",
"Similarly we could also certify the KernelSHAP explanation for the same instance, just using a different \n",
"quality criterion."
"Similarly we could also certify the KernelSHAP explanation for the same instance, just using a properly defined quality criterion that takes the SHAP `expl_func` object."
]
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 24,
"id": "fd418d56",
"metadata": {},
"outputs": [],
Expand All @@ -521,16 +545,16 @@
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": 25,
"id": "7a7dfbf4",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Time per run: 5.871 s\n",
"Found w: 0.0416 ± 0.014154\n"
"Time per run: 4.467 s\n",
"Found w: 0.0272 ± 0.002681\n"
]
}
],
Expand All @@ -547,9 +571,9 @@
"metadata": {},
"source": [
"## Observation\n",
"Note that for this instance, the LIME explanation has a larger certifier width ($\\approx 0.3$) than the \n",
"KernelSHAP explanation ($\\approx 0.04$), implying the linear explanation obtained from LIME is applicable to a \n",
"relatively large neighbourhood than the corresponding KernelSHAP explanation."
"Note that for this instance, the LIME explanation has a larger certifier width ($\\approx 0.3-0.4$) than the \n",
"KernelSHAP explanation ($\\approx 0.02-0.04$), implying the linear explanation obtained from LIME is applicable \n",
"to a relatively large neighbourhood than the corresponding KernelSHAP explanation."
]
},
{
Expand Down

0 comments on commit d80bce1

Please sign in to comment.