This is the code repository for the ACM FAccT 2022 paper "Don't Throw it Away! The Utility of Unlabeled Data in Fair Decision Making" (arXiv). The implementation is based on Pytorch and Ignite. The repository contains the necessary resources to train the models and collect the metrics for evaluating the experiments in the paper, including the datasets.
We assume that conda is already installed. For the ease of setup, we provide a
yml
environment setup file.
Give the following command to create the environment.
conda env create -f fairall_env.yml
This creates a conda environment named fairall
.
To activate the created environment, use the following command.
conda activate fairall
The repository provides all the datasets that have been used for the experiments in the paper. These include:
- Synthetic dataset (
datasets/SCB_11/
) - COMPAS (
datasets/compas/
) - German Credit (
datasets/credit/
) - MEPS Health (
datasets/meps/
)
The latter 3 datasets are real-world datasets that have been downloaded using AIF360. Please see the related licences. We also refer to the user agreement for MEPS. We have pre-processed the datasets for our compatibility and as described in our paper.
Read here for details on the key points regarding dataset formatting for compatibility.
The standard running format is:
python main.py --dataset_file {DATASET_PARAMS_FILE} --model_file {MODEL_PARAMS_FILE} --trainer_file {TRAINER_PARAMS_FILE}
Note that all params yaml
files can be found in _params/
folder. New params files should also be created here, if necessary.
Optionally, you can give -r
for results root directory, and -s
for specific seed.
E.g., one instance to run FairAll (I+II) for synthetic data would be:
python main.py --dataset_file _params/dataset_SCB_11.yaml --model_file _params/model_fairall.yaml --trainer_file _params/trainer.yaml -r results/exper_SCB_11_fairall_12 -s 100
Note that just giving python main.py
would just start executing FairAll (I+II) on synthetic data with some default parameter files provided.
You can check all the flags with:
python main.py --help
For the sake of completeness, here is the output of the argument --help
:
usage: main.py [-h] [--dataset_file DATASET_FILE] [--model_file MODEL_FILE]
[--trainer_file TRAINER_FILE] [-d KEY1=VAL1,KEY2=VAL2...]
[-m KEY1=VAL1,KEY2=VAL2...] [-o KEY1=VAL1,KEY2=VAL2...]
[-t KEY1=VAL1,KEY2=VAL2...] [-s SEED] [-r RESULT_DIR]
Authors: Ayan Majumdar, Miriam Rateike
optional arguments:
-h, --help show this help message and exit
--dataset_file DATASET_FILE
path to configuration file for the dataset
--model_file MODEL_FILE
path to configuration file for the dataset
--trainer_file TRAINER_FILE
path to configuration file for the training
-d KEY1=VAL1,KEY2=VAL2..., --dataset_dict KEY1=VAL1,KEY2=VAL2...
manually define dataset configurations as string:
KEY1=VALUE1+KEY2=VALUE2+...
-m KEY1=VAL1,KEY2=VAL2..., --model_dict KEY1=VAL1,KEY2=VAL2...
manually define model configurations as string:
KEY1=VALUE1+KEY2=VALUE2+...
-o KEY1=VAL1,KEY2=VAL2..., --optim_dict KEY1=VAL1,KEY2=VAL2...
manually define optimizer configurations as string:
KEY1=VALUE1+KEY2=VALUE2+...
-t KEY1=VAL1,KEY2=VAL2..., --trainer_dict KEY1=VAL1,KEY2=VAL2...
manually define trainer configurations as string:
KEY1=VALUE1+KEY2=VALUE2+...
-s SEED, --seed SEED set random seed
-r RESULT_DIR, --result_dir RESULT_DIR
directory for storing results
Each dataset has it's corresponding param yaml
file in _params/
, e.g., _params/dataset_SCB_11.yaml
, _params/dataset_compas.yaml
, _params/dataset_credit.yaml
, _params/dataset_meps.yaml
.
To run on any of these datasets, simply pass the param file location with the --dataset_file
flag.
How do we run the code by changing some of the parameter?
- One option would be to change the parameter
yaml
files. ReadParameter Files
below. Editing these files and running is one option. - Another option would be to pass changed parameters as arguments (using
-m
,-t
,-d
options).
One example of specifically using the flags to change some parameters could be:
python main.py --dataset_file _params/dataset_SCB_11.yaml --model_file _params/model_fairall.yaml --trainer_file _params/trainer.yaml -r results/exper_SCB_11_fairall_12 -m learn_policy=FZ+fz_method=CLF+costs=0.2 -d init_policy=RAN+percent=0.7
Here, we used:
-m learn_policy=FZ+fz_method=CLF+costs=0.2
to change the model parameters for policy, method and cost of decision.-d init_policy=RAN+percent=0.7
to change initial policy to random and change the acceptance percentage for the random policy.
Note, how different parameters (for model and dataset) are separated with +
.
Read Parameter Files
below for details about the different parameters available.
We give some examples for running each of the different policy methods we report.
Note, for these examples we use synthetic dataset (_params/dataset_SCB_11.yaml
) and the provided _params/trainer.yaml
.
Taking the optimal values from Table 6
from the Appendix
of the paper for Synthetic
data:
python main.py --dataset_file _params/dataset_SCB_11.yaml --model_file _params/model_fairall.yaml --trainer_file _params/trainer_phase1.yaml -r results -s 100 -o learning_rate=0.005 -m beta=0.8
Note, for optimal results, the saved checkpoint and hparams yaml
files should be loaded directly for FairAll (I+II) learning. See below Loading phase 1 checkpoint directly
for more details.
For training phases 1 and 2 together, we use with FZ-DEC policy.
python main.py --dataset_file _params/dataset_SCB_11.yaml --model_file _params/model_fairall.yaml --trainer_file _params/trainer.yaml -r results -s 100
We simply need phase 1 training off.
python main.py --dataset_file _params/dataset_SCB_11.yaml --model_file _params/model_fairall.yaml --trainer_file _params/trainer.yaml -r results -s 100 -t training=False
We need different loss, both phase training and FZ-LAB policy.
python main.py --dataset_file _params/dataset_SCB_11.yaml --model_file _params/model_fairlab.yaml --trainer_file _params/trainer.yaml -r results -s 100
Competing method based off Kilbertus et al. "Fair Decisions Despite Imperfect Predicitions". No phase 1 training, different loss function and policy.
python main.py --dataset_file _params/dataset_SCB_11.yaml --model_file _params/model_fairlog.yaml --trainer_file _params/trainer.yaml -r results -s 100 -t training=False
The unfair version of the competing method FairLog. Set lambda to 0 to have no fairness constraint and only optimize utility.
python main.py --dataset_file _params/dataset_SCB_11.yaml --model_file _params/model_fairlog.yaml --trainer_file _params/trainer.yaml -r results -s 100 -t training=False -m lambda=0
Note, depending on dataset and policy method, other hyperparameters (like model architecture, etc.) might need to be changed. Refer to Appendix
of paper for details.
These parameters can be accessed and modified. For more details about the different parameters, read below.
Running the code first requires providing parameters for the 3 different components of our pipeline: dataset, model and the trainer.
These should be configured in the _params/
folder.
Each dataset should have its own parameter file in _params/{DATASET}.yaml
. This file should have a particular format.
For instance, for COMPAS dataset, _params/dataset_compas.yaml
is as follows:
dataset:
name: compas
params1:
std: True # Standardize data
params2:
batch_size: 128
init_policy: HARSH
percent: 0.3 # percentage labeled datapoints, if initial policy RANdom
name
should match the folder name for the dataset.std: True
indicates standardize the features of the data before learning.batch_size
indicates the batch size to use for training in phase 1.init_policy
is the π0 starting policy used in warmup. Our work usesHARSH
,LENI
. Can also beRAN
(random).percent
is only used for randomRAN
policy to indicate the fraction of people to accept.
Any parameter in params2
can be changed using the arguments of main.py
. Give -d
option and separate each with +
, e.g.:
-d init_policy=RAN+percent=0.7
We provide parameter files for different models. The parameter files again follow a certain format.
An example would be _params/model_fairall.yaml
:
optimizer:
name: adam
params:
learning_rate: 0.001
model:
name: sscvae
params:
L: 50 #samples of Z to evaluate ELBO
K: 100 #samples for MC estimation of unsupervised KL
latent_size: 2 #latent dimension of z
h_dim_list_clf: [64, 64]
h_dim_list_enc: [64, 64]
h_dim_list_dec: [64, 64]
act_name: relu #activation function
drop_rate_clf: 0.1 #dropout rate
conditional: True #conditional VAE
alpha: 10 #factor for classifier loss
lambda: #NA
beta: 0.8 #annealing factor for KL
costs: 0.5 #costs of positive decision
learn_policy: FZ #'QXS', 'PZS', 'FZ', 'NA'
fz_method: DEC #'CLF', 'DEC', 'LAB' (IPS)
pol_sampler: LOG # One of DET, LOG
loss_function: loss_fairall
model_type: #leave empty for None, only relevant for niki (fair/unfairlog)
phase1-ckpt: #leave empty for None or paste here model with the entire model path
The components are (note that components that are unused by specific models can be left empty):
optimizer
with the name (we useadam
) and withparams
aslearning_rate
(we use0.001
).model
that defines the core model components:name
: this is alwayssscvae
as this is the backbone model object we use.L,K
: samples for evaluating ELBO and KL respectively. Not needed to change.latent_size
: VAE latent size. Change according to data.h_dim_list_enc,h_dim_list_dec
: hidden layer sizes as list for encoder and decoder. Used forfairall
andfairlab
.h_dim_list_clf
: hidden layer sizes as list for classifier. Used byfairall
,fairlog (unfairlog)
.act_name
: activation used, we fix this torelu
.drop_rate_clf
: the dropout ratep
fortorch Dropout
. Only for classifier model.conditional
: set toTrue
forfairall
andfairlab
. Not used byfairlog (unfairlog)
.alpha
: the weight to classifier loss. Used byfairall
.lambda
: fairness constraint weight used for FairLog and UnfairLog. Setlambda: 0
for UnfairLog andlambda: {positive number}
for FairLog.beta
: KL weight applied to ELBO loss. Used byfairall
andfairlab
.costs
: cost of positive decision in the policies and classifier. Depends on the dataset.learn_policy
: the policy to use for learning. Can beClassifier: QXS (fairall, fairlog, unfairlog)
,Decoder: PZS (fairall, fairlab)
andLatent Z: FZ (fairall, fairlab)
.- For
fairlog (unfairlog)
we useQXS
. - For
fairall
andfairlab
we useFZ
.
- For
fz_method
: (onlyfairall
andfairlab
) if using latent Z for policy. Can beCLF (label with classifier)
,DEC (label with decoder)
andLAB (use only labeled data)
.- For
fairall
we useDEC
. - For
fairlab
it is alwaysLAB
.
- For
pol_sampler
: the policy decision sampling method. Can be stochasticLOG
or deterministicDET
. We useLOG
in our paper.loss_function
: the loss function to use. Can be for FairAllloss_fairall
, for FairLabloss_fairlab
and for FairLog and UnfairLogloss_fairlog
.- Note that to differentiate FairLog and UnfairLog, we use the
lambda
parameter.
- Note that to differentiate FairLog and UnfairLog, we use the
model_type
: relevant for FairLog and UnfairLog. Can benn
orlr
for neural network or logistic regression. We fix it tonn
.phase1-ckpt
: only forfairall
. If we want to directly load ackpt
file, give the full path to theckpt
file here.
Note that a lot of these parameter values would be set dependent on the dataset being used. Check the appendix for details.
Any parameter in optim
can be changed using the arguments of main.py
. Give -o
option. Can only change learning_rate
.
Any parameter in model
can be changed using the arguments of main.py
. Give -m
option and separate different ones with +
, e.g.:
-m learn_policy=FZ+fz_method=CLF+costs=0.2
Finally, the training parameters are provided in the trainer.yaml
file. We provide an example here for understanding the format.
root_dir: results # Root folder to save the model
trainer:
print_every: 1
trainer1:
training: True #If True, train phase 1
epochs: 1200 # Maximum number of epochs to train
phase1_samples: #leave like this, we only used this for cross-validation
trainer2:
training: True #If True, train phase 2
only_warmup: False #If True, only do warmup phase (this is True only for cross validation)
epochs_per_ts: 1 # Number of epochs per time step
time_steps: 200 # Number of time steps
samples_per_ts: 64 # Number of samples per time step
warmup_ts: 50 # How many epochs to do warmup?
warmup_samples: 128 # How many samples do we consider as applicants for warmup?
The following points are important to note:
- For FairAll (I+II) set
trainer1
'straining: True
. - For FairAll (II) set
trainer1
'straining: False
. - For FairLab (I+II) set
trainer1
'straining: True
. - For FairLog and UnfairLog set
trainer1
'straining: False
. - For general training, always set
trainer2
'sonly_warmup: False
. root_dir
: set the root directory for storing results, usually it would be, e.g.,results/
.
All results are output to a folder. This location is printed out to console at the end of training:
All results saved to: {save_directory}
.
So, cd
to this directory. This is where all output files can be found.
All crucial results, especially those reported in the paper are dumped to JSON files. For FairAll (I+II), FairLab (I+II) we would have 6 JSON files, 3 for each phase (each reporting for training, validation and test data). For FairAll (II), FairLog, UnfairLog, we would have 3 JSON files (only for phase 2).
The files are named as output{phase}{data}.json
.
{phase}
can be 1,w,2
for phase 1, warmup or 2 respectively.
{data}
can be train,valid,test
respectively.
The following metrics can be found in the JSON files under metrics
key. Note that not all metrics will be present all the time.
The presence of a metric depends on which model (and loss) we use and which data we use for training.
- Data feature reconstruction: Real-valued features
Rec_NRMSE_X{i}
, Categorical featuresRec_ERR_X{i}
. These are only for FairAll, FairLab. - Utility reconstruction of decoder:
Rec_ERR_U
(only for FairAll, FairLab) - Classifier error for utility:
Clf_ERR_U
(only for FairAll, FairLog, UnfairLog) - Policy related: utility (
Policy_utility
), error (Policy_ERR
) - Fairness related: demographic parity (
DP_unfairness
), counterfactual fairness (CF_unfairness
, only for synthetic data) - Training effectiveness: accumulated utility
Effective_util
, accumulated DP unfairnessEffective_DPU
(only found inoutput{phase}train.json
)
Additional measures for ground truth: For synthetic data, if we also have ground truth available,
we additionally collect Policy_Gnd_utility
, Policy_Gnd_ERR
, Effective_util_gnd
.
The other outputs of interest generated are as follows:
yaml
files for params for training each phase ashparams_phase{phase}.yaml
(1, 2, warmup)- 2-dimensional PCA images visualizing latent Z as
latent_phase_{phase}.png
(1, 2, w) - Images for original and reconstructed marginal feature distributions as
marginal_phase_{phase}_{feature_dim}.png
where phase (1, 2, w) and feature_dim (0, 1, ...) - Prediction error of
S
fromZ
aspred-s-from-z-err-ph-{phase}.txt
(1, 2, w) - Prediction error of
U
fromZ
aspred-u-from-z-err-ph-{phase}.txt
(1, 2, w) - Training times stored in
time.json
- Phase 1 checkpoint and Phase 2 checkpoint(s) as
.pth
files. Note to always use the latest checkpoint files (order by time).
One can load a pre-existing phase 1 model checkpoint for running subsequent phase 2 operations.
One example situation could be running the code with only phase 1 training turned on.
Use _params/trainer_phase1.yaml
for running only phase 1 (change any parameters as desired, e.g., trainer1:epochs
).
For loading phase 1 checkpoint, give the location of the .pth
checkpoint file and the phase 1 hparams yaml
file.
Note these files would be inside the corresponding results directory as checkpoint_phase1_model_total_loss={...}.pth
and hparams_phase1.yaml
.
If desired, copy these files to other locations. To load the checkpoint related files, there are 2 options.
- Edit
model_fairall.yaml
ormodel_fairlab.yaml
. Put the path locations of the.pth
inmodel: params: phase1-ckpt
. Also put the location of thehparams_phase1.yaml
inmodel: params: phase1-hparams
. - Directly put this information in the console while executing code:
python main.py --dataset_file {DATASET PARAMS YAML} --model_file {MODEL PARAMS YAML} --trainer_file {TRAINER PARAMS YAML} -m phase1-ckpt={Location of checkpoint_phase1 pth file}+phase1-hparams={Location of hparams_phase1.yaml file}
For any queries, please contact ayanm{at}mpi-sws.org
and mrateike{at}tue.mpg.de
.
To cite this work, please cite the main paper
@inproceedings{rateike2022don,
title={Don’t Throw it Away! The Utility of Unlabeled Data in Fair Decision Making},
author={Rateike, Miriam and Majumdar, Ayan and Mineeva, Olga and Gummadi, Krishna P and Valera, Isabel},
booktitle={2022 ACM Conference on Fairness, Accountability, and Transparency},
pages={1421--1433},
year={2022}
}
The code in this repository for training VAE models on heterogeneous data is based on an older version of this repo. Thanks to Adrián Javaloy Bornás for sharing his code!