anchorboosting 0.0.1__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. anchorboosting-0.0.1/.github/workflows/build.yaml +75 -0
  2. anchorboosting-0.0.1/.github/workflows/ci.yml +56 -0
  3. anchorboosting-0.0.1/.gitignore +4 -0
  4. anchorboosting-0.0.1/.pre-commit-config.yaml +29 -0
  5. anchorboosting-0.0.1/CHANGELOG.rst +7 -0
  6. anchorboosting-0.0.1/LICENSE +13 -0
  7. anchorboosting-0.0.1/PKG-INFO +224 -0
  8. anchorboosting-0.0.1/README.md +209 -0
  9. anchorboosting-0.0.1/anchorboosting/__init__.py +4 -0
  10. anchorboosting-0.0.1/anchorboosting/models.py +368 -0
  11. anchorboosting-0.0.1/anchorboosting/objectives/__init__.py +11 -0
  12. anchorboosting-0.0.1/anchorboosting/objectives/anchor_objectives.py +123 -0
  13. anchorboosting-0.0.1/anchorboosting/objectives/mixins.py +237 -0
  14. anchorboosting-0.0.1/anchorboosting/simulate.py +25 -0
  15. anchorboosting-0.0.1/anchorboosting/utils.py +84 -0
  16. anchorboosting-0.0.1/anchorboosting.egg-info/PKG-INFO +224 -0
  17. anchorboosting-0.0.1/anchorboosting.egg-info/SOURCES.txt +33 -0
  18. anchorboosting-0.0.1/anchorboosting.egg-info/dependency_links.txt +1 -0
  19. anchorboosting-0.0.1/anchorboosting.egg-info/requires.txt +3 -0
  20. anchorboosting-0.0.1/anchorboosting.egg-info/top_level.txt +1 -0
  21. anchorboosting-0.0.1/docs/changelog.rst +1 -0
  22. anchorboosting-0.0.1/docs/index.rst +31 -0
  23. anchorboosting-0.0.1/environment.yml +22 -0
  24. anchorboosting-0.0.1/pyproject.toml +28 -0
  25. anchorboosting-0.0.1/readthedocs.yaml +17 -0
  26. anchorboosting-0.0.1/setup.cfg +4 -0
  27. anchorboosting-0.0.1/tests/test_anchor_boosting.py +180 -0
  28. anchorboosting-0.0.1/tests/test_anchor_objectives.py +102 -0
  29. anchorboosting-0.0.1/tests/test_classification_mixins.py +92 -0
  30. anchorboosting-0.0.1/tests/test_compare_to_lgbm.py +237 -0
  31. anchorboosting-0.0.1/tests/test_derivatives.py +143 -0
  32. anchorboosting-0.0.1/tests/test_refit.py +35 -0
  33. anchorboosting-0.0.1/tests/test_regression_mixin.py +25 -0
  34. anchorboosting-0.0.1/tests/test_update.py +35 -0
  35. anchorboosting-0.0.1/tests/test_utils.py +74 -0
@@ -0,0 +1,75 @@
1
+ on:
2
+ pull_request:
3
+ release:
4
+ types:
5
+ - published
6
+
7
+ name: release
8
+
9
+ jobs:
10
+
11
+ python-build:
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v3
16
+
17
+ - uses: actions/setup-python@v4
18
+ name: Install Python
19
+ with:
20
+ python-version: '3.10'
21
+
22
+ - name: Install build dependencies
23
+ run: python -m pip install build
24
+
25
+ - name: Build source distribution and wheel
26
+ run: python -m build --sdist --wheel
27
+
28
+ - name: Install wheel
29
+ run: python -m pip install dist/*.whl
30
+
31
+ - name: Test import
32
+ run: cd .. && python -c "from anchorboosting import AnchorBooster"
33
+
34
+ - uses: actions/upload-artifact@v4
35
+ with:
36
+ name: wheels
37
+ path: dist/*
38
+
39
+ upload-testpypi:
40
+ needs: [python-build]
41
+ runs-on: ubuntu-latest
42
+ if: github.event_name == 'release' && github.event.action == 'published'
43
+ environment:
44
+ name: test_release
45
+ url: https://test.pypi.org/p/anchorboosting
46
+ permissions:
47
+ id-token: write
48
+
49
+ steps:
50
+ - uses: actions/download-artifact@v4
51
+ with:
52
+ name: wheels
53
+ path: dist
54
+
55
+ - uses: pypa/gh-action-pypi-publish@release/v1
56
+ with:
57
+ repository-url: https://test.pypi.org/legacy/
58
+
59
+ upload-pypi:
60
+ needs: [python-build, upload-testpypi]
61
+ runs-on: ubuntu-latest
62
+ if: github.event_name == 'release' && github.event.action == 'published'
63
+ environment:
64
+ name: release
65
+ url: https://pypi.org/p/anchorboosting
66
+ permissions:
67
+ id-token: write
68
+
69
+ steps:
70
+ - uses: actions/download-artifact@v4
71
+ with:
72
+ name: wheels
73
+ path: dist
74
+
75
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,56 @@
1
+
2
+ on:
3
+ push:
4
+ branches: [ main ]
5
+ pull_request:
6
+
7
+ name: CI
8
+
9
+ jobs:
10
+
11
+ test:
12
+ name: Unit tests
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ fail-fast: false
16
+ defaults:
17
+ run:
18
+ # see https://github.com/conda-incubator/setup-miniconda/#important
19
+ shell: bash -l {0}
20
+ steps:
21
+ - uses: actions/checkout@v3
22
+ - name: Set up micromamba
23
+ uses: mamba-org/setup-micromamba@d05808540d968a55ca33c798e0661fb98f533c73
24
+ with:
25
+ micromamba-version: 1.5.10-0
26
+ environment-file: environment.yml
27
+ - name: Install repository
28
+ run: python -m pip install --no-build-isolation --no-deps --disable-pip-version-check -e .
29
+ - name: Pytest
30
+ run: pytest --cov --cov-report xml tests
31
+ - name: codecov
32
+ uses: codecov/codecov-action@v4.3.1
33
+ with:
34
+ file: ./coverage.xml
35
+ token: ${{ secrets.CODECOV_TOKEN }}
36
+ name: pytest-python${{ matrix.PYTHON_VERSION }}
37
+
38
+
39
+ pre-commit-checks:
40
+ name: "Pre-commit checks - Python 3.12"
41
+ runs-on: ubuntu-latest
42
+
43
+ steps:
44
+
45
+ - name: Checkout branch
46
+ uses: actions/checkout@v3
47
+ with:
48
+ ref: ${{ github.head_ref }}
49
+
50
+ - name: Install Python 3.12
51
+ uses: actions/setup-python@v2
52
+ with:
53
+ python-version: 3.12
54
+
55
+ - name: Run pre-commit checks
56
+ uses: pre-commit/action@v3.0.1
@@ -0,0 +1,4 @@
1
+ __pycache__
2
+ .pytest_cache
3
+ *.csv
4
+ *.egg-info
@@ -0,0 +1,29 @@
1
+ repos:
2
+ - repo: https://github.com/Quantco/pre-commit-mirrors-black
3
+ rev: 22.6.0
4
+ hooks:
5
+ - id: black-conda
6
+ args:
7
+ - --safe
8
+ - --target-version=py38
9
+ - repo: https://github.com/Quantco/pre-commit-mirrors-isort
10
+ rev: '5.10.1'
11
+ hooks:
12
+ - id: isort-conda
13
+ additional_dependencies: [toml]
14
+ - repo: https://github.com/Quantco/pre-commit-mirrors-flake8
15
+ rev: 5.0.4
16
+ hooks:
17
+ - id: flake8-conda
18
+ args:
19
+ - --max-line-length=88
20
+ - --extend-ignore=E203
21
+ - repo: https://github.com/Quantco/pre-commit-mirrors-mypy
22
+ rev: "0.971"
23
+ hooks:
24
+ - id: mypy-conda
25
+ additional_dependencies: [-c, conda-forge, types-setuptools]
26
+ - repo: https://github.com/Quantco/pre-commit-mirrors-pyupgrade
27
+ rev: 2.37.3
28
+ hooks:
29
+ - id: pyupgrade-conda
@@ -0,0 +1,7 @@
1
+ Changelog
2
+ =========
3
+
4
+ 0.1.0 (2025-05-26)
5
+ ------------------
6
+ - Initial release of `anchorboosting` package.
7
+ - Implemented `AnchorBooster` class for anchor regression and classification.
@@ -0,0 +1,13 @@
1
+ BSD 3-clause license
2
+ Copyright (c) 2022 Malte Londschien
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8
+
9
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
10
+
11
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,224 @@
1
+ Metadata-Version: 2.4
2
+ Name: anchorboosting
3
+ Version: 0.0.1
4
+ Summary: Anchor Boosting
5
+ Author-email: Malte Londschien <malte@londschien.ch>
6
+ Project-URL: homepage, https://github.com/mlondschien/anchorboosting/
7
+ Classifier: Programming Language :: Python
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: numpy
12
+ Requires-Dist: scipy
13
+ Requires-Dist: lightgbm
14
+ Dynamic: license-file
15
+
16
+ # Anchor Boosting
17
+
18
+ This repository combines ideas from [1, 2 and 3].
19
+ [1] suggests regularizing linear regression with the correlation between a so-called anchor variable and the regression's residuals.
20
+ The anchor variable is assumed to be exogenous to the system, i.e., not causally affected by covariates, the outcome, or relevant hidden variables.
21
+ [1] show that such regularization induces robustness of the linear regression model to shift interventions with limited magnitude in directions as observed in the data.
22
+
23
+ [2] proposes to generalize this to nonlinear regression by simply optimizing, e.g., via boosting, the regularized anchor regression loss, replacing linear regression with a more flexible model class.
24
+
25
+ Lastly, [3] give ideas to generalize anchor regression to classification.
26
+
27
+ This repository combines these approaches and implements non-linear anchor (multiclass-) classification using LGBM.
28
+
29
+ ## Classification via boosting
30
+
31
+ We first describe how to explicitly solve a classification problem with boosting.
32
+ This includes calculating the gradient and the diagonal of the hessian of the loss with respect to model parameters.
33
+ In each boosting step, the gradient is fit with a weak learner (CART tree).
34
+ The diagonal of the hessian is used as a stopping criterium only (`min_hessian_in_leaf`).
35
+
36
+ We separately describe two-class and multi-class classification, even though the former is a special case of the latter.
37
+ $K$-class classification involves learning $K$ scores (parameters), each fitted with a single weak learner.
38
+ I.e., the boosting algorithm will train $K$ trees in each set.
39
+
40
+ ### Two-class classification
41
+
42
+ Consider a setup with observations $x_i \in \mathbb{R}^p$ with outcomes $y_i \in \{-1, 1\}$ for $i=1, \ldots, n$.
43
+ We assign raw scores $f_i \in \mathbb{R}$ to each observation and use the expit to obtain probability predictions.
44
+ The estimated probability of observation $x_i$ with raw score $f_i$ to belong to class 1 is $p_i = \frac{e^f}{1 + e^f} = (1 + e^{-f})^{-1}$.
45
+ The log-likelihood of raw scores $`f=(f_i)_{i=1}^n`$ is
46
+
47
+ $$\ell(f, y) = -\sum_{i=1}^n \log(1 + e^{-yf}).$$
48
+
49
+ The gradient of the log-likelihood is
50
+
51
+ $$
52
+ \frac{d}{d f_i} \ell(f, y) = y \frac{e^{-yf}}{1 + e^{-yf}} =
53
+ \begin{cases}
54
+ 1 - p & y = 1 \\
55
+ -p & y = 0
56
+ \end{cases}
57
+ $$
58
+
59
+ The (diagonal of the) Hessian of the log-likelihood is
60
+
61
+ $$
62
+ \frac{d^2}{d^2f_i} \ell(f, y) = (1 - p_i) p_i.
63
+ $$
64
+
65
+ ### Multiclass classification
66
+ Consider a setup with observations $x_i \in \mathbb{R}^p$ with outcomes $y_i \in \{1, \ldots, K\}$ for $i=1, \ldots, n$.
67
+ We assign raw scores $f_i = (f_{i, 1}, \ldots, f_{i, K}) \in \mathbb{R}^K$ to each observation and use cross-entropy to obtain probability predictions.
68
+ For $k = 1, \ldots, K$ and $i=1, \ldots, n$ the estimated probability of observation $x_i$ with raw score $f_i$ to belong to class $k$ is $p_{i, k} := \exp(f_{i, k}) / \sum_{j=1}^K \exp(f_{i, j})$. The log-likelihood of raw scores $`(f_i)_{i=1}^n`$ is then
69
+ ```math
70
+ \ell\left((f_{i, k})_{i=1, \ldots, n}^{k=1, \ldots, K}, (y_i)_{i=1}^n\right) = \sum_{i=1}^n \left(f_{i, y} - \log\left(\sum_{j=1}^K \exp(f_{i,j})\right)\right).
71
+ ```
72
+
73
+ The gradient of the log-likelihood is
74
+
75
+ $$
76
+ \frac{d}{df_{i, k}} \ell(f, y) = \begin{cases}
77
+ 1 - p_{i, k} & y_i = k \\
78
+ -p_{i, k} & y_i \neq k \\
79
+ \end{cases}
80
+ $$
81
+
82
+ The (diagonal of the) Hessian of the log-likelihood is
83
+ ```math
84
+ \frac{d^2}{d^2 f_{i, k}} \ell(f, y) = (1 - p_{i, k}) p_{i, k}
85
+ ```
86
+
87
+ ## Anchorized classification
88
+
89
+ [1] suggest adding a regularization term based on an "anchor variable" $A$ to the linear least squares optimization problem to improve distributional robustness.
90
+
91
+ Say additional to features and outcomes we observe anchor values $a_i \in \mathbb{R}^q, i=1,\ldots K$.
92
+ Write $A = (a_i)_{i=1}^n \in \mathbb{R}^{n \times q}$ and $\pi_A$ for the linear projection onto the column space of $A$.
93
+ [1] show that
94
+
95
+ $$
96
+ b^\gamma = \underset{b}{\arg\min} \|(\mathrm{Id} - \pi_A)(Y - Xb)\|_2^2 + \gamma\|\pi_A(Y - Xb)\|_2^2
97
+ $$
98
+
99
+ minimizes the linear model's worst-case risk with respect to certain shift interventions as seen in the data.
100
+
101
+ [2] suggests applying anchor regression to nonlinear regression by boosting the above anchor loss with a flexible (nonlinear) learner.
102
+ [3] presents an idea to generalize anchor regression to different distributions than the Gaussian distribution, i.e., using different losses than the squared error.
103
+ One interesting case is the logistic loss for (two-class) classification.
104
+ This is mostly based on intelligently defining residuals to be used in the equivalent classification anchor loss.
105
+
106
+ [4] proposes the usage of a different type of residuals for classification outside of the use with anchor regression.
107
+ We apply their residuals in the context of anchor classification.
108
+
109
+ ### Anchorized two-class classification according to [3]
110
+
111
+ Note that again $y \in \{-1, 1\}$.
112
+ [3] suggest defining residuals as
113
+
114
+ $$r_i = \frac{d}{d f}(\ell(f, y)) = y \frac{e^{-yf}}{1 + e^{-yf}} =
115
+ \begin{cases}
116
+ 1 - p & y = 1 \\
117
+ -p & y = 0
118
+ \end{cases}
119
+ $$
120
+
121
+ For some tuning parameter $\gamma$, we add the regularization term $(\gamma - 1) \| \pi_A r \|_2^2$ to our optimization problem.
122
+
123
+ $$
124
+ \hat f = \underset{f}{\arg \min} \ \ell(f, Y) + (\gamma - 1) \|\pi_A r\|_2^2
125
+ $$
126
+
127
+ This encourages uncorrelatedness between the residuals and the anchor and, hopefully, better domain generalization. To optimize this, we also calculate the gradient of the regularization term. First, note that
128
+
129
+ $$
130
+ \frac{d}{d f_i} p_i = p_i (1 - p_i)
131
+ $$
132
+
133
+ such that
134
+
135
+ $$
136
+ \frac{d}{d f_i} \| \pi_A r \|_2^2 = 2 \pi_A r \cdot p (1 - p)
137
+ $$
138
+
139
+ ### Anchorized multiclass classification according to [3]
140
+
141
+ We combine the notions of [1, 2, 3].
142
+ Motivated by [3], define residuals
143
+ ```math
144
+ r_{i, k} =
145
+ \frac{d}{df_{i, k}} \ell(f, y) =
146
+ \begin{cases}
147
+ 1 - p_{i, k} & y_i = k \\
148
+ -p_{i, k} & y_i \neq k
149
+ \end{cases}
150
+ ```
151
+ such that for all $i$ we have $\sum_{k} r_{i, k} = 0$.
152
+
153
+ For some tuning parameter $\gamma$, we add the regularization term $(\gamma - 1) \| \pi_A r \|_2^2$ to our optimization problem.
154
+
155
+ $$
156
+ \hat f = \underset{f}{\arg \min} \ \ell(f, Y) + (\gamma - 1) \|\pi_A r\|_2^2
157
+ $$
158
+
159
+ This encourages uncorrelatedness between the residuals and the anchor and, hopefully, better domain generalization. To optimize this, we also calculate the gradient of the regularization term. First, note that
160
+
161
+ $$
162
+ \frac{d}{d f_{i, k}} p_{i, j} =
163
+ \frac{d}{d f_{i, k}} \frac{\exp(f_{i, j})}{\sum_l \exp(f_{i, l})} =
164
+ \begin{cases}
165
+ \frac{\exp(f_{i, k})}{\sum_l \exp(f_{i, l})} - \frac{\exp(f_{i, k})^2}{(\sum_l \exp(f_{i, l}))^2} & j = k \\
166
+ -\frac{\exp(f_{i, k})\exp{(f_{i, j})}}{(\sum_l \exp(f_{i, l}))^2} & j \neq k
167
+ \end{cases} =
168
+ \begin{cases}
169
+ p_{i, j} - p_{i, j}p_{i, k} & j = k \\
170
+ -p_{i, j} p_{i, k} & j \neq k
171
+ \end{cases}.
172
+ $$
173
+
174
+ Then,
175
+
176
+ ```math
177
+ \frac{d}{d f_{i, k}} \| \pi_A r\|_2^2 = 2 \pi_A r \cdot \left(\begin{cases}
178
+ p_{i, j} - p_{i, j}p_{i, k} & l=i, j = k \\
179
+ -p_{i, j} p_{i, k} & l=i, j \neq k \\
180
+ 0 & l \neq i
181
+ \end{cases}\right)_{l=1, \ldots, n}^{j=1, \ldots K}
182
+ =(\pi_A r)_{i, k} \ p_{i, k} - \sum_{l=1}^K (\pi_A r)_{i, l} \ p_{i, l}.
183
+ ```
184
+
185
+ <!---
186
+ The diagonal of the hessian is equal to
187
+
188
+ $$
189
+ \frac{d^2}{d f_{i, k}^2} \| \pi_A r\|^2 = 2 \pi_A r \cdot
190
+ \left(\begin{cases}
191
+ p_{i, j} - 3 p_{i, j}^2 + 2 p_{i, j}^3 & l=i, j = k \\
192
+ 2 p_{i, j} p_{i, k}^2 - p_{i, j} p{i, k} & l=i, j \neq k \\
193
+ 0 & l \neq i
194
+ \end{cases}\right)_{l=1, \ldots, n}^{j=1, \ldots K} +
195
+ 2 \pi_A \left(\begin{cases}
196
+ p_{i, j} - p_{i, j}p_{i, k} & l=i, j = k \\
197
+ - p_{i, j} p_{i, k} & l=i, j \neq k \\
198
+ 0 & l \neq i
199
+ \end{cases}\right)_{l=1, \ldots, n}^{j=1, \ldots K} \cdot \left(\begin{cases}
200
+ p_{i, j} - p_{i, j}p_{i, k} & l=i, j = k \\
201
+ - p_{i, j} p_{i, k} & l=i, j \neq k \\
202
+ 0 & l \neq i
203
+ \end{cases}\right)_{l=1, \ldots, n}^{j=1, \ldots K}
204
+ $$
205
+
206
+ $$
207
+ \frac{d^2}{d f_{i, k} d f_{i, l}} p_{i, j} =
208
+ \begin{cases}
209
+ (1 - 2 p_{i, j}) p_{i, j} (1 - p_{i, j}) & j = k \\
210
+ p_{i, j}^2 p_{i, k} & j \neq k
211
+ \end{cases}
212
+ $$ -->
213
+
214
+ ## Anchorized two-class classification with residuals motivated by [4]
215
+
216
+
217
+
218
+ ## References
219
+
220
+ [1] Rothenhäusler, D., N. Meinshausen, P. Bühlmann, and J. Peters (2021). Anchor regression: Heterogeneous data meet causality. Journal of the Royal Statistical Society Series B (Statistical Methodology) 83(2), 215–246.
221
+
222
+ [2] Bühlmann, P. (2020). Invariance, causality and robustness. Statistical Science 35(3), 404– 426.
223
+
224
+ [3] Kook, L., B. Sick, and P. Bühlmann (2022). Distributional anchor regression. Statistics and Computing 32(3), 1–19.
@@ -0,0 +1,209 @@
1
+ # Anchor Boosting
2
+
3
+ This repository combines ideas from [1, 2 and 3].
4
+ [1] suggests regularizing linear regression with the correlation between a so-called anchor variable and the regression's residuals.
5
+ The anchor variable is assumed to be exogenous to the system, i.e., not causally affected by covariates, the outcome, or relevant hidden variables.
6
+ [1] show that such regularization induces robustness of the linear regression model to shift interventions with limited magnitude in directions as observed in the data.
7
+
8
+ [2] proposes to generalize this to nonlinear regression by simply optimizing, e.g., via boosting, the regularized anchor regression loss, replacing linear regression with a more flexible model class.
9
+
10
+ Lastly, [3] give ideas to generalize anchor regression to classification.
11
+
12
+ This repository combines these approaches and implements non-linear anchor (multiclass-) classification using LGBM.
13
+
14
+ ## Classification via boosting
15
+
16
+ We first describe how to explicitly solve a classification problem with boosting.
17
+ This includes calculating the gradient and the diagonal of the hessian of the loss with respect to model parameters.
18
+ In each boosting step, the gradient is fit with a weak learner (CART tree).
19
+ The diagonal of the hessian is used as a stopping criterium only (`min_hessian_in_leaf`).
20
+
21
+ We separately describe two-class and multi-class classification, even though the former is a special case of the latter.
22
+ $K$-class classification involves learning $K$ scores (parameters), each fitted with a single weak learner.
23
+ I.e., the boosting algorithm will train $K$ trees in each set.
24
+
25
+ ### Two-class classification
26
+
27
+ Consider a setup with observations $x_i \in \mathbb{R}^p$ with outcomes $y_i \in \{-1, 1\}$ for $i=1, \ldots, n$.
28
+ We assign raw scores $f_i \in \mathbb{R}$ to each observation and use the expit to obtain probability predictions.
29
+ The estimated probability of observation $x_i$ with raw score $f_i$ to belong to class 1 is $p_i = \frac{e^f}{1 + e^f} = (1 + e^{-f})^{-1}$.
30
+ The log-likelihood of raw scores $`f=(f_i)_{i=1}^n`$ is
31
+
32
+ $$\ell(f, y) = -\sum_{i=1}^n \log(1 + e^{-yf}).$$
33
+
34
+ The gradient of the log-likelihood is
35
+
36
+ $$
37
+ \frac{d}{d f_i} \ell(f, y) = y \frac{e^{-yf}}{1 + e^{-yf}} =
38
+ \begin{cases}
39
+ 1 - p & y = 1 \\
40
+ -p & y = 0
41
+ \end{cases}
42
+ $$
43
+
44
+ The (diagonal of the) Hessian of the log-likelihood is
45
+
46
+ $$
47
+ \frac{d^2}{d^2f_i} \ell(f, y) = (1 - p_i) p_i.
48
+ $$
49
+
50
+ ### Multiclass classification
51
+ Consider a setup with observations $x_i \in \mathbb{R}^p$ with outcomes $y_i \in \{1, \ldots, K\}$ for $i=1, \ldots, n$.
52
+ We assign raw scores $f_i = (f_{i, 1}, \ldots, f_{i, K}) \in \mathbb{R}^K$ to each observation and use cross-entropy to obtain probability predictions.
53
+ For $k = 1, \ldots, K$ and $i=1, \ldots, n$ the estimated probability of observation $x_i$ with raw score $f_i$ to belong to class $k$ is $p_{i, k} := \exp(f_{i, k}) / \sum_{j=1}^K \exp(f_{i, j})$. The log-likelihood of raw scores $`(f_i)_{i=1}^n`$ is then
54
+ ```math
55
+ \ell\left((f_{i, k})_{i=1, \ldots, n}^{k=1, \ldots, K}, (y_i)_{i=1}^n\right) = \sum_{i=1}^n \left(f_{i, y} - \log\left(\sum_{j=1}^K \exp(f_{i,j})\right)\right).
56
+ ```
57
+
58
+ The gradient of the log-likelihood is
59
+
60
+ $$
61
+ \frac{d}{df_{i, k}} \ell(f, y) = \begin{cases}
62
+ 1 - p_{i, k} & y_i = k \\
63
+ -p_{i, k} & y_i \neq k \\
64
+ \end{cases}
65
+ $$
66
+
67
+ The (diagonal of the) Hessian of the log-likelihood is
68
+ ```math
69
+ \frac{d^2}{d^2 f_{i, k}} \ell(f, y) = (1 - p_{i, k}) p_{i, k}
70
+ ```
71
+
72
+ ## Anchorized classification
73
+
74
+ [1] suggest adding a regularization term based on an "anchor variable" $A$ to the linear least squares optimization problem to improve distributional robustness.
75
+
76
+ Say additional to features and outcomes we observe anchor values $a_i \in \mathbb{R}^q, i=1,\ldots K$.
77
+ Write $A = (a_i)_{i=1}^n \in \mathbb{R}^{n \times q}$ and $\pi_A$ for the linear projection onto the column space of $A$.
78
+ [1] show that
79
+
80
+ $$
81
+ b^\gamma = \underset{b}{\arg\min} \|(\mathrm{Id} - \pi_A)(Y - Xb)\|_2^2 + \gamma\|\pi_A(Y - Xb)\|_2^2
82
+ $$
83
+
84
+ minimizes the linear model's worst-case risk with respect to certain shift interventions as seen in the data.
85
+
86
+ [2] suggests applying anchor regression to nonlinear regression by boosting the above anchor loss with a flexible (nonlinear) learner.
87
+ [3] presents an idea to generalize anchor regression to different distributions than the Gaussian distribution, i.e., using different losses than the squared error.
88
+ One interesting case is the logistic loss for (two-class) classification.
89
+ This is mostly based on intelligently defining residuals to be used in the equivalent classification anchor loss.
90
+
91
+ [4] proposes the usage of a different type of residuals for classification outside of the use with anchor regression.
92
+ We apply their residuals in the context of anchor classification.
93
+
94
+ ### Anchorized two-class classification according to [3]
95
+
96
+ Note that again $y \in \{-1, 1\}$.
97
+ [3] suggest defining residuals as
98
+
99
+ $$r_i = \frac{d}{d f}(\ell(f, y)) = y \frac{e^{-yf}}{1 + e^{-yf}} =
100
+ \begin{cases}
101
+ 1 - p & y = 1 \\
102
+ -p & y = 0
103
+ \end{cases}
104
+ $$
105
+
106
+ For some tuning parameter $\gamma$, we add the regularization term $(\gamma - 1) \| \pi_A r \|_2^2$ to our optimization problem.
107
+
108
+ $$
109
+ \hat f = \underset{f}{\arg \min} \ \ell(f, Y) + (\gamma - 1) \|\pi_A r\|_2^2
110
+ $$
111
+
112
+ This encourages uncorrelatedness between the residuals and the anchor and, hopefully, better domain generalization. To optimize this, we also calculate the gradient of the regularization term. First, note that
113
+
114
+ $$
115
+ \frac{d}{d f_i} p_i = p_i (1 - p_i)
116
+ $$
117
+
118
+ such that
119
+
120
+ $$
121
+ \frac{d}{d f_i} \| \pi_A r \|_2^2 = 2 \pi_A r \cdot p (1 - p)
122
+ $$
123
+
124
+ ### Anchorized multiclass classification according to [3]
125
+
126
+ We combine the notions of [1, 2, 3].
127
+ Motivated by [3], define residuals
128
+ ```math
129
+ r_{i, k} =
130
+ \frac{d}{df_{i, k}} \ell(f, y) =
131
+ \begin{cases}
132
+ 1 - p_{i, k} & y_i = k \\
133
+ -p_{i, k} & y_i \neq k
134
+ \end{cases}
135
+ ```
136
+ such that for all $i$ we have $\sum_{k} r_{i, k} = 0$.
137
+
138
+ For some tuning parameter $\gamma$, we add the regularization term $(\gamma - 1) \| \pi_A r \|_2^2$ to our optimization problem.
139
+
140
+ $$
141
+ \hat f = \underset{f}{\arg \min} \ \ell(f, Y) + (\gamma - 1) \|\pi_A r\|_2^2
142
+ $$
143
+
144
+ This encourages uncorrelatedness between the residuals and the anchor and, hopefully, better domain generalization. To optimize this, we also calculate the gradient of the regularization term. First, note that
145
+
146
+ $$
147
+ \frac{d}{d f_{i, k}} p_{i, j} =
148
+ \frac{d}{d f_{i, k}} \frac{\exp(f_{i, j})}{\sum_l \exp(f_{i, l})} =
149
+ \begin{cases}
150
+ \frac{\exp(f_{i, k})}{\sum_l \exp(f_{i, l})} - \frac{\exp(f_{i, k})^2}{(\sum_l \exp(f_{i, l}))^2} & j = k \\
151
+ -\frac{\exp(f_{i, k})\exp{(f_{i, j})}}{(\sum_l \exp(f_{i, l}))^2} & j \neq k
152
+ \end{cases} =
153
+ \begin{cases}
154
+ p_{i, j} - p_{i, j}p_{i, k} & j = k \\
155
+ -p_{i, j} p_{i, k} & j \neq k
156
+ \end{cases}.
157
+ $$
158
+
159
+ Then,
160
+
161
+ ```math
162
+ \frac{d}{d f_{i, k}} \| \pi_A r\|_2^2 = 2 \pi_A r \cdot \left(\begin{cases}
163
+ p_{i, j} - p_{i, j}p_{i, k} & l=i, j = k \\
164
+ -p_{i, j} p_{i, k} & l=i, j \neq k \\
165
+ 0 & l \neq i
166
+ \end{cases}\right)_{l=1, \ldots, n}^{j=1, \ldots K}
167
+ =(\pi_A r)_{i, k} \ p_{i, k} - \sum_{l=1}^K (\pi_A r)_{i, l} \ p_{i, l}.
168
+ ```
169
+
170
+ <!---
171
+ The diagonal of the hessian is equal to
172
+
173
+ $$
174
+ \frac{d^2}{d f_{i, k}^2} \| \pi_A r\|^2 = 2 \pi_A r \cdot
175
+ \left(\begin{cases}
176
+ p_{i, j} - 3 p_{i, j}^2 + 2 p_{i, j}^3 & l=i, j = k \\
177
+ 2 p_{i, j} p_{i, k}^2 - p_{i, j} p{i, k} & l=i, j \neq k \\
178
+ 0 & l \neq i
179
+ \end{cases}\right)_{l=1, \ldots, n}^{j=1, \ldots K} +
180
+ 2 \pi_A \left(\begin{cases}
181
+ p_{i, j} - p_{i, j}p_{i, k} & l=i, j = k \\
182
+ - p_{i, j} p_{i, k} & l=i, j \neq k \\
183
+ 0 & l \neq i
184
+ \end{cases}\right)_{l=1, \ldots, n}^{j=1, \ldots K} \cdot \left(\begin{cases}
185
+ p_{i, j} - p_{i, j}p_{i, k} & l=i, j = k \\
186
+ - p_{i, j} p_{i, k} & l=i, j \neq k \\
187
+ 0 & l \neq i
188
+ \end{cases}\right)_{l=1, \ldots, n}^{j=1, \ldots K}
189
+ $$
190
+
191
+ $$
192
+ \frac{d^2}{d f_{i, k} d f_{i, l}} p_{i, j} =
193
+ \begin{cases}
194
+ (1 - 2 p_{i, j}) p_{i, j} (1 - p_{i, j}) & j = k \\
195
+ p_{i, j}^2 p_{i, k} & j \neq k
196
+ \end{cases}
197
+ $$ -->
198
+
199
+ ## Anchorized two-class classification with residuals motivated by [4]
200
+
201
+
202
+
203
+ ## References
204
+
205
+ [1] Rothenhäusler, D., N. Meinshausen, P. Bühlmann, and J. Peters (2021). Anchor regression: Heterogeneous data meet causality. Journal of the Royal Statistical Society Series B (Statistical Methodology) 83(2), 215–246.
206
+
207
+ [2] Bühlmann, P. (2020). Invariance, causality and robustness. Statistical Science 35(3), 404– 426.
208
+
209
+ [3] Kook, L., B. Sick, and P. Bühlmann (2022). Distributional anchor regression. Statistics and Computing 32(3), 1–19.
@@ -0,0 +1,4 @@
1
+ from .models import AnchorBooster
2
+ from .objectives.anchor_objectives import AnchorRegressionObjective
3
+
4
+ __all__ = ["AnchorRegressionObjective", "AnchorBooster"]