itstheraj commited on
Commit
c902bdf
·
0 Parent(s):

initial commit

Browse files
.gitattributes ADDED
@@ -0,0 +1 @@
 
 
1
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ .ruff_cache/
2
+ .venv/
3
+ __pycache__/
4
+ *.swp
5
+ *.kate-swp
6
+ *~
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 2026 Lowdown Labs
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.
README.md ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ tags:
4
+ - fela
5
+ - felatab
6
+ - tabular
7
+ - in-context-learning
8
+ - prior-fitted-network
9
+ - foundation-model
10
+ - delta-rule
11
+ - cpu
12
+ - on-device
13
+ library_name: transformers
14
+ pipeline_tag: tabular-classification
15
+ model-index:
16
+ - name: fela-tab
17
+ results:
18
+ - task:
19
+ type: tabular-classification
20
+ name: Tabular classification
21
+ dataset:
22
+ type: openml
23
+ name: OpenML (8 datasets)
24
+ metrics:
25
+ - type: accuracy
26
+ value: 0.819
27
+ name: mean accuracy (zero shot)
28
+ ---
29
+
30
+ # DISCLAIMER
31
+
32
+ This model is a research preview. It is offered for advancing public science and for evaluation.
33
+ It is not a substitute for a domain expert, and it is not a certified decision system.
34
+
35
+ # FelaTab: point at any table in context tabular model
36
+
37
+ FelaTab reads a small table you already have and predicts the missing cells. You give it some
38
+ example rows with their answers (the support rows) and one or more rows you want filled in (the
39
+ query rows); it learns the pattern from your examples in a single pass and returns the answer with
40
+ a calibrated confidence range. There is no per table training, no fitting, and no setup: you point
41
+ it at a table and it predicts. It runs on a plain CPU with no GPU.
42
+
43
+ It is a prior fitted network (a "foundation model for tables"), trained on millions of synthetic
44
+ tables so that it generalizes zero shot to real tables it has never seen.
45
+
46
+ # What goes in, what comes out
47
+
48
+ - Input: one table, split into SUPPORT rows (features + a known label) and QUERY rows (features
49
+ only). Features are plain numbers; the model standardizes them for you. Up to 100 feature
50
+ columns and, for classification, up to 10 classes.
51
+ - Output:
52
+ - Classification: a probability for each class on every query row (they sum to one). Pick the
53
+ top class as the prediction and read the probability as the confidence.
54
+ - Regression: a predicted value plus a standard deviation on every query row, an honest error
55
+ bar in the original units of your label, not just a point estimate.
56
+ - In plain terms: "these rows look labelled like this; fill the blank ones and tell me how sure
57
+ you are."
58
+
59
+ # Why we built it this way
60
+
61
+ The "sequence" the model attends over is the SET of rows of your table. Support rows carry their
62
+ label and write into a fixed size working state; query rows are read only (they never write the
63
+ state and never see each other, so there is no test time leakage). The mixer is a delta rule
64
+ linear attention (an overwrite update that fixes the recall weakness of plain linear attention)
65
+ plus a variable chunk landmark attention over pooled support rows. Both are linear in the number
66
+ of rows with a fixed working state, so the model scales past the quadratic roughly 10k row cap of
67
+ full attention tabular models, and its memory stays flat as the support set grows. That is what
68
+ lets it run on a low power CPU and index large tables without a GPU.
69
+
70
+ ## The two tiers (one model, pick your size)
71
+
72
+ FelaTab ships two tiers from ONE MatFormer nested training run. The small model is a strict
73
+ top left prefix slice of the big model's weights (nesting verified bit exact at full
74
+ granularity), so you get both from a single set of weights and can pick your size/accuracy
75
+ tradeoff.
76
+
77
+ | Tier | dim | layers | heads | params | fp32 file | int8 file |
78
+ |-------|-----|--------|-------|--------|-----------|-----------|
79
+ | big | 1024 | 28 | 16 | 411.9M | `model_big.safetensors` (1.65 GB) | `model_big_int8.safetensors` (416 MB) |
80
+ | small | 512 | 14 | 8 | 51.6M | `model_small.safetensors` (206 MB) | `model_small_int8.safetensors` (52 MB) |
81
+
82
+ int8 is close to lossless here (see below), so the int8 files are the recommended deploy
83
+ artifact. The in browser demo runs the small int8 tier.
84
+
85
+ # Performance and footprint
86
+
87
+ Measured on CPU. The working memory is flat as the support set grows: on the OpenML `adult`
88
+ dataset the big model's resident set stays at about **3.79 GB from 500 support rows all the way to
89
+ 10,000** support rows (the model carries a fixed size state, not a growing key value cache), while
90
+ a full attention model would grow quadratically. Row throughput is linear in the support size.
91
+
92
+ # Accuracy (held out OpenML)
93
+
94
+ Protocol: zero shot, TabPFN style. The train split of a real OpenML dataset is fed as support
95
+ rows, the test split as query rows, in one in context forward, no per dataset training. The
96
+ reported battery is 8 classification and 5 regression datasets. Field bars are untuned
97
+ scikit-learn and a TUNED LightGBM; the "full ensemble" is FelaTab combined with gradient boosted
98
+ trees and ridge. The numbers below are measured on the shipped weights.
99
+
100
+ ## Classification (8 datasets, mean accuracy)
101
+
102
+ | System | Accuracy |
103
+ |---|---|
104
+ | FelaTab big, single pass | 0.819 |
105
+ | FelaTab small, single pass | 0.810 |
106
+ | tuned LightGBM | 0.827 |
107
+ | untuned scikit-learn, best | 0.834 |
108
+ | full ensemble (FelaTab + GBT + ridge, stacked) | 0.840 |
109
+
110
+ - With no tuning, FelaTab alone matches or beats a **tuned** LightGBM on **5 of the 8** datasets.
111
+ - FelaTab alone reaches about **97%** of the full ensemble's accuracy (0.819 vs 0.840).
112
+ - Calibration is good: expected calibration error about **0.051** for FelaTab, and a conformal
113
+ wrapper hits its target coverage (empirical coverage about **0.91** for FelaTab and about **0.92**
114
+ for the ensemble at a 0.90 guarantee).
115
+ - Inference time bagging (averaging the frozen model over permuted feature orders and a reshuffled
116
+ support set) did not improve the big tier on this battery (it measured 0.811, below the single
117
+ pass), so the single pass is the number to use.
118
+
119
+ ## Regression (5 datasets)
120
+
121
+ On regression accuracy FelaTab alone beats neither untuned scikit-learn nor a tuned LightGBM on any
122
+ of the 5 datasets (**0 of 5**) but it's not off by far; its mean R2 is 0.813 versus 0.880 for a tuned LightGBM.
123
+
124
+ This is a training budget limit of the current model, not a bug. What
125
+ FelaTab ships for regression is, however, calibrated uncertainty: a Gaussian mean and standard
126
+ deviation per row, plus a conformal wrapper that reaches its coverage target (empirical coverage
127
+ about 0.91 at a 0.90 guarantee). If you need maximum regression accuracy, use a gradient boosted
128
+ tree; use FelaTab's regression head for a fast zero shot estimate with a trustworthy error bar.
129
+
130
+ ## int8 is close to lossless
131
+
132
+ Dynamic int8 on the bulk linear layers barely moves accuracy: classification 0.819 fp32 vs 0.816
133
+ int8 for the big tier and 0.810 vs 0.809 for the small tier, regression within 0.002 R2 either
134
+ way. The int8 files are 4x smaller and are the recommended deploy format.
135
+
136
+
137
+ # How to run it
138
+
139
+ See `quickstart/` for a runnable example on public sklearn datasets. The short version:
140
+
141
+ ```python
142
+ from modeling import load_model, predict
143
+
144
+ model = load_model("/path/to/repo_dir", tier="small") # or "big"; or a HF repo id
145
+ # Xtr, ytr = your labelled support rows; Xte = the rows to predict
146
+ probs = predict(model, Xtr, ytr, Xte, task="classification", n_classes=3) # [n_query, 3]
147
+ mean, std = predict(model, Xtr, ytr, Xte, task="regression") # error bars
148
+ ```
149
+
150
+ The loader ships `config_big.json` / `config_small.json` (a top level `config.json` mirrors the
151
+ big tier) and a self contained `modeling.py` with `load_model` / `from_pretrained`. It
152
+ auto detects the int8 bundles and dequantizes them, so you always get a plain fp32 model back. For
153
+ an interactive playground, see the Hugging Face Space in `space/`.
154
+
155
+ ## Serving artifacts
156
+
157
+ - `model_<tier>.safetensors` (fp32) and `model_<tier>_int8.safetensors` (int8), plus the matching
158
+ `config_<tier>.json`.
159
+ - `verify.py` runs two fixed bundled tasks (one classification, one regression), checks the output
160
+ shapes, that the regression standard deviations are positive, and that the small tier
161
+ predictions match a stored reference within the shipped int8 tolerance.
162
+ - `modeling.py` also exposes `predict_bagged` (the inference bagging path) and the full
163
+ architecture for standard tooling.
164
+
165
+ FelaTab is embedded natively in the CPU FELA server and in a PostgreSQL extension (`fela_classify`
166
+ in database), and runs client side in the browser via WebAssembly (the small int8 tier). Those
167
+ paths reproduce the Python predictions to within the int8 tolerance.
168
+
169
+ ## Training data
170
+
171
+ FelaTab is a prior fitted network: trained on synthetic tables from a structural causal model prior
172
+ (about 85% of the stream) with a small mix in of teacher baked real public OpenML tables (about
173
+ 15%), and evaluated zero shot on held out real OpenML datasets that are excluded from training by
174
+ dataset id and name. No customer data and no benchmarked test set is in the training stream. The
175
+ synthetic prior is in `prior.py` and the training objective is in `train.py`, which reproduces the
176
+ method (`python train.py --smoke`).
177
+
178
+ # Intended use, limitations, and safety
179
+
180
+ What it is for: fast, zero shot predictions and autofill on small to medium tables where you do
181
+ not want to set up and tune a model: spreadsheet autofill, in database prediction, quick
182
+ what if estimates, and calibrated triage where the confidence matters as much as the answer. It is
183
+ a good first pass and a strong no tuning baseline.
184
+
185
+ What it is not: it is not a replacement for a tuned gradient boosted tree when you want maximum
186
+ accuracy, especially on regression, where it is behind trees and ships error bars instead of
187
+ headline accuracy. It is capped at 100 features and 10 classes. It is trained and validated on
188
+ standard public tabular benchmarks; generalization to a very different distribution should be
189
+ checked on your own held out data before you rely on it. Do not use it as the sole basis for a
190
+ consequential decision.
191
+
192
+ # How to cite
193
+
194
+ ```bibtex
195
+ @misc{lowdownlabs_felatab,
196
+ title = {FelaTab: an in context tabular foundation model on the FELA linear attention base},
197
+ author = {Lowdown Labs},
198
+ year = {2026},
199
+ note = {Model card}
200
+ }
201
+ ```
202
+
203
+ # Model family
204
+
205
+ This is part of the FELA family from Lowdown Labs: one linear attention architecture across many
206
+ modalities, all CPU native and subquadratic. Sibling repos share no weights, so none carries a
207
+ `base_model` link.
208
+
209
+ # License
210
+ Apache-2.0. The model weights and the code are both under Apache-2.0 (see LICENSE): no separate model license and no non commercial restriction. Trained on synthetic data; the OpenML datasets are used only for evaluation, not training. We distribute weights only.
config.json ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "felatab",
3
+ "library_name": "transformers",
4
+ "architectures": [
5
+ "FelaTabModel"
6
+ ],
7
+ "auto_map": {
8
+ "AutoConfig": "configuration_felatab.FelaTabConfig",
9
+ "AutoModel": "modeling_felatab.FelaTabModel"
10
+ },
11
+ "arch": "FelaTab",
12
+ "tier": "big",
13
+ "params_millions": 411.86,
14
+ "max_features": 100,
15
+ "max_classes": 10,
16
+ "dim": 1024,
17
+ "n_layers": 28,
18
+ "n_heads": 16,
19
+ "head_dim": 64,
20
+ "chunk": 64,
21
+ "ffn_mult": 3,
22
+ "use_landmark": true,
23
+ "n_landmarks": 48,
24
+ "ln_eps": 1e-05,
25
+ "note": "Reads support rows (features plus label) then query rows; returns class probabilities or mean and std error bars."
26
+ }
config_big.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "felatab",
3
+ "library_name": "pytorch",
4
+ "arch": "FelaTab",
5
+ "tier": "big",
6
+ "params_millions": 411.86,
7
+ "max_features": 100,
8
+ "max_classes": 10,
9
+ "dim": 1024,
10
+ "n_layers": 28,
11
+ "n_heads": 16,
12
+ "head_dim": 64,
13
+ "chunk": 64,
14
+ "ffn_mult": 3,
15
+ "use_landmark": true,
16
+ "n_landmarks": 48,
17
+ "ln_eps": 1e-05,
18
+ "note": "Reads support rows (features plus label) then query rows; returns class probabilities or mean and std error bars."
19
+ }
config_small.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "felatab",
3
+ "library_name": "pytorch",
4
+ "arch": "FelaTab",
5
+ "tier": "small",
6
+ "params_millions": 51.56,
7
+ "max_features": 100,
8
+ "max_classes": 10,
9
+ "dim": 512,
10
+ "n_layers": 14,
11
+ "n_heads": 8,
12
+ "head_dim": 64,
13
+ "chunk": 64,
14
+ "ffn_mult": 3,
15
+ "use_landmark": true,
16
+ "n_landmarks": 48,
17
+ "ln_eps": 1e-05,
18
+ "note": "Reads support rows (features plus label) then query rows; returns class probabilities or mean and std error bars."
19
+ }
configuration_felatab.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import PretrainedConfig
2
+
3
+
4
+ class FelaTabConfig(PretrainedConfig):
5
+ model_type = "felatab"
6
+
7
+ def __init__(
8
+ self,
9
+ max_features=100,
10
+ max_classes=10,
11
+ dim=1024,
12
+ n_layers=28,
13
+ n_heads=16,
14
+ head_dim=64,
15
+ chunk=64,
16
+ ffn_mult=3,
17
+ dropout=0.0,
18
+ use_landmark=True,
19
+ n_landmarks=48,
20
+ ln_eps=1e-05,
21
+ **kwargs,
22
+ ):
23
+ self.max_features = max_features
24
+ self.max_classes = max_classes
25
+ self.dim = dim
26
+ self.n_layers = n_layers
27
+ self.n_heads = n_heads
28
+ self.head_dim = head_dim
29
+ self.chunk = chunk
30
+ self.ffn_mult = ffn_mult
31
+ self.dropout = dropout
32
+ self.use_landmark = use_landmark
33
+ self.n_landmarks = n_landmarks
34
+ self.ln_eps = ln_eps
35
+ super().__init__(**kwargs)
model.safetensors.index.json ADDED
@@ -0,0 +1,552 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "metadata": {
3
+ "total_size": 1647454116
4
+ },
5
+ "weight_map": {
6
+ "blocks.0.attn.b.bias": "model_big.safetensors",
7
+ "blocks.0.attn.b.weight": "model_big.safetensors",
8
+ "blocks.0.attn.k.weight": "model_big.safetensors",
9
+ "blocks.0.attn.o.weight": "model_big.safetensors",
10
+ "blocks.0.attn.q.weight": "model_big.safetensors",
11
+ "blocks.0.attn.v.weight": "model_big.safetensors",
12
+ "blocks.0.ff.0.bias": "model_big.safetensors",
13
+ "blocks.0.ff.0.weight": "model_big.safetensors",
14
+ "blocks.0.ff.2.bias": "model_big.safetensors",
15
+ "blocks.0.ff.2.weight": "model_big.safetensors",
16
+ "blocks.0.landmark.gate": "model_big.safetensors",
17
+ "blocks.0.landmark.k.weight": "model_big.safetensors",
18
+ "blocks.0.landmark.o.weight": "model_big.safetensors",
19
+ "blocks.0.landmark.q.weight": "model_big.safetensors",
20
+ "blocks.0.landmark.v.weight": "model_big.safetensors",
21
+ "blocks.0.n1.bias": "model_big.safetensors",
22
+ "blocks.0.n1.weight": "model_big.safetensors",
23
+ "blocks.0.n2.bias": "model_big.safetensors",
24
+ "blocks.0.n2.weight": "model_big.safetensors",
25
+ "blocks.1.attn.b.bias": "model_big.safetensors",
26
+ "blocks.1.attn.b.weight": "model_big.safetensors",
27
+ "blocks.1.attn.k.weight": "model_big.safetensors",
28
+ "blocks.1.attn.o.weight": "model_big.safetensors",
29
+ "blocks.1.attn.q.weight": "model_big.safetensors",
30
+ "blocks.1.attn.v.weight": "model_big.safetensors",
31
+ "blocks.1.ff.0.bias": "model_big.safetensors",
32
+ "blocks.1.ff.0.weight": "model_big.safetensors",
33
+ "blocks.1.ff.2.bias": "model_big.safetensors",
34
+ "blocks.1.ff.2.weight": "model_big.safetensors",
35
+ "blocks.1.landmark.gate": "model_big.safetensors",
36
+ "blocks.1.landmark.k.weight": "model_big.safetensors",
37
+ "blocks.1.landmark.o.weight": "model_big.safetensors",
38
+ "blocks.1.landmark.q.weight": "model_big.safetensors",
39
+ "blocks.1.landmark.v.weight": "model_big.safetensors",
40
+ "blocks.1.n1.bias": "model_big.safetensors",
41
+ "blocks.1.n1.weight": "model_big.safetensors",
42
+ "blocks.1.n2.bias": "model_big.safetensors",
43
+ "blocks.1.n2.weight": "model_big.safetensors",
44
+ "blocks.10.attn.b.bias": "model_big.safetensors",
45
+ "blocks.10.attn.b.weight": "model_big.safetensors",
46
+ "blocks.10.attn.k.weight": "model_big.safetensors",
47
+ "blocks.10.attn.o.weight": "model_big.safetensors",
48
+ "blocks.10.attn.q.weight": "model_big.safetensors",
49
+ "blocks.10.attn.v.weight": "model_big.safetensors",
50
+ "blocks.10.ff.0.bias": "model_big.safetensors",
51
+ "blocks.10.ff.0.weight": "model_big.safetensors",
52
+ "blocks.10.ff.2.bias": "model_big.safetensors",
53
+ "blocks.10.ff.2.weight": "model_big.safetensors",
54
+ "blocks.10.landmark.gate": "model_big.safetensors",
55
+ "blocks.10.landmark.k.weight": "model_big.safetensors",
56
+ "blocks.10.landmark.o.weight": "model_big.safetensors",
57
+ "blocks.10.landmark.q.weight": "model_big.safetensors",
58
+ "blocks.10.landmark.v.weight": "model_big.safetensors",
59
+ "blocks.10.n1.bias": "model_big.safetensors",
60
+ "blocks.10.n1.weight": "model_big.safetensors",
61
+ "blocks.10.n2.bias": "model_big.safetensors",
62
+ "blocks.10.n2.weight": "model_big.safetensors",
63
+ "blocks.11.attn.b.bias": "model_big.safetensors",
64
+ "blocks.11.attn.b.weight": "model_big.safetensors",
65
+ "blocks.11.attn.k.weight": "model_big.safetensors",
66
+ "blocks.11.attn.o.weight": "model_big.safetensors",
67
+ "blocks.11.attn.q.weight": "model_big.safetensors",
68
+ "blocks.11.attn.v.weight": "model_big.safetensors",
69
+ "blocks.11.ff.0.bias": "model_big.safetensors",
70
+ "blocks.11.ff.0.weight": "model_big.safetensors",
71
+ "blocks.11.ff.2.bias": "model_big.safetensors",
72
+ "blocks.11.ff.2.weight": "model_big.safetensors",
73
+ "blocks.11.landmark.gate": "model_big.safetensors",
74
+ "blocks.11.landmark.k.weight": "model_big.safetensors",
75
+ "blocks.11.landmark.o.weight": "model_big.safetensors",
76
+ "blocks.11.landmark.q.weight": "model_big.safetensors",
77
+ "blocks.11.landmark.v.weight": "model_big.safetensors",
78
+ "blocks.11.n1.bias": "model_big.safetensors",
79
+ "blocks.11.n1.weight": "model_big.safetensors",
80
+ "blocks.11.n2.bias": "model_big.safetensors",
81
+ "blocks.11.n2.weight": "model_big.safetensors",
82
+ "blocks.12.attn.b.bias": "model_big.safetensors",
83
+ "blocks.12.attn.b.weight": "model_big.safetensors",
84
+ "blocks.12.attn.k.weight": "model_big.safetensors",
85
+ "blocks.12.attn.o.weight": "model_big.safetensors",
86
+ "blocks.12.attn.q.weight": "model_big.safetensors",
87
+ "blocks.12.attn.v.weight": "model_big.safetensors",
88
+ "blocks.12.ff.0.bias": "model_big.safetensors",
89
+ "blocks.12.ff.0.weight": "model_big.safetensors",
90
+ "blocks.12.ff.2.bias": "model_big.safetensors",
91
+ "blocks.12.ff.2.weight": "model_big.safetensors",
92
+ "blocks.12.landmark.gate": "model_big.safetensors",
93
+ "blocks.12.landmark.k.weight": "model_big.safetensors",
94
+ "blocks.12.landmark.o.weight": "model_big.safetensors",
95
+ "blocks.12.landmark.q.weight": "model_big.safetensors",
96
+ "blocks.12.landmark.v.weight": "model_big.safetensors",
97
+ "blocks.12.n1.bias": "model_big.safetensors",
98
+ "blocks.12.n1.weight": "model_big.safetensors",
99
+ "blocks.12.n2.bias": "model_big.safetensors",
100
+ "blocks.12.n2.weight": "model_big.safetensors",
101
+ "blocks.13.attn.b.bias": "model_big.safetensors",
102
+ "blocks.13.attn.b.weight": "model_big.safetensors",
103
+ "blocks.13.attn.k.weight": "model_big.safetensors",
104
+ "blocks.13.attn.o.weight": "model_big.safetensors",
105
+ "blocks.13.attn.q.weight": "model_big.safetensors",
106
+ "blocks.13.attn.v.weight": "model_big.safetensors",
107
+ "blocks.13.ff.0.bias": "model_big.safetensors",
108
+ "blocks.13.ff.0.weight": "model_big.safetensors",
109
+ "blocks.13.ff.2.bias": "model_big.safetensors",
110
+ "blocks.13.ff.2.weight": "model_big.safetensors",
111
+ "blocks.13.landmark.gate": "model_big.safetensors",
112
+ "blocks.13.landmark.k.weight": "model_big.safetensors",
113
+ "blocks.13.landmark.o.weight": "model_big.safetensors",
114
+ "blocks.13.landmark.q.weight": "model_big.safetensors",
115
+ "blocks.13.landmark.v.weight": "model_big.safetensors",
116
+ "blocks.13.n1.bias": "model_big.safetensors",
117
+ "blocks.13.n1.weight": "model_big.safetensors",
118
+ "blocks.13.n2.bias": "model_big.safetensors",
119
+ "blocks.13.n2.weight": "model_big.safetensors",
120
+ "blocks.14.attn.b.bias": "model_big.safetensors",
121
+ "blocks.14.attn.b.weight": "model_big.safetensors",
122
+ "blocks.14.attn.k.weight": "model_big.safetensors",
123
+ "blocks.14.attn.o.weight": "model_big.safetensors",
124
+ "blocks.14.attn.q.weight": "model_big.safetensors",
125
+ "blocks.14.attn.v.weight": "model_big.safetensors",
126
+ "blocks.14.ff.0.bias": "model_big.safetensors",
127
+ "blocks.14.ff.0.weight": "model_big.safetensors",
128
+ "blocks.14.ff.2.bias": "model_big.safetensors",
129
+ "blocks.14.ff.2.weight": "model_big.safetensors",
130
+ "blocks.14.landmark.gate": "model_big.safetensors",
131
+ "blocks.14.landmark.k.weight": "model_big.safetensors",
132
+ "blocks.14.landmark.o.weight": "model_big.safetensors",
133
+ "blocks.14.landmark.q.weight": "model_big.safetensors",
134
+ "blocks.14.landmark.v.weight": "model_big.safetensors",
135
+ "blocks.14.n1.bias": "model_big.safetensors",
136
+ "blocks.14.n1.weight": "model_big.safetensors",
137
+ "blocks.14.n2.bias": "model_big.safetensors",
138
+ "blocks.14.n2.weight": "model_big.safetensors",
139
+ "blocks.15.attn.b.bias": "model_big.safetensors",
140
+ "blocks.15.attn.b.weight": "model_big.safetensors",
141
+ "blocks.15.attn.k.weight": "model_big.safetensors",
142
+ "blocks.15.attn.o.weight": "model_big.safetensors",
143
+ "blocks.15.attn.q.weight": "model_big.safetensors",
144
+ "blocks.15.attn.v.weight": "model_big.safetensors",
145
+ "blocks.15.ff.0.bias": "model_big.safetensors",
146
+ "blocks.15.ff.0.weight": "model_big.safetensors",
147
+ "blocks.15.ff.2.bias": "model_big.safetensors",
148
+ "blocks.15.ff.2.weight": "model_big.safetensors",
149
+ "blocks.15.landmark.gate": "model_big.safetensors",
150
+ "blocks.15.landmark.k.weight": "model_big.safetensors",
151
+ "blocks.15.landmark.o.weight": "model_big.safetensors",
152
+ "blocks.15.landmark.q.weight": "model_big.safetensors",
153
+ "blocks.15.landmark.v.weight": "model_big.safetensors",
154
+ "blocks.15.n1.bias": "model_big.safetensors",
155
+ "blocks.15.n1.weight": "model_big.safetensors",
156
+ "blocks.15.n2.bias": "model_big.safetensors",
157
+ "blocks.15.n2.weight": "model_big.safetensors",
158
+ "blocks.16.attn.b.bias": "model_big.safetensors",
159
+ "blocks.16.attn.b.weight": "model_big.safetensors",
160
+ "blocks.16.attn.k.weight": "model_big.safetensors",
161
+ "blocks.16.attn.o.weight": "model_big.safetensors",
162
+ "blocks.16.attn.q.weight": "model_big.safetensors",
163
+ "blocks.16.attn.v.weight": "model_big.safetensors",
164
+ "blocks.16.ff.0.bias": "model_big.safetensors",
165
+ "blocks.16.ff.0.weight": "model_big.safetensors",
166
+ "blocks.16.ff.2.bias": "model_big.safetensors",
167
+ "blocks.16.ff.2.weight": "model_big.safetensors",
168
+ "blocks.16.landmark.gate": "model_big.safetensors",
169
+ "blocks.16.landmark.k.weight": "model_big.safetensors",
170
+ "blocks.16.landmark.o.weight": "model_big.safetensors",
171
+ "blocks.16.landmark.q.weight": "model_big.safetensors",
172
+ "blocks.16.landmark.v.weight": "model_big.safetensors",
173
+ "blocks.16.n1.bias": "model_big.safetensors",
174
+ "blocks.16.n1.weight": "model_big.safetensors",
175
+ "blocks.16.n2.bias": "model_big.safetensors",
176
+ "blocks.16.n2.weight": "model_big.safetensors",
177
+ "blocks.17.attn.b.bias": "model_big.safetensors",
178
+ "blocks.17.attn.b.weight": "model_big.safetensors",
179
+ "blocks.17.attn.k.weight": "model_big.safetensors",
180
+ "blocks.17.attn.o.weight": "model_big.safetensors",
181
+ "blocks.17.attn.q.weight": "model_big.safetensors",
182
+ "blocks.17.attn.v.weight": "model_big.safetensors",
183
+ "blocks.17.ff.0.bias": "model_big.safetensors",
184
+ "blocks.17.ff.0.weight": "model_big.safetensors",
185
+ "blocks.17.ff.2.bias": "model_big.safetensors",
186
+ "blocks.17.ff.2.weight": "model_big.safetensors",
187
+ "blocks.17.landmark.gate": "model_big.safetensors",
188
+ "blocks.17.landmark.k.weight": "model_big.safetensors",
189
+ "blocks.17.landmark.o.weight": "model_big.safetensors",
190
+ "blocks.17.landmark.q.weight": "model_big.safetensors",
191
+ "blocks.17.landmark.v.weight": "model_big.safetensors",
192
+ "blocks.17.n1.bias": "model_big.safetensors",
193
+ "blocks.17.n1.weight": "model_big.safetensors",
194
+ "blocks.17.n2.bias": "model_big.safetensors",
195
+ "blocks.17.n2.weight": "model_big.safetensors",
196
+ "blocks.18.attn.b.bias": "model_big.safetensors",
197
+ "blocks.18.attn.b.weight": "model_big.safetensors",
198
+ "blocks.18.attn.k.weight": "model_big.safetensors",
199
+ "blocks.18.attn.o.weight": "model_big.safetensors",
200
+ "blocks.18.attn.q.weight": "model_big.safetensors",
201
+ "blocks.18.attn.v.weight": "model_big.safetensors",
202
+ "blocks.18.ff.0.bias": "model_big.safetensors",
203
+ "blocks.18.ff.0.weight": "model_big.safetensors",
204
+ "blocks.18.ff.2.bias": "model_big.safetensors",
205
+ "blocks.18.ff.2.weight": "model_big.safetensors",
206
+ "blocks.18.landmark.gate": "model_big.safetensors",
207
+ "blocks.18.landmark.k.weight": "model_big.safetensors",
208
+ "blocks.18.landmark.o.weight": "model_big.safetensors",
209
+ "blocks.18.landmark.q.weight": "model_big.safetensors",
210
+ "blocks.18.landmark.v.weight": "model_big.safetensors",
211
+ "blocks.18.n1.bias": "model_big.safetensors",
212
+ "blocks.18.n1.weight": "model_big.safetensors",
213
+ "blocks.18.n2.bias": "model_big.safetensors",
214
+ "blocks.18.n2.weight": "model_big.safetensors",
215
+ "blocks.19.attn.b.bias": "model_big.safetensors",
216
+ "blocks.19.attn.b.weight": "model_big.safetensors",
217
+ "blocks.19.attn.k.weight": "model_big.safetensors",
218
+ "blocks.19.attn.o.weight": "model_big.safetensors",
219
+ "blocks.19.attn.q.weight": "model_big.safetensors",
220
+ "blocks.19.attn.v.weight": "model_big.safetensors",
221
+ "blocks.19.ff.0.bias": "model_big.safetensors",
222
+ "blocks.19.ff.0.weight": "model_big.safetensors",
223
+ "blocks.19.ff.2.bias": "model_big.safetensors",
224
+ "blocks.19.ff.2.weight": "model_big.safetensors",
225
+ "blocks.19.landmark.gate": "model_big.safetensors",
226
+ "blocks.19.landmark.k.weight": "model_big.safetensors",
227
+ "blocks.19.landmark.o.weight": "model_big.safetensors",
228
+ "blocks.19.landmark.q.weight": "model_big.safetensors",
229
+ "blocks.19.landmark.v.weight": "model_big.safetensors",
230
+ "blocks.19.n1.bias": "model_big.safetensors",
231
+ "blocks.19.n1.weight": "model_big.safetensors",
232
+ "blocks.19.n2.bias": "model_big.safetensors",
233
+ "blocks.19.n2.weight": "model_big.safetensors",
234
+ "blocks.2.attn.b.bias": "model_big.safetensors",
235
+ "blocks.2.attn.b.weight": "model_big.safetensors",
236
+ "blocks.2.attn.k.weight": "model_big.safetensors",
237
+ "blocks.2.attn.o.weight": "model_big.safetensors",
238
+ "blocks.2.attn.q.weight": "model_big.safetensors",
239
+ "blocks.2.attn.v.weight": "model_big.safetensors",
240
+ "blocks.2.ff.0.bias": "model_big.safetensors",
241
+ "blocks.2.ff.0.weight": "model_big.safetensors",
242
+ "blocks.2.ff.2.bias": "model_big.safetensors",
243
+ "blocks.2.ff.2.weight": "model_big.safetensors",
244
+ "blocks.2.landmark.gate": "model_big.safetensors",
245
+ "blocks.2.landmark.k.weight": "model_big.safetensors",
246
+ "blocks.2.landmark.o.weight": "model_big.safetensors",
247
+ "blocks.2.landmark.q.weight": "model_big.safetensors",
248
+ "blocks.2.landmark.v.weight": "model_big.safetensors",
249
+ "blocks.2.n1.bias": "model_big.safetensors",
250
+ "blocks.2.n1.weight": "model_big.safetensors",
251
+ "blocks.2.n2.bias": "model_big.safetensors",
252
+ "blocks.2.n2.weight": "model_big.safetensors",
253
+ "blocks.20.attn.b.bias": "model_big.safetensors",
254
+ "blocks.20.attn.b.weight": "model_big.safetensors",
255
+ "blocks.20.attn.k.weight": "model_big.safetensors",
256
+ "blocks.20.attn.o.weight": "model_big.safetensors",
257
+ "blocks.20.attn.q.weight": "model_big.safetensors",
258
+ "blocks.20.attn.v.weight": "model_big.safetensors",
259
+ "blocks.20.ff.0.bias": "model_big.safetensors",
260
+ "blocks.20.ff.0.weight": "model_big.safetensors",
261
+ "blocks.20.ff.2.bias": "model_big.safetensors",
262
+ "blocks.20.ff.2.weight": "model_big.safetensors",
263
+ "blocks.20.landmark.gate": "model_big.safetensors",
264
+ "blocks.20.landmark.k.weight": "model_big.safetensors",
265
+ "blocks.20.landmark.o.weight": "model_big.safetensors",
266
+ "blocks.20.landmark.q.weight": "model_big.safetensors",
267
+ "blocks.20.landmark.v.weight": "model_big.safetensors",
268
+ "blocks.20.n1.bias": "model_big.safetensors",
269
+ "blocks.20.n1.weight": "model_big.safetensors",
270
+ "blocks.20.n2.bias": "model_big.safetensors",
271
+ "blocks.20.n2.weight": "model_big.safetensors",
272
+ "blocks.21.attn.b.bias": "model_big.safetensors",
273
+ "blocks.21.attn.b.weight": "model_big.safetensors",
274
+ "blocks.21.attn.k.weight": "model_big.safetensors",
275
+ "blocks.21.attn.o.weight": "model_big.safetensors",
276
+ "blocks.21.attn.q.weight": "model_big.safetensors",
277
+ "blocks.21.attn.v.weight": "model_big.safetensors",
278
+ "blocks.21.ff.0.bias": "model_big.safetensors",
279
+ "blocks.21.ff.0.weight": "model_big.safetensors",
280
+ "blocks.21.ff.2.bias": "model_big.safetensors",
281
+ "blocks.21.ff.2.weight": "model_big.safetensors",
282
+ "blocks.21.landmark.gate": "model_big.safetensors",
283
+ "blocks.21.landmark.k.weight": "model_big.safetensors",
284
+ "blocks.21.landmark.o.weight": "model_big.safetensors",
285
+ "blocks.21.landmark.q.weight": "model_big.safetensors",
286
+ "blocks.21.landmark.v.weight": "model_big.safetensors",
287
+ "blocks.21.n1.bias": "model_big.safetensors",
288
+ "blocks.21.n1.weight": "model_big.safetensors",
289
+ "blocks.21.n2.bias": "model_big.safetensors",
290
+ "blocks.21.n2.weight": "model_big.safetensors",
291
+ "blocks.22.attn.b.bias": "model_big.safetensors",
292
+ "blocks.22.attn.b.weight": "model_big.safetensors",
293
+ "blocks.22.attn.k.weight": "model_big.safetensors",
294
+ "blocks.22.attn.o.weight": "model_big.safetensors",
295
+ "blocks.22.attn.q.weight": "model_big.safetensors",
296
+ "blocks.22.attn.v.weight": "model_big.safetensors",
297
+ "blocks.22.ff.0.bias": "model_big.safetensors",
298
+ "blocks.22.ff.0.weight": "model_big.safetensors",
299
+ "blocks.22.ff.2.bias": "model_big.safetensors",
300
+ "blocks.22.ff.2.weight": "model_big.safetensors",
301
+ "blocks.22.landmark.gate": "model_big.safetensors",
302
+ "blocks.22.landmark.k.weight": "model_big.safetensors",
303
+ "blocks.22.landmark.o.weight": "model_big.safetensors",
304
+ "blocks.22.landmark.q.weight": "model_big.safetensors",
305
+ "blocks.22.landmark.v.weight": "model_big.safetensors",
306
+ "blocks.22.n1.bias": "model_big.safetensors",
307
+ "blocks.22.n1.weight": "model_big.safetensors",
308
+ "blocks.22.n2.bias": "model_big.safetensors",
309
+ "blocks.22.n2.weight": "model_big.safetensors",
310
+ "blocks.23.attn.b.bias": "model_big.safetensors",
311
+ "blocks.23.attn.b.weight": "model_big.safetensors",
312
+ "blocks.23.attn.k.weight": "model_big.safetensors",
313
+ "blocks.23.attn.o.weight": "model_big.safetensors",
314
+ "blocks.23.attn.q.weight": "model_big.safetensors",
315
+ "blocks.23.attn.v.weight": "model_big.safetensors",
316
+ "blocks.23.ff.0.bias": "model_big.safetensors",
317
+ "blocks.23.ff.0.weight": "model_big.safetensors",
318
+ "blocks.23.ff.2.bias": "model_big.safetensors",
319
+ "blocks.23.ff.2.weight": "model_big.safetensors",
320
+ "blocks.23.landmark.gate": "model_big.safetensors",
321
+ "blocks.23.landmark.k.weight": "model_big.safetensors",
322
+ "blocks.23.landmark.o.weight": "model_big.safetensors",
323
+ "blocks.23.landmark.q.weight": "model_big.safetensors",
324
+ "blocks.23.landmark.v.weight": "model_big.safetensors",
325
+ "blocks.23.n1.bias": "model_big.safetensors",
326
+ "blocks.23.n1.weight": "model_big.safetensors",
327
+ "blocks.23.n2.bias": "model_big.safetensors",
328
+ "blocks.23.n2.weight": "model_big.safetensors",
329
+ "blocks.24.attn.b.bias": "model_big.safetensors",
330
+ "blocks.24.attn.b.weight": "model_big.safetensors",
331
+ "blocks.24.attn.k.weight": "model_big.safetensors",
332
+ "blocks.24.attn.o.weight": "model_big.safetensors",
333
+ "blocks.24.attn.q.weight": "model_big.safetensors",
334
+ "blocks.24.attn.v.weight": "model_big.safetensors",
335
+ "blocks.24.ff.0.bias": "model_big.safetensors",
336
+ "blocks.24.ff.0.weight": "model_big.safetensors",
337
+ "blocks.24.ff.2.bias": "model_big.safetensors",
338
+ "blocks.24.ff.2.weight": "model_big.safetensors",
339
+ "blocks.24.landmark.gate": "model_big.safetensors",
340
+ "blocks.24.landmark.k.weight": "model_big.safetensors",
341
+ "blocks.24.landmark.o.weight": "model_big.safetensors",
342
+ "blocks.24.landmark.q.weight": "model_big.safetensors",
343
+ "blocks.24.landmark.v.weight": "model_big.safetensors",
344
+ "blocks.24.n1.bias": "model_big.safetensors",
345
+ "blocks.24.n1.weight": "model_big.safetensors",
346
+ "blocks.24.n2.bias": "model_big.safetensors",
347
+ "blocks.24.n2.weight": "model_big.safetensors",
348
+ "blocks.25.attn.b.bias": "model_big.safetensors",
349
+ "blocks.25.attn.b.weight": "model_big.safetensors",
350
+ "blocks.25.attn.k.weight": "model_big.safetensors",
351
+ "blocks.25.attn.o.weight": "model_big.safetensors",
352
+ "blocks.25.attn.q.weight": "model_big.safetensors",
353
+ "blocks.25.attn.v.weight": "model_big.safetensors",
354
+ "blocks.25.ff.0.bias": "model_big.safetensors",
355
+ "blocks.25.ff.0.weight": "model_big.safetensors",
356
+ "blocks.25.ff.2.bias": "model_big.safetensors",
357
+ "blocks.25.ff.2.weight": "model_big.safetensors",
358
+ "blocks.25.landmark.gate": "model_big.safetensors",
359
+ "blocks.25.landmark.k.weight": "model_big.safetensors",
360
+ "blocks.25.landmark.o.weight": "model_big.safetensors",
361
+ "blocks.25.landmark.q.weight": "model_big.safetensors",
362
+ "blocks.25.landmark.v.weight": "model_big.safetensors",
363
+ "blocks.25.n1.bias": "model_big.safetensors",
364
+ "blocks.25.n1.weight": "model_big.safetensors",
365
+ "blocks.25.n2.bias": "model_big.safetensors",
366
+ "blocks.25.n2.weight": "model_big.safetensors",
367
+ "blocks.26.attn.b.bias": "model_big.safetensors",
368
+ "blocks.26.attn.b.weight": "model_big.safetensors",
369
+ "blocks.26.attn.k.weight": "model_big.safetensors",
370
+ "blocks.26.attn.o.weight": "model_big.safetensors",
371
+ "blocks.26.attn.q.weight": "model_big.safetensors",
372
+ "blocks.26.attn.v.weight": "model_big.safetensors",
373
+ "blocks.26.ff.0.bias": "model_big.safetensors",
374
+ "blocks.26.ff.0.weight": "model_big.safetensors",
375
+ "blocks.26.ff.2.bias": "model_big.safetensors",
376
+ "blocks.26.ff.2.weight": "model_big.safetensors",
377
+ "blocks.26.landmark.gate": "model_big.safetensors",
378
+ "blocks.26.landmark.k.weight": "model_big.safetensors",
379
+ "blocks.26.landmark.o.weight": "model_big.safetensors",
380
+ "blocks.26.landmark.q.weight": "model_big.safetensors",
381
+ "blocks.26.landmark.v.weight": "model_big.safetensors",
382
+ "blocks.26.n1.bias": "model_big.safetensors",
383
+ "blocks.26.n1.weight": "model_big.safetensors",
384
+ "blocks.26.n2.bias": "model_big.safetensors",
385
+ "blocks.26.n2.weight": "model_big.safetensors",
386
+ "blocks.27.attn.b.bias": "model_big.safetensors",
387
+ "blocks.27.attn.b.weight": "model_big.safetensors",
388
+ "blocks.27.attn.k.weight": "model_big.safetensors",
389
+ "blocks.27.attn.o.weight": "model_big.safetensors",
390
+ "blocks.27.attn.q.weight": "model_big.safetensors",
391
+ "blocks.27.attn.v.weight": "model_big.safetensors",
392
+ "blocks.27.ff.0.bias": "model_big.safetensors",
393
+ "blocks.27.ff.0.weight": "model_big.safetensors",
394
+ "blocks.27.ff.2.bias": "model_big.safetensors",
395
+ "blocks.27.ff.2.weight": "model_big.safetensors",
396
+ "blocks.27.landmark.gate": "model_big.safetensors",
397
+ "blocks.27.landmark.k.weight": "model_big.safetensors",
398
+ "blocks.27.landmark.o.weight": "model_big.safetensors",
399
+ "blocks.27.landmark.q.weight": "model_big.safetensors",
400
+ "blocks.27.landmark.v.weight": "model_big.safetensors",
401
+ "blocks.27.n1.bias": "model_big.safetensors",
402
+ "blocks.27.n1.weight": "model_big.safetensors",
403
+ "blocks.27.n2.bias": "model_big.safetensors",
404
+ "blocks.27.n2.weight": "model_big.safetensors",
405
+ "blocks.3.attn.b.bias": "model_big.safetensors",
406
+ "blocks.3.attn.b.weight": "model_big.safetensors",
407
+ "blocks.3.attn.k.weight": "model_big.safetensors",
408
+ "blocks.3.attn.o.weight": "model_big.safetensors",
409
+ "blocks.3.attn.q.weight": "model_big.safetensors",
410
+ "blocks.3.attn.v.weight": "model_big.safetensors",
411
+ "blocks.3.ff.0.bias": "model_big.safetensors",
412
+ "blocks.3.ff.0.weight": "model_big.safetensors",
413
+ "blocks.3.ff.2.bias": "model_big.safetensors",
414
+ "blocks.3.ff.2.weight": "model_big.safetensors",
415
+ "blocks.3.landmark.gate": "model_big.safetensors",
416
+ "blocks.3.landmark.k.weight": "model_big.safetensors",
417
+ "blocks.3.landmark.o.weight": "model_big.safetensors",
418
+ "blocks.3.landmark.q.weight": "model_big.safetensors",
419
+ "blocks.3.landmark.v.weight": "model_big.safetensors",
420
+ "blocks.3.n1.bias": "model_big.safetensors",
421
+ "blocks.3.n1.weight": "model_big.safetensors",
422
+ "blocks.3.n2.bias": "model_big.safetensors",
423
+ "blocks.3.n2.weight": "model_big.safetensors",
424
+ "blocks.4.attn.b.bias": "model_big.safetensors",
425
+ "blocks.4.attn.b.weight": "model_big.safetensors",
426
+ "blocks.4.attn.k.weight": "model_big.safetensors",
427
+ "blocks.4.attn.o.weight": "model_big.safetensors",
428
+ "blocks.4.attn.q.weight": "model_big.safetensors",
429
+ "blocks.4.attn.v.weight": "model_big.safetensors",
430
+ "blocks.4.ff.0.bias": "model_big.safetensors",
431
+ "blocks.4.ff.0.weight": "model_big.safetensors",
432
+ "blocks.4.ff.2.bias": "model_big.safetensors",
433
+ "blocks.4.ff.2.weight": "model_big.safetensors",
434
+ "blocks.4.landmark.gate": "model_big.safetensors",
435
+ "blocks.4.landmark.k.weight": "model_big.safetensors",
436
+ "blocks.4.landmark.o.weight": "model_big.safetensors",
437
+ "blocks.4.landmark.q.weight": "model_big.safetensors",
438
+ "blocks.4.landmark.v.weight": "model_big.safetensors",
439
+ "blocks.4.n1.bias": "model_big.safetensors",
440
+ "blocks.4.n1.weight": "model_big.safetensors",
441
+ "blocks.4.n2.bias": "model_big.safetensors",
442
+ "blocks.4.n2.weight": "model_big.safetensors",
443
+ "blocks.5.attn.b.bias": "model_big.safetensors",
444
+ "blocks.5.attn.b.weight": "model_big.safetensors",
445
+ "blocks.5.attn.k.weight": "model_big.safetensors",
446
+ "blocks.5.attn.o.weight": "model_big.safetensors",
447
+ "blocks.5.attn.q.weight": "model_big.safetensors",
448
+ "blocks.5.attn.v.weight": "model_big.safetensors",
449
+ "blocks.5.ff.0.bias": "model_big.safetensors",
450
+ "blocks.5.ff.0.weight": "model_big.safetensors",
451
+ "blocks.5.ff.2.bias": "model_big.safetensors",
452
+ "blocks.5.ff.2.weight": "model_big.safetensors",
453
+ "blocks.5.landmark.gate": "model_big.safetensors",
454
+ "blocks.5.landmark.k.weight": "model_big.safetensors",
455
+ "blocks.5.landmark.o.weight": "model_big.safetensors",
456
+ "blocks.5.landmark.q.weight": "model_big.safetensors",
457
+ "blocks.5.landmark.v.weight": "model_big.safetensors",
458
+ "blocks.5.n1.bias": "model_big.safetensors",
459
+ "blocks.5.n1.weight": "model_big.safetensors",
460
+ "blocks.5.n2.bias": "model_big.safetensors",
461
+ "blocks.5.n2.weight": "model_big.safetensors",
462
+ "blocks.6.attn.b.bias": "model_big.safetensors",
463
+ "blocks.6.attn.b.weight": "model_big.safetensors",
464
+ "blocks.6.attn.k.weight": "model_big.safetensors",
465
+ "blocks.6.attn.o.weight": "model_big.safetensors",
466
+ "blocks.6.attn.q.weight": "model_big.safetensors",
467
+ "blocks.6.attn.v.weight": "model_big.safetensors",
468
+ "blocks.6.ff.0.bias": "model_big.safetensors",
469
+ "blocks.6.ff.0.weight": "model_big.safetensors",
470
+ "blocks.6.ff.2.bias": "model_big.safetensors",
471
+ "blocks.6.ff.2.weight": "model_big.safetensors",
472
+ "blocks.6.landmark.gate": "model_big.safetensors",
473
+ "blocks.6.landmark.k.weight": "model_big.safetensors",
474
+ "blocks.6.landmark.o.weight": "model_big.safetensors",
475
+ "blocks.6.landmark.q.weight": "model_big.safetensors",
476
+ "blocks.6.landmark.v.weight": "model_big.safetensors",
477
+ "blocks.6.n1.bias": "model_big.safetensors",
478
+ "blocks.6.n1.weight": "model_big.safetensors",
479
+ "blocks.6.n2.bias": "model_big.safetensors",
480
+ "blocks.6.n2.weight": "model_big.safetensors",
481
+ "blocks.7.attn.b.bias": "model_big.safetensors",
482
+ "blocks.7.attn.b.weight": "model_big.safetensors",
483
+ "blocks.7.attn.k.weight": "model_big.safetensors",
484
+ "blocks.7.attn.o.weight": "model_big.safetensors",
485
+ "blocks.7.attn.q.weight": "model_big.safetensors",
486
+ "blocks.7.attn.v.weight": "model_big.safetensors",
487
+ "blocks.7.ff.0.bias": "model_big.safetensors",
488
+ "blocks.7.ff.0.weight": "model_big.safetensors",
489
+ "blocks.7.ff.2.bias": "model_big.safetensors",
490
+ "blocks.7.ff.2.weight": "model_big.safetensors",
491
+ "blocks.7.landmark.gate": "model_big.safetensors",
492
+ "blocks.7.landmark.k.weight": "model_big.safetensors",
493
+ "blocks.7.landmark.o.weight": "model_big.safetensors",
494
+ "blocks.7.landmark.q.weight": "model_big.safetensors",
495
+ "blocks.7.landmark.v.weight": "model_big.safetensors",
496
+ "blocks.7.n1.bias": "model_big.safetensors",
497
+ "blocks.7.n1.weight": "model_big.safetensors",
498
+ "blocks.7.n2.bias": "model_big.safetensors",
499
+ "blocks.7.n2.weight": "model_big.safetensors",
500
+ "blocks.8.attn.b.bias": "model_big.safetensors",
501
+ "blocks.8.attn.b.weight": "model_big.safetensors",
502
+ "blocks.8.attn.k.weight": "model_big.safetensors",
503
+ "blocks.8.attn.o.weight": "model_big.safetensors",
504
+ "blocks.8.attn.q.weight": "model_big.safetensors",
505
+ "blocks.8.attn.v.weight": "model_big.safetensors",
506
+ "blocks.8.ff.0.bias": "model_big.safetensors",
507
+ "blocks.8.ff.0.weight": "model_big.safetensors",
508
+ "blocks.8.ff.2.bias": "model_big.safetensors",
509
+ "blocks.8.ff.2.weight": "model_big.safetensors",
510
+ "blocks.8.landmark.gate": "model_big.safetensors",
511
+ "blocks.8.landmark.k.weight": "model_big.safetensors",
512
+ "blocks.8.landmark.o.weight": "model_big.safetensors",
513
+ "blocks.8.landmark.q.weight": "model_big.safetensors",
514
+ "blocks.8.landmark.v.weight": "model_big.safetensors",
515
+ "blocks.8.n1.bias": "model_big.safetensors",
516
+ "blocks.8.n1.weight": "model_big.safetensors",
517
+ "blocks.8.n2.bias": "model_big.safetensors",
518
+ "blocks.8.n2.weight": "model_big.safetensors",
519
+ "blocks.9.attn.b.bias": "model_big.safetensors",
520
+ "blocks.9.attn.b.weight": "model_big.safetensors",
521
+ "blocks.9.attn.k.weight": "model_big.safetensors",
522
+ "blocks.9.attn.o.weight": "model_big.safetensors",
523
+ "blocks.9.attn.q.weight": "model_big.safetensors",
524
+ "blocks.9.attn.v.weight": "model_big.safetensors",
525
+ "blocks.9.ff.0.bias": "model_big.safetensors",
526
+ "blocks.9.ff.0.weight": "model_big.safetensors",
527
+ "blocks.9.ff.2.bias": "model_big.safetensors",
528
+ "blocks.9.ff.2.weight": "model_big.safetensors",
529
+ "blocks.9.landmark.gate": "model_big.safetensors",
530
+ "blocks.9.landmark.k.weight": "model_big.safetensors",
531
+ "blocks.9.landmark.o.weight": "model_big.safetensors",
532
+ "blocks.9.landmark.q.weight": "model_big.safetensors",
533
+ "blocks.9.landmark.v.weight": "model_big.safetensors",
534
+ "blocks.9.n1.bias": "model_big.safetensors",
535
+ "blocks.9.n1.weight": "model_big.safetensors",
536
+ "blocks.9.n2.bias": "model_big.safetensors",
537
+ "blocks.9.n2.weight": "model_big.safetensors",
538
+ "cls_head.bias": "model_big.safetensors",
539
+ "cls_head.weight": "model_big.safetensors",
540
+ "cls_label_emb.weight": "model_big.safetensors",
541
+ "feat_enc.bias": "model_big.safetensors",
542
+ "feat_enc.weight": "model_big.safetensors",
543
+ "norm.bias": "model_big.safetensors",
544
+ "norm.weight": "model_big.safetensors",
545
+ "reg_head.bias": "model_big.safetensors",
546
+ "reg_head.weight": "model_big.safetensors",
547
+ "reg_label.bias": "model_big.safetensors",
548
+ "reg_label.weight": "model_big.safetensors",
549
+ "reg_label_gate": "model_big.safetensors",
550
+ "type_emb.weight": "model_big.safetensors"
551
+ }
552
+ }
model_big.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d9560e0d033172ab26ff43e94bceffc7a9277d2b64031ae3b554011e71414b47
3
+ size 1647506252
model_big_int8.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e210edaff81b0c23876722f10bb6fe49eb8ff237fa61f2fe81946da9010f2411
3
+ size 415479124
model_small.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0a448bb4aef008af34730e09a8c2aad3d599751e65b178b83226d6a1ac56c586
3
+ size 206272140
model_small_int8.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6018b2ec8968751d47c8ba30178b13d99e10df6fd61c71c2b08893b054416ffc
3
+ size 52336084
modeling.py ADDED
@@ -0,0 +1,404 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import json
3
+ import math
4
+ import os
5
+ from dataclasses import dataclass
6
+ import numpy as np
7
+ import torch
8
+ import torch.nn as nn
9
+ import torch.nn.functional as F
10
+
11
+
12
+ @dataclass
13
+ class FelaTabConfig:
14
+ max_features: int = 100
15
+ max_classes: int = 10
16
+ dim: int = 1024
17
+ n_layers: int = 28
18
+ n_heads: int = 16
19
+ head_dim: int = 64
20
+ chunk: int = 64
21
+ ffn_mult: int = 3
22
+ dropout: float = 0.0
23
+ use_landmark: bool = True
24
+ n_landmarks: int = 48
25
+ ln_eps: float = 1e-05
26
+
27
+ def __post_init__(self):
28
+ assert self.dim % self.n_heads == 0 or self.n_heads * self.head_dim > 0
29
+
30
+
31
+ def delta_rule_chunk(q, k, v, beta, chunk):
32
+ B, H, T, D = q.shape
33
+ dev, dt = (q.device, q.dtype)
34
+ pad = (chunk - T % chunk) % chunk
35
+ if pad:
36
+ q = F.pad(q, (0, 0, 0, pad))
37
+ k = F.pad(k, (0, 0, 0, pad))
38
+ v = F.pad(v, (0, 0, 0, pad))
39
+ beta = F.pad(beta, (0, pad))
40
+ Tp = T + pad
41
+ nC = Tp // chunk
42
+ q = q.view(B, H, nC, chunk, D)
43
+ k = k.view(B, H, nC, chunk, D)
44
+ v = v.view(B, H, nC, chunk, D)
45
+ beta = beta.view(B, H, nC, chunk)
46
+ tri = torch.tril(torch.ones(chunk, chunk, device=dev, dtype=dt), -1)
47
+ eye = torch.eye(chunk, device=dev, dtype=dt)
48
+ S = torch.zeros(B, H, D, D, device=dev, dtype=dt)
49
+ outs = []
50
+ for c in range(nC):
51
+ kc = k[:, :, c]
52
+ vc = v[:, :, c]
53
+ qc = q[:, :, c]
54
+ bc = beta[:, :, c]
55
+ KK = torch.einsum("bhtd,bhsd->bhts", kc, kc)
56
+ A = bc.unsqueeze(-1) * KK * tri
57
+ kS0 = torch.einsum("bhtd,bhde->bhte", kc, S)
58
+ RHS = bc.unsqueeze(-1) * (vc - kS0)
59
+ M = eye + A
60
+ U = torch.linalg.solve_triangular(M, RHS, upper=False, unitriangular=True)
61
+ qS0 = torch.einsum("bhtd,bhde->bhte", qc, S)
62
+ QK = torch.einsum("bhtd,bhsd->bhts", qc, kc) * tri
63
+ o = qS0 + torch.einsum("bhts,bhse->bhte", QK, U)
64
+ outs.append(o)
65
+ S = S + torch.einsum("bhtd,bhte->bhde", kc, U)
66
+ o = torch.cat(outs, dim=2).view(B, H, Tp, D)[:, :, :T]
67
+ return (o, S)
68
+
69
+
70
+ def pool_landmarks(x, wm, G):
71
+ B, T, C = x.shape
72
+ ns = wm.sum(1).clamp(min=1)
73
+ gs = torch.ceil(ns / G).clamp(min=1)
74
+ pos = torch.arange(T, device=x.device)[None, :].expand(B, T)
75
+ gid = torch.minimum((pos / gs[:, None]).floor().long(), torch.full_like(pos, G - 1))
76
+ valid = wm > 0
77
+ flat = (torch.arange(B, device=x.device)[:, None] * G + gid)[valid]
78
+ src = x[valid]
79
+ land = torch.zeros(B * G, C, device=x.device, dtype=x.dtype)
80
+ cnt = torch.zeros(B * G, device=x.device, dtype=x.dtype)
81
+ land.index_add_(0, flat, src)
82
+ cnt.index_add_(0, flat, torch.ones(src.shape[0], device=x.device, dtype=x.dtype))
83
+ land = land / cnt.clamp(min=1)[:, None]
84
+ return (land.view(B, G, C), (cnt > 0).view(B, G))
85
+
86
+
87
+ class DeltaAttn(nn.Module):
88
+ def __init__(self, cfg):
89
+ super().__init__()
90
+ self.H, self.D, self.chunk = (cfg.n_heads, cfg.head_dim, cfg.chunk)
91
+ inner = cfg.n_heads * cfg.head_dim
92
+ self.q = nn.Linear(cfg.dim, inner, bias=False)
93
+ self.k = nn.Linear(cfg.dim, inner, bias=False)
94
+ self.v = nn.Linear(cfg.dim, inner, bias=False)
95
+ self.b = nn.Linear(cfg.dim, cfg.n_heads)
96
+ self.o = nn.Linear(inner, cfg.dim, bias=False)
97
+
98
+ def forward(self, x, write_mask):
99
+ B, T, _ = x.shape
100
+ H, D = (self.H, self.D)
101
+ q = self.q(x).view(B, T, H, D).transpose(1, 2)
102
+ k = self.k(x).view(B, T, H, D).transpose(1, 2)
103
+ v = self.v(x).view(B, T, H, D).transpose(1, 2)
104
+ q = F.normalize(q, dim=-1)
105
+ k = F.normalize(k, dim=-1)
106
+ beta = torch.sigmoid(self.b(x)).transpose(1, 2)
107
+ beta = beta * write_mask.unsqueeze(1)
108
+ o, _ = delta_rule_chunk(q, k, v, beta, self.chunk)
109
+ o = o.transpose(1, 2).reshape(B, T, H * D)
110
+ return self.o(o)
111
+
112
+
113
+ class LandmarkAttn(nn.Module):
114
+ def __init__(self, cfg):
115
+ super().__init__()
116
+ self.H, self.D, self.G = (cfg.n_heads, cfg.head_dim, cfg.n_landmarks)
117
+ inner = cfg.n_heads * cfg.head_dim
118
+ self.q = nn.Linear(cfg.dim, inner, bias=False)
119
+ self.k = nn.Linear(cfg.dim, inner, bias=False)
120
+ self.v = nn.Linear(cfg.dim, inner, bias=False)
121
+ self.o = nn.Linear(inner, cfg.dim, bias=False)
122
+ self.gate = nn.Parameter(torch.zeros(1))
123
+
124
+ def forward(self, x, wm):
125
+ B, T, _ = x.shape
126
+ H, D, G = (self.H, self.D, self.G)
127
+ land, lmask = pool_landmarks(x, wm, G)
128
+ q = self.q(x).view(B, T, H, D).transpose(1, 2)
129
+ k = self.k(land).view(B, G, H, D).transpose(1, 2)
130
+ v = self.v(land).view(B, G, H, D).transpose(1, 2)
131
+ att = torch.einsum("bhtd,bhgd->bhtg", q, k) / math.sqrt(D)
132
+ att = att.masked_fill(~lmask[:, None, None, :], -1000000000.0)
133
+ att = att.softmax(-1)
134
+ o = torch.einsum("bhtg,bhgd->bhtd", att, v).transpose(1, 2).reshape(B, T, H * D)
135
+ return self.o(o) * torch.tanh(self.gate)
136
+
137
+
138
+ class Block(nn.Module):
139
+ def __init__(self, cfg):
140
+ super().__init__()
141
+ self.n1 = nn.LayerNorm(cfg.dim, eps=cfg.ln_eps)
142
+ self.attn = DeltaAttn(cfg)
143
+ self.landmark = LandmarkAttn(cfg) if cfg.use_landmark else None
144
+ self.n2 = nn.LayerNorm(cfg.dim, eps=cfg.ln_eps)
145
+ hid = cfg.dim * cfg.ffn_mult
146
+ self.ff = nn.Sequential(
147
+ nn.Linear(cfg.dim, hid), nn.GELU(), nn.Linear(hid, cfg.dim)
148
+ )
149
+
150
+ def forward(self, x, wm):
151
+ h = self.n1(x)
152
+ a = self.attn(h, wm)
153
+ if self.landmark is not None:
154
+ a = a + self.landmark(h, wm)
155
+ x = x + a
156
+ x = x + self.ff(self.n2(x))
157
+ return x
158
+
159
+
160
+ class FelaTab(nn.Module):
161
+ def __init__(self, cfg: FelaTabConfig):
162
+ super().__init__()
163
+ self.cfg = cfg
164
+ self.feat_enc = nn.Linear(cfg.max_features, cfg.dim)
165
+ self.cls_label_emb = nn.Embedding(cfg.max_classes + 1, cfg.dim)
166
+ self.reg_label = nn.Linear(1, cfg.dim)
167
+ self.reg_label_gate = nn.Parameter(torch.ones(1))
168
+ self.type_emb = nn.Embedding(2, cfg.dim)
169
+ self.blocks = nn.ModuleList([Block(cfg) for _ in range(cfg.n_layers)])
170
+ self.norm = nn.LayerNorm(cfg.dim, eps=cfg.ln_eps)
171
+ self.cls_head = nn.Linear(cfg.dim, cfg.max_classes)
172
+ self.reg_head = nn.Linear(cfg.dim, 2)
173
+
174
+ def encode_tokens(self, X, y, n_support, task_type_id):
175
+ B, T, _ = X.shape
176
+ tok = self.feat_enc(X)
177
+ tok = tok + self.type_emb(task_type_id).view(1, 1, -1)
178
+ idx = torch.arange(T, device=X.device).unsqueeze(0)
179
+ wm = (idx < n_support.unsqueeze(1)).float()
180
+ if int(task_type_id) == 0:
181
+ yl = y.clamp(min=0).long()
182
+ lab = self.cls_label_emb(yl) * wm.unsqueeze(-1)
183
+ tok = tok + lab
184
+ else:
185
+ lab = (
186
+ self.reg_label(y.unsqueeze(-1).float())
187
+ * wm.unsqueeze(-1)
188
+ * self.reg_label_gate
189
+ )
190
+ tok = tok + lab
191
+ return (tok, wm)
192
+
193
+ def forward(self, X, y, n_support, task_type_id):
194
+ tok, wm = self.encode_tokens(X, y, n_support, task_type_id)
195
+ h = tok
196
+ for blk in self.blocks:
197
+ h = blk(h, wm)
198
+ h = self.norm(h)
199
+ if int(task_type_id) == 0:
200
+ return self.cls_head(h)
201
+ return self.reg_head(h)
202
+
203
+
204
+ _CONFIG_FIELDS = set(FelaTabConfig.__dataclass_fields__.keys())
205
+ TIERS = {
206
+ "big": dict(dim=1024, n_layers=28, n_heads=16, head_dim=64),
207
+ "small": dict(dim=512, n_layers=14, n_heads=8, head_dim=64),
208
+ }
209
+
210
+
211
+ def _to_config(cfg_dict: dict) -> FelaTabConfig:
212
+ return FelaTabConfig(**{k: v for k, v in cfg_dict.items() if k in _CONFIG_FIELDS})
213
+
214
+
215
+ def _read_json(path: str) -> dict:
216
+ with open(path) as f:
217
+ return json.load(f)
218
+
219
+
220
+ def _dequant_state_dict(tensors: dict) -> dict:
221
+ sd = {}
222
+ for k, v in tensors.items():
223
+ if k.endswith(".scale"):
224
+ continue
225
+ if v.dtype == torch.int8:
226
+ scale = tensors[k + ".scale"]
227
+ sd[k] = v.float() * scale[:, None]
228
+ else:
229
+ sd[k] = v.float()
230
+ return sd
231
+
232
+
233
+ def load_model(path_or_repo: str = ".", tier: str = "big", config: dict = None):
234
+ from safetensors.torch import load_file
235
+
236
+ def _pick(dir_path):
237
+ for base in (
238
+ f"model_{tier}_int8.safetensors",
239
+ f"model_{tier}.safetensors",
240
+ "model_int8.safetensors",
241
+ "model.safetensors",
242
+ ):
243
+ p = os.path.join(dir_path, base)
244
+ if os.path.isfile(p):
245
+ return p
246
+ raise FileNotFoundError(f"no model safetensors for tier '{tier}' in {dir_path}")
247
+
248
+ def _cfg(dir_path):
249
+ for base in (f"config_{tier}.json", "config.json"):
250
+ p = os.path.join(dir_path, base)
251
+ if os.path.isfile(p):
252
+ return _read_json(p)
253
+ return dict(TIERS[tier])
254
+
255
+ if os.path.isfile(path_or_repo) and path_or_repo.endswith(".safetensors"):
256
+ weights_path = path_or_repo
257
+ cfg_dict = config or _cfg(os.path.dirname(path_or_repo) or ".")
258
+ elif os.path.isdir(path_or_repo):
259
+ weights_path = _pick(path_or_repo)
260
+ cfg_dict = config or _cfg(path_or_repo)
261
+ else:
262
+ from huggingface_hub import hf_hub_download
263
+
264
+ cfg_dict = config
265
+ if cfg_dict is None:
266
+ for base in (f"config_{tier}.json", "config.json"):
267
+ try:
268
+ cfg_dict = _read_json(hf_hub_download(path_or_repo, base))
269
+ break
270
+ except Exception:
271
+ continue
272
+ cfg_dict = cfg_dict or dict(TIERS[tier])
273
+ weights_path = None
274
+ for base in (f"model_{tier}_int8.safetensors", f"model_{tier}.safetensors"):
275
+ try:
276
+ weights_path = hf_hub_download(path_or_repo, base)
277
+ break
278
+ except Exception:
279
+ continue
280
+ if weights_path is None:
281
+ raise FileNotFoundError(
282
+ f"no model_{tier}[_int8].safetensors in repo {path_or_repo}"
283
+ )
284
+ for k, val in TIERS[tier].items():
285
+ cfg_dict.setdefault(k, val)
286
+ tensors = load_file(weights_path)
287
+ sd = _dequant_state_dict(tensors)
288
+ model = FelaTab(_to_config(cfg_dict))
289
+ model.load_state_dict(sd)
290
+ model.eval()
291
+ return model
292
+
293
+
294
+ from_pretrained = load_model
295
+
296
+
297
+ def _standardize_fit(Xtr: np.ndarray):
298
+ mean = Xtr.mean(axis=0)
299
+ scale = np.sqrt(((Xtr - mean) ** 2).mean(axis=0))
300
+ scale[scale == 0] = 1.0
301
+ return (mean.astype(np.float32), scale.astype(np.float32))
302
+
303
+
304
+ def _prep(model, Xtr, Xte):
305
+ cfg = model.cfg
306
+ Xtr = np.asarray(Xtr, dtype=np.float32)
307
+ Xte = np.asarray(Xte, dtype=np.float32)
308
+ mean, scale = _standardize_fit(Xtr)
309
+ Xtr2 = (Xtr - mean) / scale
310
+ Xte2 = (Xte - mean) / scale
311
+ F_ = cfg.max_features
312
+ if Xtr2.shape[1] > F_:
313
+ try:
314
+ from sklearn.decomposition import PCA
315
+
316
+ p = PCA(n_components=F_, random_state=0).fit(Xtr2)
317
+ Xtr2, Xte2 = (p.transform(Xtr2), p.transform(Xte2))
318
+ except Exception:
319
+ Xtr2, Xte2 = (Xtr2[:, :F_], Xte2[:, :F_])
320
+
321
+ def pad(A):
322
+ out = np.zeros((A.shape[0], F_), np.float32)
323
+ out[:, : min(A.shape[1], F_)] = A[:, :F_]
324
+ return out
325
+
326
+ return (np.nan_to_num(pad(Xtr2)), np.nan_to_num(pad(Xte2)))
327
+
328
+
329
+ @torch.no_grad()
330
+ def predict(
331
+ model, Xtr, ytr, Xte, task="classification", n_classes=None, support_cap=3000
332
+ ):
333
+ is_cls = task.startswith("cls") or task == "classification"
334
+ Xtr = np.asarray(Xtr, dtype=np.float32)
335
+ ytr = np.asarray(ytr)
336
+ Xte = np.asarray(Xte, dtype=np.float32)
337
+ if len(Xtr) > support_cap:
338
+ idx = np.random.RandomState(0).permutation(len(Xtr))[:support_cap]
339
+ Xtr, ytr = (Xtr[idx], ytr[idx])
340
+ Xtr2, Xte2 = _prep(model, Xtr, Xte)
341
+ ns = len(Xtr2)
342
+ X = np.concatenate([Xtr2, Xte2], 0)[None]
343
+ if is_cls:
344
+ ncls = int(n_classes if n_classes is not None else ytr.max() + 1)
345
+ y = np.concatenate([ytr, np.zeros(len(Xte2))]).astype(np.int64)[None]
346
+ ttype = 0
347
+ else:
348
+ ymu, ysd = (float(ytr.mean()), float(ytr.std() + 1e-06))
349
+ y = np.concatenate([(ytr - ymu) / ysd, np.zeros(len(Xte2))]).astype(np.float32)[
350
+ None
351
+ ]
352
+ ttype = 1
353
+ dev = next(model.parameters()).device
354
+ Xt = torch.from_numpy(X).to(dev)
355
+ yt = torch.from_numpy(y).to(dev)
356
+ nst = torch.tensor([ns]).to(dev)
357
+ out = model(Xt, yt, nst, torch.tensor(ttype, device=dev)).float()
358
+ q = out[0, ns:]
359
+ if is_cls:
360
+ return F.softmax(q[:, :ncls], dim=-1).cpu().numpy()
361
+ mean = q[:, 0].cpu().numpy() * ysd + ymu
362
+ std = np.sqrt(np.exp(np.clip(q[:, 1].cpu().numpy(), -8, 8))) * ysd
363
+ return (mean, std)
364
+
365
+
366
+ def predict_bagged(
367
+ model,
368
+ Xtr,
369
+ ytr,
370
+ Xte,
371
+ task="classification",
372
+ n_classes=None,
373
+ support_cap=3000,
374
+ n_bag=16,
375
+ base_seed=1000,
376
+ ):
377
+ is_cls = task.startswith("cls") or task == "classification"
378
+ if not is_cls or n_bag <= 1:
379
+ return predict(model, Xtr, ytr, Xte, task, n_classes, support_cap)
380
+ Xtr = np.asarray(Xtr, dtype=np.float32)
381
+ ytr = np.asarray(ytr)
382
+ Xte = np.asarray(Xte, dtype=np.float32)
383
+ ncls = int(n_classes if n_classes is not None else ytr.max() + 1)
384
+ acc = []
385
+ for b in range(n_bag):
386
+ rng = np.random.RandomState(base_seed + b)
387
+ rp = rng.permutation(len(Xtr))
388
+ cperm = rng.permutation(Xtr.shape[1])
389
+ acc.append(
390
+ predict(
391
+ model,
392
+ Xtr[rp][:, cperm],
393
+ ytr[rp],
394
+ Xte[:, cperm],
395
+ "classification",
396
+ ncls,
397
+ support_cap,
398
+ )
399
+ )
400
+ return np.mean(acc, axis=0)
401
+
402
+
403
+ def count_params(m):
404
+ return sum((p.numel() for p in m.parameters()))
modeling_felatab.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from transformers import PreTrainedModel
3
+ from transformers.modeling_outputs import SequenceClassifierOutput
4
+
5
+ from .configuration_felatab import FelaTabConfig as _HFConfig
6
+ from .modeling import FelaTab
7
+ from .modeling import FelaTabConfig as _CoreConfig
8
+
9
+ _FIELDS = set(_CoreConfig.__dataclass_fields__.keys())
10
+
11
+
12
+ class FelaTabModel(PreTrainedModel):
13
+ config_class = _HFConfig
14
+ base_model_prefix = "model"
15
+
16
+ def __init__(self, config):
17
+ super().__init__(config)
18
+ cfg = _CoreConfig(
19
+ **{k: getattr(config, k) for k in _FIELDS if hasattr(config, k)}
20
+ )
21
+ self.model = FelaTab(cfg)
22
+ self.post_init()
23
+
24
+ def forward(self, X=None, y=None, n_support=None, task_type_id=None, **kwargs):
25
+ if task_type_id is None:
26
+ task_type_id = torch.tensor(0, device=X.device)
27
+ logits = self.model(X, y, n_support, task_type_id)
28
+ return SequenceClassifierOutput(logits=logits)
prior.py ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from dataclasses import dataclass
3
+
4
+ ACTS = ["relu", "tanh", "sin", "abs", "square", "identity", "sigmoid"]
5
+
6
+
7
+ def _act(name, x):
8
+ if name == "relu":
9
+ return np.maximum(x, 0.0)
10
+ if name == "tanh":
11
+ return np.tanh(x)
12
+ if name == "sin":
13
+ return np.sin(x)
14
+ if name == "abs":
15
+ return np.abs(x)
16
+ if name == "square":
17
+ return np.square(x) - 1.0
18
+ if name == "sigmoid":
19
+ return 1.0 / (1.0 + np.exp(-np.clip(x, -30, 30)))
20
+ return x
21
+
22
+
23
+ @dataclass
24
+ class PriorConfig:
25
+ max_features: int = 100
26
+ min_features: int = 2
27
+ max_classes: int = 10
28
+ min_rows: int = 128
29
+ max_rows: int = 2048
30
+ reg_frac: float = 0.35
31
+ cat_frac: float = 0.35
32
+ irrelevant_frac: float = 0.25
33
+ max_depth: int = 5
34
+ max_width: int = 32
35
+ noise_scale: float = 0.3
36
+
37
+
38
+ def sample_task(cfg: PriorConfig, rng: np.random.Generator):
39
+ F = int(rng.integers(cfg.min_features, cfg.max_features + 1))
40
+ N = int(rng.integers(cfg.min_rows, cfg.max_rows + 1))
41
+ is_reg = rng.random() < cfg.reg_frac
42
+ d_latent = int(rng.integers(2, 12))
43
+ h = rng.standard_normal((N, d_latent)).astype(np.float32)
44
+ if rng.random() < 0.3:
45
+ h = h * rng.gamma(2.0, 0.5, size=(1, d_latent)).astype(np.float32)
46
+ depth = int(rng.integers(2, cfg.max_depth + 1))
47
+ nodes = [h]
48
+ cur = h
49
+ for _ in range(depth):
50
+ w = int(rng.integers(4, cfg.max_width + 1))
51
+ W = rng.standard_normal((cur.shape[1], w)).astype(np.float32)
52
+ sparsity = rng.uniform(0.3, 0.9)
53
+ W *= rng.random(W.shape) > sparsity
54
+ W /= np.sqrt(np.maximum(1.0, (W != 0).sum(0, keepdims=True)))
55
+ b = rng.standard_normal((1, w)).astype(np.float32) * rng.uniform(0, 1.0)
56
+ pre = cur @ W + b
57
+ acts = rng.choice(ACTS, size=w)
58
+ post = np.empty_like(pre)
59
+ for j in range(w):
60
+ post[:, j] = _act(acts[j], pre[:, j])
61
+ if rng.random() < 0.3 and w >= 2:
62
+ i1, i2 = rng.integers(0, w, size=2)
63
+ post[:, i1] = post[:, i1] * post[:, i2]
64
+ post = np.clip(post, -30.0, 30.0)
65
+ nodes.append(post)
66
+ cur = post
67
+ allnodes = np.concatenate(nodes, axis=1)
68
+ total = allnodes.shape[1]
69
+ tgt_idx = int(rng.integers(max(0, total - cur.shape[1]), total))
70
+ target_raw = allnodes[:, tgt_idx].astype(np.float32)
71
+ k_mix = int(rng.integers(1, min(6, total)))
72
+ mix_idx = rng.choice(total, size=k_mix, replace=False)
73
+ mix_w = rng.standard_normal(k_mix).astype(np.float32)
74
+ target_raw = allnodes[:, mix_idx] @ mix_w
75
+ target_raw = target_raw + cfg.noise_scale * rng.standard_normal(N).astype(
76
+ np.float32
77
+ ) * (target_raw.std() + 1e-06)
78
+ feat_pool = [i for i in range(total) if i not in set(mix_idx.tolist())]
79
+ if len(feat_pool) == 0:
80
+ feat_pool = list(range(total))
81
+ n_real = min(F, len(feat_pool))
82
+ chosen = rng.choice(feat_pool, size=n_real, replace=False)
83
+ X = allnodes[:, chosen].astype(np.float32)
84
+ if X.shape[1] < F:
85
+ n_noise = F - X.shape[1]
86
+ noise_cols = rng.standard_normal((N, n_noise)).astype(np.float32)
87
+ X = np.concatenate([X, noise_cols], axis=1)
88
+ n_irr = int(rng.uniform(0, cfg.irrelevant_frac) * F)
89
+ if n_irr > 0:
90
+ irr = rng.choice(F, size=min(n_irr, F), replace=False)
91
+ X[:, irr] = rng.standard_normal((N, len(irr))).astype(np.float32)
92
+ perm = rng.permutation(F)
93
+ X = X[:, perm]
94
+ for c in range(F):
95
+ if rng.random() < cfg.cat_frac:
96
+ n_lvl = int(rng.integers(2, 10))
97
+ qs = np.quantile(X[:, c], np.linspace(0, 1, n_lvl + 1)[1:-1])
98
+ X[:, c] = np.digitize(X[:, c], qs).astype(np.float32)
99
+ X = X + rng.standard_normal(X.shape).astype(np.float32) * cfg.noise_scale * 0.3
100
+ X = np.nan_to_num(X, nan=0.0, posinf=0.0, neginf=0.0)
101
+ mu = X.mean(0, keepdims=True)
102
+ sd = X.std(0, keepdims=True) + 1e-06
103
+ X = (X - mu) / sd
104
+ if is_reg:
105
+ y = np.nan_to_num(target_raw, nan=0.0, posinf=0.0, neginf=0.0)
106
+ y = (y - y.mean()) / (y.std() + 1e-06)
107
+ y = y.astype(np.float32)
108
+ n_classes = 0
109
+ task_type = "reg"
110
+ else:
111
+ K = int(rng.integers(2, cfg.max_classes + 1))
112
+ t = np.nan_to_num(target_raw, nan=0.0, posinf=0.0, neginf=0.0)
113
+ if rng.random() < 0.5:
114
+ qs = np.quantile(t, np.linspace(0, 1, K + 1)[1:-1])
115
+ y = np.digitize(t, qs).astype(np.int64)
116
+ else:
117
+ scores = allnodes @ rng.standard_normal((total, K)).astype(np.float32)
118
+ y = scores.argmax(1).astype(np.int64)
119
+ uniq = np.unique(y)
120
+ if len(uniq) < 2:
121
+ y = (t > np.median(t)).astype(np.int64)
122
+ uniq = np.unique(y)
123
+ remap = {u: i for i, u in enumerate(uniq)}
124
+ y = np.array([remap[v] for v in y], dtype=np.int64)
125
+ n_classes = int(len(uniq))
126
+ task_type = "cls"
127
+ feat_mask = np.ones(F, dtype=np.float32)
128
+ return {
129
+ "X": X,
130
+ "y": y,
131
+ "n_features": F,
132
+ "n_classes": n_classes,
133
+ "task_type": task_type,
134
+ "feat_mask": feat_mask,
135
+ }
136
+
137
+
138
+ def sample_batch(cfg, rng, n_tasks):
139
+ return [sample_task(cfg, rng) for _ in range(n_tasks)]
140
+
141
+
142
+ def _worker(args):
143
+ seed, n, cfg = args
144
+ rng = np.random.default_rng(seed)
145
+ return sample_batch(cfg, rng, n)
146
+
147
+
148
+ def gen_pool(cfg, n_tasks, seed=0, workers=None):
149
+ import multiprocessing as mp
150
+
151
+ if workers is None:
152
+ workers = min(mp.cpu_count(), 64)
153
+ per = max(1, n_tasks // workers)
154
+ jobs = [(seed + i, per, cfg) for i in range(workers)]
155
+ rem = n_tasks - per * workers
156
+ if rem > 0:
157
+ jobs.append((seed + workers, rem, cfg))
158
+ with mp.Pool(workers) as pool:
159
+ outs = pool.map(_worker, jobs)
160
+ tasks = [t for sub in outs for t in sub]
161
+ return tasks[:n_tasks]
162
+
163
+
164
+ if __name__ == "__main__":
165
+ cfg = PriorConfig()
166
+ rng = np.random.default_rng(0)
167
+ print("=== 8 sampled synthetic tasks ===")
168
+ kinds = {"cls": 0, "reg": 0}
169
+ for i in range(8):
170
+ t = sample_task(cfg, rng)
171
+ kinds[t["task_type"]] += 1
172
+ if t["task_type"] == "cls":
173
+ _, cnts = np.unique(t["y"], return_counts=True)
174
+ bal = f"class_counts={cnts.tolist()}"
175
+ else:
176
+ bal = f"y_range=[{t['y'].min():.2f},{t['y'].max():.2f}] std={t['y'].std():.2f}"
177
+ print(
178
+ f"[{i}] {t['task_type']:3s} N={t['X'].shape[0]:5d} F={t['n_features']:3d} K={t['n_classes']:2d} Xstd={t['X'].std():.2f} Xmean={t['X'].mean():+.3f} {bal}"
179
+ )
180
+ print("task-type mix:", kinds)
181
+ import time
182
+
183
+ t0 = time.time()
184
+ tasks = sample_batch(cfg, rng, 500)
185
+ dt = time.time() - t0
186
+ Fs = [t["n_features"] for t in tasks]
187
+ Ns = [t["X"].shape[0] for t in tasks]
188
+ Ks = [t["n_classes"] for t in tasks if t["task_type"] == "cls"]
189
+ regf = np.mean([t["task_type"] == "reg" for t in tasks])
190
+ print(f"\n500 tasks in {dt:.2f}s ({500 / dt:.0f} tasks/s single-core)")
191
+ print(f"F: min={min(Fs)} max={max(Fs)} mean={np.mean(Fs):.1f}")
192
+ print(f"N: min={min(Ns)} max={max(Ns)} mean={np.mean(Ns):.0f}")
193
+ print(f"K(cls): min={min(Ks)} max={max(Ks)} mean={np.mean(Ks):.1f}")
194
+ print(f"regression fraction: {regf:.2f}")
quickstart/README.md ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Quickstart
2
+
3
+ Point FelaTab at a real public table and get zero shot predictions on CPU. Uses the self
4
+ contained loader and in context predict path in `../modeling.py`.
5
+
6
+ ## Steps
7
+
8
+ 1. Install the pinned requirements (CPU PyTorch + scikit-learn for the example datasets):
9
+
10
+ ```bash
11
+ pip install -r requirements.txt
12
+ ```
13
+
14
+ 2. Run. The model files ship in the parent directory, so the default `--weights` points there:
15
+
16
+ ```bash
17
+ python run.py # small int8 tier, breast cancer classification
18
+ python run.py --tier big # the 411.9M flagship
19
+ python run.py --dataset wine --bag 8 # with inference time bagging
20
+ ```
21
+
22
+ ## Load from Python
23
+
24
+ ```python
25
+ from modeling import load_model, predict, predict_bagged
26
+
27
+ # tier = "small" (dim512, ~51.6M) or "big" (dim1024, ~411.9M). The loader auto detects
28
+ # the int8 bundle and dequantizes it, so you always get a plain fp32 model.
29
+ model = load_model("/path/to/repo_dir", tier="small") # or a HF repo id, or a .safetensors path
30
+
31
+ # classification: Xtr/ytr are the support rows (your labelled examples), Xte the query rows
32
+ probs = predict(model, Xtr, ytr, Xte, task="classification", n_classes=3) # [n_query, 3]
33
+
34
+ # regression: returns (mean, std) error bars in the original label units
35
+ mean, std = predict(model, Xtr, ytr, Xte, task="regression")
36
+
37
+ # optional: inference time bagging lifts classification a little (no retraining)
38
+ probs = predict_bagged(model, Xtr, ytr, Xte, task="classification", n_classes=3, n_bag=16)
39
+ ```
40
+
41
+ There is no per table training. The support rows and query rows are packed into one sequence and
42
+ run through the model in a single forward pass; the query predictions come out calibrated. See
43
+ the model card for the honest measured numbers, the regression caveat, and the intended use.
quickstart/requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ torch==2.8.0
2
+ numpy==2.1.3
3
+ safetensors==0.5.3
4
+ huggingface-hub==0.34.4
5
+ scikit-learn==1.5.2
quickstart/run.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import os
3
+ import sys
4
+ import numpy as np
5
+
6
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
7
+ from modeling import load_model, predict, predict_bagged
8
+
9
+
10
+ def main():
11
+ ap = argparse.ArgumentParser()
12
+ ap.add_argument(
13
+ "--weights",
14
+ default=os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
15
+ help="repo dir with model_<tier>[_int8].safetensors + config, or a .safetensors path",
16
+ )
17
+ ap.add_argument("--tier", default="small", choices=["small", "big"])
18
+ ap.add_argument(
19
+ "--dataset", default="breast_cancer", choices=["breast_cancer", "wine", "iris"]
20
+ )
21
+ ap.add_argument(
22
+ "--bag", type=int, default=1, help="inference-bagging passes (classification)"
23
+ )
24
+ args = ap.parse_args()
25
+ from sklearn.model_selection import train_test_split
26
+ from sklearn.datasets import load_breast_cancer, load_wine, load_iris
27
+
28
+ loader = {
29
+ "breast_cancer": load_breast_cancer,
30
+ "wine": load_wine,
31
+ "iris": load_iris,
32
+ }[args.dataset]
33
+ d = loader()
34
+ X, y = (d.data.astype(np.float32), d.target.astype(np.int64))
35
+ names = list(getattr(d, "target_names", [str(i) for i in range(int(y.max()) + 1)]))
36
+ Xtr, Xte, ytr, yte = train_test_split(
37
+ X, y, test_size=0.3, random_state=0, stratify=y
38
+ )
39
+ model = load_model(args.weights, tier=args.tier)
40
+ ncls = int(y.max() + 1)
41
+ print(
42
+ f"dataset={args.dataset} support rows={len(Xtr)} query rows={len(Xte)} features={X.shape[1]} classes={ncls} tier={args.tier}"
43
+ )
44
+ if args.bag > 1:
45
+ probs = predict_bagged(
46
+ model, Xtr, ytr, Xte, "classification", n_classes=ncls, n_bag=args.bag
47
+ )
48
+ else:
49
+ probs = predict(model, Xtr, ytr, Xte, "classification", n_classes=ncls)
50
+ acc = float((probs.argmax(1) == yte).mean())
51
+ print(f"zero-shot accuracy: {acc:.3f} (in-context, no training)")
52
+ i = 0
53
+ p = probs[i]
54
+ print(
55
+ f"example query row 0 -> predicted '{names[p.argmax()]}' ({p.max() * 100:.1f}% confidence); true '{names[yte[i]]}'"
56
+ )
57
+ print(
58
+ " full class probabilities: "
59
+ + ", ".join((f"{names[c]}={p[c] * 100:.1f}%" for c in range(ncls)))
60
+ )
61
+ Xr = np.delete(X, 0, axis=1).astype(np.float32)
62
+ yr = X[:, 0].astype(np.float32)
63
+ Xr_tr, Xr_te, yr_tr, yr_te = train_test_split(Xr, yr, test_size=0.3, random_state=0)
64
+ mean, std = predict(model, Xr_tr, yr_tr, Xr_te, "regression")
65
+ print(
66
+ f"regression demo (predict feature 0): example -> mean {mean[0]:.3f} +/- {std[0]:.3f} (true {yr_te[0]:.3f}); this is the honest Gaussian error bar"
67
+ )
68
+
69
+
70
+ if __name__ == "__main__":
71
+ main()
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ torch==2.8.0
2
+ numpy==2.1.3
3
+ safetensors==0.5.3
4
+ huggingface-hub==0.34.4
space/README.md ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: FelaTab playground
3
+ colorFrom: indigo
4
+ colorTo: green
5
+ sdk: gradio
6
+ sdk_version: 4.44.0
7
+ app_file: app.py
8
+ pinned: false
9
+ license: other
10
+ ---
11
+
12
+ # FelaTab playground
13
+
14
+ Paste a small labelled table and a row to predict. FelaTab learns the pattern from your example
15
+ rows in a single forward pass, with no training or setup, and fills the answer with a
16
+ confidence range. Runs on CPU.
17
+
18
+ ## How to use
19
+
20
+ Put your data as comma or space separated rows, one row per line, with a header line. The last
21
+ column is the label. Use `?` in the label column for the rows you want predicted.
22
+
23
+ - If the label column holds categories (e.g. Apple / Lemon / Grape), it predicts a class with
24
+ probabilities (classification).
25
+ - If the label column holds numbers, it predicts a value with an error bar (regression).
26
+
27
+ ## Which model
28
+
29
+ The Space loads the small tier by default (dim512, about 51.6M parameters, int8, roughly 52 MB).
30
+ Set the environment variable `FELATAB_TIER=big` to load the flagship (about 411.9M parameters).
31
+
32
+ ## Honest scope
33
+
34
+ This is a research preview. FelaTab matches or slightly trails a tuned gradient boosted tree on
35
+ zero shot classification and is behind trees on regression accuracy (it ships calibrated error
36
+ bars for regression rather than headline accuracy). See the model card for the measured numbers.
space/app.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import gradio as gr
4
+ import numpy as np
5
+
6
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
7
+ from modeling import load_model, predict
8
+
9
+ WEIGHTS = os.environ.get(
10
+ "FELATAB_WEIGHTS", os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
11
+ )
12
+ TIER = os.environ.get("FELATAB_TIER", "small")
13
+ try:
14
+ MODEL = load_model(WEIGHTS, tier=TIER)
15
+ except Exception as e:
16
+ MODEL = None
17
+ _ERR = str(e)
18
+ EXAMPLE = "weight_g, sugar_pct, width_mm, label\n151, 7.2, 12, Apple\n149, 7.0, 13, Apple\n158, 7.4, 11, Apple\n102, 6.1, 3, Lemon\n98, 5.9, 2, Lemon\n110, 6.3, 4, Lemon\n6, 2.1, 16, Grape\n5, 1.9, 17, Grape\n7, 2.2, 15, Grape\n153, 7.1, 12, ?"
19
+
20
+
21
+ def _parse(text):
22
+ rows = [r.strip() for r in text.strip().splitlines() if r.strip()]
23
+ header = [c.strip() for c in rows[0].replace(",", " ").split()]
24
+ data = [[c.strip() for c in r.replace(",", " ").split()] for r in rows[1:]]
25
+ return (header, data)
26
+
27
+
28
+ def run(text):
29
+ if MODEL is None:
30
+ return f"Weights not found ({_ERR}). Set FELATAB_WEIGHTS to the repo directory."
31
+ try:
32
+ header, data = _parse(text)
33
+ label_col = len(header) - 1
34
+ feats, labels, queries = ([], [], [])
35
+ for r in data:
36
+ xs = [float(v) for v in r[:label_col]]
37
+ lab = r[label_col]
38
+ if lab in ("?", "", "-"):
39
+ queries.append(xs)
40
+ else:
41
+ feats.append(xs)
42
+ labels.append(lab)
43
+ if not feats or not queries:
44
+ return (
45
+ "Provide labelled support rows and at least one query row (label '?')."
46
+ )
47
+ except Exception as e:
48
+ return f"Could not parse the table: {e}"
49
+ Xtr = np.array(feats, dtype=np.float32)
50
+ Xte = np.array(queries, dtype=np.float32)
51
+ classes = sorted(set(labels))
52
+ is_cls = not all((_is_num(l) for l in labels))
53
+ if is_cls:
54
+ cidx = {c: i for i, c in enumerate(classes)}
55
+ ytr = np.array([cidx[l] for l in labels], dtype=np.int64)
56
+ probs = predict(MODEL, Xtr, ytr, Xte, "classification", n_classes=len(classes))
57
+ out = []
58
+ for qi, p in enumerate(probs):
59
+ top = int(p.argmax())
60
+ dist = " ".join(
61
+ (f"{classes[c]}={p[c] * 100:.0f}%" for c in range(len(classes)))
62
+ )
63
+ out.append(
64
+ f"query {qi + 1}: {classes[top]} ({p[top] * 100:.0f}% confidence)\n {dist}"
65
+ )
66
+ return "\n".join(out)
67
+ else:
68
+ ytr = np.array([float(l) for l in labels], dtype=np.float32)
69
+ mean, std = predict(MODEL, Xtr, ytr, Xte, "regression")
70
+ return "\n".join(
71
+ (
72
+ f"query {i + 1}: {mean[i]:.2f} (likely within +/- {1.645 * std[i]:.2f})"
73
+ for i in range(len(mean))
74
+ )
75
+ )
76
+
77
+
78
+ def _is_num(s):
79
+ try:
80
+ float(s)
81
+ return True
82
+ except Exception:
83
+ return False
84
+
85
+
86
+ with gr.Blocks(title="FelaTab playground") as demo:
87
+ gr.Markdown(
88
+ "# FelaTab playground\nPaste a small labelled table and a row to predict. The model learns the pattern from your examples in a single pass, with no training or setup, and fills the answer with a confidence range. Put `?` in the label column for the rows you want predicted. If the label column is numbers, it predicts a value with an error bar (regression); if it is categories, it predicts a class with probabilities. Runs on CPU."
89
+ )
90
+ inp = gr.Textbox(
91
+ label="your table (comma or space separated, last column = label, '?' = predict)",
92
+ lines=12,
93
+ value=EXAMPLE,
94
+ )
95
+ out = gr.Textbox(label="prediction", lines=8)
96
+ with gr.Row():
97
+ gr.Button("Load example").click(lambda: EXAMPLE, outputs=inp)
98
+ gr.Button("Predict").click(run, inputs=inp, outputs=out)
99
+ if __name__ == "__main__":
100
+ demo.launch()
space/requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ torch==2.8.0
2
+ numpy==2.1.3
3
+ safetensors==0.5.3
4
+ huggingface-hub==0.34.4
5
+ scikit-learn==1.5.2
6
+ gradio==4.44.0
train.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import math
3
+ import os
4
+ import time
5
+ import numpy as np
6
+ import torch
7
+ import torch.nn.functional as F
8
+ from modeling import FelaTab, FelaTabConfig, count_params
9
+ from prior import PriorConfig, gen_pool
10
+
11
+
12
+ def collate(tasks, task_type, tmax, rng, maxF, s_frac=(0.5, 0.9)):
13
+ B = len(tasks)
14
+ used, sup = ([], [])
15
+ for t in tasks:
16
+ N = t["X"].shape[0]
17
+ n = min(N, tmax)
18
+ frac = rng.uniform(*s_frac)
19
+ s = int(np.clip(int(frac * n), 16, n - 8))
20
+ used.append(n)
21
+ sup.append(s)
22
+ T = max(used)
23
+ X = np.zeros((B, T, maxF), np.float32)
24
+ y = np.zeros((B, T), np.int64 if task_type == "cls" else np.float32)
25
+ lm = np.zeros((B, T), np.float32)
26
+ ncl = np.zeros(B, np.int64)
27
+ ns = np.zeros(B, np.int64)
28
+ for b, t in enumerate(tasks):
29
+ N = t["X"].shape[0]
30
+ n = used[b]
31
+ s = sup[b]
32
+ Ft = min(t["X"].shape[1], maxF)
33
+ perm = rng.permutation(N)[:n]
34
+ X[b, :n, :Ft] = t["X"][perm][:, :Ft]
35
+ y[b, :n] = t["y"][perm]
36
+ lm[b, s:n] = 1.0
37
+ ns[b] = s
38
+ ncl[b] = t["n_classes"] if task_type == "cls" else 0
39
+ return (
40
+ torch.from_numpy(X),
41
+ torch.from_numpy(y),
42
+ torch.from_numpy(ns),
43
+ torch.from_numpy(lm),
44
+ torch.from_numpy(ncl),
45
+ )
46
+
47
+
48
+ def cls_loss_acc(logits, y, lm, ncl):
49
+ B, T, C = logits.shape
50
+ cmask = torch.arange(C, device=logits.device).view(1, C) < ncl.view(B, 1)
51
+ logits = logits.masked_fill(~cmask.unsqueeze(1), -1000000000.0)
52
+ lf = logits.reshape(B * T, C)
53
+ yf = y.reshape(B * T).clamp(min=0)
54
+ mf = lm.reshape(B * T).bool()
55
+ if mf.sum() == 0:
56
+ return (logits.sum() * 0.0, 0.0)
57
+ loss = F.cross_entropy(lf[mf], yf[mf])
58
+ acc = (lf[mf].argmax(-1) == yf[mf]).float().mean().item()
59
+ return (loss, acc)
60
+
61
+
62
+ def reg_loss_r2(out, y, lm):
63
+ mean = out[..., 0]
64
+ logvar = out[..., 1].clamp(-8, 8)
65
+ mf = lm.bool()
66
+ if mf.sum() == 0:
67
+ return (out.sum() * 0.0, 0.0)
68
+ m, lv, t = (mean[mf], logvar[mf], y[mf])
69
+ nll = 0.5 * (lv + (t - m) ** 2 / torch.exp(lv) + math.log(2 * math.pi))
70
+ ss_res = ((t - m) ** 2).sum().item()
71
+ ss_tot = ((t - t.mean()) ** 2).sum().item() + 1e-08
72
+ return (nll.mean(), 1 - ss_res / ss_tot)
73
+
74
+
75
+ def main():
76
+ ap = argparse.ArgumentParser()
77
+ ap.add_argument("--steps", type=int, default=20000)
78
+ ap.add_argument("--bs", type=int, default=16)
79
+ ap.add_argument("--tmax", type=int, default=1024)
80
+ ap.add_argument("--lr", type=float, default=0.0003)
81
+ ap.add_argument("--dim", type=int, default=512)
82
+ ap.add_argument("--layers", type=int, default=14)
83
+ ap.add_argument("--heads", type=int, default=8)
84
+ ap.add_argument("--head_dim", type=int, default=64)
85
+ ap.add_argument("--pool", type=int, default=1000)
86
+ ap.add_argument("--refresh", type=int, default=400)
87
+ ap.add_argument("--ckpt", type=str, default="ckpt/felatab.pt")
88
+ ap.add_argument(
89
+ "--smoke", action="store_true", help="tiny fast loop to prove it trains"
90
+ )
91
+ args = ap.parse_args()
92
+ if args.smoke:
93
+ args.steps, args.dim, args.layers, args.heads = (200, 128, 4, 4)
94
+ args.pool, args.bs, args.tmax = (128, 8, 256)
95
+ dev = "cuda" if torch.cuda.is_available() else "cpu"
96
+ prior_cfg = PriorConfig(max_classes=10, max_rows=6000)
97
+ cfg = FelaTabConfig(
98
+ dim=args.dim,
99
+ n_layers=args.layers,
100
+ n_heads=args.heads,
101
+ head_dim=args.head_dim,
102
+ max_features=prior_cfg.max_features,
103
+ max_classes=prior_cfg.max_classes,
104
+ )
105
+ model = FelaTab(cfg).to(dev)
106
+ print(
107
+ f"params {count_params(model) / 1000000.0:.2f}M dim={cfg.dim} L={cfg.n_layers} H={cfg.n_heads} dev={dev}"
108
+ )
109
+ opt = torch.optim.AdamW(
110
+ model.parameters(), lr=args.lr, weight_decay=0.01, betas=(0.9, 0.95)
111
+ )
112
+ sched = torch.optim.lr_scheduler.OneCycleLR(
113
+ opt,
114
+ max_lr=args.lr,
115
+ total_steps=args.steps,
116
+ pct_start=0.03,
117
+ anneal_strategy="cos",
118
+ )
119
+ rng = np.random.default_rng(1234)
120
+
121
+ def refill(seed):
122
+ pool = gen_pool(prior_cfg, args.pool, seed=seed, workers=None)
123
+ return (
124
+ [t for t in pool if t["task_type"] == "cls"],
125
+ [t for t in pool if t["task_type"] == "reg"],
126
+ )
127
+
128
+ print("generating initial synthetic pool...")
129
+ t0 = time.time()
130
+ cls_pool, reg_pool = refill(int(rng.integers(1000000000.0)))
131
+ print(
132
+ f"pool ready {time.time() - t0:.1f}s (cls {len(cls_pool)} reg {len(reg_pool)})"
133
+ )
134
+ model.train()
135
+ run_loss = run_metric = 0.0
136
+ for step in range(1, args.steps + 1):
137
+ typ = "cls" if step % 2 == 0 else "reg"
138
+ src = cls_pool if typ == "cls" else reg_pool
139
+ if not src:
140
+ continue
141
+ T_target = int(rng.integers(128, args.tmax + 1))
142
+ idx = rng.integers(0, len(src), size=args.bs)
143
+ batch = [src[i] for i in idx]
144
+ X, y, ns, lm, ncl = collate(batch, typ, T_target, rng, cfg.max_features)
145
+ X, y, ns, lm, ncl = [z.to(dev) for z in (X, y, ns, lm, ncl)]
146
+ out = model(X, y, ns, torch.tensor(0 if typ == "cls" else 1, device=dev))
147
+ if typ == "cls":
148
+ loss, metric = cls_loss_acc(out.float(), y, lm, ncl)
149
+ else:
150
+ loss, metric = reg_loss_r2(out.float(), y, lm)
151
+ opt.zero_grad()
152
+ loss.backward()
153
+ torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
154
+ opt.step()
155
+ sched.step()
156
+ run_loss += loss.item()
157
+ run_metric += metric
158
+ if step % 50 == 0:
159
+ print(
160
+ f"step {step:6d} {typ} loss {run_loss / 50:.4f} metric {run_metric / 50:.4f} lr {sched.get_last_lr()[0]:.2e}",
161
+ flush=True,
162
+ )
163
+ run_loss = run_metric = 0.0
164
+ if step % args.refresh == 0:
165
+ cls_pool, reg_pool = refill(int(rng.integers(1000000000.0)))
166
+ os.makedirs(os.path.dirname(args.ckpt) or ".", exist_ok=True)
167
+ torch.save(model.state_dict(), args.ckpt)
168
+ print(f"done. saved {args.ckpt}")
169
+
170
+
171
+ if __name__ == "__main__":
172
+ main()
verify.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import os
3
+ import sys
4
+ import numpy as np
5
+
6
+ sys.path.insert(0, os.path.dirname(__file__))
7
+ from modeling import load_model, predict
8
+
9
+
10
+ def make_cls(seed=1234, ns=48, nq=4, nf=6, k=3):
11
+ rng = np.random.default_rng(seed)
12
+ W = rng.standard_normal((nf, k)).astype(np.float32)
13
+ X = rng.standard_normal((ns + nq, nf)).astype(np.float32) * 1.6 + 0.2
14
+ y = (X @ W).argmax(1).astype(np.int64)
15
+ return (X[:ns], y[:ns], X[ns:], k)
16
+
17
+
18
+ def make_reg(seed=5678, ns=48, nq=4, nf=6):
19
+ rng = np.random.default_rng(seed)
20
+ w = rng.standard_normal(nf).astype(np.float32)
21
+ X = rng.standard_normal((ns + nq, nf)).astype(np.float32) * 2.0 - 0.3
22
+ y = (X @ w + 0.1 * rng.standard_normal(ns + nq)).astype(np.float32)
23
+ return (X[:ns], y[:ns], X[ns:])
24
+
25
+
26
+ REF_CLS_PROBS = [
27
+ [0.67778, 0.29555, 0.02667],
28
+ [0.43088, 0.44604, 0.12308],
29
+ [0.8469, 0.11382, 0.03928],
30
+ [0.34364, 0.60266, 0.0537],
31
+ ]
32
+ REF_REG_MEAN = [-3.22947, -1.90098, 0.24437, -3.92093]
33
+ TOL_PROB = 0.06
34
+ TOL_REG = 0.15
35
+
36
+
37
+ def main():
38
+ ap = argparse.ArgumentParser()
39
+ ap.add_argument("--weights", default=None, help="explicit .safetensors path")
40
+ ap.add_argument("--tier", default="small", choices=["small", "big"])
41
+ args = ap.parse_args()
42
+ src = args.weights or "."
43
+ model = load_model(src, tier=args.tier)
44
+ Xc_tr, yc_tr, Xc_te, k = make_cls()
45
+ Xr_tr, yr_tr, Xr_te = make_reg()
46
+ probs = predict(model, Xc_tr, yc_tr, Xc_te, "classification", n_classes=k)
47
+ mean, std = predict(model, Xr_tr, yr_tr, Xr_te, "regression")
48
+ ok = True
49
+ if probs.shape != (4, 3):
50
+ print(f"FAILURE: cls output shape {probs.shape} != (4, 3)")
51
+ sys.exit(1)
52
+ if mean.shape != (4,) or std.shape != (4,):
53
+ print(f"FAILURE: reg shapes mean {mean.shape} std {std.shape} != (4,)")
54
+ sys.exit(1)
55
+ print(f"Shapes OK: cls {probs.shape}, reg mean {mean.shape} std {std.shape}")
56
+ if not np.all(std > 0):
57
+ print(f"FAILURE: regression std not all positive: {std.tolist()}")
58
+ sys.exit(1)
59
+ if args.tier == "small":
60
+ d_cls = float(np.abs(probs - np.array(REF_CLS_PROBS)).max())
61
+ scale = float(np.std(REF_REG_MEAN)) + 1e-06
62
+ d_reg = float(np.abs(mean - np.array(REF_REG_MEAN)).max()) / scale
63
+ print(f"cls max abs prob diff = {d_cls:.4f} (tol {TOL_PROB})")
64
+ print(f"reg max rel mean diff = {d_reg:.4f} (tol {TOL_REG})")
65
+ if d_cls > TOL_PROB:
66
+ print("FAILURE: classification probabilities off reference")
67
+ ok = False
68
+ if d_reg > TOL_REG:
69
+ print("FAILURE: regression means off reference")
70
+ ok = False
71
+ else:
72
+ print(
73
+ "(reference stored for the small tier only; big tier ran a shape + sanity check)"
74
+ )
75
+ print("example cls probs[0]:", np.round(probs[0], 4).tolist())
76
+ print("example reg mean/std[0]:", round(float(mean[0]), 4), round(float(std[0]), 4))
77
+ print("VERIFY:", "PASS" if ok else "FAIL")
78
+ sys.exit(0 if ok else 1)
79
+
80
+
81
+ if __name__ == "__main__":
82
+ main()