Feature Extraction
Transformers
Safetensors
multilingual
qwen3_5_text
qwen3.5
classification-backbone
text-classification
knowledge-distillation
model-compression
edge-ai
Instructions to use mp-juuuns/qwen35-standalone4l-classification-base with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use mp-juuuns/qwen35-standalone4l-classification-base with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="mp-juuuns/qwen35-standalone4l-classification-base")# Load model directly from transformers import AutoTokenizer, AutoModel tokenizer = AutoTokenizer.from_pretrained("mp-juuuns/qwen35-standalone4l-classification-base") model = AutoModel.from_pretrained("mp-juuuns/qwen35-standalone4l-classification-base", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Publish task-agnostic 4L base, separate SemEval model, and 24L-to-4L distillation platform
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +1 -34
- LICENSE +203 -0
- README.md +140 -0
- SHA256SUMS +66 -0
- benchmark/reports/structural_control-seed41.json +0 -0
- benchmark/reports/structural_control-seed42.json +0 -0
- benchmark/reports/structural_control-seed43.json +0 -0
- benchmark/reports/task_agnostic-seed41.json +0 -0
- benchmark/reports/task_agnostic-seed42.json +0 -0
- benchmark/reports/task_agnostic-seed43.json +0 -0
- benchmark/semeval-transfer-summary.json +47 -0
- chat_template.jinja +120 -0
- config.json +80 -0
- distillation/README.md +108 -0
- distillation/configs/task_agnostic_base.yaml +19 -0
- distillation/examples/labels.json +5 -0
- distillation/examples/multilabel_train.jsonl +2 -0
- distillation/examples/single_label_train.jsonl +2 -0
- distillation/examples/unlabeled.jsonl +2 -0
- distillation/pyproject.toml +22 -0
- distillation/qwen35_distill/__init__.py +6 -0
- distillation/qwen35_distill/checkpoint.py +324 -0
- distillation/qwen35_distill/classification.py +386 -0
- distillation/qwen35_distill/cli.py +197 -0
- distillation/qwen35_distill/layer_maps.py +87 -0
- distillation/qwen35_distill/losses.py +98 -0
- distillation/qwen35_distill/schema.py +149 -0
- distillation/qwen35_distill/training.py +275 -0
- docs/RELEASE_CONTRACT.md +73 -0
- merges.txt +0 -0
- model.safetensors +3 -0
- models/semeval-propaganda/LICENSE +203 -0
- models/semeval-propaganda/README.md +27 -0
- models/semeval-propaganda/benchmark/final4l_aggregate.json +289 -0
- models/semeval-propaganda/benchmark/seed41.json +0 -0
- models/semeval-propaganda/benchmark/seed42.json +0 -0
- models/semeval-propaganda/benchmark/seed43.json +0 -0
- models/semeval-propaganda/chat_template.jinja +120 -0
- models/semeval-propaganda/classify.py +217 -0
- models/semeval-propaganda/config.json +122 -0
- models/semeval-propaganda/labels.json +34 -0
- models/semeval-propaganda/model.safetensors +3 -0
- models/semeval-propaganda/release_manifest.json +637 -0
- models/semeval-propaganda/requirements.txt +3 -0
- models/semeval-propaganda/seeds/seed42/chat_template.jinja +120 -0
- models/semeval-propaganda/seeds/seed42/config.json +122 -0
- models/semeval-propaganda/seeds/seed42/model.safetensors +3 -0
- models/semeval-propaganda/seeds/seed42/tokenizer.json +0 -0
- models/semeval-propaganda/seeds/seed42/tokenizer_config.json +39 -0
- models/semeval-propaganda/seeds/seed43/chat_template.jinja +120 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,2 @@
|
|
| 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 |
-
|
| 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
|
| 2 |
+
tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LICENSE
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright 2018- The Hugging Face team. All rights reserved.
|
| 2 |
+
|
| 3 |
+
Apache License
|
| 4 |
+
Version 2.0, January 2004
|
| 5 |
+
http://www.apache.org/licenses/
|
| 6 |
+
|
| 7 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 8 |
+
|
| 9 |
+
1. Definitions.
|
| 10 |
+
|
| 11 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 12 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 13 |
+
|
| 14 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 15 |
+
the copyright owner that is granting the License.
|
| 16 |
+
|
| 17 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 18 |
+
other entities that control, are controlled by, or are under common
|
| 19 |
+
control with that entity. For the purposes of this definition,
|
| 20 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 21 |
+
direction or management of such entity, whether by contract or
|
| 22 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 23 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 24 |
+
|
| 25 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 26 |
+
exercising permissions granted by this License.
|
| 27 |
+
|
| 28 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 29 |
+
including but not limited to software source code, documentation
|
| 30 |
+
source, and configuration files.
|
| 31 |
+
|
| 32 |
+
"Object" form shall mean any form resulting from mechanical
|
| 33 |
+
transformation or translation of a Source form, including but
|
| 34 |
+
not limited to compiled object code, generated documentation,
|
| 35 |
+
and conversions to other media types.
|
| 36 |
+
|
| 37 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 38 |
+
Object form, made available under the License, as indicated by a
|
| 39 |
+
copyright notice that is included in or attached to the work
|
| 40 |
+
(an example is provided in the Appendix below).
|
| 41 |
+
|
| 42 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 43 |
+
form, that is based on (or derived from) the Work and for which the
|
| 44 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 45 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 46 |
+
of this License, Derivative Works shall not include works that remain
|
| 47 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 48 |
+
the Work and Derivative Works thereof.
|
| 49 |
+
|
| 50 |
+
"Contribution" shall mean any work of authorship, including
|
| 51 |
+
the original version of the Work and any modifications or additions
|
| 52 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 53 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 54 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 55 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 56 |
+
means any form of electronic, verbal, or written communication sent
|
| 57 |
+
to the Licensor or its representatives, including but not limited to
|
| 58 |
+
communication on electronic mailing lists, source code control systems,
|
| 59 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 60 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 61 |
+
excluding communication that is conspicuously marked or otherwise
|
| 62 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 63 |
+
|
| 64 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 65 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 66 |
+
subsequently incorporated within the Work.
|
| 67 |
+
|
| 68 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 69 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 70 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 71 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 72 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 73 |
+
Work and such Derivative Works in Source or Object form.
|
| 74 |
+
|
| 75 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 76 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 77 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 78 |
+
(except as stated in this section) patent license to make, have made,
|
| 79 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 80 |
+
where such license applies only to those patent claims licensable
|
| 81 |
+
by such Contributor that are necessarily infringed by their
|
| 82 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 83 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 84 |
+
institute patent litigation against any entity (including a
|
| 85 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 86 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 87 |
+
or contributory patent infringement, then any patent licenses
|
| 88 |
+
granted to You under this License for that Work shall terminate
|
| 89 |
+
as of the date such litigation is filed.
|
| 90 |
+
|
| 91 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 92 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 93 |
+
modifications, and in Source or Object form, provided that You
|
| 94 |
+
meet the following conditions:
|
| 95 |
+
|
| 96 |
+
(a) You must give any other recipients of the Work or
|
| 97 |
+
Derivative Works a copy of this License; and
|
| 98 |
+
|
| 99 |
+
(b) You must cause any modified files to carry prominent notices
|
| 100 |
+
stating that You changed the files; and
|
| 101 |
+
|
| 102 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 103 |
+
that You distribute, all copyright, patent, trademark, and
|
| 104 |
+
attribution notices from the Source form of the Work,
|
| 105 |
+
excluding those notices that do not pertain to any part of
|
| 106 |
+
the Derivative Works; and
|
| 107 |
+
|
| 108 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 109 |
+
distribution, then any Derivative Works that You distribute must
|
| 110 |
+
include a readable copy of the attribution notices contained
|
| 111 |
+
within such NOTICE file, excluding those notices that do not
|
| 112 |
+
pertain to any part of the Derivative Works, in at least one
|
| 113 |
+
of the following places: within a NOTICE text file distributed
|
| 114 |
+
as part of the Derivative Works; within the Source form or
|
| 115 |
+
documentation, if provided along with the Derivative Works; or,
|
| 116 |
+
within a display generated by the Derivative Works, if and
|
| 117 |
+
wherever such third-party notices normally appear. The contents
|
| 118 |
+
of the NOTICE file are for informational purposes only and
|
| 119 |
+
do not modify the License. You may add Your own attribution
|
| 120 |
+
notices within Derivative Works that You distribute, alongside
|
| 121 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 122 |
+
that such additional attribution notices cannot be construed
|
| 123 |
+
as modifying the License.
|
| 124 |
+
|
| 125 |
+
You may add Your own copyright statement to Your modifications and
|
| 126 |
+
may provide additional or different license terms and conditions
|
| 127 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 128 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 129 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 130 |
+
the conditions stated in this License.
|
| 131 |
+
|
| 132 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 133 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 134 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 135 |
+
this License, without any additional terms or conditions.
|
| 136 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 137 |
+
the terms of any separate license agreement you may have executed
|
| 138 |
+
with Licensor regarding such Contributions.
|
| 139 |
+
|
| 140 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 141 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 142 |
+
except as required for reasonable and customary use in describing the
|
| 143 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 144 |
+
|
| 145 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 146 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 147 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 148 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 149 |
+
implied, including, without limitation, any warranties or conditions
|
| 150 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 151 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 152 |
+
appropriateness of using or redistributing the Work and assume any
|
| 153 |
+
risks associated with Your exercise of permissions under this License.
|
| 154 |
+
|
| 155 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 156 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 157 |
+
unless required by applicable law (such as deliberate and grossly
|
| 158 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 159 |
+
liable to You for damages, including any direct, indirect, special,
|
| 160 |
+
incidental, or consequential damages of any character arising as a
|
| 161 |
+
result of this License or out of the use or inability to use the
|
| 162 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 163 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 164 |
+
other commercial damages or losses), even if such Contributor
|
| 165 |
+
has been advised of the possibility of such damages.
|
| 166 |
+
|
| 167 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 168 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 169 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 170 |
+
or other liability obligations and/or rights consistent with this
|
| 171 |
+
License. However, in accepting such obligations, You may act only
|
| 172 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 173 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 174 |
+
defend, and hold each Contributor harmless for any liability
|
| 175 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 176 |
+
of your accepting any such warranty or additional liability.
|
| 177 |
+
|
| 178 |
+
END OF TERMS AND CONDITIONS
|
| 179 |
+
|
| 180 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 181 |
+
|
| 182 |
+
To apply the Apache License to your work, attach the following
|
| 183 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 184 |
+
replaced with your own identifying information. (Don't include
|
| 185 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 186 |
+
comment syntax for the file format. We also recommend that a
|
| 187 |
+
file or class name and description of purpose be included on the
|
| 188 |
+
same "printed page" as the copyright notice for easier
|
| 189 |
+
identification within third-party archives.
|
| 190 |
+
|
| 191 |
+
Copyright [yyyy] [name of copyright owner]
|
| 192 |
+
|
| 193 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 194 |
+
you may not use this file except in compliance with the License.
|
| 195 |
+
You may obtain a copy of the License at
|
| 196 |
+
|
| 197 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 198 |
+
|
| 199 |
+
Unless required by applicable law or agreed to in writing, software
|
| 200 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 201 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 202 |
+
See the License for the specific language governing permissions and
|
| 203 |
+
limitations under the License.
|
README.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
base_model: Qwen/Qwen3.5-0.8B
|
| 4 |
+
pipeline_tag: feature-extraction
|
| 5 |
+
library_name: transformers
|
| 6 |
+
language:
|
| 7 |
+
- en
|
| 8 |
+
tags:
|
| 9 |
+
- qwen3.5
|
| 10 |
+
- classification-backbone
|
| 11 |
+
- knowledge-distillation
|
| 12 |
+
- model-compression
|
| 13 |
+
- edge-ai
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
# Qwen3.5 Standalone 4L Classification Base
|
| 17 |
+
|
| 18 |
+
This repository publishes a **headless, classification-ready four-layer
|
| 19 |
+
Qwen3.5 text backbone**. The root model has no task labels and no classification
|
| 20 |
+
head. It is intended as a compact starting point for single-label or multilabel
|
| 21 |
+
classifiers, not as a chat model or a ready-made universal classifier.
|
| 22 |
+
|
| 23 |
+
The repository follows one integrated 1-1-1 layout:
|
| 24 |
+
|
| 25 |
+
- **Root model:** task-agnostic four-layer base trained on unlabeled general
|
| 26 |
+
text.
|
| 27 |
+
- **`models/semeval-propaganda/`:** the existing SemEval-derived propaganda
|
| 28 |
+
classifier as a separate task-specific lineage.
|
| 29 |
+
- **`distillation/`:** a reusable 24L→8L→6L→4L platform for your own data.
|
| 30 |
+
|
| 31 |
+
## Load the headless base
|
| 32 |
+
|
| 33 |
+
```python
|
| 34 |
+
from transformers import AutoModel, AutoTokenizer
|
| 35 |
+
|
| 36 |
+
model_id = "mp-juuuns/qwen35-standalone4l-classification-base"
|
| 37 |
+
tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=False)
|
| 38 |
+
backbone = AutoModel.from_pretrained(model_id, trust_remote_code=False)
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
The root uses `Qwen3_5TextModel` in Transformers 5.13.0. It has four text
|
| 42 |
+
layers in the sequence `linear → full → linear → full`, hidden size 1,024, and
|
| 43 |
+
the full upstream Qwen3.5 tokenizer. The published weight file contains no
|
| 44 |
+
`score.weight`.
|
| 45 |
+
|
| 46 |
+
## Make your own classifier
|
| 47 |
+
|
| 48 |
+
```bash
|
| 49 |
+
git clone https://huggingface.co/mp-juuuns/qwen35-standalone4l-classification-base
|
| 50 |
+
cd qwen35-standalone4l-classification-base/distillation
|
| 51 |
+
python -m pip install -e .
|
| 52 |
+
|
| 53 |
+
qwen35-distill finetune \
|
| 54 |
+
--checkpoint .. \
|
| 55 |
+
--train examples/multilabel_train.jsonl \
|
| 56 |
+
--labels examples/labels.json \
|
| 57 |
+
--mode multilabel \
|
| 58 |
+
--output my-classifier-4l
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
For a full task-specific compression run, first fine-tune a 24-layer teacher,
|
| 62 |
+
then repeat `materialize-classifier` and `distill-classifier` for `24to8`,
|
| 63 |
+
`8to6`, and `6to4`. Exact JSONL formats and commands are in
|
| 64 |
+
[`distillation/README.md`](distillation/README.md).
|
| 65 |
+
|
| 66 |
+
## How the root weights were made
|
| 67 |
+
|
| 68 |
+
The text backbone and full tokenizer were extracted from
|
| 69 |
+
[`Qwen/Qwen3.5-0.8B`](https://huggingface.co/Qwen/Qwen3.5-0.8B). Students were
|
| 70 |
+
trained sequentially through 24→8→6→4 using hidden-boundary and final
|
| 71 |
+
representation matching on unlabeled text.
|
| 72 |
+
|
| 73 |
+
The task-free training set was exactly 4,096 deterministically shuffled,
|
| 74 |
+
non-empty rows from the first locally cached WikiText-103 raw training shard.
|
| 75 |
+
No SemEval examples, labels, logits, thresholds, or evidence/span objectives
|
| 76 |
+
were read while training these root weights.
|
| 77 |
+
|
| 78 |
+
WikiText is not redistributed here. Its current Hugging Face page has a license
|
| 79 |
+
wording discrepancy: metadata lists CC BY-SA 3.0 and GFDL, while the prose says
|
| 80 |
+
CC BY-SA 4.0. Users should review the
|
| 81 |
+
[`Salesforce/wikitext`](https://huggingface.co/datasets/Salesforce/wikitext)
|
| 82 |
+
terms directly.
|
| 83 |
+
|
| 84 |
+
## One-task transfer check
|
| 85 |
+
|
| 86 |
+
We attached a fresh 14-label head and used the frozen SemEval-derived
|
| 87 |
+
article-level protocol only as a transfer probe.
|
| 88 |
+
|
| 89 |
+
| Initialization | Opened-test Macro-F1, mean ± sample SD |
|
| 90 |
+
|---|---:|
|
| 91 |
+
| Task-agnostic 4L base | **0.58748 ± 0.01579** |
|
| 92 |
+
| Same 4L structural copy without task-free KD | 0.51809 ± 0.01631 |
|
| 93 |
+
| Existing separate specialized 4L lineage | 0.58757 ± 0.00717 |
|
| 94 |
+
|
| 95 |
+
Results use seeds 41/42/43 and a previously opened 55-article public test split.
|
| 96 |
+
They are exploratory, not untouched confirmatory evidence. The derived task
|
| 97 |
+
measures article-level presence of 14 techniques and is not identical to the
|
| 98 |
+
official SemEval span- or fragment-level task. Three seed values are not a
|
| 99 |
+
confidence interval.
|
| 100 |
+
|
| 101 |
+
This check supports transfer on one English classification task only. It does
|
| 102 |
+
not establish universal classification quality, multilingual generalization,
|
| 103 |
+
calibration, safety, or production suitability.
|
| 104 |
+
|
| 105 |
+
## Separate SemEval model
|
| 106 |
+
|
| 107 |
+
The ready-to-use propaganda model is under
|
| 108 |
+
[`models/semeval-propaganda/`](models/semeval-propaganda/). It preserves the
|
| 109 |
+
currently published seed-41/42/43 checkpoints and links to the original
|
| 110 |
+
repository at immutable revision
|
| 111 |
+
[`e7e0ff1`](https://huggingface.co/mp-juuuns/qwen35-standalone4l-propaganda-classifier/tree/e7e0ff16828052687d2e8dd7849e7a521629cf38).
|
| 112 |
+
|
| 113 |
+
That specialized model is **not** claimed to descend from this new task-free
|
| 114 |
+
base. It has its own SemEval task-specific shrink/distillation and fine-tuning
|
| 115 |
+
history and a reduced 128k vocabulary, while the root base retains the full
|
| 116 |
+
upstream tokenizer.
|
| 117 |
+
|
| 118 |
+
## Limitations
|
| 119 |
+
|
| 120 |
+
- A classification head and labeled training are required before root-model
|
| 121 |
+
predictions have task meaning.
|
| 122 |
+
- The task-free corpus is small: 4,096 rows from one WikiText shard.
|
| 123 |
+
- Only one downstream transfer task was measured.
|
| 124 |
+
- Default layer maps are documented structural choices, not universal optima.
|
| 125 |
+
- Long-context, multilingual, calibration, robustness, fairness, and device
|
| 126 |
+
performance of the new root base have not been established.
|
| 127 |
+
- Do not use either model as a fact checker, safety oracle, or autonomous
|
| 128 |
+
decision maker.
|
| 129 |
+
|
| 130 |
+
## License and attribution
|
| 131 |
+
|
| 132 |
+
The released code and model artifacts use Apache-2.0, subject to upstream model
|
| 133 |
+
and data terms. Qwen3.5 is attributed to Qwen. Transformers, PyTorch,
|
| 134 |
+
Hugging Face Hub, WikiText, and SemEval remain the work of their respective
|
| 135 |
+
authors. No WikiText or SemEval source records are redistributed.
|
| 136 |
+
|
| 137 |
+
Machine-readable training manifests, transfer summaries, hashes, and the
|
| 138 |
+
release inventory are included under `provenance/`, `benchmark/`, and
|
| 139 |
+
`release_manifest.json`.
|
| 140 |
+
|
SHA256SUMS
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ee05350c7fda0af4da116020dd7b976d731de3beed4f5e717d5bcc95ead24550 .gitattributes
|
| 2 |
+
77fd4710def9ec3c0f6225800e0235f15a425abd4a8b03559127fcd782612049 LICENSE
|
| 3 |
+
4f5af67b76f19fca88e242298fa501aa6aa811b4f1f51b481d17abb46e0d39cf README.md
|
| 4 |
+
816c3249d3ec281ef73d34dc3ab7576a48415d250c32977eb94ca37f73b0b810 benchmark/reports/structural_control-seed41.json
|
| 5 |
+
0b19eabe1e31d0012c754db3b31173f8a6fcdee30973f1a1bebd3a370d11064c benchmark/reports/structural_control-seed42.json
|
| 6 |
+
6dde34173462eac50141ff082c18194f3233f8c06853532d9b12756562e69e2e benchmark/reports/structural_control-seed43.json
|
| 7 |
+
6b3a2e0372a83e1e71c2eca9596efc8cd1a3e39c28e25e3373f195431b5858a4 benchmark/reports/task_agnostic-seed41.json
|
| 8 |
+
42dc788983716be0e9847aad87ff79c7e7297884f2635a8d098639be77326639 benchmark/reports/task_agnostic-seed42.json
|
| 9 |
+
7ee52dc94a152a2f767dcff171f3c9cc1072fabd3ebc7b14db53617b7b1d33d5 benchmark/reports/task_agnostic-seed43.json
|
| 10 |
+
18c8d840dd2290ca25c517d52cbcf1ad6c6ae8d9c32c545236e96bd5ada7dab2 benchmark/semeval-transfer-summary.json
|
| 11 |
+
04b007131663760bf3e581e5a953be77044014e87efe1d2a6ca4b72ec0eac978 chat_template.jinja
|
| 12 |
+
bb33f3a52d787e15db7d516c6de36a7ddd2c675e7964d5b9309131c4e5d365f0 config.json
|
| 13 |
+
f5b7248f7781412e8b5d8f2fb3b0c309568e4e75baf55029b26a6fa985d9d608 distillation/README.md
|
| 14 |
+
adf8b6e3f0f5a5dfe042dcc09a7a8e7ef8cd1146462b089c5f6c9393e9a3131f distillation/configs/task_agnostic_base.yaml
|
| 15 |
+
8b528ab38eb2e9f53f9ec2c50d4a0c860d87594067dc9b064b4bb47ef51f8f50 distillation/examples/labels.json
|
| 16 |
+
daf5859bb93c474d766ecb0681a2585a6a8ed3cb5efcdd476c845727a0c3583c distillation/examples/multilabel_train.jsonl
|
| 17 |
+
918490e8db364c6e601cfdcb09e28c272eafc8359ffe190645a4cbecdf06aed3 distillation/examples/single_label_train.jsonl
|
| 18 |
+
7a1db673674a003ef749b1fa90a99eff6db70c3189411b9cee574cc23d084860 distillation/examples/unlabeled.jsonl
|
| 19 |
+
3c5a0f9b48849381307a6b3c258729d4079bf5d42bb6ab5d28edbdda4a1a3592 distillation/pyproject.toml
|
| 20 |
+
2965e662310bf4197d1cbd4f9b3fcf3f383950dbfc4f033729a286d026709f3a distillation/qwen35_distill/__init__.py
|
| 21 |
+
d67bf7ed3bfb6cb622d6d35a99a4292f1191395270264e81a7d19283d206e5dc distillation/qwen35_distill/checkpoint.py
|
| 22 |
+
ea06bec682622addc426def4f7e315b3d12f7f595af44f714cc811588cf64782 distillation/qwen35_distill/classification.py
|
| 23 |
+
d8fb1584484596f9c35db9c2c296dab08b2dd9bb1b7910a115710feee7562771 distillation/qwen35_distill/cli.py
|
| 24 |
+
a9c49af0ee14b307a08c096bcd1ae55fa9bc67bdb807fa9383fc313246e7edd7 distillation/qwen35_distill/layer_maps.py
|
| 25 |
+
d9b135cf5b0adf4bbfc321200395fbd6112e49fd1a027b30262bcff1c4c0b654 distillation/qwen35_distill/losses.py
|
| 26 |
+
00731ae7838317105f9fff960e3da210b2a55954feb37b3ac793bcfc81fdab2d distillation/qwen35_distill/schema.py
|
| 27 |
+
4dc432acb70e03848d2e5964e8550065490fda3857aac32fe3b79895ef72026f distillation/qwen35_distill/training.py
|
| 28 |
+
5a1d9e939cd0f350770bcc2a116bc427bb9971bc4fbbbda17ce568e9127185ef docs/RELEASE_CONTRACT.md
|
| 29 |
+
a9d356d7bdf1ef4949e3e748e95b8e10ad9d4e2e838eddc38a0a7b6b94d1db8d merges.txt
|
| 30 |
+
2732c616772fe320cdea228ab4554981418b1b2bf615c4183fb1ac8e6e2168d3 model.safetensors
|
| 31 |
+
77fd4710def9ec3c0f6225800e0235f15a425abd4a8b03559127fcd782612049 models/semeval-propaganda/LICENSE
|
| 32 |
+
8dbc2e641280c471b78064808ced7fffcb81c639784a5936b57175975a718f6f models/semeval-propaganda/README.md
|
| 33 |
+
0bae347bb54f8089a5fa5450f8366b5c56eb3e598beaf9a87f4385363a402bcb models/semeval-propaganda/benchmark/final4l_aggregate.json
|
| 34 |
+
c1bd379680abcf50b47b7d23e4aafbc8b5e7f40a0b0e9700bde21a8ce49af803 models/semeval-propaganda/benchmark/seed41.json
|
| 35 |
+
a30e38433d008db4aa7e5fbfd477ab80a0daf50e970f8e7b04397efc1a47843a models/semeval-propaganda/benchmark/seed42.json
|
| 36 |
+
96466290d09d27b8bc044a9f9a95765ce12b4d7b02dfb0123e436e7535623353 models/semeval-propaganda/benchmark/seed43.json
|
| 37 |
+
04b007131663760bf3e581e5a953be77044014e87efe1d2a6ca4b72ec0eac978 models/semeval-propaganda/chat_template.jinja
|
| 38 |
+
1c09956cc5d3553eed0b5f64c20c81064df4373ccfaf4fec26929b8f4819773a models/semeval-propaganda/classify.py
|
| 39 |
+
9f04dfbd7e1696cd3df2ccbe194f5f155efe5baefd1a0b615851f51bf46691a0 models/semeval-propaganda/config.json
|
| 40 |
+
946fed4fe4c3d320fd113ec8e5776b10f2707b936f857341bb5443d7a9038248 models/semeval-propaganda/labels.json
|
| 41 |
+
0352da6e4717a2e6f3a9d643d034be2cb1c39632d1bce0ced339ddb62acedb37 models/semeval-propaganda/model.safetensors
|
| 42 |
+
ef64dc628fe9bb4efd2a0b6a739f0c842c6fd4586b1803707e7a071e439cdf69 models/semeval-propaganda/release_manifest.json
|
| 43 |
+
2994886af208a0120c051745f9c14036e6f2caae8c4af863e5965d5a5bd68fdf models/semeval-propaganda/requirements.txt
|
| 44 |
+
04b007131663760bf3e581e5a953be77044014e87efe1d2a6ca4b72ec0eac978 models/semeval-propaganda/seeds/seed42/chat_template.jinja
|
| 45 |
+
9f04dfbd7e1696cd3df2ccbe194f5f155efe5baefd1a0b615851f51bf46691a0 models/semeval-propaganda/seeds/seed42/config.json
|
| 46 |
+
2db5f4be391d9dee64458cb5865530f39747019e7a219443b5f7850befbce697 models/semeval-propaganda/seeds/seed42/model.safetensors
|
| 47 |
+
dd2b27bbd4f5f650c7200300013ef03ee01efe620b128343163521bf04806fc8 models/semeval-propaganda/seeds/seed42/tokenizer.json
|
| 48 |
+
2215318fe27878ec67e772931bb5f40a98658348d33f2672a575f766a2190d35 models/semeval-propaganda/seeds/seed42/tokenizer_config.json
|
| 49 |
+
04b007131663760bf3e581e5a953be77044014e87efe1d2a6ca4b72ec0eac978 models/semeval-propaganda/seeds/seed43/chat_template.jinja
|
| 50 |
+
9f04dfbd7e1696cd3df2ccbe194f5f155efe5baefd1a0b615851f51bf46691a0 models/semeval-propaganda/seeds/seed43/config.json
|
| 51 |
+
81a56d497df758e8a292eea528438cfc4d5a847cd37d3a7e1107fefa7c500640 models/semeval-propaganda/seeds/seed43/model.safetensors
|
| 52 |
+
dd2b27bbd4f5f650c7200300013ef03ee01efe620b128343163521bf04806fc8 models/semeval-propaganda/seeds/seed43/tokenizer.json
|
| 53 |
+
2215318fe27878ec67e772931bb5f40a98658348d33f2672a575f766a2190d35 models/semeval-propaganda/seeds/seed43/tokenizer_config.json
|
| 54 |
+
737c4e145a5b51660c4c45a8acea4c32219cbbbc0706abe235e36b2ad430f17d models/semeval-propaganda/thresholds.json
|
| 55 |
+
dd2b27bbd4f5f650c7200300013ef03ee01efe620b128343163521bf04806fc8 models/semeval-propaganda/tokenizer.json
|
| 56 |
+
2215318fe27878ec67e772931bb5f40a98658348d33f2672a575f766a2190d35 models/semeval-propaganda/tokenizer_config.json
|
| 57 |
+
e831b8a121fe896bb9f033287a5b82a0bffad1037a45bbb1ec913fed261add92 provenance/base/finalization_manifest.json
|
| 58 |
+
ec734855870758cbfef99031dac2ded85b54c496532e5c514a9a4f63287c3493 provenance/base/training_manifest.json
|
| 59 |
+
b9ce99b993c4f4563bc2521fc731bfd82f5e8149c141133599fbd8f982ba43d9 provenance/task_agnostic_stages/24to8.json
|
| 60 |
+
ec734855870758cbfef99031dac2ded85b54c496532e5c514a9a4f63287c3493 provenance/task_agnostic_stages/6to4.json
|
| 61 |
+
65e5383afbbcfe0b7dd78a5411c319ed6bf2586659fb1892aca9ecbd5dfd4993 provenance/task_agnostic_stages/8to6.json
|
| 62 |
+
143945a19235351e00048a99c74799e362eefec3b5e3f3a2ad4c8b64611034a4 release_manifest.json
|
| 63 |
+
7b48e7811e56e552fc4aef44189e1d7c9b1352c85d2b3f10ff0bdd1ea5cdd824 requirements.txt
|
| 64 |
+
06b9509352d2af50381ab2247e083b80d32d5c0aba91c272ca9ff729b6a0e523 tokenizer.json
|
| 65 |
+
5ab9bed0a4d27949672f65ba1141d6dd5b0514fb9091d3426506ecebb5d5e294 tokenizer_config.json
|
| 66 |
+
ce99b4cb2983d118806ce0a8b777a35b093e2000a503ebde25853284c9dfa003 vocab.json
|
benchmark/reports/structural_control-seed41.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
benchmark/reports/structural_control-seed42.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
benchmark/reports/structural_control-seed43.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
benchmark/reports/task_agnostic-seed41.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
benchmark/reports/task_agnostic-seed42.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
benchmark/reports/task_agnostic-seed43.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
benchmark/semeval-transfer-summary.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"schema": "standalone4l-transfer-summary-v1",
|
| 3 |
+
"claim_scope": "Exploratory one-task transfer evidence on a previously opened 55-article SemEval-derived test split; not confirmatory.",
|
| 4 |
+
"metric": "article-level multilabel Macro-F1",
|
| 5 |
+
"seeds": [
|
| 6 |
+
41,
|
| 7 |
+
42,
|
| 8 |
+
43
|
| 9 |
+
],
|
| 10 |
+
"task_agnostic_base": {
|
| 11 |
+
"values": [
|
| 12 |
+
0.5883093293517536,
|
| 13 |
+
0.6028379036218096,
|
| 14 |
+
0.5712816197954101
|
| 15 |
+
],
|
| 16 |
+
"mean": 0.5874762842563245,
|
| 17 |
+
"sample_sd": 0.01579462678670608
|
| 18 |
+
},
|
| 19 |
+
"structural_copy_control": {
|
| 20 |
+
"values": [
|
| 21 |
+
0.5169424579262757,
|
| 22 |
+
0.5349405779540175,
|
| 23 |
+
0.5023778364496152
|
| 24 |
+
],
|
| 25 |
+
"mean": 0.5180869574433028,
|
| 26 |
+
"sample_sd": 0.016311512588628545
|
| 27 |
+
},
|
| 28 |
+
"same_seed_deltas": [
|
| 29 |
+
0.07136687142547793,
|
| 30 |
+
0.06789732566779205,
|
| 31 |
+
0.06890378334579494
|
| 32 |
+
],
|
| 33 |
+
"mean_delta": 0.06938932681302164,
|
| 34 |
+
"publication_gate_passed": true,
|
| 35 |
+
"existing_specialized_separate_lineage": {
|
| 36 |
+
"values": [
|
| 37 |
+
0.5832848452288824,
|
| 38 |
+
0.5958405181160762,
|
| 39 |
+
0.5835785045218137
|
| 40 |
+
],
|
| 41 |
+
"mean": 0.5875679559555907,
|
| 42 |
+
"sample_sd": 0.007165593191426643
|
| 43 |
+
},
|
| 44 |
+
"test_articles": 55,
|
| 45 |
+
"test_previously_opened": true,
|
| 46 |
+
"three_seed_sd_is_not_confidence_interval": true
|
| 47 |
+
}
|
chat_template.jinja
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{%- set image_count = namespace(value=0) %}
|
| 2 |
+
{%- set video_count = namespace(value=0) %}
|
| 3 |
+
{%- macro render_content(content, do_vision_count) %}
|
| 4 |
+
{%- if content is string %}
|
| 5 |
+
{{- content }}
|
| 6 |
+
{%- else %}
|
| 7 |
+
{%- for item in content %}
|
| 8 |
+
{%- if 'image' in item or 'image_url' in item or item.type == 'image' %}
|
| 9 |
+
{%- if do_vision_count %}
|
| 10 |
+
{%- set image_count.value = image_count.value + 1 %}
|
| 11 |
+
{%- endif %}
|
| 12 |
+
{%- if add_vision_id %}Picture {{ image_count.value }}: {% endif -%}
|
| 13 |
+
<|vision_start|><|image_pad|><|vision_end|>
|
| 14 |
+
{%- elif 'video' in item or item.type == 'video' %}
|
| 15 |
+
{%- if do_vision_count %}
|
| 16 |
+
{%- set video_count.value = video_count.value + 1 %}
|
| 17 |
+
{%- endif %}
|
| 18 |
+
{%- if add_vision_id %}Video {{ video_count.value }}: {% endif -%}
|
| 19 |
+
<|vision_start|><|video_pad|><|vision_end|>
|
| 20 |
+
{%- elif 'text' in item %}
|
| 21 |
+
{{- item.text }}
|
| 22 |
+
{%- endif %}
|
| 23 |
+
{%- endfor %}
|
| 24 |
+
{%- endif %}
|
| 25 |
+
{%- endmacro %}
|
| 26 |
+
{%- if tools is defined and tools is iterable and tools | length > 0 %}
|
| 27 |
+
{{- '<|im_start|>system\n' }}
|
| 28 |
+
{{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
|
| 29 |
+
{%- for tool in tools %}
|
| 30 |
+
{{- "\n" }}
|
| 31 |
+
{{- tool | tojson }}
|
| 32 |
+
{%- endfor %}
|
| 33 |
+
{{- "\n</tools>" }}
|
| 34 |
+
{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
|
| 35 |
+
{%- if messages[0].role == 'system' and messages[0].content | trim %}
|
| 36 |
+
{%- set content = render_content(messages[0].content, false) %}
|
| 37 |
+
{{- '\n\n' + content.lstrip('\n') }}
|
| 38 |
+
{%- endif %}
|
| 39 |
+
{{- '<|im_end|>\n' }}
|
| 40 |
+
{%- else %}
|
| 41 |
+
{%- if messages[0].role == 'system' %}
|
| 42 |
+
{{- '<|im_start|>system\n' + render_content(messages[0].content, false) + '<|im_end|>\n' }}
|
| 43 |
+
{%- endif %}
|
| 44 |
+
{%- endif %}
|
| 45 |
+
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
| 46 |
+
{%- for message in messages[::-1] %}
|
| 47 |
+
{%- set index = (messages|length - 1) - loop.index0 %}
|
| 48 |
+
{%- if ns.multi_step_tool and message.role == "user" %}
|
| 49 |
+
{%- set content = render_content(message.content, False) %}
|
| 50 |
+
{%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
|
| 51 |
+
{%- set ns.multi_step_tool = false %}
|
| 52 |
+
{%- set ns.last_query_index = index %}
|
| 53 |
+
{%- endif %}
|
| 54 |
+
{%- endif %}
|
| 55 |
+
{%- endfor %}
|
| 56 |
+
{%- for message in messages %}
|
| 57 |
+
{%- set content = render_content(message.content, true) %}
|
| 58 |
+
{%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
|
| 59 |
+
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
|
| 60 |
+
{%- elif message.role == "assistant" %}
|
| 61 |
+
{%- set reasoning_content = '' %}
|
| 62 |
+
{%- if message.reasoning_content is defined and message.reasoning_content is not none %}
|
| 63 |
+
{%- set reasoning_content = message.reasoning_content %}
|
| 64 |
+
{%- else %}
|
| 65 |
+
{%- if '</think>' in content %}
|
| 66 |
+
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
| 67 |
+
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
|
| 68 |
+
{%- endif %}
|
| 69 |
+
{%- endif %}
|
| 70 |
+
{%- if message.tool_calls is defined and message.tool_calls is not none and message.tool_calls is iterable and message.tool_calls | length > 0 %}
|
| 71 |
+
{%- set content = content.rstrip('\n') %}
|
| 72 |
+
{%- endif %}
|
| 73 |
+
{%- if loop.index0 > ns.last_query_index %}
|
| 74 |
+
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
|
| 75 |
+
{%- else %}
|
| 76 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 77 |
+
{%- endif %}
|
| 78 |
+
{%- if message.tool_calls is defined and message.tool_calls is not none and message.tool_calls is iterable and message.tool_calls | length > 0 %}
|
| 79 |
+
{%- for tool_call in message.tool_calls %}
|
| 80 |
+
{%- if tool_call.function is defined %}
|
| 81 |
+
{%- set tool_call = tool_call.function %}
|
| 82 |
+
{%- endif %}
|
| 83 |
+
{%- if loop.first %}
|
| 84 |
+
{%- if content|trim %}
|
| 85 |
+
{{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 86 |
+
{%- else %}
|
| 87 |
+
{{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 88 |
+
{%- endif %}
|
| 89 |
+
{%- else %}
|
| 90 |
+
{{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 91 |
+
{%- endif %}
|
| 92 |
+
{%- if tool_call.arguments is defined %}
|
| 93 |
+
{%- for args_name, args_value in tool_call.arguments|items %}
|
| 94 |
+
{{- '<parameter=' + args_name + '>\n' }}
|
| 95 |
+
{%- set args_value = args_value | tojson | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string %}
|
| 96 |
+
{{- args_value }}
|
| 97 |
+
{{- '\n</parameter>\n' }}
|
| 98 |
+
{%- endfor %}
|
| 99 |
+
{%- endif %}
|
| 100 |
+
{{- '</function>\n</tool_call>' }}
|
| 101 |
+
{%- endfor %}
|
| 102 |
+
{%- endif %}
|
| 103 |
+
{{- '<|im_end|>\n' }}
|
| 104 |
+
{%- elif message.role == "tool" %}
|
| 105 |
+
{%- if loop.previtem and loop.previtem.role != "tool" %}
|
| 106 |
+
{{- '<|im_start|>user' }}
|
| 107 |
+
{%- endif %}
|
| 108 |
+
{{- '\n<tool_response>\n' }}
|
| 109 |
+
{{- content }}
|
| 110 |
+
{{- '\n</tool_response>' }}
|
| 111 |
+
{%- if not loop.last and loop.nextitem.role != "tool" %}
|
| 112 |
+
{{- '<|im_end|>\n' }}
|
| 113 |
+
{%- elif loop.last %}
|
| 114 |
+
{{- '<|im_end|>\n' }}
|
| 115 |
+
{%- endif %}
|
| 116 |
+
{%- endif %}
|
| 117 |
+
{%- endfor %}
|
| 118 |
+
{%- if add_generation_prompt %}
|
| 119 |
+
{{- '<|im_start|>assistant\n<think>\n' }}
|
| 120 |
+
{%- endif %}
|
config.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"Qwen3_5TextModel"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"attn_output_gate": true,
|
| 8 |
+
"bos_token_id": null,
|
| 9 |
+
"dtype": "bfloat16",
|
| 10 |
+
"eos_token_id": 248046,
|
| 11 |
+
"full_attention_interval": 4,
|
| 12 |
+
"head_dim": 256,
|
| 13 |
+
"hidden_act": "silu",
|
| 14 |
+
"hidden_size": 1024,
|
| 15 |
+
"initializer_range": 0.02,
|
| 16 |
+
"intermediate_size": 3584,
|
| 17 |
+
"layer_types": [
|
| 18 |
+
"linear_attention",
|
| 19 |
+
"full_attention",
|
| 20 |
+
"linear_attention",
|
| 21 |
+
"full_attention"
|
| 22 |
+
],
|
| 23 |
+
"linear_conv_kernel_dim": 4,
|
| 24 |
+
"linear_key_head_dim": 128,
|
| 25 |
+
"linear_num_key_heads": 16,
|
| 26 |
+
"linear_num_value_heads": 16,
|
| 27 |
+
"linear_value_head_dim": 128,
|
| 28 |
+
"mamba_ssm_dtype": "float32",
|
| 29 |
+
"max_position_embeddings": 262144,
|
| 30 |
+
"mlp_only_layers": [],
|
| 31 |
+
"model_type": "qwen3_5_text",
|
| 32 |
+
"mtp_num_hidden_layers": 1,
|
| 33 |
+
"mtp_use_dedicated_embeddings": false,
|
| 34 |
+
"num_attention_heads": 8,
|
| 35 |
+
"num_hidden_layers": 4,
|
| 36 |
+
"num_key_value_heads": 2,
|
| 37 |
+
"pad_token_id": 248044,
|
| 38 |
+
"partial_rotary_factor": 0.25,
|
| 39 |
+
"rms_norm_eps": 1e-06,
|
| 40 |
+
"rope_parameters": {
|
| 41 |
+
"mrope_interleaved": true,
|
| 42 |
+
"mrope_section": [
|
| 43 |
+
11,
|
| 44 |
+
11,
|
| 45 |
+
10
|
| 46 |
+
],
|
| 47 |
+
"partial_rotary_factor": 0.25,
|
| 48 |
+
"rope_theta": 10000000,
|
| 49 |
+
"rope_type": "default"
|
| 50 |
+
},
|
| 51 |
+
"standalone4l_distillation": {
|
| 52 |
+
"schema_version": "qwen35-stage-map-v1",
|
| 53 |
+
"selected_layers": [
|
| 54 |
+
0,
|
| 55 |
+
2,
|
| 56 |
+
3,
|
| 57 |
+
5
|
| 58 |
+
],
|
| 59 |
+
"selection_basis": "predeclared_architecture_only_default",
|
| 60 |
+
"source_depth": 6,
|
| 61 |
+
"stage": "6to4",
|
| 62 |
+
"target_depth": 4,
|
| 63 |
+
"task_labels_used_for_weight_materialization": false
|
| 64 |
+
},
|
| 65 |
+
"standalone4l_role": "task_agnostic_classification_base_4l",
|
| 66 |
+
"tie_word_embeddings": true,
|
| 67 |
+
"transformers_version": "5.13.0",
|
| 68 |
+
"use_cache": false,
|
| 69 |
+
"vocab_size": 248320,
|
| 70 |
+
"standalone4l_base": {
|
| 71 |
+
"schema_version": "standalone4l-task-agnostic-base-v1",
|
| 72 |
+
"role": "classification_ready_headless_text_backbone",
|
| 73 |
+
"task_labels_used_in_base_training": false,
|
| 74 |
+
"semeval_used_in_base_training": false,
|
| 75 |
+
"tokenizer": "upstream_full_qwen35",
|
| 76 |
+
"training_corpus_rows": 4096,
|
| 77 |
+
"training_objective": "hidden_and_interface_representation_distillation",
|
| 78 |
+
"published_classification_head": false
|
| 79 |
+
}
|
| 80 |
+
}
|
distillation/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Qwen3.5 Classification Distillation
|
| 2 |
+
|
| 3 |
+
This package turns the text backbone of `Qwen/Qwen3.5-0.8B` into smaller
|
| 4 |
+
classification-ready backbones and task-specific classifiers.
|
| 5 |
+
|
| 6 |
+
Two workflows are intentionally separate:
|
| 7 |
+
|
| 8 |
+
1. **Task-agnostic base:** hidden/interface distillation on unlabeled text.
|
| 9 |
+
It produces a headless text backbone and never reads task labels.
|
| 10 |
+
2. **Task-specific classifier:** attach a single-label or multilabel head,
|
| 11 |
+
train a 24-layer teacher on a user dataset, then transfer hard labels,
|
| 12 |
+
teacher logits, and hidden/interface representations through
|
| 13 |
+
`24L → 8L → 6L → 4L`.
|
| 14 |
+
|
| 15 |
+
The published default maps are structural defaults, not a claim that they are
|
| 16 |
+
universally optimal:
|
| 17 |
+
|
| 18 |
+
| Stage | Source layers retained | Target layer types |
|
| 19 |
+
|---|---|---|
|
| 20 |
+
| 24L → 8L | `0,4,6,11,13,16,20,23` | `L,L,L,F,L,L,L,F` |
|
| 21 |
+
| 8L → 6L | `0,1,3,4,6,7` | `L,L,F,L,L,F` |
|
| 22 |
+
| 6L → 4L | `0,2,3,5` | `L,F,L,F` |
|
| 23 |
+
|
| 24 |
+
The final 6L→4L numeric map overlaps the earlier SemEval experiment, but the
|
| 25 |
+
task-agnostic base uses the fixed structural contract above and no SemEval
|
| 26 |
+
labels, examples, logits, thresholds, or span objectives. Architecture choice
|
| 27 |
+
and task-free weight training are reported as separate provenance claims.
|
| 28 |
+
|
| 29 |
+
## Dataset formats
|
| 30 |
+
|
| 31 |
+
Unlabeled:
|
| 32 |
+
|
| 33 |
+
```json
|
| 34 |
+
{"id":"u-1","text":"Unlabeled text used only for representation distillation."}
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
Single-label:
|
| 38 |
+
|
| 39 |
+
```json
|
| 40 |
+
{"id":"s-1","text":"Example text.","label":"class_a"}
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
Multilabel:
|
| 44 |
+
|
| 45 |
+
```json
|
| 46 |
+
{"id":"m-1","text":"Example text.","labels":["class_a","class_c"]}
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
Each split is a separate JSONL file. `id` must be unique across all splits.
|
| 50 |
+
An optional `group_id` is checked for cross-split leakage.
|
| 51 |
+
|
| 52 |
+
## Initial commands
|
| 53 |
+
|
| 54 |
+
```bash
|
| 55 |
+
python -m qwen35_distill.cli validate-dataset \
|
| 56 |
+
--mode unlabeled --train examples/unlabeled.jsonl
|
| 57 |
+
|
| 58 |
+
python -m qwen35_distill.cli extract-text \
|
| 59 |
+
--source /path/to/Qwen3.5-0.8B \
|
| 60 |
+
--output /path/to/text-24l
|
| 61 |
+
|
| 62 |
+
python -m qwen35_distill.cli materialize \
|
| 63 |
+
--source /path/to/text-24l \
|
| 64 |
+
--output /path/to/text-8l \
|
| 65 |
+
--stage 24to8
|
| 66 |
+
|
| 67 |
+
python -m qwen35_distill.cli distill \
|
| 68 |
+
--teacher /path/to/text-24l \
|
| 69 |
+
--student /path/to/text-8l \
|
| 70 |
+
--data /path/to/unlabeled.parquet \
|
| 71 |
+
--output /path/to/text-8l-kd \
|
| 72 |
+
--stage 24to8 --device cuda --dtype bfloat16
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
Every output is exclusive: existing directories are never overwritten.
|
| 76 |
+
|
| 77 |
+
## Build a classifier from your own labels
|
| 78 |
+
|
| 79 |
+
Put the exact label order in `labels.json`, then train a teacher or attach a
|
| 80 |
+
head directly to the published 4-layer base:
|
| 81 |
+
|
| 82 |
+
```bash
|
| 83 |
+
qwen35-distill finetune \
|
| 84 |
+
--checkpoint /path/to/text-24l \
|
| 85 |
+
--train train.jsonl --validation validation.jsonl \
|
| 86 |
+
--labels labels.json --mode multilabel \
|
| 87 |
+
--output classifier-24l
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
To preserve the classifier head while following the full compression chain,
|
| 91 |
+
repeat these two commands for `24to8`, `8to6`, and `6to4`:
|
| 92 |
+
|
| 93 |
+
```bash
|
| 94 |
+
qwen35-distill materialize-classifier \
|
| 95 |
+
--source classifier-24l --stage 24to8 \
|
| 96 |
+
--output classifier-8l-init
|
| 97 |
+
|
| 98 |
+
qwen35-distill distill-classifier \
|
| 99 |
+
--teacher classifier-24l --checkpoint classifier-8l-init \
|
| 100 |
+
--stage 24to8 --train train.jsonl --validation validation.jsonl \
|
| 101 |
+
--labels labels.json --mode multilabel \
|
| 102 |
+
--output classifier-8l
|
| 103 |
+
```
|
| 104 |
+
|
| 105 |
+
The task-specific distillation objective combines the user labels, teacher
|
| 106 |
+
logits, and aligned hidden boundaries. The teacher hashes, dataset hashes,
|
| 107 |
+
label order, environment, loss history, and fresh-reload check are written to
|
| 108 |
+
each output manifest.
|
distillation/configs/task_agnostic_base.yaml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
schema_version: qwen35-distill-config-v1
|
| 2 |
+
mode: task_agnostic
|
| 3 |
+
tokenizer: upstream_full
|
| 4 |
+
stages:
|
| 5 |
+
- 24to8
|
| 6 |
+
- 8to6
|
| 7 |
+
- 6to4
|
| 8 |
+
training:
|
| 9 |
+
seed: 41
|
| 10 |
+
epochs: 1
|
| 11 |
+
max_rows: 50000
|
| 12 |
+
max_length: 128
|
| 13 |
+
batch_size: 1
|
| 14 |
+
gradient_accumulation: 8
|
| 15 |
+
learning_rate: 0.00002
|
| 16 |
+
claims:
|
| 17 |
+
task_labels_used: false
|
| 18 |
+
semeval_used: false
|
| 19 |
+
universal_generalization_claimed: false
|
distillation/examples/labels.json
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
"tag_a",
|
| 3 |
+
"tag_b",
|
| 4 |
+
"tag_c"
|
| 5 |
+
]
|
distillation/examples/multilabel_train.jsonl
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{"id":"m-1","text":"A short example with two tags.","labels":["tag_a","tag_b"],"group_id":"g-1"}
|
| 2 |
+
{"id":"m-2","text":"A second example with one tag.","labels":["tag_c"],"group_id":"g-2"}
|
distillation/examples/single_label_train.jsonl
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{"id":"s-1","text":"The package arrived early.","label":"positive","group_id":"g-1"}
|
| 2 |
+
{"id":"s-2","text":"The package did not arrive.","label":"negative","group_id":"g-2"}
|
distillation/examples/unlabeled.jsonl
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{"id":"u-1","text":"A small public example for task-agnostic representation distillation."}
|
| 2 |
+
{"id":"u-2","text":"Users should replace these rows with their own licensed text corpus."}
|
distillation/pyproject.toml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[build-system]
|
| 2 |
+
requires = ["setuptools>=68"]
|
| 3 |
+
build-backend = "setuptools.build_meta"
|
| 4 |
+
|
| 5 |
+
[project]
|
| 6 |
+
name = "qwen35-classification-distill"
|
| 7 |
+
version = "0.1.0"
|
| 8 |
+
description = "Auditable Qwen3.5 24L→8L→6L→4L classification distillation"
|
| 9 |
+
requires-python = ">=3.11"
|
| 10 |
+
dependencies = [
|
| 11 |
+
"torch>=2.4",
|
| 12 |
+
"transformers==5.13.0",
|
| 13 |
+
"safetensors>=0.4",
|
| 14 |
+
"PyYAML>=6",
|
| 15 |
+
"pyarrow>=16",
|
| 16 |
+
]
|
| 17 |
+
|
| 18 |
+
[project.scripts]
|
| 19 |
+
qwen35-distill = "qwen35_distill.cli:main"
|
| 20 |
+
|
| 21 |
+
[tool.setuptools]
|
| 22 |
+
packages = ["qwen35_distill"]
|
distillation/qwen35_distill/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Auditable Qwen3.5 classification distillation."""
|
| 2 |
+
|
| 3 |
+
from .layer_maps import DEFAULT_STAGE_MAPS, StageMap
|
| 4 |
+
|
| 5 |
+
__all__ = ["DEFAULT_STAGE_MAPS", "StageMap"]
|
| 6 |
+
__version__ = "0.1.0"
|
distillation/qwen35_distill/checkpoint.py
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import copy
|
| 4 |
+
import hashlib
|
| 5 |
+
import json
|
| 6 |
+
import shutil
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
from typing import Any, Iterable
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
from safetensors import safe_open
|
| 12 |
+
|
| 13 |
+
from .layer_maps import StageMap
|
| 14 |
+
from .schema import sha256_file
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def _weight_files(path: Path) -> list[Path]:
|
| 18 |
+
index = path / "model.safetensors.index.json"
|
| 19 |
+
if index.is_file():
|
| 20 |
+
document = json.loads(index.read_text(encoding="utf-8"))
|
| 21 |
+
names = sorted(set(document["weight_map"].values()))
|
| 22 |
+
files = [path / name for name in names]
|
| 23 |
+
else:
|
| 24 |
+
files = sorted(path.glob("*.safetensors"))
|
| 25 |
+
if not files:
|
| 26 |
+
raise FileNotFoundError(f"no safetensors weights in {path}")
|
| 27 |
+
for file in files:
|
| 28 |
+
if not file.is_file():
|
| 29 |
+
raise FileNotFoundError(file)
|
| 30 |
+
return files
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def checkpoint_hashes(path: Path) -> dict[str, str]:
|
| 34 |
+
names = [
|
| 35 |
+
"config.json",
|
| 36 |
+
"tokenizer.json",
|
| 37 |
+
"tokenizer_config.json",
|
| 38 |
+
"vocab.json",
|
| 39 |
+
"merges.txt",
|
| 40 |
+
]
|
| 41 |
+
files = _weight_files(path) + [path / name for name in names if (path / name).is_file()]
|
| 42 |
+
return {file.name: sha256_file(file) for file in files}
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def _load_source_tensors(
|
| 46 |
+
files: Iterable[Path], *, prefix: str
|
| 47 |
+
) -> dict[str, torch.Tensor]:
|
| 48 |
+
state: dict[str, torch.Tensor] = {}
|
| 49 |
+
for file in files:
|
| 50 |
+
with safe_open(file, framework="pt", device="cpu") as handle:
|
| 51 |
+
for name in handle.keys():
|
| 52 |
+
if prefix and not name.startswith(prefix):
|
| 53 |
+
continue
|
| 54 |
+
target_name = name.removeprefix(prefix)
|
| 55 |
+
if target_name in state:
|
| 56 |
+
raise ValueError(f"duplicate tensor {target_name}")
|
| 57 |
+
state[target_name] = handle.get_tensor(name)
|
| 58 |
+
return state
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def _copy_tokenizer_files(source: Path, output: Path) -> None:
|
| 62 |
+
for name in (
|
| 63 |
+
"tokenizer.json",
|
| 64 |
+
"tokenizer_config.json",
|
| 65 |
+
"vocab.json",
|
| 66 |
+
"merges.txt",
|
| 67 |
+
"chat_template.jinja",
|
| 68 |
+
"special_tokens_map.json",
|
| 69 |
+
):
|
| 70 |
+
path = source / name
|
| 71 |
+
if path.is_file():
|
| 72 |
+
shutil.copy2(path, output / name)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def extract_text_checkpoint(source: Path, output: Path) -> dict[str, Any]:
|
| 76 |
+
"""Extract the 24-layer text backbone from the official multimodal checkpoint."""
|
| 77 |
+
from transformers.models.qwen3_5.configuration_qwen3_5 import Qwen3_5TextConfig
|
| 78 |
+
from transformers.models.qwen3_5.modeling_qwen3_5 import (
|
| 79 |
+
Qwen3_5TextModel,
|
| 80 |
+
Qwen3_5TextRotaryEmbedding,
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
source = source.resolve()
|
| 84 |
+
output = output.resolve()
|
| 85 |
+
if output.exists():
|
| 86 |
+
raise FileExistsError(output)
|
| 87 |
+
document = json.loads((source / "config.json").read_text(encoding="utf-8"))
|
| 88 |
+
if "text_config" not in document:
|
| 89 |
+
raise ValueError("source is not the official multimodal Qwen3.5 layout")
|
| 90 |
+
config_document = copy.deepcopy(document["text_config"])
|
| 91 |
+
if int(config_document["num_hidden_layers"]) != 24:
|
| 92 |
+
raise ValueError("expected a 24-layer text backbone")
|
| 93 |
+
config_document["architectures"] = ["Qwen3_5TextModel"]
|
| 94 |
+
config_document["use_cache"] = False
|
| 95 |
+
config_document["standalone4l_role"] = "task_agnostic_teacher_24l"
|
| 96 |
+
config = Qwen3_5TextConfig(**config_document)
|
| 97 |
+
with torch.device("meta"):
|
| 98 |
+
model = Qwen3_5TextModel(config)
|
| 99 |
+
mapped = _load_source_tensors(
|
| 100 |
+
_weight_files(source), prefix="model.language_model."
|
| 101 |
+
)
|
| 102 |
+
expected = set(model.state_dict())
|
| 103 |
+
if set(mapped) != expected:
|
| 104 |
+
raise ValueError(
|
| 105 |
+
f"text extraction mismatch: missing={sorted(expected-set(mapped))[:10]}, "
|
| 106 |
+
f"unexpected={sorted(set(mapped)-expected)[:10]}"
|
| 107 |
+
)
|
| 108 |
+
model.load_state_dict(mapped, strict=True, assign=True)
|
| 109 |
+
fresh_rotary = Qwen3_5TextRotaryEmbedding(config, device="cpu")
|
| 110 |
+
model.rotary_emb.inv_freq = fresh_rotary.inv_freq
|
| 111 |
+
model.rotary_emb.original_inv_freq = fresh_rotary.original_inv_freq
|
| 112 |
+
output.mkdir(parents=True, exist_ok=False)
|
| 113 |
+
model.eval().save_pretrained(output, safe_serialization=True)
|
| 114 |
+
_copy_tokenizer_files(source, output)
|
| 115 |
+
report = {
|
| 116 |
+
"schema_version": "qwen35-text-extraction-v1",
|
| 117 |
+
"source": str(source),
|
| 118 |
+
"output": str(output),
|
| 119 |
+
"source_hashes": checkpoint_hashes(source),
|
| 120 |
+
"output_hashes": checkpoint_hashes(output),
|
| 121 |
+
"tensor_count": len(mapped),
|
| 122 |
+
"semeval_or_task_labels_used": False,
|
| 123 |
+
}
|
| 124 |
+
(output / "extraction_manifest.json").write_text(
|
| 125 |
+
json.dumps(report, indent=2, ensure_ascii=False) + "\n", encoding="utf-8"
|
| 126 |
+
)
|
| 127 |
+
return report
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def map_layer_tensor_name(name: str, selected_layers: tuple[int, ...]) -> str | None:
|
| 131 |
+
prefix = "layers."
|
| 132 |
+
if not name.startswith(prefix):
|
| 133 |
+
return name
|
| 134 |
+
suffix = name.removeprefix(prefix)
|
| 135 |
+
parts = suffix.split(".", 1)
|
| 136 |
+
if len(parts) != 2:
|
| 137 |
+
raise ValueError(f"invalid layer tensor name: {name}")
|
| 138 |
+
source_index = int(parts[0])
|
| 139 |
+
try:
|
| 140 |
+
target_index = selected_layers.index(source_index)
|
| 141 |
+
except ValueError:
|
| 142 |
+
return None
|
| 143 |
+
return f"layers.{target_index}.{parts[1]}"
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def materialize_student(
|
| 147 |
+
source: Path, output: Path, stage: StageMap
|
| 148 |
+
) -> dict[str, Any]:
|
| 149 |
+
from transformers import AutoConfig
|
| 150 |
+
from transformers.models.qwen3_5.modeling_qwen3_5 import (
|
| 151 |
+
Qwen3_5TextModel,
|
| 152 |
+
Qwen3_5TextRotaryEmbedding,
|
| 153 |
+
)
|
| 154 |
+
|
| 155 |
+
source = source.resolve()
|
| 156 |
+
output = output.resolve()
|
| 157 |
+
if output.exists():
|
| 158 |
+
raise FileExistsError(output)
|
| 159 |
+
source_config = AutoConfig.from_pretrained(source, local_files_only=True)
|
| 160 |
+
target_types = stage.validate(source_config.layer_types)
|
| 161 |
+
target_config = copy.deepcopy(source_config)
|
| 162 |
+
target_config.num_hidden_layers = stage.target_depth
|
| 163 |
+
target_config.layer_types = list(target_types)
|
| 164 |
+
target_config.architectures = ["Qwen3_5TextModel"]
|
| 165 |
+
target_config.use_cache = False
|
| 166 |
+
target_config.standalone4l_distillation = {
|
| 167 |
+
"schema_version": "qwen35-stage-map-v1",
|
| 168 |
+
"stage": stage.name,
|
| 169 |
+
"source_depth": stage.source_depth,
|
| 170 |
+
"target_depth": stage.target_depth,
|
| 171 |
+
"selected_layers": list(stage.selected_layers),
|
| 172 |
+
"selection_basis": "predeclared_architecture_only_default",
|
| 173 |
+
"task_labels_used_for_weight_materialization": False,
|
| 174 |
+
}
|
| 175 |
+
with torch.device("meta"):
|
| 176 |
+
model = Qwen3_5TextModel(target_config)
|
| 177 |
+
source_state = _load_source_tensors(_weight_files(source), prefix="")
|
| 178 |
+
mapped: dict[str, torch.Tensor] = {}
|
| 179 |
+
for source_name, tensor in source_state.items():
|
| 180 |
+
target_name = map_layer_tensor_name(source_name, stage.selected_layers)
|
| 181 |
+
if target_name is None:
|
| 182 |
+
continue
|
| 183 |
+
if target_name in mapped:
|
| 184 |
+
raise ValueError(f"duplicate mapped tensor {target_name}")
|
| 185 |
+
mapped[target_name] = tensor
|
| 186 |
+
expected = set(model.state_dict())
|
| 187 |
+
if set(mapped) != expected:
|
| 188 |
+
raise ValueError(
|
| 189 |
+
f"student mapping mismatch: missing={sorted(expected-set(mapped))[:10]}, "
|
| 190 |
+
f"unexpected={sorted(set(mapped)-expected)[:10]}"
|
| 191 |
+
)
|
| 192 |
+
model.load_state_dict(mapped, strict=True, assign=True)
|
| 193 |
+
fresh_rotary = Qwen3_5TextRotaryEmbedding(target_config, device="cpu")
|
| 194 |
+
model.rotary_emb.inv_freq = fresh_rotary.inv_freq
|
| 195 |
+
model.rotary_emb.original_inv_freq = fresh_rotary.original_inv_freq
|
| 196 |
+
output.mkdir(parents=True, exist_ok=False)
|
| 197 |
+
model.eval().save_pretrained(output, safe_serialization=True)
|
| 198 |
+
_copy_tokenizer_files(source, output)
|
| 199 |
+
report = {
|
| 200 |
+
"schema_version": "qwen35-student-materialization-v1",
|
| 201 |
+
"stage": stage.name,
|
| 202 |
+
"source": str(source),
|
| 203 |
+
"output": str(output),
|
| 204 |
+
"selected_layers": list(stage.selected_layers),
|
| 205 |
+
"source_hashes": checkpoint_hashes(source),
|
| 206 |
+
"output_hashes": checkpoint_hashes(output),
|
| 207 |
+
"mapped_tensor_count": len(mapped),
|
| 208 |
+
"task_labels_used": False,
|
| 209 |
+
}
|
| 210 |
+
(output / "materialization_manifest.json").write_text(
|
| 211 |
+
json.dumps(report, indent=2, ensure_ascii=False) + "\n", encoding="utf-8"
|
| 212 |
+
)
|
| 213 |
+
return report
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
def finalize_base_checkpoint(
|
| 217 |
+
source: Path,
|
| 218 |
+
output: Path,
|
| 219 |
+
*,
|
| 220 |
+
training_corpus_rows: int = 4096,
|
| 221 |
+
training_objective: str = "hidden_and_interface_representation_distillation",
|
| 222 |
+
) -> dict[str, Any]:
|
| 223 |
+
"""Create a classification-ready, headless canonical base without changing weights."""
|
| 224 |
+
from transformers import AutoModel, AutoModelForSequenceClassification, AutoTokenizer
|
| 225 |
+
|
| 226 |
+
if training_corpus_rows < 0:
|
| 227 |
+
raise ValueError("training_corpus_rows must be non-negative")
|
| 228 |
+
if not training_objective.strip():
|
| 229 |
+
raise ValueError("training_objective must be non-empty")
|
| 230 |
+
source = source.resolve()
|
| 231 |
+
output = output.resolve()
|
| 232 |
+
if output.exists():
|
| 233 |
+
raise FileExistsError(output)
|
| 234 |
+
source_hashes = checkpoint_hashes(source)
|
| 235 |
+
config_path = source / "config.json"
|
| 236 |
+
document = json.loads(config_path.read_text(encoding="utf-8"))
|
| 237 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
| 238 |
+
source, local_files_only=True, trust_remote_code=False
|
| 239 |
+
)
|
| 240 |
+
if tokenizer.eos_token_id is None:
|
| 241 |
+
raise ValueError("tokenizer has no EOS token for classification padding")
|
| 242 |
+
if tokenizer.pad_token_id is None:
|
| 243 |
+
tokenizer.pad_token = tokenizer.eos_token
|
| 244 |
+
document["architectures"] = ["Qwen3_5TextModel"]
|
| 245 |
+
document["pad_token_id"] = int(tokenizer.pad_token_id)
|
| 246 |
+
document["eos_token_id"] = int(tokenizer.eos_token_id)
|
| 247 |
+
document["use_cache"] = False
|
| 248 |
+
document["standalone4l_role"] = "task_agnostic_classification_base_4l"
|
| 249 |
+
document.pop("id2label", None)
|
| 250 |
+
document.pop("label2id", None)
|
| 251 |
+
document.pop("num_labels", None)
|
| 252 |
+
document.pop("problem_type", None)
|
| 253 |
+
document["standalone4l_base"] = {
|
| 254 |
+
"schema_version": "standalone4l-task-agnostic-base-v1",
|
| 255 |
+
"role": "classification_ready_headless_text_backbone",
|
| 256 |
+
"task_labels_used_in_base_training": False,
|
| 257 |
+
"semeval_used_in_base_training": False,
|
| 258 |
+
"tokenizer": "upstream_full_qwen35",
|
| 259 |
+
"training_corpus_rows": training_corpus_rows,
|
| 260 |
+
"training_objective": training_objective,
|
| 261 |
+
"published_classification_head": False,
|
| 262 |
+
}
|
| 263 |
+
shutil.copytree(source, output)
|
| 264 |
+
(output / "config.json").write_text(
|
| 265 |
+
json.dumps(document, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
|
| 266 |
+
)
|
| 267 |
+
tokenizer.save_pretrained(output)
|
| 268 |
+
weight_hashes_before = {
|
| 269 |
+
name: value
|
| 270 |
+
for name, value in source_hashes.items()
|
| 271 |
+
if name.endswith(".safetensors")
|
| 272 |
+
}
|
| 273 |
+
weight_hashes_after = {
|
| 274 |
+
name: value
|
| 275 |
+
for name, value in checkpoint_hashes(output).items()
|
| 276 |
+
if name.endswith(".safetensors")
|
| 277 |
+
}
|
| 278 |
+
if weight_hashes_before != weight_hashes_after:
|
| 279 |
+
raise RuntimeError("canonical finalization changed model weight bytes")
|
| 280 |
+
base = AutoModel.from_pretrained(
|
| 281 |
+
output, local_files_only=True, trust_remote_code=False, dtype=torch.bfloat16
|
| 282 |
+
).eval()
|
| 283 |
+
batch = tokenizer(
|
| 284 |
+
["A short classification input.", "A second input."],
|
| 285 |
+
return_tensors="pt",
|
| 286 |
+
padding=True,
|
| 287 |
+
)
|
| 288 |
+
with torch.inference_mode():
|
| 289 |
+
hidden = base(**batch, use_cache=False).last_hidden_state
|
| 290 |
+
if not bool(torch.isfinite(hidden).all()):
|
| 291 |
+
raise RuntimeError("canonical base reload produced non-finite hidden states")
|
| 292 |
+
classifier = AutoModelForSequenceClassification.from_pretrained(
|
| 293 |
+
output,
|
| 294 |
+
local_files_only=True,
|
| 295 |
+
trust_remote_code=False,
|
| 296 |
+
num_labels=3,
|
| 297 |
+
ignore_mismatched_sizes=True,
|
| 298 |
+
dtype=torch.bfloat16,
|
| 299 |
+
).eval()
|
| 300 |
+
with torch.inference_mode():
|
| 301 |
+
logits = classifier(**batch).logits
|
| 302 |
+
if logits.shape != (2, 3) or not bool(torch.isfinite(logits).all()):
|
| 303 |
+
raise RuntimeError("classification-head attachment validation failed")
|
| 304 |
+
report = {
|
| 305 |
+
"schema_version": "standalone4l-base-finalization-v1",
|
| 306 |
+
"source": str(source),
|
| 307 |
+
"output": str(output),
|
| 308 |
+
"source_hashes": source_hashes,
|
| 309 |
+
"output_hashes": checkpoint_hashes(output),
|
| 310 |
+
"weight_bytes_preserved": True,
|
| 311 |
+
"pad_token_id": int(tokenizer.pad_token_id),
|
| 312 |
+
"eos_token_id": int(tokenizer.eos_token_id),
|
| 313 |
+
"base_reload_finite": True,
|
| 314 |
+
"classification_head_attachment": {
|
| 315 |
+
"validated": True,
|
| 316 |
+
"test_num_labels": 3,
|
| 317 |
+
"test_batch_size": 2,
|
| 318 |
+
"published_head": False,
|
| 319 |
+
},
|
| 320 |
+
}
|
| 321 |
+
(output / "finalization_manifest.json").write_text(
|
| 322 |
+
json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
|
| 323 |
+
)
|
| 324 |
+
return report
|
distillation/qwen35_distill/classification.py
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import copy
|
| 4 |
+
import json
|
| 5 |
+
import math
|
| 6 |
+
import platform
|
| 7 |
+
import random
|
| 8 |
+
import shutil
|
| 9 |
+
from datetime import datetime
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
from typing import Any
|
| 12 |
+
from zoneinfo import ZoneInfo
|
| 13 |
+
|
| 14 |
+
import torch
|
| 15 |
+
from torch.optim import AdamW
|
| 16 |
+
from transformers import AutoConfig, AutoModelForSequenceClassification, AutoTokenizer
|
| 17 |
+
from transformers.models.qwen3_5.modeling_qwen3_5 import (
|
| 18 |
+
Qwen3_5TextForSequenceClassification,
|
| 19 |
+
Qwen3_5TextRotaryEmbedding,
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
from .checkpoint import (
|
| 23 |
+
_copy_tokenizer_files,
|
| 24 |
+
_load_source_tensors,
|
| 25 |
+
_weight_files,
|
| 26 |
+
checkpoint_hashes,
|
| 27 |
+
map_layer_tensor_name,
|
| 28 |
+
)
|
| 29 |
+
from .layer_maps import StageMap
|
| 30 |
+
from .losses import classification_distillation_loss, task_agnostic_boundary_loss
|
| 31 |
+
from .schema import TaskMode, read_jsonl, validate_dataset
|
| 32 |
+
from .training import _dtype
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def labels_from_file(path: Path) -> list[str]:
|
| 36 |
+
labels = json.loads(path.read_text(encoding="utf-8"))
|
| 37 |
+
if not isinstance(labels, list) or any(
|
| 38 |
+
not isinstance(label, str) or not label for label in labels
|
| 39 |
+
):
|
| 40 |
+
raise ValueError("labels file must be a JSON string list")
|
| 41 |
+
if len(set(labels)) != len(labels) or len(labels) < 2:
|
| 42 |
+
raise ValueError("labels must contain at least two unique values")
|
| 43 |
+
return labels
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def encode_targets(
|
| 47 |
+
rows: list[dict[str, Any]], *, mode: TaskMode, labels: list[str]
|
| 48 |
+
) -> torch.Tensor:
|
| 49 |
+
label_to_id = {label: index for index, label in enumerate(labels)}
|
| 50 |
+
if mode == "single_label":
|
| 51 |
+
return torch.tensor([label_to_id[row["label"]] for row in rows], dtype=torch.long)
|
| 52 |
+
if mode == "multilabel":
|
| 53 |
+
target = torch.zeros((len(rows), len(labels)), dtype=torch.float32)
|
| 54 |
+
for row_index, row in enumerate(rows):
|
| 55 |
+
for label in row["labels"]:
|
| 56 |
+
target[row_index, label_to_id[label]] = 1.0
|
| 57 |
+
return target
|
| 58 |
+
raise ValueError("classification requires single_label or multilabel mode")
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def materialize_classifier(
|
| 62 |
+
source: Path, output: Path, stage: StageMap
|
| 63 |
+
) -> dict[str, Any]:
|
| 64 |
+
"""Copy a trained classifier head and the selected backbone layers."""
|
| 65 |
+
source = source.resolve()
|
| 66 |
+
output = output.resolve()
|
| 67 |
+
if output.exists():
|
| 68 |
+
raise FileExistsError(output)
|
| 69 |
+
config = AutoConfig.from_pretrained(source, local_files_only=True)
|
| 70 |
+
target_types = stage.validate(config.layer_types)
|
| 71 |
+
target_config = copy.deepcopy(config)
|
| 72 |
+
target_config.num_hidden_layers = stage.target_depth
|
| 73 |
+
target_config.layer_types = list(target_types)
|
| 74 |
+
target_config.architectures = ["Qwen3_5TextForSequenceClassification"]
|
| 75 |
+
target_config.use_cache = False
|
| 76 |
+
target_config.standalone4l_distillation = {
|
| 77 |
+
"schema_version": "qwen35-classifier-stage-map-v1",
|
| 78 |
+
"stage": stage.name,
|
| 79 |
+
"selected_layers": list(stage.selected_layers),
|
| 80 |
+
"classification_head_copied": True,
|
| 81 |
+
}
|
| 82 |
+
with torch.device("meta"):
|
| 83 |
+
model = Qwen3_5TextForSequenceClassification(target_config)
|
| 84 |
+
source_state = _load_source_tensors(_weight_files(source), prefix="")
|
| 85 |
+
mapped: dict[str, torch.Tensor] = {}
|
| 86 |
+
for source_name, tensor in source_state.items():
|
| 87 |
+
if source_name.startswith("model.layers."):
|
| 88 |
+
target_name = "model." + (
|
| 89 |
+
map_layer_tensor_name(
|
| 90 |
+
source_name.removeprefix("model."), stage.selected_layers
|
| 91 |
+
)
|
| 92 |
+
or ""
|
| 93 |
+
)
|
| 94 |
+
if target_name == "model.":
|
| 95 |
+
continue
|
| 96 |
+
else:
|
| 97 |
+
target_name = source_name
|
| 98 |
+
if target_name in mapped:
|
| 99 |
+
raise ValueError(f"duplicate mapped tensor {target_name}")
|
| 100 |
+
mapped[target_name] = tensor
|
| 101 |
+
expected = set(model.state_dict())
|
| 102 |
+
if set(mapped) != expected:
|
| 103 |
+
raise ValueError(
|
| 104 |
+
f"classifier mapping mismatch: missing={sorted(expected-set(mapped))[:10]}, "
|
| 105 |
+
f"unexpected={sorted(set(mapped)-expected)[:10]}"
|
| 106 |
+
)
|
| 107 |
+
model.load_state_dict(mapped, strict=True, assign=True)
|
| 108 |
+
fresh_rotary = Qwen3_5TextRotaryEmbedding(target_config, device="cpu")
|
| 109 |
+
model.model.rotary_emb.inv_freq = fresh_rotary.inv_freq
|
| 110 |
+
model.model.rotary_emb.original_inv_freq = fresh_rotary.original_inv_freq
|
| 111 |
+
output.mkdir(parents=True, exist_ok=False)
|
| 112 |
+
model.eval().save_pretrained(output, safe_serialization=True)
|
| 113 |
+
_copy_tokenizer_files(source, output)
|
| 114 |
+
report = {
|
| 115 |
+
"schema_version": "qwen35-classifier-materialization-v1",
|
| 116 |
+
"stage": stage.name,
|
| 117 |
+
"source": str(source),
|
| 118 |
+
"output": str(output),
|
| 119 |
+
"selected_layers": list(stage.selected_layers),
|
| 120 |
+
"head_copied": True,
|
| 121 |
+
"source_hashes": checkpoint_hashes(source),
|
| 122 |
+
"output_hashes": checkpoint_hashes(output),
|
| 123 |
+
}
|
| 124 |
+
(output / "materialization_manifest.json").write_text(
|
| 125 |
+
json.dumps(report, indent=2, ensure_ascii=False) + "\n", encoding="utf-8"
|
| 126 |
+
)
|
| 127 |
+
return report
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def _batch_rows(
|
| 131 |
+
rows: list[dict[str, Any]], batch_size: int, seed: int
|
| 132 |
+
) -> list[list[dict[str, Any]]]:
|
| 133 |
+
shuffled = list(rows)
|
| 134 |
+
random.Random(seed).shuffle(shuffled)
|
| 135 |
+
return [shuffled[i : i + batch_size] for i in range(0, len(shuffled), batch_size)]
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def _prepare_model(
|
| 139 |
+
checkpoint: Path,
|
| 140 |
+
*,
|
| 141 |
+
labels: list[str],
|
| 142 |
+
mode: TaskMode,
|
| 143 |
+
dtype: torch.dtype,
|
| 144 |
+
attach_head: bool,
|
| 145 |
+
) -> Qwen3_5TextForSequenceClassification:
|
| 146 |
+
kwargs = {
|
| 147 |
+
"local_files_only": True,
|
| 148 |
+
"dtype": dtype,
|
| 149 |
+
"num_labels": len(labels),
|
| 150 |
+
"id2label": {index: label for index, label in enumerate(labels)},
|
| 151 |
+
"label2id": {label: index for index, label in enumerate(labels)},
|
| 152 |
+
"problem_type": (
|
| 153 |
+
"single_label_classification"
|
| 154 |
+
if mode == "single_label"
|
| 155 |
+
else "multi_label_classification"
|
| 156 |
+
),
|
| 157 |
+
}
|
| 158 |
+
if attach_head:
|
| 159 |
+
kwargs["ignore_mismatched_sizes"] = True
|
| 160 |
+
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, **kwargs)
|
| 161 |
+
if not isinstance(model, Qwen3_5TextForSequenceClassification):
|
| 162 |
+
raise TypeError("checkpoint did not load as Qwen3_5TextForSequenceClassification")
|
| 163 |
+
return model
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
def _train_classifier(
|
| 167 |
+
*,
|
| 168 |
+
model: Qwen3_5TextForSequenceClassification,
|
| 169 |
+
tokenizer: Any,
|
| 170 |
+
train_rows: list[dict[str, Any]],
|
| 171 |
+
labels: list[str],
|
| 172 |
+
mode: TaskMode,
|
| 173 |
+
device: torch.device,
|
| 174 |
+
max_length: int,
|
| 175 |
+
epochs: int,
|
| 176 |
+
batch_size: int,
|
| 177 |
+
gradient_accumulation: int,
|
| 178 |
+
learning_rate: float,
|
| 179 |
+
seed: int,
|
| 180 |
+
teacher: Qwen3_5TextForSequenceClassification | None = None,
|
| 181 |
+
stage: StageMap | None = None,
|
| 182 |
+
temperature: float = 2.0,
|
| 183 |
+
hard_weight: float = 1.0,
|
| 184 |
+
logit_weight: float = 1.0,
|
| 185 |
+
hidden_weight: float = 1.0,
|
| 186 |
+
) -> list[dict[str, Any]]:
|
| 187 |
+
model.train()
|
| 188 |
+
model.gradient_checkpointing_enable()
|
| 189 |
+
optimizer = AdamW(model.parameters(), lr=learning_rate, weight_decay=0.01)
|
| 190 |
+
history: list[dict[str, Any]] = []
|
| 191 |
+
optimizer.zero_grad(set_to_none=True)
|
| 192 |
+
for epoch in range(1, epochs + 1):
|
| 193 |
+
total = 0.0
|
| 194 |
+
batches = _batch_rows(train_rows, batch_size, seed + epoch)
|
| 195 |
+
for batch_index, rows in enumerate(batches, start=1):
|
| 196 |
+
encoded = tokenizer(
|
| 197 |
+
[row["text"] for row in rows],
|
| 198 |
+
return_tensors="pt",
|
| 199 |
+
padding=True,
|
| 200 |
+
truncation=True,
|
| 201 |
+
max_length=max_length,
|
| 202 |
+
)
|
| 203 |
+
encoded = {key: value.to(device) for key, value in encoded.items()}
|
| 204 |
+
targets = encode_targets(rows, mode=mode, labels=labels).to(device)
|
| 205 |
+
output = model(**encoded, output_hidden_states=True, use_cache=False)
|
| 206 |
+
if teacher is None:
|
| 207 |
+
if mode == "single_label":
|
| 208 |
+
loss = torch.nn.functional.cross_entropy(
|
| 209 |
+
output.logits.float(), targets
|
| 210 |
+
)
|
| 211 |
+
else:
|
| 212 |
+
loss = torch.nn.functional.binary_cross_entropy_with_logits(
|
| 213 |
+
output.logits.float(), targets.float()
|
| 214 |
+
)
|
| 215 |
+
else:
|
| 216 |
+
assert stage is not None
|
| 217 |
+
with torch.inference_mode():
|
| 218 |
+
teacher_output = teacher(
|
| 219 |
+
**encoded, output_hidden_states=True, use_cache=False
|
| 220 |
+
)
|
| 221 |
+
classification_loss, _ = classification_distillation_loss(
|
| 222 |
+
output.logits,
|
| 223 |
+
teacher_output.logits,
|
| 224 |
+
targets,
|
| 225 |
+
mode=mode,
|
| 226 |
+
temperature=temperature,
|
| 227 |
+
hard_weight=hard_weight,
|
| 228 |
+
logit_weight=logit_weight,
|
| 229 |
+
)
|
| 230 |
+
hidden_loss, _ = task_agnostic_boundary_loss(
|
| 231 |
+
output.hidden_states,
|
| 232 |
+
teacher_output.hidden_states,
|
| 233 |
+
stage.teacher_boundary_indices(),
|
| 234 |
+
encoded["attention_mask"],
|
| 235 |
+
)
|
| 236 |
+
loss = classification_loss + hidden_weight * hidden_loss
|
| 237 |
+
if not bool(torch.isfinite(loss)):
|
| 238 |
+
raise FloatingPointError(f"non-finite loss at epoch {epoch} batch {batch_index}")
|
| 239 |
+
(loss / gradient_accumulation).backward()
|
| 240 |
+
total += float(loss.detach())
|
| 241 |
+
if (
|
| 242 |
+
batch_index % gradient_accumulation == 0
|
| 243 |
+
or batch_index == len(batches)
|
| 244 |
+
):
|
| 245 |
+
norm = torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
|
| 246 |
+
if not math.isfinite(float(norm)):
|
| 247 |
+
raise FloatingPointError("non-finite gradient norm")
|
| 248 |
+
optimizer.step()
|
| 249 |
+
optimizer.zero_grad(set_to_none=True)
|
| 250 |
+
value = total / len(batches)
|
| 251 |
+
history.append({"epoch": epoch, "batches": len(batches), "mean_loss": value})
|
| 252 |
+
print(
|
| 253 |
+
json.dumps(
|
| 254 |
+
{"event": "classifier_epoch", "epoch": epoch, "mean_loss": value},
|
| 255 |
+
ensure_ascii=False,
|
| 256 |
+
),
|
| 257 |
+
flush=True,
|
| 258 |
+
)
|
| 259 |
+
return history
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
def train_classifier(
|
| 263 |
+
*,
|
| 264 |
+
checkpoint: Path,
|
| 265 |
+
train_path: Path,
|
| 266 |
+
validation_path: Path | None,
|
| 267 |
+
labels_path: Path,
|
| 268 |
+
output_path: Path,
|
| 269 |
+
mode: TaskMode,
|
| 270 |
+
device_name: str,
|
| 271 |
+
dtype_name: str,
|
| 272 |
+
max_length: int,
|
| 273 |
+
epochs: int,
|
| 274 |
+
batch_size: int,
|
| 275 |
+
gradient_accumulation: int,
|
| 276 |
+
learning_rate: float,
|
| 277 |
+
seed: int,
|
| 278 |
+
teacher_path: Path | None = None,
|
| 279 |
+
stage: StageMap | None = None,
|
| 280 |
+
) -> dict[str, Any]:
|
| 281 |
+
if output_path.exists():
|
| 282 |
+
raise FileExistsError(output_path)
|
| 283 |
+
if mode not in ("single_label", "multilabel"):
|
| 284 |
+
raise ValueError("mode must be single_label or multilabel")
|
| 285 |
+
labels = labels_from_file(labels_path)
|
| 286 |
+
split_paths = {"train": train_path}
|
| 287 |
+
if validation_path:
|
| 288 |
+
split_paths["validation"] = validation_path
|
| 289 |
+
audit = validate_dataset(split_paths, mode=mode, labels=labels)
|
| 290 |
+
rows = read_jsonl(train_path)
|
| 291 |
+
device = torch.device(device_name)
|
| 292 |
+
if device.type == "cuda" and not torch.cuda.is_available():
|
| 293 |
+
raise RuntimeError("CUDA requested but unavailable")
|
| 294 |
+
dtype = _dtype(dtype_name)
|
| 295 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
| 296 |
+
checkpoint, local_files_only=True, trust_remote_code=False
|
| 297 |
+
)
|
| 298 |
+
if tokenizer.pad_token_id is None:
|
| 299 |
+
tokenizer.pad_token = tokenizer.eos_token
|
| 300 |
+
model = _prepare_model(
|
| 301 |
+
checkpoint,
|
| 302 |
+
labels=labels,
|
| 303 |
+
mode=mode,
|
| 304 |
+
dtype=dtype,
|
| 305 |
+
attach_head=teacher_path is None,
|
| 306 |
+
).to(device)
|
| 307 |
+
model.config.pad_token_id = tokenizer.pad_token_id
|
| 308 |
+
teacher = None
|
| 309 |
+
teacher_hashes = None
|
| 310 |
+
if teacher_path is not None:
|
| 311 |
+
if stage is None:
|
| 312 |
+
raise ValueError("distillation requires a stage")
|
| 313 |
+
teacher_hashes = checkpoint_hashes(teacher_path)
|
| 314 |
+
teacher = _prepare_model(
|
| 315 |
+
teacher_path,
|
| 316 |
+
labels=labels,
|
| 317 |
+
mode=mode,
|
| 318 |
+
dtype=dtype,
|
| 319 |
+
attach_head=False,
|
| 320 |
+
).to(device).eval()
|
| 321 |
+
stage.validate(teacher.config.layer_types)
|
| 322 |
+
for parameter in teacher.parameters():
|
| 323 |
+
parameter.requires_grad_(False)
|
| 324 |
+
history = _train_classifier(
|
| 325 |
+
model=model,
|
| 326 |
+
tokenizer=tokenizer,
|
| 327 |
+
train_rows=rows,
|
| 328 |
+
labels=labels,
|
| 329 |
+
mode=mode,
|
| 330 |
+
device=device,
|
| 331 |
+
max_length=max_length,
|
| 332 |
+
epochs=epochs,
|
| 333 |
+
batch_size=batch_size,
|
| 334 |
+
gradient_accumulation=gradient_accumulation,
|
| 335 |
+
learning_rate=learning_rate,
|
| 336 |
+
seed=seed,
|
| 337 |
+
teacher=teacher,
|
| 338 |
+
stage=stage,
|
| 339 |
+
)
|
| 340 |
+
if teacher_path is not None and checkpoint_hashes(teacher_path) != teacher_hashes:
|
| 341 |
+
raise RuntimeError("teacher checkpoint changed during classifier distillation")
|
| 342 |
+
output_path.mkdir(parents=True, exist_ok=False)
|
| 343 |
+
model.eval().save_pretrained(output_path, safe_serialization=True)
|
| 344 |
+
tokenizer.save_pretrained(output_path)
|
| 345 |
+
reloaded = AutoModelForSequenceClassification.from_pretrained(
|
| 346 |
+
output_path, local_files_only=True, dtype=dtype
|
| 347 |
+
).to(device).eval()
|
| 348 |
+
sample = tokenizer(rows[0]["text"], return_tensors="pt", truncation=True, max_length=max_length)
|
| 349 |
+
sample = {key: value.to(device) for key, value in sample.items()}
|
| 350 |
+
with torch.inference_mode():
|
| 351 |
+
logits = reloaded(**sample).logits
|
| 352 |
+
if logits.shape != (1, len(labels)) or not bool(torch.isfinite(logits).all()):
|
| 353 |
+
raise RuntimeError("fresh classifier reload failed")
|
| 354 |
+
report = {
|
| 355 |
+
"schema_version": "qwen35-classifier-training-v1",
|
| 356 |
+
"status": "completed",
|
| 357 |
+
"timestamp_kst": datetime.now(ZoneInfo("Asia/Seoul")).isoformat(),
|
| 358 |
+
"workflow": "task_specific_distillation" if teacher_path else "supervised_finetune",
|
| 359 |
+
"mode": mode,
|
| 360 |
+
"labels": labels,
|
| 361 |
+
"checkpoint": str(checkpoint.resolve()),
|
| 362 |
+
"teacher": str(teacher_path.resolve()) if teacher_path else None,
|
| 363 |
+
"stage": stage.name if stage else None,
|
| 364 |
+
"dataset_audit": audit,
|
| 365 |
+
"training": {
|
| 366 |
+
"seed": seed,
|
| 367 |
+
"epochs": epochs,
|
| 368 |
+
"batch_size": batch_size,
|
| 369 |
+
"gradient_accumulation": gradient_accumulation,
|
| 370 |
+
"max_length": max_length,
|
| 371 |
+
"learning_rate": learning_rate,
|
| 372 |
+
"history": history,
|
| 373 |
+
},
|
| 374 |
+
"environment": {
|
| 375 |
+
"python": platform.python_version(),
|
| 376 |
+
"torch": torch.__version__,
|
| 377 |
+
"device": str(device),
|
| 378 |
+
"dtype": dtype_name,
|
| 379 |
+
},
|
| 380 |
+
"fresh_reload_finite": True,
|
| 381 |
+
"output_hashes": checkpoint_hashes(output_path),
|
| 382 |
+
}
|
| 383 |
+
(output_path / "training_manifest.json").write_text(
|
| 384 |
+
json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
|
| 385 |
+
)
|
| 386 |
+
return report
|
distillation/qwen35_distill/cli.py
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import json
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
from .checkpoint import (
|
| 8 |
+
extract_text_checkpoint,
|
| 9 |
+
finalize_base_checkpoint,
|
| 10 |
+
materialize_student,
|
| 11 |
+
)
|
| 12 |
+
from .classification import materialize_classifier, train_classifier
|
| 13 |
+
from .layer_maps import get_stage
|
| 14 |
+
from .schema import validate_dataset
|
| 15 |
+
from .training import task_agnostic_distill
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def _print(value: object) -> None:
|
| 19 |
+
print(json.dumps(value, ensure_ascii=False, indent=2), flush=True)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def build_parser() -> argparse.ArgumentParser:
|
| 23 |
+
parser = argparse.ArgumentParser(prog="qwen35-distill")
|
| 24 |
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
| 25 |
+
|
| 26 |
+
validate = subparsers.add_parser("validate-dataset")
|
| 27 |
+
validate.add_argument(
|
| 28 |
+
"--mode", choices=("unlabeled", "single_label", "multilabel"), required=True
|
| 29 |
+
)
|
| 30 |
+
validate.add_argument("--train", type=Path, required=True)
|
| 31 |
+
validate.add_argument("--validation", type=Path)
|
| 32 |
+
validate.add_argument("--test", type=Path)
|
| 33 |
+
validate.add_argument("--labels", type=Path)
|
| 34 |
+
|
| 35 |
+
extract = subparsers.add_parser("extract-text")
|
| 36 |
+
extract.add_argument("--source", type=Path, required=True)
|
| 37 |
+
extract.add_argument("--output", type=Path, required=True)
|
| 38 |
+
|
| 39 |
+
materialize = subparsers.add_parser("materialize")
|
| 40 |
+
materialize.add_argument("--source", type=Path, required=True)
|
| 41 |
+
materialize.add_argument("--output", type=Path, required=True)
|
| 42 |
+
materialize.add_argument(
|
| 43 |
+
"--stage", choices=("24to8", "8to6", "6to4"), required=True
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
materialize_head = subparsers.add_parser("materialize-classifier")
|
| 47 |
+
materialize_head.add_argument("--source", type=Path, required=True)
|
| 48 |
+
materialize_head.add_argument("--output", type=Path, required=True)
|
| 49 |
+
materialize_head.add_argument(
|
| 50 |
+
"--stage", choices=("24to8", "8to6", "6to4"), required=True
|
| 51 |
+
)
|
| 52 |
+
|
| 53 |
+
finalize = subparsers.add_parser("finalize-base")
|
| 54 |
+
finalize.add_argument("--source", type=Path, required=True)
|
| 55 |
+
finalize.add_argument("--output", type=Path, required=True)
|
| 56 |
+
finalize.add_argument("--training-corpus-rows", type=int, default=4096)
|
| 57 |
+
finalize.add_argument(
|
| 58 |
+
"--training-objective",
|
| 59 |
+
default="hidden_and_interface_representation_distillation",
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
distill = subparsers.add_parser("distill")
|
| 63 |
+
distill.add_argument("--teacher", type=Path, required=True)
|
| 64 |
+
distill.add_argument("--student", type=Path, required=True)
|
| 65 |
+
distill.add_argument("--data", type=Path, required=True)
|
| 66 |
+
distill.add_argument("--output", type=Path, required=True)
|
| 67 |
+
distill.add_argument(
|
| 68 |
+
"--stage", choices=("24to8", "8to6", "6to4"), required=True
|
| 69 |
+
)
|
| 70 |
+
distill.add_argument("--device", default="cuda")
|
| 71 |
+
distill.add_argument(
|
| 72 |
+
"--dtype", choices=("float32", "float16", "bfloat16"), default="bfloat16"
|
| 73 |
+
)
|
| 74 |
+
distill.add_argument("--max-rows", type=int, default=50000)
|
| 75 |
+
distill.add_argument("--max-length", type=int, default=128)
|
| 76 |
+
distill.add_argument("--epochs", type=int, default=1)
|
| 77 |
+
distill.add_argument("--batch-size", type=int, default=1)
|
| 78 |
+
distill.add_argument("--gradient-accumulation", type=int, default=8)
|
| 79 |
+
distill.add_argument("--learning-rate", type=float, default=2e-5)
|
| 80 |
+
distill.add_argument("--seed", type=int, default=41)
|
| 81 |
+
distill.add_argument("--progress-every", type=int, default=100)
|
| 82 |
+
|
| 83 |
+
for name in ("finetune", "distill-classifier"):
|
| 84 |
+
command = subparsers.add_parser(name)
|
| 85 |
+
command.add_argument("--checkpoint", type=Path, required=True)
|
| 86 |
+
command.add_argument("--train", type=Path, required=True)
|
| 87 |
+
command.add_argument("--validation", type=Path)
|
| 88 |
+
command.add_argument("--labels", type=Path, required=True)
|
| 89 |
+
command.add_argument("--output", type=Path, required=True)
|
| 90 |
+
command.add_argument(
|
| 91 |
+
"--mode", choices=("single_label", "multilabel"), required=True
|
| 92 |
+
)
|
| 93 |
+
command.add_argument("--device", default="cuda")
|
| 94 |
+
command.add_argument(
|
| 95 |
+
"--dtype",
|
| 96 |
+
choices=("float32", "float16", "bfloat16"),
|
| 97 |
+
default="bfloat16",
|
| 98 |
+
)
|
| 99 |
+
command.add_argument("--max-length", type=int, default=256)
|
| 100 |
+
command.add_argument("--epochs", type=int, default=3)
|
| 101 |
+
command.add_argument("--batch-size", type=int, default=1)
|
| 102 |
+
command.add_argument("--gradient-accumulation", type=int, default=8)
|
| 103 |
+
command.add_argument("--learning-rate", type=float, default=2e-5)
|
| 104 |
+
command.add_argument("--seed", type=int, default=41)
|
| 105 |
+
if name == "distill-classifier":
|
| 106 |
+
command.add_argument("--teacher", type=Path, required=True)
|
| 107 |
+
command.add_argument(
|
| 108 |
+
"--stage", choices=("24to8", "8to6", "6to4"), required=True
|
| 109 |
+
)
|
| 110 |
+
return parser
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def main() -> None:
|
| 114 |
+
args = build_parser().parse_args()
|
| 115 |
+
if args.command == "validate-dataset":
|
| 116 |
+
paths = {"train": args.train}
|
| 117 |
+
if args.validation:
|
| 118 |
+
paths["validation"] = args.validation
|
| 119 |
+
if args.test:
|
| 120 |
+
paths["test"] = args.test
|
| 121 |
+
labels = None
|
| 122 |
+
if args.labels:
|
| 123 |
+
value = json.loads(args.labels.read_text(encoding="utf-8"))
|
| 124 |
+
if not isinstance(value, list) or any(
|
| 125 |
+
not isinstance(label, str) for label in value
|
| 126 |
+
):
|
| 127 |
+
raise ValueError("--labels must contain a JSON string list")
|
| 128 |
+
labels = value
|
| 129 |
+
_print(validate_dataset(paths, mode=args.mode, labels=labels))
|
| 130 |
+
elif args.command == "extract-text":
|
| 131 |
+
_print(extract_text_checkpoint(args.source, args.output))
|
| 132 |
+
elif args.command == "materialize":
|
| 133 |
+
_print(materialize_student(args.source, args.output, get_stage(args.stage)))
|
| 134 |
+
elif args.command == "materialize-classifier":
|
| 135 |
+
_print(materialize_classifier(args.source, args.output, get_stage(args.stage)))
|
| 136 |
+
elif args.command == "finalize-base":
|
| 137 |
+
_print(
|
| 138 |
+
finalize_base_checkpoint(
|
| 139 |
+
args.source,
|
| 140 |
+
args.output,
|
| 141 |
+
training_corpus_rows=args.training_corpus_rows,
|
| 142 |
+
training_objective=args.training_objective,
|
| 143 |
+
)
|
| 144 |
+
)
|
| 145 |
+
elif args.command == "distill":
|
| 146 |
+
_print(
|
| 147 |
+
task_agnostic_distill(
|
| 148 |
+
teacher_path=args.teacher,
|
| 149 |
+
student_path=args.student,
|
| 150 |
+
data_path=args.data,
|
| 151 |
+
output_path=args.output,
|
| 152 |
+
stage=get_stage(args.stage),
|
| 153 |
+
device_name=args.device,
|
| 154 |
+
dtype_name=args.dtype,
|
| 155 |
+
max_rows=args.max_rows,
|
| 156 |
+
max_length=args.max_length,
|
| 157 |
+
epochs=args.epochs,
|
| 158 |
+
batch_size=args.batch_size,
|
| 159 |
+
gradient_accumulation=args.gradient_accumulation,
|
| 160 |
+
learning_rate=args.learning_rate,
|
| 161 |
+
seed=args.seed,
|
| 162 |
+
progress_every=args.progress_every,
|
| 163 |
+
)
|
| 164 |
+
)
|
| 165 |
+
elif args.command in ("finetune", "distill-classifier"):
|
| 166 |
+
_print(
|
| 167 |
+
train_classifier(
|
| 168 |
+
checkpoint=args.checkpoint,
|
| 169 |
+
train_path=args.train,
|
| 170 |
+
validation_path=args.validation,
|
| 171 |
+
labels_path=args.labels,
|
| 172 |
+
output_path=args.output,
|
| 173 |
+
mode=args.mode,
|
| 174 |
+
device_name=args.device,
|
| 175 |
+
dtype_name=args.dtype,
|
| 176 |
+
max_length=args.max_length,
|
| 177 |
+
epochs=args.epochs,
|
| 178 |
+
batch_size=args.batch_size,
|
| 179 |
+
gradient_accumulation=args.gradient_accumulation,
|
| 180 |
+
learning_rate=args.learning_rate,
|
| 181 |
+
seed=args.seed,
|
| 182 |
+
teacher_path=(
|
| 183 |
+
args.teacher if args.command == "distill-classifier" else None
|
| 184 |
+
),
|
| 185 |
+
stage=(
|
| 186 |
+
get_stage(args.stage)
|
| 187 |
+
if args.command == "distill-classifier"
|
| 188 |
+
else None
|
| 189 |
+
),
|
| 190 |
+
)
|
| 191 |
+
)
|
| 192 |
+
else:
|
| 193 |
+
raise AssertionError(args.command)
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
if __name__ == "__main__":
|
| 197 |
+
main()
|
distillation/qwen35_distill/layer_maps.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from dataclasses import dataclass
|
| 4 |
+
from typing import Sequence
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
LINEAR = "linear_attention"
|
| 8 |
+
FULL = "full_attention"
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@dataclass(frozen=True)
|
| 12 |
+
class StageMap:
|
| 13 |
+
name: str
|
| 14 |
+
source_depth: int
|
| 15 |
+
target_depth: int
|
| 16 |
+
selected_layers: tuple[int, ...]
|
| 17 |
+
expected_source_types: tuple[str, ...]
|
| 18 |
+
expected_target_types: tuple[str, ...]
|
| 19 |
+
|
| 20 |
+
def validate(self, source_types: Sequence[str]) -> tuple[str, ...]:
|
| 21 |
+
observed = tuple(str(value) for value in source_types)
|
| 22 |
+
if len(observed) != self.source_depth:
|
| 23 |
+
raise ValueError(
|
| 24 |
+
f"{self.name}: source depth {len(observed)} != {self.source_depth}"
|
| 25 |
+
)
|
| 26 |
+
if observed != self.expected_source_types:
|
| 27 |
+
raise ValueError(f"{self.name}: source layer-type contract mismatch")
|
| 28 |
+
if len(self.selected_layers) != self.target_depth:
|
| 29 |
+
raise ValueError(f"{self.name}: selected-layer count mismatch")
|
| 30 |
+
if self.selected_layers[0] != 0:
|
| 31 |
+
raise ValueError(f"{self.name}: first source layer must be retained")
|
| 32 |
+
if self.selected_layers[-1] != self.source_depth - 1:
|
| 33 |
+
raise ValueError(f"{self.name}: final source layer must be retained")
|
| 34 |
+
if tuple(sorted(set(self.selected_layers))) != self.selected_layers:
|
| 35 |
+
raise ValueError(f"{self.name}: selected layers must be sorted and unique")
|
| 36 |
+
target = tuple(observed[index] for index in self.selected_layers)
|
| 37 |
+
if target != self.expected_target_types:
|
| 38 |
+
raise ValueError(
|
| 39 |
+
f"{self.name}: target types {target} != {self.expected_target_types}"
|
| 40 |
+
)
|
| 41 |
+
return target
|
| 42 |
+
|
| 43 |
+
def teacher_boundary_indices(self) -> tuple[int, ...]:
|
| 44 |
+
"""Teacher hidden-state indices aligned to every student boundary."""
|
| 45 |
+
return self.selected_layers + (self.source_depth,)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
QWEN35_24_TYPES = (LINEAR, LINEAR, LINEAR, FULL) * 6
|
| 49 |
+
QWEN35_8_TYPES = (LINEAR, LINEAR, LINEAR, FULL, LINEAR, LINEAR, LINEAR, FULL)
|
| 50 |
+
QWEN35_6_TYPES = (LINEAR, LINEAR, FULL, LINEAR, LINEAR, FULL)
|
| 51 |
+
QWEN35_4_TYPES = (LINEAR, FULL, LINEAR, FULL)
|
| 52 |
+
|
| 53 |
+
DEFAULT_STAGE_MAPS: dict[str, StageMap] = {
|
| 54 |
+
"24to8": StageMap(
|
| 55 |
+
name="24to8",
|
| 56 |
+
source_depth=24,
|
| 57 |
+
target_depth=8,
|
| 58 |
+
selected_layers=(0, 4, 6, 11, 13, 16, 20, 23),
|
| 59 |
+
expected_source_types=QWEN35_24_TYPES,
|
| 60 |
+
expected_target_types=QWEN35_8_TYPES,
|
| 61 |
+
),
|
| 62 |
+
"8to6": StageMap(
|
| 63 |
+
name="8to6",
|
| 64 |
+
source_depth=8,
|
| 65 |
+
target_depth=6,
|
| 66 |
+
selected_layers=(0, 1, 3, 4, 6, 7),
|
| 67 |
+
expected_source_types=QWEN35_8_TYPES,
|
| 68 |
+
expected_target_types=QWEN35_6_TYPES,
|
| 69 |
+
),
|
| 70 |
+
"6to4": StageMap(
|
| 71 |
+
name="6to4",
|
| 72 |
+
source_depth=6,
|
| 73 |
+
target_depth=4,
|
| 74 |
+
selected_layers=(0, 2, 3, 5),
|
| 75 |
+
expected_source_types=QWEN35_6_TYPES,
|
| 76 |
+
expected_target_types=QWEN35_4_TYPES,
|
| 77 |
+
),
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def get_stage(name: str) -> StageMap:
|
| 82 |
+
try:
|
| 83 |
+
return DEFAULT_STAGE_MAPS[name]
|
| 84 |
+
except KeyError as exc:
|
| 85 |
+
raise ValueError(
|
| 86 |
+
f"unknown stage {name!r}; expected one of {sorted(DEFAULT_STAGE_MAPS)}"
|
| 87 |
+
) from exc
|
distillation/qwen35_distill/losses.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from collections.abc import Sequence
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
from torch import Tensor
|
| 7 |
+
from torch.nn import functional as F
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def masked_normalized_hidden_loss(
|
| 11 |
+
student: Tensor, teacher: Tensor, attention_mask: Tensor
|
| 12 |
+
) -> Tensor:
|
| 13 |
+
if student.shape != teacher.shape:
|
| 14 |
+
raise ValueError(f"hidden shape mismatch: {student.shape} vs {teacher.shape}")
|
| 15 |
+
if student.ndim not in (3, 4):
|
| 16 |
+
raise ValueError("hidden tensors must be [B,S,H] or [B,K,S,H]")
|
| 17 |
+
if attention_mask.ndim != 2 or attention_mask.shape != student.shape[:1] + student.shape[-2:-1]:
|
| 18 |
+
raise ValueError("attention mask shape mismatch")
|
| 19 |
+
student_norm = F.normalize(student.float(), dim=-1)
|
| 20 |
+
teacher_norm = F.normalize(teacher.float(), dim=-1)
|
| 21 |
+
token_loss = 1.0 - (student_norm * teacher_norm).sum(dim=-1)
|
| 22 |
+
mask = attention_mask.to(dtype=token_loss.dtype)
|
| 23 |
+
if token_loss.ndim == 3:
|
| 24 |
+
mask = mask[:, None, :]
|
| 25 |
+
denominator = mask.sum() * (token_loss.shape[1] if token_loss.ndim == 3 else 1)
|
| 26 |
+
if float(denominator) <= 0:
|
| 27 |
+
raise ValueError("attention mask has no valid tokens")
|
| 28 |
+
return (token_loss * mask).sum() / denominator
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def gather_boundaries(
|
| 32 |
+
hidden_states: Sequence[Tensor], indices: Sequence[int]
|
| 33 |
+
) -> Tensor:
|
| 34 |
+
if not indices:
|
| 35 |
+
raise ValueError("boundary indices are empty")
|
| 36 |
+
if max(indices) >= len(hidden_states):
|
| 37 |
+
raise ValueError("boundary index outside hidden-state tuple")
|
| 38 |
+
return torch.stack([hidden_states[index] for index in indices], dim=1)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def task_agnostic_boundary_loss(
|
| 42 |
+
student_hidden_states: Sequence[Tensor],
|
| 43 |
+
teacher_hidden_states: Sequence[Tensor],
|
| 44 |
+
teacher_indices: Sequence[int],
|
| 45 |
+
attention_mask: Tensor,
|
| 46 |
+
) -> tuple[Tensor, dict[str, Tensor]]:
|
| 47 |
+
student_indices = tuple(range(len(student_hidden_states)))
|
| 48 |
+
student = gather_boundaries(student_hidden_states, student_indices)
|
| 49 |
+
teacher = gather_boundaries(teacher_hidden_states, teacher_indices)
|
| 50 |
+
if student.shape != teacher.shape:
|
| 51 |
+
raise ValueError(
|
| 52 |
+
f"student/teacher boundary shape mismatch: {student.shape} vs {teacher.shape}"
|
| 53 |
+
)
|
| 54 |
+
all_loss = masked_normalized_hidden_loss(student, teacher, attention_mask)
|
| 55 |
+
final_loss = masked_normalized_hidden_loss(
|
| 56 |
+
student[:, -1], teacher[:, -1], attention_mask
|
| 57 |
+
)
|
| 58 |
+
interface_loss = masked_normalized_hidden_loss(
|
| 59 |
+
student[:, 1:-1], teacher[:, 1:-1], attention_mask
|
| 60 |
+
)
|
| 61 |
+
return 0.5 * (all_loss + final_loss), {
|
| 62 |
+
"all_boundaries": all_loss,
|
| 63 |
+
"interfaces": interface_loss,
|
| 64 |
+
"final": final_loss,
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def classification_distillation_loss(
|
| 69 |
+
student_logits: Tensor,
|
| 70 |
+
teacher_logits: Tensor,
|
| 71 |
+
labels: Tensor,
|
| 72 |
+
*,
|
| 73 |
+
mode: str,
|
| 74 |
+
temperature: float,
|
| 75 |
+
hard_weight: float,
|
| 76 |
+
logit_weight: float,
|
| 77 |
+
) -> tuple[Tensor, dict[str, Tensor]]:
|
| 78 |
+
if temperature <= 0:
|
| 79 |
+
raise ValueError("temperature must be positive")
|
| 80 |
+
if mode == "single_label":
|
| 81 |
+
hard = F.cross_entropy(student_logits.float(), labels.long())
|
| 82 |
+
soft = F.kl_div(
|
| 83 |
+
F.log_softmax(student_logits.float() / temperature, dim=-1),
|
| 84 |
+
F.softmax(teacher_logits.float() / temperature, dim=-1),
|
| 85 |
+
reduction="batchmean",
|
| 86 |
+
) * temperature**2
|
| 87 |
+
elif mode == "multilabel":
|
| 88 |
+
hard = F.binary_cross_entropy_with_logits(
|
| 89 |
+
student_logits.float(), labels.float()
|
| 90 |
+
)
|
| 91 |
+
soft_targets = torch.sigmoid(teacher_logits.float() / temperature)
|
| 92 |
+
soft = F.binary_cross_entropy_with_logits(
|
| 93 |
+
student_logits.float() / temperature, soft_targets
|
| 94 |
+
) * temperature**2
|
| 95 |
+
else:
|
| 96 |
+
raise ValueError(f"unsupported classification mode: {mode}")
|
| 97 |
+
total = hard_weight * hard + logit_weight * soft
|
| 98 |
+
return total, {"hard": hard, "logit": soft}
|
distillation/qwen35_distill/schema.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import hashlib
|
| 4 |
+
import json
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
from typing import Any, Iterable, Literal
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
TaskMode = Literal["unlabeled", "single_label", "multilabel"]
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def sha256_file(path: Path) -> str:
|
| 13 |
+
digest = hashlib.sha256()
|
| 14 |
+
with path.open("rb") as handle:
|
| 15 |
+
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
| 16 |
+
digest.update(chunk)
|
| 17 |
+
return digest.hexdigest()
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def read_jsonl(path: Path) -> list[dict[str, Any]]:
|
| 21 |
+
rows: list[dict[str, Any]] = []
|
| 22 |
+
with path.open("r", encoding="utf-8") as handle:
|
| 23 |
+
for line_number, line in enumerate(handle, start=1):
|
| 24 |
+
if not line.strip():
|
| 25 |
+
continue
|
| 26 |
+
value = json.loads(line)
|
| 27 |
+
if not isinstance(value, dict):
|
| 28 |
+
raise ValueError(f"{path}:{line_number}: row must be an object")
|
| 29 |
+
rows.append(value)
|
| 30 |
+
if not rows:
|
| 31 |
+
raise ValueError(f"{path}: dataset is empty")
|
| 32 |
+
return rows
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def validate_rows(
|
| 36 |
+
rows: Iterable[dict[str, Any]],
|
| 37 |
+
*,
|
| 38 |
+
mode: TaskMode,
|
| 39 |
+
split: str,
|
| 40 |
+
known_labels: set[str] | None = None,
|
| 41 |
+
) -> dict[str, Any]:
|
| 42 |
+
ids: set[str] = set()
|
| 43 |
+
groups: set[str] = set()
|
| 44 |
+
observed_labels: set[str] = set()
|
| 45 |
+
count = 0
|
| 46 |
+
for index, row in enumerate(rows, start=1):
|
| 47 |
+
count += 1
|
| 48 |
+
sample_id = row.get("id")
|
| 49 |
+
text = row.get("text")
|
| 50 |
+
if not isinstance(sample_id, str) or not sample_id.strip():
|
| 51 |
+
raise ValueError(f"{split} row {index}: id must be a non-empty string")
|
| 52 |
+
if sample_id in ids:
|
| 53 |
+
raise ValueError(f"{split}: duplicate id {sample_id!r}")
|
| 54 |
+
ids.add(sample_id)
|
| 55 |
+
if not isinstance(text, str) or not text.strip():
|
| 56 |
+
raise ValueError(f"{split} row {index}: text must be a non-empty string")
|
| 57 |
+
group = row.get("group_id")
|
| 58 |
+
if group is not None:
|
| 59 |
+
if not isinstance(group, str) or not group.strip():
|
| 60 |
+
raise ValueError(f"{split} row {index}: invalid group_id")
|
| 61 |
+
groups.add(group)
|
| 62 |
+
if mode == "unlabeled":
|
| 63 |
+
if "label" in row or "labels" in row:
|
| 64 |
+
raise ValueError(f"{split} row {index}: unlabeled data contains labels")
|
| 65 |
+
elif mode == "single_label":
|
| 66 |
+
label = row.get("label")
|
| 67 |
+
if not isinstance(label, str) or not label:
|
| 68 |
+
raise ValueError(f"{split} row {index}: missing single label")
|
| 69 |
+
if "labels" in row:
|
| 70 |
+
raise ValueError(f"{split} row {index}: both label and labels present")
|
| 71 |
+
observed_labels.add(label)
|
| 72 |
+
elif mode == "multilabel":
|
| 73 |
+
labels = row.get("labels")
|
| 74 |
+
if not isinstance(labels, list) or not labels:
|
| 75 |
+
raise ValueError(f"{split} row {index}: labels must be a non-empty list")
|
| 76 |
+
if "label" in row:
|
| 77 |
+
raise ValueError(f"{split} row {index}: both label and labels present")
|
| 78 |
+
if any(not isinstance(label, str) or not label for label in labels):
|
| 79 |
+
raise ValueError(f"{split} row {index}: invalid multilabel value")
|
| 80 |
+
if len(set(labels)) != len(labels):
|
| 81 |
+
raise ValueError(f"{split} row {index}: duplicate label")
|
| 82 |
+
observed_labels.update(labels)
|
| 83 |
+
else:
|
| 84 |
+
raise ValueError(f"unsupported mode: {mode}")
|
| 85 |
+
if known_labels is not None:
|
| 86 |
+
unknown = observed_labels - known_labels
|
| 87 |
+
if unknown:
|
| 88 |
+
raise ValueError(f"{split}: unknown labels {sorted(unknown)}")
|
| 89 |
+
return {
|
| 90 |
+
"split": split,
|
| 91 |
+
"rows": count,
|
| 92 |
+
"ids": ids,
|
| 93 |
+
"groups": groups,
|
| 94 |
+
"observed_labels": observed_labels,
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def validate_dataset(
|
| 99 |
+
split_paths: dict[str, Path],
|
| 100 |
+
*,
|
| 101 |
+
mode: TaskMode,
|
| 102 |
+
labels: list[str] | None = None,
|
| 103 |
+
) -> dict[str, Any]:
|
| 104 |
+
if not split_paths or "train" not in split_paths:
|
| 105 |
+
raise ValueError("a train split is required")
|
| 106 |
+
if labels is not None:
|
| 107 |
+
if mode == "unlabeled":
|
| 108 |
+
raise ValueError("unlabeled mode must not define labels")
|
| 109 |
+
if not labels or len(set(labels)) != len(labels):
|
| 110 |
+
raise ValueError("labels must be non-empty and unique")
|
| 111 |
+
known_labels = set(labels)
|
| 112 |
+
else:
|
| 113 |
+
known_labels = None
|
| 114 |
+
reports: dict[str, dict[str, Any]] = {}
|
| 115 |
+
all_ids: set[str] = set()
|
| 116 |
+
split_groups: dict[str, set[str]] = {}
|
| 117 |
+
for split, path in split_paths.items():
|
| 118 |
+
path = path.resolve()
|
| 119 |
+
rows = read_jsonl(path)
|
| 120 |
+
report = validate_rows(
|
| 121 |
+
rows, mode=mode, split=split, known_labels=known_labels
|
| 122 |
+
)
|
| 123 |
+
overlap = all_ids & report["ids"]
|
| 124 |
+
if overlap:
|
| 125 |
+
raise ValueError(f"cross-split duplicate IDs: {sorted(overlap)[:10]}")
|
| 126 |
+
all_ids.update(report["ids"])
|
| 127 |
+
split_groups[split] = report["groups"]
|
| 128 |
+
reports[split] = {
|
| 129 |
+
"path": str(path),
|
| 130 |
+
"sha256": sha256_file(path),
|
| 131 |
+
"rows": report["rows"],
|
| 132 |
+
"observed_labels": sorted(report["observed_labels"]),
|
| 133 |
+
}
|
| 134 |
+
split_names = list(split_groups)
|
| 135 |
+
for left_index, left in enumerate(split_names):
|
| 136 |
+
for right in split_names[left_index + 1 :]:
|
| 137 |
+
overlap = split_groups[left] & split_groups[right]
|
| 138 |
+
if overlap:
|
| 139 |
+
raise ValueError(
|
| 140 |
+
f"group leakage between {left} and {right}: {sorted(overlap)[:10]}"
|
| 141 |
+
)
|
| 142 |
+
return {
|
| 143 |
+
"schema_version": "qwen35-classification-dataset-audit-v1",
|
| 144 |
+
"mode": mode,
|
| 145 |
+
"label_order": labels,
|
| 146 |
+
"splits": reports,
|
| 147 |
+
"cross_split_id_overlap": 0,
|
| 148 |
+
"cross_split_group_overlap": 0,
|
| 149 |
+
}
|
distillation/qwen35_distill/training.py
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
import math
|
| 5 |
+
import platform
|
| 6 |
+
import random
|
| 7 |
+
import shutil
|
| 8 |
+
from datetime import datetime
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
from typing import Any
|
| 11 |
+
from zoneinfo import ZoneInfo
|
| 12 |
+
|
| 13 |
+
import pyarrow.parquet as pq
|
| 14 |
+
import torch
|
| 15 |
+
from torch.optim import AdamW
|
| 16 |
+
from transformers import AutoTokenizer
|
| 17 |
+
from transformers.models.qwen3_5.modeling_qwen3_5 import Qwen3_5TextModel
|
| 18 |
+
|
| 19 |
+
from .checkpoint import checkpoint_hashes
|
| 20 |
+
from .layer_maps import StageMap
|
| 21 |
+
from .losses import task_agnostic_boundary_loss
|
| 22 |
+
from .schema import read_jsonl, sha256_file
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def _read_unlabeled_texts(path: Path) -> list[str]:
|
| 26 |
+
if path.suffix == ".jsonl":
|
| 27 |
+
rows = read_jsonl(path)
|
| 28 |
+
texts = []
|
| 29 |
+
for index, row in enumerate(rows, start=1):
|
| 30 |
+
if "label" in row or "labels" in row:
|
| 31 |
+
raise ValueError(f"unlabeled input contains labels at row {index}")
|
| 32 |
+
text = row.get("text")
|
| 33 |
+
if isinstance(text, str) and text.strip():
|
| 34 |
+
texts.append(text)
|
| 35 |
+
return texts
|
| 36 |
+
if path.suffix == ".parquet":
|
| 37 |
+
table = pq.read_table(path, columns=["text"])
|
| 38 |
+
return [
|
| 39 |
+
text
|
| 40 |
+
for text in table.column("text").to_pylist()
|
| 41 |
+
if isinstance(text, str) and text.strip()
|
| 42 |
+
]
|
| 43 |
+
raise ValueError("unlabeled data must be .jsonl or .parquet")
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def _dtype(name: str) -> torch.dtype:
|
| 47 |
+
choices = {
|
| 48 |
+
"float32": torch.float32,
|
| 49 |
+
"float16": torch.float16,
|
| 50 |
+
"bfloat16": torch.bfloat16,
|
| 51 |
+
}
|
| 52 |
+
try:
|
| 53 |
+
return choices[name]
|
| 54 |
+
except KeyError as exc:
|
| 55 |
+
raise ValueError(f"unsupported dtype {name}") from exc
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def _copy_non_weight_files(source: Path, output: Path) -> None:
|
| 59 |
+
for name in (
|
| 60 |
+
"tokenizer.json",
|
| 61 |
+
"tokenizer_config.json",
|
| 62 |
+
"vocab.json",
|
| 63 |
+
"merges.txt",
|
| 64 |
+
"chat_template.jinja",
|
| 65 |
+
"special_tokens_map.json",
|
| 66 |
+
):
|
| 67 |
+
path = source / name
|
| 68 |
+
if path.is_file():
|
| 69 |
+
shutil.copy2(path, output / name)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def task_agnostic_distill(
|
| 73 |
+
*,
|
| 74 |
+
teacher_path: Path,
|
| 75 |
+
student_path: Path,
|
| 76 |
+
data_path: Path,
|
| 77 |
+
output_path: Path,
|
| 78 |
+
stage: StageMap,
|
| 79 |
+
device_name: str,
|
| 80 |
+
dtype_name: str,
|
| 81 |
+
max_rows: int,
|
| 82 |
+
max_length: int,
|
| 83 |
+
epochs: int,
|
| 84 |
+
batch_size: int,
|
| 85 |
+
gradient_accumulation: int,
|
| 86 |
+
learning_rate: float,
|
| 87 |
+
seed: int,
|
| 88 |
+
progress_every: int,
|
| 89 |
+
) -> dict[str, Any]:
|
| 90 |
+
if output_path.exists():
|
| 91 |
+
raise FileExistsError(output_path)
|
| 92 |
+
if min(max_rows, max_length, epochs, batch_size, gradient_accumulation) < 1:
|
| 93 |
+
raise ValueError("row/length/epoch/batch/accumulation values must be positive")
|
| 94 |
+
if progress_every < 1:
|
| 95 |
+
raise ValueError("progress_every must be positive")
|
| 96 |
+
random.seed(seed)
|
| 97 |
+
torch.manual_seed(seed)
|
| 98 |
+
device = torch.device(device_name)
|
| 99 |
+
if device.type == "cuda" and not torch.cuda.is_available():
|
| 100 |
+
raise RuntimeError("CUDA requested but unavailable")
|
| 101 |
+
dtype = _dtype(dtype_name)
|
| 102 |
+
teacher_hashes_before = checkpoint_hashes(teacher_path)
|
| 103 |
+
student_hashes_before = checkpoint_hashes(student_path)
|
| 104 |
+
texts = _read_unlabeled_texts(data_path)
|
| 105 |
+
random.Random(seed).shuffle(texts)
|
| 106 |
+
texts = texts[:max_rows]
|
| 107 |
+
if not texts:
|
| 108 |
+
raise ValueError("no non-empty training texts")
|
| 109 |
+
|
| 110 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
| 111 |
+
student_path, local_files_only=True, trust_remote_code=False
|
| 112 |
+
)
|
| 113 |
+
if tokenizer.pad_token_id is None:
|
| 114 |
+
tokenizer.pad_token = tokenizer.eos_token
|
| 115 |
+
teacher = Qwen3_5TextModel.from_pretrained(
|
| 116 |
+
teacher_path, local_files_only=True, torch_dtype=dtype
|
| 117 |
+
).to(device)
|
| 118 |
+
student = Qwen3_5TextModel.from_pretrained(
|
| 119 |
+
student_path, local_files_only=True, torch_dtype=dtype
|
| 120 |
+
).to(device)
|
| 121 |
+
stage.validate(teacher.config.layer_types)
|
| 122 |
+
if int(student.config.num_hidden_layers) != stage.target_depth:
|
| 123 |
+
raise ValueError("student depth does not match stage")
|
| 124 |
+
for parameter in teacher.parameters():
|
| 125 |
+
parameter.requires_grad_(False)
|
| 126 |
+
teacher.eval()
|
| 127 |
+
student.train()
|
| 128 |
+
student.config.pad_token_id = tokenizer.pad_token_id
|
| 129 |
+
student.gradient_checkpointing_enable()
|
| 130 |
+
optimizer = AdamW(student.parameters(), lr=learning_rate, weight_decay=0.01)
|
| 131 |
+
history: list[dict[str, Any]] = []
|
| 132 |
+
optimizer.zero_grad(set_to_none=True)
|
| 133 |
+
global_step = 0
|
| 134 |
+
optimizer_steps = 0
|
| 135 |
+
|
| 136 |
+
for epoch in range(1, epochs + 1):
|
| 137 |
+
epoch_total = 0.0
|
| 138 |
+
epoch_interface = 0.0
|
| 139 |
+
epoch_final = 0.0
|
| 140 |
+
epoch_batches = 0
|
| 141 |
+
for offset in range(0, len(texts), batch_size):
|
| 142 |
+
batch_texts = texts[offset : offset + batch_size]
|
| 143 |
+
encoded = tokenizer(
|
| 144 |
+
batch_texts,
|
| 145 |
+
return_tensors="pt",
|
| 146 |
+
padding=True,
|
| 147 |
+
truncation=True,
|
| 148 |
+
max_length=max_length,
|
| 149 |
+
)
|
| 150 |
+
encoded = {key: value.to(device) for key, value in encoded.items()}
|
| 151 |
+
with torch.inference_mode():
|
| 152 |
+
teacher_output = teacher(
|
| 153 |
+
**encoded, output_hidden_states=True, use_cache=False
|
| 154 |
+
)
|
| 155 |
+
student_output = student(
|
| 156 |
+
**encoded, output_hidden_states=True, use_cache=False
|
| 157 |
+
)
|
| 158 |
+
loss, components = task_agnostic_boundary_loss(
|
| 159 |
+
student_output.hidden_states,
|
| 160 |
+
teacher_output.hidden_states,
|
| 161 |
+
stage.teacher_boundary_indices(),
|
| 162 |
+
encoded["attention_mask"],
|
| 163 |
+
)
|
| 164 |
+
if not bool(torch.isfinite(loss)):
|
| 165 |
+
raise FloatingPointError(f"non-finite loss at step {global_step}")
|
| 166 |
+
(loss / gradient_accumulation).backward()
|
| 167 |
+
global_step += 1
|
| 168 |
+
epoch_batches += 1
|
| 169 |
+
epoch_total += float(loss.detach())
|
| 170 |
+
epoch_interface += float(components["interfaces"].detach())
|
| 171 |
+
epoch_final += float(components["final"].detach())
|
| 172 |
+
if global_step % gradient_accumulation == 0 or offset + batch_size >= len(texts):
|
| 173 |
+
grad_norm = torch.nn.utils.clip_grad_norm_(student.parameters(), 1.0)
|
| 174 |
+
if not math.isfinite(float(grad_norm)):
|
| 175 |
+
raise FloatingPointError("non-finite gradient norm")
|
| 176 |
+
optimizer.step()
|
| 177 |
+
optimizer.zero_grad(set_to_none=True)
|
| 178 |
+
optimizer_steps += 1
|
| 179 |
+
if global_step == 1 or global_step % progress_every == 0:
|
| 180 |
+
print(
|
| 181 |
+
json.dumps(
|
| 182 |
+
{
|
| 183 |
+
"event": "distill_progress",
|
| 184 |
+
"stage": stage.name,
|
| 185 |
+
"epoch": epoch,
|
| 186 |
+
"batch": epoch_batches,
|
| 187 |
+
"batches_per_epoch": math.ceil(len(texts) / batch_size),
|
| 188 |
+
"global_step": global_step,
|
| 189 |
+
"optimizer_steps": optimizer_steps,
|
| 190 |
+
"mean_loss_so_far": epoch_total / epoch_batches,
|
| 191 |
+
},
|
| 192 |
+
ensure_ascii=False,
|
| 193 |
+
),
|
| 194 |
+
flush=True,
|
| 195 |
+
)
|
| 196 |
+
history.append(
|
| 197 |
+
{
|
| 198 |
+
"epoch": epoch,
|
| 199 |
+
"batches": epoch_batches,
|
| 200 |
+
"mean_total_loss": epoch_total / epoch_batches,
|
| 201 |
+
"mean_interface_loss": epoch_interface / epoch_batches,
|
| 202 |
+
"mean_final_loss": epoch_final / epoch_batches,
|
| 203 |
+
"optimizer_steps_total": optimizer_steps,
|
| 204 |
+
}
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
teacher_hashes_after = checkpoint_hashes(teacher_path)
|
| 208 |
+
if teacher_hashes_after != teacher_hashes_before:
|
| 209 |
+
raise RuntimeError("teacher checkpoint changed during distillation")
|
| 210 |
+
student.eval()
|
| 211 |
+
output_path.mkdir(parents=True, exist_ok=False)
|
| 212 |
+
student.save_pretrained(output_path, safe_serialization=True)
|
| 213 |
+
_copy_non_weight_files(student_path, output_path)
|
| 214 |
+
sample = tokenizer(
|
| 215 |
+
texts[0], return_tensors="pt", truncation=True, max_length=max_length
|
| 216 |
+
)
|
| 217 |
+
sample = {key: value.to(device) for key, value in sample.items()}
|
| 218 |
+
with torch.inference_mode():
|
| 219 |
+
forward = student(**sample, use_cache=False).last_hidden_state
|
| 220 |
+
if not bool(torch.isfinite(forward).all()):
|
| 221 |
+
raise RuntimeError("saved student produced non-finite forward before reload")
|
| 222 |
+
del student
|
| 223 |
+
if device.type == "cuda":
|
| 224 |
+
torch.cuda.empty_cache()
|
| 225 |
+
reloaded = Qwen3_5TextModel.from_pretrained(
|
| 226 |
+
output_path, local_files_only=True, torch_dtype=dtype
|
| 227 |
+
).to(device).eval()
|
| 228 |
+
with torch.inference_mode():
|
| 229 |
+
reload_forward = reloaded(**sample, use_cache=False).last_hidden_state
|
| 230 |
+
if not bool(torch.isfinite(reload_forward).all()):
|
| 231 |
+
raise RuntimeError("fresh-reloaded student produced non-finite forward")
|
| 232 |
+
max_abs_reload = float((forward.float() - reload_forward.float()).abs().max())
|
| 233 |
+
report = {
|
| 234 |
+
"schema_version": "qwen35-task-agnostic-kd-v1",
|
| 235 |
+
"status": "completed",
|
| 236 |
+
"timestamp_kst": datetime.now(ZoneInfo("Asia/Seoul")).isoformat(),
|
| 237 |
+
"stage": stage.name,
|
| 238 |
+
"teacher": str(teacher_path.resolve()),
|
| 239 |
+
"student_initial": str(student_path.resolve()),
|
| 240 |
+
"output": str(output_path.resolve()),
|
| 241 |
+
"data": {
|
| 242 |
+
"path": str(data_path.resolve()),
|
| 243 |
+
"sha256": sha256_file(data_path),
|
| 244 |
+
"rows_used": len(texts),
|
| 245 |
+
"labels_read": False,
|
| 246 |
+
"semeval_used": False,
|
| 247 |
+
},
|
| 248 |
+
"training": {
|
| 249 |
+
"seed": seed,
|
| 250 |
+
"epochs": epochs,
|
| 251 |
+
"batch_size": batch_size,
|
| 252 |
+
"gradient_accumulation": gradient_accumulation,
|
| 253 |
+
"max_length": max_length,
|
| 254 |
+
"learning_rate": learning_rate,
|
| 255 |
+
"progress_every": progress_every,
|
| 256 |
+
"history": history,
|
| 257 |
+
},
|
| 258 |
+
"environment": {
|
| 259 |
+
"python": platform.python_version(),
|
| 260 |
+
"torch": torch.__version__,
|
| 261 |
+
"device": str(device),
|
| 262 |
+
"dtype": dtype_name,
|
| 263 |
+
},
|
| 264 |
+
"teacher_hashes_before": teacher_hashes_before,
|
| 265 |
+
"teacher_hashes_after": teacher_hashes_after,
|
| 266 |
+
"student_initial_hashes": student_hashes_before,
|
| 267 |
+
"output_hashes": checkpoint_hashes(output_path),
|
| 268 |
+
"teacher_immutable": True,
|
| 269 |
+
"fresh_reload_finite": True,
|
| 270 |
+
"fresh_reload_max_abs": max_abs_reload,
|
| 271 |
+
}
|
| 272 |
+
(output_path / "training_manifest.json").write_text(
|
| 273 |
+
json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
|
| 274 |
+
)
|
| 275 |
+
return report
|
docs/RELEASE_CONTRACT.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Unified 1-1-1 Hugging Face release contract
|
| 2 |
+
|
| 3 |
+
## Repository identity
|
| 4 |
+
|
| 5 |
+
- New repository: `mp-juuuns/qwen35-standalone4l-classification-base`
|
| 6 |
+
- Existing repository preserved without mutation:
|
| 7 |
+
`mp-juuuns/qwen35-standalone4l-propaganda-classifier`
|
| 8 |
+
- Existing specialized fixed revision:
|
| 9 |
+
`e7e0ff16828052687d2e8dd7849e7a521629cf38`
|
| 10 |
+
|
| 11 |
+
## The three public surfaces
|
| 12 |
+
|
| 13 |
+
1. Repository root: a headless four-layer Qwen3.5 text backbone trained without
|
| 14 |
+
SemEval examples, labels, logits, thresholds, or evidence objectives.
|
| 15 |
+
2. `models/semeval-propaganda/`: the existing three-seed task-specific
|
| 16 |
+
classifier copied byte-for-byte as a separate historical lineage. It is not
|
| 17 |
+
described as a fine-tuned child of the new root base.
|
| 18 |
+
3. `distillation/`: the reusable 24L→8L→6L→4L platform for unlabeled
|
| 19 |
+
representation distillation and user-owned single-label or multilabel
|
| 20 |
+
classification data.
|
| 21 |
+
|
| 22 |
+
## Base training contract
|
| 23 |
+
|
| 24 |
+
- Source: the text backbone and full tokenizer of `Qwen/Qwen3.5-0.8B`.
|
| 25 |
+
- Structural defaults:
|
| 26 |
+
- 24→8: `0,4,6,11,13,16,20,23`
|
| 27 |
+
- 8→6: `0,1,3,4,6,7`
|
| 28 |
+
- 6→4: `0,2,3,5`
|
| 29 |
+
- Task-free data: 4,096 deterministically shuffled non-empty rows from the
|
| 30 |
+
first locally cached WikiText-103 raw training shard.
|
| 31 |
+
- Objective: aligned hidden-boundary and final-representation distillation.
|
| 32 |
+
- One epoch per stage, seed 41, maximum length 128, batch size 1, gradient
|
| 33 |
+
accumulation 8, learning rate 2e-5, BF16 CUDA.
|
| 34 |
+
- No classification head is published at the root.
|
| 35 |
+
|
| 36 |
+
The WikiText dataset page currently has inconsistent license wording: metadata
|
| 37 |
+
lists CC BY-SA 3.0 and GFDL, while the prose says CC BY-SA 4.0. The release
|
| 38 |
+
redistributes no WikiText text and discloses both statements rather than
|
| 39 |
+
silently selecting one.
|
| 40 |
+
|
| 41 |
+
## Transfer gate
|
| 42 |
+
|
| 43 |
+
The only downstream validation task is an article-level multilabel task derived
|
| 44 |
+
from SemEval-2020 Task 11. It is not the official span- or fragment-level task.
|
| 45 |
+
The public test split contains 55 previously opened articles, so results are
|
| 46 |
+
exploratory rather than confirmatory.
|
| 47 |
+
|
| 48 |
+
| Initialization | Seed 41 | Seed 42 | Seed 43 | Mean ± sample SD |
|
| 49 |
+
|---|---:|---:|---:|---:|
|
| 50 |
+
| Task-agnostic KD base | 0.58831 | 0.60284 | 0.57128 | 0.58748 ± 0.01579 |
|
| 51 |
+
| Structural 4L copy, no task-free KD | 0.51694 | 0.53494 | 0.50238 | 0.51809 ± 0.01631 |
|
| 52 |
+
|
| 53 |
+
The preregistered publication gate was that the task-agnostic base must not be
|
| 54 |
+
worse than the structural-copy control by more than 0.01 mean Macro-F1. The
|
| 55 |
+
observed mean difference was +0.06939, so the gate passed. Three training seeds
|
| 56 |
+
are not a confidence interval and no significance claim is made.
|
| 57 |
+
|
| 58 |
+
## Release gates
|
| 59 |
+
|
| 60 |
+
- Root `model.safetensors` contains no `score.weight`.
|
| 61 |
+
- Root loads with `AutoModel`, and a temporary classification head can be
|
| 62 |
+
attached with `AutoModelForSequenceClassification`.
|
| 63 |
+
- Root weight SHA-256 is
|
| 64 |
+
`2732c616772fe320cdea228ab4554981418b1b2bf615c4183fb1ac8e6e2168d3`.
|
| 65 |
+
- Base manifests say `labels_read=false` and `semeval_used=false`.
|
| 66 |
+
- Nested specialized checkpoint hashes equal the already published release.
|
| 67 |
+
- The platform validates label order and ID/group split leakage, never
|
| 68 |
+
overwrites output directories, hashes inputs/outputs, verifies immutable
|
| 69 |
+
teachers, and reloads fresh checkpoints.
|
| 70 |
+
- The release contains no SemEval or WikiText source data.
|
| 71 |
+
- The old specialized repository revision and inventory are checked before and
|
| 72 |
+
after creation of the new repository.
|
| 73 |
+
|
merges.txt
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2732c616772fe320cdea228ab4554981418b1b2bf615c4183fb1ac8e6e2168d3
|
| 3 |
+
size 668198976
|
models/semeval-propaganda/LICENSE
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright 2018- The Hugging Face team. All rights reserved.
|
| 2 |
+
|
| 3 |
+
Apache License
|
| 4 |
+
Version 2.0, January 2004
|
| 5 |
+
http://www.apache.org/licenses/
|
| 6 |
+
|
| 7 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 8 |
+
|
| 9 |
+
1. Definitions.
|
| 10 |
+
|
| 11 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 12 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 13 |
+
|
| 14 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 15 |
+
the copyright owner that is granting the License.
|
| 16 |
+
|
| 17 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 18 |
+
other entities that control, are controlled by, or are under common
|
| 19 |
+
control with that entity. For the purposes of this definition,
|
| 20 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 21 |
+
direction or management of such entity, whether by contract or
|
| 22 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 23 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 24 |
+
|
| 25 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 26 |
+
exercising permissions granted by this License.
|
| 27 |
+
|
| 28 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 29 |
+
including but not limited to software source code, documentation
|
| 30 |
+
source, and configuration files.
|
| 31 |
+
|
| 32 |
+
"Object" form shall mean any form resulting from mechanical
|
| 33 |
+
transformation or translation of a Source form, including but
|
| 34 |
+
not limited to compiled object code, generated documentation,
|
| 35 |
+
and conversions to other media types.
|
| 36 |
+
|
| 37 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 38 |
+
Object form, made available under the License, as indicated by a
|
| 39 |
+
copyright notice that is included in or attached to the work
|
| 40 |
+
(an example is provided in the Appendix below).
|
| 41 |
+
|
| 42 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 43 |
+
form, that is based on (or derived from) the Work and for which the
|
| 44 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 45 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 46 |
+
of this License, Derivative Works shall not include works that remain
|
| 47 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 48 |
+
the Work and Derivative Works thereof.
|
| 49 |
+
|
| 50 |
+
"Contribution" shall mean any work of authorship, including
|
| 51 |
+
the original version of the Work and any modifications or additions
|
| 52 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 53 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 54 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 55 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 56 |
+
means any form of electronic, verbal, or written communication sent
|
| 57 |
+
to the Licensor or its representatives, including but not limited to
|
| 58 |
+
communication on electronic mailing lists, source code control systems,
|
| 59 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 60 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 61 |
+
excluding communication that is conspicuously marked or otherwise
|
| 62 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 63 |
+
|
| 64 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 65 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 66 |
+
subsequently incorporated within the Work.
|
| 67 |
+
|
| 68 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 69 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 70 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 71 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 72 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 73 |
+
Work and such Derivative Works in Source or Object form.
|
| 74 |
+
|
| 75 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 76 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 77 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 78 |
+
(except as stated in this section) patent license to make, have made,
|
| 79 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 80 |
+
where such license applies only to those patent claims licensable
|
| 81 |
+
by such Contributor that are necessarily infringed by their
|
| 82 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 83 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 84 |
+
institute patent litigation against any entity (including a
|
| 85 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 86 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 87 |
+
or contributory patent infringement, then any patent licenses
|
| 88 |
+
granted to You under this License for that Work shall terminate
|
| 89 |
+
as of the date such litigation is filed.
|
| 90 |
+
|
| 91 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 92 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 93 |
+
modifications, and in Source or Object form, provided that You
|
| 94 |
+
meet the following conditions:
|
| 95 |
+
|
| 96 |
+
(a) You must give any other recipients of the Work or
|
| 97 |
+
Derivative Works a copy of this License; and
|
| 98 |
+
|
| 99 |
+
(b) You must cause any modified files to carry prominent notices
|
| 100 |
+
stating that You changed the files; and
|
| 101 |
+
|
| 102 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 103 |
+
that You distribute, all copyright, patent, trademark, and
|
| 104 |
+
attribution notices from the Source form of the Work,
|
| 105 |
+
excluding those notices that do not pertain to any part of
|
| 106 |
+
the Derivative Works; and
|
| 107 |
+
|
| 108 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 109 |
+
distribution, then any Derivative Works that You distribute must
|
| 110 |
+
include a readable copy of the attribution notices contained
|
| 111 |
+
within such NOTICE file, excluding those notices that do not
|
| 112 |
+
pertain to any part of the Derivative Works, in at least one
|
| 113 |
+
of the following places: within a NOTICE text file distributed
|
| 114 |
+
as part of the Derivative Works; within the Source form or
|
| 115 |
+
documentation, if provided along with the Derivative Works; or,
|
| 116 |
+
within a display generated by the Derivative Works, if and
|
| 117 |
+
wherever such third-party notices normally appear. The contents
|
| 118 |
+
of the NOTICE file are for informational purposes only and
|
| 119 |
+
do not modify the License. You may add Your own attribution
|
| 120 |
+
notices within Derivative Works that You distribute, alongside
|
| 121 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 122 |
+
that such additional attribution notices cannot be construed
|
| 123 |
+
as modifying the License.
|
| 124 |
+
|
| 125 |
+
You may add Your own copyright statement to Your modifications and
|
| 126 |
+
may provide additional or different license terms and conditions
|
| 127 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 128 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 129 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 130 |
+
the conditions stated in this License.
|
| 131 |
+
|
| 132 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 133 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 134 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 135 |
+
this License, without any additional terms or conditions.
|
| 136 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 137 |
+
the terms of any separate license agreement you may have executed
|
| 138 |
+
with Licensor regarding such Contributions.
|
| 139 |
+
|
| 140 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 141 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 142 |
+
except as required for reasonable and customary use in describing the
|
| 143 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 144 |
+
|
| 145 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 146 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 147 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 148 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 149 |
+
implied, including, without limitation, any warranties or conditions
|
| 150 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 151 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 152 |
+
appropriateness of using or redistributing the Work and assume any
|
| 153 |
+
risks associated with Your exercise of permissions under this License.
|
| 154 |
+
|
| 155 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 156 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 157 |
+
unless required by applicable law (such as deliberate and grossly
|
| 158 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 159 |
+
liable to You for damages, including any direct, indirect, special,
|
| 160 |
+
incidental, or consequential damages of any character arising as a
|
| 161 |
+
result of this License or out of the use or inability to use the
|
| 162 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 163 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 164 |
+
other commercial damages or losses), even if such Contributor
|
| 165 |
+
has been advised of the possibility of such damages.
|
| 166 |
+
|
| 167 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 168 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 169 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 170 |
+
or other liability obligations and/or rights consistent with this
|
| 171 |
+
License. However, in accepting such obligations, You may act only
|
| 172 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 173 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 174 |
+
defend, and hold each Contributor harmless for any liability
|
| 175 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 176 |
+
of your accepting any such warranty or additional liability.
|
| 177 |
+
|
| 178 |
+
END OF TERMS AND CONDITIONS
|
| 179 |
+
|
| 180 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 181 |
+
|
| 182 |
+
To apply the Apache License to your work, attach the following
|
| 183 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 184 |
+
replaced with your own identifying information. (Don't include
|
| 185 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 186 |
+
comment syntax for the file format. We also recommend that a
|
| 187 |
+
file or class name and description of purpose be included on the
|
| 188 |
+
same "printed page" as the copyright notice for easier
|
| 189 |
+
identification within third-party archives.
|
| 190 |
+
|
| 191 |
+
Copyright [yyyy] [name of copyright owner]
|
| 192 |
+
|
| 193 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 194 |
+
you may not use this file except in compliance with the License.
|
| 195 |
+
You may obtain a copy of the License at
|
| 196 |
+
|
| 197 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 198 |
+
|
| 199 |
+
Unless required by applicable law or agreed to in writing, software
|
| 200 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 201 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 202 |
+
See the License for the specific language governing permissions and
|
| 203 |
+
limitations under the License.
|
models/semeval-propaganda/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SemEval-derived propaganda classifier
|
| 2 |
+
|
| 3 |
+
This subfolder preserves the already published three-seed task-specific model.
|
| 4 |
+
It is a separate lineage and is **not** a fine-tuned child of the headless base
|
| 5 |
+
at this repository root.
|
| 6 |
+
|
| 7 |
+
- Original repository: https://huggingface.co/mp-juuuns/qwen35-standalone4l-propaganda-classifier
|
| 8 |
+
- Preserved revision: `e7e0ff16828052687d2e8dd7849e7a521629cf38`
|
| 9 |
+
- Task: article-level multilabel presence of 14 propaganda techniques derived
|
| 10 |
+
from SemEval-2020 Task 11 annotations.
|
| 11 |
+
- Scope: not the official span- or fragment-level task; not a fact checker.
|
| 12 |
+
- Root of this subfolder: seed 41. Seeds 42 and 43 are under `seeds/`.
|
| 13 |
+
|
| 14 |
+
```python
|
| 15 |
+
from transformers import AutoModelForSequenceClassification, AutoTokenizer
|
| 16 |
+
|
| 17 |
+
repo = "mp-juuuns/qwen35-standalone4l-classification-base"
|
| 18 |
+
subfolder = "models/semeval-propaganda"
|
| 19 |
+
tokenizer = AutoTokenizer.from_pretrained(repo, subfolder=subfolder)
|
| 20 |
+
model = AutoModelForSequenceClassification.from_pretrained(
|
| 21 |
+
repo, subfolder=subfolder
|
| 22 |
+
).eval()
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
The files below are byte-identical copies of the current release bundle. The
|
| 26 |
+
original release manifest and locked thresholds are included for provenance.
|
| 27 |
+
No SemEval source article or annotation file is redistributed.
|
models/semeval-propaganda/benchmark/final4l_aggregate.json
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"schema_version": "final-broad-arm-aggregate-v1",
|
| 3 |
+
"status": "completed_three_seed_audited",
|
| 4 |
+
"created_at": "2026-07-30T12:49:56+09:00",
|
| 5 |
+
"arm_id": "qwen35-standalone4l-commonhead",
|
| 6 |
+
"experiment_id": "S-20260729-final-broad-benchmark-v1",
|
| 7 |
+
"model": {
|
| 8 |
+
"description": "Qwen3.5 independently distilled standalone 4-layer L-F-L-F sequence classifier with shared 14-class head",
|
| 9 |
+
"source_checkpoint": "results/layer_shrink_v6/20260728-v6-r23-standalone4l-kd-seed41-v1/student-4l-final-bf16",
|
| 10 |
+
"source_audit_sha256": "6c53ed491d5c1d8d18610c7f68105691a12d2a999eafd0b471f2b373f92b1298",
|
| 11 |
+
"source_config_sha256": "f2e2b9547696963a08a1b9403b4ea7973b8c9626c6d0a119dfdee2713e3dfdd9",
|
| 12 |
+
"source_model_sha256": "8b3959f6326c75f557b781caa3930478d0b112faf7db8ff59ec5c6c7c444edcf",
|
| 13 |
+
"excluded_historical_joint_heads_sha256": "db886bfab8b827a17062c1fa4057641d2c77aa265e83f5fe07aa731f0de06fd1",
|
| 14 |
+
"tokenizer_sha256": "2106c29a0c7de1308e536b9f8a01833b9ff9bd0b34a526019f478bc53b6da81d",
|
| 15 |
+
"tokenizer_config_sha256": "2215318fe27878ec67e772931bb5f40a98658348d33f2672a575f766a2190d35",
|
| 16 |
+
"teacher_depth": 6,
|
| 17 |
+
"student_depth": 4,
|
| 18 |
+
"selected_teacher_layers": [
|
| 19 |
+
0,
|
| 20 |
+
2,
|
| 21 |
+
3,
|
| 22 |
+
5
|
| 23 |
+
],
|
| 24 |
+
"layer_types": [
|
| 25 |
+
"linear_attention",
|
| 26 |
+
"full_attention",
|
| 27 |
+
"linear_attention",
|
| 28 |
+
"full_attention"
|
| 29 |
+
],
|
| 30 |
+
"hidden_size": 1024,
|
| 31 |
+
"vocabulary_size": 128000,
|
| 32 |
+
"parameters": 210903360,
|
| 33 |
+
"head_policy": "Historical 3-class score weight replaced with a freshly initialized shared 14-class score weight; separate historical joint_heads.safetensors not loaded."
|
| 34 |
+
},
|
| 35 |
+
"dataset": {
|
| 36 |
+
"name": "SemEval-2020 Task 11 public professional human gold v2",
|
| 37 |
+
"train_sha256": "d5e3307374565a4bffba47c02da850c73f5616d3bace99b08f7b689bf8ce2bdf",
|
| 38 |
+
"calibration_sha256": "80911ff094cf300644e43a2baf57029cc0527d34af7881c787f0b898c2e83acd",
|
| 39 |
+
"test_sha256": "ed224269dc0a386dfefff2a8cb8cafb8b9bb0a2d8495c616f8365892b4709463",
|
| 40 |
+
"task_spec_sha256": "8ce5df61b17c1f7819b161f84f7c1e21415b0c6014abc9ce3c5c2fa4120715ff",
|
| 41 |
+
"test_articles": 55,
|
| 42 |
+
"test_previously_opened_public_gold": true
|
| 43 |
+
},
|
| 44 |
+
"protocol": {
|
| 45 |
+
"seeds": [
|
| 46 |
+
41,
|
| 47 |
+
42,
|
| 48 |
+
43
|
| 49 |
+
],
|
| 50 |
+
"epochs": 5,
|
| 51 |
+
"train_windows_per_epoch": 2428,
|
| 52 |
+
"early_stopping": false,
|
| 53 |
+
"max_length": 256,
|
| 54 |
+
"stride": 128,
|
| 55 |
+
"micro_batch_size": 1,
|
| 56 |
+
"eval_batch_size": 2,
|
| 57 |
+
"gradient_accumulation": 32,
|
| 58 |
+
"effective_batch_size": 32,
|
| 59 |
+
"learning_rate": 2e-05,
|
| 60 |
+
"weight_decay": 0.01,
|
| 61 |
+
"gradient_clip": 1.0,
|
| 62 |
+
"dtype": "bfloat16",
|
| 63 |
+
"test_access": "once_per_seed_after_epoch5_calibration_lock"
|
| 64 |
+
},
|
| 65 |
+
"seed_artifacts": [
|
| 66 |
+
{
|
| 67 |
+
"seed": 41,
|
| 68 |
+
"report": {
|
| 69 |
+
"path": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed41/report.json",
|
| 70 |
+
"sha256": "ec5314140fff0bdd46e87ee00ad67bee7e61d82c7cfa34add1510dd7d5bdb3c0"
|
| 71 |
+
},
|
| 72 |
+
"manifest": {
|
| 73 |
+
"path": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed41.run-manifest.json",
|
| 74 |
+
"sha256": "9c9de086a0679611f9ffa99a8e3b01b30889d80a859fad1b80fffb7fa3d08ea9"
|
| 75 |
+
},
|
| 76 |
+
"predictions": {
|
| 77 |
+
"path": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed41/test_predictions.jsonl",
|
| 78 |
+
"sha256": "6564a34da352ee3808b00c24e69010144fef5947af8b4913656a13547c693f1e"
|
| 79 |
+
},
|
| 80 |
+
"checkpoint": {
|
| 81 |
+
"path": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed41/checkpoint/model.safetensors",
|
| 82 |
+
"sha256": "0352da6e4717a2e6f3a9d643d034be2cb1c39632d1bce0ced339ddb62acedb37"
|
| 83 |
+
},
|
| 84 |
+
"reload_exact": true,
|
| 85 |
+
"max_probability_delta": 0.0
|
| 86 |
+
},
|
| 87 |
+
{
|
| 88 |
+
"seed": 42,
|
| 89 |
+
"report": {
|
| 90 |
+
"path": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed42/report.json",
|
| 91 |
+
"sha256": "e5c45a7dc41bebf4550da7c5763d0759c6d0922da9efda4048564c11c6d2f3af"
|
| 92 |
+
},
|
| 93 |
+
"manifest": {
|
| 94 |
+
"path": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed42.run-manifest.json",
|
| 95 |
+
"sha256": "6e61bb7ab1731c138301acf3d9877c8b18cc3750b856fecba4042099f14fa538"
|
| 96 |
+
},
|
| 97 |
+
"predictions": {
|
| 98 |
+
"path": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed42/test_predictions.jsonl",
|
| 99 |
+
"sha256": "2a1a22583b6a32860b744f7c8022379816417f09ebe4a71f34de3462e5c5e597"
|
| 100 |
+
},
|
| 101 |
+
"checkpoint": {
|
| 102 |
+
"path": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed42/checkpoint/model.safetensors",
|
| 103 |
+
"sha256": "2db5f4be391d9dee64458cb5865530f39747019e7a219443b5f7850befbce697"
|
| 104 |
+
},
|
| 105 |
+
"reload_exact": true,
|
| 106 |
+
"max_probability_delta": 0.0
|
| 107 |
+
},
|
| 108 |
+
{
|
| 109 |
+
"seed": 43,
|
| 110 |
+
"report": {
|
| 111 |
+
"path": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed43/report.json",
|
| 112 |
+
"sha256": "e05bc2146d720706780fc45a1fb05911d5c2926caa0bcb12ffead946028eef0d"
|
| 113 |
+
},
|
| 114 |
+
"manifest": {
|
| 115 |
+
"path": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed43.run-manifest.json",
|
| 116 |
+
"sha256": "092244cf229081b20eee52cbb10889f98889f486366c440a409fe1845a888905"
|
| 117 |
+
},
|
| 118 |
+
"predictions": {
|
| 119 |
+
"path": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed43/test_predictions.jsonl",
|
| 120 |
+
"sha256": "d6bb001f9cff810810ba17dce1e93edfea7c155a7b18fbf2e31c05a5eb1f16d9"
|
| 121 |
+
},
|
| 122 |
+
"checkpoint": {
|
| 123 |
+
"path": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed43/checkpoint/model.safetensors",
|
| 124 |
+
"sha256": "81a56d497df758e8a292eea528438cfc4d5a847cd37d3a7e1107fefa7c500640"
|
| 125 |
+
},
|
| 126 |
+
"reload_exact": true,
|
| 127 |
+
"max_probability_delta": 0.0
|
| 128 |
+
}
|
| 129 |
+
],
|
| 130 |
+
"quality": {
|
| 131 |
+
"test_macro_f1": {
|
| 132 |
+
"values": [
|
| 133 |
+
0.5832848452288824,
|
| 134 |
+
0.5958405181160762,
|
| 135 |
+
0.5835785045218137
|
| 136 |
+
],
|
| 137 |
+
"mean": 0.5875679559555907,
|
| 138 |
+
"sample_sd": 0.007165753447433348
|
| 139 |
+
},
|
| 140 |
+
"test_micro_f1": {
|
| 141 |
+
"values": [
|
| 142 |
+
0.6494252873563218,
|
| 143 |
+
0.6458616010854816,
|
| 144 |
+
0.6486486486486486
|
| 145 |
+
],
|
| 146 |
+
"mean": 0.647978512363484,
|
| 147 |
+
"sample_sd": 0.0018739735696546695
|
| 148 |
+
},
|
| 149 |
+
"final_calibration_macro_f1": {
|
| 150 |
+
"values": [
|
| 151 |
+
0.5555148573005716,
|
| 152 |
+
0.5351509002776785,
|
| 153 |
+
0.5298118328152787
|
| 154 |
+
],
|
| 155 |
+
"mean": 0.540159196797843,
|
| 156 |
+
"sample_sd": 0.01356368837920977
|
| 157 |
+
},
|
| 158 |
+
"locked_thresholds": [
|
| 159 |
+
0.1,
|
| 160 |
+
0.05,
|
| 161 |
+
0.05
|
| 162 |
+
],
|
| 163 |
+
"stability_warning": "All seeds showed late-epoch calibration decline and large threshold movement; two of three final thresholds hit the lower edge of the preregistered grid even though test F1 remained high.",
|
| 164 |
+
"small_test_warning": "The shared test split contains only 55 previously opened public-gold articles; high F1 and paired-seed consistency require confirmation on distinct public human-gold datasets."
|
| 165 |
+
},
|
| 166 |
+
"resources": {
|
| 167 |
+
"wall_seconds": {
|
| 168 |
+
"values": [
|
| 169 |
+
1634.488557530014,
|
| 170 |
+
1652.9397718419787,
|
| 171 |
+
1643.488202479988
|
| 172 |
+
],
|
| 173 |
+
"mean": 1643.6388439506602,
|
| 174 |
+
"sample_sd": 9.226529522850198
|
| 175 |
+
},
|
| 176 |
+
"peak_cuda_allocated_bytes": {
|
| 177 |
+
"values": [
|
| 178 |
+
2143655936,
|
| 179 |
+
2143655936,
|
| 180 |
+
2143655936
|
| 181 |
+
],
|
| 182 |
+
"mean": 2143655936.0,
|
| 183 |
+
"sample_sd": 0.0
|
| 184 |
+
},
|
| 185 |
+
"peak_cuda_reserved_bytes": {
|
| 186 |
+
"values": [
|
| 187 |
+
2281701376,
|
| 188 |
+
2281701376,
|
| 189 |
+
2273312768
|
| 190 |
+
],
|
| 191 |
+
"mean": 2278905173.3333335,
|
| 192 |
+
"sample_sd": 4843165.086926248
|
| 193 |
+
},
|
| 194 |
+
"ambient_gpu_context": {
|
| 195 |
+
"nvml_pid_at_preflight": 197568,
|
| 196 |
+
"visible_in_wsl_process_namespace": false,
|
| 197 |
+
"baseline_used_mib_range": [
|
| 198 |
+
3707,
|
| 199 |
+
3729
|
| 200 |
+
],
|
| 201 |
+
"baseline_free_mib_range": [
|
| 202 |
+
12267,
|
| 203 |
+
12289
|
| 204 |
+
],
|
| 205 |
+
"baseline_utilization_percent": 1,
|
| 206 |
+
"stopped_for_benchmark": false,
|
| 207 |
+
"runner_peak_metrics_process_local": true
|
| 208 |
+
}
|
| 209 |
+
},
|
| 210 |
+
"raw_comparisons": {
|
| 211 |
+
"qwen35_full24l": {
|
| 212 |
+
"other_macro_f1_mean": 0.5642638295960175,
|
| 213 |
+
"macro_mean_delta_this_minus_other": 0.02330412635957324,
|
| 214 |
+
"paired_seed_macro_deltas": [
|
| 215 |
+
0.011832078826701897,
|
| 216 |
+
0.03452762812104826,
|
| 217 |
+
0.023552672130969676
|
| 218 |
+
],
|
| 219 |
+
"wall_reduction_fraction": 0.8720113142644486,
|
| 220 |
+
"peak_cuda_allocated_reduction_fraction": 0.7192454897050662,
|
| 221 |
+
"parameter_reduction_fraction": 0.7196952459369882
|
| 222 |
+
},
|
| 223 |
+
"qwen35_c8q8l": {
|
| 224 |
+
"other_macro_f1_mean": 0.5366873350531403,
|
| 225 |
+
"macro_mean_delta_this_minus_other": 0.05088062090245049,
|
| 226 |
+
"paired_seed_macro_deltas": [
|
| 227 |
+
0.06653285611641446,
|
| 228 |
+
0.04916191142573212,
|
| 229 |
+
0.03694709516520478
|
| 230 |
+
],
|
| 231 |
+
"wall_reduction_fraction": 0.6170087121951836,
|
| 232 |
+
"peak_cuda_allocated_reduction_fraction": 0.4965455376253577,
|
| 233 |
+
"parameter_reduction_fraction": 0.4982463406111276
|
| 234 |
+
},
|
| 235 |
+
"xlmr_base": {
|
| 236 |
+
"other_macro_f1_mean": 0.5483113137371987,
|
| 237 |
+
"macro_mean_delta_this_minus_other": 0.03925664221839209,
|
| 238 |
+
"paired_seed_macro_deltas": [
|
| 239 |
+
0.04717846633760603,
|
| 240 |
+
0.050893521484219884,
|
| 241 |
+
0.01969793883335047
|
| 242 |
+
]
|
| 243 |
+
},
|
| 244 |
+
"qwen25_fixed8l": {
|
| 245 |
+
"other_macro_f1_mean": 0.513131621013363,
|
| 246 |
+
"macro_mean_delta_this_minus_other": 0.07443633494222779,
|
| 247 |
+
"paired_seed_macro_deltas": [
|
| 248 |
+
0.056130719634202686,
|
| 249 |
+
0.09312943034491972,
|
| 250 |
+
0.07404885484756096
|
| 251 |
+
]
|
| 252 |
+
},
|
| 253 |
+
"mbert": {
|
| 254 |
+
"other_macro_f1_mean": 0.5319538028157794,
|
| 255 |
+
"macro_mean_delta_this_minus_other": 0.05561415313981133,
|
| 256 |
+
"paired_seed_macro_deltas": [
|
| 257 |
+
0.03516462711549406,
|
| 258 |
+
0.07017577990032031,
|
| 259 |
+
0.06150205240361961
|
| 260 |
+
]
|
| 261 |
+
},
|
| 262 |
+
"minilm_multilingual": {
|
| 263 |
+
"other_macro_f1_mean": 0.5078871917240945,
|
| 264 |
+
"macro_mean_delta_this_minus_other": 0.07968076423149628,
|
| 265 |
+
"paired_seed_macro_deltas": [
|
| 266 |
+
0.09316945437553353,
|
| 267 |
+
0.08396188337177446,
|
| 268 |
+
0.061910954947180796
|
| 269 |
+
]
|
| 270 |
+
},
|
| 271 |
+
"claim_boundary": "Raw descriptive shared-protocol comparisons only. Three paired seeds and one 55-article opened public-gold test split do not establish population-level superiority, deployment superiority, or an overall systems winner."
|
| 272 |
+
},
|
| 273 |
+
"environment": {
|
| 274 |
+
"optional_fast_linear_attention_installed": false,
|
| 275 |
+
"optional_causal_convolution_installed": false,
|
| 276 |
+
"execution_path": "Transformers PyTorch fallback",
|
| 277 |
+
"tensorboard_added_mid_matrix": false
|
| 278 |
+
},
|
| 279 |
+
"exclusions": {
|
| 280 |
+
"historical_opened_calibration_diagnostic_used_as_result": false,
|
| 281 |
+
"historical_joint_heads_loaded": false,
|
| 282 |
+
"semeval2023_task3_private_or_final_data_used": false,
|
| 283 |
+
"mac_used": false,
|
| 284 |
+
"new_human_gold_created": false,
|
| 285 |
+
"uno_service_disrupted": false
|
| 286 |
+
},
|
| 287 |
+
"independent_audit": "passed",
|
| 288 |
+
"derived_research_indexes_rebuilt": false
|
| 289 |
+
}
|
models/semeval-propaganda/benchmark/seed41.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
models/semeval-propaganda/benchmark/seed42.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
models/semeval-propaganda/benchmark/seed43.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
models/semeval-propaganda/chat_template.jinja
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{%- set image_count = namespace(value=0) %}
|
| 2 |
+
{%- set video_count = namespace(value=0) %}
|
| 3 |
+
{%- macro render_content(content, do_vision_count) %}
|
| 4 |
+
{%- if content is string %}
|
| 5 |
+
{{- content }}
|
| 6 |
+
{%- else %}
|
| 7 |
+
{%- for item in content %}
|
| 8 |
+
{%- if 'image' in item or 'image_url' in item or item.type == 'image' %}
|
| 9 |
+
{%- if do_vision_count %}
|
| 10 |
+
{%- set image_count.value = image_count.value + 1 %}
|
| 11 |
+
{%- endif %}
|
| 12 |
+
{%- if add_vision_id %}Picture {{ image_count.value }}: {% endif -%}
|
| 13 |
+
<|vision_start|><|image_pad|><|vision_end|>
|
| 14 |
+
{%- elif 'video' in item or item.type == 'video' %}
|
| 15 |
+
{%- if do_vision_count %}
|
| 16 |
+
{%- set video_count.value = video_count.value + 1 %}
|
| 17 |
+
{%- endif %}
|
| 18 |
+
{%- if add_vision_id %}Video {{ video_count.value }}: {% endif -%}
|
| 19 |
+
<|vision_start|><|video_pad|><|vision_end|>
|
| 20 |
+
{%- elif 'text' in item %}
|
| 21 |
+
{{- item.text }}
|
| 22 |
+
{%- endif %}
|
| 23 |
+
{%- endfor %}
|
| 24 |
+
{%- endif %}
|
| 25 |
+
{%- endmacro %}
|
| 26 |
+
{%- if tools is defined and tools is iterable and tools | length > 0 %}
|
| 27 |
+
{{- '<|im_start|>system\n' }}
|
| 28 |
+
{{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
|
| 29 |
+
{%- for tool in tools %}
|
| 30 |
+
{{- "\n" }}
|
| 31 |
+
{{- tool | tojson }}
|
| 32 |
+
{%- endfor %}
|
| 33 |
+
{{- "\n</tools>" }}
|
| 34 |
+
{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
|
| 35 |
+
{%- if messages[0].role == 'system' and messages[0].content | trim %}
|
| 36 |
+
{%- set content = render_content(messages[0].content, false) %}
|
| 37 |
+
{{- '\n\n' + content.lstrip('\n') }}
|
| 38 |
+
{%- endif %}
|
| 39 |
+
{{- '<|im_end|>\n' }}
|
| 40 |
+
{%- else %}
|
| 41 |
+
{%- if messages[0].role == 'system' %}
|
| 42 |
+
{{- '<|im_start|>system\n' + render_content(messages[0].content, false) + '<|im_end|>\n' }}
|
| 43 |
+
{%- endif %}
|
| 44 |
+
{%- endif %}
|
| 45 |
+
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
| 46 |
+
{%- for message in messages[::-1] %}
|
| 47 |
+
{%- set index = (messages|length - 1) - loop.index0 %}
|
| 48 |
+
{%- if ns.multi_step_tool and message.role == "user" %}
|
| 49 |
+
{%- set content = render_content(message.content, False) %}
|
| 50 |
+
{%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
|
| 51 |
+
{%- set ns.multi_step_tool = false %}
|
| 52 |
+
{%- set ns.last_query_index = index %}
|
| 53 |
+
{%- endif %}
|
| 54 |
+
{%- endif %}
|
| 55 |
+
{%- endfor %}
|
| 56 |
+
{%- for message in messages %}
|
| 57 |
+
{%- set content = render_content(message.content, true) %}
|
| 58 |
+
{%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
|
| 59 |
+
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
|
| 60 |
+
{%- elif message.role == "assistant" %}
|
| 61 |
+
{%- set reasoning_content = '' %}
|
| 62 |
+
{%- if message.reasoning_content is defined and message.reasoning_content is not none %}
|
| 63 |
+
{%- set reasoning_content = message.reasoning_content %}
|
| 64 |
+
{%- else %}
|
| 65 |
+
{%- if '</think>' in content %}
|
| 66 |
+
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
| 67 |
+
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
|
| 68 |
+
{%- endif %}
|
| 69 |
+
{%- endif %}
|
| 70 |
+
{%- if message.tool_calls is defined and message.tool_calls is not none and message.tool_calls is iterable and message.tool_calls | length > 0 %}
|
| 71 |
+
{%- set content = content.rstrip('\n') %}
|
| 72 |
+
{%- endif %}
|
| 73 |
+
{%- if loop.index0 > ns.last_query_index %}
|
| 74 |
+
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
|
| 75 |
+
{%- else %}
|
| 76 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 77 |
+
{%- endif %}
|
| 78 |
+
{%- if message.tool_calls is defined and message.tool_calls is not none and message.tool_calls is iterable and message.tool_calls | length > 0 %}
|
| 79 |
+
{%- for tool_call in message.tool_calls %}
|
| 80 |
+
{%- if tool_call.function is defined %}
|
| 81 |
+
{%- set tool_call = tool_call.function %}
|
| 82 |
+
{%- endif %}
|
| 83 |
+
{%- if loop.first %}
|
| 84 |
+
{%- if content|trim %}
|
| 85 |
+
{{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 86 |
+
{%- else %}
|
| 87 |
+
{{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 88 |
+
{%- endif %}
|
| 89 |
+
{%- else %}
|
| 90 |
+
{{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 91 |
+
{%- endif %}
|
| 92 |
+
{%- if tool_call.arguments is defined %}
|
| 93 |
+
{%- for args_name, args_value in tool_call.arguments|items %}
|
| 94 |
+
{{- '<parameter=' + args_name + '>\n' }}
|
| 95 |
+
{%- set args_value = args_value | tojson | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string %}
|
| 96 |
+
{{- args_value }}
|
| 97 |
+
{{- '\n</parameter>\n' }}
|
| 98 |
+
{%- endfor %}
|
| 99 |
+
{%- endif %}
|
| 100 |
+
{{- '</function>\n</tool_call>' }}
|
| 101 |
+
{%- endfor %}
|
| 102 |
+
{%- endif %}
|
| 103 |
+
{{- '<|im_end|>\n' }}
|
| 104 |
+
{%- elif message.role == "tool" %}
|
| 105 |
+
{%- if loop.previtem and loop.previtem.role != "tool" %}
|
| 106 |
+
{{- '<|im_start|>user' }}
|
| 107 |
+
{%- endif %}
|
| 108 |
+
{{- '\n<tool_response>\n' }}
|
| 109 |
+
{{- content }}
|
| 110 |
+
{{- '\n</tool_response>' }}
|
| 111 |
+
{%- if not loop.last and loop.nextitem.role != "tool" %}
|
| 112 |
+
{{- '<|im_end|>\n' }}
|
| 113 |
+
{%- elif loop.last %}
|
| 114 |
+
{{- '<|im_end|>\n' }}
|
| 115 |
+
{%- endif %}
|
| 116 |
+
{%- endif %}
|
| 117 |
+
{%- endfor %}
|
| 118 |
+
{%- if add_generation_prompt %}
|
| 119 |
+
{{- '<|im_start|>assistant\n<think>\n' }}
|
| 120 |
+
{%- endif %}
|
models/semeval-propaganda/classify.py
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Run the public Qwen3.5 standalone-4L multilabel classifier.
|
| 3 |
+
|
| 4 |
+
The script is intentionally self-contained so the same file can be uploaded as
|
| 5 |
+
``classify.py`` to Hugging Face. Long articles are split into the frozen
|
| 6 |
+
256-token / 128-token-overlap windows and combined with a label-wise maximum.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
from __future__ import annotations
|
| 10 |
+
|
| 11 |
+
import argparse
|
| 12 |
+
import json
|
| 13 |
+
import math
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
from typing import Any, Sequence
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
from transformers import AutoModelForSequenceClassification, AutoTokenizer
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
DEFAULT_MODEL = "mp-juuuns/qwen35-standalone4l-propaganda-classifier"
|
| 22 |
+
LOCKED_THRESHOLDS = {41: 0.10, 42: 0.05, 43: 0.05}
|
| 23 |
+
MAX_LENGTH = 256
|
| 24 |
+
STRIDE = 128
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def seed_subfolder(model_id: str, seed: int) -> str | None:
|
| 28 |
+
"""Resolve the public repo layout while allowing direct local checkpoints."""
|
| 29 |
+
|
| 30 |
+
if seed == 41:
|
| 31 |
+
return None
|
| 32 |
+
local = Path(model_id)
|
| 33 |
+
if local.exists():
|
| 34 |
+
candidate = local / "seeds" / f"seed{seed}"
|
| 35 |
+
return f"seeds/seed{seed}" if candidate.is_dir() else None
|
| 36 |
+
return f"seeds/seed{seed}"
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def resolve_device(requested: str) -> torch.device:
|
| 40 |
+
if requested == "auto":
|
| 41 |
+
return torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 42 |
+
device = torch.device(requested)
|
| 43 |
+
if device.type == "cuda" and not torch.cuda.is_available():
|
| 44 |
+
raise RuntimeError("CUDA was requested but is not available")
|
| 45 |
+
return device
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def load_runtime(
|
| 49 |
+
model_id: str,
|
| 50 |
+
*,
|
| 51 |
+
seed: int,
|
| 52 |
+
device: torch.device,
|
| 53 |
+
revision: str | None = None,
|
| 54 |
+
local_files_only: bool = False,
|
| 55 |
+
) -> tuple[Any, Any, list[str]]:
|
| 56 |
+
subfolder = seed_subfolder(model_id, seed)
|
| 57 |
+
shared: dict[str, Any] = {
|
| 58 |
+
"revision": revision,
|
| 59 |
+
"local_files_only": local_files_only,
|
| 60 |
+
"trust_remote_code": False,
|
| 61 |
+
}
|
| 62 |
+
if subfolder:
|
| 63 |
+
shared["subfolder"] = subfolder
|
| 64 |
+
tokenizer = AutoTokenizer.from_pretrained(model_id, **shared)
|
| 65 |
+
if tokenizer.pad_token_id is None:
|
| 66 |
+
if tokenizer.eos_token_id is None:
|
| 67 |
+
raise RuntimeError("tokenizer has neither pad_token_id nor eos_token_id")
|
| 68 |
+
tokenizer.pad_token = tokenizer.eos_token
|
| 69 |
+
dtype = torch.bfloat16 if device.type == "cuda" and torch.cuda.is_bf16_supported() else (
|
| 70 |
+
torch.float16 if device.type == "cuda" else torch.float32
|
| 71 |
+
)
|
| 72 |
+
model = AutoModelForSequenceClassification.from_pretrained(
|
| 73 |
+
model_id,
|
| 74 |
+
dtype=dtype,
|
| 75 |
+
**shared,
|
| 76 |
+
).to(device)
|
| 77 |
+
model.eval()
|
| 78 |
+
if hasattr(model.config, "use_cache"):
|
| 79 |
+
model.config.use_cache = False
|
| 80 |
+
id2label = getattr(model.config, "id2label", {})
|
| 81 |
+
labels = [str(id2label.get(index, id2label.get(str(index), f"LABEL_{index}"))) for index in range(14)]
|
| 82 |
+
if len(labels) != 14 or any(label.startswith("LABEL_") for label in labels):
|
| 83 |
+
raise RuntimeError("checkpoint does not expose the fixed 14-label order")
|
| 84 |
+
return tokenizer, model, labels
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def classify_texts(
|
| 88 |
+
texts: Sequence[str],
|
| 89 |
+
*,
|
| 90 |
+
tokenizer: Any,
|
| 91 |
+
model: Any,
|
| 92 |
+
labels: Sequence[str],
|
| 93 |
+
threshold: float,
|
| 94 |
+
device: torch.device,
|
| 95 |
+
batch_size: int = 8,
|
| 96 |
+
) -> list[dict[str, Any]]:
|
| 97 |
+
if not texts or any(not isinstance(text, str) or not text.strip() for text in texts):
|
| 98 |
+
raise ValueError("at least one non-empty text is required")
|
| 99 |
+
if len(labels) != 14:
|
| 100 |
+
raise ValueError("expected the fixed 14-label order")
|
| 101 |
+
if not 0 < threshold < 1:
|
| 102 |
+
raise ValueError("threshold must be in (0, 1)")
|
| 103 |
+
encoded = tokenizer(
|
| 104 |
+
list(texts),
|
| 105 |
+
truncation=True,
|
| 106 |
+
max_length=MAX_LENGTH,
|
| 107 |
+
stride=STRIDE,
|
| 108 |
+
return_overflowing_tokens=True,
|
| 109 |
+
padding=True,
|
| 110 |
+
return_tensors="pt",
|
| 111 |
+
)
|
| 112 |
+
mapping = encoded.pop("overflow_to_sample_mapping")
|
| 113 |
+
model_inputs = {
|
| 114 |
+
key: value
|
| 115 |
+
for key, value in encoded.items()
|
| 116 |
+
if key in {"input_ids", "attention_mask", "token_type_ids"}
|
| 117 |
+
}
|
| 118 |
+
window_probabilities: list[torch.Tensor] = []
|
| 119 |
+
with torch.inference_mode():
|
| 120 |
+
for start in range(0, int(mapping.numel()), batch_size):
|
| 121 |
+
batch = {
|
| 122 |
+
key: value[start : start + batch_size].to(device)
|
| 123 |
+
for key, value in model_inputs.items()
|
| 124 |
+
}
|
| 125 |
+
logits = model(**batch).logits
|
| 126 |
+
if logits.ndim != 2 or logits.shape[1] != 14:
|
| 127 |
+
raise RuntimeError(f"unexpected classifier output shape: {tuple(logits.shape)}")
|
| 128 |
+
window_probabilities.append(torch.sigmoid(logits.float()).cpu())
|
| 129 |
+
probabilities = torch.cat(window_probabilities, dim=0)
|
| 130 |
+
if probabilities.shape != (mapping.numel(), 14) or not torch.isfinite(probabilities).all():
|
| 131 |
+
raise RuntimeError("non-finite or malformed probability matrix")
|
| 132 |
+
outputs: list[dict[str, Any]] = []
|
| 133 |
+
mapping_list = mapping.tolist()
|
| 134 |
+
for article_index, _ in enumerate(texts):
|
| 135 |
+
indices = [index for index, mapped in enumerate(mapping_list) if mapped == article_index]
|
| 136 |
+
if not indices:
|
| 137 |
+
raise RuntimeError(f"tokenizer produced no window for input {article_index}")
|
| 138 |
+
article_scores = probabilities[indices].amax(dim=0).tolist()
|
| 139 |
+
if not all(math.isfinite(value) for value in article_scores):
|
| 140 |
+
raise RuntimeError("non-finite article score")
|
| 141 |
+
predicted_ids = [index for index, score in enumerate(article_scores) if score >= threshold]
|
| 142 |
+
outputs.append(
|
| 143 |
+
{
|
| 144 |
+
"input_index": article_index,
|
| 145 |
+
"windows": len(indices),
|
| 146 |
+
"predicted_ids": predicted_ids,
|
| 147 |
+
"predicted_labels": [labels[index] for index in predicted_ids],
|
| 148 |
+
"threshold": threshold,
|
| 149 |
+
"scores": [
|
| 150 |
+
{"id": index, "label": label, "probability": article_scores[index]}
|
| 151 |
+
for index, label in enumerate(labels)
|
| 152 |
+
],
|
| 153 |
+
}
|
| 154 |
+
)
|
| 155 |
+
return outputs
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def parse_args() -> argparse.Namespace:
|
| 159 |
+
parser = argparse.ArgumentParser(description=__doc__)
|
| 160 |
+
parser.add_argument("--model", default=DEFAULT_MODEL)
|
| 161 |
+
parser.add_argument("--revision")
|
| 162 |
+
parser.add_argument("--seed", type=int, choices=sorted(LOCKED_THRESHOLDS), default=41)
|
| 163 |
+
parser.add_argument("--text", action="append", default=[])
|
| 164 |
+
parser.add_argument("--text-file", type=Path, action="append", default=[])
|
| 165 |
+
parser.add_argument("--threshold", type=float)
|
| 166 |
+
parser.add_argument("--device", choices=("auto", "cpu", "cuda"), default="auto")
|
| 167 |
+
parser.add_argument("--batch-size", type=int, default=8)
|
| 168 |
+
parser.add_argument("--local-files-only", action="store_true")
|
| 169 |
+
return parser.parse_args()
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
def main() -> int:
|
| 173 |
+
args = parse_args()
|
| 174 |
+
texts = list(args.text)
|
| 175 |
+
texts.extend(path.read_text(encoding="utf-8") for path in args.text_file)
|
| 176 |
+
if not texts:
|
| 177 |
+
raise SystemExit("provide --text or --text-file")
|
| 178 |
+
if args.batch_size < 1:
|
| 179 |
+
raise SystemExit("--batch-size must be positive")
|
| 180 |
+
threshold = LOCKED_THRESHOLDS[args.seed] if args.threshold is None else args.threshold
|
| 181 |
+
device = resolve_device(args.device)
|
| 182 |
+
tokenizer, model, labels = load_runtime(
|
| 183 |
+
args.model,
|
| 184 |
+
seed=args.seed,
|
| 185 |
+
device=device,
|
| 186 |
+
revision=args.revision,
|
| 187 |
+
local_files_only=args.local_files_only,
|
| 188 |
+
)
|
| 189 |
+
outputs = classify_texts(
|
| 190 |
+
texts,
|
| 191 |
+
tokenizer=tokenizer,
|
| 192 |
+
model=model,
|
| 193 |
+
labels=labels,
|
| 194 |
+
threshold=threshold,
|
| 195 |
+
device=device,
|
| 196 |
+
batch_size=args.batch_size,
|
| 197 |
+
)
|
| 198 |
+
payload = {
|
| 199 |
+
"model": args.model,
|
| 200 |
+
"seed": args.seed,
|
| 201 |
+
"threshold": threshold,
|
| 202 |
+
"device": str(device),
|
| 203 |
+
"contract": {
|
| 204 |
+
"task": "14-label multilabel propaganda-technique classification",
|
| 205 |
+
"max_length": MAX_LENGTH,
|
| 206 |
+
"stride": STRIDE,
|
| 207 |
+
"article_aggregation": "labelwise maximum probability across windows",
|
| 208 |
+
"trust_remote_code": False,
|
| 209 |
+
},
|
| 210 |
+
"outputs": outputs,
|
| 211 |
+
}
|
| 212 |
+
print(json.dumps(payload, ensure_ascii=False, indent=2, allow_nan=False))
|
| 213 |
+
return 0
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
if __name__ == "__main__":
|
| 217 |
+
raise SystemExit(main())
|
models/semeval-propaganda/config.json
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"Qwen3_5TextForSequenceClassification"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"attn_output_gate": true,
|
| 8 |
+
"bos_token_id": null,
|
| 9 |
+
"dtype": "bfloat16",
|
| 10 |
+
"eos_token_id": 127967,
|
| 11 |
+
"full_attention_interval": 4,
|
| 12 |
+
"head_dim": 256,
|
| 13 |
+
"hidden_act": "silu",
|
| 14 |
+
"hidden_size": 1024,
|
| 15 |
+
"id2label": {
|
| 16 |
+
"0": "Appeal_to_Authority",
|
| 17 |
+
"1": "Appeal_to_fear-prejudice",
|
| 18 |
+
"2": "Bandwagon,Reductio_ad_hitlerum",
|
| 19 |
+
"3": "Black-and-White_Fallacy",
|
| 20 |
+
"4": "Causal_Oversimplification",
|
| 21 |
+
"5": "Doubt",
|
| 22 |
+
"6": "Exaggeration,Minimisation",
|
| 23 |
+
"7": "Flag-Waving",
|
| 24 |
+
"8": "Loaded_Language",
|
| 25 |
+
"9": "Name_Calling,Labeling",
|
| 26 |
+
"10": "Repetition",
|
| 27 |
+
"11": "Slogans",
|
| 28 |
+
"12": "Thought-terminating_Cliches",
|
| 29 |
+
"13": "Whataboutism,Straw_Men,Red_Herring"
|
| 30 |
+
},
|
| 31 |
+
"initializer_range": 0.02,
|
| 32 |
+
"intermediate_size": 3584,
|
| 33 |
+
"label2id": {
|
| 34 |
+
"Appeal_to_Authority": 0,
|
| 35 |
+
"Appeal_to_fear-prejudice": 1,
|
| 36 |
+
"Bandwagon,Reductio_ad_hitlerum": 2,
|
| 37 |
+
"Black-and-White_Fallacy": 3,
|
| 38 |
+
"Causal_Oversimplification": 4,
|
| 39 |
+
"Doubt": 5,
|
| 40 |
+
"Exaggeration,Minimisation": 6,
|
| 41 |
+
"Flag-Waving": 7,
|
| 42 |
+
"Loaded_Language": 8,
|
| 43 |
+
"Name_Calling,Labeling": 9,
|
| 44 |
+
"Repetition": 10,
|
| 45 |
+
"Slogans": 11,
|
| 46 |
+
"Thought-terminating_Cliches": 12,
|
| 47 |
+
"Whataboutism,Straw_Men,Red_Herring": 13
|
| 48 |
+
},
|
| 49 |
+
"layer_types": [
|
| 50 |
+
"linear_attention",
|
| 51 |
+
"full_attention",
|
| 52 |
+
"linear_attention",
|
| 53 |
+
"full_attention"
|
| 54 |
+
],
|
| 55 |
+
"linear_conv_kernel_dim": 4,
|
| 56 |
+
"linear_key_head_dim": 128,
|
| 57 |
+
"linear_num_key_heads": 16,
|
| 58 |
+
"linear_num_value_heads": 16,
|
| 59 |
+
"linear_value_head_dim": 128,
|
| 60 |
+
"mamba_ssm_dtype": "float32",
|
| 61 |
+
"max_position_embeddings": 262144,
|
| 62 |
+
"mlp_only_layers": [],
|
| 63 |
+
"model_type": "qwen3_5_text",
|
| 64 |
+
"mtp_num_hidden_layers": 1,
|
| 65 |
+
"mtp_use_dedicated_embeddings": false,
|
| 66 |
+
"num_attention_heads": 8,
|
| 67 |
+
"num_hidden_layers": 4,
|
| 68 |
+
"num_key_value_heads": 2,
|
| 69 |
+
"pad_token_id": 127967,
|
| 70 |
+
"partial_rotary_factor": 0.25,
|
| 71 |
+
"phase_v6_map_selection_rank": 1,
|
| 72 |
+
"phase_v6_map_selection_split": "public_gold_train",
|
| 73 |
+
"phase_v6_materialized_role": "student6l-r3-final-teacher",
|
| 74 |
+
"phase_v6_materialized_trainable_state": "results/layer_shrink_v6/20260726-v6-r3-student6l-map013457-kd-seed41-pilot-v2/student-6l-kd-trainable.safetensors",
|
| 75 |
+
"phase_v6_selected_original_8l_layers": [
|
| 76 |
+
0,
|
| 77 |
+
3,
|
| 78 |
+
4,
|
| 79 |
+
7
|
| 80 |
+
],
|
| 81 |
+
"phase_v6_selected_source_layers": [
|
| 82 |
+
0,
|
| 83 |
+
1,
|
| 84 |
+
3,
|
| 85 |
+
4,
|
| 86 |
+
5,
|
| 87 |
+
7
|
| 88 |
+
],
|
| 89 |
+
"phase_v6_selected_source_layers_6l": [
|
| 90 |
+
0,
|
| 91 |
+
2,
|
| 92 |
+
3,
|
| 93 |
+
5
|
| 94 |
+
],
|
| 95 |
+
"phase_v6_source_teacher": "student6l-r3-final",
|
| 96 |
+
"phase_v6_standalone4l_kd": {
|
| 97 |
+
"canonical_materialization_dtype": "bfloat16",
|
| 98 |
+
"canonical_training_state_preservation": true,
|
| 99 |
+
"epochs": 3,
|
| 100 |
+
"legacy_float16_checkpoint": "results/layer_shrink_v6/20260728-v6-r23-standalone4l-kd-seed41-v1/student-4l-final",
|
| 101 |
+
"opened_calibration_used_for_model_selection": false,
|
| 102 |
+
"seed": 41,
|
| 103 |
+
"teacher": "results/layer_shrink_v6/20260728-v6-r22-standalone4l-map-probe-seed41-v1/teacher-6l-r3-final"
|
| 104 |
+
},
|
| 105 |
+
"problem_type": "multi_label_classification",
|
| 106 |
+
"rms_norm_eps": 1e-06,
|
| 107 |
+
"rope_parameters": {
|
| 108 |
+
"mrope_interleaved": true,
|
| 109 |
+
"mrope_section": [
|
| 110 |
+
11,
|
| 111 |
+
11,
|
| 112 |
+
10
|
| 113 |
+
],
|
| 114 |
+
"partial_rotary_factor": 0.25,
|
| 115 |
+
"rope_theta": 10000000,
|
| 116 |
+
"rope_type": "default"
|
| 117 |
+
},
|
| 118 |
+
"tie_word_embeddings": true,
|
| 119 |
+
"transformers_version": "5.13.0",
|
| 120 |
+
"use_cache": false,
|
| 121 |
+
"vocab_size": 128000
|
| 122 |
+
}
|
models/semeval-propaganda/labels.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"labels": [
|
| 3 |
+
"Appeal_to_Authority",
|
| 4 |
+
"Appeal_to_fear-prejudice",
|
| 5 |
+
"Bandwagon,Reductio_ad_hitlerum",
|
| 6 |
+
"Black-and-White_Fallacy",
|
| 7 |
+
"Causal_Oversimplification",
|
| 8 |
+
"Doubt",
|
| 9 |
+
"Exaggeration,Minimisation",
|
| 10 |
+
"Flag-Waving",
|
| 11 |
+
"Loaded_Language",
|
| 12 |
+
"Name_Calling,Labeling",
|
| 13 |
+
"Repetition",
|
| 14 |
+
"Slogans",
|
| 15 |
+
"Thought-terminating_Cliches",
|
| 16 |
+
"Whataboutism,Straw_Men,Red_Herring"
|
| 17 |
+
],
|
| 18 |
+
"id2label": {
|
| 19 |
+
"0": "Appeal_to_Authority",
|
| 20 |
+
"1": "Appeal_to_fear-prejudice",
|
| 21 |
+
"2": "Bandwagon,Reductio_ad_hitlerum",
|
| 22 |
+
"3": "Black-and-White_Fallacy",
|
| 23 |
+
"4": "Causal_Oversimplification",
|
| 24 |
+
"5": "Doubt",
|
| 25 |
+
"6": "Exaggeration,Minimisation",
|
| 26 |
+
"7": "Flag-Waving",
|
| 27 |
+
"8": "Loaded_Language",
|
| 28 |
+
"9": "Name_Calling,Labeling",
|
| 29 |
+
"10": "Repetition",
|
| 30 |
+
"11": "Slogans",
|
| 31 |
+
"12": "Thought-terminating_Cliches",
|
| 32 |
+
"13": "Whataboutism,Straw_Men,Red_Herring"
|
| 33 |
+
}
|
| 34 |
+
}
|
models/semeval-propaganda/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0352da6e4717a2e6f3a9d643d034be2cb1c39632d1bce0ced339ddb62acedb37
|
| 3 |
+
size 421812688
|
models/semeval-propaganda/release_manifest.json
ADDED
|
@@ -0,0 +1,637 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"schema": "qwen35-standalone4l-huggingface-release-manifest-v2",
|
| 3 |
+
"status": "complete",
|
| 4 |
+
"repo_id": "mp-juuuns/qwen35-standalone4l-propaganda-classifier",
|
| 5 |
+
"root_checkpoint_seed": 41,
|
| 6 |
+
"seed_metadata": [
|
| 7 |
+
{
|
| 8 |
+
"seed": 41,
|
| 9 |
+
"threshold": 0.1,
|
| 10 |
+
"macro_f1": 0.5832848452288824,
|
| 11 |
+
"micro_f1": 0.6494252873563218,
|
| 12 |
+
"report_sha256": "ec5314140fff0bdd46e87ee00ad67bee7e61d82c7cfa34add1510dd7d5bdb3c0",
|
| 13 |
+
"checkpoint_hashes": {
|
| 14 |
+
"model.safetensors": "0352da6e4717a2e6f3a9d643d034be2cb1c39632d1bce0ced339ddb62acedb37",
|
| 15 |
+
"config.json": "9f04dfbd7e1696cd3df2ccbe194f5f155efe5baefd1a0b615851f51bf46691a0",
|
| 16 |
+
"tokenizer.json": "dd2b27bbd4f5f650c7200300013ef03ee01efe620b128343163521bf04806fc8",
|
| 17 |
+
"tokenizer_config.json": "2215318fe27878ec67e772931bb5f40a98658348d33f2672a575f766a2190d35",
|
| 18 |
+
"chat_template.jinja": "04b007131663760bf3e581e5a953be77044014e87efe1d2a6ca4b72ec0eac978"
|
| 19 |
+
}
|
| 20 |
+
},
|
| 21 |
+
{
|
| 22 |
+
"seed": 42,
|
| 23 |
+
"threshold": 0.05,
|
| 24 |
+
"macro_f1": 0.5958405181160762,
|
| 25 |
+
"micro_f1": 0.6458616010854816,
|
| 26 |
+
"report_sha256": "e5c45a7dc41bebf4550da7c5763d0759c6d0922da9efda4048564c11c6d2f3af",
|
| 27 |
+
"checkpoint_hashes": {
|
| 28 |
+
"model.safetensors": "2db5f4be391d9dee64458cb5865530f39747019e7a219443b5f7850befbce697",
|
| 29 |
+
"config.json": "9f04dfbd7e1696cd3df2ccbe194f5f155efe5baefd1a0b615851f51bf46691a0",
|
| 30 |
+
"tokenizer.json": "dd2b27bbd4f5f650c7200300013ef03ee01efe620b128343163521bf04806fc8",
|
| 31 |
+
"tokenizer_config.json": "2215318fe27878ec67e772931bb5f40a98658348d33f2672a575f766a2190d35",
|
| 32 |
+
"chat_template.jinja": "04b007131663760bf3e581e5a953be77044014e87efe1d2a6ca4b72ec0eac978"
|
| 33 |
+
}
|
| 34 |
+
},
|
| 35 |
+
{
|
| 36 |
+
"seed": 43,
|
| 37 |
+
"threshold": 0.05,
|
| 38 |
+
"macro_f1": 0.5835785045218137,
|
| 39 |
+
"micro_f1": 0.6486486486486486,
|
| 40 |
+
"report_sha256": "e05bc2146d720706780fc45a1fb05911d5c2926caa0bcb12ffead946028eef0d",
|
| 41 |
+
"checkpoint_hashes": {
|
| 42 |
+
"model.safetensors": "81a56d497df758e8a292eea528438cfc4d5a847cd37d3a7e1107fefa7c500640",
|
| 43 |
+
"config.json": "9f04dfbd7e1696cd3df2ccbe194f5f155efe5baefd1a0b615851f51bf46691a0",
|
| 44 |
+
"tokenizer.json": "dd2b27bbd4f5f650c7200300013ef03ee01efe620b128343163521bf04806fc8",
|
| 45 |
+
"tokenizer_config.json": "2215318fe27878ec67e772931bb5f40a98658348d33f2672a575f766a2190d35",
|
| 46 |
+
"chat_template.jinja": "04b007131663760bf3e581e5a953be77044014e87efe1d2a6ca4b72ec0eac978"
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
],
|
| 50 |
+
"files_before_manifest": [
|
| 51 |
+
{
|
| 52 |
+
"path": ".gitattributes",
|
| 53 |
+
"role": "Hugging Face large-file policy",
|
| 54 |
+
"bytes": 1243,
|
| 55 |
+
"sha256": "cb9f787a7d5053ba7b8a4c7d6938a85ad902321fafd923cfbbab160eaf6b33fd",
|
| 56 |
+
"source": "generated",
|
| 57 |
+
"source_sha256": null
|
| 58 |
+
},
|
| 59 |
+
{
|
| 60 |
+
"path": "LICENSE",
|
| 61 |
+
"role": "Apache-2.0 license text",
|
| 62 |
+
"bytes": 11418,
|
| 63 |
+
"sha256": "77fd4710def9ec3c0f6225800e0235f15a425abd4a8b03559127fcd782612049",
|
| 64 |
+
"source": "references/software/transformers-5.13.0/LICENSE",
|
| 65 |
+
"source_sha256": "77fd4710def9ec3c0f6225800e0235f15a425abd4a8b03559127fcd782612049"
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"path": "README.md",
|
| 69 |
+
"role": "Hugging Face model card",
|
| 70 |
+
"bytes": 26722,
|
| 71 |
+
"sha256": "951d304189735d79403abde007b88992a0c7c6f60b8a01cb066596f703d6ed89",
|
| 72 |
+
"source": "generated",
|
| 73 |
+
"source_sha256": null
|
| 74 |
+
},
|
| 75 |
+
{
|
| 76 |
+
"path": "benchmark/MODEL_CARD_EVIDENCE.md",
|
| 77 |
+
"role": "claim-by-claim model-card evidence ledger",
|
| 78 |
+
"bytes": 14498,
|
| 79 |
+
"sha256": "80ae7e7fc6ef834188d2a02bcaa5dcc657d88b8b007b9904182fec8b5d555e61",
|
| 80 |
+
"source": "docs/huggingface/STANDALONE4L_MODEL_CARD_EVIDENCE.md",
|
| 81 |
+
"source_sha256": "80ae7e7fc6ef834188d2a02bcaa5dcc657d88b8b007b9904182fec8b5d555e61"
|
| 82 |
+
},
|
| 83 |
+
{
|
| 84 |
+
"path": "benchmark/SEALED_REPORT.md",
|
| 85 |
+
"role": "sealed benchmark narrative",
|
| 86 |
+
"bytes": 7049,
|
| 87 |
+
"sha256": "f74f85f43c3d18c836a74cb325d9cd1d6174a7932a2bf6f3664b5849eededc69",
|
| 88 |
+
"source": "results/final_broad_benchmark/20260729-v1/report/REPORT.md",
|
| 89 |
+
"source_sha256": "f74f85f43c3d18c836a74cb325d9cd1d6174a7932a2bf6f3664b5849eededc69"
|
| 90 |
+
},
|
| 91 |
+
{
|
| 92 |
+
"path": "benchmark/SEALED_REPORT_SHA256SUMS.txt",
|
| 93 |
+
"role": "sealed benchmark evidence",
|
| 94 |
+
"bytes": 2399,
|
| 95 |
+
"sha256": "f2c05574d9c89a5e6fbc2cd94ab313e533a1315cd813739fa80fd0f38df8274a",
|
| 96 |
+
"source": "docs/benchmarks/final_broad_20260730/SEALED_REPORT_SHA256SUMS.txt",
|
| 97 |
+
"source_sha256": "f2c05574d9c89a5e6fbc2cd94ab313e533a1315cd813739fa80fd0f38df8274a"
|
| 98 |
+
},
|
| 99 |
+
{
|
| 100 |
+
"path": "benchmark/availability.csv",
|
| 101 |
+
"role": "sealed benchmark evidence",
|
| 102 |
+
"bytes": 1081,
|
| 103 |
+
"sha256": "6dc0633d6c3512638754c73dabfa49603abdc1d51de36a3cc9f529332d1497de",
|
| 104 |
+
"source": "docs/benchmarks/final_broad_20260730/availability.csv",
|
| 105 |
+
"source_sha256": "6dc0633d6c3512638754c73dabfa49603abdc1d51de36a3cc9f529332d1497de"
|
| 106 |
+
},
|
| 107 |
+
{
|
| 108 |
+
"path": "benchmark/edge_systems.csv",
|
| 109 |
+
"role": "sealed benchmark evidence",
|
| 110 |
+
"bytes": 679,
|
| 111 |
+
"sha256": "8ff11a3d57e0af3ae4836a85a56cf6bcafd62801d5c851b8178918bc4e67912f",
|
| 112 |
+
"source": "docs/benchmarks/final_broad_20260730/edge_systems.csv",
|
| 113 |
+
"source_sha256": "8ff11a3d57e0af3ae4836a85a56cf6bcafd62801d5c851b8178918bc4e67912f"
|
| 114 |
+
},
|
| 115 |
+
{
|
| 116 |
+
"path": "benchmark/examples/final_release_training_examples.audit.json",
|
| 117 |
+
"role": "independent release-example audit",
|
| 118 |
+
"bytes": 7534,
|
| 119 |
+
"sha256": "9f2869c905733a125a42128726acbb55f6f0aeeb7d8a9f40ea254f6d49e9f79c",
|
| 120 |
+
"source": "docs/benchmarks/examples/final_release_training_examples.audit.json",
|
| 121 |
+
"source_sha256": "9f2869c905733a125a42128726acbb55f6f0aeeb7d8a9f40ea254f6d49e9f79c",
|
| 122 |
+
"normalization": "none"
|
| 123 |
+
},
|
| 124 |
+
{
|
| 125 |
+
"path": "benchmark/examples/final_release_training_examples.json",
|
| 126 |
+
"role": "bounded training examples and per-model outputs",
|
| 127 |
+
"bytes": 101271,
|
| 128 |
+
"sha256": "97d292125709277a5023afa012a6bdaa61d1effc3ea58633d95188f07d69831a",
|
| 129 |
+
"source": "docs/benchmarks/examples/final_release_training_examples.json",
|
| 130 |
+
"source_sha256": "97d292125709277a5023afa012a6bdaa61d1effc3ea58633d95188f07d69831a",
|
| 131 |
+
"normalization": "none"
|
| 132 |
+
},
|
| 133 |
+
{
|
| 134 |
+
"path": "benchmark/figures/00_report_overview.png",
|
| 135 |
+
"role": "benchmark figure",
|
| 136 |
+
"bytes": 295438,
|
| 137 |
+
"sha256": "10c458787ad0cc19745bde47a3c2b94e229cff00d92a24c6a128d05e0a72b4e3",
|
| 138 |
+
"source": "docs/assets/final_broad_benchmark/00_report_overview.png",
|
| 139 |
+
"source_sha256": "10c458787ad0cc19745bde47a3c2b94e229cff00d92a24c6a128d05e0a72b4e3"
|
| 140 |
+
},
|
| 141 |
+
{
|
| 142 |
+
"path": "benchmark/figures/01_quality_macro_micro.png",
|
| 143 |
+
"role": "benchmark figure",
|
| 144 |
+
"bytes": 159292,
|
| 145 |
+
"sha256": "c7c8c42a69dd9eaa42c5805656c051e3fa3fd2e9f1dc3a737c463f77dd0010ce",
|
| 146 |
+
"source": "docs/assets/final_broad_benchmark/01_quality_macro_micro.png",
|
| 147 |
+
"source_sha256": "c7c8c42a69dd9eaa42c5805656c051e3fa3fd2e9f1dc3a737c463f77dd0010ce"
|
| 148 |
+
},
|
| 149 |
+
{
|
| 150 |
+
"path": "benchmark/figures/02_cuda_latency_cold.png",
|
| 151 |
+
"role": "benchmark figure",
|
| 152 |
+
"bytes": 192134,
|
| 153 |
+
"sha256": "ef7cf15fe1b980261f046c6b810f7bac715415407ec259ee1d0f32728e400409",
|
| 154 |
+
"source": "docs/assets/final_broad_benchmark/02_cuda_latency_cold.png",
|
| 155 |
+
"source_sha256": "ef7cf15fe1b980261f046c6b810f7bac715415407ec259ee1d0f32728e400409"
|
| 156 |
+
},
|
| 157 |
+
{
|
| 158 |
+
"path": "benchmark/figures/03_cuda_memory_telemetry.png",
|
| 159 |
+
"role": "benchmark figure",
|
| 160 |
+
"bytes": 264045,
|
| 161 |
+
"sha256": "5ecbfd18aa67b453bd77c6eadf84abe3bdfcf5cba1469111a1c6053605305538",
|
| 162 |
+
"source": "docs/assets/final_broad_benchmark/03_cuda_memory_telemetry.png",
|
| 163 |
+
"source_sha256": "5ecbfd18aa67b453bd77c6eadf84abe3bdfcf5cba1469111a1c6053605305538"
|
| 164 |
+
},
|
| 165 |
+
{
|
| 166 |
+
"path": "benchmark/figures/04_edge_x86_unoq.png",
|
| 167 |
+
"role": "benchmark figure",
|
| 168 |
+
"bytes": 171643,
|
| 169 |
+
"sha256": "520518e678b7e7f8e35680d84e254c3a630ede66b9eede5ed72efc73b5c0cc5a",
|
| 170 |
+
"source": "docs/assets/final_broad_benchmark/04_edge_x86_unoq.png",
|
| 171 |
+
"source_sha256": "520518e678b7e7f8e35680d84e254c3a630ede66b9eede5ed72efc73b5c0cc5a"
|
| 172 |
+
},
|
| 173 |
+
{
|
| 174 |
+
"path": "benchmark/figures/05_pareto_quality_latency_memory.png",
|
| 175 |
+
"role": "benchmark figure",
|
| 176 |
+
"bytes": 182234,
|
| 177 |
+
"sha256": "52884ce31ecf4a309e2af8401fc9a8ec498601aee38f0ff040dacb74fd7d7442",
|
| 178 |
+
"source": "docs/assets/final_broad_benchmark/05_pareto_quality_latency_memory.png",
|
| 179 |
+
"source_sha256": "52884ce31ecf4a309e2af8401fc9a8ec498601aee38f0ff040dacb74fd7d7442"
|
| 180 |
+
},
|
| 181 |
+
{
|
| 182 |
+
"path": "benchmark/figures/06_prompt_generation_diagnostic.png",
|
| 183 |
+
"role": "benchmark figure",
|
| 184 |
+
"bytes": 180996,
|
| 185 |
+
"sha256": "0bbd9f72b151d88b0913f72a36845042bb018930fc51d08a941e88acbd1dc500",
|
| 186 |
+
"source": "docs/assets/final_broad_benchmark/06_prompt_generation_diagnostic.png",
|
| 187 |
+
"source_sha256": "0bbd9f72b151d88b0913f72a36845042bb018930fc51d08a941e88acbd1dc500"
|
| 188 |
+
},
|
| 189 |
+
{
|
| 190 |
+
"path": "benchmark/figures/07_availability_missingness.png",
|
| 191 |
+
"role": "benchmark figure",
|
| 192 |
+
"bytes": 246078,
|
| 193 |
+
"sha256": "f6ac419ac13e29a59ebf93fb1cce7c3cd783ba26f72b9387b84aa3110ec2d77e",
|
| 194 |
+
"source": "docs/assets/final_broad_benchmark/07_availability_missingness.png",
|
| 195 |
+
"source_sha256": "f6ac419ac13e29a59ebf93fb1cce7c3cd783ba26f72b9387b84aa3110ec2d77e"
|
| 196 |
+
},
|
| 197 |
+
{
|
| 198 |
+
"path": "benchmark/figures/08_depth_quality_latency_scaling.json",
|
| 199 |
+
"role": "depth chart machine-readable analysis",
|
| 200 |
+
"bytes": 2819,
|
| 201 |
+
"sha256": "9f05bccc9d8e0b6a62ab2f2d89bdc6e9007c4abaaac464dd5e8b1a492ef204de",
|
| 202 |
+
"source": "docs/assets/final_broad_benchmark/08_depth_quality_latency_scaling.json",
|
| 203 |
+
"source_sha256": "9f05bccc9d8e0b6a62ab2f2d89bdc6e9007c4abaaac464dd5e8b1a492ef204de",
|
| 204 |
+
"normalization": "none"
|
| 205 |
+
},
|
| 206 |
+
{
|
| 207 |
+
"path": "benchmark/figures/08_depth_quality_latency_scaling.png",
|
| 208 |
+
"role": "benchmark figure",
|
| 209 |
+
"bytes": 256039,
|
| 210 |
+
"sha256": "164c6269fe1aed00733366d0f88b31494e6c1f4d9d7c975f6d36cc1254336e24",
|
| 211 |
+
"source": "docs/assets/final_broad_benchmark/08_depth_quality_latency_scaling.png",
|
| 212 |
+
"source_sha256": "164c6269fe1aed00733366d0f88b31494e6c1f4d9d7c975f6d36cc1254336e24"
|
| 213 |
+
},
|
| 214 |
+
{
|
| 215 |
+
"path": "benchmark/final4l_aggregate.json",
|
| 216 |
+
"role": "three-seed aggregate",
|
| 217 |
+
"bytes": 10882,
|
| 218 |
+
"sha256": "0bae347bb54f8089a5fa5450f8366b5c56eb3e598beaf9a87f4385363a402bcb",
|
| 219 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/aggregate.json",
|
| 220 |
+
"source_sha256": "02d66036227403c9713e7c1f364f3444dfe0f17ad7b68785a8bee4adc482e2eb",
|
| 221 |
+
"normalization": "none"
|
| 222 |
+
},
|
| 223 |
+
{
|
| 224 |
+
"path": "benchmark/independent_audit.json",
|
| 225 |
+
"role": "sealed benchmark evidence",
|
| 226 |
+
"bytes": 2838,
|
| 227 |
+
"sha256": "613517f2bea2313592a3ce8955afd9d5f365784f3a875a8f7057b6697fee8da9",
|
| 228 |
+
"source": "docs/benchmarks/final_broad_20260730/independent_audit.json",
|
| 229 |
+
"source_sha256": "613517f2bea2313592a3ce8955afd9d5f365784f3a875a8f7057b6697fee8da9",
|
| 230 |
+
"normalization": "none"
|
| 231 |
+
},
|
| 232 |
+
{
|
| 233 |
+
"path": "benchmark/quality_matrix.csv",
|
| 234 |
+
"role": "sealed benchmark evidence",
|
| 235 |
+
"bytes": 3144,
|
| 236 |
+
"sha256": "c6e316f7081e2a6dcda1ec4e95b57cdcc357ae49ed8b3f5a876c5b51e0df722f",
|
| 237 |
+
"source": "docs/benchmarks/final_broad_20260730/quality_matrix.csv",
|
| 238 |
+
"source_sha256": "c6e316f7081e2a6dcda1ec4e95b57cdcc357ae49ed8b3f5a876c5b51e0df722f"
|
| 239 |
+
},
|
| 240 |
+
{
|
| 241 |
+
"path": "benchmark/seed_reports/seed41.json",
|
| 242 |
+
"role": "seed-41 frozen report",
|
| 243 |
+
"bytes": 115793,
|
| 244 |
+
"sha256": "c1bd379680abcf50b47b7d23e4aafbc8b5e7f40a0b0e9700bde21a8ce49af803",
|
| 245 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed41/report.json",
|
| 246 |
+
"source_sha256": "ec5314140fff0bdd46e87ee00ad67bee7e61d82c7cfa34add1510dd7d5bdb3c0",
|
| 247 |
+
"normalization": "project-root paths made repository-relative"
|
| 248 |
+
},
|
| 249 |
+
{
|
| 250 |
+
"path": "benchmark/seed_reports/seed42.json",
|
| 251 |
+
"role": "seed-42 frozen report",
|
| 252 |
+
"bytes": 115780,
|
| 253 |
+
"sha256": "a30e38433d008db4aa7e5fbfd477ab80a0daf50e970f8e7b04397efc1a47843a",
|
| 254 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed42/report.json",
|
| 255 |
+
"source_sha256": "e5c45a7dc41bebf4550da7c5763d0759c6d0922da9efda4048564c11c6d2f3af",
|
| 256 |
+
"normalization": "project-root paths made repository-relative"
|
| 257 |
+
},
|
| 258 |
+
{
|
| 259 |
+
"path": "benchmark/seed_reports/seed43.json",
|
| 260 |
+
"role": "seed-43 frozen report",
|
| 261 |
+
"bytes": 115985,
|
| 262 |
+
"sha256": "96466290d09d27b8bc044a9f9a95765ce12b4d7b02dfb0123e436e7535623353",
|
| 263 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed43/report.json",
|
| 264 |
+
"source_sha256": "e05bc2146d720706780fc45a1fb05911d5c2926caa0bcb12ffead946028eef0d",
|
| 265 |
+
"normalization": "project-root paths made repository-relative"
|
| 266 |
+
},
|
| 267 |
+
{
|
| 268 |
+
"path": "benchmark/summary.json",
|
| 269 |
+
"role": "sealed benchmark evidence",
|
| 270 |
+
"bytes": 5765,
|
| 271 |
+
"sha256": "0f7002222e3757052ad9f19f95b5e7fc86f119bd7e7e16c60a018d3560f76e09",
|
| 272 |
+
"source": "docs/benchmarks/final_broad_20260730/summary.json",
|
| 273 |
+
"source_sha256": "0f7002222e3757052ad9f19f95b5e7fc86f119bd7e7e16c60a018d3560f76e09",
|
| 274 |
+
"normalization": "none"
|
| 275 |
+
},
|
| 276 |
+
{
|
| 277 |
+
"path": "benchmark/unoq_lineage/01_all_models_latency_memory.png",
|
| 278 |
+
"role": "physical UNO Q lineage figure",
|
| 279 |
+
"bytes": 285503,
|
| 280 |
+
"sha256": "33a50de3de92c958978a979ceba165f491a11b9c3509aa903f794c4baf71ee97",
|
| 281 |
+
"source": "docs/assets/unoq_lineage_matrix/01_all_models_latency_memory.png",
|
| 282 |
+
"source_sha256": "33a50de3de92c958978a979ceba165f491a11b9c3509aa903f794c4baf71ee97"
|
| 283 |
+
},
|
| 284 |
+
{
|
| 285 |
+
"path": "benchmark/unoq_lineage/02_layer_reduction_efficiency.png",
|
| 286 |
+
"role": "physical UNO Q lineage figure",
|
| 287 |
+
"bytes": 211761,
|
| 288 |
+
"sha256": "38c54566de25cad0b8c34389736f7f42f73e2d96757901ecd570804238598067",
|
| 289 |
+
"source": "docs/assets/unoq_lineage_matrix/02_layer_reduction_efficiency.png",
|
| 290 |
+
"source_sha256": "38c54566de25cad0b8c34389736f7f42f73e2d96757901ecd570804238598067"
|
| 291 |
+
},
|
| 292 |
+
{
|
| 293 |
+
"path": "benchmark/unoq_lineage/03_vocab_embedding_reduction.png",
|
| 294 |
+
"role": "physical UNO Q lineage figure",
|
| 295 |
+
"bytes": 201019,
|
| 296 |
+
"sha256": "9265bfc1e41b0777022aee7bb862979908d5e722f7e01baf40fe87a1fd8f00f4",
|
| 297 |
+
"source": "docs/assets/unoq_lineage_matrix/03_vocab_embedding_reduction.png",
|
| 298 |
+
"source_sha256": "9265bfc1e41b0777022aee7bb862979908d5e722f7e01baf40fe87a1fd8f00f4"
|
| 299 |
+
},
|
| 300 |
+
{
|
| 301 |
+
"path": "benchmark/unoq_lineage/REPORT.md",
|
| 302 |
+
"role": "physical UNO Q lineage narrative",
|
| 303 |
+
"bytes": 6664,
|
| 304 |
+
"sha256": "dc09c9c12c0fd846619c024bb4a71007241d031699288f9311d4ea34fa18249a",
|
| 305 |
+
"source": "generated",
|
| 306 |
+
"source_sha256": null
|
| 307 |
+
},
|
| 308 |
+
{
|
| 309 |
+
"path": "benchmark/unoq_lineage/all_models.csv",
|
| 310 |
+
"role": "physical UNO Q all-model table",
|
| 311 |
+
"bytes": 4178,
|
| 312 |
+
"sha256": "d01079b046942aab1dfe6fccf973b43a4b054781ee5b47ee0033f4e6b5eb024c",
|
| 313 |
+
"source": "results/unoq_lineage_matrix/20260731-v1/analysis/all_models.csv",
|
| 314 |
+
"source_sha256": "d01079b046942aab1dfe6fccf973b43a4b054781ee5b47ee0033f4e6b5eb024c"
|
| 315 |
+
},
|
| 316 |
+
{
|
| 317 |
+
"path": "benchmark/unoq_lineage/analysis.json",
|
| 318 |
+
"role": "physical UNO Q lineage analysis",
|
| 319 |
+
"bytes": 13963,
|
| 320 |
+
"sha256": "c899a19e4433e3a40547a11cfcfc3b0a7b6f00c436aa8c1d742a5dc0399bf649",
|
| 321 |
+
"source": "results/unoq_lineage_matrix/20260731-v1/analysis/analysis.json",
|
| 322 |
+
"source_sha256": "c899a19e4433e3a40547a11cfcfc3b0a7b6f00c436aa8c1d742a5dc0399bf649",
|
| 323 |
+
"normalization": "none"
|
| 324 |
+
},
|
| 325 |
+
{
|
| 326 |
+
"path": "benchmark/unoq_lineage/chart_inputs.json",
|
| 327 |
+
"role": "physical UNO Q chart inputs",
|
| 328 |
+
"bytes": 11443,
|
| 329 |
+
"sha256": "3f204fcc159824a43a3b41e7b1c08ea661e6337f4277646095cca75000972050",
|
| 330 |
+
"source": "docs/assets/unoq_lineage_matrix/chart_inputs.json",
|
| 331 |
+
"source_sha256": "3f204fcc159824a43a3b41e7b1c08ea661e6337f4277646095cca75000972050",
|
| 332 |
+
"normalization": "none"
|
| 333 |
+
},
|
| 334 |
+
{
|
| 335 |
+
"path": "benchmark/unoq_lineage/manifest.json",
|
| 336 |
+
"role": "physical UNO Q model/runtime manifest",
|
| 337 |
+
"bytes": 6853,
|
| 338 |
+
"sha256": "4c74bf715c38d4577e939bf987f5e9f3ce93bd10a64f1999657cb3e93302378c",
|
| 339 |
+
"source": "results/unoq_lineage_matrix/20260731-v1/manifest.json",
|
| 340 |
+
"source_sha256": "4c74bf715c38d4577e939bf987f5e9f3ce93bd10a64f1999657cb3e93302378c",
|
| 341 |
+
"normalization": "none"
|
| 342 |
+
},
|
| 343 |
+
{
|
| 344 |
+
"path": "benchmark/x86_cuda_systems.csv",
|
| 345 |
+
"role": "sealed benchmark evidence",
|
| 346 |
+
"bytes": 2939,
|
| 347 |
+
"sha256": "11321452290bc53da5ba371d2f2524590d4abd91d981ad0f89c8708752e2a47e",
|
| 348 |
+
"source": "docs/benchmarks/final_broad_20260730/x86_cuda_systems.csv",
|
| 349 |
+
"source_sha256": "11321452290bc53da5ba371d2f2524590d4abd91d981ad0f89c8708752e2a47e"
|
| 350 |
+
},
|
| 351 |
+
{
|
| 352 |
+
"path": "chat_template.jinja",
|
| 353 |
+
"role": "seed-41 Transformers checkpoint",
|
| 354 |
+
"bytes": 6669,
|
| 355 |
+
"sha256": "04b007131663760bf3e581e5a953be77044014e87efe1d2a6ca4b72ec0eac978",
|
| 356 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed41/checkpoint/chat_template.jinja",
|
| 357 |
+
"source_sha256": "04b007131663760bf3e581e5a953be77044014e87efe1d2a6ca4b72ec0eac978"
|
| 358 |
+
},
|
| 359 |
+
{
|
| 360 |
+
"path": "classify.py",
|
| 361 |
+
"role": "self-contained Transformers CLI",
|
| 362 |
+
"bytes": 8070,
|
| 363 |
+
"sha256": "1c09956cc5d3553eed0b5f64c20c81064df4373ccfaf4fec26929b8f4819773a",
|
| 364 |
+
"source": "scripts/hf_standalone4l_classify.py",
|
| 365 |
+
"source_sha256": "1c09956cc5d3553eed0b5f64c20c81064df4373ccfaf4fec26929b8f4819773a"
|
| 366 |
+
},
|
| 367 |
+
{
|
| 368 |
+
"path": "config.json",
|
| 369 |
+
"role": "seed-41 Transformers checkpoint",
|
| 370 |
+
"bytes": 3380,
|
| 371 |
+
"sha256": "9f04dfbd7e1696cd3df2ccbe194f5f155efe5baefd1a0b615851f51bf46691a0",
|
| 372 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed41/checkpoint/config.json",
|
| 373 |
+
"source_sha256": "9f04dfbd7e1696cd3df2ccbe194f5f155efe5baefd1a0b615851f51bf46691a0"
|
| 374 |
+
},
|
| 375 |
+
{
|
| 376 |
+
"path": "edge/README.md",
|
| 377 |
+
"role": "UNO Q deployment guide",
|
| 378 |
+
"bytes": 9079,
|
| 379 |
+
"sha256": "fb03e2e2f2ae1452f634f70330ead9d9d17ce0a643473081c880bcfa6fac4668",
|
| 380 |
+
"source": "generated",
|
| 381 |
+
"source_sha256": null
|
| 382 |
+
},
|
| 383 |
+
{
|
| 384 |
+
"path": "edge/benchmark/A53_SELECTION_DECISION.md",
|
| 385 |
+
"role": "generic-vs-A53 runtime selection decision",
|
| 386 |
+
"bytes": 3765,
|
| 387 |
+
"sha256": "b3bd314d6207b4e4d6fe9e3ec348a8a3e2caa3510296b0e0651382ca5714e034",
|
| 388 |
+
"source": "results/standalone4l_edge/20260730-phase4b-a53-paired-unoq-v1/DECISION.md",
|
| 389 |
+
"source_sha256": "b3bd314d6207b4e4d6fe9e3ec348a8a3e2caa3510296b0e0651382ca5714e034"
|
| 390 |
+
},
|
| 391 |
+
{
|
| 392 |
+
"path": "edge/benchmark/a53_analysis.json",
|
| 393 |
+
"role": "preregistered generic-vs-A53 gate analysis",
|
| 394 |
+
"bytes": 5571,
|
| 395 |
+
"sha256": "d5023e573e344885b643e45fa9b6de52180c2ee09f2e857c420c41b74fda37d6",
|
| 396 |
+
"source": "results/standalone4l_edge/20260730-phase4b-a53-paired-unoq-v1/analysis/analysis.json",
|
| 397 |
+
"source_sha256": "f619bd86f14fc1706fa0c0d107e521b5517264a53d769b67e35999fa6422b6fb",
|
| 398 |
+
"normalization": "project-root paths made repository-relative"
|
| 399 |
+
},
|
| 400 |
+
{
|
| 401 |
+
"path": "edge/benchmark/a53_audit.json",
|
| 402 |
+
"role": "independent generic-vs-A53 audit",
|
| 403 |
+
"bytes": 3223,
|
| 404 |
+
"sha256": "82bef36a79a6348428ad04a2fbc816e2ede3d8c97bfb56dcb649cf3310c83a4f",
|
| 405 |
+
"source": "results/standalone4l_edge/20260730-phase4b-a53-paired-unoq-v1/analysis/AUDIT.json",
|
| 406 |
+
"source_sha256": "82bef36a79a6348428ad04a2fbc816e2ede3d8c97bfb56dcb649cf3310c83a4f",
|
| 407 |
+
"normalization": "none"
|
| 408 |
+
},
|
| 409 |
+
{
|
| 410 |
+
"path": "edge/benchmark/execution_context.json",
|
| 411 |
+
"role": "runtime selection execution context",
|
| 412 |
+
"bytes": 1418,
|
| 413 |
+
"sha256": "ab647cb7e10c63c97a7e9ad45edd4f05996463ef84b09d1835ed445f46f80283",
|
| 414 |
+
"source": "results/standalone4l_edge/20260730-phase4b-a53-paired-unoq-v1/EXECUTION_CONTEXT.json",
|
| 415 |
+
"source_sha256": "ab647cb7e10c63c97a7e9ad45edd4f05996463ef84b09d1835ed445f46f80283",
|
| 416 |
+
"normalization": "none"
|
| 417 |
+
},
|
| 418 |
+
{
|
| 419 |
+
"path": "edge/benchmark/generic_vs_a53_unoq.png",
|
| 420 |
+
"role": "raw-driven generic-vs-A53 comparison graph",
|
| 421 |
+
"bytes": 253732,
|
| 422 |
+
"sha256": "d06a0858634fab01cc43f29c8774e161f4943f8282fb512719942737e4e8c22c",
|
| 423 |
+
"source": "results/standalone4l_edge/20260730-phase4b-a53-paired-unoq-v1/analysis/generic_vs_a53_unoq.png",
|
| 424 |
+
"source_sha256": "d06a0858634fab01cc43f29c8774e161f4943f8282fb512719942737e4e8c22c"
|
| 425 |
+
},
|
| 426 |
+
{
|
| 427 |
+
"path": "edge/classify_short.py",
|
| 428 |
+
"role": "short-text Q8 inference helper",
|
| 429 |
+
"bytes": 5149,
|
| 430 |
+
"sha256": "61f11a4b64586763424a918c0cbccecfeecf27e632d0a2c50e209d8fe893d38b",
|
| 431 |
+
"source": "scripts/qwen35_standalone4l_q8_short_inference_v1.py",
|
| 432 |
+
"source_sha256": "61f11a4b64586763424a918c0cbccecfeecf27e632d0a2c50e209d8fe893d38b"
|
| 433 |
+
},
|
| 434 |
+
{
|
| 435 |
+
"path": "edge/qwen3.5-0.8b-standalone4l-joint-q8_0.gguf",
|
| 436 |
+
"role": "sealed Q8 GGUF",
|
| 437 |
+
"bytes": 228999360,
|
| 438 |
+
"sha256": "acc07b4fdd8260147b3c6cb7c7104513176bcb72c88f5277feaa25fc9080f0b3",
|
| 439 |
+
"source": "results/standalone4l_edge/20260728-phase2c-quant-ladder-v1/candidates/qwen3.5-0.8b-standalone4l-joint-q8_0.gguf",
|
| 440 |
+
"source_sha256": "acc07b4fdd8260147b3c6cb7c7104513176bcb72c88f5277feaa25fc9080f0b3"
|
| 441 |
+
},
|
| 442 |
+
{
|
| 443 |
+
"path": "edge/qwen3.5-0.8b-standalone4l-q8-arm64-runtime-v3.tar.gz",
|
| 444 |
+
"role": "sealed ARM64 runtime package",
|
| 445 |
+
"bytes": 222663144,
|
| 446 |
+
"sha256": "daf5d051c816a3aef94713e83fb532f4bd23340d3e8714818696695f9ccd26cc",
|
| 447 |
+
"source": "results/standalone4l_edge/20260729-phase3a-aarch64-q8-runtime-v3/qwen3.5-0.8b-standalone4l-q8-arm64-runtime-v3.tar.gz",
|
| 448 |
+
"source_sha256": "daf5d051c816a3aef94713e83fb532f4bd23340d3e8714818696695f9ccd26cc"
|
| 449 |
+
},
|
| 450 |
+
{
|
| 451 |
+
"path": "edge/runtime_manifest.json",
|
| 452 |
+
"role": "ARM64 package manifest",
|
| 453 |
+
"bytes": 8363,
|
| 454 |
+
"sha256": "2f91f9359efbfef94721beb8fe29b075fa04d923dd9ba829e6e75d057f7619a2",
|
| 455 |
+
"source": "results/standalone4l_edge/20260729-phase3a-aarch64-q8-runtime-v3/manifest.json",
|
| 456 |
+
"source_sha256": "2f91f9359efbfef94721beb8fe29b075fa04d923dd9ba829e6e75d057f7619a2"
|
| 457 |
+
},
|
| 458 |
+
{
|
| 459 |
+
"path": "edge/technique_bias.json",
|
| 460 |
+
"role": "required external classifier bias",
|
| 461 |
+
"bytes": 1307,
|
| 462 |
+
"sha256": "e499e4ae081129d04bcd8e9790f4aa26014436b8f0d916805798f2d638bcf1bf",
|
| 463 |
+
"source": "results/standalone4l_edge/20260728-phase2a-x86-f16-no-ui-v3/packed/technique_bias.json",
|
| 464 |
+
"source_sha256": "e499e4ae081129d04bcd8e9790f4aa26014436b8f0d916805798f2d638bcf1bf"
|
| 465 |
+
},
|
| 466 |
+
{
|
| 467 |
+
"path": "edge/tokenizer/tokenizer.json",
|
| 468 |
+
"role": "Q8 window tokenizer",
|
| 469 |
+
"bytes": 9224067,
|
| 470 |
+
"sha256": "2106c29a0c7de1308e536b9f8a01833b9ff9bd0b34a526019f478bc53b6da81d",
|
| 471 |
+
"source": "results/layer_shrink_v6/20260728-v6-r23-standalone4l-kd-seed41-v1/student-4l-final/tokenizer.json",
|
| 472 |
+
"source_sha256": "2106c29a0c7de1308e536b9f8a01833b9ff9bd0b34a526019f478bc53b6da81d"
|
| 473 |
+
},
|
| 474 |
+
{
|
| 475 |
+
"path": "edge/tokenizer/tokenizer_config.json",
|
| 476 |
+
"role": "Q8 window tokenizer",
|
| 477 |
+
"bytes": 1314,
|
| 478 |
+
"sha256": "2215318fe27878ec67e772931bb5f40a98658348d33f2672a575f766a2190d35",
|
| 479 |
+
"source": "results/layer_shrink_v6/20260728-v6-r23-standalone4l-kd-seed41-v1/student-4l-final/tokenizer_config.json",
|
| 480 |
+
"source_sha256": "2215318fe27878ec67e772931bb5f40a98658348d33f2672a575f766a2190d35"
|
| 481 |
+
},
|
| 482 |
+
{
|
| 483 |
+
"path": "edge/unoq_acceptance_audit.json",
|
| 484 |
+
"role": "UNO Q acceptance audit",
|
| 485 |
+
"bytes": 3461,
|
| 486 |
+
"sha256": "bc5c910c838f715cbea18d736832e2eafb4a43fd99cf2d69ab490b9083b3608c",
|
| 487 |
+
"source": "results/standalone4l_edge/20260729-phase3b-unoq-device-acceptance-v1/audit.json",
|
| 488 |
+
"source_sha256": "bc5c910c838f715cbea18d736832e2eafb4a43fd99cf2d69ab490b9083b3608c"
|
| 489 |
+
},
|
| 490 |
+
{
|
| 491 |
+
"path": "edge/unoq_acceptance_report.json",
|
| 492 |
+
"role": "UNO Q acceptance report",
|
| 493 |
+
"bytes": 7467,
|
| 494 |
+
"sha256": "ee0c9c2996ea65f5b18d602b66bdc9ac2e0b160aeb52faa54d255c24446217b1",
|
| 495 |
+
"source": "results/standalone4l_edge/20260729-phase3b-unoq-device-acceptance-v1/acceptance_report.json",
|
| 496 |
+
"source_sha256": "ee0c9c2996ea65f5b18d602b66bdc9ac2e0b160aeb52faa54d255c24446217b1"
|
| 497 |
+
},
|
| 498 |
+
{
|
| 499 |
+
"path": "labels.json",
|
| 500 |
+
"role": "fixed label order",
|
| 501 |
+
"bytes": 891,
|
| 502 |
+
"sha256": "946fed4fe4c3d320fd113ec8e5776b10f2707b936f857341bb5443d7a9038248",
|
| 503 |
+
"source": "generated",
|
| 504 |
+
"source_sha256": null
|
| 505 |
+
},
|
| 506 |
+
{
|
| 507 |
+
"path": "model.safetensors",
|
| 508 |
+
"role": "seed-41 Transformers checkpoint",
|
| 509 |
+
"bytes": 421812688,
|
| 510 |
+
"sha256": "0352da6e4717a2e6f3a9d643d034be2cb1c39632d1bce0ced339ddb62acedb37",
|
| 511 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed41/checkpoint/model.safetensors",
|
| 512 |
+
"source_sha256": "0352da6e4717a2e6f3a9d643d034be2cb1c39632d1bce0ced339ddb62acedb37"
|
| 513 |
+
},
|
| 514 |
+
{
|
| 515 |
+
"path": "requirements.txt",
|
| 516 |
+
"role": "inference dependency lock",
|
| 517 |
+
"bytes": 54,
|
| 518 |
+
"sha256": "2994886af208a0120c051745f9c14036e6f2caae8c4af863e5965d5a5bd68fdf",
|
| 519 |
+
"source": "generated",
|
| 520 |
+
"source_sha256": null
|
| 521 |
+
},
|
| 522 |
+
{
|
| 523 |
+
"path": "seeds/seed42/chat_template.jinja",
|
| 524 |
+
"role": "seed-42 Transformers checkpoint",
|
| 525 |
+
"bytes": 6669,
|
| 526 |
+
"sha256": "04b007131663760bf3e581e5a953be77044014e87efe1d2a6ca4b72ec0eac978",
|
| 527 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed42/checkpoint/chat_template.jinja",
|
| 528 |
+
"source_sha256": "04b007131663760bf3e581e5a953be77044014e87efe1d2a6ca4b72ec0eac978"
|
| 529 |
+
},
|
| 530 |
+
{
|
| 531 |
+
"path": "seeds/seed42/config.json",
|
| 532 |
+
"role": "seed-42 Transformers checkpoint",
|
| 533 |
+
"bytes": 3380,
|
| 534 |
+
"sha256": "9f04dfbd7e1696cd3df2ccbe194f5f155efe5baefd1a0b615851f51bf46691a0",
|
| 535 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed42/checkpoint/config.json",
|
| 536 |
+
"source_sha256": "9f04dfbd7e1696cd3df2ccbe194f5f155efe5baefd1a0b615851f51bf46691a0"
|
| 537 |
+
},
|
| 538 |
+
{
|
| 539 |
+
"path": "seeds/seed42/model.safetensors",
|
| 540 |
+
"role": "seed-42 Transformers checkpoint",
|
| 541 |
+
"bytes": 421812688,
|
| 542 |
+
"sha256": "2db5f4be391d9dee64458cb5865530f39747019e7a219443b5f7850befbce697",
|
| 543 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed42/checkpoint/model.safetensors",
|
| 544 |
+
"source_sha256": "2db5f4be391d9dee64458cb5865530f39747019e7a219443b5f7850befbce697"
|
| 545 |
+
},
|
| 546 |
+
{
|
| 547 |
+
"path": "seeds/seed42/tokenizer.json",
|
| 548 |
+
"role": "seed-42 Transformers checkpoint",
|
| 549 |
+
"bytes": 9223900,
|
| 550 |
+
"sha256": "dd2b27bbd4f5f650c7200300013ef03ee01efe620b128343163521bf04806fc8",
|
| 551 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed42/checkpoint/tokenizer.json",
|
| 552 |
+
"source_sha256": "dd2b27bbd4f5f650c7200300013ef03ee01efe620b128343163521bf04806fc8"
|
| 553 |
+
},
|
| 554 |
+
{
|
| 555 |
+
"path": "seeds/seed42/tokenizer_config.json",
|
| 556 |
+
"role": "seed-42 Transformers checkpoint",
|
| 557 |
+
"bytes": 1314,
|
| 558 |
+
"sha256": "2215318fe27878ec67e772931bb5f40a98658348d33f2672a575f766a2190d35",
|
| 559 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed42/checkpoint/tokenizer_config.json",
|
| 560 |
+
"source_sha256": "2215318fe27878ec67e772931bb5f40a98658348d33f2672a575f766a2190d35"
|
| 561 |
+
},
|
| 562 |
+
{
|
| 563 |
+
"path": "seeds/seed43/chat_template.jinja",
|
| 564 |
+
"role": "seed-43 Transformers checkpoint",
|
| 565 |
+
"bytes": 6669,
|
| 566 |
+
"sha256": "04b007131663760bf3e581e5a953be77044014e87efe1d2a6ca4b72ec0eac978",
|
| 567 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed43/checkpoint/chat_template.jinja",
|
| 568 |
+
"source_sha256": "04b007131663760bf3e581e5a953be77044014e87efe1d2a6ca4b72ec0eac978"
|
| 569 |
+
},
|
| 570 |
+
{
|
| 571 |
+
"path": "seeds/seed43/config.json",
|
| 572 |
+
"role": "seed-43 Transformers checkpoint",
|
| 573 |
+
"bytes": 3380,
|
| 574 |
+
"sha256": "9f04dfbd7e1696cd3df2ccbe194f5f155efe5baefd1a0b615851f51bf46691a0",
|
| 575 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed43/checkpoint/config.json",
|
| 576 |
+
"source_sha256": "9f04dfbd7e1696cd3df2ccbe194f5f155efe5baefd1a0b615851f51bf46691a0"
|
| 577 |
+
},
|
| 578 |
+
{
|
| 579 |
+
"path": "seeds/seed43/model.safetensors",
|
| 580 |
+
"role": "seed-43 Transformers checkpoint",
|
| 581 |
+
"bytes": 421812688,
|
| 582 |
+
"sha256": "81a56d497df758e8a292eea528438cfc4d5a847cd37d3a7e1107fefa7c500640",
|
| 583 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed43/checkpoint/model.safetensors",
|
| 584 |
+
"source_sha256": "81a56d497df758e8a292eea528438cfc4d5a847cd37d3a7e1107fefa7c500640"
|
| 585 |
+
},
|
| 586 |
+
{
|
| 587 |
+
"path": "seeds/seed43/tokenizer.json",
|
| 588 |
+
"role": "seed-43 Transformers checkpoint",
|
| 589 |
+
"bytes": 9223900,
|
| 590 |
+
"sha256": "dd2b27bbd4f5f650c7200300013ef03ee01efe620b128343163521bf04806fc8",
|
| 591 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed43/checkpoint/tokenizer.json",
|
| 592 |
+
"source_sha256": "dd2b27bbd4f5f650c7200300013ef03ee01efe620b128343163521bf04806fc8"
|
| 593 |
+
},
|
| 594 |
+
{
|
| 595 |
+
"path": "seeds/seed43/tokenizer_config.json",
|
| 596 |
+
"role": "seed-43 Transformers checkpoint",
|
| 597 |
+
"bytes": 1314,
|
| 598 |
+
"sha256": "2215318fe27878ec67e772931bb5f40a98658348d33f2672a575f766a2190d35",
|
| 599 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed43/checkpoint/tokenizer_config.json",
|
| 600 |
+
"source_sha256": "2215318fe27878ec67e772931bb5f40a98658348d33f2672a575f766a2190d35"
|
| 601 |
+
},
|
| 602 |
+
{
|
| 603 |
+
"path": "thresholds.json",
|
| 604 |
+
"role": "seed thresholds and aggregate metrics",
|
| 605 |
+
"bytes": 2631,
|
| 606 |
+
"sha256": "737c4e145a5b51660c4c45a8acea4c32219cbbbc0706abe235e36b2ad430f17d",
|
| 607 |
+
"source": "generated",
|
| 608 |
+
"source_sha256": null
|
| 609 |
+
},
|
| 610 |
+
{
|
| 611 |
+
"path": "tokenizer.json",
|
| 612 |
+
"role": "seed-41 Transformers checkpoint",
|
| 613 |
+
"bytes": 9223900,
|
| 614 |
+
"sha256": "dd2b27bbd4f5f650c7200300013ef03ee01efe620b128343163521bf04806fc8",
|
| 615 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed41/checkpoint/tokenizer.json",
|
| 616 |
+
"source_sha256": "dd2b27bbd4f5f650c7200300013ef03ee01efe620b128343163521bf04806fc8"
|
| 617 |
+
},
|
| 618 |
+
{
|
| 619 |
+
"path": "tokenizer_config.json",
|
| 620 |
+
"role": "seed-41 Transformers checkpoint",
|
| 621 |
+
"bytes": 1314,
|
| 622 |
+
"sha256": "2215318fe27878ec67e772931bb5f40a98658348d33f2672a575f766a2190d35",
|
| 623 |
+
"source": "results/final_broad_benchmark/20260729-v1/trainable/qwen35-standalone4l-commonhead/seed41/checkpoint/tokenizer_config.json",
|
| 624 |
+
"source_sha256": "2215318fe27878ec67e772931bb5f40a98658348d33f2672a575f766a2190d35"
|
| 625 |
+
}
|
| 626 |
+
],
|
| 627 |
+
"claim_boundary": {
|
| 628 |
+
"opened_public_human_gold": true,
|
| 629 |
+
"test_articles": 55,
|
| 630 |
+
"three_seed_sample_sd_not_ci": true,
|
| 631 |
+
"q8_different_head_lineage": true,
|
| 632 |
+
"task3_private_or_final_used": false,
|
| 633 |
+
"mac_used": false,
|
| 634 |
+
"recommended_edge_runtime": "generic-armv8a-v3",
|
| 635 |
+
"a53_candidate_promoted": false
|
| 636 |
+
}
|
| 637 |
+
}
|
models/semeval-propaganda/requirements.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
torch>=2.4
|
| 2 |
+
transformers==5.13.0
|
| 3 |
+
huggingface_hub>=0.34
|
models/semeval-propaganda/seeds/seed42/chat_template.jinja
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{%- set image_count = namespace(value=0) %}
|
| 2 |
+
{%- set video_count = namespace(value=0) %}
|
| 3 |
+
{%- macro render_content(content, do_vision_count) %}
|
| 4 |
+
{%- if content is string %}
|
| 5 |
+
{{- content }}
|
| 6 |
+
{%- else %}
|
| 7 |
+
{%- for item in content %}
|
| 8 |
+
{%- if 'image' in item or 'image_url' in item or item.type == 'image' %}
|
| 9 |
+
{%- if do_vision_count %}
|
| 10 |
+
{%- set image_count.value = image_count.value + 1 %}
|
| 11 |
+
{%- endif %}
|
| 12 |
+
{%- if add_vision_id %}Picture {{ image_count.value }}: {% endif -%}
|
| 13 |
+
<|vision_start|><|image_pad|><|vision_end|>
|
| 14 |
+
{%- elif 'video' in item or item.type == 'video' %}
|
| 15 |
+
{%- if do_vision_count %}
|
| 16 |
+
{%- set video_count.value = video_count.value + 1 %}
|
| 17 |
+
{%- endif %}
|
| 18 |
+
{%- if add_vision_id %}Video {{ video_count.value }}: {% endif -%}
|
| 19 |
+
<|vision_start|><|video_pad|><|vision_end|>
|
| 20 |
+
{%- elif 'text' in item %}
|
| 21 |
+
{{- item.text }}
|
| 22 |
+
{%- endif %}
|
| 23 |
+
{%- endfor %}
|
| 24 |
+
{%- endif %}
|
| 25 |
+
{%- endmacro %}
|
| 26 |
+
{%- if tools is defined and tools is iterable and tools | length > 0 %}
|
| 27 |
+
{{- '<|im_start|>system\n' }}
|
| 28 |
+
{{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
|
| 29 |
+
{%- for tool in tools %}
|
| 30 |
+
{{- "\n" }}
|
| 31 |
+
{{- tool | tojson }}
|
| 32 |
+
{%- endfor %}
|
| 33 |
+
{{- "\n</tools>" }}
|
| 34 |
+
{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
|
| 35 |
+
{%- if messages[0].role == 'system' and messages[0].content | trim %}
|
| 36 |
+
{%- set content = render_content(messages[0].content, false) %}
|
| 37 |
+
{{- '\n\n' + content.lstrip('\n') }}
|
| 38 |
+
{%- endif %}
|
| 39 |
+
{{- '<|im_end|>\n' }}
|
| 40 |
+
{%- else %}
|
| 41 |
+
{%- if messages[0].role == 'system' %}
|
| 42 |
+
{{- '<|im_start|>system\n' + render_content(messages[0].content, false) + '<|im_end|>\n' }}
|
| 43 |
+
{%- endif %}
|
| 44 |
+
{%- endif %}
|
| 45 |
+
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
| 46 |
+
{%- for message in messages[::-1] %}
|
| 47 |
+
{%- set index = (messages|length - 1) - loop.index0 %}
|
| 48 |
+
{%- if ns.multi_step_tool and message.role == "user" %}
|
| 49 |
+
{%- set content = render_content(message.content, False) %}
|
| 50 |
+
{%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
|
| 51 |
+
{%- set ns.multi_step_tool = false %}
|
| 52 |
+
{%- set ns.last_query_index = index %}
|
| 53 |
+
{%- endif %}
|
| 54 |
+
{%- endif %}
|
| 55 |
+
{%- endfor %}
|
| 56 |
+
{%- for message in messages %}
|
| 57 |
+
{%- set content = render_content(message.content, true) %}
|
| 58 |
+
{%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
|
| 59 |
+
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
|
| 60 |
+
{%- elif message.role == "assistant" %}
|
| 61 |
+
{%- set reasoning_content = '' %}
|
| 62 |
+
{%- if message.reasoning_content is defined and message.reasoning_content is not none %}
|
| 63 |
+
{%- set reasoning_content = message.reasoning_content %}
|
| 64 |
+
{%- else %}
|
| 65 |
+
{%- if '</think>' in content %}
|
| 66 |
+
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
| 67 |
+
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
|
| 68 |
+
{%- endif %}
|
| 69 |
+
{%- endif %}
|
| 70 |
+
{%- if message.tool_calls is defined and message.tool_calls is not none and message.tool_calls is iterable and message.tool_calls | length > 0 %}
|
| 71 |
+
{%- set content = content.rstrip('\n') %}
|
| 72 |
+
{%- endif %}
|
| 73 |
+
{%- if loop.index0 > ns.last_query_index %}
|
| 74 |
+
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
|
| 75 |
+
{%- else %}
|
| 76 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 77 |
+
{%- endif %}
|
| 78 |
+
{%- if message.tool_calls is defined and message.tool_calls is not none and message.tool_calls is iterable and message.tool_calls | length > 0 %}
|
| 79 |
+
{%- for tool_call in message.tool_calls %}
|
| 80 |
+
{%- if tool_call.function is defined %}
|
| 81 |
+
{%- set tool_call = tool_call.function %}
|
| 82 |
+
{%- endif %}
|
| 83 |
+
{%- if loop.first %}
|
| 84 |
+
{%- if content|trim %}
|
| 85 |
+
{{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 86 |
+
{%- else %}
|
| 87 |
+
{{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 88 |
+
{%- endif %}
|
| 89 |
+
{%- else %}
|
| 90 |
+
{{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 91 |
+
{%- endif %}
|
| 92 |
+
{%- if tool_call.arguments is defined %}
|
| 93 |
+
{%- for args_name, args_value in tool_call.arguments|items %}
|
| 94 |
+
{{- '<parameter=' + args_name + '>\n' }}
|
| 95 |
+
{%- set args_value = args_value | tojson | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string %}
|
| 96 |
+
{{- args_value }}
|
| 97 |
+
{{- '\n</parameter>\n' }}
|
| 98 |
+
{%- endfor %}
|
| 99 |
+
{%- endif %}
|
| 100 |
+
{{- '</function>\n</tool_call>' }}
|
| 101 |
+
{%- endfor %}
|
| 102 |
+
{%- endif %}
|
| 103 |
+
{{- '<|im_end|>\n' }}
|
| 104 |
+
{%- elif message.role == "tool" %}
|
| 105 |
+
{%- if loop.previtem and loop.previtem.role != "tool" %}
|
| 106 |
+
{{- '<|im_start|>user' }}
|
| 107 |
+
{%- endif %}
|
| 108 |
+
{{- '\n<tool_response>\n' }}
|
| 109 |
+
{{- content }}
|
| 110 |
+
{{- '\n</tool_response>' }}
|
| 111 |
+
{%- if not loop.last and loop.nextitem.role != "tool" %}
|
| 112 |
+
{{- '<|im_end|>\n' }}
|
| 113 |
+
{%- elif loop.last %}
|
| 114 |
+
{{- '<|im_end|>\n' }}
|
| 115 |
+
{%- endif %}
|
| 116 |
+
{%- endif %}
|
| 117 |
+
{%- endfor %}
|
| 118 |
+
{%- if add_generation_prompt %}
|
| 119 |
+
{{- '<|im_start|>assistant\n<think>\n' }}
|
| 120 |
+
{%- endif %}
|
models/semeval-propaganda/seeds/seed42/config.json
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"Qwen3_5TextForSequenceClassification"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"attn_output_gate": true,
|
| 8 |
+
"bos_token_id": null,
|
| 9 |
+
"dtype": "bfloat16",
|
| 10 |
+
"eos_token_id": 127967,
|
| 11 |
+
"full_attention_interval": 4,
|
| 12 |
+
"head_dim": 256,
|
| 13 |
+
"hidden_act": "silu",
|
| 14 |
+
"hidden_size": 1024,
|
| 15 |
+
"id2label": {
|
| 16 |
+
"0": "Appeal_to_Authority",
|
| 17 |
+
"1": "Appeal_to_fear-prejudice",
|
| 18 |
+
"2": "Bandwagon,Reductio_ad_hitlerum",
|
| 19 |
+
"3": "Black-and-White_Fallacy",
|
| 20 |
+
"4": "Causal_Oversimplification",
|
| 21 |
+
"5": "Doubt",
|
| 22 |
+
"6": "Exaggeration,Minimisation",
|
| 23 |
+
"7": "Flag-Waving",
|
| 24 |
+
"8": "Loaded_Language",
|
| 25 |
+
"9": "Name_Calling,Labeling",
|
| 26 |
+
"10": "Repetition",
|
| 27 |
+
"11": "Slogans",
|
| 28 |
+
"12": "Thought-terminating_Cliches",
|
| 29 |
+
"13": "Whataboutism,Straw_Men,Red_Herring"
|
| 30 |
+
},
|
| 31 |
+
"initializer_range": 0.02,
|
| 32 |
+
"intermediate_size": 3584,
|
| 33 |
+
"label2id": {
|
| 34 |
+
"Appeal_to_Authority": 0,
|
| 35 |
+
"Appeal_to_fear-prejudice": 1,
|
| 36 |
+
"Bandwagon,Reductio_ad_hitlerum": 2,
|
| 37 |
+
"Black-and-White_Fallacy": 3,
|
| 38 |
+
"Causal_Oversimplification": 4,
|
| 39 |
+
"Doubt": 5,
|
| 40 |
+
"Exaggeration,Minimisation": 6,
|
| 41 |
+
"Flag-Waving": 7,
|
| 42 |
+
"Loaded_Language": 8,
|
| 43 |
+
"Name_Calling,Labeling": 9,
|
| 44 |
+
"Repetition": 10,
|
| 45 |
+
"Slogans": 11,
|
| 46 |
+
"Thought-terminating_Cliches": 12,
|
| 47 |
+
"Whataboutism,Straw_Men,Red_Herring": 13
|
| 48 |
+
},
|
| 49 |
+
"layer_types": [
|
| 50 |
+
"linear_attention",
|
| 51 |
+
"full_attention",
|
| 52 |
+
"linear_attention",
|
| 53 |
+
"full_attention"
|
| 54 |
+
],
|
| 55 |
+
"linear_conv_kernel_dim": 4,
|
| 56 |
+
"linear_key_head_dim": 128,
|
| 57 |
+
"linear_num_key_heads": 16,
|
| 58 |
+
"linear_num_value_heads": 16,
|
| 59 |
+
"linear_value_head_dim": 128,
|
| 60 |
+
"mamba_ssm_dtype": "float32",
|
| 61 |
+
"max_position_embeddings": 262144,
|
| 62 |
+
"mlp_only_layers": [],
|
| 63 |
+
"model_type": "qwen3_5_text",
|
| 64 |
+
"mtp_num_hidden_layers": 1,
|
| 65 |
+
"mtp_use_dedicated_embeddings": false,
|
| 66 |
+
"num_attention_heads": 8,
|
| 67 |
+
"num_hidden_layers": 4,
|
| 68 |
+
"num_key_value_heads": 2,
|
| 69 |
+
"pad_token_id": 127967,
|
| 70 |
+
"partial_rotary_factor": 0.25,
|
| 71 |
+
"phase_v6_map_selection_rank": 1,
|
| 72 |
+
"phase_v6_map_selection_split": "public_gold_train",
|
| 73 |
+
"phase_v6_materialized_role": "student6l-r3-final-teacher",
|
| 74 |
+
"phase_v6_materialized_trainable_state": "results/layer_shrink_v6/20260726-v6-r3-student6l-map013457-kd-seed41-pilot-v2/student-6l-kd-trainable.safetensors",
|
| 75 |
+
"phase_v6_selected_original_8l_layers": [
|
| 76 |
+
0,
|
| 77 |
+
3,
|
| 78 |
+
4,
|
| 79 |
+
7
|
| 80 |
+
],
|
| 81 |
+
"phase_v6_selected_source_layers": [
|
| 82 |
+
0,
|
| 83 |
+
1,
|
| 84 |
+
3,
|
| 85 |
+
4,
|
| 86 |
+
5,
|
| 87 |
+
7
|
| 88 |
+
],
|
| 89 |
+
"phase_v6_selected_source_layers_6l": [
|
| 90 |
+
0,
|
| 91 |
+
2,
|
| 92 |
+
3,
|
| 93 |
+
5
|
| 94 |
+
],
|
| 95 |
+
"phase_v6_source_teacher": "student6l-r3-final",
|
| 96 |
+
"phase_v6_standalone4l_kd": {
|
| 97 |
+
"canonical_materialization_dtype": "bfloat16",
|
| 98 |
+
"canonical_training_state_preservation": true,
|
| 99 |
+
"epochs": 3,
|
| 100 |
+
"legacy_float16_checkpoint": "results/layer_shrink_v6/20260728-v6-r23-standalone4l-kd-seed41-v1/student-4l-final",
|
| 101 |
+
"opened_calibration_used_for_model_selection": false,
|
| 102 |
+
"seed": 41,
|
| 103 |
+
"teacher": "results/layer_shrink_v6/20260728-v6-r22-standalone4l-map-probe-seed41-v1/teacher-6l-r3-final"
|
| 104 |
+
},
|
| 105 |
+
"problem_type": "multi_label_classification",
|
| 106 |
+
"rms_norm_eps": 1e-06,
|
| 107 |
+
"rope_parameters": {
|
| 108 |
+
"mrope_interleaved": true,
|
| 109 |
+
"mrope_section": [
|
| 110 |
+
11,
|
| 111 |
+
11,
|
| 112 |
+
10
|
| 113 |
+
],
|
| 114 |
+
"partial_rotary_factor": 0.25,
|
| 115 |
+
"rope_theta": 10000000,
|
| 116 |
+
"rope_type": "default"
|
| 117 |
+
},
|
| 118 |
+
"tie_word_embeddings": true,
|
| 119 |
+
"transformers_version": "5.13.0",
|
| 120 |
+
"use_cache": false,
|
| 121 |
+
"vocab_size": 128000
|
| 122 |
+
}
|
models/semeval-propaganda/seeds/seed42/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2db5f4be391d9dee64458cb5865530f39747019e7a219443b5f7850befbce697
|
| 3 |
+
size 421812688
|
models/semeval-propaganda/seeds/seed42/tokenizer.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
models/semeval-propaganda/seeds/seed42/tokenizer_config.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"add_prefix_space": false,
|
| 3 |
+
"audio_bos_token": "<|audio_start|>",
|
| 4 |
+
"audio_eos_token": "<|audio_end|>",
|
| 5 |
+
"audio_token": "<|audio_pad|>",
|
| 6 |
+
"backend": "tokenizers",
|
| 7 |
+
"bos_token": null,
|
| 8 |
+
"clean_up_tokenization_spaces": false,
|
| 9 |
+
"eos_token": "<|im_end|>",
|
| 10 |
+
"errors": "replace",
|
| 11 |
+
"image_token": "<|image_pad|>",
|
| 12 |
+
"is_local": true,
|
| 13 |
+
"local_files_only": true,
|
| 14 |
+
"max_length": 256,
|
| 15 |
+
"model_max_length": 131072,
|
| 16 |
+
"model_specific_special_tokens": {
|
| 17 |
+
"audio_bos_token": "<|audio_start|>",
|
| 18 |
+
"audio_eos_token": "<|audio_end|>",
|
| 19 |
+
"audio_token": "<|audio_pad|>",
|
| 20 |
+
"image_token": "<|image_pad|>",
|
| 21 |
+
"video_token": "<|video_pad|>",
|
| 22 |
+
"vision_bos_token": "<|vision_start|>",
|
| 23 |
+
"vision_eos_token": "<|vision_end|>"
|
| 24 |
+
},
|
| 25 |
+
"pad_to_multiple_of": null,
|
| 26 |
+
"pad_token": "<|endoftext|>",
|
| 27 |
+
"pad_token_type_id": 0,
|
| 28 |
+
"padding_side": "right",
|
| 29 |
+
"pretokenize_regex": "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?[\\p{L}\\p{M}]+|\\p{N}| ?[^\\s\\p{L}\\p{M}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
|
| 30 |
+
"split_special_tokens": false,
|
| 31 |
+
"stride": 0,
|
| 32 |
+
"tokenizer_class": "Qwen2Tokenizer",
|
| 33 |
+
"truncation_side": "right",
|
| 34 |
+
"truncation_strategy": "longest_first",
|
| 35 |
+
"unk_token": null,
|
| 36 |
+
"video_token": "<|video_pad|>",
|
| 37 |
+
"vision_bos_token": "<|vision_start|>",
|
| 38 |
+
"vision_eos_token": "<|vision_end|>"
|
| 39 |
+
}
|
models/semeval-propaganda/seeds/seed43/chat_template.jinja
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{%- set image_count = namespace(value=0) %}
|
| 2 |
+
{%- set video_count = namespace(value=0) %}
|
| 3 |
+
{%- macro render_content(content, do_vision_count) %}
|
| 4 |
+
{%- if content is string %}
|
| 5 |
+
{{- content }}
|
| 6 |
+
{%- else %}
|
| 7 |
+
{%- for item in content %}
|
| 8 |
+
{%- if 'image' in item or 'image_url' in item or item.type == 'image' %}
|
| 9 |
+
{%- if do_vision_count %}
|
| 10 |
+
{%- set image_count.value = image_count.value + 1 %}
|
| 11 |
+
{%- endif %}
|
| 12 |
+
{%- if add_vision_id %}Picture {{ image_count.value }}: {% endif -%}
|
| 13 |
+
<|vision_start|><|image_pad|><|vision_end|>
|
| 14 |
+
{%- elif 'video' in item or item.type == 'video' %}
|
| 15 |
+
{%- if do_vision_count %}
|
| 16 |
+
{%- set video_count.value = video_count.value + 1 %}
|
| 17 |
+
{%- endif %}
|
| 18 |
+
{%- if add_vision_id %}Video {{ video_count.value }}: {% endif -%}
|
| 19 |
+
<|vision_start|><|video_pad|><|vision_end|>
|
| 20 |
+
{%- elif 'text' in item %}
|
| 21 |
+
{{- item.text }}
|
| 22 |
+
{%- endif %}
|
| 23 |
+
{%- endfor %}
|
| 24 |
+
{%- endif %}
|
| 25 |
+
{%- endmacro %}
|
| 26 |
+
{%- if tools is defined and tools is iterable and tools | length > 0 %}
|
| 27 |
+
{{- '<|im_start|>system\n' }}
|
| 28 |
+
{{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
|
| 29 |
+
{%- for tool in tools %}
|
| 30 |
+
{{- "\n" }}
|
| 31 |
+
{{- tool | tojson }}
|
| 32 |
+
{%- endfor %}
|
| 33 |
+
{{- "\n</tools>" }}
|
| 34 |
+
{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
|
| 35 |
+
{%- if messages[0].role == 'system' and messages[0].content | trim %}
|
| 36 |
+
{%- set content = render_content(messages[0].content, false) %}
|
| 37 |
+
{{- '\n\n' + content.lstrip('\n') }}
|
| 38 |
+
{%- endif %}
|
| 39 |
+
{{- '<|im_end|>\n' }}
|
| 40 |
+
{%- else %}
|
| 41 |
+
{%- if messages[0].role == 'system' %}
|
| 42 |
+
{{- '<|im_start|>system\n' + render_content(messages[0].content, false) + '<|im_end|>\n' }}
|
| 43 |
+
{%- endif %}
|
| 44 |
+
{%- endif %}
|
| 45 |
+
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
| 46 |
+
{%- for message in messages[::-1] %}
|
| 47 |
+
{%- set index = (messages|length - 1) - loop.index0 %}
|
| 48 |
+
{%- if ns.multi_step_tool and message.role == "user" %}
|
| 49 |
+
{%- set content = render_content(message.content, False) %}
|
| 50 |
+
{%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
|
| 51 |
+
{%- set ns.multi_step_tool = false %}
|
| 52 |
+
{%- set ns.last_query_index = index %}
|
| 53 |
+
{%- endif %}
|
| 54 |
+
{%- endif %}
|
| 55 |
+
{%- endfor %}
|
| 56 |
+
{%- for message in messages %}
|
| 57 |
+
{%- set content = render_content(message.content, true) %}
|
| 58 |
+
{%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
|
| 59 |
+
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
|
| 60 |
+
{%- elif message.role == "assistant" %}
|
| 61 |
+
{%- set reasoning_content = '' %}
|
| 62 |
+
{%- if message.reasoning_content is defined and message.reasoning_content is not none %}
|
| 63 |
+
{%- set reasoning_content = message.reasoning_content %}
|
| 64 |
+
{%- else %}
|
| 65 |
+
{%- if '</think>' in content %}
|
| 66 |
+
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
| 67 |
+
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
|
| 68 |
+
{%- endif %}
|
| 69 |
+
{%- endif %}
|
| 70 |
+
{%- if message.tool_calls is defined and message.tool_calls is not none and message.tool_calls is iterable and message.tool_calls | length > 0 %}
|
| 71 |
+
{%- set content = content.rstrip('\n') %}
|
| 72 |
+
{%- endif %}
|
| 73 |
+
{%- if loop.index0 > ns.last_query_index %}
|
| 74 |
+
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
|
| 75 |
+
{%- else %}
|
| 76 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 77 |
+
{%- endif %}
|
| 78 |
+
{%- if message.tool_calls is defined and message.tool_calls is not none and message.tool_calls is iterable and message.tool_calls | length > 0 %}
|
| 79 |
+
{%- for tool_call in message.tool_calls %}
|
| 80 |
+
{%- if tool_call.function is defined %}
|
| 81 |
+
{%- set tool_call = tool_call.function %}
|
| 82 |
+
{%- endif %}
|
| 83 |
+
{%- if loop.first %}
|
| 84 |
+
{%- if content|trim %}
|
| 85 |
+
{{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 86 |
+
{%- else %}
|
| 87 |
+
{{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 88 |
+
{%- endif %}
|
| 89 |
+
{%- else %}
|
| 90 |
+
{{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 91 |
+
{%- endif %}
|
| 92 |
+
{%- if tool_call.arguments is defined %}
|
| 93 |
+
{%- for args_name, args_value in tool_call.arguments|items %}
|
| 94 |
+
{{- '<parameter=' + args_name + '>\n' }}
|
| 95 |
+
{%- set args_value = args_value | tojson | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string %}
|
| 96 |
+
{{- args_value }}
|
| 97 |
+
{{- '\n</parameter>\n' }}
|
| 98 |
+
{%- endfor %}
|
| 99 |
+
{%- endif %}
|
| 100 |
+
{{- '</function>\n</tool_call>' }}
|
| 101 |
+
{%- endfor %}
|
| 102 |
+
{%- endif %}
|
| 103 |
+
{{- '<|im_end|>\n' }}
|
| 104 |
+
{%- elif message.role == "tool" %}
|
| 105 |
+
{%- if loop.previtem and loop.previtem.role != "tool" %}
|
| 106 |
+
{{- '<|im_start|>user' }}
|
| 107 |
+
{%- endif %}
|
| 108 |
+
{{- '\n<tool_response>\n' }}
|
| 109 |
+
{{- content }}
|
| 110 |
+
{{- '\n</tool_response>' }}
|
| 111 |
+
{%- if not loop.last and loop.nextitem.role != "tool" %}
|
| 112 |
+
{{- '<|im_end|>\n' }}
|
| 113 |
+
{%- elif loop.last %}
|
| 114 |
+
{{- '<|im_end|>\n' }}
|
| 115 |
+
{%- endif %}
|
| 116 |
+
{%- endif %}
|
| 117 |
+
{%- endfor %}
|
| 118 |
+
{%- if add_generation_prompt %}
|
| 119 |
+
{{- '<|im_start|>assistant\n<think>\n' }}
|
| 120 |
+
{%- endif %}
|