BarunLM-35M: 35M-parameter base language model trained on 5.7B tokens
Browse files- .gitattributes +0 -34
- CITATION.cff +12 -0
- LICENSE +201 -0
- NOTICE +26 -0
- README.md +205 -0
- SHA256SUMS +3 -0
- barun_config.json +22 -0
- barunlm/__init__.py +4 -0
- barunlm/__pycache__/__init__.cpython-312.pyc +0 -0
- barunlm/__pycache__/config.cpython-312.pyc +0 -0
- barunlm/__pycache__/model.cpython-312.pyc +0 -0
- barunlm/config.py +70 -0
- barunlm/model.py +430 -0
- benchmark_results.json +94 -0
- generate.py +89 -0
- model.safetensors +3 -0
- requirements.txt +3 -0
- tokenizer.json +0 -0
.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
|
|
|