humancompatible-train 0.1.2__tar.gz → 0.1.3__tar.gz

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 (35) hide show
  1. {humancompatible_train-0.1.2/humancompatible_train.egg-info → humancompatible_train-0.1.3}/PKG-INFO +46 -20
  2. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/README.md +39 -20
  3. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/algorithms/__init__.py +1 -0
  4. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/algorithms/ssl_alm.py +0 -1
  5. humancompatible_train-0.1.3/humancompatible/train/algorithms/ssl_alm_adam.py +330 -0
  6. humancompatible_train-0.1.3/humancompatible/train/algorithms/test/test_ssl_alm_adam.py +146 -0
  7. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3/humancompatible_train.egg-info}/PKG-INFO +46 -20
  8. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible_train.egg-info/SOURCES.txt +2 -0
  9. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible_train.egg-info/requires.txt +8 -0
  10. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/pyproject.toml +2 -1
  11. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/LICENCE.txt +0 -0
  12. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/experiments/__init__.py +0 -0
  13. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/experiments/calculate_iteration_values.py +0 -0
  14. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/experiments/run_folktables.py +0 -0
  15. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/experiments/run_folktables_torchalgs.py +0 -0
  16. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/__init__.py +0 -0
  17. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/__init__.py +0 -0
  18. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/algorithms/ssw.py +0 -0
  19. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/algorithms/test/__init__.py +0 -0
  20. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/algorithms/test/test_ssl_alm.py +0 -0
  21. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/algorithms/test/test_ssw.py +0 -0
  22. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/fairness/__init__.py +0 -0
  23. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/fairness/constraints/__init__.py +0 -0
  24. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/fairness/constraints/constraint.py +0 -0
  25. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/fairness/constraints/constraint_fns.py +0 -0
  26. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/fairness/constraints/torch/__init__.py +0 -0
  27. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/fairness/constraints/torch/constraints.py +0 -0
  28. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/fairness/utils/__init__.py +0 -0
  29. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/fairness/utils/balanced_batch_sampler.py +0 -0
  30. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/fairness/utils/tests/__init__.py +0 -0
  31. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible/train/fairness/utils/tests/test_balanced_batch_sampler.py +0 -0
  32. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible_train.egg-info/dependency_links.txt +0 -0
  33. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/humancompatible_train.egg-info/top_level.txt +0 -0
  34. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/setup.cfg +0 -0
  35. {humancompatible_train-0.1.2 → humancompatible_train-0.1.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: humancompatible-train
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: PyTorch-based package for constrained training of neural networks
5
5
  Author: Gilles Bareilles, Jana Lepsova, Jakub Marecek
6
6
  Author-email: Andrii Kliachkin <kliachkin.andrii@gmail.com>
@@ -10,6 +10,13 @@ Description-Content-Type: text/markdown
10
10
  License-File: LICENCE.txt
11
11
  Requires-Dist: torch
12
12
  Requires-Dist: numpy
13
+ Provides-Extra: examples
14
+ Requires-Dist: ipykernel; extra == "examples"
15
+ Requires-Dist: ipympl; extra == "examples"
16
+ Requires-Dist: fairret; extra == "examples"
17
+ Requires-Dist: folktables; extra == "examples"
18
+ Requires-Dist: scikit-learn; extra == "examples"
19
+ Requires-Dist: matplotlib; extra == "examples"
13
20
  Provides-Extra: benchmark
14
21
  Requires-Dist: fairret; extra == "benchmark"
15
22
  Requires-Dist: matplotlib; extra == "benchmark"
@@ -31,22 +38,23 @@ The toolkit implements algorithms for constrained training of neural networks ba
31
38
  <!-- , as well as a tool to compare stochastic-constrained stochastic optimization algorithms on a _fair learning_ task in the `experiments` folder. -->
32
39
 
33
40
  ## Table of Contents
41
+
34
42
  1. [Basic installation instructions](#basic-installation-instructions)
35
43
  2. [Using the toolkit](#using-the-toolkit)
36
- 3. [Extending the toolkit](#extending-the-toolkit)
44
+ 3. [Extending the toolkit](#extending-the-toolkit)
37
45
  4. [Reproducing the Benchmark](#reproducing-the-benchmark)
38
46
  5. [License and terms of use](#license-and-terms-of-use)
39
47
  6. [References](#references)
40
48
 
41
49
  humancompatible-train is still under active development! If you find bugs or have feature
42
50
  requests, please file a
43
- [Github issue](https://github.com/humancompatible/train/issues).
51
+ [Github issue](https://github.com/humancompatible/train/issues).
44
52
 
45
53
  ## Installation
46
54
 
47
55
  Use
48
56
 
49
- ```
57
+ ```bash
50
58
  pip install humancompatible-train
51
59
  ```
52
60
 
@@ -56,11 +64,11 @@ The only dependencies of this package are `numpy` and `torch`.
56
64
 
57
65
  The toolkit implements algorithms for constrained training of neural networks based on PyTorch.
58
66
 
59
- The algorithms follow the `dual_step()` - `step()` framework: taking inspiration from PyTorch, the `double_step` does updates related to the dual parameters and prepares for the primal update (by, e.g., saving constraint gradients), and `step()` updates the primal parameters.
67
+ The algorithms follow the `dual_step()` - `step()` framework: taking inspiration from PyTorch, the `dual_step()` does updates related to the dual parameters and prepares for the primal update (by, e.g., saving constraint gradients), and `step()` updates the primal parameters.
60
68
 
61
69
  In general, your code using `humancompatible-train` would look something like this:
62
70
 
63
- ```
71
+ ```python
64
72
  for inputs, labels in dataloader:
65
73
  # inference
66
74
  outputs = model(inputs)
@@ -79,6 +87,7 @@ for inputs, labels in dataloader:
79
87
  ```
80
88
 
81
89
  Our idea is to
90
+
82
91
  1. Deviate minimally from the usual PyTorch workflow
83
92
  2. Make different stochastic-constrained stochastic optimization algorithms nearly interchangable in the code.
84
93
 
@@ -86,6 +95,12 @@ Our idea is to
86
95
 
87
96
  You are invited to check out our new API presented in notebooks in the `examples` folder.
88
97
 
98
+ The example notebooks have additional dependencies, such as `fairret`. To install those, run
99
+
100
+ ```
101
+ pip install humancompatible-train[examples]
102
+ ```
103
+
89
104
  *The legacy API used for the benchmark is presented in `examples/_old_/algorithm_demo.ipynb` and `examples/_old_/constraint_demo.ipynb`.*
90
105
 
91
106
  ## Extending the toolkit
@@ -99,21 +114,27 @@ You are invited to check out our new API presented in notebooks in the `examples
99
114
  The code used in [our benchmark paper](https://arxiv.org/abs/2507.04033) is not migrated to the new API yet (WIP).
100
115
 
101
116
  ### Basic installation instructions
117
+
102
118
  The code requires Python version ```3.11```.
103
119
 
104
120
  1. Create a virtual environment
105
121
 
106
122
  **bash** (Linux)
123
+
107
124
  ```
108
125
  python3.11 -m venv fairbenchenv
109
126
  source fairbenchenv/bin/activate
110
127
  ```
128
+
111
129
  **cmd** (Windows)
130
+
112
131
  ```
113
132
  python -m venv fairbenchenv
114
133
  fairbenchenv\Scripts\activate.bat
115
134
  ```
135
+
116
136
  2. Install from source.
137
+
117
138
  ```
118
139
  git clone https://github.com/humancompatible/train.git
119
140
  cd train
@@ -122,11 +143,12 @@ pip install .
122
143
  ```
123
144
 
124
145
  If you wish to edit the code of the algorithms, install as an editable package:
146
+
125
147
  ```
126
148
  pip install -e .
127
149
  ```
128
150
 
129
- __Warning__: it is recommended to use Stochastic Ghost with the mkl-accelerated version of the scipy package with Stochastic Ghost; to install it, run
151
+ **Warning**: it is recommended to use Stochastic Ghost with the mkl-accelerated version of the scipy package with Stochastic Ghost; to install it, run
130
152
 
131
153
  ```pip install --force-reinstall -i https://software.repos.intel.com/python/pypi scipy```
132
154
 
@@ -140,12 +162,14 @@ after installing requirements.txt; otherwise, the algorithm will run slower. How
140
162
  ### Running the algorithms
141
163
 
142
164
  The benchmark comprises the following algorithms:
165
+
143
166
  - Stochastic Ghost [[2]](#2),
144
167
  - SSL-ALM [[3]](#3),
145
168
  - Stochastic Switching Subgradient [[4]](#4).
146
169
 
147
170
  To reproduce the experiments of the paper, run the following:
148
- ``` python
171
+
172
+ ```
149
173
  cd experiments
150
174
  python run_folktables.py data=folktables alg=sslalm
151
175
  python run_folktables.py data=folktables alg=alm
@@ -154,23 +178,25 @@ python run_folktables.py data=folktables alg=ssg
154
178
  python run_folktables.py data=folktables alg=sgd # baseline, no fairness
155
179
  python run_folktables.py data=folktables alg=fairret # baseline, fairness with regularizer
156
180
  ```
181
+
157
182
  Each command will start 10 runs of the `alg`, 30 seconds each.
158
183
  The results will be saved to `experiments/utils/saved_models` and `experiments/utils/exp_results`.
159
184
  <!-- In the repository, we include the configuration needed to reproduce the experiments in the paper. To do so, go to `experiments` and run `python run_folktables.py data=folktables alg=sslalm`. -->
160
185
  <!-- Repeat for the other algorithms by changing the `alg` parameter. -->
161
186
 
162
- This repository uses [Hydra](https://hydra.cc/) to manage parameters; see `experiments/conf` for configuration files.
163
- * To change the parameters of the experiment, such as the number of runs for each algorithm, run time, the dataset used (*note: for now supports only Folktables*) - use `experiment.yaml`.
164
- * To change the dataset settings - such as file location - or do dataset-specific adjustments - such as the configuration of the protected attributes - use `data/{dataset_name}.yaml`
165
- * To change algorithm hyperparameters, use `alg/{algorithm_name}.yaml`.
166
- * To change constraint hyperparameters, use `constraint/{constraint_name}.yaml`
187
+ This repository uses [Hydra](https://hydra.cc/) to manage parameters; see `experiments/conf` for configuration files.
188
+
189
+ - To change the parameters of the experiment, such as the number of runs for each algorithm, run time, the dataset used (*note: for now supports only Folktables*) - use `experiment.yaml`.
190
+ - To change the dataset settings - such as file location - or do dataset-specific adjustments - such as the configuration of the protected attributes - use `data/{dataset_name}.yaml`
191
+ - To change algorithm hyperparameters, use `alg/{algorithm_name}.yaml`.
192
+ - To change constraint hyperparameters, use `constraint/{constraint_name}.yaml`
167
193
 
168
194
  <!-- ; it is installed as one of the dependencies. -->
169
195
  <!-- To learn more about using Hydra, please check out the [official tutorial](https://hydra.cc/docs/tutorials/basic/your_first_app). -->
170
196
 
171
197
  ### Producing plots
172
- The plots and tables like the ones in the paper can be produced using the two notebooks. `experiments/algo_plots.ipynb` houses the convergence plots, and `experiments/model_plots.ipynb` - all the others.
173
198
 
199
+ The plots and tables like the ones in the paper can be produced using the two notebooks. `experiments/algo_plots.ipynb` houses the convergence plots, and `experiments/model_plots.ipynb` - all the others.
174
200
 
175
201
  ## License and terms of use
176
202
 
@@ -180,7 +206,7 @@ The benchmark part of the package relies on the Folktables package, provided und
180
206
  It provides code to download data from the American Community Survey
181
207
  (ACS) Public Use Microdata Sample (PUMS) files managed by the US Census Bureau.
182
208
  The data itself is governed by the terms of use provided by the Census Bureau.
183
- For more information, see https://www.census.gov/data/developers/about/terms-of-service.html
209
+ For more information, see <https://www.census.gov/data/developers/about/terms-of-service.html>
184
210
 
185
211
  <!-- ## Cite this work -->
186
212
 
@@ -206,7 +232,7 @@ For more information, see https://www.census.gov/data/developers/about/terms-of-
206
232
 
207
233
  If you use this work, we encourage you to cite [our paper](https://arxiv.org/abs/2507.04033),
208
234
 
209
- ```
235
+ ```bibtex
210
236
  @misc{kliachkin2025benchmarkingstochasticapproximationalgorithms,
211
237
  title={Benchmarking Stochastic Approximation Algorithms for Fairness-Constrained Training of Deep Neural Networks},
212
238
  author={Andrii Kliachkin and Jana Lepšová and Gilles Bareilles and Jakub Mareček},
@@ -218,14 +244,14 @@ If you use this work, we encourage you to cite [our paper](https://arxiv.org/abs
218
244
  }
219
245
  ```
220
246
 
221
- <a id="1">[1]</a>
247
+ <a id="1">[1]</a>
222
248
  Ding, Hardt & Miller et al. (2021) Retiring Adult: New Datasets for Fair Machine Learning, Curran Associates, Inc..
223
249
 
224
- <a id="2">[2]</a>
250
+ <a id="2">[2]</a>
225
251
  Facchinei & Kungurtsev (2023) Stochastic Approximation for Expectation Objective and Expectation Inequality-Constrained Nonconvex Optimization, arXiv.
226
252
 
227
- <a id="3">[3]</a>
253
+ <a id="3">[3]</a>
228
254
  Huang, Zhang & Alacaoglu (2025) Stochastic Smoothed Primal-Dual Algorithms for Nonconvex Optimization with Linear Inequality Constraints, arXiv.
229
255
 
230
- <a id="4">[4]</a>
256
+ <a id="4">[4]</a>
231
257
  Huang & Lin (2023) Oracle Complexity of Single-Loop Switching Subgradient Methods for Non-Smooth Weakly Convex Functional Constrained Optimization, Curran Associates Inc..
@@ -6,22 +6,23 @@ The toolkit implements algorithms for constrained training of neural networks ba
6
6
  <!-- , as well as a tool to compare stochastic-constrained stochastic optimization algorithms on a _fair learning_ task in the `experiments` folder. -->
7
7
 
8
8
  ## Table of Contents
9
+
9
10
  1. [Basic installation instructions](#basic-installation-instructions)
10
11
  2. [Using the toolkit](#using-the-toolkit)
11
- 3. [Extending the toolkit](#extending-the-toolkit)
12
+ 3. [Extending the toolkit](#extending-the-toolkit)
12
13
  4. [Reproducing the Benchmark](#reproducing-the-benchmark)
13
14
  5. [License and terms of use](#license-and-terms-of-use)
14
15
  6. [References](#references)
15
16
 
16
17
  humancompatible-train is still under active development! If you find bugs or have feature
17
18
  requests, please file a
18
- [Github issue](https://github.com/humancompatible/train/issues).
19
+ [Github issue](https://github.com/humancompatible/train/issues).
19
20
 
20
21
  ## Installation
21
22
 
22
23
  Use
23
24
 
24
- ```
25
+ ```bash
25
26
  pip install humancompatible-train
26
27
  ```
27
28
 
@@ -31,11 +32,11 @@ The only dependencies of this package are `numpy` and `torch`.
31
32
 
32
33
  The toolkit implements algorithms for constrained training of neural networks based on PyTorch.
33
34
 
34
- The algorithms follow the `dual_step()` - `step()` framework: taking inspiration from PyTorch, the `double_step` does updates related to the dual parameters and prepares for the primal update (by, e.g., saving constraint gradients), and `step()` updates the primal parameters.
35
+ The algorithms follow the `dual_step()` - `step()` framework: taking inspiration from PyTorch, the `dual_step()` does updates related to the dual parameters and prepares for the primal update (by, e.g., saving constraint gradients), and `step()` updates the primal parameters.
35
36
 
36
37
  In general, your code using `humancompatible-train` would look something like this:
37
38
 
38
- ```
39
+ ```python
39
40
  for inputs, labels in dataloader:
40
41
  # inference
41
42
  outputs = model(inputs)
@@ -54,6 +55,7 @@ for inputs, labels in dataloader:
54
55
  ```
55
56
 
56
57
  Our idea is to
58
+
57
59
  1. Deviate minimally from the usual PyTorch workflow
58
60
  2. Make different stochastic-constrained stochastic optimization algorithms nearly interchangable in the code.
59
61
 
@@ -61,6 +63,12 @@ Our idea is to
61
63
 
62
64
  You are invited to check out our new API presented in notebooks in the `examples` folder.
63
65
 
66
+ The example notebooks have additional dependencies, such as `fairret`. To install those, run
67
+
68
+ ```
69
+ pip install humancompatible-train[examples]
70
+ ```
71
+
64
72
  *The legacy API used for the benchmark is presented in `examples/_old_/algorithm_demo.ipynb` and `examples/_old_/constraint_demo.ipynb`.*
65
73
 
66
74
  ## Extending the toolkit
@@ -74,21 +82,27 @@ You are invited to check out our new API presented in notebooks in the `examples
74
82
  The code used in [our benchmark paper](https://arxiv.org/abs/2507.04033) is not migrated to the new API yet (WIP).
75
83
 
76
84
  ### Basic installation instructions
85
+
77
86
  The code requires Python version ```3.11```.
78
87
 
79
88
  1. Create a virtual environment
80
89
 
81
90
  **bash** (Linux)
91
+
82
92
  ```
83
93
  python3.11 -m venv fairbenchenv
84
94
  source fairbenchenv/bin/activate
85
95
  ```
96
+
86
97
  **cmd** (Windows)
98
+
87
99
  ```
88
100
  python -m venv fairbenchenv
89
101
  fairbenchenv\Scripts\activate.bat
90
102
  ```
103
+
91
104
  2. Install from source.
105
+
92
106
  ```
93
107
  git clone https://github.com/humancompatible/train.git
94
108
  cd train
@@ -97,11 +111,12 @@ pip install .
97
111
  ```
98
112
 
99
113
  If you wish to edit the code of the algorithms, install as an editable package:
114
+
100
115
  ```
101
116
  pip install -e .
102
117
  ```
103
118
 
104
- __Warning__: it is recommended to use Stochastic Ghost with the mkl-accelerated version of the scipy package with Stochastic Ghost; to install it, run
119
+ **Warning**: it is recommended to use Stochastic Ghost with the mkl-accelerated version of the scipy package with Stochastic Ghost; to install it, run
105
120
 
106
121
  ```pip install --force-reinstall -i https://software.repos.intel.com/python/pypi scipy```
107
122
 
@@ -115,12 +130,14 @@ after installing requirements.txt; otherwise, the algorithm will run slower. How
115
130
  ### Running the algorithms
116
131
 
117
132
  The benchmark comprises the following algorithms:
133
+
118
134
  - Stochastic Ghost [[2]](#2),
119
135
  - SSL-ALM [[3]](#3),
120
136
  - Stochastic Switching Subgradient [[4]](#4).
121
137
 
122
138
  To reproduce the experiments of the paper, run the following:
123
- ``` python
139
+
140
+ ```
124
141
  cd experiments
125
142
  python run_folktables.py data=folktables alg=sslalm
126
143
  python run_folktables.py data=folktables alg=alm
@@ -129,23 +146,25 @@ python run_folktables.py data=folktables alg=ssg
129
146
  python run_folktables.py data=folktables alg=sgd # baseline, no fairness
130
147
  python run_folktables.py data=folktables alg=fairret # baseline, fairness with regularizer
131
148
  ```
149
+
132
150
  Each command will start 10 runs of the `alg`, 30 seconds each.
133
151
  The results will be saved to `experiments/utils/saved_models` and `experiments/utils/exp_results`.
134
152
  <!-- In the repository, we include the configuration needed to reproduce the experiments in the paper. To do so, go to `experiments` and run `python run_folktables.py data=folktables alg=sslalm`. -->
135
153
  <!-- Repeat for the other algorithms by changing the `alg` parameter. -->
136
154
 
137
- This repository uses [Hydra](https://hydra.cc/) to manage parameters; see `experiments/conf` for configuration files.
138
- * To change the parameters of the experiment, such as the number of runs for each algorithm, run time, the dataset used (*note: for now supports only Folktables*) - use `experiment.yaml`.
139
- * To change the dataset settings - such as file location - or do dataset-specific adjustments - such as the configuration of the protected attributes - use `data/{dataset_name}.yaml`
140
- * To change algorithm hyperparameters, use `alg/{algorithm_name}.yaml`.
141
- * To change constraint hyperparameters, use `constraint/{constraint_name}.yaml`
155
+ This repository uses [Hydra](https://hydra.cc/) to manage parameters; see `experiments/conf` for configuration files.
156
+
157
+ - To change the parameters of the experiment, such as the number of runs for each algorithm, run time, the dataset used (*note: for now supports only Folktables*) - use `experiment.yaml`.
158
+ - To change the dataset settings - such as file location - or do dataset-specific adjustments - such as the configuration of the protected attributes - use `data/{dataset_name}.yaml`
159
+ - To change algorithm hyperparameters, use `alg/{algorithm_name}.yaml`.
160
+ - To change constraint hyperparameters, use `constraint/{constraint_name}.yaml`
142
161
 
143
162
  <!-- ; it is installed as one of the dependencies. -->
144
163
  <!-- To learn more about using Hydra, please check out the [official tutorial](https://hydra.cc/docs/tutorials/basic/your_first_app). -->
145
164
 
146
165
  ### Producing plots
147
- The plots and tables like the ones in the paper can be produced using the two notebooks. `experiments/algo_plots.ipynb` houses the convergence plots, and `experiments/model_plots.ipynb` - all the others.
148
166
 
167
+ The plots and tables like the ones in the paper can be produced using the two notebooks. `experiments/algo_plots.ipynb` houses the convergence plots, and `experiments/model_plots.ipynb` - all the others.
149
168
 
150
169
  ## License and terms of use
151
170
 
@@ -155,7 +174,7 @@ The benchmark part of the package relies on the Folktables package, provided und
155
174
  It provides code to download data from the American Community Survey
156
175
  (ACS) Public Use Microdata Sample (PUMS) files managed by the US Census Bureau.
157
176
  The data itself is governed by the terms of use provided by the Census Bureau.
158
- For more information, see https://www.census.gov/data/developers/about/terms-of-service.html
177
+ For more information, see <https://www.census.gov/data/developers/about/terms-of-service.html>
159
178
 
160
179
  <!-- ## Cite this work -->
161
180
 
@@ -181,7 +200,7 @@ For more information, see https://www.census.gov/data/developers/about/terms-of-
181
200
 
182
201
  If you use this work, we encourage you to cite [our paper](https://arxiv.org/abs/2507.04033),
183
202
 
184
- ```
203
+ ```bibtex
185
204
  @misc{kliachkin2025benchmarkingstochasticapproximationalgorithms,
186
205
  title={Benchmarking Stochastic Approximation Algorithms for Fairness-Constrained Training of Deep Neural Networks},
187
206
  author={Andrii Kliachkin and Jana Lepšová and Gilles Bareilles and Jakub Mareček},
@@ -193,14 +212,14 @@ If you use this work, we encourage you to cite [our paper](https://arxiv.org/abs
193
212
  }
194
213
  ```
195
214
 
196
- <a id="1">[1]</a>
215
+ <a id="1">[1]</a>
197
216
  Ding, Hardt & Miller et al. (2021) Retiring Adult: New Datasets for Fair Machine Learning, Curran Associates, Inc..
198
217
 
199
- <a id="2">[2]</a>
218
+ <a id="2">[2]</a>
200
219
  Facchinei & Kungurtsev (2023) Stochastic Approximation for Expectation Objective and Expectation Inequality-Constrained Nonconvex Optimization, arXiv.
201
220
 
202
- <a id="3">[3]</a>
221
+ <a id="3">[3]</a>
203
222
  Huang, Zhang & Alacaoglu (2025) Stochastic Smoothed Primal-Dual Algorithms for Nonconvex Optimization with Linear Inequality Constraints, arXiv.
204
223
 
205
- <a id="4">[4]</a>
206
- Huang & Lin (2023) Oracle Complexity of Single-Loop Switching Subgradient Methods for Non-Smooth Weakly Convex Functional Constrained Optimization, Curran Associates Inc..
224
+ <a id="4">[4]</a>
225
+ Huang & Lin (2023) Oracle Complexity of Single-Loop Switching Subgradient Methods for Non-Smooth Weakly Convex Functional Constrained Optimization, Curran Associates Inc..
@@ -1,4 +1,5 @@
1
1
  from .ssl_alm import SSLALM
2
2
  from .ssw import SSG
3
+ from .ssl_alm_adam import SSLALM_Adam
3
4
 
4
5
  __all__ = ["SSLALM", "SSG"]
@@ -2,7 +2,6 @@ from typing import Iterable, Optional, Union
2
2
 
3
3
  import torch
4
4
  from torch import Tensor
5
-
6
5
  from torch.optim.optimizer import Optimizer, _use_grad_for_differentiable
7
6
 
8
7
  class SSLALM(Optimizer):
@@ -0,0 +1,330 @@
1
+ from typing import Iterable, Optional, Union
2
+
3
+ import torch
4
+ from torch import Tensor
5
+ from torch.optim.optimizer import Optimizer, _use_grad_for_differentiable
6
+
7
+ class SSLALM_Adam(Optimizer):
8
+ def __init__(
9
+ self,
10
+ params,
11
+ m: int,
12
+ # tau in paper
13
+ lr: Union[float, Tensor] = 5e-2,
14
+ # eta in paper
15
+ dual_lr: Union[
16
+ float, Tensor
17
+ ] = 5e-2, # keep as tensor for different learning rates for different constraints in the future? idk
18
+ dual_bound : Union[
19
+ float, Tensor
20
+ ] = 100,
21
+ # penalty term multiplier
22
+ rho: float = 1.0,
23
+ # smoothing term multiplier
24
+ mu: float = 2.0,
25
+ # smoothing term update multiplier
26
+ beta: float = 0.5,
27
+ beta1: float = 0.9,
28
+ beta2: float = 0.999,
29
+ eps: float = 1e-8,
30
+ amsgrad: bool = False,
31
+ *,
32
+ init_dual_vars: Optional[Tensor] = None,
33
+ # whether some of the dual variables should not be updated
34
+ fix_dual_vars: Optional[Tensor] = None,
35
+ differentiable: bool = False,
36
+ # custom_project_fn: Optional[Callable] = project_fn
37
+ ):
38
+ if isinstance(lr, torch.Tensor) and lr.numel() != 1:
39
+ raise ValueError("Tensor lr must be 1-element")
40
+ if isinstance(dual_lr, torch.Tensor) and lr.numel() != 1:
41
+ raise ValueError("Tensor dual_lr must be 1-element")
42
+ if lr < 0.0:
43
+ raise ValueError(f"Invalid learning rate: {lr}")
44
+ if dual_lr < 0.0:
45
+ raise ValueError(f"Invalid dual learning rate: {dual_lr}")
46
+ if init_dual_vars is not None and len(init_dual_vars) != m:
47
+ raise ValueError(
48
+ f"init_dual_vars should be of length m: expected {m}, got {len(init_dual_vars)}"
49
+ )
50
+ if fix_dual_vars is not None:
51
+ raise NotImplementedError()
52
+ if init_dual_vars is None and fix_dual_vars is not None:
53
+ raise ValueError(
54
+ f"if fix_dual_vars is not None, init_dual_vars should not be None."
55
+ )
56
+
57
+ if differentiable:
58
+ raise NotImplementedError("TorchSSLALM does not support differentiable")
59
+
60
+ defaults = dict(
61
+ lr=lr,
62
+ dual_lr=dual_lr,
63
+ rho=rho,
64
+ mu=mu,
65
+ beta=beta,
66
+ amsgrad=amsgrad,
67
+ differentiable=differentiable,
68
+ # custom_project_fn=custom_project_fn
69
+ )
70
+
71
+ super().__init__(params, defaults)
72
+
73
+ # self.param_groups.append()
74
+
75
+ self.m = m
76
+ self.dual_lr = dual_lr
77
+ self.dual_bound = dual_bound
78
+ self.rho = rho
79
+ self.beta = beta
80
+ self.beta1 = beta1
81
+ self.beta2 = beta2
82
+ self.mu = mu
83
+ self.c_vals: list[Union[float, Tensor]] = []
84
+ self._c_val_average = [None]
85
+ self.beta1 = beta1
86
+ self.beta2 = beta2
87
+ self.eps = eps
88
+ # essentially, move everything here to self.state[param_group]
89
+ # self.state[param_group]['smoothing_avg'] <= z for that param_group;
90
+ # ...['grad'] <= grad w.r.t. that param_group
91
+ # ...['G'] <= G w.r.t. that param_group // idk if necessary
92
+ # ...['c_grad'][c_i] <= grad of ith constraint w.r.t. that group<w
93
+ if init_dual_vars is not None:
94
+ self._dual_vars = init_dual_vars
95
+ else:
96
+ self._dual_vars = torch.zeros(m, requires_grad=False)
97
+
98
+ def _init_group(
99
+ self,
100
+ group,
101
+ params,
102
+ grads,
103
+ c_grads,
104
+ exp_avgs,
105
+ exp_avg_sqs,
106
+ max_exp_avg_sqs,
107
+ state_steps,
108
+ smoothing
109
+ ):
110
+ # SHOULDN'T calculate values, only set them from the state of the respective param_group
111
+ # calculations only happen in step() (or rather in the func version of step)
112
+ has_sparse_grad = False
113
+
114
+ for p in group["params"]:
115
+ state = self.state[p]
116
+
117
+ params.append(p)
118
+
119
+ # load z (smoothing term)
120
+ # Lazy state initialization
121
+ if len(state) == 0:
122
+ state["smoothing"] = p.detach().clone()
123
+ state["c_grad"] = []
124
+
125
+ state["step"] = (
126
+ torch.tensor(0.0)
127
+ )
128
+ # Exponential moving average of gradient values
129
+ state["exp_avg"] = torch.zeros_like(
130
+ p, memory_format=torch.preserve_format
131
+ )
132
+ # Exponential moving average of squared gradient values
133
+ state["exp_avg_sq"] = torch.zeros_like(
134
+ p, memory_format=torch.preserve_format
135
+ )
136
+
137
+ if group["amsgrad"]:
138
+ # raise NotImplementedError()
139
+ # Maintains max of all exp. moving avg. of sq. grad. values
140
+ state["max_exp_avg_sq"] = torch.zeros_like(
141
+ p, memory_format=torch.preserve_format
142
+ )
143
+
144
+ exp_avgs.append(state["exp_avg"])
145
+ exp_avg_sqs.append(state["exp_avg_sq"])
146
+
147
+ if group["amsgrad"]:
148
+ max_exp_avg_sqs.append(state["max_exp_avg_sq"])
149
+ if group["differentiable"] and state["step"].requires_grad:
150
+ raise RuntimeError(
151
+ "`requires_grad` is not supported for `step` in differentiable mode"
152
+ )
153
+
154
+ smoothing.append(state.get("smoothing"))
155
+
156
+ state_steps.append(state["step"])
157
+
158
+ grads.append(p.grad)
159
+ c_grads.append(state.get("c_grad"))
160
+ return has_sparse_grad
161
+
162
+ def __setstate__(self, state):
163
+ super().__setstate__(state)
164
+ for group in self.param_groups:
165
+ group.setdefault("amsgrad", False)
166
+ group.setdefault("maximize", False)
167
+ group.setdefault("foreach", None)
168
+ group.setdefault("capturable", False)
169
+ group.setdefault("differentiable", False)
170
+ group.setdefault("decoupled_weight_decay", False)
171
+
172
+ def dual_step(self, i: int, c_val: Tensor):
173
+ r"""Perform an update of the dual parameters.
174
+ Also saves constraint gradient for weight update. To be called BEFORE :func:`step` in an iteration!
175
+
176
+ Args:
177
+ i (int): index of the constraint
178
+ c_val (Tensor): an estimate of the value of the constraint at which the gradient was computed; used for dual parameter update
179
+ """
180
+
181
+ # c_vals is cleaned in step()
182
+ self.c_vals.append(c_val.detach())
183
+
184
+ # update dual multipliers
185
+ dual_update_tensor = torch.zeros_like(self._dual_vars)
186
+ dual_update_tensor[i] = self.dual_lr * c_val
187
+ self._dual_vars.add_(dual_update_tensor)
188
+ for i in range(len(self._dual_vars)):
189
+ if self._dual_vars[i] >= self.dual_bound or self._dual_vars[i] < 0:
190
+ self._dual_vars[i].zero_()
191
+
192
+ # save constraint grad
193
+ for group in self.param_groups:
194
+ params: list[Tensor] = []
195
+ grads: list[Tensor] = []
196
+ c_grads: list[Tensor] = []
197
+ smoothing: list[Tensor] = []
198
+ exp_avgs: list[Tensor] = []
199
+ exp_avg_sqs: list[Tensor] = []
200
+ max_exp_avg_sqs: list[Tensor] = []
201
+ state_steps: list[Tensor] = []
202
+ _ = self._init_group(
203
+ group,
204
+ params,
205
+ grads,
206
+ c_grads,
207
+ exp_avgs,
208
+ exp_avg_sqs,
209
+ max_exp_avg_sqs,
210
+ state_steps,
211
+ smoothing
212
+ )
213
+
214
+ for p in group["params"]:
215
+ state = self.state[p]
216
+ # state['c_grad'] is cleaned in step()
217
+ # so it is always empty on dual_step()
218
+ state["c_grad"].append(p.grad)
219
+
220
+ @_use_grad_for_differentiable
221
+ def step(self, c_val: Union[Iterable | Tensor] = None):
222
+ r"""Perform an update of the primal parameters (network weights & slack variables). To be called AFTER :func:`dual_step` in an iteration!
223
+
224
+ Args:
225
+ c_val (Tensor): an Iterable of estimates of values of **ALL** constraints; used for primal parameter update.
226
+ Ideally, must be evaluated on an independent sample from the one used in :func:`dual_step`
227
+ """
228
+
229
+ if c_val is None:
230
+ c_val = self.c_vals
231
+ if isinstance(c_val, Iterable) and not isinstance(c_val, torch.Tensor):
232
+ # if len(c_val) == 1 and isinstance(c_val[0], torch.Tensor):
233
+ # c_val = c_val[0]
234
+ # else:
235
+ c_val = torch.stack(c_val)
236
+ if c_val.ndim > 1:
237
+ c_val = c_val.squeeze(-1)
238
+
239
+ if c_val.numel() != self.m:
240
+ raise ValueError(f"Number of elements in c_val must be equal to m={self.m}, got {c_val.numel()}")
241
+ G = []
242
+
243
+ for group in self.param_groups:
244
+ params: list[Tensor] = []
245
+ grads: list[Tensor] = []
246
+ c_grads: list[Tensor] = []
247
+ smoothing: list[Tensor] = []
248
+ exp_avgs: list[Tensor] = []
249
+ exp_avg_sqs: list[Tensor] = []
250
+ max_exp_avg_sqs: list[Tensor] = []
251
+ state_steps: list[Tensor] = []
252
+ lr = group["lr"]
253
+ amsgrad = group["amsgrad"]
254
+
255
+ _ = self._init_group(
256
+ group,
257
+ params,
258
+ grads,
259
+ c_grads,
260
+ exp_avgs,
261
+ exp_avg_sqs,
262
+ max_exp_avg_sqs,
263
+ state_steps,
264
+ smoothing
265
+ )
266
+
267
+ for i, param in enumerate(params):
268
+ ### calculate Lagrange f-n gradient (G) ###
269
+
270
+ # stack list of grads w.r.t. constraints to get
271
+ # tensor of shape (*param.shape, m)
272
+ l_term_grad = 0
273
+ aug_term_grad = 0
274
+ # if c_grads[i] is not None:
275
+ for j, c_grad in enumerate(c_grads[i]):
276
+ if c_grad is None:
277
+ continue
278
+ l_term_grad += c_grad * self._dual_vars[j]
279
+ aug_term_grad += c_grad * c_val[j]
280
+
281
+ G_i = (
282
+ grads[i]
283
+ + l_term_grad
284
+ + self.rho * aug_term_grad
285
+ + self.mu * (param - smoothing[i])
286
+ )
287
+
288
+ G.append(G_i)
289
+
290
+ exp_avg = exp_avgs[i]
291
+ exp_avg_sq = exp_avg_sqs[i]
292
+ step_t = state_steps[i]
293
+ step_t += 1
294
+ beta1 = self.beta1
295
+ beta2 = self.beta2
296
+ eps = self.eps
297
+
298
+ exp_avg.lerp_(G_i, 1 - beta1)
299
+ exp_avg_sq.mul_(beta2).addcmul_(G_i, G_i, value=1 - beta2)
300
+
301
+
302
+ smoothing[i].add_(param - smoothing[i], alpha=self.beta)
303
+
304
+ # param.add_(G_i, alpha=-lr)
305
+
306
+ bias_correction1 = 1 - beta1**step_t
307
+ bias_correction2 = 1 - beta2**step_t
308
+
309
+ step_size = lr / bias_correction1
310
+
311
+ bias_correction2_sqrt = bias_correction2**0.5
312
+
313
+ if amsgrad:
314
+ # Maintains the maximum of all 2nd moment running avg. till now
315
+ torch.maximum(max_exp_avg_sqs[i], exp_avg_sq, out=max_exp_avg_sqs[i])
316
+
317
+ # Use the max. for normalizing running avg. of gradient
318
+ denom = (max_exp_avg_sqs[i].sqrt() / bias_correction2_sqrt).add_(eps)
319
+ else:
320
+ denom = (exp_avg_sq.sqrt() / bias_correction2_sqrt).add_(eps)
321
+
322
+ param.addcdiv_(exp_avg, denom, value=-step_size)
323
+
324
+ ## PROJECT (keep in mind we do layer by layer)
325
+ ## add slack variables to params in constructor?
326
+
327
+ c_grads[i].clear()
328
+
329
+ self.c_vals.clear()
330
+ return G
@@ -0,0 +1,146 @@
1
+ import unittest
2
+ import torch
3
+ from torch import Tensor
4
+ from humancompatible.train.algorithms import SSLALM_Adam
5
+
6
+ class TestSSLALMAdam(unittest.TestCase):
7
+ def setUp(self):
8
+ # Simple model for testing
9
+ self.model = torch.nn.Linear(2, 1)
10
+ self.params = list(self.model.parameters())
11
+ self.m = 2 # Number of constraints
12
+ self.optimizer = SSLALM_Adam(
13
+ self.params,
14
+ m=self.m,
15
+ lr=0.01,
16
+ dual_lr=0.01,
17
+ dual_bound=100.0,
18
+ rho=1.0,
19
+ mu=2.0,
20
+ beta=0.5,
21
+ beta1=0.9,
22
+ beta2=0.999
23
+ )
24
+
25
+ def test_initialization(self):
26
+ # Test if the optimizer is initialized correctly
27
+ self.assertEqual(len(self.optimizer.param_groups), 1)
28
+ self.assertEqual(self.optimizer.m, self.m)
29
+ self.assertEqual(self.optimizer.dual_lr, 0.01)
30
+ self.assertEqual(self.optimizer.dual_bound, 100.0)
31
+ self.assertEqual(self.optimizer.rho, 1.0)
32
+ self.assertEqual(self.optimizer.mu, 2.0)
33
+ self.assertEqual(self.optimizer.beta, 0.5)
34
+ self.assertTrue(isinstance(self.optimizer._dual_vars, Tensor))
35
+ self.assertEqual(self.optimizer._dual_vars.shape, (self.m,))
36
+
37
+ def test_dual_step(self):
38
+ # Test dual variable update
39
+ c_val = torch.tensor([0.5, 0.1])
40
+ self.optimizer.dual_step(0, c_val[0])
41
+ self.assertEqual(self.optimizer._dual_vars[0], 0.005) # 0 + 0.01 * 0.5
42
+ self.optimizer.dual_step(1, c_val[1])
43
+ self.assertEqual(self.optimizer._dual_vars[1], 0.001) # 0 + 0.01 * 0.1
44
+
45
+ def test_dual_bound(self):
46
+ # Test dual variable bounding
47
+ self.optimizer._dual_vars = torch.tensor([101.0, -1.0])
48
+ c_val = torch.tensor([1.0, -1.0])
49
+ self.optimizer.dual_step(0, c_val[0])
50
+ self.optimizer.dual_step(1, c_val[1])
51
+ self.assertEqual(self.optimizer._dual_vars[0], 0.0) # Should be zeroed out
52
+ self.assertEqual(self.optimizer._dual_vars[1], 0.0) # Should be zeroed out
53
+
54
+ # ADD TEST DEALING WITH CONSTRAINTS THAT DONT USE SOME OF THE PARAMS
55
+
56
+ def test_step(self):
57
+ # Test primal parameter update
58
+ # Mock gradients and constraint gradients
59
+ p_pre_step = {}
60
+ for p in self.params:
61
+ p.grad = torch.ones_like(p)
62
+ p_pre_step[p] = p.detach().clone()
63
+
64
+ c_val = torch.tensor([0.1, -0.1])
65
+ c_grads = {p: [torch.ones_like(p) for _ in c_val] for p in self.params}
66
+ self.optimizer._dual_vars = torch.ones(2)
67
+
68
+ for p in self.params:
69
+ self.optimizer.state[p]["c_grad"] = [g.clone() for g in c_grads[p]]
70
+ self.optimizer.state[p]["smoothing"] = p.detach().clone()
71
+
72
+ self.optimizer.state[p]["step"] = 0
73
+ self.optimizer.state[p]["exp_avg"] = torch.ones_like(p)
74
+ self.optimizer.state[p]["exp_avg_sq"] = torch.ones_like(p)
75
+ G = self.optimizer.step(c_val)
76
+ # Check if G is computed and parameters are updated
77
+ self.assertEqual(len(G), len(self.params))
78
+ for i, p in enumerate(self.params):
79
+ ## assert correct Lagrange f-n gradient
80
+ self.assertTrue(
81
+ torch.equal(
82
+ G[i],
83
+ (
84
+ p.grad +
85
+ sum(_lambda * c_grads[p][j] for j, _lambda in enumerate(self.optimizer._dual_vars)) +
86
+ sum([c_grads[p][j] * cv for j, cv in enumerate(c_val)])
87
+ )
88
+ )
89
+ )
90
+ # assert correct update of params
91
+ beta1 = 0.9
92
+ beta2 = 0.999
93
+ fm = beta1*torch.ones_like(p) + (1-beta1)*G[i]
94
+ sm = beta2*torch.ones_like(p) + (1-beta2)*(torch.pow(G[i],2))
95
+ fm_bc = fm/(1-beta1)
96
+ sm_bc = sm/(1-beta2)
97
+
98
+ self.assertTrue(
99
+ torch.equal(
100
+ p,
101
+ p_pre_step[p] - 0.01*fm_bc/(sm_bc.sqrt() + 1e-8)
102
+ )
103
+ )
104
+ # assert correct update of exp_avg and exp_avg_sq
105
+ self.assertTrue(
106
+ torch.equal(
107
+ self.optimizer.state[p]['exp_avg'],
108
+ fm
109
+ )
110
+ )
111
+ self.assertTrue(
112
+ torch.equal(
113
+ self.optimizer.state[p]['exp_avg_sq'],
114
+ sm
115
+ )
116
+ )
117
+
118
+ def test_step_with_invalid_c_val(self):
119
+ # Test step with invalid c_val (wrong shape)
120
+ with self.assertRaises(ValueError):
121
+ self.optimizer.step(torch.tensor([0.1]))
122
+
123
+ def test_smoothing_update(self):
124
+ # Test smoothing term update
125
+ p_pre_step = {}
126
+ for p in self.params:
127
+ p.grad = torch.ones_like(p)
128
+ p_pre_step[p] = p.detach().clone()
129
+ c_val = torch.tensor([0.1, -0.1])
130
+ self.optimizer.step(c_val)
131
+ for p in self.params:
132
+ state = self.optimizer.state[p]
133
+ self.assertTrue("smoothing" in state)
134
+ self.assertTrue(torch.all(state["smoothing"] == p_pre_step[p]))
135
+
136
+ def test_error_handling(self):
137
+ # Test error handling for invalid inputs
138
+ with self.assertRaises(ValueError):
139
+ SSLALM_Adam(self.params, m=self.m, lr=-0.01)
140
+ with self.assertRaises(ValueError):
141
+ SSLALM_Adam(self.params, m=self.m, dual_lr=-0.01)
142
+ with self.assertRaises(ValueError):
143
+ SSLALM_Adam(self.params, m=self.m, init_dual_vars=torch.tensor([1.0]))
144
+
145
+ if __name__ == "__main__":
146
+ unittest.main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: humancompatible-train
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: PyTorch-based package for constrained training of neural networks
5
5
  Author: Gilles Bareilles, Jana Lepsova, Jakub Marecek
6
6
  Author-email: Andrii Kliachkin <kliachkin.andrii@gmail.com>
@@ -10,6 +10,13 @@ Description-Content-Type: text/markdown
10
10
  License-File: LICENCE.txt
11
11
  Requires-Dist: torch
12
12
  Requires-Dist: numpy
13
+ Provides-Extra: examples
14
+ Requires-Dist: ipykernel; extra == "examples"
15
+ Requires-Dist: ipympl; extra == "examples"
16
+ Requires-Dist: fairret; extra == "examples"
17
+ Requires-Dist: folktables; extra == "examples"
18
+ Requires-Dist: scikit-learn; extra == "examples"
19
+ Requires-Dist: matplotlib; extra == "examples"
13
20
  Provides-Extra: benchmark
14
21
  Requires-Dist: fairret; extra == "benchmark"
15
22
  Requires-Dist: matplotlib; extra == "benchmark"
@@ -31,22 +38,23 @@ The toolkit implements algorithms for constrained training of neural networks ba
31
38
  <!-- , as well as a tool to compare stochastic-constrained stochastic optimization algorithms on a _fair learning_ task in the `experiments` folder. -->
32
39
 
33
40
  ## Table of Contents
41
+
34
42
  1. [Basic installation instructions](#basic-installation-instructions)
35
43
  2. [Using the toolkit](#using-the-toolkit)
36
- 3. [Extending the toolkit](#extending-the-toolkit)
44
+ 3. [Extending the toolkit](#extending-the-toolkit)
37
45
  4. [Reproducing the Benchmark](#reproducing-the-benchmark)
38
46
  5. [License and terms of use](#license-and-terms-of-use)
39
47
  6. [References](#references)
40
48
 
41
49
  humancompatible-train is still under active development! If you find bugs or have feature
42
50
  requests, please file a
43
- [Github issue](https://github.com/humancompatible/train/issues).
51
+ [Github issue](https://github.com/humancompatible/train/issues).
44
52
 
45
53
  ## Installation
46
54
 
47
55
  Use
48
56
 
49
- ```
57
+ ```bash
50
58
  pip install humancompatible-train
51
59
  ```
52
60
 
@@ -56,11 +64,11 @@ The only dependencies of this package are `numpy` and `torch`.
56
64
 
57
65
  The toolkit implements algorithms for constrained training of neural networks based on PyTorch.
58
66
 
59
- The algorithms follow the `dual_step()` - `step()` framework: taking inspiration from PyTorch, the `double_step` does updates related to the dual parameters and prepares for the primal update (by, e.g., saving constraint gradients), and `step()` updates the primal parameters.
67
+ The algorithms follow the `dual_step()` - `step()` framework: taking inspiration from PyTorch, the `dual_step()` does updates related to the dual parameters and prepares for the primal update (by, e.g., saving constraint gradients), and `step()` updates the primal parameters.
60
68
 
61
69
  In general, your code using `humancompatible-train` would look something like this:
62
70
 
63
- ```
71
+ ```python
64
72
  for inputs, labels in dataloader:
65
73
  # inference
66
74
  outputs = model(inputs)
@@ -79,6 +87,7 @@ for inputs, labels in dataloader:
79
87
  ```
80
88
 
81
89
  Our idea is to
90
+
82
91
  1. Deviate minimally from the usual PyTorch workflow
83
92
  2. Make different stochastic-constrained stochastic optimization algorithms nearly interchangable in the code.
84
93
 
@@ -86,6 +95,12 @@ Our idea is to
86
95
 
87
96
  You are invited to check out our new API presented in notebooks in the `examples` folder.
88
97
 
98
+ The example notebooks have additional dependencies, such as `fairret`. To install those, run
99
+
100
+ ```
101
+ pip install humancompatible-train[examples]
102
+ ```
103
+
89
104
  *The legacy API used for the benchmark is presented in `examples/_old_/algorithm_demo.ipynb` and `examples/_old_/constraint_demo.ipynb`.*
90
105
 
91
106
  ## Extending the toolkit
@@ -99,21 +114,27 @@ You are invited to check out our new API presented in notebooks in the `examples
99
114
  The code used in [our benchmark paper](https://arxiv.org/abs/2507.04033) is not migrated to the new API yet (WIP).
100
115
 
101
116
  ### Basic installation instructions
117
+
102
118
  The code requires Python version ```3.11```.
103
119
 
104
120
  1. Create a virtual environment
105
121
 
106
122
  **bash** (Linux)
123
+
107
124
  ```
108
125
  python3.11 -m venv fairbenchenv
109
126
  source fairbenchenv/bin/activate
110
127
  ```
128
+
111
129
  **cmd** (Windows)
130
+
112
131
  ```
113
132
  python -m venv fairbenchenv
114
133
  fairbenchenv\Scripts\activate.bat
115
134
  ```
135
+
116
136
  2. Install from source.
137
+
117
138
  ```
118
139
  git clone https://github.com/humancompatible/train.git
119
140
  cd train
@@ -122,11 +143,12 @@ pip install .
122
143
  ```
123
144
 
124
145
  If you wish to edit the code of the algorithms, install as an editable package:
146
+
125
147
  ```
126
148
  pip install -e .
127
149
  ```
128
150
 
129
- __Warning__: it is recommended to use Stochastic Ghost with the mkl-accelerated version of the scipy package with Stochastic Ghost; to install it, run
151
+ **Warning**: it is recommended to use Stochastic Ghost with the mkl-accelerated version of the scipy package with Stochastic Ghost; to install it, run
130
152
 
131
153
  ```pip install --force-reinstall -i https://software.repos.intel.com/python/pypi scipy```
132
154
 
@@ -140,12 +162,14 @@ after installing requirements.txt; otherwise, the algorithm will run slower. How
140
162
  ### Running the algorithms
141
163
 
142
164
  The benchmark comprises the following algorithms:
165
+
143
166
  - Stochastic Ghost [[2]](#2),
144
167
  - SSL-ALM [[3]](#3),
145
168
  - Stochastic Switching Subgradient [[4]](#4).
146
169
 
147
170
  To reproduce the experiments of the paper, run the following:
148
- ``` python
171
+
172
+ ```
149
173
  cd experiments
150
174
  python run_folktables.py data=folktables alg=sslalm
151
175
  python run_folktables.py data=folktables alg=alm
@@ -154,23 +178,25 @@ python run_folktables.py data=folktables alg=ssg
154
178
  python run_folktables.py data=folktables alg=sgd # baseline, no fairness
155
179
  python run_folktables.py data=folktables alg=fairret # baseline, fairness with regularizer
156
180
  ```
181
+
157
182
  Each command will start 10 runs of the `alg`, 30 seconds each.
158
183
  The results will be saved to `experiments/utils/saved_models` and `experiments/utils/exp_results`.
159
184
  <!-- In the repository, we include the configuration needed to reproduce the experiments in the paper. To do so, go to `experiments` and run `python run_folktables.py data=folktables alg=sslalm`. -->
160
185
  <!-- Repeat for the other algorithms by changing the `alg` parameter. -->
161
186
 
162
- This repository uses [Hydra](https://hydra.cc/) to manage parameters; see `experiments/conf` for configuration files.
163
- * To change the parameters of the experiment, such as the number of runs for each algorithm, run time, the dataset used (*note: for now supports only Folktables*) - use `experiment.yaml`.
164
- * To change the dataset settings - such as file location - or do dataset-specific adjustments - such as the configuration of the protected attributes - use `data/{dataset_name}.yaml`
165
- * To change algorithm hyperparameters, use `alg/{algorithm_name}.yaml`.
166
- * To change constraint hyperparameters, use `constraint/{constraint_name}.yaml`
187
+ This repository uses [Hydra](https://hydra.cc/) to manage parameters; see `experiments/conf` for configuration files.
188
+
189
+ - To change the parameters of the experiment, such as the number of runs for each algorithm, run time, the dataset used (*note: for now supports only Folktables*) - use `experiment.yaml`.
190
+ - To change the dataset settings - such as file location - or do dataset-specific adjustments - such as the configuration of the protected attributes - use `data/{dataset_name}.yaml`
191
+ - To change algorithm hyperparameters, use `alg/{algorithm_name}.yaml`.
192
+ - To change constraint hyperparameters, use `constraint/{constraint_name}.yaml`
167
193
 
168
194
  <!-- ; it is installed as one of the dependencies. -->
169
195
  <!-- To learn more about using Hydra, please check out the [official tutorial](https://hydra.cc/docs/tutorials/basic/your_first_app). -->
170
196
 
171
197
  ### Producing plots
172
- The plots and tables like the ones in the paper can be produced using the two notebooks. `experiments/algo_plots.ipynb` houses the convergence plots, and `experiments/model_plots.ipynb` - all the others.
173
198
 
199
+ The plots and tables like the ones in the paper can be produced using the two notebooks. `experiments/algo_plots.ipynb` houses the convergence plots, and `experiments/model_plots.ipynb` - all the others.
174
200
 
175
201
  ## License and terms of use
176
202
 
@@ -180,7 +206,7 @@ The benchmark part of the package relies on the Folktables package, provided und
180
206
  It provides code to download data from the American Community Survey
181
207
  (ACS) Public Use Microdata Sample (PUMS) files managed by the US Census Bureau.
182
208
  The data itself is governed by the terms of use provided by the Census Bureau.
183
- For more information, see https://www.census.gov/data/developers/about/terms-of-service.html
209
+ For more information, see <https://www.census.gov/data/developers/about/terms-of-service.html>
184
210
 
185
211
  <!-- ## Cite this work -->
186
212
 
@@ -206,7 +232,7 @@ For more information, see https://www.census.gov/data/developers/about/terms-of-
206
232
 
207
233
  If you use this work, we encourage you to cite [our paper](https://arxiv.org/abs/2507.04033),
208
234
 
209
- ```
235
+ ```bibtex
210
236
  @misc{kliachkin2025benchmarkingstochasticapproximationalgorithms,
211
237
  title={Benchmarking Stochastic Approximation Algorithms for Fairness-Constrained Training of Deep Neural Networks},
212
238
  author={Andrii Kliachkin and Jana Lepšová and Gilles Bareilles and Jakub Mareček},
@@ -218,14 +244,14 @@ If you use this work, we encourage you to cite [our paper](https://arxiv.org/abs
218
244
  }
219
245
  ```
220
246
 
221
- <a id="1">[1]</a>
247
+ <a id="1">[1]</a>
222
248
  Ding, Hardt & Miller et al. (2021) Retiring Adult: New Datasets for Fair Machine Learning, Curran Associates, Inc..
223
249
 
224
- <a id="2">[2]</a>
250
+ <a id="2">[2]</a>
225
251
  Facchinei & Kungurtsev (2023) Stochastic Approximation for Expectation Objective and Expectation Inequality-Constrained Nonconvex Optimization, arXiv.
226
252
 
227
- <a id="3">[3]</a>
253
+ <a id="3">[3]</a>
228
254
  Huang, Zhang & Alacaoglu (2025) Stochastic Smoothed Primal-Dual Algorithms for Nonconvex Optimization with Linear Inequality Constraints, arXiv.
229
255
 
230
- <a id="4">[4]</a>
256
+ <a id="4">[4]</a>
231
257
  Huang & Lin (2023) Oracle Complexity of Single-Loop Switching Subgradient Methods for Non-Smooth Weakly Convex Functional Constrained Optimization, Curran Associates Inc..
@@ -10,9 +10,11 @@ humancompatible/__init__.py
10
10
  humancompatible/train/__init__.py
11
11
  humancompatible/train/algorithms/__init__.py
12
12
  humancompatible/train/algorithms/ssl_alm.py
13
+ humancompatible/train/algorithms/ssl_alm_adam.py
13
14
  humancompatible/train/algorithms/ssw.py
14
15
  humancompatible/train/algorithms/test/__init__.py
15
16
  humancompatible/train/algorithms/test/test_ssl_alm.py
17
+ humancompatible/train/algorithms/test/test_ssl_alm_adam.py
16
18
  humancompatible/train/algorithms/test/test_ssw.py
17
19
  humancompatible/train/fairness/__init__.py
18
20
  humancompatible/train/fairness/constraints/__init__.py
@@ -10,6 +10,14 @@ pot
10
10
  hydra
11
11
  omegaconf
12
12
 
13
+ [examples]
14
+ ipykernel
15
+ ipympl
16
+ fairret
17
+ folktables
18
+ scikit-learn
19
+ matplotlib
20
+
13
21
  [ghost]
14
22
  qpsolvers
15
23
  scipy
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "humancompatible-train"
7
- version = "0.1.2"
7
+ version = "0.1.3"
8
8
  dependencies = [
9
9
  "torch",
10
10
  "numpy",
@@ -24,5 +24,6 @@ readme = "README.md"
24
24
 
25
25
 
26
26
  [project.optional-dependencies]
27
+ examples = ["ipykernel", "ipympl", "fairret", "folktables", "scikit-learn", "matplotlib"]
27
28
  benchmark = ["fairret", "matplotlib", "pandas", "folktables", "pot", "hydra", "omegaconf"]
28
29
  ghost = ["qpsolvers", "scipy"]