Spaces:
Sleeping
Sleeping
Convert to CPU-compatible Space: remove ZeroGPU dependency
Browse files- Remove spaces import and @spaces.GPU decorator
- Auto-detect device (CUDA > MPS > CPU) instead of hardcoded "cuda"
- Use float32 dtype on CPU, bfloat16 on CUDA/MPS
- Pass DEVICE variable to tts_model.inference() instead of inline check
- Remove spaces package from requirements.txt
- Update README title/description for CPU variant
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- README.md +2 -2
- app.py +21 -21
- requirements.txt +0 -1
README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
---
|
| 2 |
-
title: MOSS-TTS-Nano
|
| 3 |
emoji: ποΈ
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: purple
|
|
@@ -7,7 +7,7 @@ sdk: gradio
|
|
| 7 |
sdk_version: 6.5.1
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
-
short_description: 0.1B multilingual TTS with voice cloning
|
| 11 |
---
|
| 12 |
|
| 13 |
# MOSS-TTS-Nano
|
|
|
|
| 1 |
---
|
| 2 |
+
title: MOSS-TTS-Nano CPU
|
| 3 |
emoji: ποΈ
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: purple
|
|
|
|
| 7 |
sdk_version: 6.5.1
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
+
short_description: 0.1B multilingual TTS with voice cloning (CPU)
|
| 11 |
---
|
| 12 |
|
| 13 |
# MOSS-TTS-Nano
|
app.py
CHANGED
|
@@ -8,20 +8,22 @@ from pathlib import Path
|
|
| 8 |
import gradio as gr
|
| 9 |
import torch
|
| 10 |
|
| 11 |
-
try:
|
| 12 |
-
import spaces
|
| 13 |
-
except ImportError:
|
| 14 |
-
class _SpacesFallback:
|
| 15 |
-
@staticmethod
|
| 16 |
-
def GPU(*args, **kwargs):
|
| 17 |
-
def decorator(func):
|
| 18 |
-
return func
|
| 19 |
-
return decorator
|
| 20 |
-
spaces = _SpacesFallback()
|
| 21 |
-
|
| 22 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s")
|
| 23 |
logger = logging.getLogger(__name__)
|
| 24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
MODEL_ID = "OpenMOSS-Team/MOSS-TTS-Nano-100M"
|
| 26 |
AUDIO_TOKENIZER_ID = "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano"
|
| 27 |
OUTPUT_DIR = Path("/tmp/moss-tts-output")
|
|
@@ -29,18 +31,18 @@ SAMPLE_DIR = Path("/tmp/moss-tts-samples")
|
|
| 29 |
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
| 30 |
SAMPLE_DIR.mkdir(parents=True, exist_ok=True)
|
| 31 |
|
| 32 |
-
# βββ Load models
|
| 33 |
from transformers import AutoModelForCausalLM, AutoModel, AutoTokenizer
|
| 34 |
|
| 35 |
logger.info("Loading TTS model: %s", MODEL_ID)
|
| 36 |
tts_model = AutoModelForCausalLM.from_pretrained(
|
| 37 |
MODEL_ID,
|
| 38 |
trust_remote_code=True,
|
| 39 |
-
|
| 40 |
low_cpu_mem_usage=True,
|
| 41 |
)
|
| 42 |
tts_model.eval()
|
| 43 |
-
tts_model._set_attention_implementation("sdpa")
|
| 44 |
|
| 45 |
logger.info("Loading audio tokenizer: %s", AUDIO_TOKENIZER_ID)
|
| 46 |
audio_tokenizer = AutoModel.from_pretrained(
|
|
@@ -52,9 +54,9 @@ audio_tokenizer.eval()
|
|
| 52 |
logger.info("Loading text tokenizer")
|
| 53 |
text_tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
|
| 54 |
|
| 55 |
-
logger.info("Moving models to
|
| 56 |
-
tts_model.to(
|
| 57 |
-
audio_tokenizer.to(
|
| 58 |
logger.info("All models ready.")
|
| 59 |
|
| 60 |
# βββ Sample audio files βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -108,7 +110,6 @@ LANG_TO_SAMPLE_AUDIO: dict[str, str | None] = {
|
|
| 108 |
|
| 109 |
|
| 110 |
# βββ Inference ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 111 |
-
@spaces.GPU(duration=120)
|
| 112 |
def generate_speech(
|
| 113 |
text: str,
|
| 114 |
reference_audio: str | None,
|
|
@@ -124,10 +125,9 @@ def generate_speech(
|
|
| 124 |
seed_int = int(seed) if seed else 0
|
| 125 |
if seed_int != 0:
|
| 126 |
torch.manual_seed(seed_int)
|
| 127 |
-
if
|
| 128 |
torch.cuda.manual_seed(seed_int)
|
| 129 |
|
| 130 |
-
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 131 |
output_path = str(OUTPUT_DIR / f"output_{os.getpid()}_{abs(hash(text)) % 1_000_000}.wav")
|
| 132 |
|
| 133 |
try:
|
|
@@ -139,7 +139,7 @@ def generate_speech(
|
|
| 139 |
text_tokenizer=text_tokenizer,
|
| 140 |
audio_tokenizer=audio_tokenizer,
|
| 141 |
audio_tokenizer_type="moss-audio-tokenizer-nano",
|
| 142 |
-
device=
|
| 143 |
max_new_frames=int(max_new_frames),
|
| 144 |
do_sample=bool(do_sample),
|
| 145 |
use_kv_cache=True,
|
|
|
|
| 8 |
import gradio as gr
|
| 9 |
import torch
|
| 10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s")
|
| 12 |
logger = logging.getLogger(__name__)
|
| 13 |
|
| 14 |
+
# βββ Device detection βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 15 |
+
def _get_device() -> str:
|
| 16 |
+
if torch.cuda.is_available():
|
| 17 |
+
return "cuda"
|
| 18 |
+
if torch.backends.mps.is_available():
|
| 19 |
+
return "mps"
|
| 20 |
+
return "cpu"
|
| 21 |
+
|
| 22 |
+
DEVICE = _get_device()
|
| 23 |
+
# bfloat16 works on CUDA/MPS; use float32 on CPU for stability
|
| 24 |
+
DTYPE = torch.bfloat16 if DEVICE in ("cuda", "mps") else torch.float32
|
| 25 |
+
logger.info("Running on device: %s, dtype: %s", DEVICE, DTYPE)
|
| 26 |
+
|
| 27 |
MODEL_ID = "OpenMOSS-Team/MOSS-TTS-Nano-100M"
|
| 28 |
AUDIO_TOKENIZER_ID = "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano"
|
| 29 |
OUTPUT_DIR = Path("/tmp/moss-tts-output")
|
|
|
|
| 31 |
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
| 32 |
SAMPLE_DIR.mkdir(parents=True, exist_ok=True)
|
| 33 |
|
| 34 |
+
# βββ Load models ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 35 |
from transformers import AutoModelForCausalLM, AutoModel, AutoTokenizer
|
| 36 |
|
| 37 |
logger.info("Loading TTS model: %s", MODEL_ID)
|
| 38 |
tts_model = AutoModelForCausalLM.from_pretrained(
|
| 39 |
MODEL_ID,
|
| 40 |
trust_remote_code=True,
|
| 41 |
+
torch_dtype=DTYPE,
|
| 42 |
low_cpu_mem_usage=True,
|
| 43 |
)
|
| 44 |
tts_model.eval()
|
| 45 |
+
tts_model._set_attention_implementation("sdpa")
|
| 46 |
|
| 47 |
logger.info("Loading audio tokenizer: %s", AUDIO_TOKENIZER_ID)
|
| 48 |
audio_tokenizer = AutoModel.from_pretrained(
|
|
|
|
| 54 |
logger.info("Loading text tokenizer")
|
| 55 |
text_tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
|
| 56 |
|
| 57 |
+
logger.info("Moving models to %s β¦", DEVICE)
|
| 58 |
+
tts_model.to(DEVICE)
|
| 59 |
+
audio_tokenizer.to(DEVICE)
|
| 60 |
logger.info("All models ready.")
|
| 61 |
|
| 62 |
# βββ Sample audio files βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 110 |
|
| 111 |
|
| 112 |
# βββ Inference ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 113 |
def generate_speech(
|
| 114 |
text: str,
|
| 115 |
reference_audio: str | None,
|
|
|
|
| 125 |
seed_int = int(seed) if seed else 0
|
| 126 |
if seed_int != 0:
|
| 127 |
torch.manual_seed(seed_int)
|
| 128 |
+
if DEVICE == "cuda":
|
| 129 |
torch.cuda.manual_seed(seed_int)
|
| 130 |
|
|
|
|
| 131 |
output_path = str(OUTPUT_DIR / f"output_{os.getpid()}_{abs(hash(text)) % 1_000_000}.wav")
|
| 132 |
|
| 133 |
try:
|
|
|
|
| 139 |
text_tokenizer=text_tokenizer,
|
| 140 |
audio_tokenizer=audio_tokenizer,
|
| 141 |
audio_tokenizer_type="moss-audio-tokenizer-nano",
|
| 142 |
+
device=DEVICE,
|
| 143 |
max_new_frames=int(max_new_frames),
|
| 144 |
do_sample=bool(do_sample),
|
| 145 |
use_kv_cache=True,
|
requirements.txt
CHANGED
|
@@ -5,5 +5,4 @@ safetensors>=0.4.3
|
|
| 5 |
soundfile>=0.13.1
|
| 6 |
torchaudio==2.7.1
|
| 7 |
gradio==6.5.1
|
| 8 |
-
spaces
|
| 9 |
accelerate>=0.30.0
|
|
|
|
| 5 |
soundfile>=0.13.1
|
| 6 |
torchaudio==2.7.1
|
| 7 |
gradio==6.5.1
|
|
|
|
| 8 |
accelerate>=0.30.0
|