Fix RVC HuBERT model loading error Technical details: - Add direct .pt loading attempt before safetensors conversion - Improve error handling for unsupported model formats - Add fallback mechanism for complex fairseq models
152 lines
6.6 KiB
Python
152 lines
6.6 KiB
Python
|
|
import hashlib
|
|
import torch.nn.functional as F
|
|
import librosa
|
|
import torch
|
|
import os
|
|
|
|
from .infer_pack.loaders import HubertModelWithFinalProj
|
|
|
|
def get_hash(model_path):
|
|
try:
|
|
with open(model_path, 'rb') as f:
|
|
f.seek(- 10000 * 1024, 2)
|
|
model_hash = hashlib.md5(f.read()).hexdigest()
|
|
except:
|
|
model_hash = hashlib.md5(open(model_path, 'rb').read()).hexdigest()
|
|
|
|
return model_hash
|
|
|
|
def load_hubert(model_path: str, config):
|
|
try:
|
|
print(f"🔧 Loading HuBERT model: {model_path}")
|
|
if model_path.endswith(".safetensors"):
|
|
try:
|
|
model = HubertModelWithFinalProj.from_safetensors(model_path, device=config.device)
|
|
print(f"✅ HuBERT safetensors model loaded successfully")
|
|
return model
|
|
except Exception as e:
|
|
print(f"❌ HuBERT safetensors loading error: {e}")
|
|
print(f"🔧 Error type: {type(e).__name__}")
|
|
raise
|
|
else:
|
|
# Try loading .pt file directly first (safer approach)
|
|
print(f"🔧 Attempting direct .pt loading: {model_path}")
|
|
try:
|
|
import torch
|
|
checkpoint = torch.load(model_path, map_location='cpu', weights_only=False)
|
|
|
|
# Try to create HuBERT model directly from .pt checkpoint
|
|
# This will work if the checkpoint contains the full model with config
|
|
if hasattr(checkpoint, 'eval'):
|
|
# The checkpoint itself is a model
|
|
model = checkpoint.to(config.device)
|
|
model.eval()
|
|
print(f"✅ Direct .pt model loading successful")
|
|
return model
|
|
|
|
except Exception as direct_error:
|
|
print(f"⚠️ Direct .pt loading failed: {direct_error}")
|
|
print(f"🔄 Falling back to safetensors conversion")
|
|
|
|
# Fallback: Convert .pt file to .safetensors format for compatibility
|
|
print(f"🔄 Converting .pt Hubert model to safetensors format: {model_path}")
|
|
|
|
# Generate safetensors path
|
|
safetensors_path = model_path.replace(".pt", ".safetensors")
|
|
if not safetensors_path.endswith(".safetensors"):
|
|
safetensors_path = model_path + ".safetensors"
|
|
|
|
# If safetensors version doesn't exist, create it
|
|
if not os.path.exists(safetensors_path):
|
|
try:
|
|
import torch
|
|
from safetensors.torch import save_file
|
|
|
|
print(f"🔧 Converting {model_path} to {safetensors_path}")
|
|
|
|
# Try to extract just the model weights, ignoring fairseq objects
|
|
try:
|
|
# First attempt: load with torch pickle_module to handle fairseq objects
|
|
import pickle
|
|
import io
|
|
|
|
with open(model_path, 'rb') as f:
|
|
# Load raw data and try to extract model state_dict only
|
|
checkpoint = torch.load(f, map_location='cpu', weights_only=False)
|
|
|
|
# Extract state_dict from various possible formats
|
|
if hasattr(checkpoint, 'state_dict'):
|
|
state_dict = checkpoint.state_dict()
|
|
elif isinstance(checkpoint, dict):
|
|
if 'model' in checkpoint:
|
|
if hasattr(checkpoint['model'], 'state_dict'):
|
|
state_dict = checkpoint['model'].state_dict()
|
|
else:
|
|
state_dict = checkpoint['model']
|
|
elif 'state_dict' in checkpoint:
|
|
state_dict = checkpoint['state_dict']
|
|
else:
|
|
# Assume the dict itself is the state_dict
|
|
state_dict = {k: v for k, v in checkpoint.items()
|
|
if isinstance(v, torch.Tensor)}
|
|
else:
|
|
raise ValueError("Cannot extract state_dict from checkpoint")
|
|
|
|
except Exception as load_error:
|
|
print(f"⚠️ Standard loading failed: {load_error}")
|
|
# Fallback: try to manually extract tensors
|
|
raise NotImplementedError(f"Cannot convert complex .pt file without fairseq: {model_path}")
|
|
|
|
# Save as safetensors (without config metadata - this is the limitation)
|
|
save_file(state_dict, safetensors_path)
|
|
print(f"✅ Converted to safetensors: {safetensors_path}")
|
|
print(f"⚠️ Note: Config metadata not preserved - may cause loading issues")
|
|
|
|
except Exception as conv_error:
|
|
print(f"❌ Failed to convert model: {conv_error}")
|
|
raise NotImplementedError(f"Cannot load .pt file without fairseq: {model_path}")
|
|
|
|
# Load the safetensors version
|
|
return HubertModelWithFinalProj.from_safetensors(safetensors_path, device=config.device)
|
|
except Exception as e:
|
|
print(e)
|
|
return None
|
|
|
|
def change_rms(data1, sr1, data2, sr2, rate): # 1是输入音频,2是输出音频,rate是2的占比
|
|
# print(data1.max(),data2.max())
|
|
|
|
# Manual RMS calculation to avoid librosa/numba issues in Python 3.13
|
|
def calculate_rms(y, frame_length, hop_length):
|
|
"""Calculate RMS without librosa to avoid numba issues"""
|
|
import numpy as np
|
|
|
|
# Pad the signal
|
|
y = np.asarray(y)
|
|
n_frames = 1 + (len(y) - frame_length) // hop_length
|
|
|
|
# Create frames
|
|
frames = np.lib.stride_tricks.sliding_window_view(
|
|
y, window_shape=frame_length
|
|
)[::hop_length]
|
|
|
|
# Calculate RMS for each frame
|
|
rms = np.sqrt(np.mean(frames**2, axis=1))
|
|
return rms.reshape(1, -1) # Shape: (1, n_frames)
|
|
|
|
rms1 = calculate_rms(data1, sr1 // 2 * 2, sr1 // 2) # 每半秒一个点
|
|
rms2 = calculate_rms(data2, sr2 // 2 * 2, sr2 // 2)
|
|
rms1 = torch.from_numpy(rms1)
|
|
rms1 = F.interpolate(
|
|
rms1.unsqueeze(0), size=data2.shape[0], mode="linear"
|
|
).squeeze()
|
|
rms2 = torch.from_numpy(rms2)
|
|
rms2 = F.interpolate(
|
|
rms2.unsqueeze(0), size=data2.shape[0], mode="linear"
|
|
).squeeze()
|
|
rms2 = torch.max(rms2, torch.zeros_like(rms2) + 1e-6)
|
|
data2 *= (
|
|
torch.pow(rms1, torch.tensor(1 - rate))
|
|
* torch.pow(rms2, torch.tensor(rate - 1))
|
|
).numpy()
|
|
return data2 |