Spaces:
Paused
Paused
Serhiy Stetskovych commited on
Commit ·
509e004
1
Parent(s): d44b6b5
New verbalizer, and better text processing, also upgraded styletts2-inference library with fix for short sentances generation.
Browse files- app.py +12 -6
- requirements.txt +4 -2
- verbalizer.py +48 -32
app.py
CHANGED
|
@@ -17,16 +17,19 @@ device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
|
| 17 |
|
| 18 |
prompts_dir = 'voices'
|
| 19 |
|
| 20 |
-
verbalizer = Verbalizer(
|
| 21 |
|
| 22 |
|
| 23 |
def split_to_parts(text):
|
|
|
|
|
|
|
| 24 |
split_symbols = '.?!:'
|
| 25 |
parts = ['']
|
| 26 |
index = 0
|
| 27 |
-
|
|
|
|
| 28 |
parts[index] += s
|
| 29 |
-
if s in split_symbols and len(parts[index]) > 20:
|
| 30 |
index += 1
|
| 31 |
parts.append('')
|
| 32 |
return parts
|
|
@@ -60,13 +63,12 @@ models = {
|
|
| 60 |
}
|
| 61 |
|
| 62 |
|
| 63 |
-
@spaces.GPU
|
| 64 |
def verbalize(text):
|
| 65 |
parts = split_to_parts(text)
|
| 66 |
verbalized = ''
|
| 67 |
for part in parts:
|
| 68 |
if part.strip():
|
| 69 |
-
verbalized += verbalizer.
|
| 70 |
return verbalized
|
| 71 |
|
| 72 |
description = f'''
|
|
@@ -103,6 +105,7 @@ def synthesize(model_name, text, speed, voice_name = None, progress=gr.Progress(
|
|
| 103 |
|
| 104 |
result_wav = []
|
| 105 |
for t in progress.tqdm(split_to_parts(text)):
|
|
|
|
| 106 |
|
| 107 |
t = t.strip()
|
| 108 |
t = t.replace('"', '')
|
|
@@ -111,8 +114,11 @@ def synthesize(model_name, text, speed, voice_name = None, progress=gr.Progress(
|
|
| 111 |
t = normalize('NFKC', t)
|
| 112 |
|
| 113 |
t = re.sub(r'[᠆‐‑‒–—―⁻₋−⸺⸻]', '-', t)
|
|
|
|
|
|
|
| 114 |
t = re.sub(r' - ', ': ', t)
|
| 115 |
-
|
|
|
|
| 116 |
|
| 117 |
if ps:
|
| 118 |
tokens = models[model_name]['model'].tokenizer.encode(ps)
|
|
|
|
| 17 |
|
| 18 |
prompts_dir = 'voices'
|
| 19 |
|
| 20 |
+
verbalizer = Verbalizer()
|
| 21 |
|
| 22 |
|
| 23 |
def split_to_parts(text):
|
| 24 |
+
text = re.sub(r'([^.,!:?\-])\n', r'\1. ', text)
|
| 25 |
+
text = text.replace('\n', ' ')
|
| 26 |
split_symbols = '.?!:'
|
| 27 |
parts = ['']
|
| 28 |
index = 0
|
| 29 |
+
last = len(text)-1
|
| 30 |
+
for i, s in enumerate(text):
|
| 31 |
parts[index] += s
|
| 32 |
+
if s in split_symbols and i < last and text[i+1] == ' ' and len(parts[index]) > 20:
|
| 33 |
index += 1
|
| 34 |
parts.append('')
|
| 35 |
return parts
|
|
|
|
| 63 |
}
|
| 64 |
|
| 65 |
|
|
|
|
| 66 |
def verbalize(text):
|
| 67 |
parts = split_to_parts(text)
|
| 68 |
verbalized = ''
|
| 69 |
for part in parts:
|
| 70 |
if part.strip():
|
| 71 |
+
verbalized += verbalizer.process_text(part)[0] + ' '
|
| 72 |
return verbalized
|
| 73 |
|
| 74 |
description = f'''
|
|
|
|
| 105 |
|
| 106 |
result_wav = []
|
| 107 |
for t in progress.tqdm(split_to_parts(text)):
|
| 108 |
+
|
| 109 |
|
| 110 |
t = t.strip()
|
| 111 |
t = t.replace('"', '')
|
|
|
|
| 114 |
t = normalize('NFKC', t)
|
| 115 |
|
| 116 |
t = re.sub(r'[᠆‐‑‒–—―⁻₋−⸺⸻]', '-', t)
|
| 117 |
+
if t[-1] not in '.?!:-':
|
| 118 |
+
t += '.'
|
| 119 |
t = re.sub(r' - ', ': ', t)
|
| 120 |
+
t = stressify(t)
|
| 121 |
+
ps = ipa(t)
|
| 122 |
|
| 123 |
if ps:
|
| 124 |
tokens = models[model_name]['model'].tokenizer.encode(ps)
|
requirements.txt
CHANGED
|
@@ -10,7 +10,9 @@ torch==2.2.0
|
|
| 10 |
transformers==5.0.0
|
| 11 |
git+https://github.com/patriotyk/ukrainian-word-stress.git
|
| 12 |
git+https://github.com/patriotyk/ipa-uk.git
|
| 13 |
-
git+https://github.com/patriotyk/styletts2-inference@
|
| 14 |
spaces
|
| 15 |
numpy<2
|
| 16 |
-
accelerate>=0.26.0
|
|
|
|
|
|
|
|
|
| 10 |
transformers==5.0.0
|
| 11 |
git+https://github.com/patriotyk/ukrainian-word-stress.git
|
| 12 |
git+https://github.com/patriotyk/ipa-uk.git
|
| 13 |
+
git+https://github.com/patriotyk/styletts2-inference@209352700cf703a05ff94501290095dd9072fd5f
|
| 14 |
spaces
|
| 15 |
numpy<2
|
| 16 |
+
accelerate>=0.26.0
|
| 17 |
+
ctranslate2
|
| 18 |
+
sentencepiece
|
verbalizer.py
CHANGED
|
@@ -1,39 +1,55 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
|
|
|
| 5 |
|
| 6 |
|
| 7 |
class Verbalizer():
|
| 8 |
-
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
-
self.model = MBartForConditionalGeneration.from_pretrained(verbalizer_model_name,
|
| 12 |
-
low_cpu_mem_usage=True,
|
| 13 |
-
device_map=device,
|
| 14 |
-
)
|
| 15 |
-
self.model.eval()
|
| 16 |
-
|
| 17 |
-
self.tokenizer = AutoTokenizer.from_pretrained(verbalizer_model_name)
|
| 18 |
-
self.tokenizer.src_lang = "uk_XX"
|
| 19 |
-
self.tokenizer.tgt_lang = "uk_XX"
|
| 20 |
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
encoded_input = self.tokenizer(
|
| 27 |
-
input_text,
|
| 28 |
-
return_tensors="pt",
|
| 29 |
-
padding=True,
|
| 30 |
-
truncation=True,
|
| 31 |
-
max_length=1024,
|
| 32 |
-
).to(self.device)
|
| 33 |
-
output_ids = self.model.generate(
|
| 34 |
-
**encoded_input, max_length=1024, num_beams=5, early_stopping=True
|
| 35 |
)
|
| 36 |
-
|
| 37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
-
return normalized_text.strip()
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import time
|
| 3 |
+
import ctranslate2
|
| 4 |
+
from transformers import M2M100Tokenizer
|
| 5 |
+
from huggingface_hub import snapshot_download
|
| 6 |
|
| 7 |
|
| 8 |
class Verbalizer():
|
| 9 |
+
MODEL_PATH = os.getenv("MODEL_PATH", "skypro1111/m2m100-ukr-verbalization-ct2")
|
| 10 |
+
TOKENIZER_PATH = os.getenv("TOKENIZER_PATH", "skypro1111/m2m100-ukr-verbalization")
|
| 11 |
+
def __init__(self):
|
| 12 |
+
|
| 13 |
+
print("\nInitializing CTranslate2 model and tokenizer...")
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
+
# Download the model from HuggingFace Hub
|
| 17 |
+
local_model_path = snapshot_download(
|
| 18 |
+
repo_id=self.MODEL_PATH,
|
| 19 |
+
allow_patterns=["*.bin", "*.json", "tokenizer.json", "vocab.json"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
)
|
| 21 |
+
|
| 22 |
+
self.translator = ctranslate2.Translator(
|
| 23 |
+
local_model_path,
|
| 24 |
+
device='cpu',
|
| 25 |
+
compute_type="int8", # Use INT8 quantization for CPU
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
# Load tokenizer
|
| 29 |
+
self.tokenizer = M2M100Tokenizer.from_pretrained(self.TOKENIZER_PATH)
|
| 30 |
+
self.tokenizer.src_lang = "uk"
|
| 31 |
+
|
| 32 |
+
def process_text(self, text: str):
|
| 33 |
+
"""Process a single text input using the CTranslate2 model."""
|
| 34 |
+
start_time = time.time()
|
| 35 |
+
|
| 36 |
+
# Tokenize input
|
| 37 |
+
source = self.tokenizer.convert_ids_to_tokens(self.tokenizer.encode(text))
|
| 38 |
+
target_prefix = [self.tokenizer.lang_code_to_token["uk"]]
|
| 39 |
+
|
| 40 |
+
# Run inference
|
| 41 |
+
results = self.translator.translate_batch(
|
| 42 |
+
[source],
|
| 43 |
+
target_prefix=[target_prefix],
|
| 44 |
+
beam_size=1,
|
| 45 |
+
num_hypotheses=1,
|
| 46 |
+
use_vmap=True,
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
# Get target tokens and decode
|
| 50 |
+
target = results[0].hypotheses[0][1:] # Remove language token
|
| 51 |
+
output = self.tokenizer.decode(self.tokenizer.convert_tokens_to_ids(target))
|
| 52 |
+
|
| 53 |
+
inference_time = time.time() - start_time
|
| 54 |
+
return output, inference_time
|
| 55 |
|
|
|