whisperspeech2 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- whisperspeech2/__init__.py +1 -0
- whisperspeech2/a2wav.py +72 -0
- whisperspeech2/inference.py +56 -0
- whisperspeech2/languages.py +131 -0
- whisperspeech2/modules.py +303 -0
- whisperspeech2/pipeline.py +107 -0
- whisperspeech2/quick_test.py +8 -0
- whisperspeech2/s2a_delar_mup_wds_mlang.py +378 -0
- whisperspeech2/s2a_delar_mup_wds_mlang_cond.py +434 -0
- whisperspeech2/t2s_up_wds_mlang_enclm.py +336 -0
- whisperspeech2-0.9.0.dist-info/METADATA +122 -0
- whisperspeech2-0.9.0.dist-info/RECORD +15 -0
- whisperspeech2-0.9.0.dist-info/WHEEL +5 -0
- whisperspeech2-0.9.0.dist-info/licenses/LICENSE +22 -0
- whisperspeech2-0.9.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.9.0"
|
whisperspeech2/a2wav.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
__all__ = ['Vocoder']
|
|
2
|
+
|
|
3
|
+
from vocos import Vocos
|
|
4
|
+
from whisperspeech2 import inference
|
|
5
|
+
import torch
|
|
6
|
+
|
|
7
|
+
class Vocoder:
|
|
8
|
+
def __init__(self, repo_id="charactr/vocos-encodec-24khz", device=None, cache_dir=None):
|
|
9
|
+
if device is None: device = inference.get_compute_device()
|
|
10
|
+
if device == 'mps': device = 'cpu'
|
|
11
|
+
self.device = device
|
|
12
|
+
self.vocos = Vocos.from_pretrained(repo_id).to(device)
|
|
13
|
+
|
|
14
|
+
def is_notebook(self):
|
|
15
|
+
try:
|
|
16
|
+
return get_ipython().__class__.__name__ == "ZMQInteractiveShell"
|
|
17
|
+
except:
|
|
18
|
+
return False
|
|
19
|
+
|
|
20
|
+
@torch.no_grad()
|
|
21
|
+
def decode(self, atoks):
|
|
22
|
+
if len(atoks.shape) == 3:
|
|
23
|
+
b,q,t = atoks.shape
|
|
24
|
+
atoks = atoks.permute(1,0,2)
|
|
25
|
+
else:
|
|
26
|
+
q,t = atoks.shape
|
|
27
|
+
atoks = atoks.to(self.device)
|
|
28
|
+
features = self.vocos.codes_to_features(atoks)
|
|
29
|
+
bandwidth_id = torch.tensor({2: 0, 4: 1, 8: 2}[q]).to(self.device)
|
|
30
|
+
return self.vocos.decode(features, bandwidth_id=bandwidth_id)
|
|
31
|
+
|
|
32
|
+
def _save_audio(self, fname, audio_tensor, sample_rate=24000):
|
|
33
|
+
try:
|
|
34
|
+
import torchaudio
|
|
35
|
+
torchaudio.save(fname, audio_tensor, sample_rate, backend="soundfile")
|
|
36
|
+
return
|
|
37
|
+
except (ImportError, RuntimeError, TypeError):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
import torchaudio
|
|
42
|
+
torchaudio.save(fname, audio_tensor, sample_rate)
|
|
43
|
+
return
|
|
44
|
+
except (ImportError, RuntimeError):
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
import soundfile as sf
|
|
49
|
+
audio_np = audio_tensor.numpy().T
|
|
50
|
+
sf.write(fname, audio_np, sample_rate)
|
|
51
|
+
return
|
|
52
|
+
except ImportError:
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
raise ImportError(
|
|
56
|
+
"No audio backend available. Please install either torchaudio or soundfile:\n"
|
|
57
|
+
" pip install torchaudio\n"
|
|
58
|
+
"or\n"
|
|
59
|
+
" pip install soundfile"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def decode_to_file(self, fname, atoks):
|
|
63
|
+
audio = self.decode(atoks)
|
|
64
|
+
self._save_audio(fname, audio.cpu(), 24000)
|
|
65
|
+
if self.is_notebook():
|
|
66
|
+
from IPython.display import display, HTML, Audio
|
|
67
|
+
display(HTML(f'<a href="{fname}" target="_blank">Listen to {fname}</a>'))
|
|
68
|
+
|
|
69
|
+
def decode_to_notebook(self, atoks):
|
|
70
|
+
from IPython.display import display, HTML, Audio
|
|
71
|
+
audio = self.decode(atoks)
|
|
72
|
+
display(Audio(audio.cpu().numpy(), rate=24000))
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
__all__ = ['get_compute_device']
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
import torch.nn.functional as F
|
|
5
|
+
from huggingface_hub import hf_hub_download
|
|
6
|
+
from contextlib import nullcontext
|
|
7
|
+
|
|
8
|
+
def get_default_compute_device():
|
|
9
|
+
if torch.cuda.is_available() and (torch.version.cuda or torch.version.hip):
|
|
10
|
+
return 'cuda'
|
|
11
|
+
elif torch.backends.mps.is_available():
|
|
12
|
+
return 'mps'
|
|
13
|
+
else:
|
|
14
|
+
return 'cpu'
|
|
15
|
+
|
|
16
|
+
preferred_device = None
|
|
17
|
+
|
|
18
|
+
def get_compute_device():
|
|
19
|
+
global preferred_device
|
|
20
|
+
if preferred_device is None: preferred_device = get_default_compute_device()
|
|
21
|
+
return preferred_device
|
|
22
|
+
|
|
23
|
+
def load_model(ref=None, spec=None, device='cpu', cache_dir=None):
|
|
24
|
+
if spec is not None: return spec
|
|
25
|
+
if ":" in ref:
|
|
26
|
+
repo_id, filename = ref.split(":", 1)
|
|
27
|
+
local_filename = hf_hub_download(repo_id=repo_id, filename=filename, cache_dir=cache_dir)
|
|
28
|
+
else:
|
|
29
|
+
local_filename = ref
|
|
30
|
+
return torch.load(local_filename, map_location=device)
|
|
31
|
+
|
|
32
|
+
def inference_context():
|
|
33
|
+
if torch.cuda.is_available():
|
|
34
|
+
return torch.backends.cuda.sdp_kernel(enable_flash=False, enable_mem_efficient=False, enable_math=True)
|
|
35
|
+
else:
|
|
36
|
+
return nullcontext()
|
|
37
|
+
|
|
38
|
+
def multinomial_sample_one_no_sync(probs_sort):
|
|
39
|
+
q = torch.empty_like(probs_sort).exponential_(1)
|
|
40
|
+
return torch.argmax(probs_sort / q, dim=-1, keepdim=True).to(dtype=torch.int)
|
|
41
|
+
|
|
42
|
+
def logits_to_probs(logits, T=1.0, top_k=None):
|
|
43
|
+
logits = logits / max(T, 1e-5)
|
|
44
|
+
|
|
45
|
+
if top_k is not None:
|
|
46
|
+
v, _ = torch.topk(logits, min(top_k, logits.size(-1)))
|
|
47
|
+
pivot = v.select(-1, -1).unsqueeze(-1)
|
|
48
|
+
logits = torch.where(logits < pivot, -float("Inf"), logits)
|
|
49
|
+
|
|
50
|
+
probs = torch.nn.functional.softmax(logits, dim=-1)
|
|
51
|
+
return probs
|
|
52
|
+
|
|
53
|
+
def sample(logits, T=1.0, top_k=None):
|
|
54
|
+
probs = logits_to_probs(logits, T, top_k)
|
|
55
|
+
idx_next = multinomial_sample_one_no_sync(probs)
|
|
56
|
+
return idx_next
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/B. Languages.ipynb.
|
|
2
|
+
|
|
3
|
+
# %% auto 0
|
|
4
|
+
__all__ = ['to_id']
|
|
5
|
+
|
|
6
|
+
# %% ../nbs/B. Languages.ipynb 3
|
|
7
|
+
LANGUAGES = {
|
|
8
|
+
"en": "english",
|
|
9
|
+
"zh": "chinese",
|
|
10
|
+
"de": "german",
|
|
11
|
+
"es": "spanish",
|
|
12
|
+
"ru": "russian",
|
|
13
|
+
"ko": "korean",
|
|
14
|
+
"fr": "french",
|
|
15
|
+
"ja": "japanese",
|
|
16
|
+
"pt": "portuguese",
|
|
17
|
+
"tr": "turkish",
|
|
18
|
+
"pl": "polish",
|
|
19
|
+
"ca": "catalan",
|
|
20
|
+
"nl": "dutch",
|
|
21
|
+
"ar": "arabic",
|
|
22
|
+
"sv": "swedish",
|
|
23
|
+
"it": "italian",
|
|
24
|
+
"id": "indonesian",
|
|
25
|
+
"hi": "hindi",
|
|
26
|
+
"fi": "finnish",
|
|
27
|
+
"vi": "vietnamese",
|
|
28
|
+
"he": "hebrew",
|
|
29
|
+
"uk": "ukrainian",
|
|
30
|
+
"el": "greek",
|
|
31
|
+
"ms": "malay",
|
|
32
|
+
"cs": "czech",
|
|
33
|
+
"ro": "romanian",
|
|
34
|
+
"da": "danish",
|
|
35
|
+
"hu": "hungarian",
|
|
36
|
+
"ta": "tamil",
|
|
37
|
+
"no": "norwegian",
|
|
38
|
+
"th": "thai",
|
|
39
|
+
"ur": "urdu",
|
|
40
|
+
"hr": "croatian",
|
|
41
|
+
"bg": "bulgarian",
|
|
42
|
+
"lt": "lithuanian",
|
|
43
|
+
"la": "latin",
|
|
44
|
+
"mi": "maori",
|
|
45
|
+
"ml": "malayalam",
|
|
46
|
+
"cy": "welsh",
|
|
47
|
+
"sk": "slovak",
|
|
48
|
+
"te": "telugu",
|
|
49
|
+
"fa": "persian",
|
|
50
|
+
"lv": "latvian",
|
|
51
|
+
"bn": "bengali",
|
|
52
|
+
"sr": "serbian",
|
|
53
|
+
"az": "azerbaijani",
|
|
54
|
+
"sl": "slovenian",
|
|
55
|
+
"kn": "kannada",
|
|
56
|
+
"et": "estonian",
|
|
57
|
+
"mk": "macedonian",
|
|
58
|
+
"br": "breton",
|
|
59
|
+
"eu": "basque",
|
|
60
|
+
"is": "icelandic",
|
|
61
|
+
"hy": "armenian",
|
|
62
|
+
"ne": "nepali",
|
|
63
|
+
"mn": "mongolian",
|
|
64
|
+
"bs": "bosnian",
|
|
65
|
+
"kk": "kazakh",
|
|
66
|
+
"sq": "albanian",
|
|
67
|
+
"sw": "swahili",
|
|
68
|
+
"gl": "galician",
|
|
69
|
+
"mr": "marathi",
|
|
70
|
+
"pa": "punjabi",
|
|
71
|
+
"si": "sinhala",
|
|
72
|
+
"km": "khmer",
|
|
73
|
+
"sn": "shona",
|
|
74
|
+
"yo": "yoruba",
|
|
75
|
+
"so": "somali",
|
|
76
|
+
"af": "afrikaans",
|
|
77
|
+
"oc": "occitan",
|
|
78
|
+
"ka": "georgian",
|
|
79
|
+
"be": "belarusian",
|
|
80
|
+
"tg": "tajik",
|
|
81
|
+
"sd": "sindhi",
|
|
82
|
+
"gu": "gujarati",
|
|
83
|
+
"am": "amharic",
|
|
84
|
+
"yi": "yiddish",
|
|
85
|
+
"lo": "lao",
|
|
86
|
+
"uz": "uzbek",
|
|
87
|
+
"fo": "faroese",
|
|
88
|
+
"ht": "haitian creole",
|
|
89
|
+
"ps": "pashto",
|
|
90
|
+
"tk": "turkmen",
|
|
91
|
+
"nn": "nynorsk",
|
|
92
|
+
"mt": "maltese",
|
|
93
|
+
"sa": "sanskrit",
|
|
94
|
+
"lb": "luxembourgish",
|
|
95
|
+
"my": "myanmar",
|
|
96
|
+
"bo": "tibetan",
|
|
97
|
+
"tl": "tagalog",
|
|
98
|
+
"mg": "malagasy",
|
|
99
|
+
"as": "assamese",
|
|
100
|
+
"tt": "tatar",
|
|
101
|
+
"haw": "hawaiian",
|
|
102
|
+
"ln": "lingala",
|
|
103
|
+
"ha": "hausa",
|
|
104
|
+
"ba": "bashkir",
|
|
105
|
+
"jw": "javanese",
|
|
106
|
+
"su": "sundanese",
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
# %% ../nbs/B. Languages.ipynb 4
|
|
110
|
+
# language code lookup by name, with a few language aliases
|
|
111
|
+
TO_LANGUAGE_CODE = {
|
|
112
|
+
**{language: code for code, language in LANGUAGES.items()},
|
|
113
|
+
"burmese": "my",
|
|
114
|
+
"valencian": "ca",
|
|
115
|
+
"flemish": "nl",
|
|
116
|
+
"haitian": "ht",
|
|
117
|
+
"letzeburgesch": "lb",
|
|
118
|
+
"pushto": "ps",
|
|
119
|
+
"panjabi": "pa",
|
|
120
|
+
"moldavian": "ro",
|
|
121
|
+
"moldovan": "ro",
|
|
122
|
+
"sinhalese": "si",
|
|
123
|
+
"castilian": "es",
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# %% ../nbs/B. Languages.ipynb 5
|
|
127
|
+
languages = tuple(LANGUAGES.keys())
|
|
128
|
+
|
|
129
|
+
# %% ../nbs/B. Languages.ipynb 6
|
|
130
|
+
def to_id(lang):
|
|
131
|
+
return languages.index(TO_LANGUAGE_CODE.get(lang, lang))
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
__all__ = ['LayerNorm', 'LinearHead', 'QueryHead', 'init_transformer', 'sinusoids', 'MultiHeadAttention',
|
|
2
|
+
'ResidualAttentionBlock', 'BaseDecoder', 'EmbeddingProjector', 'FlexEmbeddings']
|
|
3
|
+
|
|
4
|
+
import torch
|
|
5
|
+
import numpy as np
|
|
6
|
+
import math
|
|
7
|
+
|
|
8
|
+
from torch import Tensor, nn
|
|
9
|
+
import torch.nn.functional as F
|
|
10
|
+
from typing import Dict, Iterable, Optional
|
|
11
|
+
|
|
12
|
+
class LayerNorm(nn.LayerNorm):
|
|
13
|
+
def forward(self, x):
|
|
14
|
+
return super().forward(x.float()).type(x.dtype)
|
|
15
|
+
|
|
16
|
+
class LinearHead(nn.Linear):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
class QueryHead(nn.Linear):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
def init_transformer(m):
|
|
23
|
+
if isinstance(m, (nn.Linear, nn.Embedding)):
|
|
24
|
+
torch.nn.init.trunc_normal_(m.weight, std=.02)
|
|
25
|
+
if isinstance(m, nn.Linear) and m.bias is not None:
|
|
26
|
+
torch.nn.init.constant_(m.bias, 0)
|
|
27
|
+
elif isinstance(m, nn.LayerNorm):
|
|
28
|
+
torch.nn.init.constant_(m.bias, 0)
|
|
29
|
+
torch.nn.init.constant_(m.weight, 1.0)
|
|
30
|
+
|
|
31
|
+
def sinusoids(length, channels, max_timescale=10000):
|
|
32
|
+
assert channels % 2 == 0
|
|
33
|
+
log_timescale_increment = np.log(max_timescale) / (channels // 2 - 1)
|
|
34
|
+
inv_timescales = torch.exp(-log_timescale_increment * torch.arange(channels // 2))
|
|
35
|
+
scaled_time = torch.arange(length)[:, np.newaxis] * inv_timescales[np.newaxis, :]
|
|
36
|
+
return torch.cat([torch.sin(scaled_time), torch.cos(scaled_time)], dim=1)
|
|
37
|
+
|
|
38
|
+
class MultiHeadAttention(nn.Module):
|
|
39
|
+
def __init__(self, n_state: int, n_head: int, qk_scale: float = 1, rope: bool = False, cross=False):
|
|
40
|
+
super().__init__()
|
|
41
|
+
self.n_state = n_state
|
|
42
|
+
self.n_head = n_head
|
|
43
|
+
self.sqrt_qk_scale = math.sqrt(qk_scale)
|
|
44
|
+
self.query = QueryHead(n_state, n_state)
|
|
45
|
+
self.key = nn.Linear(n_state, n_state, bias=False)
|
|
46
|
+
self.value = nn.Linear(n_state, n_state)
|
|
47
|
+
self.out = nn.Linear(n_state, n_state)
|
|
48
|
+
self.cross = cross
|
|
49
|
+
self.query_subsampling = 1
|
|
50
|
+
self.key_subsampling = 1
|
|
51
|
+
|
|
52
|
+
self.cached_kvx = None
|
|
53
|
+
self.register_buffer('k_cache', None)
|
|
54
|
+
self.register_buffer('v_cache', None)
|
|
55
|
+
|
|
56
|
+
self.rotary = None
|
|
57
|
+
if rope:
|
|
58
|
+
self.rotary = Rotary(n_state // n_head)
|
|
59
|
+
self.qkv = None
|
|
60
|
+
self.kv = None
|
|
61
|
+
|
|
62
|
+
def setup_kv_cache(self, max_batch_size, max_seq_len, dtype=torch.float32):
|
|
63
|
+
cache_shape = (max_batch_size, self.n_head, max_seq_len, self.n_state//self.n_head)
|
|
64
|
+
self.k_cache = torch.zeros(cache_shape, dtype=dtype, device=self.key.weight.device)
|
|
65
|
+
self.v_cache = torch.zeros(cache_shape, dtype=dtype, device=self.value.weight.device)
|
|
66
|
+
|
|
67
|
+
def merge_linears(self, layers, mults):
|
|
68
|
+
bias = [x.bias for x in layers if x.bias is not None][0]
|
|
69
|
+
din, dout = layers[0].weight.shape
|
|
70
|
+
new = nn.Linear(din, len(layers) * dout).to(layers[0].weight.device)
|
|
71
|
+
with torch.no_grad():
|
|
72
|
+
new.weight[:] = torch.cat([x.weight * m for x,m in zip(layers, mults)])
|
|
73
|
+
new.bias[:] = torch.cat([torch.zeros_like(bias) if x.bias is None else x.bias * m for x, m in zip(layers, mults)])
|
|
74
|
+
return new
|
|
75
|
+
|
|
76
|
+
def convert_for_eval(self):
|
|
77
|
+
if self.qkv or self.kv: raise AttributeError("already converted")
|
|
78
|
+
|
|
79
|
+
self.odim = self.key.weight.shape[1]
|
|
80
|
+
if self.cross:
|
|
81
|
+
self.q = self.merge_linears([self.query], [self.sqrt_qk_scale])
|
|
82
|
+
self.kv = self.merge_linears([self.key, self.value],
|
|
83
|
+
[self.sqrt_qk_scale, 1])
|
|
84
|
+
else:
|
|
85
|
+
self.qkv = self.merge_linears([self.query, self.key, self.value],
|
|
86
|
+
[self.sqrt_qk_scale, self.sqrt_qk_scale, 1])
|
|
87
|
+
|
|
88
|
+
def split_heads(self, x, x_positions, rope=False, subsampling=1):
|
|
89
|
+
x = x.view(*x.shape[:2], self.n_head, -1)
|
|
90
|
+
if rope:
|
|
91
|
+
x = rope_rotate(x, x_positions * subsampling, *self.rotary(x))
|
|
92
|
+
return x.permute(0, 2, 1, 3)
|
|
93
|
+
|
|
94
|
+
def forward(
|
|
95
|
+
self,
|
|
96
|
+
qx,
|
|
97
|
+
q_positions,
|
|
98
|
+
kvx,
|
|
99
|
+
kv_positions,
|
|
100
|
+
causal = False,
|
|
101
|
+
mask=None,
|
|
102
|
+
):
|
|
103
|
+
if self.k_cache is not None:
|
|
104
|
+
assert qx.shape[0] <= self.k_cache.shape[0], "please pass in a larger max_batch_size to setup_kv_cache"
|
|
105
|
+
if self.qkv:
|
|
106
|
+
q,k,v = self.qkv(qx).split(self.odim, dim=-1)
|
|
107
|
+
elif self.kv:
|
|
108
|
+
q = self.q(qx)
|
|
109
|
+
k,v = self.kv(kvx).split(self.odim, dim=-1)
|
|
110
|
+
else:
|
|
111
|
+
q,k,v = None,None,None
|
|
112
|
+
|
|
113
|
+
if q is None: q = self.query(qx) * self.sqrt_qk_scale
|
|
114
|
+
q = self.split_heads(q, q_positions, rope = self.rotary, subsampling = self.query_subsampling)
|
|
115
|
+
|
|
116
|
+
if kvx is not self.cached_kvx:
|
|
117
|
+
if k is None: k = self.key(kvx) * self.sqrt_qk_scale
|
|
118
|
+
k = self.split_heads(k, kv_positions, rope = self.rotary, subsampling = self.key_subsampling)
|
|
119
|
+
if v is None: v = self.value(kvx)
|
|
120
|
+
v = self.split_heads(v, kv_positions)
|
|
121
|
+
if self.k_cache is not None:
|
|
122
|
+
self.k_cache[:k.shape[0],:,kv_positions] = k
|
|
123
|
+
self.v_cache[:v.shape[0],:,kv_positions] = v
|
|
124
|
+
|
|
125
|
+
if self.k_cache is not None:
|
|
126
|
+
k, v = self.k_cache[:k.shape[0]], self.v_cache[:v.shape[0]]
|
|
127
|
+
|
|
128
|
+
if mask is not None:
|
|
129
|
+
mask = mask[q_positions,:k.shape[-2]]
|
|
130
|
+
|
|
131
|
+
wv = F.scaled_dot_product_attention(q, k, v, attn_mask=mask, dropout_p=0, is_causal=causal)
|
|
132
|
+
|
|
133
|
+
return self.out(wv.permute(0, 2, 1, 3).flatten(start_dim=2))
|
|
134
|
+
|
|
135
|
+
class Rotary(torch.nn.Module):
|
|
136
|
+
def __init__(self, dim, base=10000):
|
|
137
|
+
super().__init__()
|
|
138
|
+
inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))
|
|
139
|
+
self.register_buffer("inv_freq", inv_freq)
|
|
140
|
+
self.seq_len_cached = None
|
|
141
|
+
self.cos_cached = None
|
|
142
|
+
self.sin_cached = None
|
|
143
|
+
|
|
144
|
+
def forward(self, x, seq_dim=1):
|
|
145
|
+
seq_len = x.shape[seq_dim]
|
|
146
|
+
if not self.seq_len_cached or seq_len > self.seq_len_cached:
|
|
147
|
+
self.seq_len_cached = 2500
|
|
148
|
+
|
|
149
|
+
t = torch.arange(self.seq_len_cached, device=x.device).type_as(self.inv_freq)
|
|
150
|
+
freqs = torch.einsum("i,j->ij", t, self.inv_freq)
|
|
151
|
+
emb = torch.cat((freqs, freqs), dim=-1).to(x.device)
|
|
152
|
+
self.cos_cached = emb.cos()[None, :, None, :]
|
|
153
|
+
self.sin_cached = emb.sin()[None, :, None, :]
|
|
154
|
+
return self.cos_cached, self.sin_cached
|
|
155
|
+
|
|
156
|
+
def rotate_half(x):
|
|
157
|
+
x1, x2 = x[..., : x.shape[-1] // 2], x[..., x.shape[-1] // 2 :]
|
|
158
|
+
return torch.cat(
|
|
159
|
+
(-x2, x1), dim=len(x.shape)-1
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
def rope_rotate(x, positions, cos, sin):
|
|
163
|
+
return x * cos[:,positions] + rotate_half(x) * sin[:,positions]
|
|
164
|
+
|
|
165
|
+
class ResidualAttentionBlock(nn.Module):
|
|
166
|
+
def __init__(self, n_state: int, n_head: int, cross_attention: bool = False, rope: bool = False,
|
|
167
|
+
qk_scale: float = 1, ffn_mult: int = 4):
|
|
168
|
+
super().__init__()
|
|
169
|
+
self.attn = MultiHeadAttention(n_state, n_head, qk_scale=qk_scale, rope=rope)
|
|
170
|
+
self.attn_ln = LayerNorm(n_state)
|
|
171
|
+
|
|
172
|
+
self.cross_attn = (
|
|
173
|
+
MultiHeadAttention(n_state, n_head, qk_scale=qk_scale, rope=rope, cross=True) if cross_attention else None
|
|
174
|
+
)
|
|
175
|
+
self.cross_attn_ln = LayerNorm(n_state) if cross_attention else None
|
|
176
|
+
|
|
177
|
+
n_mlp = n_state * ffn_mult
|
|
178
|
+
self.mlp = nn.Sequential(
|
|
179
|
+
nn.Linear(n_state, n_mlp), nn.GELU(), nn.Linear(n_mlp, n_state)
|
|
180
|
+
)
|
|
181
|
+
self.mlp_ln = LayerNorm(n_state)
|
|
182
|
+
|
|
183
|
+
def setup_kv_cache(self, max_batch_size, max_seq_len, max_cross_seq_len=None):
|
|
184
|
+
self.attn.setup_kv_cache(max_batch_size, max_seq_len)
|
|
185
|
+
if self.cross_attn:
|
|
186
|
+
self.cross_attn.setup_kv_cache(max_batch_size, max_cross_seq_len)
|
|
187
|
+
|
|
188
|
+
def forward(
|
|
189
|
+
self,
|
|
190
|
+
x: Tensor,
|
|
191
|
+
x_positions: Tensor = None,
|
|
192
|
+
xa: Optional[Tensor] = None,
|
|
193
|
+
xa_positions: Optional[Tensor] = None,
|
|
194
|
+
causal = False,
|
|
195
|
+
mask=None,
|
|
196
|
+
):
|
|
197
|
+
lnx = self.attn_ln(x)
|
|
198
|
+
x = x + self.attn(lnx, x_positions, lnx, x_positions, causal=causal, mask=mask)
|
|
199
|
+
if self.cross_attn:
|
|
200
|
+
lnx = self.cross_attn_ln(x)
|
|
201
|
+
x = x + self.cross_attn(lnx, x_positions, xa, xa_positions)
|
|
202
|
+
x = x + self.mlp(self.mlp_ln(x))
|
|
203
|
+
return x
|
|
204
|
+
|
|
205
|
+
class BaseDecoder(nn.Module):
|
|
206
|
+
def __init__(self, depth=6, n_head=6, width=384, qk_scale=1, ffn_mult=4, length=2250, rope=False):
|
|
207
|
+
super().__init__()
|
|
208
|
+
self.length = length
|
|
209
|
+
self.width = width
|
|
210
|
+
self.layers = nn.ModuleList([
|
|
211
|
+
ResidualAttentionBlock(
|
|
212
|
+
self.width, n_head, qk_scale=qk_scale, ffn_mult=ffn_mult, cross_attention=True, rope=rope
|
|
213
|
+
) for _ in range(math.floor(depth))
|
|
214
|
+
])
|
|
215
|
+
|
|
216
|
+
self.ln_post = LayerNorm(width)
|
|
217
|
+
|
|
218
|
+
mask = torch.empty(length, length).fill_(-torch.inf).triu_(1)
|
|
219
|
+
self.register_buffer("mask", mask, persistent=False)
|
|
220
|
+
|
|
221
|
+
def forward(self, x, x_positions, xenc, xenc_positions):
|
|
222
|
+
for i,l in enumerate(self.layers):
|
|
223
|
+
x = l(x, x_positions, xenc, xenc_positions, causal=self.training, mask=self.mask if not self.training else None)
|
|
224
|
+
|
|
225
|
+
x = self.ln_post(x)
|
|
226
|
+
|
|
227
|
+
return x
|
|
228
|
+
|
|
229
|
+
class EmbeddingProjector(nn.Linear):
|
|
230
|
+
pass
|
|
231
|
+
|
|
232
|
+
class FlexEmbeddings(nn.Module):
|
|
233
|
+
def __init__(self, codes, width, special_codes=None, frozen_width=None, special_embedding=None, unembed=True):
|
|
234
|
+
super().__init__()
|
|
235
|
+
self.codes = codes
|
|
236
|
+
self.special_codes = special_codes
|
|
237
|
+
if frozen_width is None: frozen_width = width
|
|
238
|
+
|
|
239
|
+
self.main = nn.Embedding(codes, frozen_width or width)
|
|
240
|
+
self.emb_to_hidden = EmbeddingProjector(frozen_width, width) if frozen_width != width else None
|
|
241
|
+
self.hidden_to_emb = EmbeddingProjector(width, frozen_width) if unembed and frozen_width != width else None
|
|
242
|
+
if special_codes:
|
|
243
|
+
self.special = special_embedding or nn.Embedding(special_codes, width)
|
|
244
|
+
|
|
245
|
+
self.register_buffer('merged_in', None)
|
|
246
|
+
self.register_buffer('merged_out', None)
|
|
247
|
+
self.register_buffer('bias_out', None)
|
|
248
|
+
|
|
249
|
+
def set_frozen_embeddings(self, values):
|
|
250
|
+
with torch.no_grad():
|
|
251
|
+
self.main.weight[:] = values
|
|
252
|
+
self.main.lr_scale = 0
|
|
253
|
+
|
|
254
|
+
@torch.no_grad()
|
|
255
|
+
def convert_for_eval(self):
|
|
256
|
+
if not self.special_codes: return
|
|
257
|
+
main_w = self.main.weight
|
|
258
|
+
if self.emb_to_hidden is not None: main_w = self.emb_to_hidden(main_w)
|
|
259
|
+
weight = torch.cat([main_w, self.special.weight], dim=0)
|
|
260
|
+
self.merged_in = nn.Embedding(*weight.shape, _weight=weight)
|
|
261
|
+
|
|
262
|
+
weight = self.main.weight
|
|
263
|
+
if self.hidden_to_emb: weight = weight @ self.hidden_to_emb.weight
|
|
264
|
+
self.merged_out = torch.cat([weight.T, self.special.weight.T], dim=1).T.contiguous()
|
|
265
|
+
if self.hidden_to_emb:
|
|
266
|
+
self.bias_out = torch.cat([
|
|
267
|
+
self.hidden_to_emb.bias @ self.main.weight.T,
|
|
268
|
+
torch.zeros(self.special.weight.shape[0], device=weight.device, dtype=weight.dtype)
|
|
269
|
+
], dim=0)
|
|
270
|
+
else:
|
|
271
|
+
self.bias_out = None
|
|
272
|
+
|
|
273
|
+
def forward(self, toks):
|
|
274
|
+
if not self.training and self.merged_in is not None:
|
|
275
|
+
return self.merged_in(toks)
|
|
276
|
+
|
|
277
|
+
if self.special_codes:
|
|
278
|
+
special_mask = toks >= self.codes
|
|
279
|
+
embs = self.main(torch.where(special_mask, 0, toks))
|
|
280
|
+
else:
|
|
281
|
+
embs = self.main(toks)
|
|
282
|
+
|
|
283
|
+
if self.emb_to_hidden: embs = self.emb_to_hidden(embs)
|
|
284
|
+
|
|
285
|
+
if self.special_codes:
|
|
286
|
+
embs[special_mask] = self.special(toks[special_mask] - self.codes).to(embs.dtype)
|
|
287
|
+
|
|
288
|
+
return embs
|
|
289
|
+
|
|
290
|
+
def unembed(self, embs):
|
|
291
|
+
if not self.training and self.merged_out is not None:
|
|
292
|
+
return F.linear(embs, self.merged_out, self.bias_out)
|
|
293
|
+
|
|
294
|
+
orig_embs = embs
|
|
295
|
+
if self.hidden_to_emb: embs = self.hidden_to_emb(embs)
|
|
296
|
+
|
|
297
|
+
main_logits = (embs @ self.main.weight.to(embs.dtype).T).float()
|
|
298
|
+
|
|
299
|
+
if not self.special_codes:
|
|
300
|
+
return main_logits
|
|
301
|
+
|
|
302
|
+
special_logits = (orig_embs @ self.special.weight.to(orig_embs.dtype).T).float()
|
|
303
|
+
return torch.cat([main_logits, special_logits], dim=-1)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from os.path import expanduser
|
|
2
|
+
import torch
|
|
3
|
+
from whisperspeech2.t2s_up_wds_mlang_enclm import TSARTransformer
|
|
4
|
+
from whisperspeech2.s2a_delar_mup_wds_mlang import SADelARTransformer
|
|
5
|
+
from whisperspeech2.a2wav import Vocoder
|
|
6
|
+
from whisperspeech2 import inference, s2a_delar_mup_wds_mlang_cond
|
|
7
|
+
import traceback
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
__all__ = ['Pipeline']
|
|
11
|
+
|
|
12
|
+
class Pipeline:
|
|
13
|
+
default_speaker = torch.tensor(
|
|
14
|
+
[-0.2929, -0.4503, 0.4155, -0.1417, 0.0473, -0.1624, -0.2322, 0.7071,
|
|
15
|
+
0.4800, 0.5496, 0.0410, 0.6236, 0.4729, 0.0587, 0.2194, -0.0466,
|
|
16
|
+
-0.3036, 0.0497, 0.5028, -0.1703, 0.5039, -0.6464, 0.3857, -0.7350,
|
|
17
|
+
-0.1605, 0.4808, 0.5397, -0.4851, 0.1774, -0.8712, 0.5789, 0.1785,
|
|
18
|
+
-0.1417, 0.3039, 0.4232, -0.0186, 0.2685, 0.6153, -0.3103, -0.5706,
|
|
19
|
+
-0.4494, 0.3394, -0.6184, -0.3617, 1.1041, -0.1178, -0.1885, 0.1997,
|
|
20
|
+
0.5571, -0.2906, -0.0477, -0.4048, -0.1062, 1.4779, 0.1639, -0.3712,
|
|
21
|
+
-0.1776, -0.0568, -0.6162, 0.0110, -0.0207, -0.1319, -0.3854, 0.7248,
|
|
22
|
+
0.0343, 0.5724, 0.0670, 0.0486, -0.3813, 0.1738, 0.3017, 1.0502,
|
|
23
|
+
0.1550, 0.5708, 0.0366, 0.5093, 0.0294, -0.7091, -0.8220, -0.1583,
|
|
24
|
+
-0.2343, 0.1366, 0.7372, -0.0631, 0.1505, 0.4600, -0.1252, -0.5245,
|
|
25
|
+
0.7523, -0.0386, -0.2587, 1.0066, -0.2037, 0.1617, -0.3800, 0.2790,
|
|
26
|
+
0.0184, -0.5111, -0.7291, 0.1627, 0.2367, -0.0192, 0.4822, -0.4458,
|
|
27
|
+
0.1457, -0.5884, 0.1909, 0.2563, -0.2035, -0.0377, 0.7771, 0.2139,
|
|
28
|
+
0.3801, 0.6047, -0.6043, -0.2563, -0.0726, 0.3856, 0.3217, 0.0823,
|
|
29
|
+
-0.1302, 0.3287, 0.5693, 0.2453, 0.8231, 0.0072, 1.0327, 0.6065,
|
|
30
|
+
-0.0620, -0.5572, 0.5220, 0.2485, 0.1520, 0.0222, -0.2179, -0.7392,
|
|
31
|
+
-0.3855, 0.1822, 0.1042, 0.7133, 0.3583, 0.0606, -0.0424, -0.9189,
|
|
32
|
+
-0.4882, -0.5480, -0.5719, -0.1660, -0.3439, -0.5814, -0.2542, 0.0197,
|
|
33
|
+
0.4942, 0.0915, -0.0420, -0.0035, 0.5578, 0.1051, -0.0891, 0.2348,
|
|
34
|
+
0.6876, -0.6685, 0.8215, -0.3692, -0.3150, -0.0462, -0.6806, -0.2661,
|
|
35
|
+
-0.0308, -0.0050, 0.6756, -0.1647, 1.0734, 0.0049, 0.4969, 0.0259,
|
|
36
|
+
-0.8949, 0.0731, 0.0886, 0.3442, -0.1433, -0.6804, 0.2204, 0.1859,
|
|
37
|
+
0.2702, 0.1699, -0.1443, -0.9614, 0.3261, 0.1718, 0.3545, -0.0686]
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def __init__(self, t2s_ref=None, s2a_ref=None, optimize=True, torch_compile=False, device=None, cache_dir=None):
|
|
41
|
+
if device is None: device = inference.get_compute_device()
|
|
42
|
+
self.device = device
|
|
43
|
+
args = dict(device = device, cache_dir=cache_dir)
|
|
44
|
+
try:
|
|
45
|
+
if t2s_ref:
|
|
46
|
+
args["ref"] = t2s_ref
|
|
47
|
+
self.t2s = TSARTransformer.load_model(**args)
|
|
48
|
+
if optimize: self.t2s.optimize(torch_compile=torch_compile)
|
|
49
|
+
except:
|
|
50
|
+
print("Failed to load the T2S model:")
|
|
51
|
+
print(traceback.format_exc())
|
|
52
|
+
args = dict(device = device, cache_dir=cache_dir)
|
|
53
|
+
try:
|
|
54
|
+
if s2a_ref:
|
|
55
|
+
spec = inference.load_model(ref=s2a_ref, device=device, cache_dir=cache_dir)
|
|
56
|
+
if [x for x in spec['state_dict'].keys() if x.startswith('cond_embeddings.')]:
|
|
57
|
+
cls = s2a_delar_mup_wds_mlang_cond.SADelARTransformer
|
|
58
|
+
args['spec'] = spec
|
|
59
|
+
else:
|
|
60
|
+
cls = SADelARTransformer
|
|
61
|
+
args['spec'] = spec
|
|
62
|
+
else:
|
|
63
|
+
cls = SADelARTransformer
|
|
64
|
+
self.s2a = cls.load_model(**args)
|
|
65
|
+
if optimize: self.s2a.optimize(torch_compile=torch_compile)
|
|
66
|
+
except:
|
|
67
|
+
print("Failed to load the S2A model:")
|
|
68
|
+
print(traceback.format_exc())
|
|
69
|
+
|
|
70
|
+
self.vocoder = Vocoder(device=device)
|
|
71
|
+
self.encoder = None
|
|
72
|
+
|
|
73
|
+
def extract_spk_emb(self, fname):
|
|
74
|
+
import torchaudio
|
|
75
|
+
if self.encoder is None:
|
|
76
|
+
device = self.device
|
|
77
|
+
if device == 'mps': device = 'cpu'
|
|
78
|
+
from speechbrain.pretrained import EncoderClassifier
|
|
79
|
+
self.encoder = EncoderClassifier.from_hparams("speechbrain/spkrec-ecapa-voxceleb",
|
|
80
|
+
savedir=expanduser("~/.cache/speechbrain/"),
|
|
81
|
+
run_opts={"device": device})
|
|
82
|
+
audio_info = torchaudio.info(fname)
|
|
83
|
+
actual_sample_rate = audio_info.sample_rate
|
|
84
|
+
num_frames = actual_sample_rate * 30
|
|
85
|
+
samples, sr = torchaudio.load(fname, num_frames=num_frames)
|
|
86
|
+
samples = samples[:, :num_frames]
|
|
87
|
+
samples = self.encoder.audio_normalizer(samples[0], sr)
|
|
88
|
+
spk_emb = self.encoder.encode_batch(samples.unsqueeze(0))
|
|
89
|
+
|
|
90
|
+
return spk_emb[0,0].to(self.device)
|
|
91
|
+
|
|
92
|
+
def generate_atoks(self, text, speaker=None, lang='en', cps=15, step_callback=None):
|
|
93
|
+
if speaker is None: speaker = self.default_speaker
|
|
94
|
+
elif isinstance(speaker, (str, Path)): speaker = self.extract_spk_emb(speaker)
|
|
95
|
+
text = text.replace("\n", " ")
|
|
96
|
+
stoks = self.t2s.generate(text, cps=cps, lang=lang, step=step_callback)[0]
|
|
97
|
+
atoks = self.s2a.generate(stoks, speaker.unsqueeze(0), step=step_callback)
|
|
98
|
+
return atoks
|
|
99
|
+
|
|
100
|
+
def generate(self, text, speaker=None, lang='en', cps=15, step_callback=None):
|
|
101
|
+
return self.vocoder.decode(self.generate_atoks(text, speaker, lang=lang, cps=cps, step_callback=step_callback))
|
|
102
|
+
|
|
103
|
+
def generate_to_file(self, fname, text, speaker=None, lang='en', cps=15, step_callback=None):
|
|
104
|
+
self.vocoder.decode_to_file(fname, self.generate_atoks(text, speaker, lang=lang, cps=cps, step_callback=None))
|
|
105
|
+
|
|
106
|
+
def generate_to_notebook(self, text, speaker=None, lang='en', cps=15, step_callback=None):
|
|
107
|
+
self.vocoder.decode_to_notebook(self.generate_atoks(text, speaker, lang=lang, cps=cps, step_callback=None))
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from whisperspeech2.pipeline import Pipeline
|
|
2
|
+
|
|
3
|
+
tts_pipe = Pipeline(s2a_ref='collabora/whisperspeech:s2a-q4-tiny-en+pl.model') # uncomment the line for the model you want to use
|
|
4
|
+
# tts_pipe = Pipeline(s2a_ref='collabora/whisperspeech:s2a-q4-base-en+pl.model') # uncomment the line for the model you want to use
|
|
5
|
+
# tts_pipe = Pipeline(s2a_ref='collabora/whisperspeech:s2a-q4-small-en+pl.model') # uncomment the line for the model you want to use
|
|
6
|
+
|
|
7
|
+
save_path = 'output.wav' # change the file extension to .mp3, .flac, .ogg etc. to save to a different file format
|
|
8
|
+
tts_pipe.generate_to_file(save_path, "This is a test")
|