Skip to content

Commit

Permalink
COMPAS audit using the FairnessLab
Browse files Browse the repository at this point in the history
  • Loading branch information
joebaumann committed Nov 1, 2022
2 parents aa24027 + 143fe30 commit 1f0b3f0
Show file tree
Hide file tree
Showing 15 changed files with 192 additions and 35 deletions.
Binary file added COMPAS_audit_10Oct2022.pdf
Binary file not shown.
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ What sets the FairnessLab apart from existing tools is that it allows you to cre

The app is publicly available at [https://joebaumann.github.io/FairnessLab](https://joebaumann.github.io/FairnessLab/).

You can see the FairnessLab in action if you read through this audit of the COMPAS algorithm that provides new insights into the COMPAS bias discussions.
You can see the FairnessLab in action if you read through this audit of the COMPAS algorithm that provides new insights into the COMPAS bias discussions: [COMPAS_audit_10Oct2022.pdf](COMPAS_audit_10Oct2022.pdf).

![FairnessLabDemo](demo/demo.gif)

Expand Down Expand Up @@ -41,4 +41,19 @@ More features will be added in the future to better reflect the flexibility of o

## Get in touch

Do you want to audit a model using the FairnessLab or our approach and want to discuss this with us? Do you have ideas for how to improve the FairnessLab? Then don't hesitate to reach out to us: [Corinna](corinna.hertweck@uzh.ch) and [Joe](baumann@ifi.uzh.ch).
Do you want to audit a model using the FairnessLab or our approach and want to discuss this with us? Do you have ideas for how to improve the FairnessLab? Then don't hesitate to reach out to us: [Corinna](mailto:corinna.hertweck@uzh.ch) and [Joe](mailto:baumann@ifi.uzh.ch).

## Citing this work

```
@article{BaumannHertweck2022UnificationGroupFairness,
archivePrefix = {arXiv},
arxivId = {2206.02897},
author = {Baumann, Joachim and Hertweck, Corinna and Loi, Michele and Heitz, Christoph},
eprint = {2206.02897},
month = {jun},
title = {{Distributive Justice as the Foundational Premise of Fair ML: Unification, Extension, and Interpretation of Group Fairness Metrics}},
url = {http://arxiv.org/abs/2206.02897},
year = {2022}
}
```
5 changes: 2 additions & 3 deletions frontend/src/Components/Audit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,22 @@ import { getFairnessScoreDescription } from '../../store/fairnessScore';
function Audit(props) {
// the datasetSelectionCounter is incremented every time the datasetSelection or the justifier changes
// this makes sure that the decision subject utilities are recalculated after the justifier has been applied to the dataset
const [datasetSelectionCounter, setDatasetSelectionCounter] = useState(0);
const [colors, setColors] = useState(Array(11 * 11).fill('#4e87ad'));
const fairnessScoreDescription = useSelector(getFairnessScoreDescription);

return(
<div className="Audit">
<Header title="Audit"/>
<div className="Content">
<DatasetSelector datasetSelectionCounter={datasetSelectionCounter} setDatasetSelectionCounter={setDatasetSelectionCounter} />
<DatasetSelector/>
<Terminology/>
<Configuration/>
<h1>Audit</h1>
<h2>Resulting fairness metric</h2>
In the audit, we will use the fairness metric that you defined with your inputs above. Specifically, we will look at the following fairness metric:
<div><i>{fairnessScoreDescription}</i></div>
<SubjectsUtility colors={colors} />
<ParetoPlot colors={colors} setColors={setColors} datasetSelectionCounter={datasetSelectionCounter} />
<ParetoPlot colors={colors} setColors={setColors} />
<SelectedPointsTable />
<ScoreDistribution colors={colors} />
</div>
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/Components/Configuration/Configuration.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.Slider {
width: 50%;
}

input {
margin-left: 1rem;
}

select {
margin-left: 1rem;
}

.sliderSpan {
margin-left: 0.8rem;
}
9 changes: 2 additions & 7 deletions frontend/src/Components/DatasetSelector/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import ACSEmploymentCA_file from '../../data_static/ACS/ACSEmployment_CA.json';
import { getDatasetSelection, changeDatasetSelection, changeFilteredData, changeUnfilteredData } from '../../store/dataset';
import { getJustifier } from '../../store/fairnessScore';

function DatasetSelector({datasetSelectionCounter, setDatasetSelectionCounter}) {
function DatasetSelector({}) {
const datasets = {
'COMPAS': {
'file': compas_file
Expand Down Expand Up @@ -126,7 +126,6 @@ function DatasetSelector({datasetSelectionCounter, setDatasetSelectionCounter})
setUnfilteredData({'y': [[],[]], 'scores': [[],[]], 'd': [[],[]]})
setFileError(true)
}
setDatasetSelectionCounter(datasetSelectionCounter + 1)
}

function applyJustifierToRow(row, justifier) {
Expand Down Expand Up @@ -198,11 +197,7 @@ function DatasetSelector({datasetSelectionCounter, setDatasetSelectionCounter})

useEffect(() => {
processData()
}, [datasetSelection, justifier]);

useEffect(() => {
processData()
}, [uploadedData, datasetSelection]);
}, [datasetSelection, justifier, uploadedData]);

useEffect(() => {
if (uploadedData.length !== 0) {
Expand Down
41 changes: 41 additions & 0 deletions frontend/src/Components/Hamburger/Hamburger.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.hamburger {
width: 2rem;
height: 2rem;
display: flex;
/* position: fixed; */
justify-content: space-around;
flex-flow: column nowrap;
z-index: 10;
/* top: 2vh; */
}

.burger {
width: 2rem;
height: 0.25rem;
border-radius: 2px;
background-color: white;
transform-origin: 1px;
transition: all 0.3s linear;
}

div.burger.burger1.open {
transform: rotate(45deg);
}
div.burger.burger1.closed {
transform: rotate(0);
}

div.burger.burger2.open {
opacity: 0;
}
div.burger.burger2.closed {
transform: translateX(0);
opacity: 1;
}

div.burger.burger3.open {
transform: rotate(-45deg);
}
div.burger.burger3.closed {
transform: rotate(0);
}
14 changes: 14 additions & 0 deletions frontend/src/Components/Hamburger/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import './Hamburger.css';

const Hamburger = ({isOpen}) => {
return (
<div className='hamburger'>
<div className={isOpen? 'burger burger1 open' : 'burger burger1 closed'}/>
<div className={isOpen? 'burger burger2 open' : 'burger burger2 closed'}/>
<div className={isOpen? 'burger burger3 open' : 'burger burger3 closed'}/>
</div>
);
}

export default Hamburger;
2 changes: 1 addition & 1 deletion frontend/src/Components/Header/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import './Header.css';
const Header = ({title}) => {
return (
<header className="Header">
<h1 dangerouslySetInnerHTML={{__html: title}} />
<h1>{title}</h1>
</header>
);
}
Expand Down
54 changes: 49 additions & 5 deletions frontend/src/Components/Navbar/Navbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@
height: 10vh;
}

@media (min-height: 800px), screen and (orientation: portrait) {
.Navbar {
height: 8vh;
.Navbar-hamburger {
display: none;
}
}

.Navbar-icon {
background-color: transparent;
Expand All @@ -26,10 +24,21 @@
float: left;
padding: 1vh 3vh;
}

.Navbar ul {
display: fkex;
flex-wrap: wrap;
margin: 0;
padding: 0;
}

.Navbar ul li {
list-style-type: none;
padding-right: 10vw;
}

.Navbar-link {
float: left;
text-align: center;
display: block;
padding: 2.5vh 3vh 1vh;
color: white;
Expand All @@ -45,4 +54,39 @@
.active, .Navbar-link:hover {
background-size: 80% 2px, auto;
color: white;
}

@media (max-width: 767px) {
.Navbar-hamburger {
display: block;
float: left;
padding: 2vh 3vh;
}

.Navbar ul {
top: 10vh;
left: 0;
background-color: var(--main);
height: 35vh;
width: 50vw;
position: fixed;
}

ul.closed {
display: none;
}

ul.open {
display: inline;
}
}

@media (min-height: 800px), screen and (orientation: portrait) {
.Navbar {
height: 8vh;
}

.Navbar ul {
top: 8vh;
}
}
31 changes: 26 additions & 5 deletions frontend/src/Components/Navbar/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
import React from 'react';
import React, { useState } from 'react';
import { NavLink, Link } from 'react-router-dom';
import './Navbar.css';
import logo from "./logo.ico";
import Hamburger from "../Hamburger";

const Navbar = () => {
const [hamburgerOpen, setHamburgerOpen] = useState(false);
const toggleHamburger = () => {
setHamburgerOpen(!hamburgerOpen)
}
const closeHamburger = () => {
setHamburgerOpen(false)
}
return (
<nav className="Navbar">
<div className='Navbar-hamburger' onClick={toggleHamburger}>
<Hamburger isOpen={hamburgerOpen}/>
</div>
<Link to="/">
<img alt="Logo" className="Navbar-logo" src={logo}/>
</Link>
<NavLink key="audit" className="Navbar-link" exact to="/audit" activeClassName="active">Audit</NavLink>
<NavLink key="compas" className="Navbar-link" exact to="/compas" activeClassName="active">COMPAS Case Study</NavLink>
<NavLink key="faq" className="Navbar-link" exact to="/faq" activeClassName="active">FAQ</NavLink>
<NavLink key="contact" className="Navbar-link" exact to="/contact" activeClassName="active">Contact</NavLink>
<ul className={hamburgerOpen ? 'open' : 'closed'}>
<li>
<NavLink key="audit" className="Navbar-link" exact to="/audit" activeClassName="active" onClick={closeHamburger}>Audit</NavLink>
</li>
<li>
<NavLink key="compas" className="Navbar-link" exact to="/compas" activeClassName="active" onClick={closeHamburger}>COMPAS Case Study</NavLink>
</li>
<li>
<NavLink key="faq" className="Navbar-link" exact to="/faq" activeClassName="active" onClick={closeHamburger}>FAQ</NavLink>
</li>
<li>
<NavLink key="contact" className="Navbar-link" exact to="/contact" activeClassName="active" onClick={closeHamburger}>Contact</NavLink>
</li>
</ul>
</nav>
);
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/Components/ParetoPlot/ParetoPlot.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
}

input {
margin-left: 10px;
margin-left: 1rem;
}

select {
margin-left: 10px;
margin-left: 1rem;
}

.sliderSpan {
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/Components/ParetoPlot/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { getDecisionMakerCurrency, getDmuFN, getDmuFP, getDmuTN, getDmuTP } from
import { getSubjectsCurrency, getSuTP1, getSuFP1, getSuFN1, getSuTN1, getSuTP2, getSuFP2, getSuFN2, getSuTN2, getGroup1, getGroup2, getPattern, getSufficientarianismThreshold, getPrioritarianismWeight, getFairnessScoreDescription } from '../../store/fairnessScore';
import { addSelectedPoint, changeDecisionMakerUtility, changeEvaluationOfD, changeFairnessScores, changeNumThresholds, changeSubjectsUtility, changeThresholdTuples, deleteSelectedPoint, deselectAllPoints, getColorOfD, getDecisionMakerUtility, getEvaluationOfD, getFairnessScores, getNumThresholds, getSelectedPoints, getThresholdTuples, selectEvaluationOfD } from '../../store/paretoPlot';

function ParetoPlot({datasetSelectionCounter, colors, setColors}) {
function ParetoPlot({colors, setColors}) {

const dispatch = useDispatch ()
function setNumThresholds(value) {dispatch(changeNumThresholds(value))}
Expand All @@ -22,7 +22,6 @@ function ParetoPlot({datasetSelectionCounter, colors, setColors}) {

const datasetSelection = useSelector(getDatasetSelection)
const decisionMakerCurrency = useSelector(getDecisionMakerCurrency)
const subjectsCurrency = useSelector(getSubjectsCurrency)
const dmuTP = useSelector(getDmuTP);
const dmuFP = useSelector(getDmuFP);
const dmuFN = useSelector(getDmuFN);
Expand Down Expand Up @@ -250,7 +249,7 @@ function ParetoPlot({datasetSelectionCounter, colors, setColors}) {
if (filteredData['d'][0].length !== 0 || filteredData['d'][1].length !== 0) {
dispatch(selectEvaluationOfD())
}
}, [datasetSelection, datasetSelectionCounter]);
}, [datasetSelection, filteredData, unfilteredData]);

useEffect(() => {
updateThresholdCalculations()
Expand Down Expand Up @@ -347,6 +346,7 @@ function ParetoPlot({datasetSelectionCounter, colors, setColors}) {
xaxis: { title: `Fairness score<br>${fairnessScoreDescription}`},
yaxis: { title: `Decision maker's utility (in ${decisionMakerCurrency.replace('*', '')})` },
hovermode:'closest',
responsive: true
} }

onClick={(data) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
}

.LeftPlot {
margin-right: 0px;
margin-right: 0;
}

.RightPlot {
margin-left: 0px;
margin-left: 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@

#selectedPoints td, #selectedPoints th {
border: 1px solid #ddd;
padding: 2px;
padding-left: 10px;
padding-right: 10px;
padding: 0.3rem;
padding-left: 1rem;
padding-right: 1rem;
}

#selectedPoints tr:nth-child(even){background-color: #f2f2f2;}

#selectedPoints tr:hover {background-color: #ddd;}

#selectedPoints th {
padding: 10px;
padding: 1rem;
text-align: center;
background-color: #4e87ad;
color: white;
Expand Down
Loading

0 comments on commit 1f0b3f0

Please sign in to comment.