Skip to content

Commit d249c51

Browse files
committed
Signed-off-by: Kalousios <[email protected]>
1 parent e011686 commit d249c51

File tree

20 files changed

+4700
-0
lines changed

20 files changed

+4700
-0
lines changed

aif360/algorithms/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from aif360.algorithms.transformer import Transformer, addmetadata
2+
from aif360.algorithms.intersectional_fairness import IntersectionalFairness

aif360/algorithms/intersectional_fairness.py

Lines changed: 1010 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
#
3+
# Copyright 2023 Fujitsu Limited
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
18+
from aif360.algorithms.inprocessing.adversarial_debiasing import AdversarialDebiasing as AD
19+
import tensorflow as tf
20+
21+
from aif360.algorithms.isf_helpers.inprocessing.inprocessing import InProcessing
22+
23+
24+
tf.compat.v1.disable_eager_execution()
25+
26+
27+
class AdversarialDebiasing(InProcessing):
28+
29+
"""
30+
Debiasing intersectional bias with adversarial learning(AD) called by ISF.
31+
32+
Parameters
33+
----------
34+
options : dictionary
35+
parameter of AdversarialDebiasing
36+
num_epochs: trials of model training
37+
batch_size:Batch size for model training
38+
39+
Notes
40+
-----
41+
https://aif360.readthedocs.io/en/v0.2.3/_modules/aif360/algorithms/inprocessing/adversarial_debiasing.html
42+
43+
"""
44+
45+
def __init__(self, options):
46+
super().__init__()
47+
self.ds_train = None
48+
self.options = options
49+
50+
def fit(self, ds_train):
51+
"""
52+
Save training dataset
53+
54+
Attributes
55+
----------
56+
ds_train : Dataset
57+
Dataset for training
58+
"""
59+
self.ds_train = ds_train.copy(deepcopy=True)
60+
61+
def predict(self, ds_test):
62+
"""
63+
Model learning with debias using the training dataset imported by fit(), and predict using that model
64+
65+
Parameters
66+
----------
67+
ds_test : Dataset
68+
Dataset for prediction
69+
70+
Returns
71+
-------
72+
ds_predict : numpy.ndarray
73+
Predicted label
74+
"""
75+
ikey = ds_test.protected_attribute_names[0]
76+
priv_g = [{ikey: ds_test.privileged_protected_attributes[0]}]
77+
upriv_g = [{ikey: ds_test.unprivileged_protected_attributes[0]}]
78+
sess = tf.compat.v1.Session()
79+
model = AD(
80+
privileged_groups=priv_g,
81+
unprivileged_groups=upriv_g,
82+
scope_name='debiased_classifier',
83+
debias=True,
84+
sess=sess)
85+
model.fit(self.ds_train)
86+
ds_predict = model.predict(ds_test)
87+
sess.close()
88+
tf.compat.v1.reset_default_graph()
89+
return ds_predict
90+
91+
def bias_predict(self, ds_train):
92+
"""
93+
Model learning and prediction using AdversarialDebiasing of AIF360 without debias.
94+
95+
Parameters
96+
----------
97+
ds_train : Dataset
98+
Dataset for training and prediction
99+
100+
Returns
101+
-------
102+
ds_predict : numpy.ndarray
103+
Predicted label
104+
"""
105+
ikey = ds_train.protected_attribute_names[0]
106+
priv_g = [{ikey: ds_train.privileged_protected_attributes[0]}]
107+
upriv_g = [{ikey: ds_train.unprivileged_protected_attributes[0]}]
108+
sess = tf.compat.v1.Session()
109+
model = AD(
110+
privileged_groups=priv_g,
111+
unprivileged_groups=upriv_g,
112+
scope_name='plain_classifier',
113+
debias=False,
114+
sess=sess,
115+
num_epochs=self.options['num_epochs'],
116+
batch_size=self.options['batch_size'])
117+
model.fit(ds_train)
118+
ds_predict = model.predict(ds_train)
119+
sess.close()
120+
tf.compat.v1.reset_default_graph()
121+
return ds_predict
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
#
3+
# Copyright 2023 Fujitsu Limited
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
from abc import ABCMeta
18+
from abc import abstractmethod
19+
20+
21+
class InProcessing(metaclass=ABCMeta):
22+
"""
23+
Abstract Base Class for all inprocessing techniques.
24+
"""
25+
def __init__(self):
26+
super().__init__()
27+
self.model = None
28+
29+
@abstractmethod
30+
def fit(self, ds_train):
31+
"""
32+
Train a model on the input.
33+
34+
Parameters
35+
----------
36+
ds_train : Dataset
37+
Training Dataset.
38+
"""
39+
pass
40+
41+
@abstractmethod
42+
def predict(self, ds):
43+
"""
44+
Predict on the input.
45+
46+
Parameters
47+
----------
48+
ds : Dataset
49+
Dataset to predict.
50+
"""
51+
pass
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# -*- coding: utf-8 -*-
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Copyright 2023 Fujitsu Limited
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
import pandas as pd
19+
import matplotlib.pyplot as plt
20+
from matplotlib.gridspec import GridSpec
21+
import seaborn as sns
22+
23+
from aif360.algorithms.isf_helpers.isf_metrics.disparate_impact import DisparateImpact
24+
from aif360.algorithms.isf_helpers.isf_utils.common import create_multi_group_label
25+
26+
27+
def calc_intersectionalbias(dataset, metric="DisparateImpact"):
28+
"""
29+
Calculate intersectional bias(DisparateImpact) by more than one sensitive attributes
30+
31+
Parameters
32+
----------
33+
dataset : StructuredDataset
34+
A dataset containing more than one sensitive attributes
35+
36+
metric : str
37+
Fairness metric name
38+
["DisparateImpact"]
39+
40+
Returns
41+
-------
42+
df_result : DataFrame
43+
Intersectional bias(DisparateImpact)
44+
"""
45+
46+
df = dataset.convert_to_dataframe()[0]
47+
label_info = {dataset.label_names[0]: dataset.favorable_label}
48+
49+
if metric == "DisparateImpact":
50+
fs = DisparateImpact()
51+
else:
52+
raise ValueError("metric name not in the list of allowed metrics")
53+
54+
df_result = pd.DataFrame(columns=[metric])
55+
for multi_group_label in create_multi_group_label(dataset)[0]:
56+
protected_attr_info = multi_group_label[0]
57+
di = fs.bias_predict(df,
58+
protected_attr_info=protected_attr_info,
59+
label_info=label_info)
60+
name = ''
61+
for k, v in protected_attr_info.items():
62+
name += k + " = " + str(v) + ","
63+
df_result.loc[name[:-1]] = di
64+
65+
return df_result
66+
67+
68+
def plot_intersectionalbias_compare(ds_bef, ds_aft, vmax=1, vmin=0, center=0,
69+
metric="DisparateImpact",
70+
title={"right": "before", "left": "after"},
71+
filename=None):
72+
"""
73+
Compare drawing of intersectional bias in heat map
74+
75+
Parameters
76+
----------
77+
ds_bef : StructuredDataset
78+
Dataset containing two sensitive attributes (left figure)
79+
ds_aft : StructuredDataset
80+
Dataset containing two sensitive attributes (right figure)
81+
filename : str, optional
82+
File name(png)
83+
e.g. "./result/pict.png"
84+
metric : str
85+
Fairness metric name
86+
["DisparateImpact"]
87+
title : dictonary, optional
88+
Graph title (right figure, left figure)
89+
"""
90+
91+
df_bef = calc_intersectionalbias_matrix(ds_bef, metric)
92+
df_aft = calc_intersectionalbias_matrix(ds_aft, metric)
93+
94+
gs = GridSpec(1, 2)
95+
ss1 = gs.new_subplotspec((0, 0))
96+
ss2 = gs.new_subplotspec((0, 1))
97+
98+
ax1 = plt.subplot(ss1)
99+
ax2 = plt.subplot(ss2)
100+
101+
ax1.set_title(title['right'])
102+
sns.heatmap(df_bef, ax=ax1, vmax=vmax, vmin=vmin, center=center, annot=True, cmap='hot')
103+
104+
ax2.set_title(title['left'])
105+
sns.heatmap(df_aft, ax=ax2, vmax=vmax, vmin=vmin, center=center, annot=True, cmap='hot')
106+
107+
if filename is not None:
108+
plt.savefig(filename, format="png", dpi=300)
109+
plt.show()
110+
111+
112+
def calc_intersectionalbias_matrix(dataset, metric="DisparateImpact"):
113+
"""
114+
Comparison drawing of intersectional bias in heat map
115+
116+
Parameters
117+
----------
118+
dataset : StructuredDataset
119+
Dataset containing two sensitive attributes
120+
metric : str
121+
Fairness metric name
122+
["DisparateImpact"]
123+
124+
Returns
125+
-------
126+
df_result : DataFrame
127+
Intersectional bias(DisparateImpact)
128+
"""
129+
130+
protect_attr = dataset.protected_attribute_names
131+
132+
if len(protect_attr) != 2:
133+
raise ValueError("specify 2 sensitive attributes.")
134+
135+
if metric == "DisparateImpact":
136+
fs = DisparateImpact()
137+
else:
138+
raise ValueError("metric name not in the list of allowed metrics")
139+
140+
df = dataset.convert_to_dataframe()[0]
141+
label_info = {dataset.label_names[0]: dataset.favorable_label}
142+
143+
protect_attr0_values = list(set(df[protect_attr[0]]))
144+
protect_attr1_values = list(set(df[protect_attr[1]]))
145+
146+
df_result = pd.DataFrame(columns=protect_attr1_values)
147+
148+
for val0 in protect_attr0_values:
149+
tmp_li = []
150+
col_list = []
151+
for val1 in protect_attr1_values:
152+
di = fs.bias_predict(df,
153+
protected_attr_info={protect_attr[0]: val0, protect_attr[1]: val1},
154+
label_info=label_info)
155+
tmp_li += [di]
156+
col_list += [protect_attr[1]+"="+str(val1)]
157+
158+
df_result.loc[protect_attr[0]+"="+str(val0)] = tmp_li
159+
df_result = df_result.set_axis(col_list, axis=1)
160+
161+
return df_result

0 commit comments

Comments
 (0)