Instructions to use YaTharThShaRma999/finetunedmodel with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use YaTharThShaRma999/finetunedmodel with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf") model = PeftModel.from_pretrained(base_model, "YaTharThShaRma999/finetunedmodel") - Notebooks
- Google Colab
- Kaggle
Create tts.py
Browse files
tts.py
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
from dataclasses import dataclass, field
|
| 3 |
+
from enum import Enum
|
| 4 |
+
import huggingface_hub
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
import random
|
| 7 |
+
import time
|
| 8 |
+
import typing as tp
|
| 9 |
+
|
| 10 |
+
import numpy as np
|
| 11 |
+
from safetensors.torch import load_file
|
| 12 |
+
import torch
|
| 13 |
+
|
| 14 |
+
from moshi.conditioners import ConditionAttributes, dropout_all_conditions, TensorCondition
|
| 15 |
+
from moshi.models import loaders
|
| 16 |
+
from moshi.models.lm import _LMGenState, LMGen
|
| 17 |
+
from moshi.models.tts import TTSModel, Entry, State, StateMachine, DEFAULT_DSM_TTS_REPO
|
| 18 |
+
from moshi.modules.transformer import StreamingMultiheadAttention
|
| 19 |
+
from pydantic import BaseModel
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class MaskFlags(Enum):
|
| 23 |
+
# Output PCM is ready
|
| 24 |
+
HAS_PCM = 1
|
| 25 |
+
# Generation is done, no need to step again.
|
| 26 |
+
IS_EOS = 2
|
| 27 |
+
# One word was consumed in the text stream.
|
| 28 |
+
WORD_FINISHED = 4
|
| 29 |
+
# One AR step was performed.
|
| 30 |
+
AR_STEP = 8
|
| 31 |
+
# AR step was skipped because the client is not sending words fast enough.
|
| 32 |
+
MISSING_WORDS = 16
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def flags_out_from_mask_(flags_out: np.ndarray, mask: torch.Tensor, value: int):
|
| 36 |
+
flags_out[mask.numpy()] |= value
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def split_at_specific_separator(text: str, separator: str, index_of_separator: int) -> tuple[str, str]:
|
| 40 |
+
""" kyutai/tts-voices/unmute-prod-website/*.safetensors
|
| 41 |
+
becomes
|
| 42 |
+
('kyutai/tts-voices', 'unmute-prod-website/*.safetensors)
|
| 43 |
+
with index_of_separator=1.
|
| 44 |
+
"""
|
| 45 |
+
if text.count(separator) <= index_of_separator:
|
| 46 |
+
raise ValueError(f"Separator '{separator}' not found {index_of_separator + 1} times in `{text}`.")
|
| 47 |
+
parts = text.split(separator, index_of_separator + 1)
|
| 48 |
+
return separator.join(parts[:-1]), parts[-1]
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
class Config(BaseModel):
|
| 52 |
+
log_folder: Path = Path.home() / 'tmp/tts-service'
|
| 53 |
+
hf_repo: str = DEFAULT_DSM_TTS_REPO
|
| 54 |
+
mimi_weight: Path | None = None
|
| 55 |
+
moshi_weight: Path | None = None
|
| 56 |
+
config_path: Path | None = None
|
| 57 |
+
tokenizer: Path | None = None
|
| 58 |
+
device: str = 'cuda'
|
| 59 |
+
|
| 60 |
+
n_q: int = 4
|
| 61 |
+
# This can have multiple formats:
|
| 62 |
+
# - A path to a folder with voices, e.g. `models/tts`
|
| 63 |
+
# - A huggingface snapshot, e.g. `hf-snapshot://kyutai/tts-voices`
|
| 64 |
+
# - A huggingface snapshot with a pattern,
|
| 65 |
+
# e.g. `hf-snapshot://kyutai/tts-voices/unmute-prod-website/*.safetensors`
|
| 66 |
+
voice_folder: str = str(Path.home() / 'models/tts-voices')
|
| 67 |
+
default_voice: str = "barack_demo.wav"
|
| 68 |
+
|
| 69 |
+
temp: float = 0.6
|
| 70 |
+
cfg_coef: float = 2.
|
| 71 |
+
|
| 72 |
+
max_padding: int = 8
|
| 73 |
+
initial_padding: int = 2
|
| 74 |
+
final_padding: int = 4
|
| 75 |
+
padding_between: int = 1
|
| 76 |
+
|
| 77 |
+
interleaved_text_only: int = 2
|
| 78 |
+
debug: bool = False
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def init(batch_size: int, config_override: dict) -> 'TTSService':
|
| 82 |
+
config = Config(**config_override)
|
| 83 |
+
config.log_folder.mkdir(parents=True, exist_ok=True)
|
| 84 |
+
print("ADJUSTED CODE")
|
| 85 |
+
print("retrieving checkpoint")
|
| 86 |
+
checkpoint_info = loaders.CheckpointInfo.from_hf_repo(
|
| 87 |
+
config.hf_repo, moshi_weights=config.moshi_weight, mimi_weights=config.mimi_weight,
|
| 88 |
+
config_path=config.config_path, tokenizer=config.tokenizer)
|
| 89 |
+
|
| 90 |
+
cfg_condition = None
|
| 91 |
+
tts_model = TTSModel.from_checkpoint_info(
|
| 92 |
+
checkpoint_info, n_q=config.n_q, temp=config.temp, cfg_coef=config.cfg_coef,
|
| 93 |
+
max_padding=config.max_padding, initial_padding=config.initial_padding, final_padding=config.final_padding,
|
| 94 |
+
device=config.device, dtype=torch.float16)
|
| 95 |
+
if tts_model.valid_cfg_conditionings:
|
| 96 |
+
# Model was trained with CFG distillation.
|
| 97 |
+
cfg_condition = tts_model.cfg_coef
|
| 98 |
+
tts_model.cfg_coef = 1.
|
| 99 |
+
cfg_is_no_text = False
|
| 100 |
+
else:
|
| 101 |
+
cfg_is_no_text = True
|
| 102 |
+
|
| 103 |
+
voice_suffix = tts_model.voice_suffix
|
| 104 |
+
print(f"loading voices from {config.voice_folder}, with suffix {voice_suffix}.")
|
| 105 |
+
all_attributes = {}
|
| 106 |
+
voice_folder = config.voice_folder
|
| 107 |
+
if voice_folder.startswith("hf-snapshot://"):
|
| 108 |
+
voice_folder = voice_folder.removeprefix("hf-snapshot://")
|
| 109 |
+
# We detect if there is a pattern in the voice folder.
|
| 110 |
+
if voice_folder.count("/") > 1:
|
| 111 |
+
voice_folder, pattern = split_at_specific_separator(voice_folder, '/', 1)
|
| 112 |
+
else:
|
| 113 |
+
pattern = None
|
| 114 |
+
print(f"retrieving voices from {voice_folder}")
|
| 115 |
+
voice_folder = huggingface_hub.snapshot_download(voice_folder, allow_patterns=pattern)
|
| 116 |
+
voice_folder = Path(voice_folder)
|
| 117 |
+
|
| 118 |
+
for file in voice_folder.glob(f'**/*{voice_suffix}'):
|
| 119 |
+
relative = file.relative_to(voice_folder)
|
| 120 |
+
name = str(relative.with_name(relative.name.removesuffix(voice_suffix)))
|
| 121 |
+
try:
|
| 122 |
+
attributes = tts_model.make_condition_attributes([file, file], cfg_coef=cfg_condition)
|
| 123 |
+
except Exception:
|
| 124 |
+
print(f"[WARNING] failed to load voice {name}")
|
| 125 |
+
else:
|
| 126 |
+
all_attributes[name] = attributes
|
| 127 |
+
|
| 128 |
+
if not all_attributes:
|
| 129 |
+
raise RuntimeError(
|
| 130 |
+
"No voices found, please check your voice folder. "
|
| 131 |
+
f"Searched for files matching {voice_folder}/**/*{voice_suffix}"
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
if config.default_voice not in all_attributes:
|
| 135 |
+
raise RuntimeError(
|
| 136 |
+
f"Default voice {config.default_voice}, please check your voice folder. "
|
| 137 |
+
f"Expected {voice_folder}/{config.default_voice}{voice_suffix} to exist"
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
service = TTSService(
|
| 141 |
+
batch_size=batch_size, default_attribute_name=config.default_voice,
|
| 142 |
+
all_attributes=all_attributes,
|
| 143 |
+
tts_model=tts_model,
|
| 144 |
+
cfg_condition=cfg_condition,
|
| 145 |
+
cfg_is_no_text=cfg_is_no_text,
|
| 146 |
+
padding_between=config.padding_between,
|
| 147 |
+
debug=config.debug,
|
| 148 |
+
interleaved_text_only=config.interleaved_text_only)
|
| 149 |
+
|
| 150 |
+
return service
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
@dataclass
|
| 154 |
+
class ClientState:
|
| 155 |
+
is_complete: bool = False
|
| 156 |
+
state: State | None = None
|
| 157 |
+
offset: int = 0
|
| 158 |
+
|
| 159 |
+
def reset(self, state_machine: StateMachine) -> None:
|
| 160 |
+
self.is_complete = False
|
| 161 |
+
self.offset = 0
|
| 162 |
+
self.state = state_machine.new_state([])
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
@dataclass
|
| 166 |
+
class TTSService:
|
| 167 |
+
batch_size: int
|
| 168 |
+
default_attribute_name: str
|
| 169 |
+
all_attributes: dict[str, ConditionAttributes]
|
| 170 |
+
|
| 171 |
+
tts_model: TTSModel
|
| 172 |
+
|
| 173 |
+
cfg_is_no_text: bool = True
|
| 174 |
+
cfg_condition: float | None = None
|
| 175 |
+
padding_between: int = 1
|
| 176 |
+
n_q: int = 4
|
| 177 |
+
debug: bool = False
|
| 178 |
+
interleaved_text_only: int = 0
|
| 179 |
+
|
| 180 |
+
flags_out: np.ndarray | None = None
|
| 181 |
+
clients: list[ClientState] = field(default_factory=list)
|
| 182 |
+
cross_attention_cache: dict[str, torch.Tensor] = field(default_factory=dict)
|
| 183 |
+
cross_attentions: list[StreamingMultiheadAttention] = field(default_factory=list)
|
| 184 |
+
|
| 185 |
+
def __post_init__(self):
|
| 186 |
+
print(self.n_q)
|
| 187 |
+
print("NQ AMOUNT")
|
| 188 |
+
|
| 189 |
+
lm = self.tts_model.lm
|
| 190 |
+
tts_model = self.tts_model
|
| 191 |
+
mimi = self.tts_model.mimi
|
| 192 |
+
machine = self.tts_model.machine
|
| 193 |
+
|
| 194 |
+
self.device = lm.device
|
| 195 |
+
self.dtype = lm.dtype
|
| 196 |
+
self.remaining_text_only = self.interleaved_text_only
|
| 197 |
+
|
| 198 |
+
for _ in range(self.batch_size):
|
| 199 |
+
client = ClientState()
|
| 200 |
+
self.clients.append(client)
|
| 201 |
+
|
| 202 |
+
print("Filling cross attention cache.")
|
| 203 |
+
for name, attributes in self.all_attributes.items():
|
| 204 |
+
self.cross_attention_cache[name] = self._get_cross_attention_source([attributes])
|
| 205 |
+
|
| 206 |
+
assert lm.condition_provider is not None
|
| 207 |
+
|
| 208 |
+
cas = [self.all_attributes[self.default_attribute_name]] * self.batch_size
|
| 209 |
+
if self.tts_model.cfg_coef != 1.0:
|
| 210 |
+
nulled = make_null(cas)
|
| 211 |
+
cas = cas + nulled
|
| 212 |
+
prepared = lm.condition_provider.prepare(cas)
|
| 213 |
+
condition_tensors = lm.condition_provider(prepared)
|
| 214 |
+
|
| 215 |
+
for module in lm.modules():
|
| 216 |
+
if isinstance(module, StreamingMultiheadAttention) and module.cross_attention:
|
| 217 |
+
self.cross_attentions.append(module)
|
| 218 |
+
|
| 219 |
+
self.lm_gen = LMGen(
|
| 220 |
+
lm, temp=tts_model.temp, temp_text=tts_model.temp, cfg_coef=tts_model.cfg_coef,
|
| 221 |
+
condition_tensors=condition_tensors, on_text_hook=self._on_text_hook,
|
| 222 |
+
on_audio_hook=self._on_audio_hook, cfg_is_no_text=self.cfg_is_no_text,
|
| 223 |
+
support_out_of_sync=True)
|
| 224 |
+
self.lm_gen.streaming_forever(self.batch_size)
|
| 225 |
+
mimi.streaming_forever(self.batch_size)
|
| 226 |
+
|
| 227 |
+
missing = lm.n_q - lm.dep_q
|
| 228 |
+
self.input_tokens = torch.full(
|
| 229 |
+
(self.batch_size, missing, 1), machine.token_ids.zero,
|
| 230 |
+
dtype=torch.long, device=self.device)
|
| 231 |
+
self.no_depformer_tokens = torch.full(
|
| 232 |
+
(self.batch_size, lm.dep_q, 1), machine.token_ids.zero,
|
| 233 |
+
dtype=torch.long, device=self.device)
|
| 234 |
+
self.last_actives: list[bool] = [False] * self.batch_size
|
| 235 |
+
print("warming up.")
|
| 236 |
+
for _ in range(3):
|
| 237 |
+
mimi.set_exec_mask(torch.ones(self.batch_size, dtype=torch.bool))
|
| 238 |
+
self.lm_gen.set_exec_mask(torch.ones(self.batch_size, dtype=torch.bool))
|
| 239 |
+
frame = self.lm_gen.step(self.input_tokens)
|
| 240 |
+
assert frame is not None
|
| 241 |
+
mimi.decode(frame[:, 1:].clamp(min=0))
|
| 242 |
+
print("ready to roll.")
|
| 243 |
+
|
| 244 |
+
def _get_cross_attention_source(self, all_attributes: list[ConditionAttributes]) -> torch.Tensor:
|
| 245 |
+
lm = self.tts_model.lm
|
| 246 |
+
assert lm.condition_provider is not None
|
| 247 |
+
assert lm.fuser is not None
|
| 248 |
+
prepared = lm.condition_provider.prepare(all_attributes)
|
| 249 |
+
condition_tensors = lm.condition_provider(prepared)
|
| 250 |
+
cross = lm.fuser.get_cross(condition_tensors)
|
| 251 |
+
assert cross is not None
|
| 252 |
+
return cross.to(device=self.device, dtype=self.dtype)
|
| 253 |
+
|
| 254 |
+
@property
|
| 255 |
+
def _lm_gen_state(self) -> _LMGenState:
|
| 256 |
+
assert self.lm_gen._streaming_state is not None
|
| 257 |
+
return self.lm_gen._streaming_state
|
| 258 |
+
|
| 259 |
+
def _on_audio_hook(self, audio_tokens: torch.Tensor) -> None:
|
| 260 |
+
delays = self.lm_gen.delays_cuda[1: 1 + self.tts_model.lm.dep_q]
|
| 261 |
+
mask = self._lm_gen_state.offsets[:, None] < delays + self.tts_model.delay_steps
|
| 262 |
+
audio_tokens.masked_fill_(mask, self.tts_model.machine.token_ids.zero)
|
| 263 |
+
|
| 264 |
+
def _on_text_hook(self, text_tokens) -> None:
|
| 265 |
+
tokens = text_tokens.tolist()
|
| 266 |
+
out_tokens = []
|
| 267 |
+
for b, (token, client) in enumerate(zip(tokens, self.clients)):
|
| 268 |
+
if not self.last_actives[b]:
|
| 269 |
+
out_tokens.append(token)
|
| 270 |
+
continue
|
| 271 |
+
assert client.state is not None
|
| 272 |
+
out_token, consumed_new_word = self.tts_model.machine.process(client.offset, client.state, token)
|
| 273 |
+
|
| 274 |
+
if self.flags_out is not None and consumed_new_word:
|
| 275 |
+
self.flags_out[b] |= MaskFlags.WORD_FINISHED.value
|
| 276 |
+
out_tokens.append(out_token)
|
| 277 |
+
text_tokens[:] = torch.tensor(out_tokens, dtype=torch.long, device=text_tokens.device)
|
| 278 |
+
|
| 279 |
+
def _print(self, *args, **kwargs):
|
| 280 |
+
if self.debug:
|
| 281 |
+
print(*args, **kwargs)
|
| 282 |
+
|
| 283 |
+
@torch.no_grad()
|
| 284 |
+
def step(self, updates: list[tuple[int, list[int], np.ndarray | str | None]], pcm_out: np.ndarray,
|
| 285 |
+
flags_out: np.ndarray, code_out: np.ndarray) -> None:
|
| 286 |
+
mimi = self.tts_model.mimi
|
| 287 |
+
machine = self.tts_model.machine
|
| 288 |
+
delay_steps = self.tts_model.delay_steps
|
| 289 |
+
|
| 290 |
+
self.flags_out = flags_out
|
| 291 |
+
flags_out[:] = 0
|
| 292 |
+
|
| 293 |
+
reset_mask = torch.zeros(self.batch_size, dtype=torch.bool)
|
| 294 |
+
# List of pre computed cross attention values.
|
| 295 |
+
new_cross_sources: list[torch.Tensor] = []
|
| 296 |
+
new_cross_indexes: list[int] = []
|
| 297 |
+
# List of new dynamic conditioning that we need to compute.
|
| 298 |
+
new_voice_indexes: list[int] = []
|
| 299 |
+
new_voice_sources: list[torch.Tensor] = []
|
| 300 |
+
for b, new_entry, voice in updates:
|
| 301 |
+
client = self.clients[b]
|
| 302 |
+
if not new_entry:
|
| 303 |
+
self._print(f"[{b}] NO TOKENS REALLY LAURENT.")
|
| 304 |
+
if new_entry[0] == -1:
|
| 305 |
+
client.reset(machine)
|
| 306 |
+
reset_mask[b] = True
|
| 307 |
+
new_entry = new_entry[1:]
|
| 308 |
+
if isinstance(voice, np.ndarray):
|
| 309 |
+
new_voice_indexes.append(b)
|
| 310 |
+
new_voice_sources.append(torch.from_numpy(voice))
|
| 311 |
+
else:
|
| 312 |
+
cross_source = self.cross_attention_cache.get(voice or '', None)
|
| 313 |
+
if cross_source is None:
|
| 314 |
+
cross_source = self.cross_attention_cache[self.default_attribute_name]
|
| 315 |
+
new_cross_sources.append(cross_source)
|
| 316 |
+
new_cross_indexes.append(b)
|
| 317 |
+
self._print(f"[{b}] Reset, voice is {voice}.")
|
| 318 |
+
if client.state is None:
|
| 319 |
+
self._print(f"[{b}] Trying to push {new_entry}, but not assigned.")
|
| 320 |
+
elif new_entry == [-2]:
|
| 321 |
+
self._print(f"[{b}] Done.")
|
| 322 |
+
client.is_complete = True
|
| 323 |
+
else:
|
| 324 |
+
self._print(f"[{b}] Pushing {new_entry}.")
|
| 325 |
+
padding = 0
|
| 326 |
+
if self.padding_between > 0:
|
| 327 |
+
padding = max(0, self.padding_between + len(new_entry) - 1)
|
| 328 |
+
client.state.entries.append(Entry(new_entry, '', padding=padding))
|
| 329 |
+
|
| 330 |
+
actives = []
|
| 331 |
+
mimi_actives = []
|
| 332 |
+
in_text_onlys = []
|
| 333 |
+
for b, client in enumerate(self.clients):
|
| 334 |
+
if client.state is None:
|
| 335 |
+
# client is not currently assigned.
|
| 336 |
+
active = False
|
| 337 |
+
elif client.is_complete:
|
| 338 |
+
# We got all the words from the client and are wrapping up.
|
| 339 |
+
active = True
|
| 340 |
+
elif client.state.forced_padding > 0:
|
| 341 |
+
# We are sure we won't try to consume a word at this point.
|
| 342 |
+
active = True
|
| 343 |
+
elif len(client.state.entries) > self.tts_model.machine.second_stream_ahead:
|
| 344 |
+
# We have some words ready to be consumed.
|
| 345 |
+
active = True
|
| 346 |
+
else:
|
| 347 |
+
flags_out[b] |= MaskFlags.MISSING_WORDS.value
|
| 348 |
+
active = False
|
| 349 |
+
actives.append(active)
|
| 350 |
+
|
| 351 |
+
real_offset = client.offset - self.lm_gen.max_delay
|
| 352 |
+
|
| 353 |
+
mimi_active = active and (real_offset >= delay_steps)
|
| 354 |
+
mimi_actives.append(mimi_active)
|
| 355 |
+
|
| 356 |
+
in_text_only = active and (client.offset < delay_steps)
|
| 357 |
+
in_text_onlys.append(in_text_only)
|
| 358 |
+
|
| 359 |
+
in_text_only_mask = torch.tensor(in_text_onlys, dtype=torch.bool)
|
| 360 |
+
run_in_text_only = self.remaining_text_only > 0 and in_text_only_mask.any()
|
| 361 |
+
|
| 362 |
+
if run_in_text_only:
|
| 363 |
+
self.remaining_text_only -= 1
|
| 364 |
+
mimi_exec_mask = torch.zeros(self.batch_size, dtype=torch.bool)
|
| 365 |
+
exec_mask = in_text_only_mask
|
| 366 |
+
actives = in_text_onlys
|
| 367 |
+
else:
|
| 368 |
+
self.remaining_text_only = self.interleaved_text_only
|
| 369 |
+
exec_mask = torch.tensor(actives, dtype=torch.bool)
|
| 370 |
+
mimi_exec_mask = torch.tensor(mimi_actives, dtype=torch.bool)
|
| 371 |
+
del mimi_actives
|
| 372 |
+
self.last_actives = actives
|
| 373 |
+
|
| 374 |
+
flags_out_from_mask_(flags_out, exec_mask, MaskFlags.AR_STEP.value)
|
| 375 |
+
flags_out_from_mask_(flags_out, mimi_exec_mask, MaskFlags.HAS_PCM.value)
|
| 376 |
+
|
| 377 |
+
# We check on exec_mask whether we actually need to run anything, before we move it to CUDA.
|
| 378 |
+
# However, we still need to perform the reset and update of cross attention for models
|
| 379 |
+
# with a text lookahead stream.
|
| 380 |
+
skip_exec = not exec_mask.any()
|
| 381 |
+
|
| 382 |
+
exec_mask = exec_mask.to(self.device)
|
| 383 |
+
mimi_exec_mask = mimi_exec_mask.to(self.device)
|
| 384 |
+
need_reset = reset_mask.any()
|
| 385 |
+
reset_mask = reset_mask.to(self.device)
|
| 386 |
+
|
| 387 |
+
if new_voice_sources:
|
| 388 |
+
all_attributes = [make_condition_attributes([voice_source], cfg_condition=self.cfg_condition)
|
| 389 |
+
for voice_source in new_voice_sources]
|
| 390 |
+
new_cross_sources += self._get_cross_attention_source(all_attributes).split(1)
|
| 391 |
+
new_cross_indexes += new_voice_indexes
|
| 392 |
+
if new_cross_sources:
|
| 393 |
+
cross_source = torch.cat(new_cross_sources)
|
| 394 |
+
cross_indexes = torch.tensor(new_cross_indexes, dtype=torch.long, device=self.device)
|
| 395 |
+
for attention in self.cross_attentions:
|
| 396 |
+
k, v = attention._compute_cross_attention(cross_source, cross_source)
|
| 397 |
+
state = attention._streaming_state
|
| 398 |
+
assert state is not None
|
| 399 |
+
assert state.k_cross is not None
|
| 400 |
+
assert state.v_cross is not None
|
| 401 |
+
state.k_cross.index_copy_(0, cross_indexes, k)
|
| 402 |
+
state.v_cross.index_copy_(0, cross_indexes, v)
|
| 403 |
+
|
| 404 |
+
if need_reset:
|
| 405 |
+
self.lm_gen.reset_streaming(reset_mask=reset_mask)
|
| 406 |
+
mimi.reset_streaming(reset_mask=reset_mask)
|
| 407 |
+
|
| 408 |
+
if skip_exec:
|
| 409 |
+
return
|
| 410 |
+
|
| 411 |
+
self.lm_gen.set_exec_mask(exec_mask)
|
| 412 |
+
mimi.set_exec_mask(mimi_exec_mask)
|
| 413 |
+
|
| 414 |
+
depformer_replace_tokens = self.no_depformer_tokens if run_in_text_only else None
|
| 415 |
+
frame = self.lm_gen.step(self.input_tokens, depformer_replace_tokens=depformer_replace_tokens)
|
| 416 |
+
assert frame is not None
|
| 417 |
+
audio_frame = frame[:, 1:]
|
| 418 |
+
audio_frame.clamp_(min=0)
|
| 419 |
+
|
| 420 |
+
if run_in_text_only:
|
| 421 |
+
pcm = None
|
| 422 |
+
else:
|
| 423 |
+
pcm = mimi.decode(audio_frame)
|
| 424 |
+
pcm.clamp_(-0.99, 0.99)
|
| 425 |
+
|
| 426 |
+
for b, client in enumerate(self.clients):
|
| 427 |
+
if actives[b]:
|
| 428 |
+
assert client.state is not None
|
| 429 |
+
client.offset += 1
|
| 430 |
+
self._print(f"[{b}] Offset {client.offset: 3d}, pendings={len(client.state.entries): 3d}.")
|
| 431 |
+
if client.is_complete and client.state.end_step is not None:
|
| 432 |
+
# We were waiting for the end of the generation.
|
| 433 |
+
real_end = (
|
| 434 |
+
client.state.end_step + delay_steps + self.tts_model.final_padding + self.lm_gen.max_delay)
|
| 435 |
+
if client.offset >= real_end:
|
| 436 |
+
self._print(f"[{b}] Done.")
|
| 437 |
+
client.reset(machine)
|
| 438 |
+
flags_out[b] |= MaskFlags.IS_EOS.value
|
| 439 |
+
if pcm is not None:
|
| 440 |
+
pcm_out[:] = pcm[:, 0].cpu().numpy()
|
| 441 |
+
code_out[:, :frame.shape[1]] = frame[:, :, 0].int().cpu().numpy()
|
| 442 |
+
code_out[:, frame.shape[1]:] = 0
|
| 443 |
+
self.flags_out = None
|
| 444 |
+
|
| 445 |
+
|
| 446 |
+
class Profiler:
|
| 447 |
+
"""Context manager wrapper for xformers profiler.
|
| 448 |
+
"""
|
| 449 |
+
def __init__(self, enabled: bool = False):
|
| 450 |
+
self.profiler: tp.Optional[tp.Any] = None
|
| 451 |
+
if enabled:
|
| 452 |
+
from xformers.profiler import profile
|
| 453 |
+
from xformers.profiler.api import PyTorchProfiler
|
| 454 |
+
output_dir = './profiler_data'
|
| 455 |
+
schedule = (
|
| 456 |
+
(PyTorchProfiler, 6, 12),
|
| 457 |
+
)
|
| 458 |
+
self.profiler = profile(output_dir=output_dir, schedule=schedule)
|
| 459 |
+
|
| 460 |
+
def step(self):
|
| 461 |
+
if self.profiler is not None:
|
| 462 |
+
self.profiler.step() # type: ignore
|
| 463 |
+
|
| 464 |
+
def __enter__(self):
|
| 465 |
+
if self.profiler is not None:
|
| 466 |
+
return self.profiler.__enter__() # type: ignore
|
| 467 |
+
|
| 468 |
+
def __exit__(self, exc_type, exc_value, exc_tb):
|
| 469 |
+
if self.profiler is not None:
|
| 470 |
+
return self.profiler.__exit__(exc_type, exc_value, exc_tb) # type: ignore
|
| 471 |
+
|
| 472 |
+
|
| 473 |
+
def make_condition_attributes(voices: list[Path | torch.Tensor],
|
| 474 |
+
max_speakers: int = 5,
|
| 475 |
+
cfg_condition: float | None = None) -> ConditionAttributes:
|
| 476 |
+
assert voices
|
| 477 |
+
voice_tensor = None
|
| 478 |
+
mask = None
|
| 479 |
+
for idx in range(5):
|
| 480 |
+
if idx < len(voices):
|
| 481 |
+
voice = voices[idx]
|
| 482 |
+
if isinstance(voice, Path):
|
| 483 |
+
emb = load_file(voice, device='cuda')['speaker_wavs']
|
| 484 |
+
else:
|
| 485 |
+
emb = voice
|
| 486 |
+
assert emb.dim() == 3
|
| 487 |
+
if voice_tensor is None:
|
| 488 |
+
voice_tensor = torch.zeros(1, max_speakers, emb.shape[2], emb.shape[1], device='cuda')
|
| 489 |
+
if mask is None:
|
| 490 |
+
mask = torch.zeros(1, max_speakers, emb.shape[2], dtype=torch.bool, device='cuda')
|
| 491 |
+
voice_tensor[:, idx, :, :] = emb.transpose(1, 2)
|
| 492 |
+
mask[:, idx, :] = True
|
| 493 |
+
assert voice_tensor is not None
|
| 494 |
+
assert mask is not None
|
| 495 |
+
voice_tensor = voice_tensor.view(1, -1, voice_tensor.shape[-1])
|
| 496 |
+
mask = mask.view(1, -1)
|
| 497 |
+
tensors = {
|
| 498 |
+
'speaker_wavs': TensorCondition(voice_tensor, mask)
|
| 499 |
+
}
|
| 500 |
+
text: dict[str, str | None] = {
|
| 501 |
+
'control': 'ok',
|
| 502 |
+
}
|
| 503 |
+
if cfg_condition is None:
|
| 504 |
+
text['cfg'] = None
|
| 505 |
+
else:
|
| 506 |
+
text['cfg'] = format(cfg_condition, '.1f')
|
| 507 |
+
return ConditionAttributes(text=dict(text), tensor=tensors)
|
| 508 |
+
|
| 509 |
+
|
| 510 |
+
def make_null(all_attributes: tp.Sequence[ConditionAttributes]) -> list[ConditionAttributes]:
|
| 511 |
+
return dropout_all_conditions(all_attributes)
|
| 512 |
+
|