TeraSpace commited on
Commit
40f546e
·
verified ·
1 Parent(s): 78f17ad

Release Accentik experimental context v2

Browse files

FP32 safetensors, self-contained PyTorch/Transformers inference code, matched tokenizer, documented evaluation tradeoff and public-project attribution license.

LICENSE ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Accentik License
2
+ Open RAIL-M with Public Project Attribution, version 1.0
3
+
4
+ Copyright 2026 TeraSpace, for the Accentik contributions.
5
+
6
+ 1. Scope and permission
7
+
8
+ This license covers the Accentik model weights (the "Model") and the original
9
+ Accentik inference source code distributed in this repository (the "Code").
10
+ Independently licensed upstream material retains its own license as described
11
+ in section 4. "Derivatives of the Model" has the meaning given in the
12
+ accompanying BigScience Open RAIL-M License.
13
+
14
+ The BigScience Open RAIL-M License dated August 18, 2022, reproduced in full in
15
+ LICENSE-OPENRAIL, is incorporated into this license for the Model and its
16
+ derivatives. Its conditions, including use-based restrictions, redistribution
17
+ requirements and notice requirements, apply alongside the additional public
18
+ project attribution condition below. TeraSpace adopts those terms for its Model
19
+ contributions; this does not relicense independently licensed upstream material.
20
+
21
+ Subject to the applicable terms, permission is granted without any license fee
22
+ or royalty payable to TeraSpace to use, copy, modify, fine-tune, convert, distill,
23
+ host and redistribute the Model and its derivatives, including commercially.
24
+ Permission is also granted without any license fee or royalty to use, copy,
25
+ modify, merge, publish, distribute, sublicense and sell copies of the original
26
+ Code, subject to sections 2, 3 and 5 of this license. Donations are voluntary
27
+ and are not a condition of any permission granted here.
28
+
29
+ 2. Additional public project attribution condition
30
+
31
+ If you incorporate Accentik, its original Code, or a modified, fine-tuned,
32
+ quantized, converted or distilled version of the Model into a public project,
33
+ you must clearly acknowledge Accentik by TeraSpace in the project's publicly
34
+ accessible description. Include a link to
35
+ https://huggingface.co/TeraSpace/accentik wherever hyperlinks are supported.
36
+
37
+ A public project includes a public source-code repository, publicly released
38
+ application, publicly offered product or service, public research project, or
39
+ publicly distributed derivative model. This condition applies to commercial
40
+ and noncommercial projects alike, whether the Model or Code runs locally,
41
+ is embedded in an application, or operates on a backend providing functionality.
42
+
43
+ An acceptable acknowledgement for the unmodified Model or Code is:
44
+ "Russian word stress powered by Accentik by TeraSpace."
45
+
46
+ An acceptable acknowledgement for a derivative is:
47
+ "Russian word stress based on Accentik by TeraSpace."
48
+
49
+ Place the acknowledgement in a reasonably visible and readable public README,
50
+ documentation, product page, app-store listing or comparable project description.
51
+ A credit present only in source-code comments or a bundled license file does
52
+ not by itself satisfy this additional condition. Equivalent wording is allowed
53
+ if it identifies Accentik and TeraSpace and includes the link where supported.
54
+
55
+ Private internal use without a public project does not require a public
56
+ announcement. This condition does not require disclosure of an application's
57
+ source code. Distributing individual annotated texts alone does not trigger
58
+ this additional attribution condition, and credit need not accompany every
59
+ generated annotation. Public products or services that provide annotations
60
+ using the Model or Code remain covered. Applicable Open RAIL-M output provisions
61
+ continue to apply to the Model.
62
+
63
+ 3. Redistribution
64
+
65
+ Redistributors of the Model, Code or their derivatives must include this LICENSE,
66
+ LICENSE-OPENRAIL, LICENSE-APACHE-2.0 and NOTICE, preserve applicable copyright and
67
+ attribution notices, and preserve the public project attribution condition for
68
+ the Accentik contributions in terms supplied to downstream recipients.
69
+ Applicable Open RAIL-M redistribution terms apply to the Model and its
70
+ derivatives. Applicable Apache 2.0 terms apply to upstream RuModernBERT material.
71
+
72
+ 4. Independently licensed components
73
+
74
+ The Model includes material derived from deepvk/RuModernBERT-small:
75
+ https://huggingface.co/deepvk/RuModernBERT-small
76
+
77
+ The upstream weights and tokenizer remain subject to the Apache License 2.0,
78
+ reproduced in LICENSE-APACHE-2.0. Nothing in this license removes permissions
79
+ granted directly under that upstream license or changes licenses of third-party
80
+ libraries and datasets. Only TeraSpace's contributions are offered under the
81
+ additional terms of this license. See NOTICE for provenance and acknowledgements.
82
+
83
+ 5. No endorsement; warranty and liability
84
+
85
+ Attribution does not imply endorsement by TeraSpace or upstream contributors.
86
+ No trademark permission is granted beyond identification necessary for credit.
87
+ The warranty disclaimers and limitations of liability in LICENSE-OPENRAIL apply
88
+ to the Model and TeraSpace's Model contributions.
89
+
90
+ THE ORIGINAL CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
91
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
92
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
93
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
94
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
95
+ WITH THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
LICENSE-APACHE-2.0 ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
LICENSE-OPENRAIL ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ BigScience Open RAIL-M License
2
+ dated August 18, 2022
3
+
4
+ Section I: PREAMBLE
5
+
6
+ This Open RAIL-M License was created by BigScience, a collaborative open innovation project aimed at
7
+ the responsible development and use of large multilingual datasets and Large Language Models
8
+ (“LLMs”). While a similar license was originally designed for the BLOOM model, we decided to adapt it
9
+ and create this license in order to propose a general open and responsible license applicable to other
10
+ machine learning based AI models (e.g. multimodal generative models).
11
+ In short, this license strives for both the open and responsible downstream use of the accompanying
12
+ model. When it comes to the open character, we took inspiration from open source permissive licenses
13
+ regarding the grant of IP rights. Referring to the downstream responsible use, we added use-based
14
+ restrictions not permitting the use of the Model in very specific scenarios, in order for the licensor to be
15
+ able to enforce the license in case potential misuses of the Model may occur. Even though downstream
16
+ derivative versions of the model could be released under different licensing terms, the latter will always
17
+ have to include - at minimum - the same use-based restrictions as the ones in the original license (this
18
+ license).
19
+ The development and use of artificial intelligence (“AI”), does not come without concerns. The world has
20
+ witnessed how AI techniques may, in some instances, become risky for the public in general. These risks
21
+ come in many forms, from racial discrimination to the misuse of sensitive information.
22
+ BigScience believes in the intersection between open and responsible AI development; thus, this License
23
+ aims to strike a balance between both in order to enable responsible open-science in the field of AI.
24
+ This License governs the use of the model (and its derivatives) and is informed by the model card
25
+ associated with the model.
26
+
27
+ NOW THEREFORE, You and Licensor agree as follows:
28
+
29
+ 1. Definitions
30
+ (a) "License" means the terms and conditions for use, reproduction, and Distribution as defined in
31
+ this document.
32
+ (b) “Data” means a collection of information and/or content extracted from the dataset used with the
33
+ Model, including to train, pretrain, or otherwise evaluate the Model. The Data is not licensed under
34
+ this License.
35
+ (c)“Output” means the results of operating a Model as embodied in informational content resulting
36
+ therefrom.
37
+ (d)“Model” means any accompanying machine-learning based assemblies (including checkpoints),
38
+ consisting of learnt weights, parameters (including optimizer states), corresponding to the model
39
+ architecture as embodied in the Complementary Material, that have been trained or tuned, in whole or
40
+ in part on the Data, using the Complementary Material.
41
+ (e) “Derivatives of the Model” means all modifications to the Model, works based on the Model, or any
42
+ other model which is created or initialized by transfer of patterns of the weights, parameters,
43
+ activations or output of the Model, to the other model, in order to cause the other model to perform
44
+ similarly to the Model, including - but not limited to - distillation methods entailing the use of
45
+ intermediate data representations or methods based on the generation of synthetic data by the Model
46
+ for training the other model.
47
+ (f)“Complementary Material” means the accompanying source code and scripts used to define,
48
+ run, load, benchmark or evaluate the Model, and used to prepare data for training or evaluation, if
49
+ any. This includes any accompanying documentation, tutorials, examples, etc, if any.
50
+ (g) “Distribution” means any transmission, reproduction, publication or other sharing of the Model or
51
+ Derivatives of the Model to a third party, including providing the Model as a hosted service made
52
+ available by electronic or other remote means - e.g. API-based or web access.
53
+ (h) “Licensor” means the copyright owner or entity authorized by the copyright owner that is
54
+ granting the License, including the persons or entities that may have rights in the Model and/or
55
+ distributing the Model.
56
+ (i) "You" (or "Your") means an individual or Legal Entity exercising permissions granted by this
57
+ License and/or making use of the Model for whichever purpose and in any field of use, including
58
+ usage of the Model in an end-use application - e.g. chatbot, translator, image generator.
59
+ (j) “Third Parties” means individuals or legal entities that are not under common control with
60
+ Licensor or You.
61
+ (k) "Contribution" means any work of authorship, including the original version of the Model and
62
+ any modifications or additions to that Model or Derivatives of the Model thereof, that is
63
+ intentionally submitted to Licensor for inclusion in the Model by the copyright owner or by an
64
+ individual or Legal Entity authorized to submit on behalf of the copyright owner. For the
65
+ purposes of this definition,
66
+ “submitted” means any form of electronic, verbal, or written
67
+ communication sent to the Licensor or its representatives, including but not limited to
68
+ communication on electronic mailing lists, source code control systems, and issue tracking
69
+ systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and
70
+ improving the Model, but excluding communication that is conspicuously marked or otherwise
71
+ designated in writing by the copyright owner as "Not a Contribution."
72
+ (l) "Contributor" means Licensor and any individual or Legal Entity on behalf of whom a
73
+ Contribution has been received by Licensor and subsequently incorporated within the Model.
74
+
75
+
76
+ Section II: INTELLECTUAL PROPERTY RIGHTS
77
+
78
+ Both copyright and patent grants apply to the Model, Derivatives of the Model and Complementary
79
+ Material. The Model and Derivatives of the Model are subject to additional terms as described in Section III.
80
+
81
+ 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor
82
+ hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare, publicly display, publicly perform, sublicense, and distribute the
83
+ Complementary Material, the Model, and Derivatives of the Model.
84
+
85
+ 3. Grant of Patent License. Subject to the terms and conditions of this License and where and as
86
+ applicable, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge,
87
+ royalty-free, irrevocable (except as stated in this paragraph) patent license to make, have made, use, offer
88
+ to sell, sell, import, and otherwise transfer the Model and the Complementary Material, where such
89
+ license applies only to those patent claims licensable by such Contributor that are necessarily infringed by
90
+ their Contribution(s) alone or by combination of their Contribution(s) with the Model to which such
91
+ Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim
92
+ or counterclaim in a lawsuit) alleging that the Model and/or Complementary Material or a Contribution
93
+ incorporated within the Model and/or Complementary Material constitutes direct or contributory patent
94
+ infringement, then any patent licenses granted to You under this License for the Model and/or Work shall
95
+ terminate as of the date such litigation is asserted or filed.
96
+ Section III: CONDITIONS OF USAGE, DISTRIBUTION AND REDISTRIBUTION
97
+
98
+ 4. Distribution and Redistribution. You may host for Third Party remote access purposes (e.g.
99
+ software-as-a-service), reproduce and distribute copies of the Model or Derivatives of the Model thereof
100
+ in any medium, with or without modifications, provided that You meet the following conditions:
101
+
102
+ a. Use-based restrictions as referenced in paragraph 5 MUST be included as an enforceable provision
103
+ by You in any type of legal agreement (e.g. a license) governing the use and/or distribution of the
104
+ Model or Derivatives of the Model, and You shall give notice to subsequent users You Distribute to,
105
+ that the Model or Derivatives of the Model are subject to paragraph 5. This provision does not apply
106
+ to the use of Complementary Material.
107
+
108
+ b. You must give any Third Party recipients of the Model or Derivatives of the Model a copy of this
109
+ License;
110
+
111
+ c. You must cause any modified files to carry prominent notices stating that You changed the files;
112
+
113
+ d. You must retain all copyright, patent, trademark, and attribution notices excluding those notices
114
+ that do not pertain to any part of the Model, Derivatives of the Model.
115
+ You may add Your own copyright statement to Your modifications and may provide additional or
116
+ different license terms and conditions - respecting paragraph 4.a.
117
+ - for use, reproduction, or Distribution
118
+ of Your modifications, or for any such Derivatives of the Model as a whole, provided Your use,
119
+ reproduction, and Distribution of the Model otherwise complies with the conditions stated in this License.
120
+
121
+ 5. Use-based restrictions. The restrictions set forth in Attachment A are considered Use-based restrictions.
122
+ Therefore You cannot use the Model and the Derivatives of the Model for the specified restricted uses. You
123
+ may use the Model subject to this License, including only for lawful purposes and in accordance with the
124
+ License. Use may include creating any content with, finetuning, updating, running, training, evaluating and/or
125
+ reparametrizing the Model. You shall require all of Your users who use the Model or a Derivative of the Model
126
+ to comply with the terms of this paragraph (paragraph 5).
127
+
128
+ 6. The Output You Generate. Except as set forth herein, Licensor claims no rights in the Output You
129
+ generate using the Model. You are accountable for the Output you generate and its subsequent uses. No
130
+ use of the output can contravene any provision as stated in the License.
131
+
132
+ Section IV: OTHER PROVISIONS
133
+
134
+ 7. Updates and Runtime Restrictions. To the maximum extent permitted by law, Licensor reserves the
135
+ right to restrict (remotely or otherwise) usage of the Model in violation of this License, update the Model
136
+ through electronic means, or modify the Output of the Model based on updates. You shall undertake
137
+ reasonable efforts to use the latest version of the Model.
138
+
139
+ 8. Trademarks and related. Nothing in this License permits You to make use of Licensors’ trademarks,
140
+ trade names, logos or to otherwise suggest endorsement or misrepresent the relationship between the
141
+ parties; and any rights not expressly granted herein are reserved by the Licensors.
142
+
143
+ 9. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides
144
+ the Model and the Complementary Material (and each Contributor provides its Contributions) on an "AS
145
+ IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
146
+ including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT,
147
+ MERCHANTABILITY , or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for
148
+ determining the appropriateness of using or redistributing the Model, Derivatives of the Model, and the
149
+ Complementary Material and assume any risks associated with Your exercise of permissions under this
150
+ License.
151
+
152
+ 10. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence),
153
+ contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or
154
+ agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect,
155
+ special, incidental, or consequential damages of any character arising as a result of this License or out of
156
+ the use or inability to use the Model and the Complementary Material (including but not limited to
157
+ damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other
158
+ commercial damages or losses), even if such Contributor has been advised of the possibility of such
159
+ damages.
160
+
161
+ 11. Accepting Warranty or Additional Liability. While redistributing the Model, Derivatives of the
162
+ Model and the Complementary Material thereof, You may choose to offer, and charge a fee for, acceptance
163
+ of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License.
164
+ However, in accepting such obligations, You may act only on Your own behalf and on Your sole
165
+ responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and
166
+ hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor
167
+ by reason of your accepting any such warranty or additional liability.
168
+
169
+ 12. If any provision of this License is held to be invalid, illegal or unenforceable, the remaining
170
+ provisions shall be unaffected thereby and remain valid as if such provision had not been set forth herein.
171
+
172
+ END OF TERMS AND CONDITIONS
173
+
174
+ Attachment A
175
+
176
+ Use Restrictions
177
+
178
+ You agree not to use the Model or Derivatives of the Model:
179
+ (a) In any way that violates any applicable national, federal, state, local or international law
180
+ or regulation;
181
+ (b) For the purpose of exploiting, harming or attempting to exploit or harm minors in any
182
+ way;
183
+ (c) To generate or disseminate verifiably false information and/or content with the purpose of
184
+ harming others;
185
+ (d) To generate or disseminate personal identifiable information that can be used to harm an
186
+ individual;
187
+ (e) To generate or disseminate information and/or content (e.g. images, code, posts, articles),
188
+ and place the information and/or content in any context (e.g. bot generating tweets)
189
+ without expressly and intelligibly disclaiming that the information and/or content is
190
+ machine generated;
191
+ (f) To defame, disparage or otherwise harass others;
192
+ (g) To impersonate or attempt to impersonate (e.g. deepfakes) others without their consent;
193
+ (h) For fully automated decision making that adversely impacts an individual’s legal rights or
194
+ otherwise creates or modifies a binding, enforceable obligation;
195
+ (i) For any use intended to or which has the effect of discriminating against or harming
196
+ individuals or groups based on online or offline social behavior or known or predicted
197
+ personal or personality characteristics;
198
+ (j) To exploit any of the vulnerabilities of a specific group of persons based on their age,
199
+ social, physical or mental characteristics, in order to materially distort the behavior of a
200
+ person pertaining to that group in a manner that causes or is likely to cause that person or
201
+ another person physical or psychological harm;
202
+ (k) For any use intended to or which has the effect of discriminating against individuals or
203
+ groups based on legally protected characteristics or categories;
204
+ (l) To provide medical advice and medical results interpretation;
205
+ (m) To generate or disseminate information for the purpose to be used for administration of
206
+ justice, law enforcement, immigration or asylum processes, such as predicting an
207
+ individual will commit fraud/crime commitment (e.g. by text profiling, drawing causal
208
+ relationships between assertions made in documents, indiscriminate and
209
+ arbitrarily-targeted use).
NOTICE ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Accentik — experimental context v2
2
+ Copyright 2026 TeraSpace, for Accentik contributions.
3
+ https://huggingface.co/TeraSpace/accentik
4
+
5
+ The encoder and bundled tokenizer derive from deepvk/RuModernBERT-small,
6
+ revision cdfe49388e1f5261c69e4b1c8c41fa9e60b9ebef (patched-tokenizer branch).
7
+ https://huggingface.co/deepvk/RuModernBERT-small
8
+ These upstream materials retain the Apache License 2.0; see LICENSE-APACHE-2.0.
9
+ RuModernBERT builds on ModernBERT: https://arxiv.org/abs/2412.13663
10
+
11
+ Accentik's word-stress and local-context heads, inference helpers, and model
12
+ refinement are TeraSpace contributions under LICENSE. This repository does not
13
+ claim ownership of independently licensed upstream material or dependencies.
14
+
15
+ Training acknowledgements (training datasets are not redistributed):
16
+ - iv-stoliar/RussianHomographDataset, source repository GPL-3.0.
17
+ https://github.com/iv-stoliar/RussianHomographDataset
18
+ - TeraTTS/stress_dataset_sft_proza, stress_dataset_sft_poetry, open_accent,
19
+ dataset metadata MIT.
20
+ - RUAccent (Den4ikAI), pseudo-label teacher and evaluation comparison.
21
+ No RUAccent dictionary, morphological assets or code are required at inference.
22
+ - MashaPo/russtress, external evaluation reference.
23
+
24
+ See README.md and evaluation.json for methodology, source revisions and limits.
25
+ Third-party names identify sources and do not imply endorsement.
README.md ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: other
3
+ license_name: accentik-openrail-m-public-attribution
4
+ license_link: https://huggingface.co/TeraSpace/accentik/blob/main/LICENSE
5
+ library_name: transformers
6
+ language:
7
+ - ru
8
+ base_model: deepvk/RuModernBERT-small
9
+ tags:
10
+ - russian
11
+ - accentuation
12
+ - word-stress
13
+ - custom-code
14
+ - pytorch
15
+ - safetensors
16
+ ---
17
+
18
+ ## 💜 Support Accentik
19
+
20
+ If Accentik is useful to you, consider supporting its development. Thank you for your support!
21
+
22
+ | Currency | Donation address |
23
+ | :--- | :--- |
24
+ | **ETH** | `0x19766c207F82303c3998976d852DB82e8Dcc5d77` |
25
+ | **GRAM (TON)** | `UQDYx1blVO8H31APnRUXVg2uTXV--wXX8HdTyKxnOKQciPQj` |
26
+ | **SOL** | `DvBjAqaj4AYWi3JBMcW4FmXsaU4Nu6yjxrox4XJZ6o6Z` |
27
+ | **Tether USDt (TRON / TRC20)** | `TZ6QeYZSr2h22rzGqQZQZTMzWYVPdUej1y` |
28
+ | **BTC** | `bc1q2l0xdlylqpweu4sm3y53f95g0nazcm8yu42gkq` |
29
+
30
+ ---
31
+
32
+ # Accentik — experimental context v2
33
+
34
+ Accentik adds Russian word-stress marks to text. It inserts `+` before the stressed vowel in Russian words with more than one vowel, using the surrounding text to resolve ambiguous words.
35
+
36
+ ```text
37
+ Input: на двери в замок висел замок.
38
+ Output: на двер+и в з+амок вис+ел зам+ок.
39
+ ```
40
+
41
+ This is the **experimental context v2** checkpoint. Contextual homograph accuracy improved substantially over the initial Accentik model, while general RusStress accuracy decreased slightly. See the measurements below before choosing it for your project.
42
+
43
+ The model has **34.85 million parameters**, a single encoder pass, and parallel vowel classification. It uses [deepvk/RuModernBERT-small](https://huggingface.co/deepvk/RuModernBERT-small) with a learned word-stress head and local-context residual head. Inference uses PyTorch and Transformers, without a dictionary, morphological analyzer, RUAccent installation, or autoregressive decoding.
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ pip install "torch>=2.6" "transformers>=5.15.1,<6" "safetensors>=0.4.3" "numpy>=1.24"
49
+ ```
50
+
51
+ Tested with Python 3.10, PyTorch 2.9.0 and Transformers 5.15.1. CPU and CUDA are supported. The encoder uses PyTorch SDPA; installing `flash-attn` is unnecessary.
52
+
53
+ ## Load with Transformers
54
+
55
+ The repository includes the inference code, matched tokenizer, config and one FP32 `model.safetensors` file, approximately 140 MB. The first call loads the bundled tokenizer from the same repository revision as the weights.
56
+
57
+ ```python
58
+ import torch
59
+ from transformers import AutoModel
60
+
61
+ torch.set_num_threads(2) # Optional: avoids excessive CPU thread overhead.
62
+
63
+ model = AutoModel.from_pretrained(
64
+ "TeraSpace/accentik",
65
+ trust_remote_code=True,
66
+ ).eval()
67
+
68
+ text = "на двери в замок висел замок."
69
+ print(model.accentuate(text))
70
+ # на двер+и в з+амок вис+ел зам+ок.
71
+ ```
72
+
73
+ For CUDA, move the model before inference:
74
+
75
+ ```python
76
+ model = model.to("cuda")
77
+ print(model.accentuate("Он уже закончил работу."))
78
+ # Он +уже зак+ончил раб+оту.
79
+ ```
80
+
81
+ The text API uses BF16 autocast on CUDA and FP32 on CPU. Keep the model weights in FP32 when using these examples. For a reproducible deployment, pass an explicit Hub commit hash as `revision=` when loading the model. Its automatically loaded tokenizer follows that revision.
82
+
83
+ ## Batch inference and manual stress
84
+
85
+ ```python
86
+ texts = [
87
+ "Для платья купили атлас.",
88
+ "В старом з+амке тихо.\nМолоко́ на столе. English. 2+2=4.",
89
+ ]
90
+ for result in model.accentuate_batch(texts):
91
+ print(result)
92
+ ```
93
+
94
+ Existing manual `+` marks before Russian vowels and combining acute marks after vowels override model decisions. The original markers remain in the output. Capitalization, punctuation, spaces, line breaks, mathematical plus signs, English text and the original `е`/`ё` spelling are preserved. New stress marks are added only to Russian words with at least two vowels.
95
+
96
+ For decisions and confidence scores:
97
+
98
+ ```python
99
+ result = model.annotate("Он уже закончил работу.")
100
+ print(result["text"])
101
+ for word in result["words"]:
102
+ print(word["word"], word["position"], word["confidence"], word["supplied"])
103
+ ```
104
+
105
+ `start`, `end` and `position` are character offsets in the input after removing stress markers; `end` is exclusive. Confidence is an uncalibrated softmax score, not an estimated correctness probability. Manually supplied stress has `confidence=None` and `supplied=True`.
106
+
107
+ You can supply the tokenizer explicitly with `tokenizer=AutoTokenizer.from_pretrained("TeraSpace/accentik", trust_remote_code=True)` to any text method. Use the same revision as the model.
108
+
109
+ ## Long texts and offline use
110
+
111
+ Text is processed in whole-word windows of at most **512 tokens**. Longer inputs split at whitespace, retain all source characters and use token-budget batching. Context across window boundaries is limited. A single unbroken token sequence that cannot fit the window raises an error instead of silently truncating it.
112
+
113
+ Download a complete snapshot once, then load it locally:
114
+
115
+ ```python
116
+ from huggingface_hub import snapshot_download
117
+ from transformers import AutoModel
118
+
119
+ folder = snapshot_download("TeraSpace/accentik")
120
+ model = AutoModel.from_pretrained(
121
+ folder, trust_remote_code=True, local_files_only=True,
122
+ ).eval()
123
+ print(model.accentuate("Для платья купили атлас."))
124
+ ```
125
+
126
+ The repository also contains a command-line script:
127
+
128
+ ```bash
129
+ python inference.py --text "на двери в замок висел замок."
130
+ python inference.py --input input.txt --output accented.txt --device cuda
131
+ ```
132
+
133
+ ## Evaluation
134
+
135
+ Word-stress target accuracy, measured using the same evaluation procedures for both Accentik checkpoints:
136
+
137
+ | Evaluation | Targets | Initial Accentik | Experimental context v2 |
138
+ | :--- | ---: | ---: | ---: |
139
+ | Held-out manually annotated Russian homograph contexts | 7,673 | 85.13% | **97.51%** |
140
+ | General RusStress multi-vowel targets | 11,089 | **94.75%** | 94.10% |
141
+ | Supplied dialogue-label agreement | 50,564 | **99.02%** | 98.72% |
142
+
143
+ On the same general RusStress targets, the native RUAccent comparison achieved **95.15%**. This release does not claim better overall accuracy than RUAccent. The dialogue result measures agreement with supplied labels rather than an independent human annotation benchmark.
144
+
145
+ The new checkpoint resolves 9/10 words in a small set of reported contextual diagnostics, compared with 4/10 for the initial model. Those examples were repeatedly inspected during development; they are not a representative independent benchmark. One remaining failure is `На двери замка висел замок.`, where the castle form can still receive the wrong stress.
146
+
147
+ Checkpoint selection used validation data. Fixed evaluations were subsequently reused during development. Exact external benchmark contexts were excluded from refinement data, but overlap with the original teacher-labelled pretraining corpus is unknown. The general accuracy regression failed the internal promotion gate; this checkpoint is published explicitly as an experimental alternative. Machine-readable counts are in [evaluation.json](evaluation.json).
148
+
149
+ ## Training sources
150
+
151
+ The initial model learned from existing RUAccent-labelled Russian speech transcripts. Refinement added manually annotated homograph contexts, the supplied datasets below, and a small amount of authored contextual training material. Unmarked words were treated as unknown labels; absent stress was not trained as a negative. Known corrupt labels and ambiguous teacher replay targets were filtered or masked.
152
+
153
+ - [iv-stoliar/RussianHomographDataset](https://github.com/iv-stoliar/RussianHomographDataset): manually annotated homograph contexts; the source repository provides GPL-3.0.
154
+ - [TeraTTS/stress_dataset_sft_proza](https://huggingface.co/datasets/TeraTTS/stress_dataset_sft_proza), [TeraTTS/stress_dataset_sft_poetry](https://huggingface.co/datasets/TeraTTS/stress_dataset_sft_poetry) and [TeraTTS/open_accent](https://huggingface.co/datasets/TeraTTS/open_accent): dataset metadata specifies MIT. Positive-labelled examples were used; negative-labelled examples were excluded.
155
+ - [RUAccent](https://github.com/Den4ikAI/ruaccent): original pseudo-label teacher and evaluation comparison; not an inference dependency.
156
+
157
+ Training datasets are not redistributed in this repository. Dataset metadata and source revisions are recorded in `evaluation.json`.
158
+
159
+ ## Limitations
160
+
161
+ Russian lexical stress can depend on meaning that the available context does not resolve. Names, rare forms and ambiguous words can still be wrong, including at high confidence. Manual stress remains useful for known exceptions. The model does not restore `ё`, normalize spelling, predict pauses, label emphasis or generate speech.
162
+
163
+ ## License and attribution
164
+
165
+ Accentik is available without a license fee or royalty for personal and commercial use, subject to [LICENSE](LICENSE) and [LICENSE-OPENRAIL](LICENSE-OPENRAIL). Donations are optional.
166
+
167
+ **Public projects using Accentik must give visible credit**, whether commercial or noncommercial. Put the credit in the project's public README, documentation, product page or comparable public description, with a link where supported:
168
+
169
+ > Russian word stress powered by [Accentik by TeraSpace](https://huggingface.co/TeraSpace/accentik).
170
+
171
+ For a derivative model, use “based on Accentik by TeraSpace.” Private internal use does not require a public announcement. You do not need to credit each individual annotated text. Redistributing the model or code requires retaining the license and notices. These terms do not require publishing your application's source code.
172
+
173
+ The upstream RuModernBERT material retains its Apache 2.0 license; see [NOTICE](NOTICE) and [LICENSE-APACHE-2.0](LICENSE-APACHE-2.0). The repository uses a custom attribution license and is not licensed as MIT or Apache 2.0 as a whole.
accentik_features.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Construct the exact parallel vowel-ranking features used in training."""
2
+ import numpy as np
3
+ import torch
4
+
5
+ WORD = np.dtype([('first', '<u2'), ('last', '<u2'), ('offset', '<u8'), ('n', '<u2'),
6
+ ('label', '<i2'), ('start', '<u4'), ('end', '<u4')])
7
+
8
+
9
+ CANDIDATE = np.dtype([('first', '<u2'), ('last', '<u2'), ('position', '<u4'),
10
+ ('letter', 'u1'), ('relative_start', '<f4'), ('relative_end', '<f4')])
11
+
12
+
13
+ def collate(examples, pad=50283):
14
+ """examples are (tokens, structured words, structured candidates)."""
15
+ width = max(len(t) for t, w, c in examples)
16
+ width = (width+7)//8*8
17
+ nw = sum(len(w) for t, w, c in examples)
18
+ nv = max([int(w['n'].max()) for t, w, c in examples if len(w)] or [2])
19
+ tokens = np.full((len(examples), width), pad, dtype=np.int64)
20
+ attention = np.zeros_like(tokens)
21
+ wf = np.zeros(nw, dtype=np.int64); wl = np.zeros_like(wf)
22
+ cf = np.zeros((nw, nv), dtype=np.int64); cl = np.zeros_like(cf)
23
+ letters = np.zeros_like(cf); rank = np.zeros_like(cf); count = np.zeros_like(cf)
24
+ scalars = np.zeros((nw, nv, 4), dtype=np.float32)
25
+ valid = np.zeros((nw, nv), dtype=np.bool_)
26
+ target = np.full(nw, -100, dtype=np.int64)
27
+ row = 0
28
+ for bi, (t, words, candidates) in enumerate(examples):
29
+ tokens[bi, :len(t)] = t; attention[bi, :len(t)] = 1
30
+ for w in words:
31
+ n, off = int(w['n']), int(w['offset'])
32
+ c = candidates[off:off+n]
33
+ wf[row], wl[row] = bi*width+int(w['first']), bi*width+int(w['last'])
34
+ cf[row, :n], cl[row, :n] = bi*width+c['first'], bi*width+c['last']
35
+ letters[row, :n] = c['letter']; rank[row, :n] = np.minimum(np.arange(n), 63)
36
+ count[row, :n] = min(n, 63)
37
+ scalars[row, :n, 0], scalars[row, :n, 1] = c['relative_start'], c['relative_end']
38
+ scalars[row, :n, 2] = np.arange(n)/max(n-1, 1)
39
+ scalars[row, :n, 3] = 1/max(int(w['end'])-int(w['start']), 1)
40
+ valid[row, :n] = True; target[row] = int(w['label']); row += 1
41
+ x = {k: torch.from_numpy(v) for k, v in dict(tokens=tokens, attention=attention,
42
+ word_first=wf, word_last=wl, candidate_first=cf, candidate_last=cl,
43
+ letters=letters, rank=rank, count=count, scalars=scalars, valid=valid).items()}
44
+ return x, torch.from_numpy(target)
accentik_inference.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """High-level source-preserving text API for Accentik."""
2
+ import numpy as np
3
+ import torch
4
+ from transformers import AutoTokenizer
5
+ from .accentik_text import WORD as RUSSIAN_WORD, VOWELS, parse, encode, context_parts
6
+ from .accentik_features import WORD, CANDIDATE, collate
7
+
8
+
9
+ class AccentikInferenceMixin:
10
+ def _get_tokenizer(self, tokenizer=None):
11
+ if tokenizer is not None:
12
+ if not tokenizer.is_fast:
13
+ raise ValueError("Accentik requires its bundled fast tokenizer")
14
+ return tokenizer
15
+ if getattr(self, "_accentik_tokenizer", None) is None:
16
+ source = getattr(self, "_accentik_source", None) or self.config._name_or_path
17
+ if not source:
18
+ raise ValueError("Supply tokenizer= when constructing the model directly")
19
+ self._accentik_tokenizer = AutoTokenizer.from_pretrained(
20
+ source, config=self.config, **getattr(self, "_accentik_tokenizer_options", {}))
21
+ return self._accentik_tokenizer
22
+
23
+ @torch.inference_mode()
24
+ def annotate_many(self, texts, tokenizer=None):
25
+ if isinstance(texts, str):
26
+ raise TypeError('Pass a list of strings or use annotate(text)')
27
+ texts = list(texts)
28
+ if any(not isinstance(t, str) for t in texts):
29
+ raise TypeError('Every input must be a string')
30
+ if not texts:
31
+ return []
32
+ tokenizer = self._get_tokenizer(tokenizer)
33
+ parsed = [parse(t) for t in texts]
34
+ selections = [[] for _ in texts]
35
+ pending = []
36
+ for ri, (plain, supplied) in enumerate(parsed):
37
+ if not any(sum(c.lower() in VOWELS for c in w.group()) > 1
38
+ for w in RUSSIAN_WORD.finditer(plain)):
39
+ continue
40
+ for a, b in context_parts(plain, tokenizer, self.config.context_window):
41
+ t, w, c = encode(plain[a:b], {}, tokenizer)
42
+ if not w:
43
+ continue
44
+ pending.append((ri, a, (t, np.asarray(w, dtype=WORD), np.asarray(c, dtype=CANDIDATE))))
45
+ position = 0
46
+ while position < len(pending):
47
+ end, width = position, 0
48
+ while end < len(pending):
49
+ n = len(pending[end][2][0]); proposed = max(width, (n+7)//8*8)
50
+ if end > position and (proposed*(end-position+1) > self.config.batch_token_budget or end-position >= 128):
51
+ break
52
+ width = proposed; end += 1
53
+ chunk = pending[position:end]
54
+ x, _ = collate([r[2] for r in chunk], tokenizer.pad_token_id)
55
+ x = {k: v.to(self.device) for k, v in x.items()}
56
+ if self.device.type == 'cuda':
57
+ with torch.autocast('cuda', dtype=torch.bfloat16):
58
+ logits = self(**x).logits
59
+ else:
60
+ logits = self(**x).logits
61
+ probs = logits.softmax(-1); choice = probs.argmax(-1).cpu().numpy()
62
+ confidence = probs.max(-1).values.cpu().numpy()
63
+ wi = 0
64
+ for ri, a, (t, words, candidates) in chunk:
65
+ for w in words:
66
+ off, n = int(w['offset']), int(w['n'])
67
+ pos = a+int(candidates[off+int(choice[wi])]['position'])
68
+ selections[ri].append(dict(start=a+int(w['start']), end=a+int(w['end']),
69
+ position=pos, vowel_ordinal=int(choice[wi]), confidence=float(confidence[wi])))
70
+ wi += 1
71
+ position = end
72
+ results = []
73
+ for raw, (plain, supplied), words in zip(texts, parsed, selections):
74
+ source_map = []
75
+ for i, c in enumerate(raw):
76
+ if c == '+' and i+1 < len(raw) and raw[i+1].lower() in VOWELS:
77
+ continue
78
+ if c == '\u0301' and source_map and raw[source_map[-1]].lower() in VOWELS:
79
+ continue
80
+ source_map.append(i)
81
+ assert ''.join(raw[i] for i in source_map) == plain
82
+ new = set()
83
+ for w in words:
84
+ manual = sorted(p for p in supplied if w['start'] <= p < w['end'])
85
+ w['word'] = plain[w['start']:w['end']]
86
+ w['supplied'] = bool(manual)
87
+ if manual:
88
+ w['position'] = manual[0]; w['confidence'] = None
89
+ w['vowel_ordinal'] = sum(
90
+ c.lower() in VOWELS for c in plain[w['start']:manual[0]])
91
+ else:
92
+ new.add(source_map[w['position']])
93
+ output = ''.join(('+' if i in new else '')+c for i, c in enumerate(raw))
94
+ # Undo only newly inserted characters, preserving supplied acute/+,
95
+ # math signs, е/ё, case, whitespace and every source character.
96
+ recovered = list(output)
97
+ for extra, p in reversed(list(enumerate(sorted(new)))):
98
+ del recovered[p+extra]
99
+ assert ''.join(recovered) == raw
100
+ results.append(dict(input=raw, text=output, words=words))
101
+ return results
102
+
103
+ def annotate(self, text, tokenizer=None):
104
+ """Return annotated text and vowel decisions; offsets refer to unmarked input.
105
+
106
+ Existing +/combining-acute stress overrides the model. Confidence is an
107
+ uncalibrated softmax score, or None for a supplied manual stress mark.
108
+ """
109
+ return self.annotate_many([text], tokenizer=tokenizer)[0]
110
+
111
+ def accentuate(self, text, tokenizer=None):
112
+ """Insert + before stressed vowels, retaining every source character."""
113
+ return self.annotate(text, tokenizer=tokenizer)["text"]
114
+
115
+ def accentuate_batch(self, texts, tokenizer=None):
116
+ """Annotate a list of texts using token-budget batching."""
117
+ return [r["text"] for r in self.annotate_many(texts, tokenizer=tokenizer)]
accentik_text.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Source-preserving stress parsing and whole-word context windows."""
2
+ import re
3
+
4
+ VOWELS = 'аеёиоуыэюя'
5
+
6
+
7
+ WORD = re.compile('[А-Яа-яЁё]+')
8
+
9
+
10
+ def parse(text):
11
+ plain, marks = [], set()
12
+ for i, c in enumerate(text):
13
+ if c == '+' and i+1 < len(text) and text[i+1].lower() in VOWELS:
14
+ marks.add(len(plain))
15
+ elif c == '\u0301' and plain and plain[-1].lower() in VOWELS:
16
+ marks.add(len(plain)-1)
17
+ else:
18
+ plain.append(c)
19
+ return ''.join(plain), marks
20
+
21
+
22
+ def encode(text, target, tokenizer, encoded=None):
23
+ """Keep the complete original string and map each vowel to all BPE pieces."""
24
+ if encoded is None:
25
+ encoded = tokenizer(text, add_special_tokens=True, return_offsets_mapping=True,
26
+ return_attention_mask=False, truncation=False)
27
+ offsets = encoded['offset_mapping']
28
+ # Fast tokenizer byte pieces can share a character span: use both ends.
29
+ covering = {}
30
+ for token, (a, b) in enumerate(offsets):
31
+ for p in range(a, b):
32
+ if p in covering:
33
+ covering[p][1] = token
34
+ else:
35
+ covering[p] = [token, token]
36
+ words, candidates = [], []
37
+ for wi, w in enumerate(WORD.finditer(text)):
38
+ vowels = [p for p in range(w.start(), w.end()) if text[p].lower() in VOWELS]
39
+ if len(vowels) <= 1:
40
+ continue
41
+ if any(p not in covering for p in range(w.start(), w.end())):
42
+ raise ValueError('Tokenizer omitted characters inside a Russian word')
43
+ first, last = covering[w.start()][0], covering[w.end()-1][1]
44
+ off = len(candidates)
45
+ for vi, pos in enumerate(vowels):
46
+ a, b = covering[pos]
47
+ candidates.append((a, b, pos, VOWELS.index(text[pos].lower()),
48
+ (pos-w.start())/len(w.group()), (w.end()-pos-1)/len(w.group())))
49
+ label = int(target.get(wi, -100))
50
+ if label != -100 and not 0 <= label < len(vowels):
51
+ raise ValueError('Label outside candidate vowels')
52
+ words.append((first, last, off, len(vowels), label, w.start(), w.end()))
53
+ return encoded['input_ids'], words, candidates
54
+
55
+
56
+ def context_parts(text, tokenizer, limit=512):
57
+ """Split only on whitespace, never truncate or cut a Russian word."""
58
+ if len(tokenizer.encode(text, add_special_tokens=True, verbose=False)) <= limit:
59
+ return [(0, len(text))]
60
+ spans = [(m.start(), m.end()) for m in re.finditer(r'\S+', text)]
61
+ parts = []
62
+ start_word = 0
63
+ while start_word < len(spans):
64
+ lo, hi = start_word+1, len(spans)
65
+ best = None
66
+ while lo <= hi:
67
+ mid = (lo+hi)//2
68
+ a = 0 if start_word == 0 else spans[start_word][0]
69
+ b = len(text) if mid == len(spans) else spans[mid][0]
70
+ if len(tokenizer.encode(text[a:b], add_special_tokens=True, verbose=False)) <= limit:
71
+ best = (a, b, mid); lo = mid+1
72
+ else:
73
+ hi = mid-1
74
+ if best is None:
75
+ raise ValueError('One unbroken word exceeds the encoder context')
76
+ a, b, start_word = best
77
+ parts.append((a, b))
78
+ assert ''.join(text[a:b] for a, b in parts) == text
79
+ return parts
config.json ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "AccentikModel"
4
+ ],
5
+ "auto_map": {
6
+ "AutoConfig": "configuration_accentik.AccentikConfig",
7
+ "AutoModel": "modeling_accentik.AccentikModel"
8
+ },
9
+ "batch_token_budget": 8192,
10
+ "context_window": 512,
11
+ "dtype": "float32",
12
+ "encoder_config": {
13
+ "accentor_local_head": true,
14
+ "activation_function": "gelu",
15
+ "allow_embedding_resizing": true,
16
+ "architectures": [
17
+ "ModernBertForMaskedLM"
18
+ ],
19
+ "attention_bias": false,
20
+ "attention_dropout": 0.0,
21
+ "attention_layer": "rope",
22
+ "attention_probs_dropout_prob": 0.0,
23
+ "attn_out_bias": false,
24
+ "attn_out_dropout_prob": 0.1,
25
+ "attn_qkv_bias": false,
26
+ "bert_layer": "prenorm",
27
+ "bos_token_id": 50281,
28
+ "chunk_size_feed_forward": 0,
29
+ "classifier_activation": "gelu",
30
+ "classifier_bias": false,
31
+ "classifier_dropout": 0.0,
32
+ "classifier_pooling": "cls",
33
+ "cls_token_id": 50281,
34
+ "compile_model": true,
35
+ "decoder_bias": true,
36
+ "deterministic_flash_attn": false,
37
+ "dtype": "float32",
38
+ "embed_dropout_prob": 0.0,
39
+ "embed_norm": true,
40
+ "embedding_dropout": 0.0,
41
+ "embedding_layer": "sans_pos",
42
+ "eos_token_id": 50282,
43
+ "final_norm": true,
44
+ "global_attn_every_n_layers": 3,
45
+ "head_pred_act": "gelu",
46
+ "hidden_act": "gelu",
47
+ "hidden_activation": "gelu",
48
+ "hidden_size": 384,
49
+ "id2label": {
50
+ "0": "LABEL_0",
51
+ "1": "LABEL_1"
52
+ },
53
+ "init_method": "full_megatron",
54
+ "initializer_cutoff_factor": 2.0,
55
+ "initializer_range": 0.02,
56
+ "intermediate_size": 576,
57
+ "is_encoder_decoder": false,
58
+ "label2id": {
59
+ "LABEL_0": 0,
60
+ "LABEL_1": 1
61
+ },
62
+ "layer_types": [
63
+ "full_attention",
64
+ "sliding_attention",
65
+ "sliding_attention",
66
+ "full_attention",
67
+ "sliding_attention",
68
+ "sliding_attention",
69
+ "full_attention",
70
+ "sliding_attention",
71
+ "sliding_attention",
72
+ "full_attention",
73
+ "sliding_attention",
74
+ "sliding_attention"
75
+ ],
76
+ "local_attention": 256,
77
+ "local_attn_rotary_emb_base": 10000.0,
78
+ "loss_function": "fa_cross_entropy",
79
+ "loss_kwargs": {
80
+ "reduction": "mean"
81
+ },
82
+ "masked_prediction": true,
83
+ "max_position_embeddings": 8192,
84
+ "mlp_bias": false,
85
+ "mlp_dropout": 0.0,
86
+ "mlp_dropout_prob": 0.0,
87
+ "mlp_in_bias": false,
88
+ "mlp_layer": "glu",
89
+ "mlp_out_bias": false,
90
+ "model_type": "modernbert",
91
+ "norm_bias": false,
92
+ "norm_eps": 1e-05,
93
+ "norm_kwargs": {
94
+ "bias": false,
95
+ "eps": 1e-05
96
+ },
97
+ "normalization": "layernorm",
98
+ "num_attention_heads": 6,
99
+ "num_hidden_layers": 12,
100
+ "output_attentions": false,
101
+ "output_hidden_states": false,
102
+ "pad_token_id": 50283,
103
+ "padding": "unpadded",
104
+ "problem_type": null,
105
+ "repad_logits_with_grad": false,
106
+ "return_dict": true,
107
+ "rope_parameters": {
108
+ "full_attention": {
109
+ "rope_theta": 160000.0,
110
+ "rope_type": "default"
111
+ },
112
+ "sliding_attention": {
113
+ "rope_theta": 10000.0,
114
+ "rope_type": "default"
115
+ }
116
+ },
117
+ "rotary_emb_base": 160000.0,
118
+ "rotary_emb_dim": null,
119
+ "rotary_emb_interleaved": false,
120
+ "rotary_emb_scale_base": null,
121
+ "sep_token_id": 50282,
122
+ "skip_first_prenorm": true,
123
+ "sparse_pred_ignore_index": -100,
124
+ "sparse_prediction": false,
125
+ "tie_word_embeddings": true,
126
+ "transformers_version": "5.15.1",
127
+ "unpad_embeddings": true,
128
+ "vocab_size": 50368
129
+ },
130
+ "model_type": "accentik",
131
+ "release": "experimental-context-v2",
132
+ "tie_word_embeddings": false,
133
+ "transformers_version": "5.15.1"
134
+ }
configuration_accentik.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Configuration for Accentik's parallel Russian vowel-ranking model."""
2
+ from copy import deepcopy
3
+
4
+ from transformers import PreTrainedConfig
5
+
6
+
7
+ class AccentikConfig(PreTrainedConfig):
8
+ model_type = "accentik"
9
+
10
+ def __init__(self, encoder_config=None, context_window=512,
11
+ batch_token_budget=8192, release="experimental-context-v2", **kwargs):
12
+ super().__init__(**kwargs)
13
+ self.encoder_config = deepcopy(encoder_config or {})
14
+ self.context_window = int(context_window)
15
+ self.batch_token_budget = int(batch_token_budget)
16
+ self.release = release
17
+ if self.context_window < 8 or self.context_window % 8:
18
+ raise ValueError("context_window must be a positive multiple of eight")
19
+ if self.batch_token_budget < self.context_window:
20
+ raise ValueError("batch_token_budget must fit one complete context window")
evaluation.json ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "release": "experimental-context-v2",
3
+ "status": "experimental, explicitly approved for public release",
4
+ "parameters": 34851578,
5
+ "weights_sha256": "8e0be4ecb637a35c4f152e2cb887bca8e95ab1f1b17daa4f62a1706331f7aa3e",
6
+ "source_checkpoint_sha256": "d406729a83a1cd65e17431a5ec642f32d26df089238f694d0fbf2d8ae34052da",
7
+ "selection_step": 200,
8
+ "manual_homograph_contexts": {
9
+ "original": {
10
+ "correct": 6532,
11
+ "total": 7673,
12
+ "accuracy": 0.8512967548546853,
13
+ "macro_accuracy": 0.8031411032280502
14
+ },
15
+ "experimental_context_v2": {
16
+ "correct": 7482,
17
+ "total": 7673,
18
+ "accuracy": 0.975107519874886,
19
+ "macro_accuracy": 0.9637305667643875
20
+ }
21
+ },
22
+ "general_russtress": {
23
+ "targets": 11089,
24
+ "original_correct": 10507,
25
+ "original_accuracy": 0.9475155559563532,
26
+ "experimental_correct": 10435,
27
+ "experimental_accuracy": 0.9410226350437371,
28
+ "native_ruaccent_correct": 10551,
29
+ "native_ruaccent_accuracy": 0.9514834520696186,
30
+ "reference": {
31
+ "repository": "MashaPo/russtress",
32
+ "revision": "8c46eac0750b1cd6eabcd5c380091d36d9aec7c4",
33
+ "path": "data/data_ru.csv",
34
+ "url": "https://raw.githubusercontent.com/MashaPo/russtress/8c46eac0750b1cd6eabcd5c380091d36d9aec7c4/data/data_ru.csv",
35
+ "sha256": "4e5002a50c375db6805ada2a8b4178997b4b481affc261dce4189ce5a93b8129"
36
+ },
37
+ "single_vowel_targets_excluded": 35
38
+ },
39
+ "supplied_dialogue_agreement": {
40
+ "original": {
41
+ "correct": 50069,
42
+ "words": 50564,
43
+ "accuracy": 0.9902104263903172,
44
+ "cross_entropy": 0.06666617238458393,
45
+ "mean_confidence": 0.99781406469883
46
+ },
47
+ "experimental": {
48
+ "correct": 49915,
49
+ "words": 50564,
50
+ "accuracy": 0.9871647812673048,
51
+ "cross_entropy": 0.057279157993131664,
52
+ "mean_confidence": 0.9928592729866481
53
+ },
54
+ "independent_human_benchmark": false
55
+ },
56
+ "targeted_diagnostics": {
57
+ "original_correct": 4,
58
+ "experimental_correct": 9,
59
+ "targets": 10,
60
+ "representative_benchmark": false,
61
+ "repeatedly_inspected_during_development": true
62
+ },
63
+ "internal_promotion_gate": {
64
+ "passed": false,
65
+ "gates": {
66
+ "manual_context_macro_improved": true,
67
+ "manual_context_micro_improved": true,
68
+ "external_no_material_regression": false,
69
+ "dialogue_no_material_regression": true,
70
+ "reported_lowercase_castle_lock_fixed": true,
71
+ "contextual_regressions_improved": true,
72
+ "cpu_reported_case_fixed": true,
73
+ "cpu_source_preserved": true,
74
+ "source_preserved": true
75
+ },
76
+ "note": "General accuracy regressed; released as an experimental alternative by explicit author approval."
77
+ },
78
+ "base_model": {
79
+ "model": "deepvk/RuModernBERT-small",
80
+ "revision": "cdfe49388e1f5261c69e4b1c8c41fa9e60b9ebef",
81
+ "branch": "patched-tokenizer"
82
+ },
83
+ "manual_training_context_source": {
84
+ "repository": "https://github.com/iv-stoliar/RussianHomographDataset",
85
+ "revision": "d8f65f2004a6ae0bbc0dc9bd70801a63d6a00ff3",
86
+ "license": "GPL-3.0"
87
+ },
88
+ "additional_training_sources": [
89
+ {
90
+ "repository": "TeraTTS/open_accent",
91
+ "revision": "19842ee44cd6f2d098ca7e6eeb36848e6e261410",
92
+ "license": "mit"
93
+ },
94
+ {
95
+ "repository": "TeraTTS/stress_dataset_sft_poetry",
96
+ "revision": "ab82b64cb446366894e273ff0abf5177d33479d6",
97
+ "license": "mit"
98
+ },
99
+ {
100
+ "repository": "TeraTTS/stress_dataset_sft_proza",
101
+ "revision": "c1277f16ec3f70f11d711163bb5b622a17a8bd77",
102
+ "license": "mit"
103
+ }
104
+ ],
105
+ "methodology_limits": [
106
+ "Validation selected checkpoint; fixed tests were reused during development.",
107
+ "Exact benchmark contexts excluded from refinement; overlap with original pseudo-label corpus unknown.",
108
+ "Teacher pretraining overlap with external references unknown.",
109
+ "No training texts redistributed in this model repository."
110
+ ]
111
+ }
inference.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Run Accentik from Hugging Face or a downloaded model folder."""
2
+ import argparse
3
+ from pathlib import Path
4
+
5
+ import torch
6
+ from transformers import AutoModel
7
+
8
+
9
+ def main():
10
+ parser = argparse.ArgumentParser(description=__doc__)
11
+ parser.add_argument("--model", default="TeraSpace/accentik")
12
+ parser.add_argument("--revision", help="Optional pinned Hub commit")
13
+ parser.add_argument("--device", default="cpu")
14
+ parser.add_argument("--threads", type=int, default=2)
15
+ inputs = parser.add_mutually_exclusive_group(required=True)
16
+ inputs.add_argument("--text")
17
+ inputs.add_argument("--input", type=Path)
18
+ parser.add_argument("--output", type=Path)
19
+ args = parser.parse_args()
20
+ torch.set_num_threads(args.threads)
21
+ model = AutoModel.from_pretrained(
22
+ args.model, revision=args.revision, trust_remote_code=True,
23
+ ).to(args.device).eval()
24
+ text = args.text if args.text is not None else args.input.read_text(encoding="utf-8")
25
+ result = model.accentuate(text)
26
+ if args.output:
27
+ args.output.write_text(result, encoding="utf-8")
28
+ else:
29
+ print(result)
30
+
31
+
32
+ if __name__ == "__main__":
33
+ main()
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8e0be4ecb637a35c4f152e2cb887bca8e95ab1f1b17daa4f62a1706331f7aa3e
3
+ size 139414960
modeling_accentik.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """PyTorch/Transformers implementation of Accentik experimental context v2."""
2
+ from dataclasses import dataclass
3
+ from typing import Optional
4
+
5
+ import torch
6
+ from torch import nn
7
+ from transformers import AutoConfig, AutoModel, PreTrainedModel
8
+ from transformers.utils import ModelOutput
9
+
10
+ from .configuration_accentik import AccentikConfig
11
+ from .accentik_inference import AccentikInferenceMixin
12
+
13
+
14
+ @dataclass
15
+ class AccentikOutput(ModelOutput):
16
+ """One row per multi-vowel Russian word, one column per candidate vowel."""
17
+ loss: Optional[torch.Tensor] = None
18
+ logits: Optional[torch.Tensor] = None
19
+
20
+
21
+ class AccentikModel(AccentikInferenceMixin, PreTrainedModel):
22
+ config_class = AccentikConfig
23
+ base_model_prefix = "encoder"
24
+ main_input_name = "tokens"
25
+ _supports_sdpa = True
26
+
27
+ def __init__(self, config):
28
+ super().__init__(config)
29
+ cfg = dict(config.encoder_config)
30
+ kind = cfg.pop("model_type")
31
+ cfg.pop("accentor_local_head", None)
32
+ cfg["reference_compile"] = False
33
+ self.encoder = AutoModel.from_config(
34
+ AutoConfig.for_model(kind, **cfg), attn_implementation="sdpa"
35
+ )
36
+ h = self.encoder.config.hidden_size
37
+ self.letter = nn.Embedding(10, 12)
38
+ self.rank = nn.Embedding(64, 12)
39
+ self.count = nn.Embedding(64, 12)
40
+ self.head = nn.Sequential(
41
+ nn.Linear(h * 2 + 40, 192), nn.GELU(), nn.Dropout(0.1), nn.Linear(192, 1)
42
+ )
43
+ self.local_head = nn.Sequential(
44
+ nn.Linear(h * 4 + 40, 192), nn.GELU(), nn.Dropout(0.1), nn.Linear(192, 1)
45
+ )
46
+ self.post_init()
47
+
48
+ @classmethod
49
+ def from_pretrained(cls, pretrained_model_name_or_path, *args, **kwargs):
50
+ loaded = super().from_pretrained(pretrained_model_name_or_path, *args, **kwargs)
51
+ model = loaded[0] if isinstance(loaded, tuple) else loaded
52
+ model._accentik_source = str(pretrained_model_name_or_path)
53
+ # Keep the tokenizer pinned to the same Hub snapshot as the weights.
54
+ options = {key: kwargs[key] for key in
55
+ ("cache_dir", "local_files_only", "token", "subfolder", "force_download")
56
+ if key in kwargs}
57
+ revision = getattr(model.config, "_commit_hash", None) or kwargs.get("revision")
58
+ if revision is not None:
59
+ options["revision"] = revision
60
+ model._accentik_tokenizer_options = options
61
+ return loaded
62
+
63
+ def forward(self, tokens, attention, word_first, word_last, candidate_first,
64
+ candidate_last, letters, rank, count, scalars, valid,
65
+ labels=None, return_dict=True):
66
+ """Score candidate vowels; use accentuate()/annotate() for ordinary text.
67
+
68
+ Feature indices are flattened across the padded token batch. The supplied
69
+ inference helpers construct these features without truncating words.
70
+ """
71
+ hidden = self.encoder(input_ids=tokens, attention_mask=attention).last_hidden_state
72
+ flat = hidden.reshape(-1, hidden.shape[-1])
73
+ width = tokens.shape[1]
74
+ word = (flat[word_first] + flat[word_last]) * 0.5
75
+ vowel = (flat[candidate_first] + flat[candidate_last]) * 0.5
76
+ features = torch.cat((
77
+ vowel, word[:, None, :].expand_as(vowel), self.letter(letters),
78
+ self.rank(rank), self.count(count), scalars.to(vowel.dtype)
79
+ ), dim=-1)
80
+ begin = (word_first // width) * width
81
+ end = begin + width - 1
82
+ mask = attention.reshape(-1)
83
+ neighbors = []
84
+ for anchor, direction in [(word_first, -1), (word_last, 1)]:
85
+ offsets = torch.tensor([1, 2, 3], device=tokens.device)
86
+ indices = (anchor[:, None] + direction * offsets).maximum(
87
+ begin[:, None]).minimum(end[:, None])
88
+ weights = mask[indices].to(flat.dtype)
89
+ neighbors.append((flat[indices] * weights[:, :, None]).sum(1) /
90
+ weights.sum(1).clamp_min(1)[:, None])
91
+ local = torch.cat((
92
+ features, neighbors[0][:, None, :].expand_as(vowel),
93
+ neighbors[1][:, None, :].expand_as(vowel)
94
+ ), dim=-1)
95
+ logits = (self.head(features).squeeze(-1).float() +
96
+ self.local_head(local).squeeze(-1).float())
97
+ logits = logits.masked_fill(~valid, -1e9)
98
+ loss = None
99
+ if labels is not None:
100
+ if (labels != -100).any():
101
+ loss = nn.functional.cross_entropy(logits, labels, ignore_index=-100)
102
+ else:
103
+ loss = logits.sum() * 0.0
104
+ if not return_dict:
105
+ return (loss, logits) if loss is not None else (logits,)
106
+ return AccentikOutput(loss=loss, logits=logits)
release_manifest.json ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "release": "experimental-context-v2",
3
+ "parameters": 34851578,
4
+ "tensor_precision": "float32",
5
+ "source_checkpoint_sha256": "d406729a83a1cd65e17431a5ec642f32d26df089238f694d0fbf2d8ae34052da",
6
+ "files": {
7
+ "LICENSE": {
8
+ "sha256": "60cdc5a7bdc8928a0580d8101c83830278aaa12cdd466251e97f1a1ef392381c",
9
+ "bytes": 5238
10
+ },
11
+ "LICENSE-APACHE-2.0": {
12
+ "sha256": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
13
+ "bytes": 11358
14
+ },
15
+ "LICENSE-OPENRAIL": {
16
+ "sha256": "0d944a9110fed9a9602d60e0423a272903e7bd21ab060490774efc77c2275e9f",
17
+ "bytes": 15007
18
+ },
19
+ "NOTICE": {
20
+ "sha256": "a4dc2e8cdd693187272ecc287ff7fd5517accfa0a64955b0b3df3956066a0457",
21
+ "bytes": 1358
22
+ },
23
+ "README.md": {
24
+ "sha256": "50aa45874a444c27eb46890ca3594adbbee275c981c19d5afe21b1305929c1e1",
25
+ "bytes": 9864
26
+ },
27
+ "accentik_features.py": {
28
+ "sha256": "225818fd735d8d9cee68353ee929c35653b4e8a7bf8c48d4d4d180ad6434ebce",
29
+ "bytes": 2334
30
+ },
31
+ "accentik_inference.py": {
32
+ "sha256": "d27420f296a5fe01f9002bb92fb811fe670c6c179727e3a34977f3e81edcb553",
33
+ "bytes": 5826
34
+ },
35
+ "accentik_text.py": {
36
+ "sha256": "cdbe0263cee84f590c1b6cc37939c169297a36b3af844b1fc0cd7d8c217d7686",
37
+ "bytes": 3246
38
+ },
39
+ "config.json": {
40
+ "sha256": "0e6caa1dfd026b7673887eabb9c30081366f883e819a297266797452b09f88de",
41
+ "bytes": 3582
42
+ },
43
+ "configuration_accentik.py": {
44
+ "sha256": "6e171612c15849379a49418cc78aee3a901178e89de2af1008f05558acdc9d2d",
45
+ "bytes": 893
46
+ },
47
+ "evaluation.json": {
48
+ "sha256": "63c52751ce883f484b45ae7302bf5fb9dac6ff531cadc0edc29770f65fef0268",
49
+ "bytes": 3879
50
+ },
51
+ "inference.py": {
52
+ "sha256": "6a2544fc70272489ffb3567b019deb7df9d8b2f44befb52c85bc0a0e4c916b96",
53
+ "bytes": 1149
54
+ },
55
+ "model.safetensors": {
56
+ "sha256": "8e0be4ecb637a35c4f152e2cb887bca8e95ab1f1b17daa4f62a1706331f7aa3e",
57
+ "bytes": 139414960
58
+ },
59
+ "modeling_accentik.py": {
60
+ "sha256": "54d4b79235b4e715c9540a2e2154d21a9d1f3f42ec1de9c47002f69da775ae86",
61
+ "bytes": 4702
62
+ },
63
+ "requirements.txt": {
64
+ "sha256": "bb0103c01f47cb453da2a1b51b9ef4976e22cf2d7640681c2bafa6868f4aa09b",
65
+ "bytes": 66
66
+ },
67
+ "tokenizer.json": {
68
+ "sha256": "a648622822a938fca174540ec4bf7753541cb4130aaf52b120b186d33fae6ee6",
69
+ "bytes": 4752657
70
+ },
71
+ "tokenizer_config.json": {
72
+ "sha256": "8ffb7a7dfa44bde33ceaf6824a840042fed4e408921e8208f0233a5d43c3a53b",
73
+ "bytes": 377
74
+ }
75
+ }
76
+ }
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ torch>=2.6
2
+ transformers>=5.15.1,<6
3
+ safetensors>=0.4.3
4
+ numpy>=1.24
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "backend": "tokenizers",
3
+ "clean_up_tokenization_spaces": true,
4
+ "cls_token": "[CLS]",
5
+ "is_local": true,
6
+ "local_files_only": true,
7
+ "mask_token": "[MASK]",
8
+ "model_input_names": [
9
+ "input_ids",
10
+ "attention_mask"
11
+ ],
12
+ "model_max_length": 512,
13
+ "pad_token": "[PAD]",
14
+ "sep_token": "[SEP]",
15
+ "tokenizer_class": "TokenizersBackend",
16
+ "unk_token": "[UNK]"
17
+ }