demeleww commited on
Commit
8c9756c
·
verified ·
1 Parent(s): 36b0bbc

Upload folder using huggingface_hub

Browse files
Files changed (8) hide show
  1. LICENSE +57 -0
  2. README.md +94 -0
  3. config.yaml +127 -0
  4. full.pth +3 -0
  5. gpt.pth +3 -0
  6. model_v2.py +840 -0
  7. tokenizer_ethiopic.metadata.json +16 -0
  8. tokenizer_ethiopic.py +462 -0
LICENSE ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ bilibili Model Use License Agreement
2
+
3
+ By clicking “I agree” to this bilibili Model Use License Agreement (“this Agreement”) , or by otherwise using any portion or element of the Model or any Derivative Work, you will be deemed to have recognized and accepted the content of this Agreement, which is effective immediately. If you do not agree to this Agreement, you must immediately cease all use and permanently delete the Model and any Derivative Works.
4
+
5
+ 1. Definitions
6
+ 1.1 “This Agreement”: means the bilibili Model Use License Agreement, including all of its terms and conditions.
7
+ 1.2 “We”, “us”, or “our”: means bilibili , the original right-holder of the Model.
8
+ 1.3 “You”: means any natural person or legal entity exercising rights granted by this Agreement and/or using the Model for any purpose and in any field of use.
9
+ 1.4 “Model”: means the artificial-intelligence model named “bilibili indextts2”, including but not limited to model weights and final code, in each case only to the extent that such components are published by us at https://github.com/index-tts/index-tts.
10
+ 1.5 “Derivative Work”: means any derivative of the Model, including without limitation:
11
+  (i) any modification of the Model, model outputs, or their derivatives;
12
+  (ii) any work based on the Model, model outputs, or their derivatives;
13
+  (iii) any other machine learning model which is created by re-training, fine-tuning, quantizing, LoRA, parameter-efficient fine-tuning, or any other method involving incremental weights or merged checkpoints, in each case based on the Model, model outputs, or their derivatives.
14
+ 1.6 “Use”: means downloading, copying, training, modifying, creating Derivative Works, distributing, publishing, running, fine-tuning, publicly displaying, communicating to the public, or otherwise exploiting the Model or any Derivative Work.
15
+
16
+ 2. Scope of License and Restrictions
17
+ 2.1 Subject to the terms and conditions of this Agreement, we grant you a worldwide, non-exclusive, non-transferable, royalty-free limited license to Use the Model or any Derivative Work based on the intellectual properties or other rights owned by Us embodied in the Model or any Derivative Work.
18
+ 2.2 If You intend to Use, or have already Used, the Model or any Derivative Work, and either (i) your or any of your Affiliates’ products or services had more than 100 million monthly active users in the immediately preceding calendar month, or (ii) your or any of your Affiliates’ annual revenue in the immediately preceding calendar year exceeded RMB 1 billion, You must request a separated license from us, which We may grant to You in our sole discretion. You are not authorized to exercise any of the rights under this Agreement unless and until We have expressly granted You such rights in writing.
19
+ 2.3 This Agreement is an open-source license for the Model in which we possess intellectual properties and other rights. It governs your Use of the Model only and does not limit any rights that we have regarding the Model.
20
+
21
+ 3. Disclaimer and Risk Allocation
22
+ 3.1 The Model and any outputs generated thereby are provided “AS IS,” without warranty of any kind, express or implied, including but not limited to warranties of merchantability, fitness for a particular purpose, non-infringement, absence of errors or omissions, continuity, accuracy, reliability, or stability. You are solely responsible for determining the appropriateness of using or redistributing the Model and assume all risks associated with exercising any rights granted under this Agreement.
23
+ 3.2 You shall bear sole responsibility for any infringement, illegality, breach of contract, damages, fines, regulatory investigations, or other liabilities (including, without limitation, infringement of third-party patents, copyrights, trademarks, trade secrets, personality rights, data-protection rights, or any other rights) arising out of or related to your Use of the Model or any outputs generated thereby. We assume no joint, several, supplementary, or advance payment liability.
24
+ 3.3 Under no circumstances shall we be liable to you or any third party for any direct, indirect, incidental, special, punitive, or consequential damages (including, without limitation, loss of data, business interruption, or loss of profits) arising out of or related to the Use of the Model, even if we have been advised of the possibility of such damages.
25
+ 3.4 Additional Obligations for You and Downstream Recipients
26
+ a) You must ensure that any downstream recipient of the Model or any Derivative Work that you distribute complies with this Agreement, and you must impose appropriate contractual terms on such downstream recipients. If any downstream recipient breaches this Agreement, you shall be responsible for the consequences thereof.
27
+ b) You must retain all original copyright notices and a copy of this Agreement in every copy of the Model or any Derivative Work that you Use.
28
+ c) You may not Use the bilibili indextts2 or any Derivative Work to improve any AI model, except for the bilibili indextts2 itself, its Derivative Works,or non-commercial AI models.
29
+
30
+ 4. Compliance Obligations
31
+ 4.1 Usage Restrictions
32
+ a) If you distribute a Derivative Work, you must clearly state in the distribution page or accompanying documentation: “Any modifications made to the original model in this Derivative Work are not endorsed, warranted, or guaranteed by the original right-holder of the original model, and the original right-holder disclaims all liability related to this Derivative Work.”
33
+ b) If your Use of the Model or any Derivative Work incorporates any third-party data or weights, you must obtain all necessary authorizations on your own and bear full responsibility for compliance.
34
+ c) You may not Use the Model or any Derivative Work for any purpose that violates the laws or regulatory requirements of the jurisdiction where the outputs and/or the Model are generated or used (including, without limitation, generating false information, discriminatory content, or content that infringes privacy).
35
+ d) If the Model or any Derivative Work is capable of generating content, you must ensure that such content does not violate the laws or regulatory requirements of the applicable jurisdiction (including, without limitation, generating false information, discriminatory content, or content that infringes privacy).
36
+ 4.2 Prohibited High-Risk Use
37
+ You must ensure that the Model and any Derivative Work are not deployed, directly or indirectly, in high-risk scenarios such as medical diagnosis, autonomous driving, military applications, critical-infrastructure control, large-scale biometric surveillance, or automated decision-making (e.g., credit or employment evaluations). If you insist on such deployment, you must independently complete all compliance obligations under applicable laws and regulations (including but not limited to GDPR, CCPA, HIPAA, export-control laws, and AI-specific regulations), and we shall bear no liability for any consequences arising therefrom.
38
+ 4.3 Infringement Liability
39
+ Should any third party raise claims against you with respect to any Derivative Work you develop or your Use of the Model or any Derivative Work, you shall bear full and independent responsibility for defending against and resolving such claims. If your actions cause us to incur any third-party claims, administrative penalties, or other losses, you shall indemnify us for all losses we thereby suffer, including but not limited to attorney fees, litigation costs, damages, and fines, and shall take all necessary measures to eliminate any adverse impact on us.
40
+
41
+ 5. Reserved Rights
42
+ 5.1 We reserve the right to revoke the license granted to you under this Agreement in the event of your breach. Upon revocation, you must immediately cease all Use and permanently delete all copies of the Model and any Derivative Work. Sections 3 and 6 of this Agreement shall survive termination of this Agreement under this circumstance.
43
+ 5.2 Nothing in this Agreement grants you any right to use our trade names, trademarks, service marks, or product names, except as reasonably and customarily required to describe the origin of the Model or any Derivative Work—such as reproducing the content of a NOTICE file under Section 3.4 of this Agreement.
44
+ 5.3 If you or any of your Affiliates institutes or participates in any legal proceeding (including any cross-claim or counterclaim in a lawsuit) against us or any of our Affiliates, alleging that the Model or any output or any portion thereof infringes any intellectual property or other rights that you own or control, all licenses granted to you under this Agreement shall terminate automatically as of the date such proceeding is filed.
45
+
46
+ 6. Governing Law and Dispute Resolution
47
+ 6.1 This Agreement shall be governed by and construed in accordance with the laws of the People’s Republic of China.
48
+ 6.2 In the event of any dispute arising out of or in connection with this Agreement, the parties shall first attempt to resolve such dispute through friendly negotiation. If negotiation fails, the dispute shall be submitted to the Shanghai Arbitration Commission for arbitration in accordance with its then-effective arbitration rules. The arbitration award shall be final and binding on both parties. The prevailing party shall be entitled to recover reasonable costs, including notarization and investigation fees, arbitration costs, attorneys’ fees, and travel expenses.
49
+
50
+ 7. Severability
51
+ If any provision of this Agreement is held to be invalid or unenforceable, the remaining provisions shall remain in full force and effect. The invalid or unenforceable provision shall be replaced with a valid and enforceable provision that, to the maximum extent permitted by law, most closely reflects the original intent of the invalid or unenforceable provision.
52
+
53
+ 8. Version Updates
54
+ We may release new versions of the AI Model Use License Agreement. Any new version will apply only to Uses occurring after the date of its release. If you obtained the Model under an earlier version, the new version will not have retroactive effect; nevertheless, you are encouraged to adopt the new version voluntarily.
55
+
56
+ 9. Language Version
57
+ In the event of any discrepancy or conflict between the English-language version set forth above and the Chinese-language version of this bilibili Model Use License Agreement, the Chinese-language version shall prevail for all purposes and shall govern the rights and obligations of the parties.
README.md ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: other
3
+ license_name: bilibili-model-use-license
4
+ license_link: https://huggingface.co/IndexTeam/IndexTTS-2.5/blob/main/LICENSE
5
+ language:
6
+ - am
7
+ - om
8
+ - ti
9
+ tags:
10
+ - text-to-speech
11
+ - amharic
12
+ - afaan-oromoo
13
+ - tigrinya
14
+ - ethiopia
15
+ - voice-cloning
16
+ - emotional-speech
17
+ - lab.et
18
+ - azemari
19
+ - indextts
20
+ pipeline_tag: text-to-speech
21
+ ---
22
+
23
+ # Azemari — Ethiopian Multilingual Voice (TTS)
24
+
25
+ **146 language tokens · 4 languages · 8 emotions · voice cloning · built in Addis Ababa**
26
+
27
+ [lab.et](https://lab.et) · [voice.et](https://voice.et) · [yakal.et](https://yakal.et)
28
+
29
+ ---
30
+
31
+ ## What is Azemari?
32
+
33
+ Named after the **Azemari** — Ethiopia's traditional improvising singer who can take any voice and any mood — Azemari is a fine-tuned IndexTTS-2.5 model that speaks **Amharic, Afaan Oromoo, and Tigrinya** with controlled emotion and voice cloning.
34
+
35
+ Built by [**lab.et**](https://lab.et), the AI lab of Ethiopia.
36
+
37
+ ## Capabilities
38
+
39
+ | Control | Token |
40
+ |---|---|
41
+ | Amharic | `<\|am\|>` |
42
+ | Afaan Oromoo | `<\|om\|>` |
43
+ | Tigrinya | `<\|ti\|>` |
44
+ | Shewa dialect | `<\|am\|><\|DIALECT_SHEWA\|>` |
45
+ | Emotion | 8-dim vector (happy, angry, sad, afraid, disgusted, melancholic, surprised, calm) |
46
+ | Voice | CAMPPlus speaker embedding from reference audio |
47
+
48
+ ## Usage
49
+
50
+ This model is the GPT component of [IndexTTS-2.5](https://huggingface.co/IndexTeam/IndexTTS-2.5). To run it:
51
+
52
+ 1. Set up the [IndexTTS-2.5 codebase](https://github.com/index-tts/index-tts)
53
+ 2. Replace the base `gpt.pth` with this checkpoint
54
+ 3. Use the provided `model_v2.py`, `tokenizer_ethiopic.py`, and `config.yaml`
55
+
56
+ ```python
57
+ # Example with the IndexTTS runtime
58
+ from indextts.infer_v2 import IndexTTS2
59
+ # ... standard IndexTTS-2.5 setup, pointing to this checkpoint
60
+ ```
61
+
62
+ ## Samples
63
+
64
+ Listen to generated samples at [Lab-et/indextts-samples](https://huggingface.co/datasets/Lab-et/indextts-samples) — emotions, voice cloning, multi-language tests.
65
+
66
+ ## Training
67
+
68
+ Fine-tuned from the [IndexTTS-2.5](https://huggingface.co/IndexTeam/IndexTTS-2.5) base checkpoint on **41,371 clips of human ground-truth Ethiopian speech** (605 hours — Amharic 61.6k, Afaan Oromoo 40.3k, Tigrinya 40.0k), with language-specific embedding rows added (109 total, +2 for OM/TI) and 5 dialect/emotion tokens extended (60,514 total, +4).
69
+
70
+ - Training: 58,000 steps, batch duration 1600s/update, learning rate 3.2e-3 → warmup → cosine
71
+ - Post-training: language/token embedding-only refinement pass (backbone frozen)
72
+ - Validation loss: 5.018 (step 3,000) → ~3.0 (step 58,000)
73
+
74
+ ## License
75
+
76
+ Inherited from [IndexTeam/IndexTTS-2.5](https://huggingface.co/IndexTeam/IndexTTS-2.5) — [bilibili Model Use License Agreement](https://huggingface.co/IndexTeam/IndexTTS-2.5/blob/main/LICENSE).
77
+
78
+ ## About lab.et
79
+
80
+ [**lab.et**](https://lab.et) is the AI lab of Ethiopia — building speech AI, models and infrastructure for Ethiopian languages in Addis Ababa. Our products: [voice.et](https://voice.et) (speech APIs) and [yakal.et](https://yakal.et) (the AI console, prepaid in Birr).
81
+
82
+ Open data: [amharic-corpus](https://huggingface.co/datasets/Lab-et/amharic-corpus) — 408.5 hours of Amharic speech.
83
+
84
+ ## Citation
85
+
86
+ ```bibtex
87
+ @model{labet_azemari,
88
+ title = {Azemari — Ethiopian Multilingual Voice (TTS)},
89
+ author = {lab.et},
90
+ year = {2026},
91
+ url = {https://huggingface.co/Lab-et/azemari},
92
+ note = {Fine-tuned from IndexTTS-2.5}
93
+ }
94
+ ```
config.yaml ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ dataset:
2
+ bpe_model: bpe.model
3
+ sample_rate: 24000
4
+ squeeze: false
5
+ mel:
6
+ sample_rate: 24000
7
+ n_fft: 1024
8
+ hop_length: 256
9
+ win_length: 1024
10
+ n_mels: 100
11
+ mel_fmin: 0
12
+ normalize: false
13
+ gpt:
14
+ model_dim: 1280
15
+ max_mel_tokens: 1815
16
+ max_text_tokens: 600
17
+ heads: 20
18
+ use_mel_codes_as_input: true
19
+ mel_length_compression: 1024
20
+ layers: 24
21
+ number_text_tokens: 60513
22
+ number_mel_codes: 8194
23
+ start_mel_token: 8192
24
+ stop_mel_token: 8193
25
+ start_text_token: 0
26
+ stop_text_token: 1
27
+ train_solo_embeddings: false
28
+ condition_type: conformer_perceiver
29
+ condition_module:
30
+ output_size: 512
31
+ linear_units: 2048
32
+ attention_heads: 8
33
+ num_blocks: 6
34
+ input_layer: conv2d2
35
+ perceiver_mult: 2
36
+ emo_condition_module:
37
+ output_size: 512
38
+ linear_units: 1024
39
+ attention_heads: 4
40
+ num_blocks: 4
41
+ input_layer: conv2d2
42
+ perceiver_mult: 2
43
+ semantic_codec:
44
+ codebook_size: 8192
45
+ hidden_size: 1024
46
+ codebook_dim: 8
47
+ vocos_dim: 384
48
+ vocos_intermediate_dim: 2048
49
+ vocos_num_layers: 12
50
+ s2mel:
51
+ preprocess_params:
52
+ sr: 22050
53
+ spect_params:
54
+ n_fft: 1024
55
+ win_length: 1024
56
+ hop_length: 256
57
+ n_mels: 80
58
+ fmin: 0
59
+ fmax: None
60
+ dit_type: DiT
61
+ reg_loss_type: l1
62
+ style_encoder:
63
+ dim: 192
64
+ length_regulator:
65
+ channels: 512
66
+ is_discrete: false
67
+ in_channels: 1024
68
+ content_codebook_size: 2048
69
+ sampling_ratios:
70
+ - 1
71
+ - 1
72
+ - 1
73
+ - 1
74
+ vector_quantize: false
75
+ n_codebooks: 1
76
+ quantizer_dropout: 0.0
77
+ f0_condition: false
78
+ n_f0_bins: 512
79
+ DiT:
80
+ hidden_dim: 512
81
+ num_heads: 8
82
+ depth: 13
83
+ class_dropout_prob: 0.1
84
+ block_size: 8192
85
+ in_channels: 80
86
+ style_condition: true
87
+ final_layer_type: wavenet
88
+ target: mel
89
+ content_dim: 512
90
+ content_codebook_size: 1024
91
+ content_type: discrete
92
+ f0_condition: false
93
+ n_f0_bins: 512
94
+ content_codebooks: 1
95
+ is_causal: false
96
+ long_skip_connection: true
97
+ zero_prompt_speech_token: false
98
+ time_as_token: false
99
+ style_as_token: false
100
+ uvit_skip_connection: true
101
+ add_resblock_in_transformer: false
102
+ wavenet:
103
+ hidden_dim: 512
104
+ num_layers: 8
105
+ kernel_size: 5
106
+ dilation_rate: 1
107
+ p_dropout: 0.2
108
+ style_condition: true
109
+ gpt_checkpoint: gpt.pth
110
+ w2v_stat: wav2vec2bert_stats.pt
111
+ s2mel_checkpoint: s2mel.pth
112
+ emo_matrix: feat2.pt
113
+ spk_matrix: feat1.pt
114
+ emo_num:
115
+ - 3
116
+ - 17
117
+ - 2
118
+ - 8
119
+ - 4
120
+ - 5
121
+ - 10
122
+ - 24
123
+ qwen_emo_path: qwen0.6bemo4-merge/
124
+ vocoder:
125
+ type: bigvgan
126
+ name: bigvgan_generator.pt
127
+ version: 2.5
full.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:394e53c4d14773b7a10928a56788ac2703211edd3326cdec99adf5f83fa9ae01
3
+ size 7851398177
gpt.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e82164f653fe881dd4587d7df47706533e0aed5588ea277a60dace8308ed49a0
3
+ size 3259583223
model_v2.py ADDED
@@ -0,0 +1,840 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import functools
2
+
3
+ import torch
4
+ import torch.nn as nn
5
+ import torch.nn.functional as F
6
+
7
+ import transformers
8
+ from transformers import GPT2Config, LogitsProcessorList
9
+ from indextts_ethiopic.gpt.transformers_gpt2 import GPT2PreTrainedModel, GPT2Model
10
+
11
+ # from transformers import GPT2Config, GPT2PreTrainedModel, LogitsProcessorList
12
+ from transformers.modeling_outputs import CausalLMOutputWithCrossAttentions
13
+ from transformers.utils.model_parallel_utils import (assert_device_map,
14
+ get_device_map)
15
+
16
+ from indextts_ethiopic.gpt.conformer_encoder import ConformerEncoder
17
+ from indextts_ethiopic.gpt.perceiver import PerceiverResampler
18
+ from indextts_ethiopic.utils.arch_util import AttentionBlock
19
+ from indextts_ethiopic.utils.typical_sampling import TypicalLogitsWarper
20
+ from indextts_ethiopic.utils.tokenizer import LANGUAGE_DICT
21
+
22
+
23
+ def null_position_embeddings(range, dim):
24
+ return torch.zeros((range.shape[0], range.shape[1], dim), device=range.device)
25
+
26
+
27
+ class ResBlock(nn.Module):
28
+ """
29
+ Basic residual convolutional block that uses GroupNorm.
30
+ """
31
+
32
+ def __init__(self, chan):
33
+ super().__init__()
34
+ self.net = nn.Sequential(
35
+ nn.Conv1d(chan, chan, kernel_size=3, padding=1),
36
+ nn.GroupNorm(chan // 8, chan),
37
+ nn.ReLU(),
38
+ nn.Conv1d(chan, chan, kernel_size=3, padding=1),
39
+ nn.GroupNorm(chan // 8, chan)
40
+ )
41
+
42
+ def forward(self, x):
43
+ return F.relu(self.net(x) + x)
44
+
45
+
46
+ class GPT2InferenceModel(GPT2PreTrainedModel):
47
+ def __init__(self, config, gpt, text_pos_emb, embeddings, norm, linear, kv_cache=False):
48
+ super().__init__(config)
49
+ # Note: the argument named `text_pos_emb` here actually represents the mel position embedding
50
+ self.transformer = gpt
51
+ self.text_pos_embedding = text_pos_emb
52
+ self.embeddings = embeddings
53
+ self.final_norm = norm
54
+ self.lm_head = nn.Sequential(norm, linear)
55
+ self.kv_cache = kv_cache
56
+
57
+ # Model parallel
58
+ self.model_parallel = False
59
+ self.device_map = None
60
+ self.cached_mel_emb = None
61
+
62
+ def parallelize(self, device_map=None):
63
+ self.device_map = (
64
+ get_device_map(len(self.transformer.h), range(max(1, torch.cuda.device_count())))
65
+ if device_map is None
66
+ else device_map
67
+ )
68
+ assert_device_map(self.device_map, len(self.transformer.h))
69
+ self.transformer.parallelize(self.device_map)
70
+ self.lm_head = self.lm_head.to(self.transformer.first_device)
71
+ self.model_parallel = True
72
+
73
+ def deparallelize(self):
74
+ self.transformer.deparallelize()
75
+ self.transformer = self.transformer.to("cpu")
76
+ self.lm_head = self.lm_head.to("cpu")
77
+ self.model_parallel = False
78
+ torch.cuda.empty_cache()
79
+ if torch.backends.mps.is_available():
80
+ torch.mps.empty_cache()
81
+
82
+ def get_output_embeddings(self):
83
+ return self.lm_head
84
+
85
+ def set_output_embeddings(self, new_embeddings):
86
+ self.lm_head = new_embeddings
87
+
88
+ def store_mel_emb(self, mel_emb):
89
+ self.cached_mel_emb = mel_emb
90
+
91
+ def prepare_inputs_for_generation(self, input_ids, past_key_values=None, **kwargs):
92
+ token_type_ids = kwargs.get("token_type_ids", None) # usually None
93
+ if not self.kv_cache:
94
+ past_key_values = None
95
+ # only last token for inputs_ids if past is defined in kwargs
96
+ if past_key_values:
97
+ input_ids = input_ids[:, -1].unsqueeze(-1)
98
+ if token_type_ids is not None:
99
+ token_type_ids = token_type_ids[:, -1].unsqueeze(-1)
100
+
101
+ attention_mask = kwargs.get("attention_mask", None)
102
+ position_ids = kwargs.get("position_ids", None)
103
+
104
+ if attention_mask is not None and position_ids is None:
105
+ # create position_ids on the fly for batch generation
106
+ position_ids = attention_mask.long().cumsum(-1) - 1
107
+ position_ids.masked_fill_(attention_mask == 0, 0)
108
+ if past_key_values:
109
+ position_ids = position_ids[:, -1].unsqueeze(-1)
110
+ else:
111
+ position_ids = None
112
+ return {
113
+ "input_ids": input_ids,
114
+ "past_key_values": past_key_values,
115
+ "use_cache": kwargs.get("use_cache"),
116
+ "position_ids": position_ids,
117
+ "attention_mask": attention_mask,
118
+ "token_type_ids": token_type_ids,
119
+ }
120
+
121
+ def forward(
122
+ self,
123
+ input_ids=None,
124
+ past_key_values=None,
125
+ attention_mask=None,
126
+ token_type_ids=None,
127
+ position_ids=None,
128
+ head_mask=None,
129
+ inputs_embeds=None,
130
+ encoder_hidden_states=None,
131
+ encoder_attention_mask=None,
132
+ labels=None,
133
+ use_cache=None,
134
+ output_attentions=None,
135
+ output_hidden_states=None,
136
+ return_dict=None,
137
+ ):
138
+ assert self.cached_mel_emb is not None
139
+ assert inputs_embeds is None # Not supported by this inference model.
140
+ assert labels is None # Training not supported by this inference model.
141
+ return_dict = (
142
+ return_dict if return_dict is not None else self.config.use_return_dict
143
+ )
144
+ # Create embedding
145
+ mel_len = self.cached_mel_emb.shape[1]
146
+ if input_ids.shape[1] != 1:
147
+ text_inputs = input_ids[:, mel_len:]
148
+ text_emb = self.embeddings(text_inputs)
149
+ text_emb = text_emb + self.text_pos_embedding(text_emb)
150
+ if self.cached_mel_emb.shape[0] != text_emb.shape[0]:
151
+ mel_emb = self.cached_mel_emb.repeat_interleave(
152
+ text_emb.shape[0] // self.cached_mel_emb.shape[0], 0
153
+ )
154
+ else: # this outcome only occurs once per loop in most cases
155
+ mel_emb = self.cached_mel_emb
156
+ emb = torch.cat([mel_emb, text_emb], dim=1)
157
+ else:
158
+ emb = self.embeddings(input_ids)
159
+ emb = emb + self.text_pos_embedding.get_fixed_embedding(
160
+ attention_mask.shape[1] - mel_len, attention_mask.device
161
+ )
162
+ transformer_outputs = self.transformer(
163
+ inputs_embeds=emb,
164
+ past_key_values=past_key_values,
165
+ attention_mask=attention_mask,
166
+ token_type_ids=token_type_ids,
167
+ position_ids=position_ids,
168
+ head_mask=head_mask,
169
+ encoder_hidden_states=encoder_hidden_states,
170
+ encoder_attention_mask=encoder_attention_mask,
171
+ use_cache=use_cache,
172
+ output_attentions=output_attentions,
173
+ output_hidden_states=output_hidden_states,
174
+ return_dict=return_dict,
175
+ )
176
+ hidden_states = transformer_outputs[0]
177
+
178
+ # Set device for model parallelism
179
+ if self.model_parallel:
180
+ if torch.backends.mps.is_available():
181
+ self.to(self.transformer.first_device)
182
+ else:
183
+ torch.cuda.set_device(self.transformer.first_device)
184
+ hidden_states = hidden_states.to(self.lm_head.weight.device)
185
+
186
+ lm_logits = self.lm_head(hidden_states)
187
+
188
+ if not return_dict:
189
+ return (lm_logits,) + transformer_outputs[1:]
190
+
191
+ return CausalLMOutputWithCrossAttentions(
192
+ loss=None,
193
+ logits=lm_logits,
194
+ past_key_values=transformer_outputs.past_key_values,
195
+ hidden_states=transformer_outputs.hidden_states,
196
+ attentions=transformer_outputs.attentions,
197
+ cross_attentions=transformer_outputs.cross_attentions,
198
+ )
199
+
200
+ @staticmethod
201
+ def _reorder_cache(past, beam_idx):
202
+ """
203
+ This function is used to re-order the :obj:`past_key_values` cache if
204
+ :meth:`~transformers.PreTrainedModel.beam_search` or :meth:`~transformers.PreTrainedModel.beam_sample` is
205
+ called. This is required to match :obj:`past_key_values` with the correct beam_idx at every generation step.
206
+ """
207
+ return tuple(
208
+ tuple(
209
+ past_state.index_select(0, beam_idx.to(past_state.device))
210
+ for past_state in layer_past
211
+ )
212
+ for layer_past in past
213
+ )
214
+
215
+
216
+ class ConditioningEncoder(nn.Module):
217
+ def __init__(self,
218
+ spec_dim,
219
+ embedding_dim,
220
+ attn_blocks=6,
221
+ num_attn_heads=4,
222
+ do_checkpointing=False,
223
+ mean=False):
224
+ super().__init__()
225
+ attn = []
226
+ self.init = nn.Conv1d(spec_dim, embedding_dim, kernel_size=1)
227
+ for a in range(attn_blocks):
228
+ attn.append(AttentionBlock(embedding_dim, num_attn_heads))
229
+ self.attn = nn.Sequential(*attn)
230
+ self.dim = embedding_dim
231
+ self.do_checkpointing = do_checkpointing
232
+ self.mean = mean
233
+
234
+ def forward(self, x):
235
+ h = self.init(x)
236
+ h = self.attn(h)
237
+ if self.mean:
238
+ return h.mean(dim=2)
239
+ else:
240
+ return h
241
+ # return h[:, :, 0]
242
+
243
+
244
+ class LearnedPositionEmbeddings(nn.Module):
245
+ def __init__(self, seq_len, model_dim, init=.02):
246
+ super().__init__()
247
+ self.emb = nn.Embedding(seq_len, model_dim)
248
+ # Initializing this way is standard for GPT-2
249
+ self.emb.weight.data.normal_(mean=0.0, std=init)
250
+
251
+ def forward(self, x):
252
+ sl = x.shape[1]
253
+ return self.emb(torch.arange(0, sl, device=x.device))
254
+
255
+ def get_fixed_embedding(self, ind, dev):
256
+ return self.emb(torch.tensor([ind], device=dev)).unsqueeze(0)
257
+
258
+
259
+ def build_hf_gpt_transformer(layers, model_dim, heads, max_mel_seq_len, max_text_seq_len, checkpointing):
260
+ """
261
+ GPT-2 implemented by the HuggingFace library.
262
+ """
263
+ from transformers import GPT2Config, GPT2Model
264
+ gpt_config = GPT2Config(vocab_size=256, # Unused.
265
+ n_positions=max_mel_seq_len + max_text_seq_len,
266
+ n_ctx=max_mel_seq_len + max_text_seq_len,
267
+ n_embd=model_dim,
268
+ n_layer=layers,
269
+ n_head=heads,
270
+ gradient_checkpointing=checkpointing,
271
+ use_cache=not checkpointing)
272
+ gpt = GPT2Model(gpt_config)
273
+ # Override the built in positional embeddings
274
+ del gpt.wpe
275
+ gpt.wpe = functools.partial(null_position_embeddings, dim=model_dim)
276
+ # Built-in token embeddings are unused.
277
+ del gpt.wte
278
+ return gpt, LearnedPositionEmbeddings(max_mel_seq_len, model_dim), LearnedPositionEmbeddings(max_text_seq_len, model_dim), \
279
+ None, None
280
+
281
+
282
+ class MelEncoder(nn.Module):
283
+ def __init__(self, channels, mel_channels=80, resblocks_per_reduction=2):
284
+ super().__init__()
285
+ self.channels = channels
286
+ self.encoder = nn.Sequential(nn.Conv1d(mel_channels, channels // 4, kernel_size=3, padding=1),
287
+ nn.Sequential(*[ResBlock(channels // 4) for _ in range(resblocks_per_reduction)]),
288
+ nn.Conv1d(channels // 4, channels // 2, kernel_size=3, stride=2, padding=1),
289
+ nn.GroupNorm(channels // 16, channels // 2),
290
+ nn.ReLU(),
291
+ nn.Sequential(*[ResBlock(channels // 2) for _ in range(resblocks_per_reduction)]),
292
+ nn.Conv1d(channels // 2, channels, kernel_size=3, stride=2, padding=1),
293
+ nn.GroupNorm(channels // 8, channels),
294
+ nn.ReLU(),
295
+ nn.Sequential(*[ResBlock(channels) for _ in range(resblocks_per_reduction)]),
296
+ )
297
+ self.reduction = 4
298
+
299
+ def forward(self, x):
300
+ for e in self.encoder:
301
+ x = e(x)
302
+ return x.permute(0, 2, 1)
303
+
304
+
305
+ class UnifiedVoice(nn.Module):
306
+ def __init__(self, layers=8, model_dim=512, heads=8, max_text_tokens=120, max_mel_tokens=250, max_conditioning_inputs=1,
307
+ mel_length_compression=1024, number_text_tokens=256,
308
+ start_text_token=0, stop_text_token=1, number_mel_codes=8194, start_mel_token=8192, stop_mel_token=8193,
309
+ train_solo_embeddings=False, use_mel_codes_as_input=True,
310
+ checkpointing=True, types=1,
311
+ condition_num_latent=32, condition_type="perceiver", condition_module=None, emo_condition_module=None, use_accel=False,
312
+ spk_cond_mode="conformer"):
313
+ """
314
+ Args:
315
+ layers: Number of layers in transformer stack.
316
+ model_dim: Operating dimensions of the transformer
317
+ heads: Number of transformer heads. Must be divisible by model_dim. Recommend model_dim//64
318
+ max_text_tokens: Maximum number of text tokens that will be encountered by model.
319
+ max_mel_tokens: Maximum number of MEL tokens that will be encountered by model.
320
+ max_conditioning_inputs: Maximum number of conditioning inputs provided to the model. If (1), conditioning input can be of format (b,80,s), otherwise (b,n,80,s).
321
+ mel_length_compression: The factor between <number_input_samples> and <mel_tokens>. Used to compute MEL code padding given wav input length.
322
+ number_text_tokens:
323
+ start_text_token:
324
+ stop_text_token:
325
+ number_mel_codes:
326
+ start_mel_token:
327
+ stop_mel_token:
328
+ train_solo_embeddings:
329
+ use_mel_codes_as_input:
330
+ checkpointing:
331
+ condition_type: perceiver, gst or default encoder
332
+ """
333
+ super().__init__()
334
+ self.number_text_tokens = number_text_tokens
335
+ self.start_text_token = start_text_token
336
+ self.stop_text_token = stop_text_token
337
+ self.number_mel_codes = number_mel_codes
338
+ self.start_mel_token = start_mel_token
339
+ self.stop_mel_token = stop_mel_token
340
+ self.layers = layers
341
+ self.heads = heads
342
+ self.max_mel_tokens = max_mel_tokens
343
+ self.max_text_tokens = max_text_tokens
344
+ self.model_dim = model_dim
345
+ self.max_conditioning_inputs = max_conditioning_inputs
346
+ self.mel_length_compression = mel_length_compression
347
+ self.condition_type = condition_type
348
+ self.cond_num = condition_num_latent
349
+ self.cond_mask_pad = nn.ConstantPad1d((self.cond_num, 0), True)
350
+ self.emo_cond_mask_pad = nn.ConstantPad1d((1, 0), True)
351
+ self.spk_cond_mode = spk_cond_mode
352
+ if spk_cond_mode == "campplus":
353
+ self.spk_emb_proj = nn.Linear(192, model_dim)
354
+ else:
355
+ if condition_type == "perceiver":
356
+ self.conditioning_encoder = ConditioningEncoder(1024, model_dim, num_attn_heads=heads)
357
+ self.perceiver_encoder = PerceiverResampler(model_dim, dim_context=model_dim, num_latents=self.cond_num)
358
+ elif condition_type == "conformer_perceiver" or condition_type == "conformer_encoder":
359
+ self.conditioning_encoder = ConformerEncoder(input_size=1024,
360
+ output_size=condition_module['output_size'],
361
+ linear_units=condition_module['linear_units'],
362
+ attention_heads=condition_module['attention_heads'],
363
+ num_blocks=condition_module['num_blocks'],
364
+ input_layer=condition_module['input_layer'])
365
+ if condition_type == "conformer_perceiver":
366
+ self.perceiver_encoder = PerceiverResampler(model_dim, dim_context=condition_module['output_size'],
367
+ ff_mult=condition_module['perceiver_mult'],
368
+ heads=condition_module['attention_heads'],
369
+ num_latents=self.cond_num)
370
+ else:
371
+ self.conditioning_encoder = ConditioningEncoder(1024, model_dim, num_attn_heads=heads, mean=True)
372
+ self.speed_emb = nn.Embedding(2, model_dim)
373
+ self.speed_emb.weight.data.normal_(mean=0.0, std=0.0)
374
+
375
+ self.emo_conditioning_encoder = ConformerEncoder(input_size=1024,
376
+ output_size=emo_condition_module['output_size'],
377
+ linear_units=emo_condition_module['linear_units'],
378
+ attention_heads=emo_condition_module['attention_heads'],
379
+ num_blocks=emo_condition_module['num_blocks'],
380
+ input_layer=emo_condition_module['input_layer'])
381
+ self.emo_perceiver_encoder = PerceiverResampler(1024, dim_context=emo_condition_module['output_size'],
382
+ ff_mult=emo_condition_module['perceiver_mult'],
383
+ heads=emo_condition_module['attention_heads'],
384
+ num_latents=1)
385
+
386
+
387
+
388
+ self.text_embedding = nn.Embedding(self.number_text_tokens * types + 1, model_dim)
389
+ if spk_cond_mode == "campplus":
390
+ self.lang_embedding = nn.Embedding(len(LANGUAGE_DICT) + 1, model_dim)
391
+ self.emo_layer = nn.Linear(model_dim, model_dim)
392
+ self.emovec_layer = nn.Linear(1024, model_dim)
393
+
394
+ if use_mel_codes_as_input:
395
+ self.mel_embedding = nn.Embedding(self.number_mel_codes, model_dim)
396
+ else:
397
+ self.mel_embedding = MelEncoder(model_dim, resblocks_per_reduction=1)
398
+ self.gpt, self.mel_pos_embedding, self.text_pos_embedding, self.mel_layer_pos_embedding, self.text_layer_pos_embedding = \
399
+ build_hf_gpt_transformer(layers, model_dim, heads, self.max_mel_tokens + 2 + self.max_conditioning_inputs,
400
+ self.max_text_tokens + 2, checkpointing)
401
+ if train_solo_embeddings:
402
+ self.mel_solo_embedding = nn.Parameter(torch.randn(1, 1, model_dim) * .02, requires_grad=True)
403
+ self.text_solo_embedding = nn.Parameter(torch.randn(1, 1, model_dim) * .02, requires_grad=True)
404
+ else:
405
+ self.mel_solo_embedding = 0
406
+ self.text_solo_embedding = 0
407
+
408
+ self.final_norm = nn.LayerNorm(model_dim)
409
+ self.text_head = nn.Linear(model_dim, self.number_text_tokens * types + 1)
410
+ self.mel_head = nn.Linear(model_dim, self.number_mel_codes)
411
+
412
+ # Initialize the embeddings per the GPT-2 scheme
413
+ embeddings = [self.text_embedding]
414
+ if use_mel_codes_as_input:
415
+ embeddings.append(self.mel_embedding)
416
+ for module in embeddings:
417
+ module.weight.data.normal_(mean=0.0, std=.02)
418
+
419
+ self.use_accel = use_accel
420
+ self.accel_engine = None # Will be initialized in post_init_gpt2_config
421
+
422
+ def post_init_gpt2_config(self, use_deepspeed=False, kv_cache=False, half=False):
423
+ seq_length = self.max_mel_tokens + self.max_text_tokens + 2
424
+ gpt_config = GPT2Config(
425
+ vocab_size=self.number_mel_codes,
426
+ n_positions=seq_length,
427
+ n_ctx=seq_length,
428
+ n_embd=self.model_dim,
429
+ n_layer=self.layers,
430
+ n_head=self.heads,
431
+ gradient_checkpointing=False,
432
+ use_cache=True,
433
+ )
434
+
435
+ if self.use_accel and torch.cuda.is_available():
436
+ # Check if flash attention is available
437
+ try:
438
+ import flash_attn
439
+ except ImportError:
440
+ raise ImportError("flash_attn is required for acceleration but not installed. Please install from https://github.com/Dao-AILab/flash-attention/releases/")
441
+
442
+ from indextts_ethiopic.accel import GPT2AccelModel, AccelInferenceEngine
443
+
444
+ # Create accel model
445
+ accel_gpt = GPT2AccelModel(gpt_config)
446
+ accel_gpt.load_state_dict(self.gpt.state_dict(), strict=False)
447
+
448
+ if half:
449
+ accel_gpt = accel_gpt.half().cuda()
450
+ else:
451
+ accel_gpt = accel_gpt.cuda()
452
+ accel_gpt.eval()
453
+
454
+ lm_head_with_norm = nn.Sequential(self.final_norm, self.mel_head)
455
+ self.accel_engine = AccelInferenceEngine(
456
+ model=accel_gpt,
457
+ lm_head=lm_head_with_norm,
458
+ num_layers=self.layers,
459
+ num_heads=self.heads,
460
+ head_dim=self.model_dim // self.heads,
461
+ block_size=256,
462
+ num_blocks=16, # Reduce to save memory (16*256 = 4096 tokens capacity)
463
+ use_cuda_graph=True,
464
+ )
465
+ print("acceleration engine initialized")
466
+ self.inference_model = GPT2InferenceModel(
467
+ gpt_config,
468
+ self.gpt,
469
+ self.mel_pos_embedding,
470
+ self.mel_embedding,
471
+ self.final_norm,
472
+ self.mel_head,
473
+ kv_cache=kv_cache,
474
+ )
475
+ if use_deepspeed and half and torch.cuda.is_available():
476
+ import deepspeed
477
+ self.ds_engine = deepspeed.init_inference(model=self.inference_model,
478
+ mp_size=1,
479
+ replace_with_kernel_inject=True,
480
+ dtype=torch.float16)
481
+ self.inference_model = self.ds_engine.module.eval()
482
+ elif use_deepspeed and torch.cuda.is_available():
483
+ import deepspeed
484
+ self.ds_engine = deepspeed.init_inference(model=self.inference_model,
485
+ mp_size=1,
486
+ replace_with_kernel_inject=True,
487
+ dtype=torch.float32)
488
+ self.inference_model = self.ds_engine.module.eval()
489
+ else:
490
+ self.inference_model = self.inference_model.eval()
491
+
492
+ # self.inference_model = PrunedGPT2InferenceModel(gpt_config, self.gpt, self.mel_pos_embedding, self.mel_embedding, self.final_norm, self.mel_head)
493
+ self.gpt.wte = self.mel_embedding
494
+
495
+ def build_aligned_inputs_and_targets(self, input, start_token, stop_token):
496
+ inp = F.pad(input, (1, 0), value=start_token)
497
+ tar = F.pad(input, (0, 1), value=stop_token)
498
+ return inp, tar
499
+
500
+ def set_mel_padding(self, mel_input_tokens, mel_lengths):
501
+ """
502
+ Given mel tokens that are derived from a padded audio clip and the actual lengths of each batch element in
503
+ that audio clip, reformats the tokens with STOP_MEL_TOKEN in place of the zero padding. This is required
504
+ preformatting to create a working TTS model.
505
+ """
506
+ for b in range(len(mel_lengths)):
507
+ # Due to the convolutional nature of how these tokens are generated,
508
+ # it would be best if the model predicts a token past the actual last token.
509
+ actual_end = mel_lengths[b]
510
+ if actual_end < mel_input_tokens.shape[-1]:
511
+ mel_input_tokens[b, actual_end:] = self.stop_mel_token
512
+ return mel_input_tokens
513
+
514
+ def set_text_padding(self, text_input_tokens, text_lengths):
515
+ """
516
+ Given mel tokens that are derived from a padded audio clip and the actual lengths of each batch element in
517
+ that audio clip, reformats the tokens with STOP_MEL_TOKEN in place of the zero padding. This is required
518
+ preformatting to create a working TTS model.
519
+ """
520
+ for b in range(len(text_lengths)):
521
+ # Due to the convolutional nature of how these tokens are generated,
522
+ # it would be best if the model predicts a token past the actual last token.
523
+ actual_end = text_lengths[b]
524
+ if actual_end < text_input_tokens.shape[-1]:
525
+ text_input_tokens[b, actual_end:] = self.stop_text_token
526
+ return text_input_tokens
527
+
528
+ def get_logits(self, speech_conditioning_inputs, first_inputs, first_head, second_inputs=None, second_head=None, get_attns=False, return_latent=False):
529
+ if second_inputs is not None:
530
+ emb = torch.cat([speech_conditioning_inputs, first_inputs, second_inputs], dim=1)
531
+ else:
532
+ emb = torch.cat([speech_conditioning_inputs, first_inputs], dim=1)
533
+
534
+ gpt_out = self.gpt(inputs_embeds=emb, return_dict=True, output_attentions=get_attns)
535
+ if get_attns:
536
+ return gpt_out.attentions
537
+
538
+ offset = speech_conditioning_inputs.shape[1]
539
+ enc = gpt_out.last_hidden_state[:, offset:]
540
+ enc = self.final_norm(enc)
541
+
542
+ if return_latent:
543
+ return enc[:, :first_inputs.shape[1]], enc[:, -second_inputs.shape[1]:]
544
+
545
+ first_logits = enc[:, :first_inputs.shape[1]]
546
+ first_logits = first_head(first_logits)
547
+ first_logits = first_logits.permute(0, 2, 1)
548
+ if second_inputs is not None:
549
+ second_logits = enc[:, -second_inputs.shape[1]:]
550
+ second_logits = second_head(second_logits)
551
+ second_logits = second_logits.permute(0, 2, 1)
552
+ return first_logits, second_logits
553
+ else:
554
+ return first_logits
555
+
556
+ def get_conditioning(self, speech_conditioning_input, cond_mel_lengths=None):
557
+ if self.condition_type == "perceiver":
558
+ if speech_conditioning_input.ndim == 4:
559
+ speech_conditioning_input = speech_conditioning_input.squeeze(1)
560
+ speech_conditioning_input = self.conditioning_encoder(speech_conditioning_input) # (b, d, s)
561
+ conds = self.perceiver_encoder(speech_conditioning_input.transpose(1, 2)) # (b, 32, d)
562
+ elif self.condition_type == "conformer_perceiver":
563
+ speech_conditioning_input, mask = self.conditioning_encoder(speech_conditioning_input.transpose(1, 2),
564
+ cond_mel_lengths) # (b, s, d), (b, 1, s)
565
+ if self.condition_type == "conformer_perceiver":
566
+ # conds_mask = torch.cat([torch.ones((mask.shape[0], self.cond_num), dtype=torch.bool), mask.squeeze(1)], dim=1)
567
+ conds_mask = self.cond_mask_pad(mask.squeeze(1))
568
+ conds = self.perceiver_encoder(speech_conditioning_input, conds_mask) # (b, 32, d)
569
+ elif self.condition_type == "gst":
570
+ if speech_conditioning_input.ndim == 4:
571
+ speech_conditioning_input = speech_conditioning_input.squeeze(1)
572
+ conds = self.gst_encoder(speech_conditioning_input.transpose(1, 2)) # (b, 1, d)
573
+ else:
574
+ speech_conditioning_input = (
575
+ speech_conditioning_input.unsqueeze(1)
576
+ if len(speech_conditioning_input.shape) == 3
577
+ else speech_conditioning_input
578
+ )
579
+ conds = []
580
+ for j in range(speech_conditioning_input.shape[1]):
581
+ conds.append(self.conditioning_encoder(speech_conditioning_input[:, j]))
582
+ conds = torch.stack(conds, dim=1)
583
+ conds = conds.mean(dim=1)
584
+ conds = conds.unsqueeze(1)
585
+ return conds
586
+
587
+
588
+ def get_emo_conditioning(self, speech_conditioning_input, cond_mel_lengths=None):
589
+ speech_conditioning_input, mask = self.emo_conditioning_encoder(speech_conditioning_input.transpose(1, 2),
590
+ cond_mel_lengths) # (b, s, d), (b, 1, s)
591
+ conds_mask = self.emo_cond_mask_pad(mask.squeeze(1))
592
+ conds = self.emo_perceiver_encoder(speech_conditioning_input, conds_mask) # (b, 1, d)
593
+ return conds.squeeze(1)
594
+
595
+
596
+ def forward(self, speech_conditioning_latent, text_inputs, text_lengths, mel_codes, mel_codes_lengths, emo_speech_conditioning_latent,
597
+ cond_mel_lengths=None, emo_cond_mel_lengths=None, emo_vec=None, use_speed=None, do_spk_cond=False, langs=None):
598
+ """
599
+ Forward pass that uses both text and voice in either text conditioning mode or voice conditioning mode
600
+
601
+ speech_conditioning_input: MEL float tensor, (b,1024)
602
+ text_inputs: long tensor, (b,t)
603
+ text_lengths: long tensor, (b,)
604
+ mel_inputs: long tensor, (b,m)
605
+ wav_lengths: long tensor, (b,)
606
+
607
+ If return_attentions is specified, only logits are returned.
608
+ If return_latent is specified, loss & logits are not computed or returned. Only the predicted latents are returned.
609
+ """
610
+
611
+ if do_spk_cond:
612
+ if self.spk_cond_mode == "campplus":
613
+ speech_conditioning_latent = self.spk_emb_proj(speech_conditioning_latent)
614
+ if speech_conditioning_latent.ndim != 3:
615
+ speech_conditioning_latent = speech_conditioning_latent.unsqueeze(1)
616
+ else:
617
+ speech_conditioning_latent = self.get_conditioning(speech_conditioning_latent.transpose(1,2), cond_mel_lengths)
618
+ else:
619
+ speech_conditioning_latent = speech_conditioning_latent
620
+
621
+ if emo_vec is None:
622
+ emo_vec_syn_ori = self.get_emo_conditioning(emo_speech_conditioning_latent.transpose(1,2), emo_cond_mel_lengths)
623
+ emo_vec_syn = self.emovec_layer(emo_vec_syn_ori)
624
+ emo_vec = self.emo_layer(emo_vec_syn)
625
+
626
+ text_inputs = self.set_text_padding(text_inputs, text_lengths)
627
+ text_inputs = F.pad(text_inputs, (0, 1), value=self.stop_text_token)
628
+
629
+ mel_codes = self.set_mel_padding(mel_codes, mel_codes_lengths)
630
+ mel_codes = F.pad(mel_codes, (0, 1), value=self.stop_mel_token)
631
+
632
+ if self.spk_cond_mode == "campplus":
633
+ conds = torch.cat((speech_conditioning_latent + emo_vec.unsqueeze(1), torch.zeros(speech_conditioning_latent.size(0), 2, speech_conditioning_latent.size(2)).to(speech_conditioning_latent.device)), 1)
634
+ else:
635
+ duration_emb = self.speed_emb(torch.zeros_like(use_speed))
636
+ duration_emb_half = self.speed_emb(torch.ones_like(use_speed))
637
+ conds = torch.cat((speech_conditioning_latent + emo_vec.unsqueeze(1), duration_emb_half.unsqueeze(1), duration_emb.unsqueeze(1)), 1)
638
+ text_inputs, text_targets = self.build_aligned_inputs_and_targets(text_inputs, self.start_text_token, self.stop_text_token)
639
+ text_emb = self.text_embedding(text_inputs) + self.text_pos_embedding(text_inputs)
640
+ if langs is not None and self.spk_cond_mode == "campplus":
641
+ text_emb = text_emb + self.lang_embedding(langs).unsqueeze(1)
642
+ mel_codes, mel_targets = self.build_aligned_inputs_and_targets(mel_codes, self.start_mel_token, self.stop_mel_token)
643
+
644
+ mel_emb = self.mel_embedding(mel_codes)
645
+ mel_emb = mel_emb + self.mel_pos_embedding(mel_codes)
646
+
647
+ text_logits, mel_logits = self.get_logits(conds, text_emb, self.text_head, mel_emb, self.mel_head, get_attns=False, return_latent=True)
648
+ return mel_logits[:, :-2] # Despite the name, these are not logits. Strip off the two tokens added by this forward pass.
649
+
650
+ def prepare_gpt_inputs(
651
+ self,
652
+ conditional_latents: torch.Tensor,
653
+ text_inputs: torch.Tensor,
654
+ langs: torch.Tensor = None,
655
+ ):
656
+
657
+ """
658
+ Prepare the inputs for the GPT2InferenceModel to generate.
659
+ Args:
660
+ conds_latent: (b, 32, dim) audio conditioning embedding by `get_conditioning()`
661
+ text_inputs: (b, L)
662
+ Returns:
663
+ input_ids: (b, s+1) the input ids for the GPT2InferenceModel.generate()
664
+ inputs_embeds: (b, s+1, dim) the input embeddings for the GPT2InferenceModel.forward()
665
+ attention_mask: (b, s+1) the attention mask for the GPT2InferenceModel.generate()
666
+ """
667
+ b, L = text_inputs.shape[:2]
668
+ device = text_inputs.device
669
+ single_cond = conditional_latents.ndim == 3 and conditional_latents.shape[0] == 1
670
+ if not single_cond:
671
+ assert conditional_latents.shape[0] == b, f"batch size mismatch: {conditional_latents.shape[0]} vs {b}"
672
+ batched_mel_emb = []
673
+ attention_masks = []
674
+ target_len = conditional_latents.shape[1] + L + 2
675
+ for i in range(b):
676
+ valid_mask = (text_inputs[i] != self.stop_text_token) & (text_inputs[i] != self.start_text_token)
677
+ text_input = text_inputs[i][valid_mask]
678
+ text_input = F.pad(text_input, (1, 0), value=self.start_text_token)
679
+ text_input = F.pad(text_input, (0, 1), value=self.stop_text_token)
680
+ text_input_pos = torch.arange(0, text_input.size(-1), device=device)
681
+ text_emb = self.text_embedding(text_input) + self.text_pos_embedding.emb(text_input_pos)
682
+ if langs is not None and self.spk_cond_mode == "campplus":
683
+ text_emb += self.lang_embedding(langs[i])
684
+ # concatenate [conditional latents][text embeddings]
685
+ conds_text_emb = [
686
+ conditional_latents.squeeze(0) if single_cond else conditional_latents[i],
687
+ text_emb,
688
+ ]
689
+ # +1 for the start_mel_token
690
+ attention_mask = torch.ones(target_len+1, dtype=torch.long, device=device)
691
+ # check this text input is padded
692
+ padding: int = L + 2 - text_input.size(-1)
693
+ # pad left of [cond][text] -> [pad][cond][text]
694
+ if padding > 0:
695
+ pad = torch.zeros((padding, conditional_latents.size(-1)), dtype=text_emb.dtype, device=device) # [p, dim]
696
+ conds_text_emb.insert(0, pad)
697
+ attention_mask[:padding] = 0
698
+ mel_emb = torch.cat(conds_text_emb) #[s, dim]
699
+ assert mel_emb.shape[0] == target_len, f"mel_emb.shape: {mel_emb.shape}, target_len: {target_len}"
700
+ batched_mel_emb.append(mel_emb)
701
+ attention_masks.append(attention_mask)
702
+ # [b, s, dim]
703
+ batched_mel_emb = torch.stack(batched_mel_emb, dim=0)
704
+ # [b, s+1]
705
+ attention_mask = torch.stack(attention_masks, dim=0)
706
+ # [b, s+1]
707
+ fake_inputs = torch.ones(
708
+ (
709
+ batched_mel_emb.shape[0],
710
+ batched_mel_emb.shape[1] + 1, # +1 for the start_mel_token
711
+ ),
712
+ dtype=torch.long,
713
+ device=device,
714
+ )
715
+ fake_inputs[:, -1] = self.start_mel_token
716
+ return fake_inputs, batched_mel_emb, attention_mask
717
+
718
+ def inference_speech(self, speech_condition, text_inputs, langs=None,
719
+ emo_speech_condition=None, cond_lengths=None, emo_cond_lengths=None, emo_vec=None,
720
+ use_speed=False, campplus_embedding=None, wav=None,
721
+ input_tokens=None, num_return_sequences=1,
722
+ max_generate_length=None, typical_sampling=False, typical_mass=.9, **hf_generate_kwargs):
723
+ """
724
+ Args:
725
+ speech_condition: (b, d, frames) or (d, frames)
726
+ text_inputs: (b, L)
727
+ cond_mel_lengths: lengths of the conditioning mel spectrograms in shape (b,) or (1,)
728
+ input_tokens: additional tokens for generation in shape (b, s) or (s,)
729
+ max_generate_length: limit the number of generated tokens
730
+ hf_generate_kwargs: kwargs for `GPT2InferenceModel.generate(**hf_generate_kwargs)`
731
+ """
732
+
733
+ if speech_condition.ndim == 2:
734
+ speech_condition = speech_condition.unsqueeze(0)
735
+ if emo_speech_condition is None:
736
+ emo_speech_condition = speech_condition
737
+ if cond_lengths is None:
738
+ cond_lengths = torch.tensor([speech_condition.shape[-1]], device=speech_condition.device)
739
+ if emo_cond_lengths is None:
740
+ emo_cond_lengths = torch.tensor([emo_speech_condition.shape[-1]], device=speech_condition.device)
741
+
742
+ if self.spk_cond_mode == "campplus":
743
+ if campplus_embedding is not None:
744
+ speech_conditioning_latent = campplus_embedding
745
+ elif wav is not None:
746
+ if not hasattr(self, 'sv_pipeline'):
747
+ from modelscope.pipelines import pipeline
748
+ self.sv_pipeline = pipeline(
749
+ task='speaker-verification',
750
+ model='iic/speech_campplus_sv_zh-cn_16k-common',
751
+ device='cpu'
752
+ )
753
+ speech_conditioning_latent = torch.tensor(self.sv_pipeline([wav], output_emb=True)['embs']).to(text_inputs.device)
754
+ else:
755
+ raise ValueError("campplus mode requires campplus_embedding or wav")
756
+ speech_conditioning_latent = self.spk_emb_proj(speech_conditioning_latent)
757
+ speech_conditioning_latent = speech_conditioning_latent.unsqueeze(0) if speech_conditioning_latent.ndim != 3 else speech_conditioning_latent
758
+ else:
759
+ speech_conditioning_latent = self.get_conditioning(speech_condition.transpose(1,2), cond_lengths)
760
+
761
+ if emo_vec is None:
762
+ print('compute emo vec')
763
+ emo_vec = self.get_emo_conditioning(emo_speech_condition.transpose(1,2), emo_cond_lengths)
764
+ emo_vec = self.emovec_layer(emo_vec)
765
+ emo_vec = self.emo_layer(emo_vec)
766
+ else:
767
+ print('Use the specified emotion vector')
768
+
769
+ if self.spk_cond_mode == "campplus":
770
+ conds_latent = torch.cat((speech_conditioning_latent + emo_vec.unsqueeze(1), torch.zeros(speech_conditioning_latent.size(0), 2, speech_conditioning_latent.size(2)).to(speech_conditioning_latent.device)), 1)
771
+ else:
772
+ tmp = torch.zeros(text_inputs.size(0)).to(text_inputs.device)
773
+ duration_emb = self.speed_emb(torch.zeros_like(tmp).long())
774
+ duration_emb_half = self.speed_emb(torch.ones_like(tmp).long())
775
+ conds_latent = torch.cat((speech_conditioning_latent + emo_vec.unsqueeze(1), duration_emb_half.unsqueeze(1), duration_emb.unsqueeze(1)), 1)
776
+ input_ids, inputs_embeds, attention_mask = self.prepare_gpt_inputs(conds_latent, text_inputs, langs)
777
+ self.inference_model.store_mel_emb(inputs_embeds)
778
+ if input_tokens is None:
779
+ inputs = input_ids
780
+ else:
781
+ if input_tokens.ndim == 1:
782
+ input_tokens = input_tokens.unsqueeze(0)
783
+ assert num_return_sequences % input_tokens.shape[0] == 0, \
784
+ "The num_return_sequences must be divisible by the batch number of input_tokens"
785
+ assert num_return_sequences % text_inputs.shape[0] == 0, \
786
+ "The num_return_sequences must be divisible by the batch number of text_inputs"
787
+ b = num_return_sequences // input_ids.shape[0]
788
+ if b > 1:
789
+ input_ids = input_ids.repeat(b, 1)
790
+ attention_mask = attention_mask.repeat(b, 1)
791
+ input_tokens = input_tokens.repeat(num_return_sequences // input_tokens.shape[0], 1)
792
+ inputs = torch.cat([input_ids, input_tokens], dim=1)
793
+ attention_mask = F.pad(attention_mask, (0, input_tokens.shape[1]), value=1)
794
+ trunc_index = inputs.shape[1]
795
+ logits_processor = LogitsProcessorList()
796
+ if typical_sampling:
797
+ # employ custom typical sampling
798
+ if not (typical_mass > 0.0 and typical_mass < 1.0):
799
+ raise ValueError(f"`typical_mass` has to be a float > 0 and < 1, but is {typical_mass}")
800
+ min_tokens_to_keep = 2 if hf_generate_kwargs.get("num_beams", 1) > 1 else 1
801
+ logits_processor.append(TypicalLogitsWarper(mass=typical_mass, min_tokens_to_keep=min_tokens_to_keep))
802
+ max_length = (trunc_index + self.max_mel_tokens - 1) if max_generate_length is None else trunc_index + max_generate_length
803
+
804
+ # Use accel engine if available (single sequence only)
805
+ if self.accel_engine is not None and num_return_sequences == 1:
806
+ output = self.accel_engine.generate(
807
+ inputs, # fake input_ids (all 1s + start_mel_token)
808
+ max_new_tokens=max_length - trunc_index,
809
+ attention_mask=attention_mask,
810
+ temperature=hf_generate_kwargs.get('temperature', 1),
811
+ stop_tokens=[self.stop_mel_token],
812
+ tts_embeddings=inputs_embeds, # [pad][cond][text] embeddings (87 tokens, NO start_mel_token)
813
+ tts_mel_embedding=self.inference_model.embeddings, # mel_embedding layer
814
+ tts_text_pos_embedding=self.inference_model.text_pos_embedding, # text_pos_embedding layer
815
+ )
816
+ else:
817
+ output = self.inference_model.generate(inputs,
818
+ bos_token_id=self.start_mel_token, pad_token_id=self.stop_mel_token,
819
+ eos_token_id=self.stop_mel_token, attention_mask=attention_mask,
820
+ max_length=max_length, logits_processor=logits_processor,
821
+ num_return_sequences=num_return_sequences,
822
+ **hf_generate_kwargs)
823
+ if isinstance(output, torch.Tensor):
824
+ return output[:, trunc_index:], speech_conditioning_latent
825
+ # GenerateOutput
826
+ output.sequences = output.sequences[:, trunc_index:]
827
+ return output, speech_conditioning_latent
828
+
829
+ def get_emovec(self, emo_speech_conditioning_latent, emo_cond_lengths):
830
+ emo_vec_syn_ori = self.get_emo_conditioning(emo_speech_conditioning_latent.transpose(1,2), emo_cond_lengths)
831
+ emo_vec_syn = self.emovec_layer(emo_vec_syn_ori)
832
+ emo_vec = self.emo_layer(emo_vec_syn)
833
+ return emo_vec
834
+
835
+ def merge_emovec(self, speech_conditioning_latent, emo_speech_conditioning_latent, cond_lengths, emo_cond_lengths, alpha = 1.0):
836
+ emo_vec = self.get_emovec(emo_speech_conditioning_latent, emo_cond_lengths)
837
+ base_vec = self.get_emovec(speech_conditioning_latent, cond_lengths)
838
+
839
+ out = base_vec + alpha * (emo_vec - base_vec)
840
+ return out
tokenizer_ethiopic.metadata.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "addis_control": null,
3
+ "appended_language_ids": {
4
+ "om": 106,
5
+ "ti": 107
6
+ },
7
+ "appended_special_tokens": {
8
+ "<|DIALECT_GOJJAM|>": 60511,
9
+ "<|DIALECT_SHEWA|>": 60512,
10
+ "<|om|>": 60509,
11
+ "<|ti|>": 60510
12
+ },
13
+ "base_mergeable_tokens": 58836,
14
+ "routing_note": "Addis/Leyu uses the existing <|am|> control",
15
+ "schema_version": 1
16
+ }
tokenizer_ethiopic.py ADDED
@@ -0,0 +1,462 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import os
3
+ from functools import lru_cache
4
+ from typing import Optional
5
+ import torch
6
+ from transformers import AutoTokenizer
7
+ from whisper.tokenizer import Tokenizer
8
+
9
+ import tiktoken
10
+
11
+ LANGUAGES = {
12
+ "en": "english",
13
+ "zh": "chinese",
14
+ "de": "german",
15
+ "es": "spanish",
16
+ "ru": "russian",
17
+ "ko": "korean",
18
+ "fr": "french",
19
+ "ja": "japanese",
20
+ "pt": "portuguese",
21
+ "tr": "turkish",
22
+ "pl": "polish",
23
+ "ca": "catalan",
24
+ "nl": "dutch",
25
+ "ar": "arabic",
26
+ "sv": "swedish",
27
+ "it": "italian",
28
+ "id": "indonesian",
29
+ "hi": "hindi",
30
+ "fi": "finnish",
31
+ "vi": "vietnamese",
32
+ "he": "hebrew",
33
+ "uk": "ukrainian",
34
+ "el": "greek",
35
+ "ms": "malay",
36
+ "cs": "czech",
37
+ "ro": "romanian",
38
+ "da": "danish",
39
+ "hu": "hungarian",
40
+ "ta": "tamil",
41
+ "no": "norwegian",
42
+ "th": "thai",
43
+ "ur": "urdu",
44
+ "hr": "croatian",
45
+ "bg": "bulgarian",
46
+ "lt": "lithuanian",
47
+ "la": "latin",
48
+ "mi": "maori",
49
+ "ml": "malayalam",
50
+ "cy": "welsh",
51
+ "sk": "slovak",
52
+ "te": "telugu",
53
+ "fa": "persian",
54
+ "lv": "latvian",
55
+ "bn": "bengali",
56
+ "sr": "serbian",
57
+ "az": "azerbaijani",
58
+ "sl": "slovenian",
59
+ "kn": "kannada",
60
+ "et": "estonian",
61
+ "mk": "macedonian",
62
+ "br": "breton",
63
+ "eu": "basque",
64
+ "is": "icelandic",
65
+ "hy": "armenian",
66
+ "ne": "nepali",
67
+ "mn": "mongolian",
68
+ "bs": "bosnian",
69
+ "kk": "kazakh",
70
+ "sq": "albanian",
71
+ "sw": "swahili",
72
+ "gl": "galician",
73
+ "mr": "marathi",
74
+ "pa": "punjabi",
75
+ "si": "sinhala",
76
+ "km": "khmer",
77
+ "sn": "shona",
78
+ "yo": "yoruba",
79
+ "so": "somali",
80
+ "af": "afrikaans",
81
+ "oc": "occitan",
82
+ "ka": "georgian",
83
+ "be": "belarusian",
84
+ "tg": "tajik",
85
+ "sd": "sindhi",
86
+ "gu": "gujarati",
87
+ "am": "amharic",
88
+ "yi": "yiddish",
89
+ "lo": "lao",
90
+ "uz": "uzbek",
91
+ "fo": "faroese",
92
+ "ht": "haitian creole",
93
+ "ps": "pashto",
94
+ "tk": "turkmen",
95
+ "nn": "nynorsk",
96
+ "mt": "maltese",
97
+ "sa": "sanskrit",
98
+ "lb": "luxembourgish",
99
+ "my": "myanmar",
100
+ "bo": "tibetan",
101
+ "tl": "tagalog",
102
+ "mg": "malagasy",
103
+ "as": "assamese",
104
+ "tt": "tatar",
105
+ "haw": "hawaiian",
106
+ "ln": "lingala",
107
+ "ha": "hausa",
108
+ "ba": "bashkir",
109
+ "jw": "javanese",
110
+ "su": "sundanese",
111
+ "yue": "cantonese",
112
+ "minnan": "minnan",
113
+ "wuyu": "wuyu",
114
+ "dialect": "dialect",
115
+ "zh/en": "zh/en",
116
+ "en/zh": "en/zh",
117
+ "common": "common",
118
+ }
119
+
120
+ # Append-only model language rows. Addis/Leyu keeps the existing am row.
121
+ LANGUAGES['om'] = 'afaan oromo'
122
+ LANGUAGES['ti'] = 'tigrinya'
123
+
124
+ # 增加 LANGUAGE_DICT 用于映射
125
+ LANGUAGE_DICT = {lang: index for index, lang in enumerate(LANGUAGES.keys())}
126
+
127
+ # language code lookup by name, with a few language aliases
128
+ TO_LANGUAGE_CODE = {
129
+ **{language: code for code, language in LANGUAGES.items()},
130
+ "burmese": "my",
131
+ "valencian": "ca",
132
+ "flemish": "nl",
133
+ "haitian": "ht",
134
+ "letzeburgesch": "lb",
135
+ "pushto": "ps",
136
+ "panjabi": "pa",
137
+ "moldavian": "ro",
138
+ "moldovan": "ro",
139
+ "sinhalese": "si",
140
+ "castilian": "es",
141
+ "mandarin": "zh",
142
+ }
143
+
144
+ AUDIO_EVENT = {
145
+ "ASR": "ASR",
146
+ "AED": "AED",
147
+ "SER": "SER",
148
+ "Speech": "Speech",
149
+ "/Speech": "/Speech",
150
+ "BGM": "BGM",
151
+ "/BGM": "/BGM",
152
+ "Laughter": "Laughter",
153
+ "/Laughter": "/Laughter",
154
+ "Applause": "Applause",
155
+ "/Applause": "/Applause",
156
+ }
157
+
158
+ EMOTION = {
159
+ "HAPPY": "HAPPY",
160
+ "SAD": "SAD",
161
+ "ANGRY": "ANGRY",
162
+ "NEUTRAL": "NEUTRAL",
163
+ }
164
+
165
+ TTS_Vocal_Token = {
166
+ "TTS/B": "TTS/B",
167
+ "TTS/O": "TTS/O",
168
+ "TTS/Q": "TTS/Q",
169
+ "TTS/A": "TTS/A",
170
+ "TTS/CO": "TTS/CO",
171
+ "TTS/CL": "TTS/CL",
172
+ "TTS/H": "TTS/H",
173
+ **{f"TTS/SP{i:02d}": f"TTS/SP{i:02d}" for i in range(1, 14)}
174
+ }
175
+
176
+
177
+ def lang_to_token(lang):
178
+ lang = lang.lower()
179
+ if lang not in LANGUAGE_DICT:
180
+ lang = "common"
181
+ return LANGUAGE_DICT[lang]
182
+
183
+
184
+ @lru_cache(maxsize=None)
185
+ def get_encoding(name: str = "gpt2", num_languages: int = 99, model_dir: str = "checkpoints"):
186
+ vocab_path = os.path.join(model_dir, f'{name}.tiktoken')
187
+
188
+ ranks = {
189
+ base64.b64decode(token): int(rank)
190
+ for token, rank in (line.split() for line in open(vocab_path) if line)
191
+ }
192
+ n_vocab = len(ranks)
193
+ special_tokens = {}
194
+
195
+ specials = [
196
+ "<|endoftext|>",
197
+ "<|startoftranscript|>",
198
+ *[f"<|{lang}|>" for lang in list(LANGUAGES.keys())[:num_languages]],
199
+ *[f"<|{audio_event}|>" for audio_event in list(AUDIO_EVENT.keys())],
200
+ *[f"<|{emotion}|>" for emotion in list(EMOTION.keys())],
201
+ "<|translate|>",
202
+ "<|transcribe|>",
203
+ "<|startoflm|>",
204
+ "<|startofprev|>",
205
+ "<|nospeech|>",
206
+ "<|notimestamps|>",
207
+ *[f"<|SPECIAL_TOKEN_{i}|>" for i in range(1, 31)], # register special tokens for ASR
208
+ *[f"<|{tts}|>" for tts in list(TTS_Vocal_Token.keys())], # register special tokens for TTS
209
+ *[f"<|{i * 0.02:.2f}|>" for i in range(1501)],
210
+ ]
211
+
212
+
213
+ # Append-only Ethiopian TTS controls. Addis/Leyu uses existing <|am|>.
214
+ ETHIOPIC_CONTROL_TOKENS = [
215
+ '<|om|>',
216
+ '<|ti|>',
217
+ '<|DIALECT_GOJJAM|>',
218
+ '<|DIALECT_SHEWA|>',
219
+ ]
220
+ specials.extend(ETHIOPIC_CONTROL_TOKENS)
221
+
222
+ for token in specials:
223
+ special_tokens[token] = n_vocab
224
+ n_vocab += 1
225
+
226
+ return tiktoken.Encoding(
227
+ name=os.path.basename(vocab_path),
228
+ explicit_n_vocab=n_vocab,
229
+ pat_str=r"""'s|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+""",
230
+ mergeable_ranks=ranks,
231
+ special_tokens=special_tokens,
232
+ )
233
+
234
+
235
+
236
+ class WhisperTokenizer(Tokenizer):
237
+ """
238
+ Whisper tokenizer 没有提供 tokenize, convert_tokens_to_ids, convert_ids_to_tokens 函数
239
+ 如果使用 encode 将 str 转为 list[int] 再单独 decode 每个 token 会丢失上下文信息,对于像日语单个字符可能需要多个token来表示
240
+ 所以无法单纯使用 token_list = [tokenizer.decode([token_id]) for token_id in token_ids] 去做 token->index 的转换
241
+ 因此这里添加了 3 个函数来做这件事
242
+ """
243
+ def __init__(self, *args, **kwargs):
244
+ super().__init__(*args, **kwargs)
245
+
246
+ def tokenize(self, text, max_token_comb=4):
247
+ """
248
+ 将输入文本根据token切分开转为list[str]
249
+ 通过智能组合token避免出现不完整的乱码字符
250
+ """
251
+ token_ids = self.encode(text, allowed_special="all")
252
+
253
+ # 分组合并token以获得有意义的字符
254
+ tokens = []
255
+ i = 0
256
+
257
+ while i < len(token_ids):
258
+ # 从当前位置开始,尝试不同长度的组合
259
+ best_token = None
260
+ best_length = 0
261
+
262
+ # 尝试1到4个token的组合(根据需要可以调整这个范围)
263
+ for length in range(1, min(max_token_comb+1, len(token_ids) - i + 1)):
264
+ try:
265
+ candidate_ids = token_ids[i:i+length]
266
+ candidate_token = self.decode(candidate_ids)
267
+ # 检查是否是有效token(没有乱码)
268
+ if "\ufffd" not in candidate_token and candidate_token.strip():
269
+ best_token = candidate_token
270
+ best_length = length
271
+ break # 找到第一个有效的就停止
272
+ except:
273
+ continue
274
+
275
+ # 如果找到了有效token
276
+ if best_token is not None:
277
+ tokens.append(best_token)
278
+ i += best_length
279
+ else:
280
+ # 如果没有找到,就使用单个token(即使可能有乱码)
281
+ try:
282
+ single_token = self.decode([token_ids[i]])
283
+ tokens.append(single_token)
284
+ except:
285
+ tokens.append("<UNK>")
286
+ i += 1
287
+
288
+ return tokens
289
+
290
+
291
+
292
+ @lru_cache(maxsize=None)
293
+ def get_tokenizer(
294
+ multilingual: bool,
295
+ *,
296
+ num_languages: int = 99,
297
+ language: Optional[str] = None,
298
+ task: Optional[str] = None, # Literal["transcribe", "translate", None]
299
+ model_dir: str = "checkpoints",
300
+ ) -> Tokenizer:
301
+ if language is not None:
302
+ language = language.lower()
303
+ if language not in LANGUAGES:
304
+ if language in TO_LANGUAGE_CODE:
305
+ language = TO_LANGUAGE_CODE[language]
306
+ else:
307
+ raise ValueError(f"Unsupported language: {language}")
308
+
309
+ if multilingual:
310
+ encoding_name = "multilingual_zh_ja_yue_char_del"
311
+ language = language or "en"
312
+ task = task or "transcribe"
313
+ else:
314
+ encoding_name = "gpt2"
315
+ language = None
316
+ task = None
317
+
318
+ encoding = get_encoding(name=encoding_name, num_languages=num_languages, model_dir=model_dir)
319
+
320
+ return WhisperTokenizer(
321
+ encoding=encoding, num_languages=num_languages, language=language, task=task
322
+ )
323
+
324
+
325
+ class QwenTokenizer():
326
+ def __init__(self, token_path, skip_special_tokens=True):
327
+ super().__init__()
328
+ # NOTE: non-chat model, all these special tokens keep randomly initialized.
329
+ special_tokens = {
330
+ 'eos_token': '<|endoftext|>',
331
+ 'pad_token': '<|endoftext|>',
332
+ 'additional_special_tokens': [
333
+ '<|im_start|>', '<|im_end|>', '<|endofprompt|>',
334
+ '[breath]', '<strong>', '</strong>', '[noise]',
335
+ '[laughter]', '[cough]', '[clucking]', '[accent]',
336
+ '[quick_breath]',
337
+ "<laughter>", "</laughter>",
338
+ "[hissing]", "[sigh]", "[vocalized-noise]",
339
+ "[lipsmack]", "[mn]"
340
+ ],
341
+ 'nonverbalspeech38k_speech_tokens': [
342
+ '[snore]', '[throatclearing]', '[crying]',
343
+ '[sniff]', '[laughing]', '[coughing]',
344
+ '[gasp]', '[yawn]', '<B>', '</B>'
345
+ ]
346
+ }
347
+ self.special_tokens = special_tokens
348
+ self.tokenizer = AutoTokenizer.from_pretrained(token_path)
349
+ self.tokenizer.add_special_tokens(special_tokens)
350
+ self.skip_special_tokens = skip_special_tokens
351
+
352
+ def encode(self, text, **kwargs):
353
+ tokens = self.tokenizer([text], return_tensors="pt")
354
+ tokens = tokens["input_ids"][0].cpu().tolist()
355
+ return tokens
356
+
357
+ def decode(self, tokens):
358
+ tokens = torch.tensor(tokens, dtype=torch.int64)
359
+ text = self.tokenizer.batch_decode([tokens], skip_special_tokens=self.skip_special_tokens)[0]
360
+ return text
361
+
362
+
363
+ @lru_cache(maxsize=None)
364
+ def get_qwen_tokenizer(
365
+ token_path: str,
366
+ skip_special_tokens: bool
367
+ ) -> QwenTokenizer:
368
+ return QwenTokenizer(token_path=token_path, skip_special_tokens=skip_special_tokens)
369
+
370
+
371
+
372
+
373
+
374
+ if __name__ == "__main__":
375
+
376
+ text_list = [
377
+ "IndexTTS 正式发布1.0版本了,效果666",
378
+ "晕XUAN4是一种GAN3觉",
379
+ "我爱你!",
380
+ "I love you!",
381
+ "“我爱你”的英语是“I love you”",
382
+ "2.5平方电线",
383
+ "共465篇,约315万字",
384
+ "2002年的第一场雪,下在了2003年",
385
+ "速度是10km/h",
386
+ "现在是北京时间2025年01月11日 20:00",
387
+ "他这条裤子是2012年买的,花了200块钱",
388
+ "电话:135-4567-8900",
389
+ "1键3连",
390
+ "他这条视频点赞3000+,评论1000+,收藏500+",
391
+ "这是1024元的手机,你要吗?",
392
+ "受不liao3你了",
393
+ "“衣裳”不读衣chang2,而是读衣shang5",
394
+ "最zhong4要的是:不要chong2蹈覆辙",
395
+ "不zuo1死就不会死",
396
+ "See you at 8:00 AM",
397
+ "8:00 AM 开会",
398
+ "Couting down 3, 2, 1, go!",
399
+ "数到3就开始:1、2、3",
400
+ "This sales for 2.5% off, only $12.5.",
401
+ "5G网络是4G网络的升级版,2G网络是3G网络的前身",
402
+ "苹果于2030/1/2发布新 iPhone 2X 系列手机,最低售价仅 ¥12999",
403
+ "这酒...里...有毒...",
404
+ # 异常case
405
+ "只有,,,才是最好的",
406
+ "babala2是什么?", # babala二是什么?
407
+ "用beta1测试", # 用beta一测试
408
+ "have you ever been to beta2?", # have you ever been to beta two?
409
+ "such as XTTS, CosyVoice2, Fish-Speech, and F5-TTS", # such as xtts,cosyvoice two,fish-speech,and f five-tts
410
+ "where's the money?", # where is the money?
411
+ "who's there?", # who is there?
412
+ "which's the best?", # which is the best?
413
+ "how's it going?", # how is it going?
414
+ "今天是个好日子 it's a good day", # 今天是个好日子 it is a good day
415
+ # 人名
416
+ "约瑟夫·高登-莱维特(Joseph Gordon-Levitt is an American actor)",
417
+ "蒂莫西·唐纳德·库克(英文名:Timothy Donald Cook),通称蒂姆·库克(Tim Cook),美国商业经理、工业工程师和工业开发商,现任苹果公司首席执行官。",
418
+ # 长句子
419
+ "《盗梦空间》是由美国华纳兄弟影片公司出品的电影,由克里斯托弗·诺兰执导并编剧,莱昂纳多·迪卡普里奥、玛丽昂·歌迪亚、约瑟夫·高登-莱维特、艾利奥特·佩吉、汤姆·哈迪等联袂主演,2010年7月16日在美国上映,2010年9月1日在中国内地上映,2020年8月28日在中国内地重映。影片剧情游走于梦境与现实之间,被定义为“发生在意识结构内的当代动作科幻片”,讲述了由莱昂纳多·迪卡普里奥扮演的造梦师,带领特工团队进入他人梦境,从他人的潜意识中盗取机密,并重塑他人梦境的故事。",
420
+ "清晨拉开窗帘,阳光洒在窗台的Bloomixy花艺礼盒上——薰衣草香薰蜡烛唤醒嗅觉,永生花束折射出晨露般光泽。设计师将“自然绽放美学”融入每个细节:手工陶瓷花瓶可作首饰收纳,香薰精油含依兰依兰舒缓配方。限量款附赠《365天插花灵感手册》,让每个平凡日子都有花开仪式感。\n宴会厅灯光暗下的刹那,Glimmeria星月系列耳坠开始发光——瑞士冷珐琅工艺让蓝宝石如银河流动,钛合金骨架仅3.2g无负重感。设计师秘密:内置微型重力感应器,随步伐产生0.01mm振幅,打造“行走的星光”。七夕限定礼盒含星座定制铭牌,让爱意如星辰永恒闪耀。",
421
+ "电影1:“黑暗骑士”(演员:克里斯蒂安·贝尔、希斯·莱杰;导演:克里斯托弗·诺兰);电影2:“盗梦空间”(演员:莱昂纳多·迪卡普里奥;导演:克里斯托弗·诺兰);电影3:“钢琴家”(演员:艾德里安·布洛迪;导演:罗曼·波兰斯基);电影4:“泰坦尼克号”(演员:莱昂纳多·迪卡普里奥;导演:詹姆斯·卡梅隆);电影5:“阿凡达”(演员:萨姆·沃辛顿;导演:詹姆斯·卡梅隆);电影6:“南方公园:大电影”(演员:马特·斯���、托马斯·艾恩格瑞;导演:特雷·帕克)",
422
+ "そうですね、ほんと1年前、まあコロナだったので家のリビングからあの話して、すごい緊張してしまって、もう手が冷たくなったのを今でも覚えてるんですけど、新潟にいるメンバーが",
423
+ "また、青少年健全育成などに功績がある、市内の団体を表彰する団体省令の推薦も合わせて受け付けています",
424
+ "たねん、おんてきであるは、しかがどのにこうさんして、 しゅくんにたいしてゆみをひくとゆうことは。",
425
+ "実は昨年、11kgの減量にも成功していたという。",
426
+ ]
427
+
428
+ from indextts.utils.common import tokenize_by_CJK_char
429
+ tokenizer = get_tokenizer(multilingual=True)
430
+ success_count = 0
431
+ error_count = 0
432
+ for raw_text in text_list:
433
+ # print(f"raw text: {text}")
434
+ text = tokenize_by_CJK_char(raw_text)
435
+ # print(f"cleaned text: {text}")
436
+ text_ja = f'<|ja|> {text}'
437
+
438
+ # 验证 tokenize 函数
439
+ tokens = tokenizer.tokenize(text_ja)
440
+ ret1 = text_ja == "".join(tokens)
441
+ print(f"tokens: {tokens}")
442
+ # print(text_ja == "".join(tokens))
443
+ # print(f"text_ja: {text_ja}")
444
+ # print("tokens: ", "".join(tokens))
445
+
446
+ # # 验证 convert_tokens_to_ids 和 convert_ids_to_tokens 函数
447
+ # ids = tokenizer.encode(text_ja, allowed_special="all")
448
+ # ids_to_tokens = tokenizer.convert_ids_to_tokens(ids)
449
+ # tokens_to_ids = tokenizer.convert_tokens_to_ids(ids_to_tokens)
450
+ # ret2 = ids == tokens_to_ids
451
+ # print(f"raw_ids : {ids}")
452
+ # print(f"tokens_to_ids: {tokens_to_ids}")
453
+ # print(ids == tokens_to_ids)
454
+
455
+ # if ret1 and ret2:
456
+ if ret1:
457
+ print("Success:", raw_text)
458
+ success_count += 1
459
+ else:
460
+ print("Error:", raw_text)
461
+ error_count += 1
462
+ print(f"Total Success: {success_count}, Total Error: {error_count}")