Text-to-Speech
Transformers
Safetensors
arktts
feature-extraction
audio
tts
voice-cloning
zero-shot
multilingual
custom_code
Instructions to use Edge0/Audio8-TTS-Realistic-Air with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Edge0/Audio8-TTS-Realistic-Air with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-to-speech", model="Edge0/Audio8-TTS-Realistic-Air", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Edge0/Audio8-TTS-Realistic-Air", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Prepare Audio8-TTS-Realistic-0.6B private release
Browse filesComplete inference package, model card, multilingual evaluation, and verified SGLang Omni compatibility.
- .gitattributes +1 -0
- LICENSE +201 -0
- NOTICE +4 -0
- README.md +303 -0
- audio8-banner.jpeg +0 -0
- codec.pth +3 -0
- config.json +64 -0
- configuration_arktts.py +114 -0
- generation_config.json +10 -0
- model.safetensors +3 -0
- modeling_arktts.py +831 -0
- modeling_arktts_codec.py +520 -0
- preprocessor_config.json +10 -0
- processing_arktts.py +259 -0
- processor_config.json +9 -0
- special_tokens_map.json +0 -0
- tokenizer.json +3 -0
- tokenizer_config.json +0 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* 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
|
|
|
|
|
|
| 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
|
| 36 |
+
tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright [yyyy] [name of copyright owner]
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
NOTICE
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
audio8_tts
|
| 2 |
+
|
| 3 |
+
The model architecture is inspired by the DualAR design used in Fish Audio
|
| 4 |
+
S2 Pro.
|
README.md
CHANGED
|
@@ -1,3 +1,306 @@
|
|
| 1 |
---
|
| 2 |
license: apache-2.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
license: apache-2.0
|
| 3 |
+
language:
|
| 4 |
+
- zh
|
| 5 |
+
- en
|
| 6 |
+
- ja
|
| 7 |
+
- ko
|
| 8 |
+
- de
|
| 9 |
+
- fr
|
| 10 |
+
- es
|
| 11 |
+
- it
|
| 12 |
+
- ru
|
| 13 |
+
- ar
|
| 14 |
+
- fi
|
| 15 |
+
- hi
|
| 16 |
+
- id
|
| 17 |
+
- ms
|
| 18 |
+
- nl
|
| 19 |
+
- "no"
|
| 20 |
+
- pl
|
| 21 |
+
- pt
|
| 22 |
+
- sv
|
| 23 |
+
- th
|
| 24 |
+
- tr
|
| 25 |
+
- vi
|
| 26 |
+
- yue
|
| 27 |
+
library_name: transformers
|
| 28 |
+
pipeline_tag: text-to-speech
|
| 29 |
+
base_model: Edge0/Audio8-TTS-Preview-0.6b
|
| 30 |
+
tags:
|
| 31 |
+
- audio
|
| 32 |
+
- text-to-speech
|
| 33 |
+
- tts
|
| 34 |
+
- voice-cloning
|
| 35 |
+
- zero-shot
|
| 36 |
+
- multilingual
|
| 37 |
---
|
| 38 |
+
|
| 39 |
+
<div align="center">
|
| 40 |
+
|
| 41 |
+
<img src="./audio8-banner.jpeg" alt="Audio8" width="760">
|
| 42 |
+
|
| 43 |
+
# Audio8-TTS-Realistic-0.6B
|
| 44 |
+
|
| 45 |
+
**More realistic speech. More languages. Still 0.6B.**
|
| 46 |
+
|
| 47 |
+
[](https://github.com/Edge0-AI/Audio8_TTS)
|
| 48 |
+
[](./LICENSE)
|
| 49 |
+

|
| 50 |
+
|
| 51 |
+
</div>
|
| 52 |
+
|
| 53 |
+
**Audio8-TTS-Realistic-0.6B** builds on [Audio8-TTS-Preview-0.6b](https://huggingface.co/Edge0/Audio8-TTS-Preview-0.6b), delivering **substantially improved speech realism** while retaining its compact **0.6B** architecture and zero-shot voice cloning capabilities.
|
| 54 |
+
|
| 55 |
+
This release supports **22 languages plus Cantonese**, adding **12 languages** to Preview's coverage. It uses the same Transformers inference interface as Preview. This repository includes the complete model checkpoint, its 44.1 kHz neural audio codec, tokenizer, processor, and custom Transformers code.
|
| 56 |
+
|
| 57 |
+
## Highlights
|
| 58 |
+
|
| 59 |
+
- **Improved realism:** more natural prosody and expressive delivery, building on Preview.
|
| 60 |
+
- **Broader multilingual coverage:** 22 languages plus Cantonese, with 12 newly supported languages.
|
| 61 |
+
- **Compact model:** 601 million main-model parameters, with the audio codec bundled separately.
|
| 62 |
+
- **Flexible generation:** zero-shot voice cloning from a reference recording and transcript, or speech generation without a reference.
|
| 63 |
+
- **GPU serving:** an SGLang Omni adapter with paged attention, dynamic batching, and an OpenAI-compatible speech API.
|
| 64 |
+
|
| 65 |
+
## Supported Languages
|
| 66 |
+
|
| 67 |
+
| Coverage | Languages |
|
| 68 |
+
|---|---|
|
| 69 |
+
| Retained from Preview | Chinese, Dutch, English, French, German, Italian, Japanese, Korean, Polish, Spanish |
|
| 70 |
+
| Newly supported | Arabic, Finnish, Hindi, Indonesian, Malay, Norwegian, Portuguese, Russian, Swedish, Thai, Turkish, Vietnamese |
|
| 71 |
+
| Also retained | Cantonese |
|
| 72 |
+
|
| 73 |
+
Language quality varies; see the per-language evaluation below. Benchmark coverage includes some additional languages and is distinct from the supported-language list.
|
| 74 |
+
|
| 75 |
+
## Model Details
|
| 76 |
+
|
| 77 |
+
Audio8 TTS uses a DualAR architecture inspired by [Fish Audio S2 Pro](https://github.com/fishaudio/fish-speech). The slow AR transformer predicts one semantic token for each audio frame. The fast AR transformer predicts the frame's codec codebooks, conditioned on the slow hidden state and preceding codebooks.
|
| 78 |
+
|
| 79 |
+
| Component | Configuration |
|
| 80 |
+
|---|---|
|
| 81 |
+
| Main model | 601,159,424 parameters, excluding the codec |
|
| 82 |
+
| Slow AR | 24 layers, width 896, 14 attention heads, 2 KV heads |
|
| 83 |
+
| Fast AR | 4 layers, width 896, 14 attention heads, 2 KV heads |
|
| 84 |
+
| Acoustic tokens | 10 codebooks, 4,096 entries per codebook |
|
| 85 |
+
| Codec | 44.1 kHz, 2,048 samples per model frame (~21.5 frames/s) |
|
| 86 |
+
| Context | Up to 2,048 packed text/audio positions |
|
| 87 |
+
|
| 88 |
+
The bundled codec handles both reference-audio encoding and waveform decoding, so no additional codec checkpoint is required. The **0.6B** size refers to the main DualAR model; the codec is excluded from that parameter count.
|
| 89 |
+
|
| 90 |
+
## Installation
|
| 91 |
+
|
| 92 |
+
Python 3.10 or newer and a CUDA-capable GPU are recommended.
|
| 93 |
+
|
| 94 |
+
```bash
|
| 95 |
+
pip install "torch>=2.5.0" "torchaudio>=2.5.0" \
|
| 96 |
+
"transformers>=4.57.0,<5" "soundfile>=0.12" "safetensors>=0.4"
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
The Transformers release package was smoke-tested with PyTorch 2.8.0 and Transformers 4.57.3 on an NVIDIA H20 using BF16. Checks covered model loading, English and Chinese generation with and without reference audio, and generation in Arabic, Cantonese, Portuguese, and Russian. These are runtime checks, separate from the benchmark results below.
|
| 100 |
+
|
| 101 |
+
## Usage
|
| 102 |
+
|
| 103 |
+
The model uses custom Transformers code. Review the files in this repository, then load it with `trust_remote_code=True`.
|
| 104 |
+
|
| 105 |
+
### Zero-shot voice cloning
|
| 106 |
+
|
| 107 |
+
Use a clean reference recording. Its transcript must match the spoken content exactly.
|
| 108 |
+
|
| 109 |
+
```python
|
| 110 |
+
import soundfile as sf
|
| 111 |
+
import torch
|
| 112 |
+
from transformers import AutoModel, AutoProcessor
|
| 113 |
+
|
| 114 |
+
model_id = "Edge0/Audio8-TTS-Realistic-0.6b"
|
| 115 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 116 |
+
dtype = torch.bfloat16 if device == "cuda" else torch.float32
|
| 117 |
+
|
| 118 |
+
processor = AutoProcessor.from_pretrained(model_id, trust_remote_code=True)
|
| 119 |
+
model = AutoModel.from_pretrained(
|
| 120 |
+
model_id,
|
| 121 |
+
trust_remote_code=True,
|
| 122 |
+
dtype=dtype,
|
| 123 |
+
).eval().to(device)
|
| 124 |
+
|
| 125 |
+
inputs = processor(
|
| 126 |
+
text=["Welcome to Audio8 TTS. It is wonderful to hear from you."],
|
| 127 |
+
reference_audio=["reference.wav"],
|
| 128 |
+
reference_text=["The exact transcript of the reference recording."],
|
| 129 |
+
return_tensors="pt",
|
| 130 |
+
)
|
| 131 |
+
inputs = {name: value.to(device) for name, value in inputs.items()}
|
| 132 |
+
|
| 133 |
+
with torch.inference_mode():
|
| 134 |
+
output = model.generate(
|
| 135 |
+
**inputs,
|
| 136 |
+
max_new_tokens=1024,
|
| 137 |
+
temperature=0.8,
|
| 138 |
+
top_p=0.95,
|
| 139 |
+
top_k=50,
|
| 140 |
+
do_sample=True,
|
| 141 |
+
return_dict_in_generate=True,
|
| 142 |
+
)
|
| 143 |
+
waveforms, waveform_lengths = model.decode_audio(output.codes)
|
| 144 |
+
|
| 145 |
+
audio = waveforms[0, : int(waveform_lengths[0])].float().cpu().numpy()
|
| 146 |
+
sf.write("output.wav", audio, model.config.codec_sample_rate)
|
| 147 |
+
```
|
| 148 |
+
|
| 149 |
+
### Generation without a reference
|
| 150 |
+
|
| 151 |
+
Replace the processor call above with:
|
| 152 |
+
|
| 153 |
+
```python
|
| 154 |
+
inputs = processor(
|
| 155 |
+
text=["This utterance does not use a reference voice."],
|
| 156 |
+
return_tensors="pt",
|
| 157 |
+
)
|
| 158 |
+
```
|
| 159 |
+
|
| 160 |
+
Then move the inputs to the model device and run the same generation and decoding steps. For command-line inference, batching, and supervised fine-tuning, see the [Audio8 TTS repository](https://github.com/Edge0-AI/Audio8_TTS), using `Edge0/Audio8-TTS-Realistic-0.6b` as the model path.
|
| 161 |
+
|
| 162 |
+
## High-throughput serving: SGLang Omni
|
| 163 |
+
|
| 164 |
+
The [SGLang Omni adapter](https://github.com/Edge0-AI/Audio8_TTS/tree/master/sglang_omni) provides GPU serving for Audio8 TTS. It is installed as an independent model plugin and does not overwrite SGLang Omni core files.
|
| 165 |
+
|
| 166 |
+
| Capability | Support |
|
| 167 |
+
|---|---|
|
| 168 |
+
| Attention and batching | SGLang paged attention and dynamic batching |
|
| 169 |
+
| DualAR execution | Slow AR serving with a fixed KV cache for the Fast AR codebook decoder |
|
| 170 |
+
| Voice cloning | Reference-audio encoding and waveform decoding |
|
| 171 |
+
| API | OpenAI-compatible `/v1/audio/speech` service |
|
| 172 |
+
|
| 173 |
+
The adapter targets SGLang Omni revision `68a572348837f7b004857b4b07993c20ade4c017`, with PyTorch 2.9.1, SGLang 0.5.8, and Transformers 4.57.1. Follow the [deployment instructions](https://github.com/Edge0-AI/Audio8_TTS#sglang-omni-serving) for installation, configuration, and API examples, and set the model path to `Edge0/Audio8-TTS-Realistic-0.6b` or its downloaded snapshot. The adapter supports generation without a reference and zero-shot voice cloning.
|
| 174 |
+
|
| 175 |
+
Use the **0.6B** configuration, `sglang_omni/configs/audio8_tts_0_6b.yaml`. This Realistic checkpoint passed non-streaming `/v1/audio/speech` smoke tests both with and without a reference on an NVIDIA H20, using the versions above and the adapter from Audio8_TTS commit `07e40f5d0b03fc473635ef378654bfb581027ac3`. These checks used BF16 with Torch compilation and CUDA graphs disabled; they validate compatibility, not throughput or streaming performance.
|
| 176 |
+
|
| 177 |
+
## Evaluation
|
| 178 |
+
|
| 179 |
+
Error rates are percentages (**lower is better**). Speaker similarity (SIM) is on a **0–1 scale** (**higher is better**). These metrics measure intelligibility and speaker similarity; they do not directly measure perceived speech realism.
|
| 180 |
+
|
| 181 |
+
### Benchmark summary
|
| 182 |
+
|
| 183 |
+
Results for **Audio8-TTS-Realistic (0.6B)**:
|
| 184 |
+
|
| 185 |
+
| Benchmark | Coverage | Mean error % ↓ | Mean SIM ↑ |
|
| 186 |
+
|---|---|---:|---:|
|
| 187 |
+
| Seed-TTS | English / Chinese | **1.25** | **0.704** |
|
| 188 |
+
| CV3 | 9 core languages | **4.86** | **0.661** |
|
| 189 |
+
| MiniMax | 24 evaluated languages | **5.89** | **0.754** |
|
| 190 |
+
|
| 191 |
+
All three summary error rates are unweighted language macro averages. Seed-TTS averages English WER and Chinese CER, excluding hard Chinese. CV3 averages Chinese, English, Japanese, Korean, German, French, Spanish, Italian, and Russian, excluding hard subsets and the 13 additional languages. MiniMax averages all 24 evaluated languages, including languages outside the supported-language list.
|
| 192 |
+
|
| 193 |
+
### Reference comparison
|
| 194 |
+
|
| 195 |
+
The following error rates come from the sources used in our technical report. Different evaluation pipelines and text normalization make this a reference comparison rather than a strictly matched ranking. A dash means that a result is unavailable in the comparison, not that the model lacks support.
|
| 196 |
+
|
| 197 |
+
| Model | Parameters (B) | Seed-TTS EN/ZH error ↓ | CV3 (9) error ↓ | MiniMax (24) error ↓ | Result source |
|
| 198 |
+
|---|---:|---:|---:|---:|---|
|
| 199 |
+
| **Audio8-TTS-Realistic** | **0.6** | **1.25** | **4.86** | **5.89** | Our evaluation |
|
| 200 |
+
| Qwen3-TTS-12Hz-0.6B | 0.6 | 1.12 | — | — | Qwen report |
|
| 201 |
+
| Qwen3-TTS-1.7B | 1.7 | 1.01 | 7.73 | 27.41 | Qwen report / Boson evaluation |
|
| 202 |
+
| Higgs Audio v3 | 4 | 1.11 | 4.41 | 2.74 | Boson evaluation |
|
| 203 |
+
| Higgs Audio v2 | 4.7 | 1.17 | 4.49 | — | Our reevaluation |
|
| 204 |
+
| Fish S2 Pro | 4.6 | 1.32 | 4.49 | 4.40 | Our reevaluation / Fish report |
|
| 205 |
+
| MOSS-TTS-v1.5 | 8.5 | 1.52 | 6.11 | 3.78 | VoxCPM compilation / Boson evaluation |
|
| 206 |
+
| CosyVoice3-1.5B | 1.5 | 1.67 | 6.89 | — | VoxCPM compilation |
|
| 207 |
+
| VoxCPM2 | 2.3 | 1.41 | 5.35 | — | VoxCPM compilation |
|
| 208 |
+
|
| 209 |
+
Sources: [Qwen3-TTS report](https://arxiv.org/abs/2601.15621), [Boson AI Higgs Audio v3 benchmarks](https://huggingface.co/bosonai/higgs-audio-v3-tts-4b), [Fish Audio S2 report](https://arxiv.org/abs/2603.08823), and the [VoxCPM repository](https://github.com/OpenBMB/VoxCPM). Fish S2 Pro was reevaluated on Seed-TTS and CV3 because its official evaluation uses a different normalizer; Higgs Audio v2 was evaluated locally. Cross-project SIM values are omitted here because speaker encoders and evaluation conventions differ.
|
| 210 |
+
|
| 211 |
+
Parameter conventions also differ: Audio8's 0.6B excludes its separate codec; Qwen and CosyVoice use release labels; Higgs Audio v3 uses its nominal **4B** release label. The remaining sizes are rounded checkpoint counts used in our report (VoxCPM2 excludes its separate AudioVAE). They should not be interpreted as identically counted end-to-end system sizes.
|
| 212 |
+
|
| 213 |
+
### Seed-TTS detail
|
| 214 |
+
|
| 215 |
+
| Split | Metric | Error % ↓ | SIM ↑ |
|
| 216 |
+
|---|---|---:|---:|
|
| 217 |
+
| English | WER | 1.464 | 0.663 |
|
| 218 |
+
| Chinese | CER | 1.029 | 0.745 |
|
| 219 |
+
| Chinese, hard | CER | 12.797 | 0.702 |
|
| 220 |
+
|
| 221 |
+
### Multilingual detail
|
| 222 |
+
|
| 223 |
+
<details>
|
| 224 |
+
<summary>CV3: 22 languages and hard subsets</summary>
|
| 225 |
+
|
| 226 |
+
| Language | Metric | Error % ↓ | SIM ↑ |
|
| 227 |
+
|---|---|---:|---:|
|
| 228 |
+
| Chinese | CER | 1.814 | 0.6898 |
|
| 229 |
+
| English | WER | 2.907 | 0.6295 |
|
| 230 |
+
| Japanese | CER | 7.511 | 0.6659 |
|
| 231 |
+
| Korean | CER | 4.801 | 0.6962 |
|
| 232 |
+
| German | WER | 3.200 | 0.6669 |
|
| 233 |
+
| French | WER | 7.938 | 0.6355 |
|
| 234 |
+
| Spanish | WER | 3.389 | 0.6680 |
|
| 235 |
+
| Italian | WER | 4.933 | 0.6498 |
|
| 236 |
+
| Russian | WER | 7.258 | 0.6451 |
|
| 237 |
+
| Arabic | WER | 21.098 | 0.6122 |
|
| 238 |
+
| Finnish | WER | 32.279 | 0.7028 |
|
| 239 |
+
| Hindi | WER | 21.714 | 0.7245 |
|
| 240 |
+
| Indonesian | WER | 13.743 | 0.6682 |
|
| 241 |
+
| Malay | WER | 17.798 | 0.6710 |
|
| 242 |
+
| Dutch | WER | 10.873 | 0.6918 |
|
| 243 |
+
| Norwegian | WER | 17.961 | 0.6905 |
|
| 244 |
+
| Polish | WER | 19.233 | 0.6738 |
|
| 245 |
+
| Portuguese | WER | 6.871 | 0.6772 |
|
| 246 |
+
| Swedish | WER | 19.252 | 0.7104 |
|
| 247 |
+
| Thai | WER | 48.574 | 0.6691 |
|
| 248 |
+
| Turkish | WER | 12.567 | 0.6496 |
|
| 249 |
+
| Vietnamese | WER | 28.111 | 0.6790 |
|
| 250 |
+
|
| 251 |
+
Hard Chinese CER is **10.665%** and hard English WER is **4.736%**. These hard subsets are excluded from the 9-language summary. Each of the 22 regular language rows contains 500 evaluation items. Chinese, Japanese, and Korean use CER; the other regular language rows use WER.
|
| 252 |
+
|
| 253 |
+
</details>
|
| 254 |
+
|
| 255 |
+
<details>
|
| 256 |
+
<summary>MiniMax: all 24 evaluated languages</summary>
|
| 257 |
+
|
| 258 |
+
| Language | Metric | Error % ↓ | SIM ↑ |
|
| 259 |
+
|---|---|---:|---:|
|
| 260 |
+
| Arabic | CER | 2.403 | 0.7040 |
|
| 261 |
+
| Cantonese | CER | 31.349 | 0.7710 |
|
| 262 |
+
| Chinese | CER | 1.112 | 0.7950 |
|
| 263 |
+
| Czech † | WER | 8.628 | 0.7330 |
|
| 264 |
+
| Dutch | WER | 1.715 | 0.7050 |
|
| 265 |
+
| English | WER | 2.161 | 0.7740 |
|
| 266 |
+
| Finnish | WER | 5.072 | 0.7910 |
|
| 267 |
+
| French | WER | 3.198 | 0.6660 |
|
| 268 |
+
| German | WER | 0.898 | 0.7510 |
|
| 269 |
+
| Greek † | CER | 4.181 | 0.7910 |
|
| 270 |
+
| Hindi | CER | 9.209 | 0.8170 |
|
| 271 |
+
| Indonesian | WER | 2.128 | 0.7270 |
|
| 272 |
+
| Italian | WER | 1.958 | 0.7370 |
|
| 273 |
+
| Japanese | CER | 5.142 | 0.7890 |
|
| 274 |
+
| Korean | CER | 3.731 | 0.7850 |
|
| 275 |
+
| Polish | WER | 1.957 | 0.7890 |
|
| 276 |
+
| Portuguese | WER | 2.234 | 0.7860 |
|
| 277 |
+
| Romanian † | WER | 17.425 | 0.7330 |
|
| 278 |
+
| Russian | WER | 3.071 | 0.7490 |
|
| 279 |
+
| Spanish | WER | 1.350 | 0.7450 |
|
| 280 |
+
| Thai | CER | 6.674 | 0.7760 |
|
| 281 |
+
| Turkish | WER | 1.560 | 0.7650 |
|
| 282 |
+
| Ukrainian † | WER | 16.343 | 0.6940 |
|
| 283 |
+
| Vietnamese | CER | 7.966 | 0.7120 |
|
| 284 |
+
|
| 285 |
+
Each language has 100 evaluation items. Rows marked † are evaluated for completeness and are outside this release's supported-language list. The metric column preserves the evaluation suite's language-specific WER/CER choice. Cantonese is included in the 24-language average.
|
| 286 |
+
|
| 287 |
+
</details>
|
| 288 |
+
|
| 289 |
+
### Compared with Preview
|
| 290 |
+
|
| 291 |
+
In the same archived MiniMax evaluation pipeline, the 24-language mean error rate decreased from **29.637%** for Preview to **5.894%** for Realistic, while mean SIM increased from **0.6932** to **0.7535**. This is an average over the full benchmark, including languages beyond the supported list; improvements are not uniform across every language and split. For example, ordinary Seed-TTS Chinese CER was **0.943% → 1.029%** in that same evaluation series. Preview values in its original public model card were measured in an earlier evaluation and should not be mixed with this comparison.
|
| 292 |
+
|
| 293 |
+
The main release goal is improved speech realism alongside expanded language coverage. WER/CER and SIM alone do not establish a human preference score.
|
| 294 |
+
|
| 295 |
+
## Limitations and Responsible Use
|
| 296 |
+
|
| 297 |
+
- Quality varies by language and evaluation set. The detailed results include difficult cases, including Cantonese and some newly supported languages; the support list is not a claim of equal quality across languages.
|
| 298 |
+
- Noisy reference audio or mismatched transcripts can reduce stability and speaker similarity. The reference audio, transcript, and target text share a finite context budget; split long passages into manageable utterances.
|
| 299 |
+
- Speech may contain pronunciation errors, repetitions, omissions, or early stopping. Listen to generated audio before using it in a finished product.
|
| 300 |
+
- Obtain permission to clone a voice and disclose synthetic audio where appropriate.
|
| 301 |
+
|
| 302 |
+
## License and Acknowledgements
|
| 303 |
+
|
| 304 |
+
The code and model weights are released under the [Apache License 2.0](./LICENSE). See [NOTICE](./NOTICE) for attribution details.
|
| 305 |
+
|
| 306 |
+
We thank the Fish Audio team for publishing the DualAR architecture used in Fish Audio S2 Pro.
|
audio8-banner.jpeg
ADDED
|
codec.pth
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c310505aa11fe2f6cc63b8d3130dc7e77e73227774f5c62575769b1f47a8d048
|
| 3 |
+
size 1349857559
|
config.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"ArkttsModel"
|
| 4 |
+
],
|
| 5 |
+
"attention_o_bias": false,
|
| 6 |
+
"attention_qk_norm": false,
|
| 7 |
+
"attention_qkv_bias": true,
|
| 8 |
+
"auto_map": {
|
| 9 |
+
"AutoConfig": "configuration_arktts.ArkttsConfig",
|
| 10 |
+
"AutoModel": "modeling_arktts.ArkttsModel",
|
| 11 |
+
"AutoModelForCausalLM": "modeling_arktts.ArkttsModel"
|
| 12 |
+
},
|
| 13 |
+
"codebook_size": 4096,
|
| 14 |
+
"codec_filename": "codec.pth",
|
| 15 |
+
"codec_frame_size": 2048,
|
| 16 |
+
"codec_post_intermediate_size": 1216,
|
| 17 |
+
"codec_post_n_head": 16,
|
| 18 |
+
"codec_post_n_layer": 8,
|
| 19 |
+
"codec_post_n_local_heads": 8,
|
| 20 |
+
"codec_sample_rate": 44100,
|
| 21 |
+
"dim": 896,
|
| 22 |
+
"dropout": 0.0,
|
| 23 |
+
"dtype": "bfloat16",
|
| 24 |
+
"eos_token_id": 151645,
|
| 25 |
+
"fast_attention_o_bias": false,
|
| 26 |
+
"fast_attention_qk_norm": false,
|
| 27 |
+
"fast_attention_qkv_bias": false,
|
| 28 |
+
"fast_dim": 896,
|
| 29 |
+
"fast_head_dim": 64,
|
| 30 |
+
"fast_intermediate_size": 4864,
|
| 31 |
+
"fast_n_head": 14,
|
| 32 |
+
"fast_n_local_heads": 2,
|
| 33 |
+
"head_dim": 64,
|
| 34 |
+
"hidden_size": 896,
|
| 35 |
+
"initializer_range": 0.02,
|
| 36 |
+
"intermediate_size": 4864,
|
| 37 |
+
"max_position_embeddings": 2048,
|
| 38 |
+
"max_seq_len": 2048,
|
| 39 |
+
"model_type": "arktts",
|
| 40 |
+
"n_fast_layer": 4,
|
| 41 |
+
"n_head": 14,
|
| 42 |
+
"n_layer": 24,
|
| 43 |
+
"n_local_heads": 2,
|
| 44 |
+
"norm_eps": 1e-06,
|
| 45 |
+
"norm_fastlayer_input": true,
|
| 46 |
+
"num_attention_heads": 14,
|
| 47 |
+
"num_codebooks": 10,
|
| 48 |
+
"num_hidden_layers": 24,
|
| 49 |
+
"num_key_value_heads": 2,
|
| 50 |
+
"pad_token_id": 151643,
|
| 51 |
+
"ras_temperature": 1.0,
|
| 52 |
+
"ras_top_p": 0.9,
|
| 53 |
+
"ras_window_size": 10,
|
| 54 |
+
"rms_norm_eps": 1e-06,
|
| 55 |
+
"rope_base": 1000000.0,
|
| 56 |
+
"rope_theta": 1000000.0,
|
| 57 |
+
"scale_codebook_embeddings": false,
|
| 58 |
+
"semantic_begin_id": 151678,
|
| 59 |
+
"semantic_end_id": 155773,
|
| 60 |
+
"transformers_version": "4.57.3",
|
| 61 |
+
"use_gradient_checkpointing": false,
|
| 62 |
+
"vocab_size": 155776,
|
| 63 |
+
"tie_word_embeddings": true
|
| 64 |
+
}
|
configuration_arktts.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from transformers import PretrainedConfig
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class ArkttsConfig(PretrainedConfig):
|
| 7 |
+
model_type = "arktts"
|
| 8 |
+
|
| 9 |
+
def __init__(
|
| 10 |
+
self,
|
| 11 |
+
vocab_size: int = 155776,
|
| 12 |
+
dim: int = 896,
|
| 13 |
+
n_layer: int = 24,
|
| 14 |
+
n_head: int = 14,
|
| 15 |
+
n_local_heads: int = 2,
|
| 16 |
+
head_dim: int = 64,
|
| 17 |
+
intermediate_size: int = 4864,
|
| 18 |
+
max_seq_len: int = 2048,
|
| 19 |
+
rope_base: float = 1_000_000,
|
| 20 |
+
norm_eps: float = 1e-6,
|
| 21 |
+
dropout: float = 0.0,
|
| 22 |
+
attention_qkv_bias: bool = True,
|
| 23 |
+
attention_qk_norm: bool = False,
|
| 24 |
+
attention_o_bias: bool = False,
|
| 25 |
+
tie_word_embeddings: bool = True,
|
| 26 |
+
codebook_size: int = 4096,
|
| 27 |
+
num_codebooks: int = 10,
|
| 28 |
+
semantic_begin_id: int = 151678,
|
| 29 |
+
semantic_end_id: int = 155773,
|
| 30 |
+
n_fast_layer: int = 4,
|
| 31 |
+
fast_dim: int = 896,
|
| 32 |
+
fast_n_head: int = 14,
|
| 33 |
+
fast_n_local_heads: int = 2,
|
| 34 |
+
fast_head_dim: int = 64,
|
| 35 |
+
fast_intermediate_size: int = 4864,
|
| 36 |
+
fast_attention_qkv_bias: bool = False,
|
| 37 |
+
fast_attention_qk_norm: bool = False,
|
| 38 |
+
fast_attention_o_bias: bool = False,
|
| 39 |
+
norm_fastlayer_input: bool = True,
|
| 40 |
+
initializer_range: float = 0.02,
|
| 41 |
+
use_gradient_checkpointing: bool = False,
|
| 42 |
+
codec_filename: str = "codec.pth",
|
| 43 |
+
codec_sample_rate: int = 44100,
|
| 44 |
+
codec_frame_size: int = 2048,
|
| 45 |
+
codec_post_n_layer: int = 8,
|
| 46 |
+
codec_post_n_head: int = 16,
|
| 47 |
+
codec_post_n_local_heads: int = 8,
|
| 48 |
+
codec_post_intermediate_size: int = 1216,
|
| 49 |
+
ras_window_size: int = 10,
|
| 50 |
+
ras_temperature: float = 1.0,
|
| 51 |
+
ras_top_p: float = 0.9,
|
| 52 |
+
eos_token_id: int = 151645,
|
| 53 |
+
pad_token_id: int = 151643,
|
| 54 |
+
**kwargs,
|
| 55 |
+
):
|
| 56 |
+
kwargs.pop("model_type", None)
|
| 57 |
+
kwargs.pop("audio_embed_dim", None)
|
| 58 |
+
kwargs.pop("is_reward_model", None)
|
| 59 |
+
super().__init__(
|
| 60 |
+
eos_token_id=eos_token_id,
|
| 61 |
+
pad_token_id=pad_token_id,
|
| 62 |
+
tie_word_embeddings=tie_word_embeddings,
|
| 63 |
+
**kwargs,
|
| 64 |
+
)
|
| 65 |
+
self.vocab_size = int(vocab_size)
|
| 66 |
+
self.dim = int(dim)
|
| 67 |
+
self.hidden_size = self.dim
|
| 68 |
+
self.n_layer = int(n_layer)
|
| 69 |
+
self.num_hidden_layers = self.n_layer
|
| 70 |
+
self.n_head = int(n_head)
|
| 71 |
+
self.num_attention_heads = self.n_head
|
| 72 |
+
self.n_local_heads = int(n_local_heads)
|
| 73 |
+
self.num_key_value_heads = self.n_local_heads
|
| 74 |
+
self.head_dim = int(head_dim)
|
| 75 |
+
self.intermediate_size = int(intermediate_size)
|
| 76 |
+
self.max_seq_len = int(max_seq_len)
|
| 77 |
+
self.max_position_embeddings = self.max_seq_len
|
| 78 |
+
self.rope_base = float(rope_base)
|
| 79 |
+
self.rope_theta = self.rope_base
|
| 80 |
+
self.norm_eps = float(norm_eps)
|
| 81 |
+
self.rms_norm_eps = self.norm_eps
|
| 82 |
+
self.dropout = float(dropout)
|
| 83 |
+
self.attention_qkv_bias = bool(attention_qkv_bias)
|
| 84 |
+
self.attention_qk_norm = bool(attention_qk_norm)
|
| 85 |
+
self.attention_o_bias = bool(attention_o_bias)
|
| 86 |
+
self.codebook_size = int(codebook_size)
|
| 87 |
+
self.num_codebooks = int(num_codebooks)
|
| 88 |
+
self.semantic_begin_id = int(semantic_begin_id)
|
| 89 |
+
self.semantic_end_id = int(semantic_end_id)
|
| 90 |
+
self.n_fast_layer = int(n_fast_layer)
|
| 91 |
+
self.fast_dim = int(fast_dim)
|
| 92 |
+
self.fast_n_head = int(fast_n_head)
|
| 93 |
+
self.fast_n_local_heads = int(fast_n_local_heads)
|
| 94 |
+
self.fast_head_dim = int(fast_head_dim)
|
| 95 |
+
self.fast_intermediate_size = int(fast_intermediate_size)
|
| 96 |
+
self.fast_attention_qkv_bias = bool(fast_attention_qkv_bias)
|
| 97 |
+
self.fast_attention_qk_norm = bool(fast_attention_qk_norm)
|
| 98 |
+
self.fast_attention_o_bias = bool(fast_attention_o_bias)
|
| 99 |
+
self.norm_fastlayer_input = bool(norm_fastlayer_input)
|
| 100 |
+
self.initializer_range = float(initializer_range)
|
| 101 |
+
self.use_gradient_checkpointing = bool(use_gradient_checkpointing)
|
| 102 |
+
self.codec_filename = str(codec_filename)
|
| 103 |
+
self.codec_sample_rate = int(codec_sample_rate)
|
| 104 |
+
self.codec_frame_size = int(codec_frame_size)
|
| 105 |
+
self.codec_post_n_layer = int(codec_post_n_layer)
|
| 106 |
+
self.codec_post_n_head = int(codec_post_n_head)
|
| 107 |
+
self.codec_post_n_local_heads = int(codec_post_n_local_heads)
|
| 108 |
+
self.codec_post_intermediate_size = int(codec_post_intermediate_size)
|
| 109 |
+
self.ras_window_size = int(ras_window_size)
|
| 110 |
+
self.ras_temperature = float(ras_temperature)
|
| 111 |
+
self.ras_top_p = float(ras_top_p)
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
__all__ = ["ArkttsConfig"]
|
generation_config.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"do_sample": true,
|
| 3 |
+
"eos_token_id": 151645,
|
| 4 |
+
"max_new_tokens": 512,
|
| 5 |
+
"pad_token_id": 151643,
|
| 6 |
+
"temperature": 0.7,
|
| 7 |
+
"top_p": 0.9,
|
| 8 |
+
"transformers_version": "4.57.3",
|
| 9 |
+
"top_k": 50
|
| 10 |
+
}
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f590053e980a45ebcbb2e787cff4f2dead932b2b61e5cf54d05664d9764e12d8
|
| 3 |
+
size 1202342528
|
modeling_arktts.py
ADDED
|
@@ -0,0 +1,831 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import math
|
| 4 |
+
import shutil
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from typing import Optional
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
from torch import Tensor
|
| 13 |
+
from torch.nn.attention import SDPBackend, sdpa_kernel
|
| 14 |
+
from transformers import GenerationConfig, PreTrainedModel
|
| 15 |
+
from transformers.generation import (
|
| 16 |
+
LogitsProcessor,
|
| 17 |
+
LogitsProcessorList,
|
| 18 |
+
StoppingCriteriaList,
|
| 19 |
+
)
|
| 20 |
+
from transformers.modeling_outputs import ModelOutput
|
| 21 |
+
from transformers.utils.hub import cached_file
|
| 22 |
+
|
| 23 |
+
from .configuration_arktts import ArkttsConfig
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
@dataclass
|
| 27 |
+
class ArkttsModelOutput(ModelOutput):
|
| 28 |
+
logits: Optional[Tensor] = None
|
| 29 |
+
hidden_states: Optional[Tensor] = None
|
| 30 |
+
codebook_logits: Optional[Tensor] = None
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
@dataclass
|
| 34 |
+
class ArkttsGenerateOutput(ModelOutput):
|
| 35 |
+
codes: Optional[Tensor] = None
|
| 36 |
+
code_lengths: Optional[Tensor] = None
|
| 37 |
+
finished: Optional[Tensor] = None
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class ArkttsSemanticLogitsProcessor(LogitsProcessor):
|
| 41 |
+
def __init__(self, semantic_begin_id: int, semantic_end_id: int, eos_token_id: int):
|
| 42 |
+
self.semantic_begin_id = int(semantic_begin_id)
|
| 43 |
+
self.semantic_end_id = int(semantic_end_id)
|
| 44 |
+
self.eos_token_id = int(eos_token_id)
|
| 45 |
+
|
| 46 |
+
def __call__(self, input_ids: Tensor, scores: Tensor) -> Tensor:
|
| 47 |
+
filtered = torch.full_like(scores, float("-inf"))
|
| 48 |
+
filtered[:, self.semantic_begin_id : self.semantic_end_id + 1] = scores[
|
| 49 |
+
:, self.semantic_begin_id : self.semantic_end_id + 1
|
| 50 |
+
]
|
| 51 |
+
filtered[:, self.eos_token_id] = scores[:, self.eos_token_id]
|
| 52 |
+
return filtered
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
class ArkttsLegacyTopKTopPLogitsProcessor(LogitsProcessor):
|
| 56 |
+
"""Matches the candidate filtering order used by the original inference code."""
|
| 57 |
+
|
| 58 |
+
def __init__(self, top_k: int, top_p: float):
|
| 59 |
+
if top_k <= 0:
|
| 60 |
+
raise ValueError("top_k must be positive")
|
| 61 |
+
if not 0.0 < top_p <= 1.0:
|
| 62 |
+
raise ValueError("top_p must be in (0, 1]")
|
| 63 |
+
self.top_k = int(top_k)
|
| 64 |
+
self.top_p = float(top_p)
|
| 65 |
+
|
| 66 |
+
def __call__(self, input_ids: Tensor, scores: Tensor) -> Tensor:
|
| 67 |
+
sorted_scores, sorted_indices = torch.sort(scores, descending=True, dim=-1)
|
| 68 |
+
cumulative = torch.cumsum(torch.softmax(sorted_scores, dim=-1), dim=-1)
|
| 69 |
+
positions = torch.arange(sorted_scores.shape[-1], device=scores.device)
|
| 70 |
+
threshold = torch.tensor(self.top_p, dtype=cumulative.dtype, device=cumulative.device)
|
| 71 |
+
remove_sorted = (cumulative > threshold) | (positions >= self.top_k)
|
| 72 |
+
remove_sorted[..., 0] = False
|
| 73 |
+
remove = torch.zeros_like(remove_sorted).scatter(1, sorted_indices, remove_sorted)
|
| 74 |
+
return scores.masked_fill(remove, float("-inf"))
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
class ArkttsKVCache(nn.Module):
|
| 78 |
+
def __init__(
|
| 79 |
+
self,
|
| 80 |
+
batch_size: int,
|
| 81 |
+
max_length: int,
|
| 82 |
+
heads: int,
|
| 83 |
+
head_dim: int,
|
| 84 |
+
dtype,
|
| 85 |
+
return_full: bool = False,
|
| 86 |
+
):
|
| 87 |
+
super().__init__()
|
| 88 |
+
shape = (batch_size, heads, max_length, head_dim)
|
| 89 |
+
self.register_buffer("keys", torch.zeros(shape, dtype=dtype), persistent=False)
|
| 90 |
+
self.register_buffer("values", torch.zeros(shape, dtype=dtype), persistent=False)
|
| 91 |
+
self.return_full = bool(return_full)
|
| 92 |
+
|
| 93 |
+
def update(self, cache_position: Tensor, keys: Tensor, values: Tensor):
|
| 94 |
+
self.keys[:, :, cache_position] = keys
|
| 95 |
+
self.values[:, :, cache_position] = values
|
| 96 |
+
end = self.keys.shape[2] if self.return_full else int(cache_position[-1].item()) + 1
|
| 97 |
+
return self.keys[:, :, :end], self.values[:, :, :end]
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
class ArkttsRMSNorm(nn.Module):
|
| 101 |
+
def __init__(self, dim: int, eps: float):
|
| 102 |
+
super().__init__()
|
| 103 |
+
self.eps = float(eps)
|
| 104 |
+
self.weight = nn.Parameter(torch.ones(dim))
|
| 105 |
+
|
| 106 |
+
def forward(self, x: Tensor) -> Tensor:
|
| 107 |
+
normalized = x.float() * torch.rsqrt(x.float().pow(2).mean(-1, keepdim=True) + self.eps)
|
| 108 |
+
return normalized.to(x.dtype) * self.weight
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def _precompute_rope(length: int, head_dim: int, base: float) -> Tensor:
|
| 112 |
+
frequencies = 1.0 / (
|
| 113 |
+
base ** (torch.arange(0, head_dim, 2).float()[: head_dim // 2] / head_dim)
|
| 114 |
+
)
|
| 115 |
+
phases = torch.outer(torch.arange(length), frequencies)
|
| 116 |
+
values = torch.polar(torch.ones_like(phases), phases)
|
| 117 |
+
return torch.stack((values.real, values.imag), dim=-1).to(torch.bfloat16)
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def _apply_rope(x: Tensor, rope: Tensor) -> Tensor:
|
| 121 |
+
shaped = x.float().reshape(*x.shape[:-1], -1, 2)
|
| 122 |
+
if rope.ndim == 3:
|
| 123 |
+
rope = rope[None, :, None]
|
| 124 |
+
elif rope.ndim == 4:
|
| 125 |
+
rope = rope[:, :, None]
|
| 126 |
+
else:
|
| 127 |
+
raise ValueError(f"Unexpected RoPE shape: {tuple(rope.shape)}")
|
| 128 |
+
output = torch.stack(
|
| 129 |
+
(
|
| 130 |
+
shaped[..., 0] * rope[..., 0] - shaped[..., 1] * rope[..., 1],
|
| 131 |
+
shaped[..., 1] * rope[..., 0] + shaped[..., 0] * rope[..., 1],
|
| 132 |
+
),
|
| 133 |
+
dim=-1,
|
| 134 |
+
)
|
| 135 |
+
return output.flatten(3).to(x.dtype)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
class ArkttsAttention(nn.Module):
|
| 139 |
+
def __init__(
|
| 140 |
+
self,
|
| 141 |
+
dim: int,
|
| 142 |
+
n_head: int,
|
| 143 |
+
n_local_heads: int,
|
| 144 |
+
head_dim: int,
|
| 145 |
+
qkv_bias: bool,
|
| 146 |
+
output_bias: bool,
|
| 147 |
+
qk_norm: bool,
|
| 148 |
+
norm_eps: float,
|
| 149 |
+
dropout: float,
|
| 150 |
+
use_sdpa: bool,
|
| 151 |
+
):
|
| 152 |
+
super().__init__()
|
| 153 |
+
total = (n_head + 2 * n_local_heads) * head_dim
|
| 154 |
+
self.wqkv = nn.Linear(dim, total, bias=qkv_bias)
|
| 155 |
+
self.wo = nn.Linear(n_head * head_dim, dim, bias=output_bias)
|
| 156 |
+
self.n_head = int(n_head)
|
| 157 |
+
self.n_local_heads = int(n_local_heads)
|
| 158 |
+
self.head_dim = int(head_dim)
|
| 159 |
+
self.dropout = float(dropout)
|
| 160 |
+
self.use_sdpa = bool(use_sdpa)
|
| 161 |
+
self.qk_norm = bool(qk_norm)
|
| 162 |
+
if self.qk_norm:
|
| 163 |
+
self.q_norm = ArkttsRMSNorm(head_dim, norm_eps)
|
| 164 |
+
self.k_norm = ArkttsRMSNorm(head_dim, norm_eps)
|
| 165 |
+
self.kv_cache: Optional[ArkttsKVCache] = None
|
| 166 |
+
|
| 167 |
+
def forward(
|
| 168 |
+
self,
|
| 169 |
+
x: Tensor,
|
| 170 |
+
rope: Tensor,
|
| 171 |
+
attention_mask: Optional[Tensor],
|
| 172 |
+
cache_position: Optional[Tensor] = None,
|
| 173 |
+
) -> Tensor:
|
| 174 |
+
batch, length, _ = x.shape
|
| 175 |
+
query_size = self.n_head * self.head_dim
|
| 176 |
+
kv_size = self.n_local_heads * self.head_dim
|
| 177 |
+
query, key, value = self.wqkv(x).split((query_size, kv_size, kv_size), dim=-1)
|
| 178 |
+
query = query.view(batch, length, self.n_head, self.head_dim)
|
| 179 |
+
key = key.view(batch, length, self.n_local_heads, self.head_dim)
|
| 180 |
+
value = value.view(batch, length, self.n_local_heads, self.head_dim)
|
| 181 |
+
if self.qk_norm:
|
| 182 |
+
query = self.q_norm(query)
|
| 183 |
+
key = self.k_norm(key)
|
| 184 |
+
query = _apply_rope(query, rope).transpose(1, 2)
|
| 185 |
+
key = _apply_rope(key, rope).transpose(1, 2)
|
| 186 |
+
value = value.transpose(1, 2)
|
| 187 |
+
if self.kv_cache is not None:
|
| 188 |
+
if cache_position is None:
|
| 189 |
+
raise ValueError("cache_position is required when KV cache is enabled")
|
| 190 |
+
key, value = self.kv_cache.update(cache_position, key, value)
|
| 191 |
+
repeats = self.n_head // self.n_local_heads
|
| 192 |
+
key = key.repeat_interleave(repeats, dim=1)
|
| 193 |
+
value = value.repeat_interleave(repeats, dim=1)
|
| 194 |
+
if self.use_sdpa:
|
| 195 |
+
output = F.scaled_dot_product_attention(
|
| 196 |
+
query,
|
| 197 |
+
key,
|
| 198 |
+
value,
|
| 199 |
+
attn_mask=attention_mask,
|
| 200 |
+
dropout_p=self.dropout if self.training else 0.0,
|
| 201 |
+
)
|
| 202 |
+
else:
|
| 203 |
+
scores = query @ key.transpose(-2, -1) / math.sqrt(self.head_dim)
|
| 204 |
+
if attention_mask is not None:
|
| 205 |
+
scores = scores.masked_fill(~attention_mask, float("-inf"))
|
| 206 |
+
probabilities = torch.softmax(scores, dim=-1)
|
| 207 |
+
if self.training and self.dropout:
|
| 208 |
+
probabilities = F.dropout(probabilities, p=self.dropout)
|
| 209 |
+
output = probabilities @ value
|
| 210 |
+
output = output.transpose(1, 2).contiguous().view(batch, length, query_size)
|
| 211 |
+
return self.wo(output)
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
class ArkttsFeedForward(nn.Module):
|
| 215 |
+
def __init__(self, dim: int, intermediate_size: int):
|
| 216 |
+
super().__init__()
|
| 217 |
+
self.w1 = nn.Linear(dim, intermediate_size, bias=False)
|
| 218 |
+
self.w2 = nn.Linear(intermediate_size, dim, bias=False)
|
| 219 |
+
self.w3 = nn.Linear(dim, intermediate_size, bias=False)
|
| 220 |
+
|
| 221 |
+
def forward(self, x: Tensor) -> Tensor:
|
| 222 |
+
return self.w2(F.silu(self.w1(x)) * self.w3(x))
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
class ArkttsTransformerBlock(nn.Module):
|
| 226 |
+
def __init__(
|
| 227 |
+
self,
|
| 228 |
+
dim: int,
|
| 229 |
+
intermediate_size: int,
|
| 230 |
+
n_head: int,
|
| 231 |
+
n_local_heads: int,
|
| 232 |
+
head_dim: int,
|
| 233 |
+
qkv_bias: bool,
|
| 234 |
+
output_bias: bool,
|
| 235 |
+
qk_norm: bool,
|
| 236 |
+
norm_eps: float,
|
| 237 |
+
dropout: float,
|
| 238 |
+
use_sdpa: bool,
|
| 239 |
+
):
|
| 240 |
+
super().__init__()
|
| 241 |
+
self.attention = ArkttsAttention(
|
| 242 |
+
dim, n_head, n_local_heads, head_dim, qkv_bias, output_bias,
|
| 243 |
+
qk_norm, norm_eps, dropout, use_sdpa,
|
| 244 |
+
)
|
| 245 |
+
self.feed_forward = ArkttsFeedForward(dim, intermediate_size)
|
| 246 |
+
self.ffn_norm = ArkttsRMSNorm(dim, norm_eps)
|
| 247 |
+
self.attention_norm = ArkttsRMSNorm(dim, norm_eps)
|
| 248 |
+
|
| 249 |
+
def forward(self, x, rope, attention_mask, cache_position=None):
|
| 250 |
+
hidden = x + self.attention(self.attention_norm(x), rope, attention_mask, cache_position)
|
| 251 |
+
return hidden + self.feed_forward(self.ffn_norm(hidden))
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
class ArkttsModel(PreTrainedModel):
|
| 255 |
+
config_class = ArkttsConfig
|
| 256 |
+
base_model_prefix = ""
|
| 257 |
+
main_input_name = "input_ids"
|
| 258 |
+
_no_split_modules = ["ArkttsTransformerBlock"]
|
| 259 |
+
_supports_sdpa = True
|
| 260 |
+
|
| 261 |
+
def __init__(self, config: ArkttsConfig):
|
| 262 |
+
super().__init__(config)
|
| 263 |
+
self.embeddings = nn.Embedding(config.vocab_size, config.dim)
|
| 264 |
+
self.codebook_embeddings = nn.Embedding(config.codebook_size * config.num_codebooks, config.dim)
|
| 265 |
+
self.layers = nn.ModuleList([
|
| 266 |
+
ArkttsTransformerBlock(
|
| 267 |
+
config.dim, config.intermediate_size, config.n_head, config.n_local_heads,
|
| 268 |
+
config.head_dim, config.attention_qkv_bias, config.attention_o_bias,
|
| 269 |
+
config.attention_qk_norm, config.norm_eps, config.dropout, True,
|
| 270 |
+
)
|
| 271 |
+
for _ in range(config.n_layer)
|
| 272 |
+
])
|
| 273 |
+
self.norm = ArkttsRMSNorm(config.dim, config.norm_eps)
|
| 274 |
+
self.fast_project_in = (
|
| 275 |
+
nn.Linear(config.dim, config.fast_dim)
|
| 276 |
+
if config.fast_dim != config.dim else nn.Identity()
|
| 277 |
+
)
|
| 278 |
+
self.fast_embeddings = nn.Embedding(config.codebook_size, config.fast_dim)
|
| 279 |
+
self.fast_layers = nn.ModuleList([
|
| 280 |
+
ArkttsTransformerBlock(
|
| 281 |
+
config.fast_dim, config.fast_intermediate_size, config.fast_n_head,
|
| 282 |
+
config.fast_n_local_heads, config.fast_head_dim,
|
| 283 |
+
config.fast_attention_qkv_bias, config.fast_attention_o_bias,
|
| 284 |
+
config.fast_attention_qk_norm, config.norm_eps, config.dropout, False,
|
| 285 |
+
)
|
| 286 |
+
for _ in range(config.n_fast_layer)
|
| 287 |
+
])
|
| 288 |
+
self.fast_norm = ArkttsRMSNorm(config.fast_dim, config.norm_eps)
|
| 289 |
+
self.fast_output = nn.Linear(config.fast_dim, config.codebook_size, bias=False)
|
| 290 |
+
self.register_buffer(
|
| 291 |
+
"freqs_cis", _precompute_rope(config.max_seq_len, config.head_dim, config.rope_base),
|
| 292 |
+
persistent=False,
|
| 293 |
+
)
|
| 294 |
+
self.register_buffer(
|
| 295 |
+
"fast_freqs_cis",
|
| 296 |
+
_precompute_rope(config.num_codebooks, config.fast_head_dim, config.rope_base),
|
| 297 |
+
persistent=False,
|
| 298 |
+
)
|
| 299 |
+
self.__dict__["_arktts_codec"] = None
|
| 300 |
+
self.post_init()
|
| 301 |
+
|
| 302 |
+
def _init_weights(self, module):
|
| 303 |
+
if isinstance(module, (nn.Linear, nn.Embedding)):
|
| 304 |
+
module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)
|
| 305 |
+
if isinstance(module, nn.Linear) and module.bias is not None:
|
| 306 |
+
module.bias.data.zero_()
|
| 307 |
+
|
| 308 |
+
def get_input_embeddings(self):
|
| 309 |
+
return self.embeddings
|
| 310 |
+
|
| 311 |
+
def set_input_embeddings(self, value):
|
| 312 |
+
self.embeddings = value
|
| 313 |
+
|
| 314 |
+
def get_output_embeddings(self):
|
| 315 |
+
return None
|
| 316 |
+
|
| 317 |
+
def _embed(self, input_ids: Tensor) -> Tensor:
|
| 318 |
+
codebook_embeds = []
|
| 319 |
+
for index in range(self.config.num_codebooks):
|
| 320 |
+
codebook_embeds.append(
|
| 321 |
+
self.codebook_embeddings(input_ids[:, index + 1] + index * self.config.codebook_size)
|
| 322 |
+
)
|
| 323 |
+
codebook_sum = torch.stack(codebook_embeds, dim=1).sum(dim=1)
|
| 324 |
+
semantic = (input_ids[:, 0] >= self.config.semantic_begin_id) & (
|
| 325 |
+
input_ids[:, 0] <= self.config.semantic_end_id
|
| 326 |
+
)
|
| 327 |
+
codebook_sum = torch.where(semantic.unsqueeze(-1), codebook_sum, 0.0)
|
| 328 |
+
return self.embeddings(input_ids[:, 0]) + codebook_sum
|
| 329 |
+
|
| 330 |
+
@staticmethod
|
| 331 |
+
def _causal_mask(attention_mask: Tensor, query_positions: Tensor, key_length: int) -> Tensor:
|
| 332 |
+
if attention_mask.shape[1] < key_length:
|
| 333 |
+
attention_mask = F.pad(
|
| 334 |
+
attention_mask,
|
| 335 |
+
(0, key_length - attention_mask.shape[1]),
|
| 336 |
+
value=0,
|
| 337 |
+
)
|
| 338 |
+
key_positions = torch.arange(key_length, device=attention_mask.device)
|
| 339 |
+
causal = key_positions[None, :] <= query_positions[:, None]
|
| 340 |
+
return causal[None, None] & attention_mask[:, None, None, :key_length].bool()
|
| 341 |
+
|
| 342 |
+
def forward(
|
| 343 |
+
self,
|
| 344 |
+
input_ids: Tensor,
|
| 345 |
+
attention_mask: Optional[Tensor] = None,
|
| 346 |
+
labels: Optional[Tensor] = None,
|
| 347 |
+
output_hidden_states: Optional[bool] = None,
|
| 348 |
+
return_dict: Optional[bool] = None,
|
| 349 |
+
**kwargs,
|
| 350 |
+
):
|
| 351 |
+
del labels, output_hidden_states, kwargs
|
| 352 |
+
if input_ids.ndim != 3 or input_ids.shape[1] != self.config.num_codebooks + 1:
|
| 353 |
+
raise ValueError(
|
| 354 |
+
f"input_ids must have shape [B, {self.config.num_codebooks + 1}, T]"
|
| 355 |
+
)
|
| 356 |
+
batch, _, length = input_ids.shape
|
| 357 |
+
if attention_mask is None:
|
| 358 |
+
attention_mask = torch.ones((batch, length), dtype=torch.long, device=input_ids.device)
|
| 359 |
+
position_ids = attention_mask.long().cumsum(-1).sub(1).clamp_min(0)
|
| 360 |
+
rope = self.freqs_cis[position_ids]
|
| 361 |
+
mask = self._causal_mask(
|
| 362 |
+
attention_mask,
|
| 363 |
+
torch.arange(length, device=input_ids.device),
|
| 364 |
+
length,
|
| 365 |
+
)
|
| 366 |
+
hidden = self._embed(input_ids)
|
| 367 |
+
for layer in self.layers:
|
| 368 |
+
hidden = layer(hidden, rope, mask)
|
| 369 |
+
normalized = self.norm(hidden)
|
| 370 |
+
logits = F.linear(normalized, self.embeddings.weight)
|
| 371 |
+
output = ArkttsModelOutput(logits=logits, hidden_states=hidden)
|
| 372 |
+
if return_dict is False:
|
| 373 |
+
return (logits, hidden)
|
| 374 |
+
return output
|
| 375 |
+
|
| 376 |
+
def _setup_generation_caches(self, batch_size: int, max_length: int, dtype):
|
| 377 |
+
del max_length
|
| 378 |
+
for layer in self.layers:
|
| 379 |
+
layer.attention.kv_cache = ArkttsKVCache(
|
| 380 |
+
batch_size,
|
| 381 |
+
self.config.max_seq_len,
|
| 382 |
+
self.config.n_local_heads,
|
| 383 |
+
self.config.head_dim,
|
| 384 |
+
dtype,
|
| 385 |
+
return_full=True,
|
| 386 |
+
).to(self.device)
|
| 387 |
+
for layer in self.fast_layers:
|
| 388 |
+
layer.attention.kv_cache = ArkttsKVCache(
|
| 389 |
+
batch_size, self.config.num_codebooks, self.config.fast_n_local_heads,
|
| 390 |
+
self.config.fast_head_dim, dtype, return_full=True,
|
| 391 |
+
).to(self.device)
|
| 392 |
+
|
| 393 |
+
def _slow_step(
|
| 394 |
+
self,
|
| 395 |
+
input_ids: Tensor,
|
| 396 |
+
cache_position: Tensor,
|
| 397 |
+
position_ids: Tensor,
|
| 398 |
+
attention_mask: Tensor,
|
| 399 |
+
) -> tuple[Tensor, Tensor]:
|
| 400 |
+
hidden = self._embed(input_ids)
|
| 401 |
+
rope = self.freqs_cis[position_ids]
|
| 402 |
+
mask = self._causal_mask(
|
| 403 |
+
attention_mask,
|
| 404 |
+
cache_position,
|
| 405 |
+
self.config.max_seq_len,
|
| 406 |
+
)
|
| 407 |
+
for layer in self.layers:
|
| 408 |
+
hidden = layer(hidden, rope, mask, cache_position)
|
| 409 |
+
hidden = hidden[:, -1:]
|
| 410 |
+
normalized = self.norm(hidden)
|
| 411 |
+
logits = F.linear(normalized, self.embeddings.weight)[:, -1]
|
| 412 |
+
fast_hidden = normalized if self.config.norm_fastlayer_input else hidden
|
| 413 |
+
return logits, fast_hidden
|
| 414 |
+
|
| 415 |
+
def _fast_step(self, hidden: Tensor, position: int) -> Tensor:
|
| 416 |
+
cache_position = torch.tensor([position], device=hidden.device, dtype=torch.long)
|
| 417 |
+
rope = self.fast_freqs_cis[cache_position]
|
| 418 |
+
key_mask = torch.ones(
|
| 419 |
+
(hidden.shape[0], self.config.num_codebooks),
|
| 420 |
+
device=hidden.device,
|
| 421 |
+
dtype=torch.bool,
|
| 422 |
+
)
|
| 423 |
+
mask = self._causal_mask(key_mask, cache_position, self.config.num_codebooks)
|
| 424 |
+
for layer in self.fast_layers:
|
| 425 |
+
hidden = layer(hidden, rope, mask, cache_position)
|
| 426 |
+
return self.fast_output(self.fast_norm(hidden))[:, -1]
|
| 427 |
+
|
| 428 |
+
@staticmethod
|
| 429 |
+
def _as_processor_list(value) -> LogitsProcessorList:
|
| 430 |
+
if value is None:
|
| 431 |
+
return LogitsProcessorList()
|
| 432 |
+
if isinstance(value, LogitsProcessorList):
|
| 433 |
+
return value
|
| 434 |
+
return LogitsProcessorList(value)
|
| 435 |
+
|
| 436 |
+
@staticmethod
|
| 437 |
+
def _sample(scores: Tensor, generator=None) -> Tensor:
|
| 438 |
+
probabilities = torch.softmax(scores, dim=-1)
|
| 439 |
+
random = torch.rand(
|
| 440 |
+
probabilities.shape,
|
| 441 |
+
dtype=probabilities.dtype,
|
| 442 |
+
device=probabilities.device,
|
| 443 |
+
generator=generator,
|
| 444 |
+
)
|
| 445 |
+
noise = -torch.log(random)
|
| 446 |
+
return torch.argmax(probabilities / noise, dim=-1)
|
| 447 |
+
|
| 448 |
+
def _processed_scores(
|
| 449 |
+
self,
|
| 450 |
+
input_ids: Tensor,
|
| 451 |
+
scores: Tensor,
|
| 452 |
+
processors: LogitsProcessorList,
|
| 453 |
+
top_k: int,
|
| 454 |
+
top_p: float,
|
| 455 |
+
temperature: float,
|
| 456 |
+
) -> Tensor:
|
| 457 |
+
scores = processors(input_ids, scores)
|
| 458 |
+
scores = ArkttsLegacyTopKTopPLogitsProcessor(top_k, top_p)(input_ids, scores)
|
| 459 |
+
temperature_value = torch.tensor(
|
| 460 |
+
temperature, dtype=scores.dtype, device=scores.device
|
| 461 |
+
).clamp_min(1e-5)
|
| 462 |
+
return scores / temperature_value
|
| 463 |
+
|
| 464 |
+
def _sample_semantic(
|
| 465 |
+
self,
|
| 466 |
+
history: Tensor,
|
| 467 |
+
logits: Tensor,
|
| 468 |
+
custom_processors: LogitsProcessorList,
|
| 469 |
+
top_k: int,
|
| 470 |
+
top_p: float,
|
| 471 |
+
temperature: float,
|
| 472 |
+
previous: Optional[Tensor],
|
| 473 |
+
do_sample: bool,
|
| 474 |
+
generator=None,
|
| 475 |
+
) -> Tensor:
|
| 476 |
+
processors = LogitsProcessorList([
|
| 477 |
+
ArkttsSemanticLogitsProcessor(
|
| 478 |
+
self.config.semantic_begin_id,
|
| 479 |
+
self.config.semantic_end_id,
|
| 480 |
+
self.config.eos_token_id,
|
| 481 |
+
),
|
| 482 |
+
*custom_processors,
|
| 483 |
+
])
|
| 484 |
+
regular_scores = self._processed_scores(
|
| 485 |
+
history, logits, processors, top_k, top_p, temperature
|
| 486 |
+
)
|
| 487 |
+
if not do_sample:
|
| 488 |
+
return regular_scores.argmax(dim=-1)
|
| 489 |
+
normal = self._sample(regular_scores, generator=generator)
|
| 490 |
+
high_scores = self._processed_scores(
|
| 491 |
+
history,
|
| 492 |
+
logits,
|
| 493 |
+
processors,
|
| 494 |
+
top_k,
|
| 495 |
+
self.config.ras_top_p,
|
| 496 |
+
self.config.ras_temperature,
|
| 497 |
+
)
|
| 498 |
+
high = self._sample(high_scores, generator=generator)
|
| 499 |
+
if previous is None:
|
| 500 |
+
return normal
|
| 501 |
+
repeated = (previous == normal[:, None]).any(dim=1)
|
| 502 |
+
semantic = (normal >= self.config.semantic_begin_id) & (
|
| 503 |
+
normal <= self.config.semantic_end_id
|
| 504 |
+
)
|
| 505 |
+
return torch.where(repeated & semantic, high, normal)
|
| 506 |
+
|
| 507 |
+
def _generate_codebooks(
|
| 508 |
+
self,
|
| 509 |
+
slow_hidden: Tensor,
|
| 510 |
+
semantic: Tensor,
|
| 511 |
+
processors: LogitsProcessorList,
|
| 512 |
+
top_k: int,
|
| 513 |
+
top_p: float,
|
| 514 |
+
temperature: float,
|
| 515 |
+
do_sample: bool,
|
| 516 |
+
generator=None,
|
| 517 |
+
) -> Tensor:
|
| 518 |
+
hidden = self.fast_project_in(slow_hidden)
|
| 519 |
+
self._fast_step(hidden, 0)
|
| 520 |
+
current = (semantic - self.config.semantic_begin_id).clamp(0, self.config.codebook_size - 1)
|
| 521 |
+
codebooks = [current]
|
| 522 |
+
fast_history = current[:, None]
|
| 523 |
+
hidden = self.fast_embeddings(current)[:, None]
|
| 524 |
+
for position in range(1, self.config.num_codebooks):
|
| 525 |
+
scores = self._fast_step(hidden, position)
|
| 526 |
+
scores = self._processed_scores(
|
| 527 |
+
fast_history, scores, processors, top_k, top_p, temperature
|
| 528 |
+
)
|
| 529 |
+
current = self._sample(scores, generator=generator) if do_sample else scores.argmax(dim=-1)
|
| 530 |
+
codebooks.append(current)
|
| 531 |
+
fast_history = torch.cat((fast_history, current[:, None]), dim=1)
|
| 532 |
+
hidden = self.fast_embeddings(current)[:, None]
|
| 533 |
+
return torch.stack(codebooks, dim=1)
|
| 534 |
+
|
| 535 |
+
def _prepare_prompt(
|
| 536 |
+
self,
|
| 537 |
+
input_ids=None,
|
| 538 |
+
attention_mask=None,
|
| 539 |
+
prefix_input_ids=None,
|
| 540 |
+
prefix_attention_mask=None,
|
| 541 |
+
suffix_input_ids=None,
|
| 542 |
+
suffix_attention_mask=None,
|
| 543 |
+
reference_codes=None,
|
| 544 |
+
reference_code_lengths=None,
|
| 545 |
+
reference_audio_values=None,
|
| 546 |
+
reference_audio_lengths=None,
|
| 547 |
+
):
|
| 548 |
+
if input_ids is not None:
|
| 549 |
+
if input_ids.ndim != 3 or input_ids.shape[1] != self.config.num_codebooks + 1:
|
| 550 |
+
raise ValueError("Direct input_ids must have shape [B, num_codebooks + 1, T]")
|
| 551 |
+
if attention_mask is None:
|
| 552 |
+
attention_mask = torch.ones(
|
| 553 |
+
input_ids.shape[0], input_ids.shape[-1], dtype=torch.long, device=input_ids.device
|
| 554 |
+
)
|
| 555 |
+
return input_ids.to(self.device), attention_mask.to(self.device)
|
| 556 |
+
if prefix_input_ids is None or suffix_input_ids is None:
|
| 557 |
+
raise ValueError("Processor output or direct input_ids is required")
|
| 558 |
+
prefix_input_ids = prefix_input_ids.to(self.device)
|
| 559 |
+
suffix_input_ids = suffix_input_ids.to(self.device)
|
| 560 |
+
prefix_attention_mask = prefix_attention_mask.to(self.device)
|
| 561 |
+
suffix_attention_mask = suffix_attention_mask.to(self.device)
|
| 562 |
+
if reference_audio_values is not None:
|
| 563 |
+
if reference_codes is not None:
|
| 564 |
+
raise ValueError("Provide reference audio or reference codes, not both")
|
| 565 |
+
reference_codes, reference_code_lengths = self.encode_audio(
|
| 566 |
+
reference_audio_values.to(self.device), reference_audio_lengths.to(self.device)
|
| 567 |
+
)
|
| 568 |
+
if reference_codes is not None:
|
| 569 |
+
reference_codes = reference_codes.to(self.device)
|
| 570 |
+
reference_code_lengths = reference_code_lengths.to(self.device)
|
| 571 |
+
batch_size = prefix_input_ids.shape[0]
|
| 572 |
+
rows = []
|
| 573 |
+
for batch_index in range(batch_size):
|
| 574 |
+
prefix = prefix_input_ids[batch_index, prefix_attention_mask[batch_index].bool()]
|
| 575 |
+
suffix = suffix_input_ids[batch_index, suffix_attention_mask[batch_index].bool()]
|
| 576 |
+
if reference_codes is None:
|
| 577 |
+
semantic_row = torch.cat((prefix, suffix))
|
| 578 |
+
values = torch.zeros(
|
| 579 |
+
(self.config.num_codebooks + 1, semantic_row.numel()),
|
| 580 |
+
dtype=torch.long,
|
| 581 |
+
device=self.device,
|
| 582 |
+
)
|
| 583 |
+
values[0] = semantic_row
|
| 584 |
+
else:
|
| 585 |
+
length = int(reference_code_lengths[batch_index])
|
| 586 |
+
codes = reference_codes[batch_index, :, :length].long()
|
| 587 |
+
semantic_codes = codes[0] + self.config.semantic_begin_id
|
| 588 |
+
semantic_row = torch.cat((prefix, semantic_codes, suffix))
|
| 589 |
+
values = torch.zeros(
|
| 590 |
+
(self.config.num_codebooks + 1, semantic_row.numel()),
|
| 591 |
+
dtype=torch.long,
|
| 592 |
+
device=self.device,
|
| 593 |
+
)
|
| 594 |
+
values[0] = semantic_row
|
| 595 |
+
values[1:, prefix.numel() : prefix.numel() + length] = codes
|
| 596 |
+
rows.append(values)
|
| 597 |
+
max_length = max(row.shape[1] for row in rows)
|
| 598 |
+
prompt = torch.zeros(
|
| 599 |
+
(batch_size, self.config.num_codebooks + 1, max_length),
|
| 600 |
+
dtype=torch.long,
|
| 601 |
+
device=self.device,
|
| 602 |
+
)
|
| 603 |
+
prompt[:, 0] = self.config.pad_token_id
|
| 604 |
+
prompt_mask = torch.zeros((batch_size, max_length), dtype=torch.long, device=self.device)
|
| 605 |
+
for batch_index, row in enumerate(rows):
|
| 606 |
+
start = max_length - row.shape[1]
|
| 607 |
+
prompt[batch_index, :, start:] = row
|
| 608 |
+
prompt_mask[batch_index, start:] = 1
|
| 609 |
+
return prompt, prompt_mask
|
| 610 |
+
|
| 611 |
+
@torch.inference_mode()
|
| 612 |
+
def generate(
|
| 613 |
+
self,
|
| 614 |
+
input_ids: Optional[Tensor] = None,
|
| 615 |
+
attention_mask: Optional[Tensor] = None,
|
| 616 |
+
prefix_input_ids: Optional[Tensor] = None,
|
| 617 |
+
prefix_attention_mask: Optional[Tensor] = None,
|
| 618 |
+
suffix_input_ids: Optional[Tensor] = None,
|
| 619 |
+
suffix_attention_mask: Optional[Tensor] = None,
|
| 620 |
+
reference_codes: Optional[Tensor] = None,
|
| 621 |
+
reference_code_lengths: Optional[Tensor] = None,
|
| 622 |
+
reference_audio_values: Optional[Tensor] = None,
|
| 623 |
+
reference_audio_lengths: Optional[Tensor] = None,
|
| 624 |
+
generation_config: Optional[GenerationConfig] = None,
|
| 625 |
+
logits_processor=None,
|
| 626 |
+
codebook_logits_processor=None,
|
| 627 |
+
stopping_criteria=None,
|
| 628 |
+
max_new_tokens: Optional[int] = None,
|
| 629 |
+
temperature: Optional[float] = None,
|
| 630 |
+
top_p: Optional[float] = None,
|
| 631 |
+
top_k: Optional[int] = None,
|
| 632 |
+
do_sample: Optional[bool] = None,
|
| 633 |
+
return_dict_in_generate: bool = False,
|
| 634 |
+
generator=None,
|
| 635 |
+
**kwargs,
|
| 636 |
+
):
|
| 637 |
+
if kwargs:
|
| 638 |
+
raise TypeError(f"Unexpected generation arguments: {sorted(kwargs)}")
|
| 639 |
+
config = generation_config or getattr(self, "generation_config", GenerationConfig())
|
| 640 |
+
config_max_new = getattr(config, "max_new_tokens", None)
|
| 641 |
+
config_temperature = getattr(config, "temperature", None)
|
| 642 |
+
config_top_p = getattr(config, "top_p", None)
|
| 643 |
+
config_top_k = getattr(config, "top_k", None)
|
| 644 |
+
max_new_tokens = int(max_new_tokens if max_new_tokens is not None else (config_max_new or 512))
|
| 645 |
+
temperature = float(temperature if temperature is not None else (config_temperature or 0.7))
|
| 646 |
+
top_p = float(top_p if top_p is not None else (config_top_p or 0.9))
|
| 647 |
+
top_k = int(top_k if top_k is not None else (config_top_k or 50))
|
| 648 |
+
do_sample = bool(do_sample if do_sample is not None else getattr(config, "do_sample", True))
|
| 649 |
+
if max_new_tokens <= 0:
|
| 650 |
+
raise ValueError("max_new_tokens must be positive")
|
| 651 |
+
if temperature <= 0:
|
| 652 |
+
raise ValueError("temperature must be positive")
|
| 653 |
+
prompt, prompt_mask = self._prepare_prompt(
|
| 654 |
+
input_ids, attention_mask, prefix_input_ids, prefix_attention_mask,
|
| 655 |
+
suffix_input_ids, suffix_attention_mask, reference_codes,
|
| 656 |
+
reference_code_lengths, reference_audio_values, reference_audio_lengths,
|
| 657 |
+
)
|
| 658 |
+
batch_size, _, prompt_width = prompt.shape
|
| 659 |
+
if prompt_width >= self.config.max_seq_len:
|
| 660 |
+
raise ValueError(
|
| 661 |
+
f"Prompt length {prompt_width} must be smaller than {self.config.max_seq_len}"
|
| 662 |
+
)
|
| 663 |
+
max_new_tokens = min(max_new_tokens, self.config.max_seq_len - prompt_width)
|
| 664 |
+
self._setup_generation_caches(
|
| 665 |
+
batch_size, prompt_width + max_new_tokens, next(self.parameters()).dtype
|
| 666 |
+
)
|
| 667 |
+
semantic_processors = self._as_processor_list(logits_processor)
|
| 668 |
+
codebook_processors = self._as_processor_list(codebook_logits_processor)
|
| 669 |
+
criteria = stopping_criteria or StoppingCriteriaList()
|
| 670 |
+
if not isinstance(criteria, StoppingCriteriaList):
|
| 671 |
+
criteria = StoppingCriteriaList(criteria)
|
| 672 |
+
cache_position = torch.arange(prompt_width, device=self.device, dtype=torch.long)
|
| 673 |
+
position_ids = prompt_mask.cumsum(-1).sub(1).clamp_min(0)
|
| 674 |
+
logits, slow_hidden = self._slow_step(
|
| 675 |
+
prompt, cache_position, position_ids, prompt_mask
|
| 676 |
+
)
|
| 677 |
+
semantic_history = prompt[:, 0]
|
| 678 |
+
prompt_lengths = prompt_mask.sum(-1)
|
| 679 |
+
previous = None
|
| 680 |
+
finished = torch.zeros(batch_size, dtype=torch.bool, device=self.device)
|
| 681 |
+
code_lengths = torch.zeros(batch_size, dtype=torch.long, device=self.device)
|
| 682 |
+
generated_frames = []
|
| 683 |
+
|
| 684 |
+
for step in range(max_new_tokens):
|
| 685 |
+
active_before = ~finished
|
| 686 |
+
semantic = self._sample_semantic(
|
| 687 |
+
semantic_history, logits, semantic_processors, top_k, top_p,
|
| 688 |
+
temperature, previous, do_sample, generator,
|
| 689 |
+
)
|
| 690 |
+
codebooks = self._generate_codebooks(
|
| 691 |
+
slow_hidden, semantic, codebook_processors, top_k, top_p,
|
| 692 |
+
temperature, do_sample, generator,
|
| 693 |
+
)
|
| 694 |
+
emitted = active_before & (semantic != self.config.eos_token_id)
|
| 695 |
+
frame = torch.where(emitted[:, None], codebooks, -1)
|
| 696 |
+
generated_frames.append(frame)
|
| 697 |
+
code_lengths += emitted.long()
|
| 698 |
+
semantic_history = torch.cat((semantic_history, semantic[:, None]), dim=1)
|
| 699 |
+
|
| 700 |
+
if previous is None:
|
| 701 |
+
previous = torch.zeros(
|
| 702 |
+
(batch_size, self.config.ras_window_size),
|
| 703 |
+
dtype=torch.long,
|
| 704 |
+
device=self.device,
|
| 705 |
+
)
|
| 706 |
+
else:
|
| 707 |
+
previous = previous.roll(-1, dims=1)
|
| 708 |
+
previous[:, -1] = semantic
|
| 709 |
+
finished |= semantic.eq(self.config.eos_token_id)
|
| 710 |
+
if criteria:
|
| 711 |
+
stopped = criteria(semantic_history, logits)
|
| 712 |
+
if not isinstance(stopped, Tensor):
|
| 713 |
+
stopped = torch.full_like(finished, bool(stopped))
|
| 714 |
+
finished |= stopped.to(device=self.device, dtype=torch.bool)
|
| 715 |
+
if finished.all():
|
| 716 |
+
break
|
| 717 |
+
|
| 718 |
+
next_column = torch.cat((semantic[:, None], codebooks), dim=1).unsqueeze(-1)
|
| 719 |
+
new_valid = active_before.long()[:, None]
|
| 720 |
+
prompt_mask = torch.cat((prompt_mask, new_valid), dim=1)
|
| 721 |
+
physical_position = torch.tensor([prompt_width + step], device=self.device)
|
| 722 |
+
token_position = (prompt_lengths + step)[:, None]
|
| 723 |
+
with sdpa_kernel(SDPBackend.MATH):
|
| 724 |
+
logits, slow_hidden = self._slow_step(
|
| 725 |
+
next_column, physical_position, token_position, prompt_mask
|
| 726 |
+
)
|
| 727 |
+
|
| 728 |
+
if generated_frames:
|
| 729 |
+
codes = torch.stack(generated_frames, dim=2)
|
| 730 |
+
max_valid = int(code_lengths.max().item()) if code_lengths.numel() else 0
|
| 731 |
+
codes = codes[:, :, :max_valid]
|
| 732 |
+
else:
|
| 733 |
+
codes = torch.empty(
|
| 734 |
+
(batch_size, self.config.num_codebooks, 0), dtype=torch.long, device=self.device
|
| 735 |
+
)
|
| 736 |
+
result = ArkttsGenerateOutput(codes=codes, code_lengths=code_lengths, finished=finished)
|
| 737 |
+
return result if return_dict_in_generate else codes
|
| 738 |
+
|
| 739 |
+
def _codec_path(self) -> str:
|
| 740 |
+
source = str(getattr(self.config, "_name_or_path", ""))
|
| 741 |
+
local = Path(source)
|
| 742 |
+
if local.is_dir() and (local / self.config.codec_filename).is_file():
|
| 743 |
+
return str(local / self.config.codec_filename)
|
| 744 |
+
resolved = cached_file(source, self.config.codec_filename)
|
| 745 |
+
if resolved is None:
|
| 746 |
+
raise FileNotFoundError(f"Could not resolve {self.config.codec_filename} from {source}")
|
| 747 |
+
return resolved
|
| 748 |
+
|
| 749 |
+
def save_pretrained(self, save_directory, *args, **kwargs):
|
| 750 |
+
result = super().save_pretrained(save_directory, *args, **kwargs)
|
| 751 |
+
source = Path(self._codec_path()).resolve()
|
| 752 |
+
destination = Path(save_directory) / self.config.codec_filename
|
| 753 |
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
| 754 |
+
if source != destination.resolve():
|
| 755 |
+
shutil.copy2(source, destination)
|
| 756 |
+
return result
|
| 757 |
+
|
| 758 |
+
def load_codec(self, device=None, dtype=None):
|
| 759 |
+
codec = self.__dict__.get("_arktts_codec")
|
| 760 |
+
target_device = torch.device(device) if device is not None else self.device
|
| 761 |
+
target_dtype = dtype or self.dtype
|
| 762 |
+
if target_device.type == "cpu":
|
| 763 |
+
target_dtype = torch.float32
|
| 764 |
+
if codec is None:
|
| 765 |
+
from .modeling_arktts_codec import ArkttsCodec
|
| 766 |
+
|
| 767 |
+
codec = ArkttsCodec(self.config)
|
| 768 |
+
state = torch.load(self._codec_path(), map_location="cpu", weights_only=True)
|
| 769 |
+
if "state_dict" in state:
|
| 770 |
+
state = state["state_dict"]
|
| 771 |
+
if any("generator." in key for key in state):
|
| 772 |
+
state = {
|
| 773 |
+
key.replace("generator.", ""): value
|
| 774 |
+
for key, value in state.items() if "generator." in key
|
| 775 |
+
}
|
| 776 |
+
state = {
|
| 777 |
+
key: value
|
| 778 |
+
for key, value in state.items()
|
| 779 |
+
if not key.endswith(("freqs_cis", "causal_mask"))
|
| 780 |
+
}
|
| 781 |
+
codec.load_state_dict(state, strict=True)
|
| 782 |
+
codec.eval()
|
| 783 |
+
self.__dict__["_arktts_codec"] = codec
|
| 784 |
+
codec.to(device=target_device, dtype=target_dtype)
|
| 785 |
+
return codec
|
| 786 |
+
|
| 787 |
+
@torch.inference_mode()
|
| 788 |
+
def encode_audio(self, audio_values: Tensor, audio_lengths: Optional[Tensor] = None):
|
| 789 |
+
codec = self.load_codec(device=audio_values.device)
|
| 790 |
+
audio_values = audio_values.to(dtype=next(codec.parameters()).dtype)
|
| 791 |
+
return codec.encode(audio_values, audio_lengths)
|
| 792 |
+
|
| 793 |
+
@torch.inference_mode()
|
| 794 |
+
def decode_audio(self, codes: Tensor):
|
| 795 |
+
if codes.ndim == 2:
|
| 796 |
+
codes = codes.unsqueeze(0)
|
| 797 |
+
if codes.ndim != 3 or codes.shape[1] != self.config.num_codebooks:
|
| 798 |
+
raise ValueError(f"codes must have shape [B, {self.config.num_codebooks}, T]")
|
| 799 |
+
codec = self.load_codec(device=codes.device)
|
| 800 |
+
waveforms = []
|
| 801 |
+
lengths = []
|
| 802 |
+
for item in codes:
|
| 803 |
+
valid = (item >= 0).all(dim=0)
|
| 804 |
+
length = int(valid.sum().item())
|
| 805 |
+
if length == 0:
|
| 806 |
+
waveform = torch.empty(0, device=codes.device, dtype=torch.float32)
|
| 807 |
+
else:
|
| 808 |
+
waveform = codec.decode(item[:, :length].unsqueeze(0))[0, 0].float()
|
| 809 |
+
waveforms.append(waveform)
|
| 810 |
+
lengths.append(waveform.numel())
|
| 811 |
+
max_length = max(lengths, default=0)
|
| 812 |
+
padded = torch.zeros((len(waveforms), max_length), dtype=torch.float32, device=codes.device)
|
| 813 |
+
for index, waveform in enumerate(waveforms):
|
| 814 |
+
padded[index, : waveform.numel()] = waveform
|
| 815 |
+
return padded, torch.tensor(lengths, dtype=torch.long, device=codes.device)
|
| 816 |
+
|
| 817 |
+
@torch.inference_mode()
|
| 818 |
+
def generate_audio(self, **kwargs):
|
| 819 |
+
codes = self.generate(**kwargs)
|
| 820 |
+
waveforms, lengths = self.decode_audio(codes)
|
| 821 |
+
return waveforms, lengths, codes
|
| 822 |
+
|
| 823 |
+
|
| 824 |
+
__all__ = [
|
| 825 |
+
"ArkttsConfig",
|
| 826 |
+
"ArkttsGenerateOutput",
|
| 827 |
+
"ArkttsLegacyTopKTopPLogitsProcessor",
|
| 828 |
+
"ArkttsModel",
|
| 829 |
+
"ArkttsModelOutput",
|
| 830 |
+
"ArkttsSemanticLogitsProcessor",
|
| 831 |
+
]
|
modeling_arktts_codec.py
ADDED
|
@@ -0,0 +1,520 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import math
|
| 4 |
+
from dataclasses import dataclass
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
import torch.nn as nn
|
| 8 |
+
import torch.nn.functional as F
|
| 9 |
+
from torch import Tensor
|
| 10 |
+
from torch.nn.utils import weight_norm as legacy_weight_norm
|
| 11 |
+
from torch.nn.utils.parametrizations import weight_norm
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def _rope(length: int, head_dim: int, base: float, device=None) -> Tensor:
|
| 15 |
+
frequencies = 1.0 / (
|
| 16 |
+
base ** (torch.arange(0, head_dim, 2, device=device).float() / head_dim)
|
| 17 |
+
)
|
| 18 |
+
phases = torch.outer(torch.arange(length, device=device), frequencies)
|
| 19 |
+
values = torch.polar(torch.ones_like(phases), phases)
|
| 20 |
+
return torch.stack((values.real, values.imag), dim=-1).to(torch.bfloat16)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def _apply_rope(x: Tensor, values: Tensor) -> Tensor:
|
| 24 |
+
shaped = x.float().reshape(*x.shape[:-1], -1, 2)
|
| 25 |
+
values = values.view(1, shaped.shape[1], 1, shaped.shape[3], 2)
|
| 26 |
+
output = torch.stack(
|
| 27 |
+
(
|
| 28 |
+
shaped[..., 0] * values[..., 0] - shaped[..., 1] * values[..., 1],
|
| 29 |
+
shaped[..., 1] * values[..., 0] + shaped[..., 0] * values[..., 1],
|
| 30 |
+
),
|
| 31 |
+
dim=-1,
|
| 32 |
+
)
|
| 33 |
+
return output.flatten(3).to(x.dtype)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class ArkttsCodecRMSNorm(nn.Module):
|
| 37 |
+
def __init__(self, dim: int, eps: float = 1e-5):
|
| 38 |
+
super().__init__()
|
| 39 |
+
self.eps = float(eps)
|
| 40 |
+
self.weight = nn.Parameter(torch.ones(dim))
|
| 41 |
+
|
| 42 |
+
def forward(self, x):
|
| 43 |
+
output = x.float() * torch.rsqrt(x.float().pow(2).mean(-1, keepdim=True) + self.eps)
|
| 44 |
+
return output.to(x.dtype) * self.weight
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class ArkttsCodecLayerScale(nn.Module):
|
| 48 |
+
def __init__(self, dim: int, init_values: float = 1e-2, inplace: bool = False):
|
| 49 |
+
super().__init__()
|
| 50 |
+
self.inplace = bool(inplace)
|
| 51 |
+
self.gamma = nn.Parameter(init_values * torch.ones(dim))
|
| 52 |
+
|
| 53 |
+
def forward(self, x):
|
| 54 |
+
return x.mul_(self.gamma) if self.inplace else x * self.gamma
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
@dataclass
|
| 58 |
+
class ArkttsCodecTransformerConfig:
|
| 59 |
+
n_layer: int
|
| 60 |
+
n_head: int
|
| 61 |
+
dim: int
|
| 62 |
+
intermediate_size: int
|
| 63 |
+
n_local_heads: int = -1
|
| 64 |
+
head_dim: int = 64
|
| 65 |
+
rope_base: float = 10000
|
| 66 |
+
norm_eps: float = 1e-5
|
| 67 |
+
dropout_rate: float = 0.1
|
| 68 |
+
attn_dropout_rate: float = 0.1
|
| 69 |
+
channels_first: bool = True
|
| 70 |
+
|
| 71 |
+
def __post_init__(self):
|
| 72 |
+
if self.n_local_heads == -1:
|
| 73 |
+
self.n_local_heads = self.n_head
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
class ArkttsCodecAttention(nn.Module):
|
| 77 |
+
def __init__(self, config: ArkttsCodecTransformerConfig):
|
| 78 |
+
super().__init__()
|
| 79 |
+
total = (config.n_head + 2 * config.n_local_heads) * config.head_dim
|
| 80 |
+
self.wqkv = nn.Linear(config.dim, total, bias=False)
|
| 81 |
+
self.wo = nn.Linear(config.head_dim * config.n_head, config.dim, bias=False)
|
| 82 |
+
self.n_head = config.n_head
|
| 83 |
+
self.n_local_heads = config.n_local_heads
|
| 84 |
+
self.head_dim = config.head_dim
|
| 85 |
+
self.attn_dropout_rate = config.attn_dropout_rate
|
| 86 |
+
|
| 87 |
+
def forward(self, x, rope_values, mask):
|
| 88 |
+
batch, length, _ = x.shape
|
| 89 |
+
query_size = self.n_head * self.head_dim
|
| 90 |
+
kv_size = self.n_local_heads * self.head_dim
|
| 91 |
+
query, key, value = self.wqkv(x).split((query_size, kv_size, kv_size), dim=-1)
|
| 92 |
+
query = query.view(batch, length, self.n_head, self.head_dim)
|
| 93 |
+
key = key.view(batch, length, self.n_local_heads, self.head_dim)
|
| 94 |
+
value = value.view(batch, length, self.n_local_heads, self.head_dim)
|
| 95 |
+
query = _apply_rope(query, rope_values).transpose(1, 2)
|
| 96 |
+
key = _apply_rope(key, rope_values).transpose(1, 2)
|
| 97 |
+
value = value.transpose(1, 2)
|
| 98 |
+
repeat = self.n_head // self.n_local_heads
|
| 99 |
+
key = key.repeat_interleave(repeat, dim=1)
|
| 100 |
+
value = value.repeat_interleave(repeat, dim=1)
|
| 101 |
+
output = F.scaled_dot_product_attention(
|
| 102 |
+
query,
|
| 103 |
+
key,
|
| 104 |
+
value,
|
| 105 |
+
attn_mask=mask,
|
| 106 |
+
dropout_p=self.attn_dropout_rate if self.training else 0.0,
|
| 107 |
+
)
|
| 108 |
+
output = output.transpose(1, 2).contiguous().view(batch, length, query_size)
|
| 109 |
+
return self.wo(output)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
class ArkttsCodecFeedForward(nn.Module):
|
| 113 |
+
def __init__(self, config: ArkttsCodecTransformerConfig):
|
| 114 |
+
super().__init__()
|
| 115 |
+
self.w1 = nn.Linear(config.dim, config.intermediate_size, bias=False)
|
| 116 |
+
self.w3 = nn.Linear(config.dim, config.intermediate_size, bias=False)
|
| 117 |
+
self.w2 = nn.Linear(config.intermediate_size, config.dim, bias=False)
|
| 118 |
+
self.dropout = nn.Dropout(config.dropout_rate)
|
| 119 |
+
|
| 120 |
+
def forward(self, x):
|
| 121 |
+
return self.w2(self.dropout(F.silu(self.w1(x)) * self.w3(x)))
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
class ArkttsCodecTransformerBlock(nn.Module):
|
| 125 |
+
def __init__(self, config):
|
| 126 |
+
super().__init__()
|
| 127 |
+
self.attention = ArkttsCodecAttention(config)
|
| 128 |
+
self.feed_forward = ArkttsCodecFeedForward(config)
|
| 129 |
+
self.ffn_norm = ArkttsCodecRMSNorm(config.dim, config.norm_eps)
|
| 130 |
+
self.attention_norm = ArkttsCodecRMSNorm(config.dim, config.norm_eps)
|
| 131 |
+
self.attention_layer_scale = ArkttsCodecLayerScale(config.dim, inplace=True)
|
| 132 |
+
self.ffn_layer_scale = ArkttsCodecLayerScale(config.dim, inplace=True)
|
| 133 |
+
|
| 134 |
+
def forward(self, x, rope_values, mask):
|
| 135 |
+
hidden = x + self.attention_layer_scale(
|
| 136 |
+
self.attention(self.attention_norm(x), rope_values, mask)
|
| 137 |
+
)
|
| 138 |
+
return hidden + self.ffn_layer_scale(self.feed_forward(self.ffn_norm(hidden)))
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
class ArkttsCodecWindowTransformer(nn.Module):
|
| 142 |
+
def __init__(self, config, input_dim: int, window_size: int | None, causal: bool = True):
|
| 143 |
+
super().__init__()
|
| 144 |
+
self.layers = nn.ModuleList([ArkttsCodecTransformerBlock(config) for _ in range(config.n_layer)])
|
| 145 |
+
self.norm = ArkttsCodecRMSNorm(config.dim, config.norm_eps)
|
| 146 |
+
self.window_size = window_size
|
| 147 |
+
self.causal = causal
|
| 148 |
+
self.channels_first = config.channels_first
|
| 149 |
+
self.input_proj = nn.Linear(input_dim, config.dim) if input_dim != config.dim else nn.Identity()
|
| 150 |
+
self.output_proj = nn.Linear(config.dim, input_dim) if input_dim != config.dim else nn.Identity()
|
| 151 |
+
self.look_ahead_conv = nn.Identity()
|
| 152 |
+
self.head_dim = config.head_dim
|
| 153 |
+
self.rope_base = config.rope_base
|
| 154 |
+
|
| 155 |
+
def forward(self, x, x_lens=None):
|
| 156 |
+
del x_lens
|
| 157 |
+
if self.channels_first:
|
| 158 |
+
x = x.transpose(1, 2)
|
| 159 |
+
x = self.look_ahead_conv(self.input_proj(x))
|
| 160 |
+
length = x.shape[1]
|
| 161 |
+
row = torch.arange(length, device=x.device)[:, None]
|
| 162 |
+
column = torch.arange(length, device=x.device)[None, :]
|
| 163 |
+
mask = column <= row
|
| 164 |
+
if self.window_size is not None:
|
| 165 |
+
mask &= column >= (row - self.window_size + 1).clamp_min(0)
|
| 166 |
+
mask = mask[None, None]
|
| 167 |
+
rope_values = _rope(length, self.head_dim, self.rope_base, x.device)
|
| 168 |
+
for layer in self.layers:
|
| 169 |
+
x = layer(x, rope_values, mask)
|
| 170 |
+
x = self.output_proj(self.norm(x))
|
| 171 |
+
return x.transpose(1, 2) if self.channels_first else x
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
def _extra_padding(x, kernel_size: int, stride: int, padding_total: int = 0) -> int:
|
| 175 |
+
length = x.shape[-1]
|
| 176 |
+
frames = (length - kernel_size + padding_total) / stride + 1
|
| 177 |
+
ideal = (math.ceil(frames) - 1) * stride + kernel_size - padding_total
|
| 178 |
+
return ideal - length
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
class ArkttsCausalConv1d(nn.Module):
|
| 182 |
+
def __init__(self, in_channels, out_channels, kernel_size, dilation=1, stride=1, groups=1):
|
| 183 |
+
super().__init__()
|
| 184 |
+
self.conv = nn.Conv1d(
|
| 185 |
+
in_channels, out_channels, kernel_size, stride=stride, dilation=dilation, groups=groups
|
| 186 |
+
)
|
| 187 |
+
self.stride = stride
|
| 188 |
+
self.kernel_size = (kernel_size - 1) * dilation + 1
|
| 189 |
+
self.padding = self.kernel_size - self.stride
|
| 190 |
+
|
| 191 |
+
def forward(self, x):
|
| 192 |
+
right = _extra_padding(x, self.kernel_size, self.stride, self.padding)
|
| 193 |
+
return self.conv(F.pad(x, (self.padding, right))).contiguous()
|
| 194 |
+
|
| 195 |
+
def apply_weight_norm(self):
|
| 196 |
+
self.conv = weight_norm(self.conv)
|
| 197 |
+
return self
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
class ArkttsCausalConvTranspose1d(nn.Module):
|
| 201 |
+
def __init__(self, in_channels, out_channels, kernel_size, stride=1, dilation=1):
|
| 202 |
+
super().__init__()
|
| 203 |
+
self.conv = nn.ConvTranspose1d(
|
| 204 |
+
in_channels, out_channels, kernel_size, stride=stride, dilation=dilation
|
| 205 |
+
)
|
| 206 |
+
self.stride = stride
|
| 207 |
+
self.kernel_size = kernel_size
|
| 208 |
+
|
| 209 |
+
def forward(self, x):
|
| 210 |
+
x = self.conv(x)
|
| 211 |
+
crop = self.kernel_size - self.stride
|
| 212 |
+
return x[..., : x.shape[-1] - crop].contiguous() if crop else x.contiguous()
|
| 213 |
+
|
| 214 |
+
def apply_weight_norm(self):
|
| 215 |
+
self.conv = weight_norm(self.conv)
|
| 216 |
+
return self
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
def _causal_wn_conv(*args, **kwargs):
|
| 220 |
+
return ArkttsCausalConv1d(*args, **kwargs).apply_weight_norm()
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
def _causal_wn_transpose(*args, **kwargs):
|
| 224 |
+
return ArkttsCausalConvTranspose1d(*args, **kwargs).apply_weight_norm()
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
@torch.jit.script
|
| 228 |
+
def _arktts_snake(x: Tensor, alpha: Tensor) -> Tensor:
|
| 229 |
+
shape = x.shape
|
| 230 |
+
x = x.reshape(shape[0], shape[1], -1)
|
| 231 |
+
x = x + (alpha + 1e-9).reciprocal() * torch.sin(alpha * x).pow(2)
|
| 232 |
+
return x.reshape(shape)
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
class ArkttsSnake1d(nn.Module):
|
| 236 |
+
def __init__(self, channels):
|
| 237 |
+
super().__init__()
|
| 238 |
+
self.alpha = nn.Parameter(torch.ones(1, channels, 1))
|
| 239 |
+
|
| 240 |
+
def forward(self, x):
|
| 241 |
+
return _arktts_snake(x, self.alpha)
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
class ArkttsResidualUnit(nn.Module):
|
| 245 |
+
def __init__(self, dim: int, dilation: int):
|
| 246 |
+
super().__init__()
|
| 247 |
+
self.block = nn.Sequential(
|
| 248 |
+
ArkttsSnake1d(dim),
|
| 249 |
+
_causal_wn_conv(dim, dim, kernel_size=7, dilation=dilation),
|
| 250 |
+
ArkttsSnake1d(dim),
|
| 251 |
+
_causal_wn_conv(dim, dim, kernel_size=1),
|
| 252 |
+
)
|
| 253 |
+
|
| 254 |
+
def forward(self, x):
|
| 255 |
+
output = self.block(x)
|
| 256 |
+
difference = x.shape[-1] - output.shape[-1]
|
| 257 |
+
if difference > 0:
|
| 258 |
+
x = x[..., :-difference]
|
| 259 |
+
return x + output
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
class ArkttsEncoderBlock(nn.Module):
|
| 263 |
+
def __init__(self, dim: int, stride: int, transformer_layers: int):
|
| 264 |
+
super().__init__()
|
| 265 |
+
modules = [
|
| 266 |
+
ArkttsResidualUnit(dim // 2, 1),
|
| 267 |
+
ArkttsResidualUnit(dim // 2, 3),
|
| 268 |
+
ArkttsResidualUnit(dim // 2, 9),
|
| 269 |
+
ArkttsSnake1d(dim // 2),
|
| 270 |
+
_causal_wn_conv(dim // 2, dim, kernel_size=2 * stride, stride=stride),
|
| 271 |
+
]
|
| 272 |
+
if transformer_layers:
|
| 273 |
+
config = ArkttsCodecTransformerConfig(
|
| 274 |
+
n_layer=transformer_layers,
|
| 275 |
+
n_head=dim // 64,
|
| 276 |
+
dim=dim,
|
| 277 |
+
intermediate_size=dim * 3,
|
| 278 |
+
)
|
| 279 |
+
modules.append(ArkttsCodecWindowTransformer(config, dim, window_size=512))
|
| 280 |
+
else:
|
| 281 |
+
modules.append(nn.Identity())
|
| 282 |
+
self.block = nn.Sequential(*modules)
|
| 283 |
+
|
| 284 |
+
def forward(self, x):
|
| 285 |
+
return self.block(x)
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
class ArkttsEncoder(nn.Module):
|
| 289 |
+
def __init__(self):
|
| 290 |
+
super().__init__()
|
| 291 |
+
dim = 64
|
| 292 |
+
modules = [_causal_wn_conv(1, dim, kernel_size=7)]
|
| 293 |
+
for stride, transformer_layers in zip((2, 4, 8, 8), (0, 0, 0, 4)):
|
| 294 |
+
dim *= 2
|
| 295 |
+
modules.append(ArkttsEncoderBlock(dim, stride, transformer_layers))
|
| 296 |
+
modules.extend((ArkttsSnake1d(dim), _causal_wn_conv(dim, 1024, kernel_size=3)))
|
| 297 |
+
self.block = nn.Sequential(*modules)
|
| 298 |
+
|
| 299 |
+
def forward(self, x):
|
| 300 |
+
return self.block(x)
|
| 301 |
+
|
| 302 |
+
|
| 303 |
+
class ArkttsDecoderBlock(nn.Module):
|
| 304 |
+
def __init__(self, input_dim: int, output_dim: int, stride: int):
|
| 305 |
+
super().__init__()
|
| 306 |
+
self.block = nn.Sequential(
|
| 307 |
+
ArkttsSnake1d(input_dim),
|
| 308 |
+
_causal_wn_transpose(input_dim, output_dim, kernel_size=2 * stride, stride=stride),
|
| 309 |
+
ArkttsResidualUnit(output_dim, 1),
|
| 310 |
+
ArkttsResidualUnit(output_dim, 3),
|
| 311 |
+
ArkttsResidualUnit(output_dim, 9),
|
| 312 |
+
)
|
| 313 |
+
|
| 314 |
+
def forward(self, x):
|
| 315 |
+
return self.block(x)
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
class ArkttsDecoder(nn.Module):
|
| 319 |
+
def __init__(self):
|
| 320 |
+
super().__init__()
|
| 321 |
+
channels = 1536
|
| 322 |
+
modules = [_causal_wn_conv(1024, channels, kernel_size=7)]
|
| 323 |
+
for index, stride in enumerate((8, 8, 4, 2)):
|
| 324 |
+
input_dim = channels // (2**index)
|
| 325 |
+
output_dim = channels // (2 ** (index + 1))
|
| 326 |
+
modules.append(ArkttsDecoderBlock(input_dim, output_dim, stride))
|
| 327 |
+
modules.extend((ArkttsSnake1d(output_dim), _causal_wn_conv(output_dim, 1, kernel_size=7), nn.Tanh()))
|
| 328 |
+
self.model = nn.Sequential(*modules)
|
| 329 |
+
|
| 330 |
+
def forward(self, x):
|
| 331 |
+
return self.model(x)
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
class ArkttsVectorQuantizer(nn.Module):
|
| 335 |
+
def __init__(self, input_dim: int, codebook_size: int, codebook_dim: int):
|
| 336 |
+
super().__init__()
|
| 337 |
+
self.codebook_size = int(codebook_size)
|
| 338 |
+
self.codebook_dim = int(codebook_dim)
|
| 339 |
+
self.in_proj = legacy_weight_norm(nn.Conv1d(input_dim, codebook_dim, kernel_size=1))
|
| 340 |
+
self.out_proj = legacy_weight_norm(nn.Conv1d(codebook_dim, input_dim, kernel_size=1))
|
| 341 |
+
self.codebook = nn.Embedding(codebook_size, codebook_dim)
|
| 342 |
+
|
| 343 |
+
def decode_code(self, indices):
|
| 344 |
+
return F.embedding(indices, self.codebook.weight).transpose(1, 2)
|
| 345 |
+
|
| 346 |
+
def decode_latents(self, latents):
|
| 347 |
+
batch, _, length = latents.shape
|
| 348 |
+
flattened = latents.transpose(1, 2).reshape(batch * length, -1)
|
| 349 |
+
flattened = F.normalize(flattened)
|
| 350 |
+
codebook = F.normalize(self.codebook.weight)
|
| 351 |
+
distances = (
|
| 352 |
+
flattened.pow(2).sum(1, keepdim=True)
|
| 353 |
+
- 2 * flattened @ codebook.t()
|
| 354 |
+
+ codebook.pow(2).sum(1, keepdim=True).t()
|
| 355 |
+
)
|
| 356 |
+
indices = (-distances).argmax(1).view(batch, length)
|
| 357 |
+
return self.decode_code(indices), indices
|
| 358 |
+
|
| 359 |
+
def forward(self, z):
|
| 360 |
+
projected = self.in_proj(z)
|
| 361 |
+
quantized, indices = self.decode_latents(projected)
|
| 362 |
+
quantized_st = projected + (quantized - projected).detach()
|
| 363 |
+
return self.out_proj(quantized_st), indices, projected
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
class ArkttsResidualQuantizer(nn.Module):
|
| 367 |
+
def __init__(self, input_dim: int, n_codebooks: int, codebook_size: int, codebook_dim: int):
|
| 368 |
+
super().__init__()
|
| 369 |
+
self.n_codebooks = int(n_codebooks)
|
| 370 |
+
self.codebook_size = int(codebook_size)
|
| 371 |
+
self.quantizers = nn.ModuleList([
|
| 372 |
+
ArkttsVectorQuantizer(input_dim, codebook_size, codebook_dim)
|
| 373 |
+
for _ in range(n_codebooks)
|
| 374 |
+
])
|
| 375 |
+
|
| 376 |
+
def forward(self, z):
|
| 377 |
+
quantized_sum = 0.0
|
| 378 |
+
residual = z
|
| 379 |
+
codes = []
|
| 380 |
+
for quantizer in self.quantizers:
|
| 381 |
+
quantized, indices, _ = quantizer(residual)
|
| 382 |
+
quantized_sum = quantized_sum + quantized
|
| 383 |
+
residual = residual - quantized
|
| 384 |
+
codes.append(indices)
|
| 385 |
+
return quantized_sum, torch.stack(codes, dim=1)
|
| 386 |
+
|
| 387 |
+
def from_codes(self, codes):
|
| 388 |
+
output = 0.0
|
| 389 |
+
for index in range(codes.shape[1]):
|
| 390 |
+
projected = self.quantizers[index].decode_code(codes[:, index])
|
| 391 |
+
output = output + self.quantizers[index].out_proj(projected)
|
| 392 |
+
return output
|
| 393 |
+
|
| 394 |
+
|
| 395 |
+
class ArkttsConvNeXtBlock(nn.Module):
|
| 396 |
+
def __init__(self, dim: int):
|
| 397 |
+
super().__init__()
|
| 398 |
+
self.dwconv = ArkttsCausalConv1d(dim, dim, kernel_size=7, groups=dim)
|
| 399 |
+
self.norm = nn.LayerNorm(dim, eps=1e-6)
|
| 400 |
+
self.pwconv1 = nn.Linear(dim, 4 * dim)
|
| 401 |
+
self.act = nn.GELU()
|
| 402 |
+
self.pwconv2 = nn.Linear(4 * dim, dim)
|
| 403 |
+
self.gamma = nn.Parameter(1e-6 * torch.ones(dim))
|
| 404 |
+
|
| 405 |
+
def forward(self, x):
|
| 406 |
+
residual = x
|
| 407 |
+
x = self.dwconv(x).permute(0, 2, 1)
|
| 408 |
+
x = self.pwconv2(self.act(self.pwconv1(self.norm(x))))
|
| 409 |
+
x = (self.gamma * x).permute(0, 2, 1)
|
| 410 |
+
return residual + x
|
| 411 |
+
|
| 412 |
+
|
| 413 |
+
class ArkttsDownsampleQuantizer(nn.Module):
|
| 414 |
+
def __init__(self, config=None):
|
| 415 |
+
super().__init__()
|
| 416 |
+
self.semantic_quantizer = ArkttsResidualQuantizer(1024, 1, 4096, 8)
|
| 417 |
+
self.quantizer = ArkttsResidualQuantizer(1024, 9, 1024, 8)
|
| 418 |
+
self.downsample = nn.Sequential(
|
| 419 |
+
nn.Sequential(
|
| 420 |
+
ArkttsCausalConv1d(1024, 1024, kernel_size=2, stride=2),
|
| 421 |
+
ArkttsConvNeXtBlock(1024),
|
| 422 |
+
),
|
| 423 |
+
nn.Sequential(
|
| 424 |
+
ArkttsCausalConv1d(1024, 1024, kernel_size=2, stride=2),
|
| 425 |
+
ArkttsConvNeXtBlock(1024),
|
| 426 |
+
),
|
| 427 |
+
)
|
| 428 |
+
self.upsample = nn.Sequential(
|
| 429 |
+
nn.Sequential(
|
| 430 |
+
ArkttsCausalConvTranspose1d(1024, 1024, kernel_size=2, stride=2),
|
| 431 |
+
ArkttsConvNeXtBlock(1024),
|
| 432 |
+
),
|
| 433 |
+
nn.Sequential(
|
| 434 |
+
ArkttsCausalConvTranspose1d(1024, 1024, kernel_size=2, stride=2),
|
| 435 |
+
ArkttsConvNeXtBlock(1024),
|
| 436 |
+
),
|
| 437 |
+
)
|
| 438 |
+
pre_transformer_config = ArkttsCodecTransformerConfig(
|
| 439 |
+
n_layer=8,
|
| 440 |
+
n_head=16,
|
| 441 |
+
dim=1024,
|
| 442 |
+
intermediate_size=3072,
|
| 443 |
+
)
|
| 444 |
+
post_transformer_config = ArkttsCodecTransformerConfig(
|
| 445 |
+
n_layer=int(getattr(config, "codec_post_n_layer", 8)),
|
| 446 |
+
n_head=int(getattr(config, "codec_post_n_head", 16)),
|
| 447 |
+
n_local_heads=int(getattr(config, "codec_post_n_local_heads", 8)),
|
| 448 |
+
dim=1024,
|
| 449 |
+
intermediate_size=int(getattr(config, "codec_post_intermediate_size", 1216)),
|
| 450 |
+
)
|
| 451 |
+
self.pre_module = ArkttsCodecWindowTransformer(
|
| 452 |
+
pre_transformer_config, 1024, window_size=128
|
| 453 |
+
)
|
| 454 |
+
self.post_module = ArkttsCodecWindowTransformer(
|
| 455 |
+
post_transformer_config, 1024, window_size=128
|
| 456 |
+
)
|
| 457 |
+
self.semantic_predictor_module = nn.Identity()
|
| 458 |
+
|
| 459 |
+
def forward(self, z):
|
| 460 |
+
original_length = z.shape[-1]
|
| 461 |
+
z = self.pre_module(self.downsample(z))
|
| 462 |
+
semantic, semantic_codes = self.semantic_quantizer(z)
|
| 463 |
+
residual, residual_codes = self.quantizer(z - semantic)
|
| 464 |
+
z = self.upsample(self.post_module(semantic + residual))
|
| 465 |
+
difference = original_length - z.shape[-1]
|
| 466 |
+
if difference > 0:
|
| 467 |
+
z = F.pad(z, (difference, 0))
|
| 468 |
+
elif difference < 0:
|
| 469 |
+
z = z[..., -difference:]
|
| 470 |
+
return z, torch.cat((semantic_codes, residual_codes), dim=1)
|
| 471 |
+
|
| 472 |
+
def decode(self, indices):
|
| 473 |
+
indices = indices.clone()
|
| 474 |
+
indices[:, 0].clamp_(0, self.semantic_quantizer.codebook_size - 1)
|
| 475 |
+
indices[:, 1:].clamp_(0, self.quantizer.codebook_size - 1)
|
| 476 |
+
semantic = self.semantic_quantizer.from_codes(indices[:, :1])
|
| 477 |
+
residual = self.quantizer.from_codes(indices[:, 1:])
|
| 478 |
+
return self.upsample(self.post_module(semantic + residual))
|
| 479 |
+
|
| 480 |
+
|
| 481 |
+
class ArkttsCodec(nn.Module):
|
| 482 |
+
sample_rate = 44100
|
| 483 |
+
hop_length = 512
|
| 484 |
+
frame_length = 2048
|
| 485 |
+
|
| 486 |
+
def __init__(self, config=None):
|
| 487 |
+
super().__init__()
|
| 488 |
+
self.encoder = ArkttsEncoder()
|
| 489 |
+
self.quantizer = ArkttsDownsampleQuantizer(config)
|
| 490 |
+
self.decoder = ArkttsDecoder()
|
| 491 |
+
|
| 492 |
+
@torch.inference_mode()
|
| 493 |
+
def encode(self, audio, audio_lengths=None):
|
| 494 |
+
if audio.ndim == 2:
|
| 495 |
+
audio = audio[:, None]
|
| 496 |
+
if audio.ndim != 3 or audio.shape[1] != 1:
|
| 497 |
+
raise ValueError("audio must have shape [B, 1, samples]")
|
| 498 |
+
original_length = audio.shape[-1]
|
| 499 |
+
right = math.ceil(original_length / self.frame_length) * self.frame_length - original_length
|
| 500 |
+
if right:
|
| 501 |
+
audio = F.pad(audio, (0, right))
|
| 502 |
+
if audio_lengths is None:
|
| 503 |
+
audio_lengths = torch.full(
|
| 504 |
+
(audio.shape[0],), original_length, device=audio.device, dtype=torch.long
|
| 505 |
+
)
|
| 506 |
+
encoded = self.encoder(audio)
|
| 507 |
+
_, codes = self.quantizer(encoded)
|
| 508 |
+
code_lengths = torch.ceil(audio_lengths.float() / self.frame_length).long()
|
| 509 |
+
max_codes = codes.shape[-1]
|
| 510 |
+
padded = torch.full_like(codes, -1)
|
| 511 |
+
for index, length in enumerate(code_lengths.tolist()):
|
| 512 |
+
padded[index, :, : min(length, max_codes)] = codes[index, :, : min(length, max_codes)]
|
| 513 |
+
return padded, code_lengths.clamp_max(max_codes)
|
| 514 |
+
|
| 515 |
+
@torch.inference_mode()
|
| 516 |
+
def decode(self, codes):
|
| 517 |
+
return self.decoder(self.quantizer.decode(codes.long()))
|
| 518 |
+
|
| 519 |
+
|
| 520 |
+
__all__ = ["ArkttsCodec"]
|
preprocessor_config.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"audio_sampling_rate": 44100,
|
| 3 |
+
"auto_map": {
|
| 4 |
+
"AutoProcessor": "processing_arktts.ArkttsProcessor"
|
| 5 |
+
},
|
| 6 |
+
"feature_extractor_type": "ArkttsProcessor",
|
| 7 |
+
"num_codebooks": 10,
|
| 8 |
+
"processor_class": "ArkttsProcessor",
|
| 9 |
+
"semantic_begin_id": 151678
|
| 10 |
+
}
|
processing_arktts.py
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import inspect
|
| 4 |
+
import json
|
| 5 |
+
import os
|
| 6 |
+
import re
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
from typing import Any, Sequence
|
| 9 |
+
|
| 10 |
+
import numpy as np
|
| 11 |
+
import torch
|
| 12 |
+
from transformers import AutoTokenizer
|
| 13 |
+
from transformers.feature_extraction_utils import BatchFeature
|
| 14 |
+
from transformers.processing_utils import ProcessorMixin
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def _clean_text(text: str) -> str:
|
| 18 |
+
return " ".join(str(text).strip().split())
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def _as_list(value: Any, batch_size: int, name: str) -> list[Any]:
|
| 22 |
+
if isinstance(value, (str, Path)) or value is None or np.isscalar(value):
|
| 23 |
+
return [value] * batch_size
|
| 24 |
+
if isinstance(value, torch.Tensor) and value.ndim <= 2:
|
| 25 |
+
return [value] if batch_size == 1 else list(value)
|
| 26 |
+
if isinstance(value, np.ndarray) and value.ndim <= 2:
|
| 27 |
+
return [value] if batch_size == 1 else list(value)
|
| 28 |
+
values = list(value)
|
| 29 |
+
if len(values) != batch_size:
|
| 30 |
+
raise ValueError(f"{name} must contain {batch_size} items, got {len(values)}")
|
| 31 |
+
return values
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def _pad_1d(rows: list[torch.Tensor], pad_value: int) -> tuple[torch.Tensor, torch.Tensor]:
|
| 35 |
+
max_len = max((row.numel() for row in rows), default=0)
|
| 36 |
+
values = torch.full((len(rows), max_len), pad_value, dtype=torch.long)
|
| 37 |
+
mask = torch.zeros((len(rows), max_len), dtype=torch.long)
|
| 38 |
+
for idx, row in enumerate(rows):
|
| 39 |
+
length = row.numel()
|
| 40 |
+
values[idx, :length] = row
|
| 41 |
+
mask[idx, :length] = 1
|
| 42 |
+
return values, mask
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class ArkttsProcessor(ProcessorMixin):
|
| 46 |
+
attributes = ["tokenizer"]
|
| 47 |
+
tokenizer_class = ("PreTrainedTokenizerFast", "PreTrainedTokenizer")
|
| 48 |
+
valid_kwargs = ["num_codebooks", "semantic_begin_id", "audio_sampling_rate"]
|
| 49 |
+
|
| 50 |
+
def __init__(
|
| 51 |
+
self,
|
| 52 |
+
tokenizer,
|
| 53 |
+
num_codebooks: int = 10,
|
| 54 |
+
semantic_begin_id: int = 151678,
|
| 55 |
+
audio_sampling_rate: int = 44100,
|
| 56 |
+
**kwargs,
|
| 57 |
+
):
|
| 58 |
+
super().__init__(tokenizer)
|
| 59 |
+
self.num_codebooks = int(num_codebooks)
|
| 60 |
+
self.semantic_begin_id = int(semantic_begin_id)
|
| 61 |
+
self.audio_sampling_rate = int(audio_sampling_rate)
|
| 62 |
+
|
| 63 |
+
@classmethod
|
| 64 |
+
def from_pretrained(cls, pretrained_model_name_or_path: str, **kwargs) -> "ArkttsProcessor":
|
| 65 |
+
trust_remote_code = bool(kwargs.pop("trust_remote_code", False))
|
| 66 |
+
shared_names = {
|
| 67 |
+
"cache_dir", "force_download", "local_files_only", "token", "revision", "subfolder"
|
| 68 |
+
}
|
| 69 |
+
shared = {key: kwargs[key] for key in list(kwargs) if key in shared_names}
|
| 70 |
+
config = {}
|
| 71 |
+
local_config = os.path.join(str(pretrained_model_name_or_path), "processor_config.json")
|
| 72 |
+
if os.path.isfile(local_config):
|
| 73 |
+
with open(local_config, "r", encoding="utf-8") as handle:
|
| 74 |
+
config = json.load(handle)
|
| 75 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
| 76 |
+
pretrained_model_name_or_path,
|
| 77 |
+
use_fast=True,
|
| 78 |
+
trust_remote_code=trust_remote_code,
|
| 79 |
+
fix_mistral_regex=False,
|
| 80 |
+
**shared,
|
| 81 |
+
)
|
| 82 |
+
return cls(
|
| 83 |
+
tokenizer=tokenizer,
|
| 84 |
+
num_codebooks=config.get("num_codebooks", 10),
|
| 85 |
+
semantic_begin_id=config.get("semantic_begin_id", 151678),
|
| 86 |
+
audio_sampling_rate=config.get("audio_sampling_rate", 44100),
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
def _encode(self, text: str) -> torch.Tensor:
|
| 90 |
+
encode_kwargs = {"add_special_tokens": False}
|
| 91 |
+
if "allowed_special" in inspect.signature(self.tokenizer.encode).parameters:
|
| 92 |
+
encode_kwargs["allowed_special"] = "all"
|
| 93 |
+
return torch.tensor(self.tokenizer.encode(text, **encode_kwargs), dtype=torch.long)
|
| 94 |
+
|
| 95 |
+
@staticmethod
|
| 96 |
+
def _format_reference_text(text: str) -> str:
|
| 97 |
+
cleaned = _clean_text(text)
|
| 98 |
+
if re.search(r"<\|speaker:\d+\|>", cleaned):
|
| 99 |
+
return cleaned
|
| 100 |
+
return f"<|speaker:0|>{cleaned}"
|
| 101 |
+
|
| 102 |
+
def _prompt_segments(self, text: str, reference_text: str | None, has_reference: bool):
|
| 103 |
+
target = _clean_text(text)
|
| 104 |
+
if not target:
|
| 105 |
+
raise ValueError("text must not be empty")
|
| 106 |
+
def encode_parts(parts: list[str]) -> torch.Tensor:
|
| 107 |
+
return torch.cat([self._encode(part) for part in parts])
|
| 108 |
+
|
| 109 |
+
if not has_reference:
|
| 110 |
+
full = encode_parts([
|
| 111 |
+
"<|im_start|>system\n",
|
| 112 |
+
"convert the provided text to speech",
|
| 113 |
+
"<|im_end|>\n",
|
| 114 |
+
"<|im_start|>user\n",
|
| 115 |
+
target,
|
| 116 |
+
"<|im_end|>\n",
|
| 117 |
+
"<|im_start|>assistant\n<|voice|>",
|
| 118 |
+
])
|
| 119 |
+
return full, self._encode("")
|
| 120 |
+
if not reference_text:
|
| 121 |
+
raise ValueError("reference_text is required when a reference voice is provided")
|
| 122 |
+
prefix = encode_parts([
|
| 123 |
+
"<|im_start|>system\n",
|
| 124 |
+
"convert the provided text to speech reference to the following:\n\nText:\n",
|
| 125 |
+
self._format_reference_text(reference_text),
|
| 126 |
+
"\n\nSpeech:\n",
|
| 127 |
+
])
|
| 128 |
+
suffix = encode_parts([
|
| 129 |
+
"<|im_end|>\n",
|
| 130 |
+
"<|im_start|>user\n",
|
| 131 |
+
target,
|
| 132 |
+
"<|im_end|>\n",
|
| 133 |
+
"<|im_start|>assistant\n<|voice|>",
|
| 134 |
+
])
|
| 135 |
+
return prefix, suffix
|
| 136 |
+
|
| 137 |
+
def _load_audio(self, value: Any, sampling_rate: int | None) -> torch.Tensor:
|
| 138 |
+
source_rate = sampling_rate
|
| 139 |
+
if isinstance(value, (str, Path)):
|
| 140 |
+
try:
|
| 141 |
+
import soundfile as sf
|
| 142 |
+
except ImportError as exc:
|
| 143 |
+
raise ImportError("soundfile is required for reference audio paths") from exc
|
| 144 |
+
array, source_rate = sf.read(str(value), dtype="float32", always_2d=True)
|
| 145 |
+
array = array.mean(axis=1)
|
| 146 |
+
audio = torch.from_numpy(np.asarray(array, dtype=np.float32))
|
| 147 |
+
else:
|
| 148 |
+
if isinstance(value, dict):
|
| 149 |
+
source_rate = value.get("sampling_rate", source_rate)
|
| 150 |
+
value = value.get("array")
|
| 151 |
+
if isinstance(value, (tuple, list)) and len(value) == 2 and np.isscalar(value[1]):
|
| 152 |
+
value, source_rate = value
|
| 153 |
+
audio = torch.as_tensor(value, dtype=torch.float32)
|
| 154 |
+
if audio.ndim == 2:
|
| 155 |
+
audio = audio.mean(dim=0)
|
| 156 |
+
if audio.ndim != 1:
|
| 157 |
+
raise ValueError(f"reference audio must be mono or channels-first, got {tuple(audio.shape)}")
|
| 158 |
+
if audio.numel() == 0:
|
| 159 |
+
raise ValueError("reference audio must not be empty")
|
| 160 |
+
if source_rate is None:
|
| 161 |
+
raise ValueError("sampling_rate is required for reference audio arrays")
|
| 162 |
+
if int(source_rate) != self.audio_sampling_rate:
|
| 163 |
+
try:
|
| 164 |
+
from torchaudio.functional import resample
|
| 165 |
+
except ImportError as exc:
|
| 166 |
+
raise ImportError("torchaudio is required to resample reference audio") from exc
|
| 167 |
+
audio = resample(audio, int(source_rate), self.audio_sampling_rate)
|
| 168 |
+
return audio.contiguous()
|
| 169 |
+
|
| 170 |
+
def __call__(
|
| 171 |
+
self,
|
| 172 |
+
text: str | Sequence[str],
|
| 173 |
+
reference_text: str | Sequence[str] | None = None,
|
| 174 |
+
reference_audio: Any = None,
|
| 175 |
+
reference_codes: Any = None,
|
| 176 |
+
sampling_rate: int | Sequence[int] | None = None,
|
| 177 |
+
return_tensors: str = "pt",
|
| 178 |
+
**kwargs,
|
| 179 |
+
) -> BatchFeature:
|
| 180 |
+
if kwargs:
|
| 181 |
+
raise TypeError(f"Unexpected processor arguments: {sorted(kwargs)}")
|
| 182 |
+
if return_tensors != "pt":
|
| 183 |
+
raise ValueError("ArkttsProcessor currently supports return_tensors='pt' only")
|
| 184 |
+
texts = [text] if isinstance(text, str) else list(text)
|
| 185 |
+
if not texts:
|
| 186 |
+
raise ValueError("text batch must not be empty")
|
| 187 |
+
batch_size = len(texts)
|
| 188 |
+
ref_texts = _as_list(reference_text, batch_size, "reference_text")
|
| 189 |
+
if reference_audio is not None and reference_codes is not None:
|
| 190 |
+
raise ValueError("Provide reference_audio or reference_codes, not both")
|
| 191 |
+
has_reference = reference_audio is not None or reference_codes is not None
|
| 192 |
+
|
| 193 |
+
prefix_rows, suffix_rows = zip(*[
|
| 194 |
+
self._prompt_segments(item, ref_texts[idx], has_reference)
|
| 195 |
+
for idx, item in enumerate(texts)
|
| 196 |
+
])
|
| 197 |
+
prefix_ids, prefix_mask = _pad_1d(list(prefix_rows), self.tokenizer.pad_token_id)
|
| 198 |
+
suffix_ids, suffix_mask = _pad_1d(list(suffix_rows), self.tokenizer.pad_token_id)
|
| 199 |
+
data: dict[str, torch.Tensor] = {
|
| 200 |
+
"prefix_input_ids": prefix_ids,
|
| 201 |
+
"prefix_attention_mask": prefix_mask,
|
| 202 |
+
"suffix_input_ids": suffix_ids,
|
| 203 |
+
"suffix_attention_mask": suffix_mask,
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
if reference_codes is not None:
|
| 207 |
+
code_items = _as_list(reference_codes, batch_size, "reference_codes")
|
| 208 |
+
loaded = []
|
| 209 |
+
for item in code_items:
|
| 210 |
+
if isinstance(item, (str, Path)):
|
| 211 |
+
item = np.load(str(item))
|
| 212 |
+
codes = torch.as_tensor(item, dtype=torch.long)
|
| 213 |
+
if codes.ndim != 2 or codes.shape[0] != self.num_codebooks or codes.shape[1] == 0:
|
| 214 |
+
raise ValueError(
|
| 215 |
+
f"reference codes must have shape [{self.num_codebooks}, T>0], got {tuple(codes.shape)}"
|
| 216 |
+
)
|
| 217 |
+
if codes.min() < 0 or codes.max() >= 4096:
|
| 218 |
+
raise ValueError("reference codes must be in [0, 4095]")
|
| 219 |
+
loaded.append(codes)
|
| 220 |
+
max_frames = max(item.shape[1] for item in loaded)
|
| 221 |
+
padded = torch.full((batch_size, self.num_codebooks, max_frames), -1, dtype=torch.long)
|
| 222 |
+
lengths = torch.empty(batch_size, dtype=torch.long)
|
| 223 |
+
for idx, codes in enumerate(loaded):
|
| 224 |
+
lengths[idx] = codes.shape[1]
|
| 225 |
+
padded[idx, :, : codes.shape[1]] = codes
|
| 226 |
+
data["reference_codes"] = padded
|
| 227 |
+
data["reference_code_lengths"] = lengths
|
| 228 |
+
|
| 229 |
+
if reference_audio is not None:
|
| 230 |
+
audio_items = _as_list(reference_audio, batch_size, "reference_audio")
|
| 231 |
+
rate_items = _as_list(sampling_rate, batch_size, "sampling_rate")
|
| 232 |
+
loaded_audio = [self._load_audio(item, rate_items[idx]) for idx, item in enumerate(audio_items)]
|
| 233 |
+
max_samples = max(item.numel() for item in loaded_audio)
|
| 234 |
+
padded_audio = torch.zeros((batch_size, 1, max_samples), dtype=torch.float32)
|
| 235 |
+
lengths = torch.empty(batch_size, dtype=torch.long)
|
| 236 |
+
for idx, audio in enumerate(loaded_audio):
|
| 237 |
+
lengths[idx] = audio.numel()
|
| 238 |
+
padded_audio[idx, 0, : audio.numel()] = audio
|
| 239 |
+
data["reference_audio_values"] = padded_audio
|
| 240 |
+
data["reference_audio_lengths"] = lengths
|
| 241 |
+
|
| 242 |
+
return BatchFeature(data=data)
|
| 243 |
+
|
| 244 |
+
@property
|
| 245 |
+
def model_input_names(self) -> list[str]:
|
| 246 |
+
return [
|
| 247 |
+
"prefix_input_ids", "prefix_attention_mask", "suffix_input_ids",
|
| 248 |
+
"suffix_attention_mask", "reference_codes", "reference_code_lengths",
|
| 249 |
+
"reference_audio_values", "reference_audio_lengths",
|
| 250 |
+
]
|
| 251 |
+
|
| 252 |
+
def batch_decode(self, *args, **kwargs):
|
| 253 |
+
return self.tokenizer.batch_decode(*args, **kwargs)
|
| 254 |
+
|
| 255 |
+
def decode(self, *args, **kwargs):
|
| 256 |
+
return self.tokenizer.decode(*args, **kwargs)
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
__all__ = ["ArkttsProcessor"]
|
processor_config.json
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"audio_sampling_rate": 44100,
|
| 3 |
+
"auto_map": {
|
| 4 |
+
"AutoProcessor": "processing_arktts.ArkttsProcessor"
|
| 5 |
+
},
|
| 6 |
+
"num_codebooks": 10,
|
| 7 |
+
"processor_class": "ArkttsProcessor",
|
| 8 |
+
"semantic_begin_id": 151678
|
| 9 |
+
}
|
special_tokens_map.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
tokenizer.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f24e08099d45a8adf3f52f5f0b03276e433bb9d689bb15fcbcc48ce58744588b
|
| 3 |
+
size 12217872
|
tokenizer_config.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|