harrrshall commited on
Commit
66ccdde
·
verified ·
1 Parent(s): 4cd637e

BarunLM-35M: 35M-parameter base language model trained on 5.7B tokens

Browse files
.gitattributes CHANGED
@@ -1,35 +1 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
  *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  *.safetensors filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
CITATION.cff ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ cff-version: 1.2.0
2
+ message: "If you use BarunLM-35M, please cite this release."
3
+ title: "BarunLM-35M"
4
+ type: software
5
+ version: 1.0.0
6
+ date-released: 2026-07-31
7
+ authors:
8
+ - family-names: Singh
9
+ given-names: Harshal
10
+ repository-code: "https://github.com/harrrshall/barunlm-35m"
11
+ url: "https://huggingface.co/harrrshall/BarunLM-35M"
12
+ license: Apache-2.0
LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2026 Harshal Singh
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
NOTICE ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ BarunLM-35M
2
+ Copyright 2026 Harshal Singh
3
+
4
+ The BarunLM source code and released model weights are licensed under the
5
+ Apache License 2.0. Training datasets are not redistributed with this release
6
+ and retain their own licenses and terms:
7
+
8
+ - HuggingFaceTB/smollm-corpus (FineWeb-Edu and Cosmopedia v2), revision
9
+ 3ba9d605774198c5868892d7a8deda78031a781f: ODC-By 1.0. FineWeb-derived
10
+ material is also subject to the applicable Common Crawl terms.
11
+ - HuggingFaceTB/finemath (FineMath-4+), revision
12
+ e92b25a616738fe95dc186b64dfb19f9c8525594: ODC-By 1.0 and Common Crawl
13
+ terms of use.
14
+ - mlfoundations/dclm-baseline-1.0-parquet, revision
15
+ 817d6752765f6a41261085171dd546b104f60626: CC BY 4.0. The dataset card
16
+ describes DCLM-Baseline as a research baseline.
17
+ - code-search-net/code_search_net (Python), revision
18
+ bd0cf261e357a3eb5c8fba490d23ec1a1cd59555: source repositories retain
19
+ their individual licenses.
20
+ - codeparrot/codeparrot-clean, revision
21
+ 35a59fb025bc0a102f7d96eac09d145b896d487b: source files retain the
22
+ repository-level licenses recorded by the dataset.
23
+
24
+ Users are responsible for reviewing the upstream dataset cards and terms for
25
+ their intended use. This notice is informational and does not replace those
26
+ terms.
README.md ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ language:
4
+ - en
5
+ pipeline_tag: text-generation
6
+ library_name: pytorch
7
+ tags:
8
+ - base-model
9
+ - causal-lm
10
+ - small-language-model
11
+ - text-generation
12
+ datasets:
13
+ - HuggingFaceTB/smollm-corpus
14
+ - HuggingFaceTB/finemath
15
+ - mlfoundations/dclm-baseline-1.0-parquet
16
+ - code-search-net/code_search_net
17
+ - codeparrot/codeparrot-clean
18
+ ---
19
+
20
+ # BarunLM-35M
21
+
22
+ BarunLM-35M is a compact, decoder-only base language model built for strong
23
+ capability per parameter. With exactly **35,072,768 parameters**, it reaches
24
+ **41.01%** on a fixed, decontaminated nine-task zero-shot suite and exceeds
25
+ [LFM2.5-230M-Base](https://huggingface.co/LiquidAI/LFM2.5-230M-Base) while
26
+ using **6.55× fewer parameters**.
27
+
28
+ [Download the model](https://huggingface.co/harrrshall/BarunLM-35M) ·
29
+ [Inspect benchmark data](benchmark_results.json) · [License](LICENSE)
30
+
31
+ ## Highlights
32
+
33
+ - **Parameter-efficient hybrid attention.** Three local-attention layers feed
34
+ each full-attention layer, combining a 256-token working window with periodic
35
+ global information exchange.
36
+ - **Selective residual routing.** A learned convex selector every four layers
37
+ chooses between the block-group input and its transformed state, providing a
38
+ lightweight path for preserving useful representations.
39
+ - **Stable small-model optimization.** Grouped-query attention, partial RoPE,
40
+ QK normalization, gated attention outputs, and bounded SwiGLU are integrated
41
+ into a single 35M-parameter design.
42
+ - **Capacity-aligned pretraining.** The model was trained on 5.70B tokens—about
43
+ 162.5 tokens per parameter—from a curated blend of educational web text,
44
+ synthetic exposition, mathematics, general web text, and code.
45
+ - **Contamination-aware evaluation.** Reported scores exclude 1,854 samples
46
+ identified by a correctness-blind exact 13-token scan over the complete
47
+ training history.
48
+
49
+ ## Results
50
+
51
+ All rows below were evaluated zero-shot with LM Evaluation Harness 0.4.12 on
52
+ ARC-Challenge, ARC-Easy, BoolQ, HellaSwag, LAMBADA OpenAI, OpenBookQA, PIQA,
53
+ SciQ, and WinoGrande. The reported value is the unweighted macro average after
54
+ applying the same frozen decontamination decisions to every model.
55
+
56
+ | Model | Loaded parameters | Macro accuracy | BarunLM lead |
57
+ |---|---:|---:|---:|
58
+ | **BarunLM-35M** | **35.1M** | **41.01%** | — |
59
+ | [LFM2.5-230M-Base](https://huggingface.co/LiquidAI/LFM2.5-230M-Base) | 229.7M | 39.20% | **+1.81 pp** |
60
+ | [Pythia-160M](https://huggingface.co/EleutherAI/pythia-160m-deduped) | 162.3M | 37.35% | **+3.66 pp** |
61
+ | [Stentor-30M](https://huggingface.co/StentorLabs/Stentor-30M) | 30.4M | 36.46% | **+4.55 pp** |
62
+ | [TinyStories-33M](https://huggingface.co/roneneldan/TinyStories-33M)¹ | 68.5M | 33.16% | **+7.85 pp** |
63
+ | [Pythia-70M](https://huggingface.co/EleutherAI/pythia-70m-deduped) | 70.4M | 31.71% | **+9.30 pp** |
64
+
65
+ The paired 10,000-resample bootstrap interval for the BarunLM minus LFM2.5
66
+ macro difference is **[+0.92, +2.71] percentage points**. Exact revisions,
67
+ task scores, confidence intervals, sample counts, and evidence hashes are in
68
+ [`benchmark_results.json`](benchmark_results.json).
69
+
70
+ ¹ TinyStories is included as a narrow-domain diagnostic rather than a
71
+ general-purpose peer. Parameter counts are computed from the loaded models,
72
+ not inferred from repository names.
73
+
74
+ These results establish parameter efficiency on this evaluation suite; they
75
+ do not imply universal superiority across tasks or deployment settings.
76
+
77
+ ## Architecture
78
+
79
+ | Component | Configuration |
80
+ |---|---|
81
+ | Parameters | 35,072,768 |
82
+ | Layers / width | 12 / 448 |
83
+ | Attention | 7 query heads, 1 key/value head |
84
+ | Attention rhythm | 3 local layers, then 1 full layer |
85
+ | Local window | 256 tokens |
86
+ | Position encoding | 50% partial RoPE |
87
+ | Feed-forward width | 1,228 |
88
+ | Residual selection | Every 4 layers |
89
+ | Vocabulary | 16,384 byte-level BPE tokens |
90
+ | Context length | 2,048 tokens |
91
+ | Embeddings | Input/output weights tied |
92
+
93
+ The design treats global attention as a periodic communication layer rather
94
+ than a cost paid at every depth. Local layers concentrate computation on nearby
95
+ structure, while the global layer propagates information across the sequence.
96
+ The residual selector then gives each four-layer group a learned preservation
97
+ path with negligible parameter overhead.
98
+
99
+ ## Quick start
100
+
101
+ BarunLM uses a small native PyTorch implementation. It is a completion model,
102
+ not a chat model, and does not require `trust_remote_code`.
103
+
104
+ For the shortest path from this model repository:
105
+
106
+ ```bash
107
+ hf download harrrshall/BarunLM-35M --local-dir BarunLM-35M
108
+ cd BarunLM-35M
109
+ python -m venv .venv
110
+ source .venv/bin/activate
111
+ pip install -r requirements.txt
112
+ python generate.py \
113
+ --prompt "The future of efficient language models is" \
114
+ --max-new-tokens 48 \
115
+ --temperature 0.8
116
+ ```
117
+
118
+ The maintained source package and tests are available in the
119
+ [GitHub repository](https://github.com/harrrshall/barunlm-35m):
120
+
121
+ ```bash
122
+ git clone https://github.com/harrrshall/barunlm-35m.git
123
+ cd barunlm-35m
124
+ python -m venv .venv
125
+ source .venv/bin/activate
126
+ pip install -e .
127
+ python examples/generate.py \
128
+ --prompt "The future of efficient language models is" \
129
+ --max-new-tokens 48 \
130
+ --temperature 0.8
131
+ ```
132
+
133
+ Both examples verify the three required model artifacts and select CUDA
134
+ automatically when available. For deterministic greedy decoding, pass
135
+ `--temperature 0`.
136
+
137
+ To download the release without running it:
138
+
139
+ ```bash
140
+ hf download harrrshall/BarunLM-35M --local-dir BarunLM-35M
141
+ sha256sum -c BarunLM-35M/SHA256SUMS
142
+ ```
143
+
144
+ ## Training
145
+
146
+ BarunLM-35M was pretrained on **5,699,985,408 realized tokens** at sequence
147
+ length 2,048. The final 4B-token continuation used Muon with a peak learning
148
+ rate of `1e-4`, weight decay `0.1`, batch size 48, and 40,690 optimizer steps
149
+ on one NVIDIA H200.
150
+
151
+ The complete corpus combines the following pinned public sources:
152
+
153
+ - [FineWeb-Edu and Cosmopedia v2](https://huggingface.co/datasets/HuggingFaceTB/smollm-corpus)
154
+ - [FineMath-4+](https://huggingface.co/datasets/HuggingFaceTB/finemath)
155
+ - [DCLM-Baseline](https://huggingface.co/datasets/mlfoundations/dclm-baseline-1.0-parquet)
156
+ - [CodeSearchNet Python](https://huggingface.co/datasets/code-search-net/code_search_net)
157
+ - [CodeParrot Clean](https://huggingface.co/datasets/codeparrot/codeparrot-clean)
158
+
159
+ Documents were deduplicated across stages before admission. Source revisions
160
+ and upstream licensing notices are recorded in [`NOTICE`](NOTICE); no training
161
+ data is redistributed in this repository or the model release.
162
+
163
+ ## Release integrity
164
+
165
+ | Artifact | SHA-256 |
166
+ |---|---|
167
+ | `model.safetensors` | `f2a7c88b9f2c2e3584809081407ab136795d82e30e89b730e007781c45d01447` |
168
+ | `barun_config.json` | `9b3a1d71baa95a198744d250f9629231738d942570b8685c44307fd83dd33565` |
169
+ | `tokenizer.json` | `70ded9605fccd09c2340ca7e225361eab0ae8b4dbbb0d6e26343ab5183979db6` |
170
+
171
+ The checkpoint contains 35,072,768 unique trainable parameters. All stored
172
+ floating-point tensors passed an independent finiteness and shape audit.
173
+
174
+ ## Intended use and limitations
175
+
176
+ BarunLM-35M is intended for research on compact language models, controlled
177
+ text-generation experiments, education, and local prototyping.
178
+
179
+ - It is a base model, not instruction-tuned, and should be prompted as a text
180
+ continuation model.
181
+ - Its 2,048-token context and 35M-parameter capacity limit factual recall,
182
+ multi-step reasoning, instruction following, and long-context synthesis.
183
+ - Training and evaluation are English-centric.
184
+ - Outputs may be inaccurate, biased, unsafe, repetitive, or fabricated.
185
+ - The model has not undergone a comprehensive safety evaluation and should not
186
+ be used for medical, legal, financial, or other high-stakes decisions.
187
+
188
+ Users should evaluate the model in their own domain and apply appropriate
189
+ guardrails before deployment.
190
+
191
+ ## Acknowledgements
192
+
193
+ BarunLM builds on ideas developed across the open language-model ecosystem,
194
+ including [grouped-query attention](https://arxiv.org/abs/2305.13245),
195
+ [rotary position embeddings](https://arxiv.org/abs/2104.09864), and
196
+ [SwiGLU](https://arxiv.org/abs/2002.05202). We thank the teams behind PyTorch,
197
+ Hugging Face, LM Evaluation Harness, FineWeb-Edu, Cosmopedia, FineMath, DCLM,
198
+ CodeSearchNet, and CodeParrot for the open infrastructure and data that made
199
+ this release possible.
200
+
201
+ ## License
202
+
203
+ The source code and released weights are available under the
204
+ [Apache License 2.0](LICENSE). Upstream datasets retain their own licenses and
205
+ terms as described in [`NOTICE`](NOTICE).
SHA256SUMS ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ f2a7c88b9f2c2e3584809081407ab136795d82e30e89b730e007781c45d01447 model.safetensors
2
+ 9b3a1d71baa95a198744d250f9629231738d942570b8685c44307fd83dd33565 barun_config.json
3
+ 70ded9605fccd09c2340ca7e225361eab0ae8b4dbbb0d6e26343ab5183979db6 tokenizer.json
barun_config.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "activation_clip": 10.0,
3
+ "attention_gate": true,
4
+ "dim": 448,
5
+ "dropout": 0.0,
6
+ "ffn_dim": 1228,
7
+ "full_attention_every": 4,
8
+ "local_window": 256,
9
+ "max_seq_len": 2048,
10
+ "mtp_loss_weight": 0.0,
11
+ "mtp_offset": 2,
12
+ "n_heads": 7,
13
+ "n_kv_heads": 1,
14
+ "n_layers": 12,
15
+ "norm_eps": 1e-06,
16
+ "qk_norm": true,
17
+ "residual_select_every": 4,
18
+ "rope_fraction": 0.5,
19
+ "rope_theta": 10000.0,
20
+ "tie_embeddings": true,
21
+ "vocab_size": 16384
22
+ }
barunlm/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from .config import BarunConfig
2
+ from .model import BarunLM
3
+
4
+ __all__ = ["BarunConfig", "BarunLM"]
barunlm/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (362 Bytes). View file
 
barunlm/__pycache__/config.cpython-312.pyc ADDED
Binary file (4.6 kB). View file
 
barunlm/__pycache__/model.cpython-312.pyc ADDED
Binary file (30.4 kB). View file
 
barunlm/config.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import asdict, dataclass
5
+ from pathlib import Path
6
+
7
+
8
+ @dataclass(slots=True)
9
+ class BarunConfig:
10
+ vocab_size: int = 16_384
11
+ dim: int = 448
12
+ n_layers: int = 12
13
+ n_heads: int = 7
14
+ n_kv_heads: int = 1
15
+ ffn_dim: int = 1_228
16
+ max_seq_len: int = 2_048
17
+ rope_theta: float = 10_000.0
18
+ rope_fraction: float = 0.5
19
+ local_window: int = 256
20
+ full_attention_every: int = 4
21
+ attention_gate: bool = True
22
+ qk_norm: bool = True
23
+ residual_select_every: int = 4
24
+ activation_clip: float = 10.0
25
+ dropout: float = 0.0
26
+ norm_eps: float = 1e-6
27
+ tie_embeddings: bool = True
28
+ mtp_offset: int = 2
29
+ mtp_loss_weight: float = 0.0
30
+
31
+ def __post_init__(self) -> None:
32
+ if self.dim % self.n_heads:
33
+ raise ValueError("dim must be divisible by n_heads")
34
+ if self.n_heads % self.n_kv_heads:
35
+ raise ValueError("n_heads must be divisible by n_kv_heads")
36
+ head_dim = self.dim // self.n_heads
37
+ rope_dim = int(head_dim * self.rope_fraction)
38
+ if rope_dim < 2 or rope_dim % 2:
39
+ raise ValueError("rope_fraction must yield a positive, even rotary dimension")
40
+ if not 0 <= self.dropout < 1:
41
+ raise ValueError("dropout must be in [0, 1)")
42
+ if self.local_window < 1:
43
+ raise ValueError("local_window must be positive")
44
+ if self.full_attention_every < 1:
45
+ raise ValueError("full_attention_every must be positive")
46
+ if self.residual_select_every < 0:
47
+ raise ValueError("residual_select_every cannot be negative")
48
+ if self.mtp_offset < 1:
49
+ raise ValueError("mtp_offset must be positive")
50
+
51
+ @property
52
+ def head_dim(self) -> int:
53
+ return self.dim // self.n_heads
54
+
55
+ @property
56
+ def rope_dim(self) -> int:
57
+ value = int(self.head_dim * self.rope_fraction)
58
+ return value - value % 2
59
+
60
+ @classmethod
61
+ def from_json(cls, path: str | Path) -> BarunConfig:
62
+ with Path(path).open() as handle:
63
+ return cls(**json.load(handle))
64
+
65
+ def save_json(self, path: str | Path) -> None:
66
+ target = Path(path)
67
+ target.parent.mkdir(parents=True, exist_ok=True)
68
+ with target.open("w") as handle:
69
+ json.dump(asdict(self), handle, indent=2, sort_keys=True)
70
+ handle.write("\n")
barunlm/model.py ADDED
@@ -0,0 +1,430 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from functools import lru_cache
5
+
6
+ import torch
7
+ import torch.nn.functional as F
8
+ from torch import Tensor, nn
9
+
10
+ from .config import BarunConfig
11
+
12
+ try:
13
+ from flash_attn import flash_attn_func
14
+ except ImportError: # pragma: no cover - exercised on GPU installations only
15
+ flash_attn_func = None
16
+
17
+ try:
18
+ from torch.nn.attention.flex_attention import create_block_mask, flex_attention
19
+ except ImportError: # pragma: no cover - compatibility fallback for older PyTorch
20
+ create_block_mask = None
21
+ flex_attention = None
22
+
23
+ compiled_flex_attention = (
24
+ torch.compile(flex_attention, dynamic=False) if flex_attention is not None else None
25
+ )
26
+
27
+
28
+ @dataclass
29
+ class BarunOutput:
30
+ logits: Tensor
31
+ loss: Tensor | None = None
32
+ causal_loss: Tensor | None = None
33
+ mtp_loss: Tensor | None = None
34
+ past_key_values: list[tuple[Tensor, Tensor]] | None = None
35
+
36
+
37
+ class RMSNorm(nn.Module):
38
+ def __init__(self, dim: int, eps: float = 1e-6, affine: bool = True) -> None:
39
+ super().__init__()
40
+ self.eps = eps
41
+ self.weight = nn.Parameter(torch.ones(dim)) if affine else None
42
+
43
+ def forward(self, x: Tensor) -> Tensor:
44
+ dtype = x.dtype
45
+ y = x.float() * torch.rsqrt(x.float().square().mean(dim=-1, keepdim=True) + self.eps)
46
+ if self.weight is not None:
47
+ y = y * self.weight.float()
48
+ return y.to(dtype)
49
+
50
+
51
+ def _rotate_half(x: Tensor) -> Tensor:
52
+ x1, x2 = x.chunk(2, dim=-1)
53
+ return torch.cat((-x2, x1), dim=-1)
54
+
55
+
56
+ class PartialRotaryEmbedding(nn.Module):
57
+ def __init__(self, dim: int, max_seq_len: int, theta: float) -> None:
58
+ super().__init__()
59
+ inv_freq = theta ** (-torch.arange(0, dim, 2, dtype=torch.float32) / dim)
60
+ positions = torch.arange(max_seq_len, dtype=torch.float32)
61
+ freqs = torch.outer(positions, inv_freq)
62
+ angles = torch.cat((freqs, freqs), dim=-1)
63
+ self.register_buffer("cos", angles.cos()[None, None, :, :], persistent=False)
64
+ self.register_buffer("sin", angles.sin()[None, None, :, :], persistent=False)
65
+ self.dim = dim
66
+
67
+ def forward(self, q: Tensor, k: Tensor, position_offset: int = 0) -> tuple[Tensor, Tensor]:
68
+ seq_len = q.shape[-2]
69
+ end = position_offset + seq_len
70
+ if end > self.cos.shape[-2]:
71
+ raise ValueError(
72
+ f"rotary position {end} exceeds configured maximum {self.cos.shape[-2]}"
73
+ )
74
+ cos = self.cos[:, :, position_offset:end].to(device=q.device, dtype=q.dtype)
75
+ sin = self.sin[:, :, position_offset:end].to(device=q.device, dtype=q.dtype)
76
+ q_rot, q_pass = q[..., : self.dim], q[..., self.dim :]
77
+ k_rot, k_pass = k[..., : self.dim], k[..., self.dim :]
78
+ q = torch.cat((q_rot * cos + _rotate_half(q_rot) * sin, q_pass), dim=-1)
79
+ k = torch.cat((k_rot * cos + _rotate_half(k_rot) * sin, k_pass), dim=-1)
80
+ return q, k
81
+
82
+
83
+ @lru_cache(maxsize=32)
84
+ def _local_causal_mask(seq_len: int, window: int, device_type: str) -> Tensor:
85
+ device = torch.device(device_type)
86
+ row = torch.arange(seq_len, device=device)[:, None]
87
+ col = torch.arange(seq_len, device=device)[None, :]
88
+ allowed = (col <= row) & (col > row - window)
89
+ mask = torch.zeros((seq_len, seq_len), device=device, dtype=torch.float32)
90
+ return mask.masked_fill(~allowed, float("-inf"))
91
+
92
+
93
+ @lru_cache(maxsize=32)
94
+ def _local_bidirectional_mask(seq_len: int, window: int, device_type: str) -> Tensor:
95
+ """Window constraint for custom masks such as bidirectional PrefixLM prompts."""
96
+ device = torch.device(device_type)
97
+ row = torch.arange(seq_len, device=device)[:, None]
98
+ col = torch.arange(seq_len, device=device)[None, :]
99
+ allowed = (col - row).abs() < window
100
+ mask = torch.zeros((seq_len, seq_len), device=device, dtype=torch.float32)
101
+ return mask.masked_fill(~allowed, float("-inf"))
102
+
103
+
104
+ @lru_cache(maxsize=32)
105
+ def _local_block_mask(seq_len: int, window: int, device: str):
106
+ if create_block_mask is None:
107
+ return None
108
+
109
+ def causal_window(batch, head, query_index, key_index):
110
+ del batch, head
111
+ return (query_index >= key_index) & (query_index - key_index < window)
112
+
113
+ return create_block_mask(
114
+ causal_window,
115
+ B=None,
116
+ H=None,
117
+ Q_LEN=seq_len,
118
+ KV_LEN=seq_len,
119
+ device=device,
120
+ _compile=True,
121
+ )
122
+
123
+
124
+ class GroupedAttention(nn.Module):
125
+ def __init__(self, config: BarunConfig, *, is_full: bool) -> None:
126
+ super().__init__()
127
+ self.config = config
128
+ self.is_full = is_full
129
+ self.q_proj = nn.Linear(config.dim, config.n_heads * config.head_dim, bias=False)
130
+ self.k_proj = nn.Linear(config.dim, config.n_kv_heads * config.head_dim, bias=False)
131
+ self.v_proj = nn.Linear(config.dim, config.n_kv_heads * config.head_dim, bias=False)
132
+ self.o_proj = nn.Linear(config.n_heads * config.head_dim, config.dim, bias=False)
133
+ self.g_proj = (
134
+ nn.Linear(config.dim, config.n_heads * config.head_dim, bias=False)
135
+ if config.attention_gate
136
+ else None
137
+ )
138
+ self.q_norm = RMSNorm(config.head_dim, config.norm_eps) if config.qk_norm else nn.Identity()
139
+ self.k_norm = RMSNorm(config.head_dim, config.norm_eps) if config.qk_norm else nn.Identity()
140
+ self.rope = PartialRotaryEmbedding(config.rope_dim, config.max_seq_len, config.rope_theta)
141
+
142
+ def _torch_attention(self, q: Tensor, k: Tensor, v: Tensor, mask: Tensor | None) -> Tensor:
143
+ use_flex = (
144
+ not self.is_full
145
+ and mask is None
146
+ and compiled_flex_attention is not None
147
+ and q.is_cuda
148
+ and self.config.dropout == 0
149
+ )
150
+ if use_flex:
151
+ block_mask = _local_block_mask(q.shape[-2], self.config.local_window, str(q.device))
152
+ return compiled_flex_attention(q, k, v, block_mask=block_mask, enable_gqa=True)
153
+ repeat = self.config.n_heads // self.config.n_kv_heads
154
+ if repeat > 1:
155
+ k = k.repeat_interleave(repeat, dim=1)
156
+ v = v.repeat_interleave(repeat, dim=1)
157
+ if mask is not None:
158
+ mask = mask.to(dtype=q.dtype)
159
+ if not self.is_full:
160
+ local_mask = _local_bidirectional_mask(
161
+ q.shape[-2], self.config.local_window, q.device.type
162
+ ).to(dtype=q.dtype)
163
+ mask = mask + local_mask
164
+ return F.scaled_dot_product_attention(
165
+ q, k, v, attn_mask=mask, dropout_p=self.config.dropout if self.training else 0.0
166
+ )
167
+ if self.is_full:
168
+ return F.scaled_dot_product_attention(
169
+ q, k, v, is_causal=True, dropout_p=self.config.dropout if self.training else 0.0
170
+ )
171
+ local_mask = _local_causal_mask(q.shape[-2], self.config.local_window, q.device.type)
172
+ return F.scaled_dot_product_attention(
173
+ q,
174
+ k,
175
+ v,
176
+ attn_mask=local_mask,
177
+ dropout_p=self.config.dropout if self.training else 0.0,
178
+ )
179
+
180
+ def _project(self, x: Tensor, position_offset: int) -> tuple[Tensor, Tensor, Tensor]:
181
+ batch, seq_len, _ = x.shape
182
+ q = self.q_proj(x).view(batch, seq_len, self.config.n_heads, self.config.head_dim)
183
+ k = self.k_proj(x).view(batch, seq_len, self.config.n_kv_heads, self.config.head_dim)
184
+ v = self.v_proj(x).view(batch, seq_len, self.config.n_kv_heads, self.config.head_dim)
185
+ q = self.q_norm(q).transpose(1, 2)
186
+ k = self.k_norm(k).transpose(1, 2)
187
+ v = v.transpose(1, 2)
188
+ q, k = self.rope(q, k, position_offset)
189
+ return q, k, v
190
+
191
+ def _finish(self, x: Tensor, out: Tensor) -> Tensor:
192
+ batch, seq_len, _ = x.shape
193
+ out = out.transpose(1, 2).contiguous().view(batch, seq_len, -1)
194
+ if self.g_proj is not None:
195
+ out = out * torch.sigmoid(self.g_proj(x))
196
+ return self.o_proj(out)
197
+
198
+ def forward(self, x: Tensor, attention_mask: Tensor | None = None) -> Tensor:
199
+ q, k, v = self._project(x, position_offset=0)
200
+
201
+ use_flash = flash_attn_func is not None and x.is_cuda and attention_mask is None
202
+ if use_flash:
203
+ window = (-1, -1) if self.is_full else (self.config.local_window - 1, 0)
204
+ out = flash_attn_func(
205
+ q.transpose(1, 2),
206
+ k.transpose(1, 2),
207
+ v.transpose(1, 2),
208
+ dropout_p=self.config.dropout if self.training else 0.0,
209
+ causal=True,
210
+ window_size=window,
211
+ ).transpose(1, 2)
212
+ else:
213
+ out = self._torch_attention(q, k, v, attention_mask)
214
+ return self._finish(x, out)
215
+
216
+ def forward_cached(
217
+ self,
218
+ x: Tensor,
219
+ cache: tuple[Tensor, Tensor] | None,
220
+ position_offset: int,
221
+ attention_mask: Tensor | None = None,
222
+ ) -> tuple[Tensor, tuple[Tensor, Tensor]]:
223
+ q, new_k, new_v = self._project(x, position_offset)
224
+ if cache is None:
225
+ k, v = new_k, new_v
226
+ out = self._torch_attention(q, k, v, mask=attention_mask)
227
+ else:
228
+ k = torch.cat((cache[0], new_k), dim=-2)
229
+ v = torch.cat((cache[1], new_v), dim=-2)
230
+ if not self.is_full:
231
+ k = k[:, :, -self.config.local_window :]
232
+ v = v[:, :, -self.config.local_window :]
233
+ repeat = self.config.n_heads // self.config.n_kv_heads
234
+ expanded_k = k.repeat_interleave(repeat, dim=1) if repeat > 1 else k
235
+ expanded_v = v.repeat_interleave(repeat, dim=1) if repeat > 1 else v
236
+ if attention_mask is not None:
237
+ attention_mask = attention_mask[..., -k.shape[-2] :].to(dtype=q.dtype)
238
+ out = F.scaled_dot_product_attention(
239
+ q,
240
+ expanded_k,
241
+ expanded_v,
242
+ attn_mask=attention_mask,
243
+ is_causal=False,
244
+ )
245
+ if not self.is_full:
246
+ k = k[:, :, -self.config.local_window :]
247
+ v = v[:, :, -self.config.local_window :]
248
+ return self._finish(x, out), (k, v)
249
+
250
+
251
+ class BoundedSwiGLU(nn.Module):
252
+ def __init__(self, config: BarunConfig) -> None:
253
+ super().__init__()
254
+ self.gate_up = nn.Linear(config.dim, 2 * config.ffn_dim, bias=False)
255
+ self.down = nn.Linear(config.ffn_dim, config.dim, bias=False)
256
+ self.clip = config.activation_clip
257
+
258
+ def forward(self, x: Tensor) -> Tensor:
259
+ gate, up = self.gate_up(x).chunk(2, dim=-1)
260
+ if self.clip > 0:
261
+ gate = gate.clamp(max=self.clip)
262
+ up = up.clamp(min=-self.clip, max=self.clip)
263
+ return self.down(F.silu(gate) * up)
264
+
265
+
266
+ class BarunBlock(nn.Module):
267
+ def __init__(self, config: BarunConfig, *, is_full: bool) -> None:
268
+ super().__init__()
269
+ self.attn_norm = RMSNorm(config.dim, config.norm_eps)
270
+ self.attn = GroupedAttention(config, is_full=is_full)
271
+ self.ffn_norm = RMSNorm(config.dim, config.norm_eps)
272
+ self.ffn = BoundedSwiGLU(config)
273
+ self.dropout = config.dropout
274
+
275
+ def forward(self, x: Tensor, attention_mask: Tensor | None = None) -> Tensor:
276
+ x = x + F.dropout(self.attn(self.attn_norm(x), attention_mask), self.dropout, self.training)
277
+ x = x + F.dropout(self.ffn(self.ffn_norm(x)), self.dropout, self.training)
278
+ return x
279
+
280
+ def forward_cached(
281
+ self,
282
+ x: Tensor,
283
+ cache: tuple[Tensor, Tensor] | None,
284
+ position_offset: int,
285
+ attention_mask: Tensor | None = None,
286
+ ) -> tuple[Tensor, tuple[Tensor, Tensor]]:
287
+ attention, new_cache = self.attn.forward_cached(
288
+ self.attn_norm(x), cache, position_offset, attention_mask
289
+ )
290
+ x = x + attention
291
+ x = x + self.ffn(self.ffn_norm(x))
292
+ return x, new_cache
293
+
294
+
295
+ class ResidualSelector(nn.Module):
296
+ """Convexly select between a group's input checkpoint and its transformed output."""
297
+
298
+ def __init__(self, config: BarunConfig) -> None:
299
+ super().__init__()
300
+ self.norm = RMSNorm(config.dim, config.norm_eps, affine=False)
301
+ self.score = nn.Linear(config.dim, 1, bias=False)
302
+ self.last_mean_weights: Tensor | None = None
303
+
304
+ def forward(self, checkpoint: Tensor, current: Tensor) -> Tensor:
305
+ candidates = torch.stack((checkpoint, current), dim=-2)
306
+ weights = self.score(self.norm(candidates)).softmax(dim=-2)
307
+ if not torch.compiler.is_compiling():
308
+ self.last_mean_weights = weights.detach().float().mean(dim=(0, 1, 3))
309
+ return (weights * candidates).sum(dim=-2)
310
+
311
+
312
+ class BarunLM(nn.Module):
313
+ def __init__(self, config: BarunConfig) -> None:
314
+ super().__init__()
315
+ self.config = config
316
+ self.embedding = nn.Embedding(config.vocab_size, config.dim)
317
+ self.layers = nn.ModuleList(
318
+ BarunBlock(config, is_full=(index + 1) % config.full_attention_every == 0)
319
+ for index in range(config.n_layers)
320
+ )
321
+ selector_count = (
322
+ config.n_layers // config.residual_select_every if config.residual_select_every else 0
323
+ )
324
+ self.selectors = nn.ModuleList(ResidualSelector(config) for _ in range(selector_count))
325
+ self.final_norm = RMSNorm(config.dim, config.norm_eps)
326
+ self.lm_head = nn.Linear(config.dim, config.vocab_size, bias=False)
327
+ if config.mtp_loss_weight > 0:
328
+ self.mtp_norm = RMSNorm(config.dim, config.norm_eps)
329
+ self.mtp_proj = nn.Linear(config.dim, config.dim, bias=False)
330
+ else:
331
+ self.mtp_norm = None
332
+ self.mtp_proj = None
333
+ if config.tie_embeddings:
334
+ self.lm_head.weight = self.embedding.weight
335
+ self.apply(self._init_weights)
336
+
337
+ def _init_weights(self, module: nn.Module) -> None:
338
+ if isinstance(module, nn.Linear | nn.Embedding):
339
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
340
+ for name, parameter in module.named_parameters(recurse=False):
341
+ if name == "weight" and isinstance(module, RMSNorm) and parameter is not None:
342
+ nn.init.ones_(parameter)
343
+
344
+ def forward(
345
+ self,
346
+ input_ids: Tensor,
347
+ labels: Tensor | None = None,
348
+ attention_mask: Tensor | None = None,
349
+ past_key_values: list[tuple[Tensor, Tensor]] | None = None,
350
+ use_cache: bool = False,
351
+ position_offset: int = 0,
352
+ ) -> BarunOutput:
353
+ if position_offset + input_ids.shape[1] > self.config.max_seq_len:
354
+ raise ValueError(f"sequence length exceeds max_seq_len={self.config.max_seq_len}")
355
+ if past_key_values is not None and len(past_key_values) != len(self.layers):
356
+ raise ValueError("past_key_values must have one entry per layer")
357
+ x = self.embedding(input_ids)
358
+ checkpoint = x
359
+ selector_index = 0
360
+ new_past_key_values = [] if use_cache else None
361
+ for index, layer in enumerate(self.layers):
362
+ if use_cache:
363
+ layer_cache = past_key_values[index] if past_key_values is not None else None
364
+ x, new_cache = layer.forward_cached(
365
+ x, layer_cache, position_offset, attention_mask
366
+ )
367
+ new_past_key_values.append(new_cache)
368
+ else:
369
+ x = layer(x, attention_mask)
370
+ stride = self.config.residual_select_every
371
+ if stride and (index + 1) % stride == 0:
372
+ x = self.selectors[selector_index](checkpoint, x)
373
+ checkpoint = x
374
+ selector_index += 1
375
+ hidden = self.final_norm(x)
376
+ logits = self.lm_head(hidden)
377
+ if labels is None:
378
+ return BarunOutput(logits=logits, past_key_values=new_past_key_values)
379
+
380
+ causal_loss = F.cross_entropy(logits.reshape(-1, logits.shape[-1]), labels.reshape(-1))
381
+ mtp_loss = None
382
+ loss = causal_loss
383
+ offset = self.config.mtp_offset
384
+ if self.mtp_proj is not None and hidden.shape[1] > offset:
385
+ mtp_hidden = self.mtp_norm(hidden[:, :-offset] + self.mtp_proj(hidden[:, :-offset]))
386
+ mtp_logits = self.lm_head(mtp_hidden)
387
+ mtp_labels = input_ids[:, offset:].clone()
388
+ target_mask = labels[:, offset - 1 : -1] == -100
389
+ mtp_labels.masked_fill_(target_mask, -100)
390
+ mtp_loss = F.cross_entropy(
391
+ mtp_logits.reshape(-1, mtp_logits.shape[-1]), mtp_labels.reshape(-1)
392
+ )
393
+ loss = loss + self.config.mtp_loss_weight * mtp_loss
394
+ return BarunOutput(
395
+ logits=logits,
396
+ loss=loss,
397
+ causal_loss=causal_loss,
398
+ mtp_loss=mtp_loss,
399
+ past_key_values=new_past_key_values,
400
+ )
401
+
402
+ @torch.no_grad()
403
+ def generate(self, input_ids: Tensor, max_new_tokens: int, temperature: float = 0.8) -> Tensor:
404
+ self.eval()
405
+ if input_ids.shape[1] + max_new_tokens > self.config.max_seq_len:
406
+ raise ValueError("prompt plus generation exceeds max_seq_len")
407
+ output = self(input_ids, use_cache=True)
408
+ past_key_values = output.past_key_values
409
+ for generated in range(max_new_tokens):
410
+ logits = output.logits[:, -1]
411
+ if temperature <= 0:
412
+ next_token = logits.argmax(dim=-1, keepdim=True)
413
+ else:
414
+ probs = (logits / temperature).softmax(dim=-1)
415
+ next_token = torch.multinomial(probs, num_samples=1)
416
+ input_ids = torch.cat((input_ids, next_token), dim=1)
417
+ if generated + 1 < max_new_tokens:
418
+ output = self(
419
+ next_token,
420
+ past_key_values=past_key_values,
421
+ use_cache=True,
422
+ position_offset=input_ids.shape[1] - 1,
423
+ )
424
+ past_key_values = output.past_key_values
425
+ return input_ids
426
+
427
+ def parameter_counts(self) -> dict[str, int]:
428
+ total = sum(parameter.numel() for parameter in self.parameters())
429
+ embedding = self.embedding.weight.numel()
430
+ return {"total": total, "non_embedding": total - embedding, "embedding": embedding}
benchmark_results.json ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema_version": 1,
3
+ "model": {
4
+ "id": "harrrshall/BarunLM-35M",
5
+ "parameters": 35072768,
6
+ "training_tokens": 5699985408,
7
+ "checkpoint_sha256": "f2a7c88b9f2c2e3584809081407ab136795d82e30e89b730e007781c45d01447"
8
+ },
9
+ "protocol": {
10
+ "harness": "lm-eval==0.4.12",
11
+ "num_fewshot": 0,
12
+ "dtype": "bfloat16",
13
+ "seed": 1234,
14
+ "bootstrap_iterations": 10000,
15
+ "tasks": [
16
+ "arc_challenge",
17
+ "arc_easy",
18
+ "boolq",
19
+ "hellaswag",
20
+ "lambada_openai",
21
+ "openbookqa",
22
+ "piqa",
23
+ "sciq",
24
+ "winogrande"
25
+ ],
26
+ "decontamination": {
27
+ "method": "correctness-blind exact 13-token scan over the complete training history",
28
+ "training_tokens_scanned": 5700000000,
29
+ "excluded_samples": 1854
30
+ }
31
+ },
32
+ "results": {
33
+ "macro_accuracy": 0.4101019372910188,
34
+ "task_scores": {
35
+ "arc_challenge": 0.222990492653414,
36
+ "arc_easy": 0.38862761864044465,
37
+ "boolq": 0.5593303791235844,
38
+ "hellaswag": 0.2899662473151273,
39
+ "lambada_openai": 0.2450731707317073,
40
+ "openbookqa": 0.28,
41
+ "piqa": 0.5926950747094631,
42
+ "sciq": 0.5992115637319316,
43
+ "winogrande": 0.5130228887134964
44
+ }
45
+ },
46
+ "comparisons": [
47
+ {
48
+ "id": "LiquidAI/LFM2.5-230M-Base",
49
+ "revision": "9d2be5519834990d30996f878b6771cccbd24f2c",
50
+ "parameters": 229693184,
51
+ "macro_accuracy": 0.39197443061078113,
52
+ "barunlm_lead": 0.01812750668023767,
53
+ "barunlm_lead_ci95": [0.00919175854904696, 0.02710881312805468]
54
+ },
55
+ {
56
+ "id": "EleutherAI/pythia-160m-deduped",
57
+ "revision": "582159a2dfe3e712a8d47ae83dec95ae3bde8e7e",
58
+ "parameters": 162322944,
59
+ "macro_accuracy": 0.3734593023210671,
60
+ "barunlm_lead": 0.036642634969951675,
61
+ "barunlm_lead_ci95": [0.02735687681514485, 0.04598902006059502]
62
+ },
63
+ {
64
+ "id": "StentorLabs/Stentor-30M",
65
+ "revision": "6a69098f55961cc0df3a0751e44082616e4afdfa",
66
+ "parameters": 30419712,
67
+ "macro_accuracy": 0.3645857462123749,
68
+ "barunlm_lead": 0.045516191078643894,
69
+ "barunlm_lead_ci95": [0.03729788428965733, 0.05363598444691119]
70
+ },
71
+ {
72
+ "id": "roneneldan/TinyStories-33M",
73
+ "revision": "2ad0a164221b7c4d21cac7c46aec74f6f98dbfc8",
74
+ "parameters": 68514048,
75
+ "macro_accuracy": 0.33156687160177734,
76
+ "barunlm_lead": 0.07853506568924143,
77
+ "barunlm_lead_ci95": [0.0686233699582622, 0.08816152027826075],
78
+ "scope": "narrow-domain diagnostic"
79
+ },
80
+ {
81
+ "id": "EleutherAI/pythia-70m-deduped",
82
+ "revision": "e93a9faa9c77e5d09219f6c868bfc7a1bd65593c",
83
+ "parameters": 70426624,
84
+ "macro_accuracy": 0.3171108992671423,
85
+ "barunlm_lead": 0.09299103802387654,
86
+ "barunlm_lead_ci95": [0.08290362117614143, 0.10295299346141393]
87
+ }
88
+ ],
89
+ "evidence_sha256": {
90
+ "candidate_results": "0e0137bd990a6c0c0d8e12a53cd58dc7ab1faec928cecc5df630572deb13248e",
91
+ "peer_summary": "16ec0e53ac64e2bca9f4cf28be561ab8172d8a35bcc82cf6f810a288942054f4",
92
+ "lfm_pair_summary": "f3ba7d8c1ba50bc1fe34aaa546d3c4746a97f4526147a9d53127328fe2c60ee7"
93
+ }
94
+ }
generate.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import hashlib
5
+ from pathlib import Path
6
+
7
+ import torch
8
+ from safetensors.torch import load_model
9
+ from tokenizers import Tokenizer
10
+
11
+ from barunlm import BarunConfig, BarunLM
12
+
13
+ ROOT = Path(__file__).resolve().parent
14
+ EXPECTED_SHA256 = {
15
+ "model.safetensors": "f2a7c88b9f2c2e3584809081407ab136795d82e30e89b730e007781c45d01447",
16
+ "barun_config.json": "9b3a1d71baa95a198744d250f9629231738d942570b8685c44307fd83dd33565",
17
+ "tokenizer.json": "70ded9605fccd09c2340ca7e225361eab0ae8b4dbbb0d6e26343ab5183979db6",
18
+ }
19
+
20
+
21
+ def sha256(path: Path) -> str:
22
+ digest = hashlib.sha256()
23
+ with path.open("rb") as handle:
24
+ for block in iter(lambda: handle.read(1024 * 1024), b""):
25
+ digest.update(block)
26
+ return digest.hexdigest()
27
+
28
+
29
+ def parse_args() -> argparse.Namespace:
30
+ parser = argparse.ArgumentParser(description="Generate text with BarunLM-35M.")
31
+ parser.add_argument("--prompt", default="The future of small language models is")
32
+ parser.add_argument("--max-new-tokens", type=int, default=48)
33
+ parser.add_argument("--temperature", type=float, default=0.8)
34
+ parser.add_argument("--device", choices=("auto", "cpu", "cuda"), default="auto")
35
+ parser.add_argument("--verify-only", action="store_true")
36
+ return parser.parse_args()
37
+
38
+
39
+ def main() -> None:
40
+ args = parse_args()
41
+ for name, expected in EXPECTED_SHA256.items():
42
+ actual = sha256(ROOT / name)
43
+ if actual != expected:
44
+ raise RuntimeError(f"{name} SHA-256 mismatch: expected {expected}, got {actual}")
45
+
46
+ config = BarunConfig.from_json(ROOT / "barun_config.json")
47
+ model = BarunLM(config)
48
+ missing, unexpected = load_model(model, ROOT / "model.safetensors", strict=False)
49
+ if missing or unexpected:
50
+ raise RuntimeError(f"checkpoint mismatch: missing={missing}, unexpected={unexpected}")
51
+ count = model.parameter_counts()["total"]
52
+ if count != 35_072_768:
53
+ raise RuntimeError(f"unexpected parameter count: {count}")
54
+ print(f"verified parameters={count} checkpoint_sha256={EXPECTED_SHA256['model.safetensors']}")
55
+ if args.verify_only:
56
+ return
57
+
58
+ if args.max_new_tokens < 1:
59
+ raise ValueError("--max-new-tokens must be positive")
60
+ if args.temperature < 0:
61
+ raise ValueError("--temperature cannot be negative")
62
+ device_name = "cuda" if args.device == "auto" and torch.cuda.is_available() else args.device
63
+ if device_name == "auto":
64
+ device_name = "cpu"
65
+ if device_name == "cuda" and not torch.cuda.is_available():
66
+ raise RuntimeError("CUDA was requested but is unavailable")
67
+ device = torch.device(device_name)
68
+ dtype = torch.bfloat16 if device.type == "cuda" else torch.float32
69
+ model.to(device=device, dtype=dtype).eval()
70
+
71
+ tokenizer = Tokenizer.from_file(str(ROOT / "tokenizer.json"))
72
+ prompt_ids = tokenizer.encode(args.prompt, add_special_tokens=False).ids
73
+ if not prompt_ids:
74
+ raise ValueError("prompt must encode to at least one token")
75
+ if len(prompt_ids) + args.max_new_tokens > config.max_seq_len:
76
+ raise ValueError("prompt and continuation exceed the 2,048-token context")
77
+
78
+ input_ids = torch.tensor([prompt_ids], dtype=torch.long, device=device)
79
+ with torch.inference_mode():
80
+ output_ids = model.generate(
81
+ input_ids,
82
+ max_new_tokens=args.max_new_tokens,
83
+ temperature=args.temperature,
84
+ )
85
+ print(tokenizer.decode(output_ids[0].tolist(), skip_special_tokens=True))
86
+
87
+
88
+ if __name__ == "__main__":
89
+ main()
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f2a7c88b9f2c2e3584809081407ab136795d82e30e89b730e007781c45d01447
3
+ size 140304464
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ torch>=2.4
2
+ safetensors>=0.4.5
3
+ tokenizers>=0.20,<1
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff