fintfm 0.5.5__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.
- fintfm-0.5.5/LICENSE +202 -0
- fintfm-0.5.5/PKG-INFO +407 -0
- fintfm-0.5.5/README.md +367 -0
- fintfm-0.5.5/pyproject.toml +81 -0
- fintfm-0.5.5/pyproject.toml.orig +96 -0
- fintfm-0.5.5/src/fintfm/__init__.py +33 -0
- fintfm-0.5.5/src/fintfm/config.py +329 -0
- fintfm-0.5.5/src/fintfm/configs/default.yaml +87 -0
- fintfm-0.5.5/src/fintfm/evaluation/__init__.py +24 -0
- fintfm-0.5.5/src/fintfm/evaluation/bench.py +259 -0
- fintfm-0.5.5/src/fintfm/evaluation/boosting.py +109 -0
- fintfm-0.5.5/src/fintfm/evaluation/datasets.py +376 -0
- fintfm-0.5.5/src/fintfm/evaluation/fetch.py +115 -0
- fintfm-0.5.5/src/fintfm/evaluation/metrics.py +311 -0
- fintfm-0.5.5/src/fintfm/experiments/__init__.py +5 -0
- fintfm-0.5.5/src/fintfm/experiments/capability.py +1074 -0
- fintfm-0.5.5/src/fintfm/experiments/context_sweep.py +245 -0
- fintfm-0.5.5/src/fintfm/experiments/openml_breadth.py +227 -0
- fintfm-0.5.5/src/fintfm/experiments/prior_ablation.py +621 -0
- fintfm-0.5.5/src/fintfm/experiments/prior_score.py +302 -0
- fintfm-0.5.5/src/fintfm/experiments/retrieval_grouping.py +279 -0
- fintfm-0.5.5/src/fintfm/experiments/term_structure.py +279 -0
- fintfm-0.5.5/src/fintfm/experiments/v4_out_of_time.py +362 -0
- fintfm-0.5.5/src/fintfm/experiments/v4_protocol.py +600 -0
- fintfm-0.5.5/src/fintfm/inference/__init__.py +6 -0
- fintfm-0.5.5/src/fintfm/inference/binning.py +248 -0
- fintfm-0.5.5/src/fintfm/inference/categorical.py +259 -0
- fintfm-0.5.5/src/fintfm/inference/classifier.py +661 -0
- fintfm-0.5.5/src/fintfm/inference/preprocess.py +114 -0
- fintfm-0.5.5/src/fintfm/inference/regressor.py +190 -0
- fintfm-0.5.5/src/fintfm/inference/retrieval.py +247 -0
- fintfm-0.5.5/src/fintfm/modeling/__init__.py +6 -0
- fintfm-0.5.5/src/fintfm/modeling/hazard.py +222 -0
- fintfm-0.5.5/src/fintfm/modeling/model.py +671 -0
- fintfm-0.5.5/src/fintfm/modeling/train.py +536 -0
- fintfm-0.5.5/src/fintfm/prior/__init__.py +16 -0
- fintfm-0.5.5/src/fintfm/prior/base.py +139 -0
- fintfm-0.5.5/src/fintfm/prior/crossed.py +180 -0
- fintfm-0.5.5/src/fintfm/prior/financial.py +634 -0
- fintfm-0.5.5/src/fintfm/prior/mixture.py +242 -0
- fintfm-0.5.5/src/fintfm/prior/scm.py +353 -0
- fintfm-0.5.5/src/fintfm/prior/tree.py +169 -0
- fintfm-0.5.5/src/fintfm/prior/trivial.py +79 -0
- fintfm-0.5.5/src/fintfm/py.typed +0 -0
fintfm-0.5.5/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
fintfm-0.5.5/PKG-INFO
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fintfm
|
|
3
|
+
Version: 0.5.5
|
|
4
|
+
Summary: A tabular foundation model for financial risk — pretrained on synthetic company financials, so it predicts credit default with no training on your data
|
|
5
|
+
Author: Mukharbek Organokov
|
|
6
|
+
Author-email: Mukharbek Organokov <mukharbek.organokov@gmail.com>
|
|
7
|
+
License-Expression: Apache-2.0
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
10
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Dist: numpy>=2.0
|
|
17
|
+
Requires-Dist: pandas>=2.2
|
|
18
|
+
Requires-Dist: scikit-learn>=1.5
|
|
19
|
+
Requires-Dist: pyyaml>=6.0
|
|
20
|
+
Requires-Dist: scipy>=1.18.1
|
|
21
|
+
Requires-Dist: torch>=2.4
|
|
22
|
+
Requires-Dist: catboost>=1.2 ; extra == 'bench'
|
|
23
|
+
Requires-Dist: lightgbm>=4.0 ; extra == 'bench'
|
|
24
|
+
Requires-Dist: xgboost>=2.0 ; extra == 'bench'
|
|
25
|
+
Requires-Dist: pyarrow>=17 ; extra == 'bench'
|
|
26
|
+
Requires-Dist: huggingface-hub>=0.34 ; extra == 'hf'
|
|
27
|
+
Requires-Dist: kaggle>=1.6 ; extra == 'kaggle'
|
|
28
|
+
Requires-Dist: kagglehub>=0.3 ; extra == 'kaggle'
|
|
29
|
+
Requires-Dist: pyarrow>=17 ; extra == 'kaggle'
|
|
30
|
+
Requires-Python: >=3.12
|
|
31
|
+
Project-URL: Homepage, https://github.com/kabartay/fintfm
|
|
32
|
+
Project-URL: Repository, https://github.com/kabartay/fintfm
|
|
33
|
+
Project-URL: Changelog, https://github.com/kabartay/fintfm/blob/main/docs/CHANGELOG.md
|
|
34
|
+
Project-URL: Checkpoint, https://huggingface.co/kabartay/fintfm-binary
|
|
35
|
+
Project-URL: Measurement log, https://github.com/kabartay/fintfm/blob/main/docs/results/FINDINGS.md
|
|
36
|
+
Provides-Extra: bench
|
|
37
|
+
Provides-Extra: hf
|
|
38
|
+
Provides-Extra: kaggle
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# FinTFM
|
|
42
|
+
|
|
43
|
+
[](https://github.com/kabartay/fintfm/actions/workflows/ci.yml)
|
|
44
|
+
[](LICENSE)
|
|
45
|
+
[](pyproject.toml)
|
|
46
|
+
[](https://huggingface.co/kabartay/fintfm-binary)
|
|
47
|
+
[](docs/results/TABARENA.md)
|
|
48
|
+
|
|
49
|
+
**A tabular foundation model for corporate credit risk, built from scratch and measured in
|
|
50
|
+
public.**
|
|
51
|
+
|
|
52
|
+
Given a table of labelled rows, FinTFM predicts new rows in a **single forward pass**, with your
|
|
53
|
+
data supplied as context rather than trained on. There are no gradient steps at fit time and no
|
|
54
|
+
per-dataset tuning — the model is pretrained once, on **synthetic data only**, and never sees a
|
|
55
|
+
real table during training.
|
|
56
|
+
|
|
57
|
+
That last property is the point. A model that provably never saw your benchmark cannot have
|
|
58
|
+
memorised it, and in a regulated domain the ability to *demonstrate* that is worth more than a
|
|
59
|
+
few points of accuracy.
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from huggingface_hub import hf_hub_download
|
|
63
|
+
from fintfm.inference import FinancialTFMClassifier
|
|
64
|
+
|
|
65
|
+
ckpt = hf_hub_download("kabartay/fintfm-binary", "v4-cellattn-labels.pt")
|
|
66
|
+
clf = FinancialTFMClassifier(ckpt, device="cpu")
|
|
67
|
+
clf.fit(X_train, y_train) # stores the table as context; no training happens
|
|
68
|
+
pd_estimates = clf.predict_proba(X_test)[:, 1]
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The checkpoint is on Hugging Face at
|
|
72
|
+
[**kabartay/fintfm-binary**](https://huggingface.co/kabartay/fintfm-binary) — 885K parameters,
|
|
73
|
+
binary, up to 136 features, **Apache-2.0**. It is the checkpoint every published binary number
|
|
74
|
+
below was measured on, so those results are reproducible against this file rather than a
|
|
75
|
+
variant of it.
|
|
76
|
+
|
|
77
|
+
## Status, stated plainly
|
|
78
|
+
|
|
79
|
+
**FinTFM is a research codebase with a public claims ledger, not a product.** It exists to find
|
|
80
|
+
out whether in-context tabular learning can be made competitive with gradient-boosted trees on
|
|
81
|
+
financial tables, and to say honestly where it currently is not.
|
|
82
|
+
|
|
83
|
+
| | |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| **External benchmark** | [TabArena](https://tabarena.ai), 27 binary datasets against 94 other methods: **rank 93 of 95** |
|
|
86
|
+
| **On real credit panels** | calibration consistently among the best measured; discrimination consistently loses to tuned gradient boosting — both, on every panel tried |
|
|
87
|
+
| **Licence** | Apache-2.0, **code and weights** — chosen separately, not inherited (see [Licensing](#licensing--provenance)) |
|
|
88
|
+
| **Checkpoint** | [kabartay/fintfm-binary](https://huggingface.co/kabartay/fintfm-binary) — 885K, binary, ≤136 features |
|
|
89
|
+
| **Tests** | 246, plus `ruff`, the openspec validator, a dependency-licence check and a documentation-link check — all in CI |
|
|
90
|
+
| **Measurement log** | 121 numbered findings, each declaring how its numbers were produced |
|
|
91
|
+
| **Problem types** | binary declared; multiclass and regression implemented but **not** declared (§121) |
|
|
92
|
+
|
|
93
|
+
**Read [`docs/paper/CLAIMS.md`](docs/paper/CLAIMS.md) before anything else.** Every claim this
|
|
94
|
+
project could make is tagged SURVIVES / SINGLE DRAW / SUPERSEDED / RETRACTED / OPEN against its
|
|
95
|
+
evidence. That file, not this one, is the source of truth — and it records what stopped being
|
|
96
|
+
true as prominently as what holds.
|
|
97
|
+
|
|
98
|
+
> Throughout this repository, **§N** refers to entry N in
|
|
99
|
+
> [`docs/results/FINDINGS.md`](docs/results/FINDINGS.md), a measurement log in which every number states how it
|
|
100
|
+
> was produced (MEASURED, SMOKE-TEST, SIMULATED or ESTIMATED) and negative results are kept
|
|
101
|
+
> deliberately, because they are what stops the same wrong conclusion being reached twice.
|
|
102
|
+
|
|
103
|
+
## Results, as measured
|
|
104
|
+
|
|
105
|
+
TabArena, 27 binary datasets, one fold each, against 94 other methods:
|
|
106
|
+
|
|
107
|
+
| change | mean ROC-AUC | Elo | rank |
|
|
108
|
+
| --- | --- | --- | --- |
|
|
109
|
+
| label encoding (§98) | 0.7642 | 662 | 93 / 95 |
|
|
110
|
+
| **+ out-of-fold target statistics** (§101) | **0.7823** | 765 | 93 / 95 |
|
|
111
|
+
| + multiclass-capable prior (§105) | 0.7817 | **813** | 93 / 95 |
|
|
112
|
+
| + 5.0M parameters, matched task count (§114) | 0.7774 | 751 | 93 / 95 |
|
|
113
|
+
| + tree-structured prior (§115), two seeds | 0.7926 / 0.7913 | 842 / 826 | 93 / 95 |
|
|
114
|
+
|
|
115
|
+
**One change has moved the number on the benchmark that matters, and it was preprocessing
|
|
116
|
+
rather than architecture.** The tree prior adds a further +0.0094 here and replicates across two
|
|
117
|
+
seeds — but costs **−0.0221 average precision on credit data** (§116), negative on 5 of 5 folds
|
|
118
|
+
with three surviving multiple-comparison correction at p < 0.001, so it ships off by default.
|
|
119
|
+
Everything else tried — parameter scale at matched task volume, training volume, a widened
|
|
120
|
+
structural-causal prior — has been null or negative. **The rank has never moved.**
|
|
121
|
+
|
|
122
|
+
## What this project does that others do not
|
|
123
|
+
|
|
124
|
+
Three things, stated at their true weight:
|
|
125
|
+
|
|
126
|
+
**Incoherent PD term structures are impossible by construction.** The field's standard
|
|
127
|
+
per-horizon construction produces non-monotone cumulative default curves for **39% of firms** on
|
|
128
|
+
real data — a firm whose 3-year default probability is below its 2-year. A hazard head makes
|
|
129
|
+
that unrepresentable, at zero measured accuracy cost.
|
|
130
|
+
|
|
131
|
+
**A severe capacity defect was found by measurement, not review.** The original architecture had
|
|
132
|
+
capped discrimination regardless of true task difficulty, caught against an exactly-known
|
|
133
|
+
Bayes-optimal AUC. Its cause was isolated to the architecture rather than the prior after
|
|
134
|
+
eliminating seven other candidates one at a time, then closed by a two-way cell-attention change
|
|
135
|
+
worth +0.042 average precision on real data.
|
|
136
|
+
|
|
137
|
+
**The record corrects itself in public.** This README has been wrong about its own results at
|
|
138
|
+
least twice (§107, §112). Both are retracted in place, with the reasoning kept. No peer project
|
|
139
|
+
publishes a document that tags its own claims RETRACTED as prominently as its wins, and in a
|
|
140
|
+
domain where a model must arrive with its own validation evidence, that is the differentiator.
|
|
141
|
+
|
|
142
|
+
## How it works
|
|
143
|
+
|
|
144
|
+
1. **Priors** (`src/fintfm/prior/`) — synthetic task generators, sampled and mixed per batch
|
|
145
|
+
(`mixture.py`).
|
|
146
|
+
- `financial.py` — a structural generative story for company balance sheets, P&L and
|
|
147
|
+
default labels: accounting identities, sector and macro effects, a wide derived-ratio
|
|
148
|
+
family, missingness, a sharpness (signal-to-noise) knob, and a default-rate envelope that
|
|
149
|
+
can reach real low-default-portfolio rates (~0.2%).
|
|
150
|
+
- `scm.py` — a generic random-graph structural-causal-model prior (the TabPFN/TabICL-style
|
|
151
|
+
idea): random layered functions with several nonlinearities, for general nonlinear and
|
|
152
|
+
multiclass structure the financial prior does not cover.
|
|
153
|
+
- `tree.py` — ensembles of oblivious decision trees, generating the **axis-aligned,
|
|
154
|
+
piecewise-constant** structure real tabular data is full of and the other priors do not.
|
|
155
|
+
Added on *distinctiveness* grounds after `fintfm-priorscore` measured it as the only
|
|
156
|
+
member of the mixture with a positive tree-versus-linear gap (§112). Off by default
|
|
157
|
+
(`p_tree=0.0`).
|
|
158
|
+
- `trivial.py`, `crossed.py` — diagnostic-only priors used to isolate specific hypotheses
|
|
159
|
+
(whether the architecture can learn at all; whether a prior's features or its label
|
|
160
|
+
mechanism carries a measured effect). Not part of the default training mixture.
|
|
161
|
+
2. **Model** (`src/fintfm/modeling/model.py`) — a from-scratch Transformer. Cells are embedded
|
|
162
|
+
individually and carry a random per-task column identity (so the model can tell columns
|
|
163
|
+
apart without a positional embedding, which would break invariance to column order).
|
|
164
|
+
Columns attend to each other within a row; optionally — `ModelConfig.n_cell_blocks`,
|
|
165
|
+
currently experimental — cells also attend across rows *within one feature* before that,
|
|
166
|
+
giving each column a distribution-derived identity rather than only a random tag. Rows are
|
|
167
|
+
then pooled and attend to context rows to perform in-context learning. An optional hazard
|
|
168
|
+
head (`modeling/hazard.py`) produces a cumulative PD term structure that is monotone by
|
|
169
|
+
construction. Independent implementation of ideas described in the public TabPFN / TabICL
|
|
170
|
+
literature — no code or weights from any existing project (see Licensing below).
|
|
171
|
+
3. **Training** (`src/fintfm/modeling/train.py`, `fintfm-train`) — infinite synthetic data, one
|
|
172
|
+
gradient step per fresh batch, cosine LR schedule, checkpointing, a held-out quality metric
|
|
173
|
+
that reports per-task AUC (not the pooled-across-tasks number, which is inflated whenever
|
|
174
|
+
task base rates differ) and Brier skill against a base-rate-only predictor.
|
|
175
|
+
4. **Inference** (`src/fintfm/inference/`) — an sklearn-compatible `FinancialTFMClassifier`:
|
|
176
|
+
`fit()` stores the table as context, `predict_proba()` runs the frozen network. Several
|
|
177
|
+
context-construction strategies (uniform, balanced, hybrid, retrieval, prototype) and a
|
|
178
|
+
base-rate correction, plus ensembling over column-identity draws to recover the
|
|
179
|
+
distributional column-order invariance a random identity trades for expressiveness.
|
|
180
|
+
`inference/categorical.py` encodes categorical columns as **out-of-fold** smoothed target
|
|
181
|
+
statistics, because the model reads every cell as an ordered scalar and label encoding's
|
|
182
|
+
arbitrary order is measurably worse than no order at all (§100). The out-of-fold step is
|
|
183
|
+
not a refinement: the naive form puts a row's own label into its own encoding, which makes
|
|
184
|
+
the context self-predictive and the feature absent at query time — it harms the model
|
|
185
|
+
rather than flattering the score, so it survives careless validation.
|
|
186
|
+
`inference/regressor.py` adds a `FinancialTFMRegressor` on the same frozen network and no
|
|
187
|
+
new architecture: a continuous target cut into K quantile bins is an integer index over K
|
|
188
|
+
outcomes, so the existing classification head regresses as-is, and the output is natively
|
|
189
|
+
**distributional** — quantiles and prediction intervals come free, and the predicted
|
|
190
|
+
density can be bimodal, which is what loss given default actually is and what a Gaussian
|
|
191
|
+
head cannot represent.
|
|
192
|
+
5. **Evaluation** (`src/fintfm/evaluation/`, `src/fintfm/experiments/`) — real corporate-default
|
|
193
|
+
panels (V4FinBench via its published protocol, `fintfm-v4protocol`; Polish and Taiwan
|
|
194
|
+
bankruptcy, `fintfm-bench`), an out-of-time harness, a synthetic capability suite
|
|
195
|
+
(`fintfm-capability`, including a class-count sweep for multiclass, §99) with probes of a
|
|
196
|
+
*known* ceiling — some with a closed-form
|
|
197
|
+
Bayes-optimal AUC — specifically built to catch a model that looks fine on average while
|
|
198
|
+
capped in a way an aggregate score cannot see. Average precision is reported alongside
|
|
199
|
+
ROC-AUC everywhere, and read first at low base rates (ROC-AUC's chance floor is 0.5
|
|
200
|
+
regardless of prevalence; AP's floor is the prevalence itself, so it stays legible at the
|
|
201
|
+
base rates this project's target segment actually has).
|
|
202
|
+
`fintfm-priorscore` scores a *prior* rather than a model, on the three criteria the
|
|
203
|
+
literature converged on — performance, diversity, distinctiveness — using **fitted**
|
|
204
|
+
baselines only, so it cannot confuse "the prior lacks this structure" with "our model cannot
|
|
205
|
+
learn it". Its first version could, and §112 is the record of what that cost.
|
|
206
|
+
|
|
207
|
+
## Quickstart
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
uv sync --extra bench --extra hf # naming one extra uninstalls the others
|
|
211
|
+
uv run pytest # 246 tests; 1 skip is expected, more means look
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Using a checkpoint
|
|
215
|
+
|
|
216
|
+
The estimators are scikit-learn compatible. `fit()` **stores** the table as context — it takes
|
|
217
|
+
no gradient steps — and `predict_proba()` runs the frozen network once.
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
from fintfm.inference import FinancialTFMClassifier, FinancialTFMRegressor
|
|
221
|
+
|
|
222
|
+
clf = FinancialTFMClassifier(ckpt, device="mps") # ckpt from hf_hub_download, above
|
|
223
|
+
clf.fit(X_train, y_train) # stores context; no training happens
|
|
224
|
+
proba = clf.predict_proba(X_test)[:, 1]
|
|
225
|
+
|
|
226
|
+
reg = FinancialTFMRegressor("runs/v4-regression.pt", n_bins=10)
|
|
227
|
+
reg.fit(X_train, y_cont)
|
|
228
|
+
point = reg.predict(X_test) # distribution mean
|
|
229
|
+
lo, hi = reg.predict_interval(X_test, 0.8) # 80% interval, free from the same head
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Categorical columns need encoding before they reach the model — it reads every cell as an
|
|
233
|
+
ordered scalar, and label encoding is measurably worse than no order at all (§100). Use
|
|
234
|
+
`fintfm.inference.categorical.CategoricalTargetEncoder`, which is out-of-fold on the context
|
|
235
|
+
rows for reasons that are **not** optional; see [How it works](#how-it-works).
|
|
236
|
+
|
|
237
|
+
### Training a checkpoint
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
uv run fintfm-train --steps 300 --d-model 32 --d-cell 16 --n-layers 2 --max-features 16 \
|
|
241
|
+
--max-classes 2 --device cpu --out runs/v0-smoke.pt # pipeline check, a couple of minutes
|
|
242
|
+
uv run fintfm-bench --model runs/v0-smoke.pt --credit # real corporate-default panels
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
That smoke config exists to check the pipeline runs, not to produce a usable checkpoint — see
|
|
246
|
+
[`docs/infra/COMPUTE.md`](docs/infra/COMPUTE.md) for measured step costs at real scale. A real pretraining
|
|
247
|
+
run (thousands of steps, `--d-model 128`+, `--max-features` matching your data) takes
|
|
248
|
+
hours-to-a-day and needs a GPU: `--device mps` on Apple Silicon, `--device cuda`, or see
|
|
249
|
+
[`docs/infra/HF_JOBS.md`](docs/infra/HF_JOBS.md) for the Hugging Face Jobs recipe this project actually
|
|
250
|
+
uses for training runs. **Check `uptime` before running anything heavy locally** — see
|
|
251
|
+
[`CLAUDE.md`](CLAUDE.md) for why.
|
|
252
|
+
|
|
253
|
+
Both `fintfm-bench --credit` (Polish/Taiwan need 64/95 features) and `fintfm-v4protocol`
|
|
254
|
+
(V4FinBench needs 136) will print `SKIPPED for fintfm: model takes 16 features, data has N` —
|
|
255
|
+
correct, expected behaviour for the smoke checkpoint above, not a bug. Baselines still run and
|
|
256
|
+
score normally; only the FinTFM arm needs a checkpoint pretrained with a matching
|
|
257
|
+
`--max-features` to be evaluated.
|
|
258
|
+
|
|
259
|
+
## Configuration
|
|
260
|
+
|
|
261
|
+
The numbers experiments use — split years, context sizes, strategies, seeds, scoring
|
|
262
|
+
thresholds, the prior's default-rate and sharpness envelope — live in
|
|
263
|
+
[`src/fintfm/configs/default.yaml`](src/fintfm/configs/default.yaml), not scattered through
|
|
264
|
+
the code. Every entry point takes `--config` with a file that is **deep-merged** over that
|
|
265
|
+
default, so an override carries only what it changes:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
uv run fintfm-ctxsweep --model runs/m.pt --config configs/context-sweep-3seed.yaml
|
|
269
|
+
uv run fintfm-v4oot --model runs/m.pt --config configs/retrieval-best.yaml
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Resolution order is packaged default → `--config` (or `$FINTFM_CONFIG`) → explicit CLI flag.
|
|
273
|
+
Runs print the layers they used and record them as `config_sources` in their output JSON.
|
|
274
|
+
Unknown keys are refused by name rather than ignored, and an overlapping train/test split is
|
|
275
|
+
rejected before the run starts. [`configs/README.md`](configs/README.md) explains what
|
|
276
|
+
belongs in configuration and what deliberately stays in code; several files under
|
|
277
|
+
[`configs/`](configs/) are **deliberately diagnostic, not production** (their own headers say
|
|
278
|
+
so), used to isolate a single variable while chasing a specific finding.
|
|
279
|
+
|
|
280
|
+
## Licensing / provenance
|
|
281
|
+
|
|
282
|
+
Everything here — the prior, the architecture, the training loop — is original code written
|
|
283
|
+
for this project. **No weights, datasets, or source from Neuralk (Seldon), Fundamental
|
|
284
|
+
(NEXUS), Google TabFM, TabPFN, TabICL, TabDPT, or any other tabular-foundation-model product
|
|
285
|
+
are used or may be added.** Those are cited in project discussion purely as public
|
|
286
|
+
research/product context. Every third-party dataset or dependency's licence is checked before
|
|
287
|
+
use, and a *weight* licence is checked separately from its *code* licence, every time — never
|
|
288
|
+
assumed from a prior check. Direct dependencies are all permissive — numpy, pandas and
|
|
289
|
+
scikit-learn BSD-3, scipy BSD-3, torch BSD-3, PyYAML MIT, and the optional benchmark extras
|
|
290
|
+
lightgbm MIT, xgboost Apache-2.0, catboost Apache-2.0, pyarrow Apache-2.0 — and
|
|
291
|
+
`scripts/check_licences.py` enforces that in CI rather than leaving it to this paragraph.
|
|
292
|
+
|
|
293
|
+
**One honest exception, which CI found and this paragraph previously did not mention.** On
|
|
294
|
+
**Linux**, `torch` pulls in around fifteen NVIDIA CUDA runtime packages that are **NVIDIA
|
|
295
|
+
Proprietary**, not permissive. They are a transitive runtime dependency rather than a choice
|
|
296
|
+
made here, this project does **not redistribute them** — `uv build` produces a pure-Python
|
|
297
|
+
wheel and pip fetches them from PyPI under NVIDIA's own terms — and they are absent on macOS,
|
|
298
|
+
which is why a check run only on a developer's Mac reported everything permissive while CI did
|
|
299
|
+
not. **Anyone shipping this in a product must read NVIDIA's EULA themselves**; it is not
|
|
300
|
+
something this repository can assert on their behalf.
|
|
301
|
+
|
|
302
|
+
## What would change the picture
|
|
303
|
+
|
|
304
|
+
Stated so the project is falsifiable rather than open-ended. The ~0.035 deficit to the field
|
|
305
|
+
has no measured axis of variation, and these are the remaining candidates:
|
|
306
|
+
|
|
307
|
+
| still open | why it is a candidate | status |
|
|
308
|
+
| --- | --- | --- |
|
|
309
|
+
| **factorized attention** | the encoder is memory-bound at every turn, and three peers independently chose the cheaper form | proposed (44.x), prior art recorded |
|
|
310
|
+
| **a joint objective** | `p(x, y \| D)` rather than `p(y \| x, D)` makes every column a training signal, not just the target | proposed (48.15), scoped as a measurement before a rewrite |
|
|
311
|
+
| **prior design** | the only lever a peer's own ablations identify as decisive | instrument built and partly falsified — see below |
|
|
312
|
+
|
|
313
|
+
### Closed by measurement
|
|
314
|
+
|
|
315
|
+
Not abandoned — **tested and ruled out**, which is the more useful half of the record and the
|
|
316
|
+
reason the list above is short:
|
|
317
|
+
|
|
318
|
+
| candidate | what the measurement said |
|
|
319
|
+
| --- | --- |
|
|
320
|
+
| parameter scale | **−0.0049** at 5.7× the parameters on *matched* task volume (§114). A peer's published curve returns +0.0049 R² for 16.7×, against a 0.035 deficit. |
|
|
321
|
+
| training volume | null at 5× (§93); **+0.0028** at 2× (§114). |
|
|
322
|
+
| depth over width | **could not be scored at all** — the harness's per-dataset time limit, after 8 of 27 datasets (§114). Depth costs inference time and this model is already slow. |
|
|
323
|
+
| `column_id_dim` | peaks at the value chosen by accident; all four non-peak arms below it on all five folds (§104). |
|
|
324
|
+
| a widened structural-causal prior | **p = 1.000** against its own matched control. |
|
|
325
|
+
| a tree-structured prior | +0.0094 on general tabular data, replicated across two seeds (§115) — and **−0.0221 average precision on credit** (§116). Ships off by default. |
|
|
326
|
+
|
|
327
|
+
**The prior-scoring instrument is half-confirmed and half-falsified.** It predicted the
|
|
328
|
+
general-tabular gain above before it was measured (§112 → §115). A second arm, added to ask the
|
|
329
|
+
credit question, then predicted the wrong direction on its first real test — raising the
|
|
330
|
+
structural-causal prior's share cost **−0.0339 AP**, 0 of 5 folds, all five significant (§118).
|
|
331
|
+
So it predicts **breadth, not fit**, and no claim rests on its credit arm.
|
|
332
|
+
|
|
333
|
+
## Reproducing the measurements
|
|
334
|
+
|
|
335
|
+
Every number in [`docs/results/FINDINGS.md`](docs/results/FINDINGS.md) names the command that produced it.
|
|
336
|
+
The entry points:
|
|
337
|
+
|
|
338
|
+
| command | what it measures |
|
|
339
|
+
| --- | --- |
|
|
340
|
+
| `fintfm-v4protocol` | V4FinBench under its **published** five-fold protocol — the benchmark this project's claims rest on |
|
|
341
|
+
| `fintfm-bench --credit` | Polish and Taiwan bankruptcy panels |
|
|
342
|
+
| `fintfm-v4oot` | out-of-time split, which the published protocol is not |
|
|
343
|
+
| `fintfm-capability` | synthetic probes with a **known** ceiling, some with a closed-form Bayes-optimal AUC |
|
|
344
|
+
| `fintfm-priorscore` | scores a *prior*, not a model, on performance / diversity / distinctiveness |
|
|
345
|
+
| `fintfm-ctxsweep` | context construction, which explains more variance than model family (§5) |
|
|
346
|
+
|
|
347
|
+
Two conventions worth knowing before reading any of it. **Every number declares how it was
|
|
348
|
+
produced** — SMOKE-TEST, MEASURED, SIMULATED or ESTIMATED — because in an ML repository a wrong
|
|
349
|
+
number does not crash, it looks like a result. And **negative and superseded results are kept**,
|
|
350
|
+
because they are what stops the same wrong conclusion being reached twice.
|
|
351
|
+
|
|
352
|
+
## Where this is going
|
|
353
|
+
|
|
354
|
+
[`docs/`](docs/) holds the project's reasoning, indexed in [`docs/README.md`](docs/README.md).
|
|
355
|
+
|
|
356
|
+
- [`docs/paper/CLAIMS.md`](docs/paper/CLAIMS.md) — **start here.** Every claim this project
|
|
357
|
+
could make, tagged by status, newest evidence wins.
|
|
358
|
+
- [`docs/results/FINDINGS.md`](docs/results/FINDINGS.md) — the full measurement log, numbered sequentially
|
|
359
|
+
(121 entries and counting), each declaring how its numbers were produced.
|
|
360
|
+
- [`docs/design/DECISIONS.md`](docs/design/DECISIONS.md) — why the project is built the way it is, and what
|
|
361
|
+
would reverse each choice.
|
|
362
|
+
- [`docs/roadmap/STRATEGY.md`](docs/roadmap/STRATEGY.md) — the plan of record.
|
|
363
|
+
- [`docs/design/ARCHITECTURE.md`](docs/design/ARCHITECTURE.md) — how the model works, in more depth than
|
|
364
|
+
this file.
|
|
365
|
+
- [`docs/results/TABARENA.md`](docs/results/TABARENA.md) — the external-evaluation recipe, the coverage
|
|
366
|
+
fraction any score must carry, and four silent failure modes including a results cache that
|
|
367
|
+
returns stale numbers after a preprocessing change.
|
|
368
|
+
- [`docs/paper/RELATED_WORK.md`](docs/paper/RELATED_WORK.md) — the competing models, read from
|
|
369
|
+
primary sources, with what each costs this project's positioning. Its digest is the shortest
|
|
370
|
+
useful summary of why the remaining lever is the prior.
|
|
371
|
+
- [`docs/results/POSTMORTEM.md`](docs/results/POSTMORTEM.md) — wrong diagnoses, each caught by measurement
|
|
372
|
+
rather than review, kept on the record deliberately.
|
|
373
|
+
- [`openspec/changes/`](openspec/changes/) — the live roadmap as structured proposals with
|
|
374
|
+
verifiable tasks, not prose. `uv run python openspec/tools/validate.py` checks every
|
|
375
|
+
proposal is well-formed and every finding declares its provenance.
|
|
376
|
+
|
|
377
|
+
Short version: the mechanism claims ("no training on your data", "no feature engineering") are
|
|
378
|
+
already owned by better-funded competitors, so the thesis is not the mechanism. It is a credit
|
|
379
|
+
model that arrives with its own validation evidence — calibrated, auditably free of benchmark
|
|
380
|
+
contamination, and eventually backed by a pre-registered forward track record that cannot be
|
|
381
|
+
bought — plus a public, self-correcting record of what has and has not been shown to be true.
|
|
382
|
+
|
|
383
|
+
## Citing
|
|
384
|
+
|
|
385
|
+
A `CITATION.cff` is in the repository root, so GitHub's **"Cite this repository"** button
|
|
386
|
+
renders BibTeX and APA directly.
|
|
387
|
+
|
|
388
|
+
Cite the **repository** for the method, the measurement log or any finding; cite the
|
|
389
|
+
**checkpoint** ([kabartay/fintfm-binary](https://huggingface.co/kabartay/fintfm-binary)) when
|
|
390
|
+
the specific weights matter to what you are reporting. They are different artifacts and a
|
|
391
|
+
reader can only check the one you name.
|
|
392
|
+
|
|
393
|
+
## Contributing, and what cannot come in
|
|
394
|
+
|
|
395
|
+
Issues and discussion are welcome. Two hard boundaries, both of which protect the only claim
|
|
396
|
+
this project has that competitors do not:
|
|
397
|
+
|
|
398
|
+
**No code, weights, or training data from any tabular-foundation-model product** — TabPFN,
|
|
399
|
+
TabICL, TabDPT, LimiX, Nori, MITRA, or any other — may enter this repository. Their published
|
|
400
|
+
papers are legitimate to read and cite, and [`docs/paper/RELATED_WORK.md`](docs/paper/RELATED_WORK.md)
|
|
401
|
+
does exactly that for ten of them. Ingesting any of it would destroy the provenance argument
|
|
402
|
+
above, and nothing would warn us.
|
|
403
|
+
|
|
404
|
+
**Check a weights licence separately from its code licence, every time.** Four of the ten peer
|
|
405
|
+
projects read for this release ship permissive code with **non-commercial weights**, a split
|
|
406
|
+
invisible from a repository's headline licence badge. One of them restricts commercial use of
|
|
407
|
+
the model's *output*, not merely the weights.
|