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
Files changed (3) hide show
  1. app.py +12 -6
  2. requirements.txt +4 -2
  3. 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(device=device)
21
 
22
 
23
  def split_to_parts(text):
 
 
24
  split_symbols = '.?!:'
25
  parts = ['']
26
  index = 0
27
- for s in text:
 
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.generate_text(part) + ' '
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
- ps = ipa(stressify(t))
 
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@105aed29fa1a7698d08d920986890e9bbd03447c
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
- from transformers import MBartForConditionalGeneration, AutoTokenizer
3
-
4
- verbalizer_model_name = "skypro1111/mbart-large-50-verbalization"
 
5
 
6
 
7
  class Verbalizer():
8
- def __init__(self, device):
9
- self.device = device
 
 
 
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
- def generate_text(self, text):
22
- """Generate text for a single input."""
23
- # Prepare input
24
- input_text = "<verbalization>:" + text
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
- normalized_text = self.tokenizer.decode(output_ids[0], skip_special_tokens=True)
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