diff --git a/CHANGELOG.md b/CHANGELOG.md index a08ab0624..e9d13ffe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## Version [1.3.0] - 2024-09-16 +### Changed + +- Fixed bug in `NeuroTreeExt` extensions. [#475] + ### Added - Added basic support for the T-CREx counterfactual generator. [#473] +- Added docstrings for package extensions to documentation. [#475] ## Version [1.2.0] - 2024-09-10 diff --git a/README.md b/README.md index f64963927..40cdad007 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ There is also a corresponding paper, [*Explaining Black-Box Models through Count volume = {1}, number = {1}, pages = {130}, - author = {Patrick Altmeyer and Arie van Deursen and Cynthia C. s. Liem}, + author = {Patrick Altmeyer and Arie van Deursen and Cynthia C. S. Liem}, title = {Explaining Black-Box Models through Counterfactuals}, journal = {Proceedings of the JuliaCon Conferences} } @@ -62,13 +62,50 @@ Counterfactual Explanations have a few properties that are desirable in the cont - Clear link to Algorithmic Recourse and Causal Inference. - Less susceptible to adversarial attacks than LIME and SHAP. +### Simple Usage Example + +To get started, try out this simple usage example with synthetic data: + +``` julia +using CounterfactualExplanations +using CounterfactualExplanations.Models +using Plots +using TaijaData +using TaijaPlotting + +# Data and Model: +data = load_linearly_separable() +counterfactual_data = CounterfactualData(data...) +M = fit_model(counterfactual_data, :Linear) + +# Choose factual: +target = 2 +factual = 1 +chosen = findall(predict_label(M, counterfactual_data) .== factual) |> + rand +x = select_factual(counterfactual_data, chosen) + +# Generate counterfactuals +generator = WachterGenerator() +ce = generate_counterfactual( + x, # factual + target, # target + counterfactual_data, # data + M, # model + generator # counterfactual generator +) +plot(ce) +``` + +![](README_files/figure-commonmark/cell-3-output-1.svg) + ### Example: Give Me Some Credit Consider the following real-world scenario: a retail bank is using a black-box model trained on their clients’ credit history to decide whether they will provide credit to new applicants. To simulate this scenario, we have pre-trained a binary classifier on the publicly available Give Me Some Credit dataset that ships with this package (Kaggle 2011). The figure below shows counterfactuals for 10 randomly chosen individuals that would have been denied credit initially. -![](README_files/figure-commonmark/cell-5-output-1.svg) +![](README_files/figure-commonmark/cell-6-output-1.svg) ### Example: MNIST @@ -86,7 +123,7 @@ generator = GradientBasedGenerator() end ``` -![](README_files/figure-commonmark/cell-10-output-1.svg) +![](README_files/figure-commonmark/cell-11-output-1.svg) ## 🔍 Usage example @@ -125,7 +162,7 @@ ce = generate_counterfactual( The plot below shows the resulting counterfactual path: -![](README_files/figure-commonmark/cell-15-output-1.svg) +![](README_files/figure-commonmark/cell-16-output-1.svg) ## ☑️ Implemented Counterfactual Generators @@ -134,13 +171,16 @@ Currently, the following counterfactual generators are implemented: - ClaPROAR (Altmeyer et al. 2023) - CLUE (Antorán et al. 2020) - DiCE (Mothilal, Sharma, and Tan 2020) +- ECCCo (Altmeyer et al. 2024) - FeatureTweak (Tolomei et al. 2017) - Generic - GravitationalGenerator (Altmeyer et al. 2023) - Greedy (Schut et al. 2021) - GrowingSpheres (Laugel et al. 2017) +- MINT (Karimi et al. 2020) (**causal CE**) - PROBE (Pawelczyk et al. 2023) - REVISE (Joshi et al. 2019) +- T-CREx (Bewley et al. 2024) (**global CE**) - Wachter (Wachter, Mittelstadt, and Russell 2017) ## 🎯 Goals and limitations @@ -199,12 +239,18 @@ If you want to use this codebase, please consider citing the corresponding paper Altmeyer, Patrick, Giovan Angela, Aleksander Buszydlik, Karol Dobiczek, Arie van Deursen, and Cynthia CS Liem. 2023. “Endogenous Macrodynamics in Algorithmic Recourse.” In *2023 IEEE Conference on Secure and Trustworthy Machine Learning (SaTML)*, 418–31. IEEE. +Altmeyer, Patrick, Mojtaba Farmanbar, Arie van Deursen, and Cynthia CS Liem. 2024. “Faithful Model Explanations Through Energy-Constrained Conformal Counterfactuals.” In *Proceedings of the AAAI Conference on Artificial Intelligence*, 38:10829–37. 10. + Antorán, Javier, Umang Bhatt, Tameem Adel, Adrian Weller, and José Miguel Hernández-Lobato. 2020. “Getting a Clue: A Method for Explaining Uncertainty Estimates.” . +Bewley, Tom, Salim I. Amoukou, Saumitra Mishra, Daniele Magazzeni, and Manuela Veloso. 2024. “Counterfactual Metarules for Local and Global Recourse.” . + Joshi, Shalmali, Oluwasanmi Koyejo, Warut Vijitbenjaronk, Been Kim, and Joydeep Ghosh. 2019. “Towards Realistic Individual Recourse and Actionable Explanations in Black-Box Decision Making Systems.” . Kaggle. 2011. “Give Me Some Credit, Improve on the State of the Art in Credit Scoring by Predicting the Probability That Somebody Will Experience Financial Distress in the Next Two Years.” https://www.kaggle.com/c/GiveMeSomeCredit; Kaggle. . +Karimi, Amir-Hossein, Julius Von Kügelgen, Bernhard Schölkopf, and Isabel Valera. 2020. “Algorithmic Recourse Under Imperfect Causal Knowledge: A Probabilistic Approach.” . + Laugel, Thibault, Marie-Jeanne Lesot, Christophe Marsala, Xavier Renard, and Marcin Detyniecki. 2017. “Inverse Classification for Comparison-Based Interpretability in Machine Learning.” . Mothilal, Ramaravind K, Amit Sharma, and Chenhao Tan. 2020. “Explaining Machine Learning Classifiers Through Diverse Counterfactual Explanations.” In *Proceedings of the 2020 Conference on Fairness, Accountability, and Transparency*, 607–17. . diff --git a/README_files/figure-commonmark/cell-10-output-1.svg b/README_files/figure-commonmark/cell-10-output-1.svg index acd00361a..041f65e99 100644 --- a/README_files/figure-commonmark/cell-10-output-1.svg +++ b/README_files/figure-commonmark/cell-10-output-1.svg @@ -1,179 +1,174 @@ - + - + - + - + - + - - - + + + - + - + - - - + + + diff --git a/README_files/figure-commonmark/cell-11-output-1.svg b/README_files/figure-commonmark/cell-11-output-1.svg index 559a31fd7..c8775e268 100644 --- a/README_files/figure-commonmark/cell-11-output-1.svg +++ b/README_files/figure-commonmark/cell-11-output-1.svg @@ -1,32 +1,29 @@ - + - + - + - + - + - - - + + + - - - - - - - - - - - + + + + + + + + + + + diff --git a/README_files/figure-commonmark/cell-15-output-1.svg b/README_files/figure-commonmark/cell-15-output-1.svg index 361580662..58f23f719 100644 --- a/README_files/figure-commonmark/cell-15-output-1.svg +++ b/README_files/figure-commonmark/cell-15-output-1.svg @@ -1,1401 +1,651 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/README_files/figure-commonmark/cell-16-output-1.svg b/README_files/figure-commonmark/cell-16-output-1.svg new file mode 100644 index 000000000..267ec3945 --- /dev/null +++ b/README_files/figure-commonmark/cell-16-output-1.svg @@ -0,0 +1,651 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README_files/figure-commonmark/cell-3-output-1.svg b/README_files/figure-commonmark/cell-3-output-1.svg new file mode 100644 index 000000000..fcf93b91e --- /dev/null +++ b/README_files/figure-commonmark/cell-3-output-1.svg @@ -0,0 +1,356 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README_files/figure-commonmark/cell-5-output-1.svg b/README_files/figure-commonmark/cell-5-output-1.svg index 27bd1c746..71cb664f7 100644 --- a/README_files/figure-commonmark/cell-5-output-1.svg +++ b/README_files/figure-commonmark/cell-5-output-1.svg @@ -1,98 +1,94 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README_files/figure-commonmark/cell-6-output-1.svg b/README_files/figure-commonmark/cell-6-output-1.svg new file mode 100644 index 000000000..15b8f698c --- /dev/null +++ b/README_files/figure-commonmark/cell-6-output-1.svg @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_freeze/docs/src/index/execute-results/md.json b/_freeze/docs/src/index/execute-results/md.json index f0a0d384a..f38fc2337 100644 --- a/_freeze/docs/src/index/execute-results/md.json +++ b/_freeze/docs/src/index/execute-results/md.json @@ -2,7 +2,7 @@ "hash": "ee93a4e1e12fb1617447f2a56e4b9304", "result": { "engine": "jupyter", - "markdown": "```@meta\nCurrentModule = CounterfactualExplanations\n```\n\n![](assets/wide_logo.png)\n\nDocumentation for [CounterfactualExplanations.jl](https://github.com/juliatrustworthyai/CounterfactualExplanations.jl).\n\n# CounterfactualExplanations\n\n*Counterfactual Explanations and Algorithmic Recourse in Julia.*\n\n[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://juliatrustworthyai.github.io/CounterfactualExplanations.jl/stable) \n[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://juliatrustworthyai.github.io/CounterfactualExplanations.jl/dev) \n[![Build Status](https://github.com/juliatrustworthyai/CounterfactualExplanations.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/juliatrustworthyai/CounterfactualExplanations.jl/actions/workflows/CI.yml?query=branch%3Amain) \n[![Coverage](https://codecov.io/gh/juliatrustworthyai/CounterfactualExplanations.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/juliatrustworthyai/CounterfactualExplanations.jl) \n[![Code Style: Blue](https://img.shields.io/badge/code%20style-blue-4495d1.svg)](https://github.com/invenia/BlueStyle) \n[![License](https://img.shields.io/github/license/juliatrustworthyai/CounterfactualExplanations.jl)](LICENSE) \n[![Package Downloads](https://img.shields.io/badge/dynamic/json?url=http%3A%2F%2Fjuliapkgstats.com%2Fapi%2Fv1%2Fmonthly_downloads%2FCounterfactualExplanations&query=total_requests&suffix=%2Fmonth&label=Downloads)](http://juliapkgstats.com/pkg/CounterfactualExplanations) \n[![Aqua QA](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)\n\n\n\n`CounterfactualExplanations.jl` is a package for generating Counterfactual Explanations (CE) and Algorithmic Recourse (AR) for black-box algorithms. Both CE and AR are related tools for explainable artificial intelligence (XAI). While the package is written purely in Julia, it can be used to explain machine learning algorithms developed and trained in other popular programming languages like Python and R. See below for a short introduction and other resources or dive straight into the [docs](https://juliatrustworthyai.github.io/CounterfactualExplanations.jl/dev).\n\nThere is also a corresponding paper, [*Explaining Black-Box Models through Counterfactuals*](https://proceedings.juliacon.org/papers/10.21105/jcon.00130), which has been published in JuliaCon Proceedings. Please consider citing the paper, if you use this package in your work:\n\n[![DOI](https://proceedings.juliacon.org/papers/10.21105/jcon.00130/status.svg)](https://doi.org/10.21105/jcon.00130) [![DOI](https://zenodo.org/badge/440782065.svg)](https://zenodo.org/badge/latestdoi/440782065) \n\n```\n@article{Altmeyer2023,\n doi = {10.21105/jcon.00130},\n url = {https://doi.org/10.21105/jcon.00130},\n year = {2023},\n publisher = {The Open Journal},\n volume = {1},\n number = {1},\n pages = {130},\n author = {Patrick Altmeyer and Arie van Deursen and Cynthia C. s. Liem},\n title = {Explaining Black-Box Models through Counterfactuals},\n journal = {Proceedings of the JuliaCon Conferences}\n}\n```\n\n## 🚩 Installation\n\nYou can install the stable release from [Julia's General Registry](https://github.com/JuliaRegistries/General) as follows:\n\n``` julia\nusing Pkg\nPkg.add(\"CounterfactualExplanations\")\n```\n\n`CounterfactualExplanations.jl` is under active development. To install the development version of the package you can run the following command:\n\n``` julia\nusing Pkg\nPkg.add(url=\"https://github.com/juliatrustworthyai/CounterfactualExplanations.jl\")\n```\n\n## 🤔 Background and Motivation\n\nMachine learning models like Deep Neural Networks have become so complex, opaque and underspecified in the data that they are generally considered Black Boxes. Nonetheless, such models often play a key role in data-driven decision-making systems. This creates the following problem: human operators in charge of such systems have to rely on them blindly, while those individuals subject to them generally have no way of challenging an undesirable outcome:\n\n> \"You cannot appeal to (algorithms). They do not listen. Nor do they bend.\"\n>\n> --- Cathy O'Neil in [*Weapons of Math Destruction*](https://en.wikipedia.org/wiki/Weapons_of_Math_Destruction), 2016\n\n## 🔮 Enter: Counterfactual Explanations\n\nCounterfactual Explanations can help human stakeholders make sense of the systems they develop, use or endure: they explain how inputs into a system need to change for it to produce different decisions. Explainability benefits internal as well as external quality assurance.\n\nCounterfactual Explanations have a few properties that are desirable in the context of Explainable Artificial Intelligence (XAI). These include:\n\n- Full fidelity to the black-box model, since no proxy is involved.\n- No need for (reasonably) interpretable features as opposed to LIME and SHAP.\n- Clear link to Algorithmic Recourse and Causal Inference.\n- Less susceptible to adversarial attacks than LIME and SHAP.\n\n### Example: Give Me Some Credit\n\n\n\nConsider the following real-world scenario: a retail bank is using a black-box model trained on their clients' credit history to decide whether they will provide credit to new applicants. To simulate this scenario, we have pre-trained a binary classifier on the publicly available Give Me Some Credit dataset that ships with this package [@kaggle2011give].\n\n::: {.cell execution_count=3}\n\n::: {.cell-output .cell-output-display .cell-output-markdown execution_count=4}\nThe figure below shows counterfactuals for 10 randomly chosen individuals that would have been denied credit initially. \n\n:::\n:::\n\n\n::: {.cell execution_count=4}\n\n::: {.cell-output .cell-output-display}\n![](index_files/figure-commonmark/cell-5-output-1.svg){}\n:::\n:::\n\n\n### Example: MNIST\n\n\n\n\n\n::: {.cell execution_count=7}\n\n::: {.cell-output .cell-output-display .cell-output-markdown execution_count=8}\nThe figure below shows a counterfactual generated for an image classifier trained on MNIST: in particular, it demonstrates which pixels need to change in order for the classifier to predict 3 instead of 8. \n\n:::\n:::\n\n\nSince `v0.1.9` counterfactual generators are fully composable. Here we have composed a generator that combines ideas from @wachter2017counterfactual and @altmeyer2023endogenous:\n\n::: {.cell execution_count=8}\n``` {.julia .cell-code}\n# Compose generator:\nusing CounterfactualExplanations.Objectives: distance_mad, distance_from_target\ngenerator = GradientBasedGenerator()\n@chain generator begin\n @objective logitcrossentropy + 0.2distance_mad + 0.1distance_from_target\n @with_optimiser Adam(0.1) \nend\n```\n:::\n\n\n::: {.cell execution_count=9}\n\n::: {.cell-output .cell-output-display}\n![](index_files/figure-commonmark/cell-10-output-1.svg){}\n:::\n:::\n\n\n## 🔍 Usage example\n\nGenerating counterfactuals will typically look like follows. Below we first fit a simple model to a synthetic dataset with linearly separable features and then draw a random sample:\n\n::: {.cell execution_count=10}\n``` {.julia .cell-code}\n# Data and Classifier:\ncounterfactual_data = CounterfactualData(load_linearly_separable()...)\nM = fit_model(counterfactual_data, :Linear)\n\n# Select random sample:\ntarget = 2\nfactual = 1\nchosen = rand(findall(predict_label(M, counterfactual_data) .== factual))\nx = select_factual(counterfactual_data, chosen)\n```\n:::\n\n\nTo this end, we specify a counterfactual generator of our choice:\n\n::: {.cell execution_count=11}\n``` {.julia .cell-code}\n# Counterfactual search:\ngenerator = DiCEGenerator(λ=[0.1,0.3])\n```\n:::\n\n\n::: {.cell execution_count=12}\n\n::: {.cell-output .cell-output-display .cell-output-markdown execution_count=13}\nHere, we have chosen to use the `GradientBasedGenerator` to move the individual from its factual label 1 to the target label 2.\n\n:::\n:::\n\n\nWith all of our ingredients specified, we finally generate counterfactuals using a simple API call:\n\n::: {.cell execution_count=13}\n``` {.julia .cell-code}\nconv = conv = CounterfactualExplanations.Convergence.GeneratorConditionsConvergence()\nce = generate_counterfactual(\n x, target, counterfactual_data, M, generator; \n num_counterfactuals=3, convergence=conv,\n)\n```\n:::\n\n\nThe plot below shows the resulting counterfactual path:\n\n::: {.cell execution_count=14}\n\n::: {.cell-output .cell-output-display execution_count=15}\n![](index_files/figure-commonmark/cell-15-output-1.svg){}\n:::\n:::\n\n\n## ☑️ Implemented Counterfactual Generators\n\nCurrently, the following counterfactual generators are implemented:\n\n- ClaPROAR [@altmeyer2023endogenous]\n- CLUE [@antoran2020getting]\n- DiCE [@mothilal2020explaining]\n- FeatureTweak [@tolomei2017interpretable]\n- Generic\n- GravitationalGenerator [@altmeyer2023endogenous]\n- Greedy [@schut2021generating]\n- GrowingSpheres [@laugel2017inverse]\n- PROBE [@pawelczyk2022probabilistically]\n- REVISE [@joshi2019realistic]\n- Wachter [@wachter2017counterfactual]\n\n## 🎯 Goals and limitations\n\nThe goal of this library is to contribute to efforts towards trustworthy machine learning in Julia. The Julia language has an edge when it comes to trustworthiness: it is very transparent. Packages like this one are generally written in pure Julia, which makes it easy for users and developers to understand and contribute to open-source code. Eventually, this project aims to offer a one-stop-shop of counterfactual explanations.\n\nOur ambition is to enhance the package through the following features:\n\n1. Support for all supervised machine learning models trained in [`MLJ.jl`](https://alan-turing-institute.github.io/MLJ.jl/dev/).\n2. Support for regression models.\n\n## 🛠 Contribute\n\nContributions of any kind are very much welcome! Take a look at the [issue](https://github.com/juliatrustworthyai/CounterfactualExplanations.jl/issues) to see what things we are currently working on. If you have an idea for a new feature or want to report a bug, please open a new issue. \n\n### Development\n\nIf your looking to contribute code, it may be helpful to check out the [Explanation](explanation/index.qmd) section of the docs. \n\n#### Testing\n\nPlease always make sure to add tests for any new features or changes.\n\n#### Documentation\n\nIf you add new features or change existing ones, please make sure to update the documentation accordingly. The documentation is written in [Documenter.jl](https://juliadocs.github.io/Documenter.jl/stable/) and is located in the `docs/src` folder. \n\n#### Log Changes\n\nAs of version `1.1.1`, we have tried to be more stringent about logging changes. Please make sure to add a note to the [CHANGELOG.md](CHANGELOG.md) file for any changes you make. It is sufficient to add a note under the `Unreleased` section. \n\n### General Pointers\n\nThere are also some general pointers for people looking to contribute to any of our Taija packages [here](https://github.com/JuliaTrustworthyAI#general-pointers-for-contributors).\n\nPlease follow the [SciML ColPrac guide](https://github.com/SciML/ColPrac).\n\n\n\n## 🎓 Citation\n\nIf you want to use this codebase, please consider citing the corresponding paper:\n\n``` \n@article{Altmeyer2023,\n doi = {10.21105/jcon.00130},\n url = {https://doi.org/10.21105/jcon.00130},\n year = {2023},\n publisher = {The Open Journal},\n volume = {1},\n number = {1},\n pages = {130},\n author = {Patrick Altmeyer and Arie van Deursen and Cynthia C. s. Liem},\n title = {Explaining Black-Box Models through Counterfactuals},\n journal = {Proceedings of the JuliaCon Conferences}\n}\n```\n\n## 📚 References\n\n", + "markdown": "```@meta\nCurrentModule = CounterfactualExplanations\n```\n\n![](assets/wide_logo.png)\n\nDocumentation for [CounterfactualExplanations.jl](https://github.com/juliatrustworthyai/CounterfactualExplanations.jl).\n\n\n# CounterfactualExplanations\n\n*Counterfactual Explanations and Algorithmic Recourse in Julia.*\n\n[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://juliatrustworthyai.github.io/CounterfactualExplanations.jl/stable) \n[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://juliatrustworthyai.github.io/CounterfactualExplanations.jl/dev) \n[![Build Status](https://github.com/juliatrustworthyai/CounterfactualExplanations.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/juliatrustworthyai/CounterfactualExplanations.jl/actions/workflows/CI.yml?query=branch%3Amain) \n[![Coverage](https://codecov.io/gh/juliatrustworthyai/CounterfactualExplanations.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/juliatrustworthyai/CounterfactualExplanations.jl) \n[![Code Style: Blue](https://img.shields.io/badge/code%20style-blue-4495d1.svg)](https://github.com/invenia/BlueStyle) \n[![License](https://img.shields.io/github/license/juliatrustworthyai/CounterfactualExplanations.jl)](LICENSE) \n[![Package Downloads](https://img.shields.io/badge/dynamic/json?url=http%3A%2F%2Fjuliapkgstats.com%2Fapi%2Fv1%2Fmonthly_downloads%2FCounterfactualExplanations&query=total_requests&suffix=%2Fmonth&label=Downloads)](http://juliapkgstats.com/pkg/CounterfactualExplanations) \n[![Aqua QA](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)\n\n\n\n`CounterfactualExplanations.jl` is a package for generating Counterfactual Explanations (CE) and Algorithmic Recourse (AR) for black-box algorithms. Both CE and AR are related tools for explainable artificial intelligence (XAI). While the package is written purely in Julia, it can be used to explain machine learning algorithms developed and trained in other popular programming languages like Python and R. See below for a short introduction and other resources or dive straight into the [docs](https://juliatrustworthyai.github.io/CounterfactualExplanations.jl/dev).\n\nThere is also a corresponding paper, [*Explaining Black-Box Models through Counterfactuals*](https://proceedings.juliacon.org/papers/10.21105/jcon.00130), which has been published in JuliaCon Proceedings. Please consider citing the paper, if you use this package in your work:\n\n[![DOI](https://proceedings.juliacon.org/papers/10.21105/jcon.00130/status.svg)](https://doi.org/10.21105/jcon.00130) [![DOI](https://zenodo.org/badge/440782065.svg)](https://zenodo.org/badge/latestdoi/440782065) \n\n```\n@article{Altmeyer2023,\n doi = {10.21105/jcon.00130},\n url = {https://doi.org/10.21105/jcon.00130},\n year = {2023},\n publisher = {The Open Journal},\n volume = {1},\n number = {1},\n pages = {130},\n author = {Patrick Altmeyer and Arie van Deursen and Cynthia C. S. Liem},\n title = {Explaining Black-Box Models through Counterfactuals},\n journal = {Proceedings of the JuliaCon Conferences}\n}\n```\n\n## 🚩 Installation\n\nYou can install the stable release from [Julia's General Registry](https://github.com/JuliaRegistries/General) as follows:\n\n``` julia\nusing Pkg\nPkg.add(\"CounterfactualExplanations\")\n```\n\n`CounterfactualExplanations.jl` is under active development. To install the development version of the package you can run the following command:\n\n``` julia\nusing Pkg\nPkg.add(url=\"https://github.com/juliatrustworthyai/CounterfactualExplanations.jl\")\n```\n\n## 🤔 Background and Motivation\n\nMachine learning models like Deep Neural Networks have become so complex, opaque and underspecified in the data that they are generally considered Black Boxes. Nonetheless, such models often play a key role in data-driven decision-making systems. This creates the following problem: human operators in charge of such systems have to rely on them blindly, while those individuals subject to them generally have no way of challenging an undesirable outcome:\n\n> \"You cannot appeal to (algorithms). They do not listen. Nor do they bend.\"\n>\n> --- Cathy O'Neil in [*Weapons of Math Destruction*](https://en.wikipedia.org/wiki/Weapons_of_Math_Destruction), 2016\n\n## 🔮 Enter: Counterfactual Explanations\n\nCounterfactual Explanations can help human stakeholders make sense of the systems they develop, use or endure: they explain how inputs into a system need to change for it to produce different decisions. Explainability benefits internal as well as external quality assurance.\n\nCounterfactual Explanations have a few properties that are desirable in the context of Explainable Artificial Intelligence (XAI). These include:\n\n- Full fidelity to the black-box model, since no proxy is involved.\n- No need for (reasonably) interpretable features as opposed to LIME and SHAP.\n- Clear link to Algorithmic Recourse and Causal Inference.\n- Less susceptible to adversarial attacks than LIME and SHAP.\n\n### Simple Usage Example\n\nTo get started, try out this simple usage example with synthetic data:\n\n::: {.cell execution_count=2}\n``` {.julia .cell-code}\nusing CounterfactualExplanations\nusing CounterfactualExplanations.Models\nusing Plots\nusing TaijaData\nusing TaijaPlotting\n\n# Data and Model:\ndata = load_linearly_separable()\ncounterfactual_data = CounterfactualData(data...)\nM = fit_model(counterfactual_data, :Linear)\n\n# Choose factual:\ntarget = 2\nfactual = 1\nchosen = findall(predict_label(M, counterfactual_data) .== factual) |>\n rand\nx = select_factual(counterfactual_data, chosen)\n\n# Generate counterfactuals\ngenerator = WachterGenerator()\nce = generate_counterfactual(\n x, # factual\n target, # target\n counterfactual_data, # data\n M, # model\n generator # counterfactual generator\n)\nplot(ce)\n```\n\n::: {.cell-output .cell-output-display execution_count=3}\n![](index_files/figure-commonmark/cell-3-output-1.svg){}\n:::\n:::\n\n\n### Example: Give Me Some Credit\n\n\n\nConsider the following real-world scenario: a retail bank is using a black-box model trained on their clients' credit history to decide whether they will provide credit to new applicants. To simulate this scenario, we have pre-trained a binary classifier on the publicly available Give Me Some Credit dataset that ships with this package [@kaggle2011give].\n\n::: {.cell execution_count=4}\n\n::: {.cell-output .cell-output-display .cell-output-markdown execution_count=5}\nThe figure below shows counterfactuals for 10 randomly chosen individuals that would have been denied credit initially. \n\n:::\n:::\n\n\n::: {.cell execution_count=5}\n\n::: {.cell-output .cell-output-display}\n![](index_files/figure-commonmark/cell-6-output-1.svg){}\n:::\n:::\n\n\n### Example: MNIST\n\n\n\n\n\n::: {.cell execution_count=8}\n\n::: {.cell-output .cell-output-display .cell-output-markdown execution_count=9}\nThe figure below shows a counterfactual generated for an image classifier trained on MNIST: in particular, it demonstrates which pixels need to change in order for the classifier to predict 3 instead of 8. \n\n:::\n:::\n\n\nSince `v0.1.9` counterfactual generators are fully composable. Here we have composed a generator that combines ideas from @wachter2017counterfactual and @altmeyer2023endogenous:\n\n::: {.cell execution_count=9}\n``` {.julia .cell-code}\n# Compose generator:\nusing CounterfactualExplanations.Objectives: distance_mad, distance_from_target\ngenerator = GradientBasedGenerator()\n@chain generator begin\n @objective logitcrossentropy + 0.2distance_mad + 0.1distance_from_target\n @with_optimiser Adam(0.1) \nend\n```\n:::\n\n\n::: {.cell execution_count=10}\n\n::: {.cell-output .cell-output-display}\n![](index_files/figure-commonmark/cell-11-output-1.svg){}\n:::\n:::\n\n\n## 🔍 Usage example\n\nGenerating counterfactuals will typically look like follows. Below we first fit a simple model to a synthetic dataset with linearly separable features and then draw a random sample:\n\n::: {.cell execution_count=11}\n``` {.julia .cell-code}\n# Data and Classifier:\ncounterfactual_data = CounterfactualData(load_linearly_separable()...)\nM = fit_model(counterfactual_data, :Linear)\n\n# Select random sample:\ntarget = 2\nfactual = 1\nchosen = rand(findall(predict_label(M, counterfactual_data) .== factual))\nx = select_factual(counterfactual_data, chosen)\n```\n:::\n\n\nTo this end, we specify a counterfactual generator of our choice:\n\n::: {.cell execution_count=12}\n``` {.julia .cell-code}\n# Counterfactual search:\ngenerator = DiCEGenerator(λ=[0.1,0.3])\n```\n:::\n\n\n::: {.cell execution_count=13}\n\n::: {.cell-output .cell-output-display .cell-output-markdown execution_count=14}\nHere, we have chosen to use the `GradientBasedGenerator` to move the individual from its factual label 1 to the target label 2.\n\n:::\n:::\n\n\nWith all of our ingredients specified, we finally generate counterfactuals using a simple API call:\n\n::: {.cell execution_count=14}\n``` {.julia .cell-code}\nconv = conv = CounterfactualExplanations.Convergence.GeneratorConditionsConvergence()\nce = generate_counterfactual(\n x, target, counterfactual_data, M, generator; \n num_counterfactuals=3, convergence=conv,\n)\n```\n:::\n\n\nThe plot below shows the resulting counterfactual path:\n\n::: {.cell execution_count=15}\n\n::: {.cell-output .cell-output-display execution_count=16}\n![](index_files/figure-commonmark/cell-16-output-1.svg){}\n:::\n:::\n\n\n## ☑️ Implemented Counterfactual Generators\n\nCurrently, the following counterfactual generators are implemented:\n\n- ClaPROAR [@altmeyer2023endogenous]\n- CLUE [@antoran2020getting]\n- DiCE [@mothilal2020explaining]\n- ECCCo [@altmeyer2024faithful]\n- FeatureTweak [@tolomei2017interpretable]\n- Generic\n- GravitationalGenerator [@altmeyer2023endogenous]\n- Greedy [@schut2021generating]\n- GrowingSpheres [@laugel2017inverse]\n- MINT [@karimi2020algorithmic] (**causal CE**)\n- PROBE [@pawelczyk2022probabilistically]\n- REVISE [@joshi2019realistic]\n- T-CREx [@bewley2024counterfactual] (**global CE**)\n- Wachter [@wachter2017counterfactual]\n\n## 🎯 Goals and limitations\n\nThe goal of this library is to contribute to efforts towards trustworthy machine learning in Julia. The Julia language has an edge when it comes to trustworthiness: it is very transparent. Packages like this one are generally written in pure Julia, which makes it easy for users and developers to understand and contribute to open-source code. Eventually, this project aims to offer a one-stop-shop of counterfactual explanations.\n\nOur ambition is to enhance the package through the following features:\n\n1. Support for all supervised machine learning models trained in [`MLJ.jl`](https://alan-turing-institute.github.io/MLJ.jl/dev/).\n2. Support for regression models.\n\n## 🛠 Contribute\n\nContributions of any kind are very much welcome! Take a look at the [issue](https://github.com/juliatrustworthyai/CounterfactualExplanations.jl/issues) to see what things we are currently working on. If you have an idea for a new feature or want to report a bug, please open a new issue. \n\n### Development\n\nIf your looking to contribute code, it may be helpful to check out the [Explanation](explanation/index.qmd) section of the docs. \n\n#### Testing\n\nPlease always make sure to add tests for any new features or changes.\n\n#### Documentation\n\nIf you add new features or change existing ones, please make sure to update the documentation accordingly. The documentation is written in [Documenter.jl](https://juliadocs.github.io/Documenter.jl/stable/) and is located in the `docs/src` folder. \n\n#### Log Changes\n\nAs of version `1.1.1`, we have tried to be more stringent about logging changes. Please make sure to add a note to the [CHANGELOG.md](CHANGELOG.md) file for any changes you make. It is sufficient to add a note under the `Unreleased` section. \n\n### General Pointers\n\nThere are also some general pointers for people looking to contribute to any of our Taija packages [here](https://github.com/JuliaTrustworthyAI#general-pointers-for-contributors).\n\nPlease follow the [SciML ColPrac guide](https://github.com/SciML/ColPrac).\n\n\n\n## 🎓 Citation\n\nIf you want to use this codebase, please consider citing the corresponding paper:\n\n``` \n@article{Altmeyer2023,\n doi = {10.21105/jcon.00130},\n url = {https://doi.org/10.21105/jcon.00130},\n year = {2023},\n publisher = {The Open Journal},\n volume = {1},\n number = {1},\n pages = {130},\n author = {Patrick Altmeyer and Arie van Deursen and Cynthia C. s. Liem},\n title = {Explaining Black-Box Models through Counterfactuals},\n journal = {Proceedings of the JuliaCon Conferences}\n}\n```\n\n## 📚 References\n\n", "supporting": [ "index_files" ], diff --git a/_freeze/docs/src/index/figure-commonmark/cell-11-output-1.svg b/_freeze/docs/src/index/figure-commonmark/cell-11-output-1.svg index 6b814dc81..6893ed5be 100644 --- a/_freeze/docs/src/index/figure-commonmark/cell-11-output-1.svg +++ b/_freeze/docs/src/index/figure-commonmark/cell-11-output-1.svg @@ -1,269 +1,292 @@ - + - + - + - + - + - - - + + + - - - - - - - - - - - + + + + + + + + + + + diff --git a/_freeze/docs/src/index/figure-commonmark/cell-16-output-1.svg b/_freeze/docs/src/index/figure-commonmark/cell-16-output-1.svg new file mode 100644 index 000000000..67d4caeff --- /dev/null +++ b/_freeze/docs/src/index/figure-commonmark/cell-16-output-1.svg @@ -0,0 +1,651 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_freeze/docs/src/index/figure-commonmark/cell-3-output-1.svg b/_freeze/docs/src/index/figure-commonmark/cell-3-output-1.svg new file mode 100644 index 000000000..eef474ace --- /dev/null +++ b/_freeze/docs/src/index/figure-commonmark/cell-3-output-1.svg @@ -0,0 +1,356 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_freeze/docs/src/index/figure-commonmark/cell-6-output-1.svg b/_freeze/docs/src/index/figure-commonmark/cell-6-output-1.svg new file mode 100644 index 000000000..fa07f62da --- /dev/null +++ b/_freeze/docs/src/index/figure-commonmark/cell-6-output-1.svg @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/playground.jl b/dev/playground.jl index 89c43a779..d22d2fc80 100644 --- a/dev/playground.jl +++ b/dev/playground.jl @@ -24,32 +24,36 @@ generator = Generators.GenericGenerator() ce = generate_counterfactual(x, target, data, M, generator) # T-CREx ################################################################### -ρ = 0.02 +ρ = 0.5 τ = 0.9 -generator = Generators.TCRExGenerator(ρ) +generator = Generators.TCRExGenerator(; ρ=ρ, τ=τ) + +DTExt = Base.get_extension(CounterfactualExplanations, :DecisionTreeExt) # (a) ############################## # Surrogate: -model, fitresult = Generators.grow_surrogate(generator, ce) +model, fitresult = DTExt.grow_surrogate(generator, ce.data, ce.M) M_sur = CounterfactualExplanations.DecisionTreeModel(model; fitresult=fitresult) plot(M_sur, data; ms=3, markerstrokewidth=0, size=(500, 500), colorbar=false) # Extract rules: -R = Generators.extract_rules(fitresult[1]) +R = DTExt.extract_rules(fitresult[1]) +println("Expected: ", length(fitresult[1]) * 2 - 1) +println("Observed: ", length(R)) print_tree(fitresult[1]) # Compute feasibility and accuracy: -feas = Generators.rule_feasibility.(R, (X,)) +feas = DTExt.rule_feasibility.(R, (X,)) @assert minimum(feas) >= ρ -acc_factual = Generators.rule_accuracy.(R, (X,), (fx,), (factual,)) -acc_target = Generators.rule_accuracy.(R, (X,), (fx,), (target,)) +acc_factual = DTExt.rule_accuracy.(R, (X,), (fx,), (factual,)) +acc_target = DTExt.rule_accuracy.(R, (X,), (fx,), (target,)) @assert all(acc_target .+ acc_factual .== 1.0) # (b) ############################## -R_max = Generators.max_valid(R, X, fx, target, τ) -feas_max = Generators.rule_feasibility.(R_max, (X,)) -acc_max = Generators.rule_accuracy.(R_max, (X,), (fx,), (target,)) +R_max = DTExt.max_valid(R, X, fx, target, τ) +feas_max = DTExt.rule_feasibility.(R_max, (X,)) +acc_max = DTExt.rule_accuracy.(R_max, (X,), (fx,), (target,)) plt = plot(data; ms=3, markerstrokewidth=0, size=(500, 500)) p1 = deepcopy(plt) rectangle(w, h, x, y) = Shape(x .+ [0, w, w, 0], y .+ [0, 0, h, h]) @@ -70,7 +74,7 @@ end p1 # (c) ############################## -_grid = Generators.induced_grid(R_max) +_grid = DTExt.induced_grid(R_max) p2 = deepcopy(p1) function plot_grid!(p) for (i, (bounds_x, bounds_y)) in enumerate(_grid) @@ -94,16 +98,16 @@ plot_grid!(p2) p2 # (d) ############################## -xs = Generators.prototype.(_grid, (X,); pick_arbitrary=false) -Rᶜ = Generators.cre.((R_max,), xs, (X,); return_index=true) +xs = DTExt.prototype.(_grid, (X,); pick_arbitrary=false) +Rᶜ = DTExt.cre.((R_max,), xs, (X,); return_index=true) p3 = deepcopy(p2) scatter!(p3, eachrow(hcat(xs...))...; ms=10, label=nothing, color=Rᶜ .+ 2) p3 # (e) - (f) ######################## -bounds = Generators.partition_bounds(R_max) -tree = Generators.classify_prototypes(hcat(xs...)', Rᶜ, bounds) -R_final, labels = Generators.extract_leaf_rules(tree) +bounds = DTExt.partition_bounds(R_max) +tree = DTExt.classify_prototypes(hcat(xs...)', Rᶜ, bounds) +R_final, labels = DTExt.extract_leaf_rules(tree) p4 = deepcopy(plt) for (i, rule) in enumerate(R_final) ubx, uby = minimum([rule[1][2], maximum(X[1, :])]), diff --git a/docs/Project.toml b/docs/Project.toml index 34e00bbaf..b11a1d0ef 100755 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -29,6 +29,7 @@ MLJModels = "d491faf4-2d78-11e9-2867-c94bc002c0b7" MLUtils = "f1d291b0-491e-4a28-83b9-f70985020b54" MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" Measures = "442fdcdd-2543-5da2-b0f3-8c86c306513e" +NeuroTreeModels = "1db4e0a5-a364-4b0c-897c-2bd5a4a3a1f2" Optimisers = "3bd65402-5787-11e9-1adc-39752487f4e2" PlotThemes = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" diff --git a/docs/make.jl b/docs/make.jl index 82c5f0433..3503c3886 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -12,6 +12,12 @@ Changelog.generate( repo="juliatrustworthyai/CounterfactualExplanations.jl", ) +# Load weak deps for extensions +using DecisionTree +using JointEnergyModels +using LaplaceRedux +using NeuroTreeModels + makedocs(; doctest=false, modules=[CounterfactualExplanations], diff --git a/docs/src/CHANGELOG.md b/docs/src/CHANGELOG.md index a08ab0624..e9d13ffe4 100644 --- a/docs/src/CHANGELOG.md +++ b/docs/src/CHANGELOG.md @@ -8,9 +8,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## Version [1.3.0] - 2024-09-16 +### Changed + +- Fixed bug in `NeuroTreeExt` extensions. [#475] + ### Added - Added basic support for the T-CREx counterfactual generator. [#473] +- Added docstrings for package extensions to documentation. [#475] ## Version [1.2.0] - 2024-09-10 diff --git a/docs/src/_intro.qmd b/docs/src/_intro.qmd index 465fcf4e1..279f01e16 100644 --- a/docs/src/_intro.qmd +++ b/docs/src/_intro.qmd @@ -32,7 +32,7 @@ There is also a corresponding paper, [*Explaining Black-Box Models through Count volume = {1}, number = {1}, pages = {130}, - author = {Patrick Altmeyer and Arie van Deursen and Cynthia C. s. Liem}, + author = {Patrick Altmeyer and Arie van Deursen and Cynthia C. S. Liem}, title = {Explaining Black-Box Models through Counterfactuals}, journal = {Proceedings of the JuliaCon Conferences} } @@ -73,6 +73,43 @@ Counterfactual Explanations have a few properties that are desirable in the cont - Clear link to Algorithmic Recourse and Causal Inference. - Less susceptible to adversarial attacks than LIME and SHAP. +### Simple Usage Example + +To get started, try out this simple usage example with synthetic data: + +```{julia} +#| output: true + +using CounterfactualExplanations +using CounterfactualExplanations.Models +using Plots +using TaijaData +using TaijaPlotting + +# Data and Model: +data = load_linearly_separable() +counterfactual_data = CounterfactualData(data...) +M = fit_model(counterfactual_data, :Linear) + +# Choose factual: +target = 2 +factual = 1 +chosen = findall(predict_label(M, counterfactual_data) .== factual) |> + rand +x = select_factual(counterfactual_data, chosen) + +# Generate counterfactuals +generator = WachterGenerator() +ce = generate_counterfactual( + x, # factual + target, # target + counterfactual_data, # data + M, # model + generator # counterfactual generator +) +plot(ce) +``` + ### Example: Give Me Some Credit ```{julia} @@ -114,7 +151,7 @@ x1 = :income # Amount of given credit x2 = :age # Generator: -generator = GravitationalGenerator( +generator = GenericGenerator( opt = Descent(0.1) ) ``` @@ -139,9 +176,6 @@ Markdown.parse( counterfactuals = generate_counterfactual( x, target_class, counterfactual_data, M, generator; initialization = :identity, - convergence = Convergence.GeneratorConditionsConvergence( - decision_threshold=0.975 - ) ) # Plotting: @@ -344,10 +378,10 @@ Currently, the following counterfactual generators are implemented: - GravitationalGenerator [@altmeyer2023endogenous] - Greedy [@schut2021generating] - GrowingSpheres [@laugel2017inverse] -- MINT [@karimi2020algorithmic] (causal CE) +- MINT [@karimi2020algorithmic] (**causal CE**) - PROBE [@pawelczyk2022probabilistically] - REVISE [@joshi2019realistic] -- T-CREx [@bewley2024counterfactual] (global CE) +- T-CREx [@bewley2024counterfactual] (**global CE**) - Wachter [@wachter2017counterfactual] ## 🎯 Goals and limitations diff --git a/docs/src/index.md b/docs/src/index.md index 5f0868ee5..14e17fec6 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -35,7 +35,7 @@ There is also a corresponding paper, [*Explaining Black-Box Models through Count volume = {1}, number = {1}, pages = {130}, - author = {Patrick Altmeyer and Arie van Deursen and Cynthia C. s. Liem}, + author = {Patrick Altmeyer and Arie van Deursen and Cynthia C. S. Liem}, title = {Explaining Black-Box Models through Counterfactuals}, journal = {Proceedings of the JuliaCon Conferences} } @@ -75,13 +75,50 @@ Counterfactual Explanations have a few properties that are desirable in the cont - Clear link to Algorithmic Recourse and Causal Inference. - Less susceptible to adversarial attacks than LIME and SHAP. +### Simple Usage Example + +To get started, try out this simple usage example with synthetic data: + +``` julia +using CounterfactualExplanations +using CounterfactualExplanations.Models +using Plots +using TaijaData +using TaijaPlotting + +# Data and Model: +data = load_linearly_separable() +counterfactual_data = CounterfactualData(data...) +M = fit_model(counterfactual_data, :Linear) + +# Choose factual: +target = 2 +factual = 1 +chosen = findall(predict_label(M, counterfactual_data) .== factual) |> + rand +x = select_factual(counterfactual_data, chosen) + +# Generate counterfactuals +generator = WachterGenerator() +ce = generate_counterfactual( + x, # factual + target, # target + counterfactual_data, # data + M, # model + generator # counterfactual generator +) +plot(ce) +``` + +![](index_files/figure-commonmark/cell-3-output-1.svg) + ### Example: Give Me Some Credit Consider the following real-world scenario: a retail bank is using a black-box model trained on their clients’ credit history to decide whether they will provide credit to new applicants. To simulate this scenario, we have pre-trained a binary classifier on the publicly available Give Me Some Credit dataset that ships with this package (Kaggle 2011). The figure below shows counterfactuals for 10 randomly chosen individuals that would have been denied credit initially. -![](index_files/figure-commonmark/cell-5-output-1.svg) +![](index_files/figure-commonmark/cell-6-output-1.svg) ### Example: MNIST @@ -99,7 +136,7 @@ generator = GradientBasedGenerator() end ``` -![](index_files/figure-commonmark/cell-10-output-1.svg) +![](index_files/figure-commonmark/cell-11-output-1.svg) ## 🔍 Usage example @@ -138,7 +175,7 @@ ce = generate_counterfactual( The plot below shows the resulting counterfactual path: -![](index_files/figure-commonmark/cell-15-output-1.svg) +![](index_files/figure-commonmark/cell-16-output-1.svg) ## ☑️ Implemented Counterfactual Generators @@ -147,13 +184,16 @@ Currently, the following counterfactual generators are implemented: - ClaPROAR (Altmeyer et al. 2023) - CLUE (Antorán et al. 2020) - DiCE (Mothilal, Sharma, and Tan 2020) +- ECCCo (Altmeyer et al. 2024) - FeatureTweak (Tolomei et al. 2017) - Generic - GravitationalGenerator (Altmeyer et al. 2023) - Greedy (Schut et al. 2021) - GrowingSpheres (Laugel et al. 2017) +- MINT (Karimi et al. 2020) (**causal CE**) - PROBE (Pawelczyk et al. 2023) - REVISE (Joshi et al. 2019) +- T-CREx (Bewley et al. 2024) (**global CE**) - Wachter (Wachter, Mittelstadt, and Russell 2017) ## 🎯 Goals and limitations @@ -212,12 +252,18 @@ If you want to use this codebase, please consider citing the corresponding paper Altmeyer, Patrick, Giovan Angela, Aleksander Buszydlik, Karol Dobiczek, Arie van Deursen, and Cynthia CS Liem. 2023. “Endogenous Macrodynamics in Algorithmic Recourse.” In *2023 IEEE Conference on Secure and Trustworthy Machine Learning (SaTML)*, 418–31. IEEE. +Altmeyer, Patrick, Mojtaba Farmanbar, Arie van Deursen, and Cynthia CS Liem. 2024. “Faithful Model Explanations Through Energy-Constrained Conformal Counterfactuals.” In *Proceedings of the AAAI Conference on Artificial Intelligence*, 38:10829–37. 10. + Antorán, Javier, Umang Bhatt, Tameem Adel, Adrian Weller, and José Miguel Hernández-Lobato. 2020. “Getting a Clue: A Method for Explaining Uncertainty Estimates.” . +Bewley, Tom, Salim I. Amoukou, Saumitra Mishra, Daniele Magazzeni, and Manuela Veloso. 2024. “Counterfactual Metarules for Local and Global Recourse.” . + Joshi, Shalmali, Oluwasanmi Koyejo, Warut Vijitbenjaronk, Been Kim, and Joydeep Ghosh. 2019. “Towards Realistic Individual Recourse and Actionable Explanations in Black-Box Decision Making Systems.” . Kaggle. 2011. “Give Me Some Credit, Improve on the State of the Art in Credit Scoring by Predicting the Probability That Somebody Will Experience Financial Distress in the Next Two Years.” https://www.kaggle.com/c/GiveMeSomeCredit; Kaggle. . +Karimi, Amir-Hossein, Julius Von Kügelgen, Bernhard Schölkopf, and Isabel Valera. 2020. “Algorithmic Recourse Under Imperfect Causal Knowledge: A Probabilistic Approach.” . + Laugel, Thibault, Marie-Jeanne Lesot, Christophe Marsala, Xavier Renard, and Marcin Detyniecki. 2017. “Inverse Classification for Comparison-Based Interpretability in Machine Learning.” . Mothilal, Ramaravind K, Amit Sharma, and Chenhao Tan. 2020. “Explaining Machine Learning Classifiers Through Diverse Counterfactual Explanations.” In *Proceedings of the 2020 Conference on Fairness, Accountability, and Transparency*, 607–17. . diff --git a/docs/src/index_files/figure-commonmark/cell-11-output-1.svg b/docs/src/index_files/figure-commonmark/cell-11-output-1.svg index 6b814dc81..6893ed5be 100644 --- a/docs/src/index_files/figure-commonmark/cell-11-output-1.svg +++ b/docs/src/index_files/figure-commonmark/cell-11-output-1.svg @@ -1,269 +1,292 @@ - + - + - + - + - + - - - + + + - - - - - - - - - - - + + + + + + + + + + + diff --git a/docs/src/index_files/figure-commonmark/cell-16-output-1.svg b/docs/src/index_files/figure-commonmark/cell-16-output-1.svg new file mode 100644 index 000000000..67d4caeff --- /dev/null +++ b/docs/src/index_files/figure-commonmark/cell-16-output-1.svg @@ -0,0 +1,651 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/index_files/figure-commonmark/cell-3-output-1.svg b/docs/src/index_files/figure-commonmark/cell-3-output-1.svg new file mode 100644 index 000000000..eef474ace --- /dev/null +++ b/docs/src/index_files/figure-commonmark/cell-3-output-1.svg @@ -0,0 +1,356 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/index_files/figure-commonmark/cell-6-output-1.svg b/docs/src/index_files/figure-commonmark/cell-6-output-1.svg new file mode 100644 index 000000000..fa07f62da --- /dev/null +++ b/docs/src/index_files/figure-commonmark/cell-6-output-1.svg @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/reference.md b/docs/src/reference.md index 298bc7928..09dfe54ac 100644 --- a/docs/src/reference.md +++ b/docs/src/reference.md @@ -18,7 +18,7 @@ In other words, you come here because you want to take a very close look at the ``` @contents Pages = ["reference.md"] -Depth = 2 +Depth = 2:3 ``` ## Exported functions @@ -52,3 +52,15 @@ Modules = [ ] Public = false ``` + +## Extensions + +``` @autodocs +Modules = [ + Base.get_extension(CounterfactualExplanations, :DecisionTreeExt), + Base.get_extension(CounterfactualExplanations, :JEMExt), + Base.get_extension(CounterfactualExplanations, :LaplaceReduxExt), + Base.get_extension(CounterfactualExplanations, :NeuroTreeExt), +] +``` + diff --git a/docs/src/reference.qmd b/docs/src/reference.qmd deleted file mode 100644 index b002d017a..000000000 --- a/docs/src/reference.qmd +++ /dev/null @@ -1,51 +0,0 @@ -```@meta -CurrentModule = CounterfactualExplanations -``` - -# Reference -In this reference, you will find a detailed overview of the package API. - -> Reference guides are technical descriptions of the machinery and how to operate it. Reference material is information-oriented. -> -> --- [Diátaxis](https://diataxis.fr/reference/) - -In other words, you come here because you want to take a very close look at the code 🧐. - -## Content - -```@contents -Pages = ["reference.md"] -Depth = 2 -``` - -## Exported functions - -```@autodocs -Modules = [ - CounterfactualExplanations, - CounterfactualExplanations.Convergence, - CounterfactualExplanations.Evaluation, - CounterfactualExplanations.DataPreprocessing, - CounterfactualExplanations.Models, - CounterfactualExplanations.GenerativeModels, - CounterfactualExplanations.Generators, - CounterfactualExplanations.Objectives -] -Private = false -``` - -## Internal functions - -```@autodocs -Modules = [ - CounterfactualExplanations, - CounterfactualExplanations.Convergence, - CounterfactualExplanations.Evaluation, - CounterfactualExplanations.DataPreprocessing, - CounterfactualExplanations.Models, - CounterfactualExplanations.GenerativeModels, - CounterfactualExplanations.Generators, - CounterfactualExplanations.Objectives -] -Public = false -``` \ No newline at end of file diff --git a/docs/src/release-notes.md b/docs/src/release-notes.md index 8519ee9a6..93cff27f1 100644 --- a/docs/src/release-notes.md +++ b/docs/src/release-notes.md @@ -12,9 +12,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## Version [1.3.0] - 2024-09-16 +### Changed + +- Fixed bug in `NeuroTreeExt` extensions. [#475](https://github.com/juliatrustworthyai/CounterfactualExplanations.jl/issues/475) + ### Added - Added basic support for the T-CREx counterfactual generator. [#473](https://github.com/juliatrustworthyai/CounterfactualExplanations.jl/issues/473) +- Added docstrings for package extensions to documentation. [#475](https://github.com/juliatrustworthyai/CounterfactualExplanations.jl/issues/475) ## Version [1.2.0] - 2024-09-10 diff --git a/docs/src/www/mnist_factual.png b/docs/src/www/mnist_factual.png index bd7ddf841..9709267ce 100644 Binary files a/docs/src/www/mnist_factual.png and b/docs/src/www/mnist_factual.png differ diff --git a/ext/DecisionTreeExt/t_crex/t_crex.jl b/ext/DecisionTreeExt/t_crex/t_crex.jl index 93339015d..d02c2e385 100644 --- a/ext/DecisionTreeExt/t_crex/t_crex.jl +++ b/ext/DecisionTreeExt/t_crex/t_crex.jl @@ -13,7 +13,7 @@ include("utils.jl") M::Models.AbstractModel ) -Applies the [`TCRExGenerator`](@ref) to a given `target` and `data` using the +Applies the [`Generators.TCRExGenerator`](@ref) to a given `target` and `data` using the `M` model. $DOC_TCREx """ function (generator::Generators.TCRExGenerator)( diff --git a/ext/JEMExt/jem.jl b/ext/JEMExt/jem.jl index fd9c744f2..c77cd6770 100644 --- a/ext/JEMExt/jem.jl +++ b/ext/JEMExt/jem.jl @@ -117,7 +117,7 @@ end X::AbstractArray, ) -Overloads the [probs](@ref) method for NeuroTree models. +Overloads the [Models.probs](@ref) method for NeuroTree models. """ function Models.probs( M::Models.Model, type::CounterfactualExplanations.JEM, X::AbstractArray diff --git a/ext/NeuroTreeExt/neurotree.jl b/ext/NeuroTreeExt/neurotree.jl index e21da2bba..6e39e2049 100644 --- a/ext/NeuroTreeExt/neurotree.jl +++ b/ext/NeuroTreeExt/neurotree.jl @@ -83,6 +83,7 @@ logits = Models.logits(M, x) # calculates the logit scores for each output class function Models.logits( M::Models.Model, type::CounterfactualExplanations.NeuroTreeModel, X::AbstractArray ) + X = X[:, :] return M.fitresult(X) end @@ -98,8 +99,5 @@ Overloads the [probs](@ref) method for NeuroTree models. function Models.probs( M::Models.Model, type::CounterfactualExplanations.NeuroTreeModel, X::AbstractArray ) - if ndims(X) == 1 - X = X[:, :] # account for 1-dimensional inputs - end return softmax(logits(M, X)) end diff --git a/test/models/neurotree/neurotree.jl b/test/models/neurotree/neurotree.jl index 8b8987666..1447c3626 100644 --- a/test/models/neurotree/neurotree.jl +++ b/test/models/neurotree/neurotree.jl @@ -17,6 +17,10 @@ using TaijaData batchsize=10, ) + # Predictions: + yhat = logits(M, data.X) # matrix + yhat = logits(M, data.X[:, 1]) # vector + # Select a factual instance: target = 2 factual = 1