565faacb7b
- Migrate backend from REST API to Gradio @gradio/client for generation - Fix Gradio parameter alignment (positions 36-49) for reference/cover audio - Add LoRA training pipeline with dataset upload, preprocessing, and export - Add News page with dismiss/restore and GitHub star button - Add localization info icon in Settings language section - Fix upload audio URL prefix, add missing MIME types - Add training API routes and Python preprocess script - Update i18n with news keys for all languages
141 lines
5.1 KiB
Python
141 lines
5.1 KiB
Python
"""
|
|
Standalone dataset preprocessor for ACE-Step LoRA training.
|
|
|
|
Converts labeled audio samples from a dataset JSON into pre-computed
|
|
tensor files (.pt) suitable for training. This script loads the VAE and
|
|
text encoder independently, so it does NOT require the Gradio app to be
|
|
running.
|
|
|
|
Usage:
|
|
python preprocess_dataset.py --dataset /path/to/dataset.json --output /path/to/tensors [--json]
|
|
|
|
The --json flag makes the script output a final JSON summary line to stdout.
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Preprocess dataset to tensors for LoRA training")
|
|
parser.add_argument("--dataset", required=True, help="Path to dataset JSON file")
|
|
parser.add_argument("--output", required=True, help="Output directory for tensor files")
|
|
parser.add_argument("--max-duration", type=float, default=240.0, help="Max audio duration in seconds")
|
|
parser.add_argument("--json", action="store_true", help="Output JSON summary")
|
|
args = parser.parse_args()
|
|
|
|
if not os.path.exists(args.dataset):
|
|
print(f"Error: Dataset file not found: {args.dataset}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
# Add ACE-Step root to path for imports
|
|
ace_step_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
# Walk up to find ACE-Step-1.5 directory
|
|
for candidate in [
|
|
os.path.join(ace_step_root, "ACE-Step-1.5"),
|
|
os.path.join(os.path.dirname(ace_step_root), "ACE-Step-1.5"),
|
|
os.getcwd(),
|
|
]:
|
|
if os.path.isdir(candidate) and os.path.isdir(os.path.join(candidate, "acestep")):
|
|
ace_step_root = candidate
|
|
break
|
|
|
|
if ace_step_root not in sys.path:
|
|
sys.path.insert(0, ace_step_root)
|
|
|
|
try:
|
|
from acestep.training.dataset_builder import DatasetBuilder
|
|
except ImportError as e:
|
|
print(f"Error: Could not import ACE-Step modules: {e}", file=sys.stderr)
|
|
print("Make sure this script is run from the ACE-Step-1.5 directory or with the correct Python environment.", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
# Load dataset JSON
|
|
print(f"Loading dataset: {args.dataset}")
|
|
with open(args.dataset, "r") as f:
|
|
dataset_data = json.load(f)
|
|
|
|
# Reconstruct DatasetBuilder from JSON
|
|
builder = DatasetBuilder()
|
|
builder.load_from_dict(dataset_data)
|
|
|
|
labeled_count = sum(1 for s in builder.samples if s.labeled)
|
|
total_count = len(builder.samples)
|
|
print(f"Dataset loaded: {total_count} samples, {labeled_count} labeled")
|
|
|
|
if labeled_count == 0:
|
|
msg = "No labeled samples found. Please label samples before preprocessing."
|
|
print(f"Warning: {msg}", file=sys.stderr)
|
|
if args.json:
|
|
print(json.dumps({"status": "error", "message": msg, "labeled": 0, "total": total_count}))
|
|
sys.exit(1)
|
|
|
|
# Load models for preprocessing
|
|
print("Loading models for preprocessing (this may take a moment)...")
|
|
try:
|
|
from acestep.pipeline_ace_step import ACEStepPipeline
|
|
|
|
checkpoint_dir = os.path.join(ace_step_root, "checkpoints")
|
|
if not os.path.isdir(checkpoint_dir):
|
|
checkpoint_dir = os.path.join(ace_step_root, "checkpoints", "ACE-Step-v1.5")
|
|
|
|
pipe = ACEStepPipeline(checkpoint_dir=checkpoint_dir)
|
|
pipe.load_checkpoint()
|
|
|
|
# Create a minimal dit_handler-like object for preprocess_to_tensors
|
|
class DitHandlerProxy:
|
|
def __init__(self, pipeline):
|
|
self.model = pipeline.dit
|
|
self.vae = pipeline.vae
|
|
self.text_encoder = pipeline.text_encoder
|
|
self.text_tokenizer = pipeline.text_tokenizer
|
|
self.silence_latent = getattr(pipeline, "silence_latent", None)
|
|
self.device = pipeline.device
|
|
self.dtype = pipeline.dtype
|
|
|
|
handler = DitHandlerProxy(pipe)
|
|
except Exception as e:
|
|
# If pipeline loading fails, try a simpler approach
|
|
print(f"Warning: Could not load full pipeline: {e}", file=sys.stderr)
|
|
print("Preprocessing requires model access. Please use the Gradio UI for preprocessing.", file=sys.stderr)
|
|
if args.json:
|
|
print(json.dumps({
|
|
"status": "error",
|
|
"message": f"Model loading failed: {str(e)}. Use Gradio UI preprocess instead.",
|
|
"labeled": labeled_count,
|
|
"total": total_count,
|
|
}))
|
|
sys.exit(1)
|
|
|
|
# Run preprocessing
|
|
os.makedirs(args.output, exist_ok=True)
|
|
print(f"Preprocessing to: {args.output}")
|
|
|
|
def progress_cb(msg):
|
|
print(f" {msg}")
|
|
|
|
output_paths, status = builder.preprocess_to_tensors(
|
|
dit_handler=handler,
|
|
output_dir=args.output,
|
|
max_duration=args.max_duration,
|
|
progress_callback=progress_cb,
|
|
)
|
|
|
|
print(f"Done: {status}")
|
|
print(f"Output files: {len(output_paths)}")
|
|
|
|
if args.json:
|
|
print(json.dumps({
|
|
"status": "complete",
|
|
"message": status,
|
|
"output_files": len(output_paths),
|
|
"output_dir": args.output,
|
|
"labeled": labeled_count,
|
|
"total": total_count,
|
|
}))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|