humancompatible-train 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. experiments/__init__.py +0 -0
  2. experiments/calculate_iteration_values.py +310 -0
  3. experiments/run_folktables.py +408 -0
  4. experiments/run_folktables_torchalgs.py +956 -0
  5. humancompatible/__init__.py +0 -0
  6. humancompatible/train/__init__.py +3 -0
  7. humancompatible/train/algorithms/Algorithm.py +25 -0
  8. humancompatible/train/algorithms/__init__.py +8 -0
  9. humancompatible/train/algorithms/ghost.py +250 -0
  10. humancompatible/train/algorithms/sgd.py +107 -0
  11. humancompatible/train/algorithms/ssl_alm.py +311 -0
  12. humancompatible/train/algorithms/switching_subgradient.py +192 -0
  13. humancompatible/train/algorithms/torch/__init__.py +4 -0
  14. humancompatible/train/algorithms/torch/ssl_alm.py +212 -0
  15. humancompatible/train/algorithms/torch/ssw.py +155 -0
  16. humancompatible/train/algorithms/utils.py +61 -0
  17. humancompatible/train/constraints/__init__.py +11 -0
  18. humancompatible/train/constraints/constraint.py +87 -0
  19. humancompatible/train/constraints/constraint_fns.py +118 -0
  20. humancompatible/train/fairness/__init__.py +0 -0
  21. humancompatible/train/fairness/constraints/__init__.py +15 -0
  22. humancompatible/train/fairness/constraints/constraint.py +97 -0
  23. humancompatible/train/fairness/constraints/constraint_fns.py +244 -0
  24. humancompatible/train/fairness/constraints/torch/__init__.py +1 -0
  25. humancompatible/train/fairness/constraints/torch/constraints.py +36 -0
  26. humancompatible/train/fairness/utils/__init__.py +1 -0
  27. humancompatible/train/fairness/utils/balanced_batch_sampler.py +65 -0
  28. humancompatible_train-0.1.0.dist-info/METADATA +188 -0
  29. humancompatible_train-0.1.0.dist-info/RECORD +32 -0
  30. humancompatible_train-0.1.0.dist-info/WHEEL +5 -0
  31. humancompatible_train-0.1.0.dist-info/licenses/LICENCE.txt +201 -0
  32. humancompatible_train-0.1.0.dist-info/top_level.txt +2 -0
File without changes
@@ -0,0 +1,310 @@
1
+ from copy import deepcopy
2
+ import importlib
3
+ from itertools import combinations
4
+ import os
5
+ import timeit
6
+ import warnings
7
+ import hydra
8
+ import numpy as np
9
+ import pandas as pd
10
+ import torch
11
+ from omegaconf import DictConfig, OmegaConf
12
+ from torch import nn, tensor
13
+ from torch.utils.data import TensorDataset, DataLoader, SubsetRandomSampler
14
+ from humancompatible.train.fairness.constraints.constraint_fns import fairret_stat_equality
15
+ from utils.load_folktables import prepare_folktables_multattr
16
+ from utils.network import SimpleNet
17
+ from humancompatible.train.algorithms.utils import net_grads_to_tensor, net_params_to_tensor
18
+ from itertools import combinations
19
+ from humancompatible.train.fairness.constraints import FairnessConstraint
20
+
21
+
22
+
23
+ # @hydra.main(version_base=None, config_path="conf", config_name="experiment")
24
+ def run(cfg: DictConfig) -> None:
25
+ warnings.filterwarnings("ignore", category=FutureWarning)
26
+
27
+ print(OmegaConf.to_yaml(cfg))
28
+ N_RUNS = cfg.n_runs
29
+ FT_STATE = cfg.data.state
30
+ FT_TASK = cfg.data.task
31
+ DOWNLOAD_DATA = cfg.data.download
32
+ DATA_PATH = cfg.data.path
33
+
34
+ if cfg.device == "cpu":
35
+ device = "cpu"
36
+ elif cfg.alg == "ghost":
37
+ device = "cpu"
38
+ print("CUDA not supported for Stochastic Ghost")
39
+ elif torch.cuda.is_available():
40
+ device = "cuda"
41
+ print("CUDA found")
42
+ else:
43
+ device = "cpu"
44
+ print("CUDA not found")
45
+
46
+ print(f"{device = }")
47
+ torch.set_default_device(device)
48
+
49
+ DTYPE = torch.float32
50
+
51
+ ## load data ##
52
+
53
+ torch.set_default_dtype(DTYPE)
54
+ DATASET_NAME = FT_TASK + "_" + FT_STATE
55
+
56
+ (
57
+ X_train,
58
+ y_train,
59
+ group_ind_train,
60
+ group_onehot_train,
61
+ sep_group_ind_train,
62
+ X_test,
63
+ y_test,
64
+ group_ind_test,
65
+ sep_group_ind_test,
66
+ group_onehot_test,
67
+ _
68
+ ) = prepare_folktables_multattr(
69
+ FT_TASK,
70
+ state=FT_STATE.upper(),
71
+ random_state=42,
72
+ onehot=False,
73
+ download=DOWNLOAD_DATA,
74
+ path=DATA_PATH,
75
+ sens_cols=cfg.data.sens_attr,
76
+ binarize=cfg.data.binarize,
77
+ stratify=False,
78
+ )
79
+ print('Groups:')
80
+ print(len(group_ind_train))
81
+ X_train_tensor = tensor(X_train, dtype=DTYPE)
82
+ y_train_tensor = tensor(y_train, dtype=DTYPE)
83
+ train_ds = TensorDataset(X_train_tensor, y_train_tensor)
84
+
85
+ print(f"Train data loaded: {(FT_TASK, FT_STATE)}")
86
+ print(f"Data shape: {X_train_tensor.shape}")
87
+
88
+ PATH =
89
+
90
+ ## prepare to save results ##
91
+
92
+ if "save_name" in cfg["alg"].keys():
93
+ alg_save_name = cfg.alg.save_name
94
+ else:
95
+ alg_save_name = cfg.alg.import_name
96
+
97
+ saved_models_path = os.path.abspath(
98
+ os.path.join(os.path.dirname(__file__), "utils", "saved_models")
99
+ )
100
+ directory = os.path.join(
101
+ saved_models_path, DATASET_NAME, CONSTRAINT, f"{BOUND:.0E}"
102
+ )
103
+
104
+ model_name = os.path.join(directory, f"{alg_save_name}_{BOUND}")
105
+
106
+ if not os.path.exists(directory):
107
+ os.makedirs(directory)
108
+
109
+ ## run experiments ##
110
+
111
+ histories = pd.read_csv(cfg.checkpoint_df_path)
112
+
113
+ ####################################################
114
+ ### CALCULATE STATS ON EVERY ALGORITHM ITERATION ###
115
+ ####################################################
116
+
117
+ loss_fn = nn.BCEWithLogitsLoss()
118
+ constraint_fn_module = importlib.import_module("humancompatible.train.fairness.constraints")
119
+ constraint_fn = getattr(constraint_fn_module, cfg.constraint.import_name)
120
+
121
+ print("----")
122
+ print("")
123
+
124
+ exp_iter_indices = [
125
+ histories.loc[exp_idx, :]
126
+ .index.get_level_values("iteration")[histories.loc[exp_idx]["w"].notna()]
127
+ .to_list()
128
+ for exp_idx in histories.index.get_level_values("trial").unique()
129
+ ]
130
+ exp_maxiter = np.argmax([ind[-1] for ind in exp_iter_indices])
131
+ longest_exp_indices = exp_iter_indices[exp_maxiter]
132
+ longest_exp_indices.extend(
133
+ [ei[-1] for ei in exp_iter_indices if ei[-1] not in longest_exp_indices]
134
+ )
135
+ longest_exp_indices = list(set(longest_exp_indices))
136
+ longest_exp_indices.sort()
137
+
138
+ index = pd.MultiIndex.from_product(
139
+ [longest_exp_indices, range(N_RUNS)],
140
+ names=("iteration", "trial"),
141
+ )
142
+ full_eval_train = pd.DataFrame(
143
+ index=index, columns=["G", "f", "fg", "c", "cg"]
144
+ ).sort_index()
145
+ full_eval_test = pd.DataFrame(
146
+ index=index, columns=["G", "f", "fg", "c", "cg"]
147
+ ).sort_index()
148
+
149
+ loss_fn = nn.BCEWithLogitsLoss()
150
+ X_test_tensor = tensor(X_test, dtype=DTYPE).to(device)
151
+ y_test_tensor = tensor(y_test, dtype=DTYPE).to(device)
152
+ X_train_tensor = X_train_tensor.to(device=device)
153
+ y_train_tensor = y_train_tensor.to(device=device)
154
+
155
+ save_train = True
156
+ save_test = True
157
+ histories.dropna(subset=["w"], inplace=True)
158
+
159
+ for exp_idx in range(N_RUNS):
160
+ for alg_iteration in histories.loc[exp_idx, :].index:
161
+ print(f"{exp_idx} | {alg_iteration}", end="\r")
162
+
163
+ w = histories["w"].loc[exp_idx, alg_iteration]
164
+ net.load_state_dict(w)
165
+ net = net.to(device)
166
+ if cfg.alg.import_name.lower() == "sslalm":
167
+ x_t = net_params_to_tensor(net, flatten=True, copy=True)
168
+ lambdas = histories["dual_ms"].loc[exp_idx, alg_iteration]
169
+ z = histories["z"].loc[exp_idx, alg_iteration]
170
+ params = {
171
+ "x_t": x_t,
172
+ "lambdas": lambdas,
173
+ "z": z,
174
+ "rho": cfg.alg.params.rho,
175
+ "mu": cfg.alg.params.mu,
176
+ }
177
+
178
+ if save_train:
179
+ if cfg.constraint.import_name == 'abs_max_dev_from_overall_tpr':
180
+ data_c = [[
181
+ (X_train_tensor[g_idx], y_train_tensor[g_idx]) for g_idx in group_ind_train
182
+ ]]
183
+ elif cfg.constraint.import_name in ['abs_diff_pr', 'abs_diff_tpr']:
184
+ data_c = [
185
+ (
186
+ (X_train_tensor[g_idx], y_train_tensor[g_idx]),
187
+ (X_train_tensor, y_train_tensor)
188
+ )
189
+ for g_idx in group_ind_train
190
+ ]
191
+ else:
192
+ data_c = [
193
+ (
194
+ (X_train_tensor[g_idx_1], y_train_tensor[g_idx_1]),
195
+ (X_train_tensor[g_idx_2], y_train_tensor[g_idx_2]),
196
+ )
197
+ for g_idx_1, g_idx_2 in combinations(group_ind_train, 2)
198
+ ]
199
+ calculate_iteration_values(
200
+ alg=cfg.alg.import_name,
201
+ full_eval=full_eval_train,
202
+ index_to_save=[alg_iteration, exp_idx],
203
+ c=c,
204
+ loss_fn=loss_fn,
205
+ data_f=[X_train_tensor, y_train_tensor],
206
+ data_c=data_c,
207
+ net=net,
208
+ device=device,
209
+ add_negative=cfg.constraint.add_negative,
210
+ **params,
211
+ )
212
+
213
+
214
+ if save_test:
215
+ if cfg.constraint.import_name == 'abs_max_dev_from_overall_tpr':
216
+ data_c = [[
217
+ (X_test_tensor[g_idx], y_test_tensor[g_idx]) for g_idx in group_ind_test
218
+ ]]
219
+ elif cfg.constraint.import_name in ['abs_diff_tpr', 'abs_diff_pr']:
220
+ data_c = [
221
+ (
222
+ (X_test_tensor[g_idx], y_test_tensor[g_idx]),
223
+ (X_test_tensor, y_test_tensor)
224
+ )
225
+ for g_idx in group_ind_test
226
+ ]
227
+ else:
228
+ data_c = [
229
+ (
230
+ (X_test_tensor[g_idx_1], y_test_tensor[g_idx_1]),
231
+ (X_test_tensor[g_idx_2], y_test_tensor[g_idx_2]),
232
+ )
233
+ for g_idx_1, g_idx_2 in combinations(group_ind_test, 2)
234
+ ]
235
+ calculate_iteration_values(
236
+ alg=cfg.alg.import_name,
237
+ full_eval=full_eval_test,
238
+ index_to_save=[alg_iteration, exp_idx],
239
+ c=c,
240
+ loss_fn=loss_fn,
241
+ data_f=[X_test_tensor, y_test_tensor],
242
+ data_c=data_c,
243
+ net=net,
244
+ device=device,
245
+ add_negative=cfg.constraint.add_negative,
246
+ **params,
247
+ )
248
+
249
+ net.zero_grad()
250
+
251
+ fname = f"AFTER_{alg_save_name}_{DATASET_NAME}_{BOUND}"
252
+ fext = ".csv"
253
+ if save_train:
254
+ fname_train = fname + "_train" + fext
255
+ save_path = os.path.join(utils_path, fname_train)
256
+ print(f"Saving to: {save_path}")
257
+ full_eval_train.to_pickle(save_path)
258
+
259
+ if save_test:
260
+ fname_test = fname + "_test" + fext
261
+ save_path = os.path.join(utils_path, fname_test)
262
+ print(f"Saving to: {save_path}")
263
+ full_eval_test.to_pickle(save_path)
264
+
265
+
266
+ # helper function to calculate relevant values on full dataset (e.g. constraint gradient, AL function, etc)
267
+ # used to calculate those values at different points during algorithms run
268
+ def calculate_iteration_values(
269
+ alg,
270
+ full_eval,
271
+ index_to_save,
272
+ c,
273
+ loss_fn,
274
+ data_f,
275
+ data_c,
276
+ net,
277
+ device,
278
+ add_negative,
279
+ **params,
280
+ ):
281
+ c_val_vec, c_grads_mat = [], []
282
+
283
+ for i, c_i in enumerate(c):
284
+ cv = c_i.eval(net, data_c[i // 2 if add_negative else i])
285
+ c_val_vec.append(cv)
286
+ cv.backward()
287
+ cg = net_grads_to_tensor(net, flatten=True, device=device)
288
+ net.zero_grad()
289
+ c_grads_mat.append(cg)
290
+ c_val_vec = torch.tensor(c_val_vec)
291
+ c_grads_mat = torch.stack(c_grads_mat)
292
+ full_eval.loc[*index_to_save]["c"] = [c_val_vec.detach().cpu().numpy()]
293
+ full_eval.loc[*index_to_save]["cg"] = [c_grads_mat.detach().cpu().numpy()]
294
+
295
+ X_tensor, y_tensor = data_f
296
+ outs = net(X_tensor)
297
+ if y_tensor.ndim < outs.ndim:
298
+ y_tensor = y_tensor.unsqueeze(1)
299
+ loss = loss_fn(outs, y_tensor)
300
+ loss.backward()
301
+ fg = net_grads_to_tensor(net, flatten=True, device=device)
302
+ net.zero_grad()
303
+
304
+ full_eval.loc[*index_to_save]["f"] = loss.detach().cpu().numpy()
305
+ full_eval.loc[*index_to_save]["fg"] = [fg.detach().cpu().numpy()]
306
+
307
+
308
+
309
+ if __name__ == "__main__":
310
+ run()