hubert_transformers / convert_rvc_hubert_to_transformers.py
layris's picture
Upload convert_rvc_hubert_to_transformers.py
b3f5409 verified
Raw
History Blame Contribute Delete
4.41 kB
"""Convert RVC WebUI's fairseq HuBERT checkpoint to Transformers format."""
from __future__ import annotations
import argparse
from pathlib import Path
import torch
from transformers import HubertConfig, HubertModel
VOICECHANGER_ROOT = Path(__file__).resolve().parents[1]
def resolve_path(path: str) -> Path:
value = Path(path)
if value.is_absolute():
return value
return VOICECHANGER_ROOT / value
def map_state_dict(fairseq_state: dict[str, torch.Tensor], hf_state: dict[str, torch.Tensor]) -> dict[str, torch.Tensor]:
mapped: dict[str, torch.Tensor] = {}
def put(hf_key: str, fs_key: str) -> None:
value = fairseq_state[fs_key]
if tuple(value.shape) != tuple(hf_state[hf_key].shape):
raise ValueError(
f"Shape mismatch: {fs_key} {tuple(value.shape)} -> "
f"{hf_key} {tuple(hf_state[hf_key].shape)}"
)
mapped[hf_key] = value
if "masked_spec_embed" in hf_state and "mask_emb" in fairseq_state:
put("masked_spec_embed", "mask_emb")
for idx in range(7):
put(f"feature_extractor.conv_layers.{idx}.conv.weight", f"feature_extractor.conv_layers.{idx}.0.weight")
put("feature_extractor.conv_layers.0.layer_norm.weight", "feature_extractor.conv_layers.0.2.weight")
put("feature_extractor.conv_layers.0.layer_norm.bias", "feature_extractor.conv_layers.0.2.bias")
put("feature_projection.layer_norm.weight", "layer_norm.weight")
put("feature_projection.layer_norm.bias", "layer_norm.bias")
put("feature_projection.projection.weight", "post_extract_proj.weight")
put("feature_projection.projection.bias", "post_extract_proj.bias")
put("encoder.pos_conv_embed.conv.bias", "encoder.pos_conv.0.bias")
put("encoder.pos_conv_embed.conv.parametrizations.weight.original0", "encoder.pos_conv.0.weight_g")
put("encoder.pos_conv_embed.conv.parametrizations.weight.original1", "encoder.pos_conv.0.weight_v")
put("encoder.layer_norm.weight", "encoder.layer_norm.weight")
put("encoder.layer_norm.bias", "encoder.layer_norm.bias")
for idx in range(12):
prefix_hf = f"encoder.layers.{idx}"
prefix_fs = f"encoder.layers.{idx}"
for proj in ("k", "v", "q", "out"):
put(f"{prefix_hf}.attention.{proj}_proj.weight", f"{prefix_fs}.self_attn.{proj}_proj.weight")
put(f"{prefix_hf}.attention.{proj}_proj.bias", f"{prefix_fs}.self_attn.{proj}_proj.bias")
put(f"{prefix_hf}.layer_norm.weight", f"{prefix_fs}.self_attn_layer_norm.weight")
put(f"{prefix_hf}.layer_norm.bias", f"{prefix_fs}.self_attn_layer_norm.bias")
put(f"{prefix_hf}.feed_forward.intermediate_dense.weight", f"{prefix_fs}.fc1.weight")
put(f"{prefix_hf}.feed_forward.intermediate_dense.bias", f"{prefix_fs}.fc1.bias")
put(f"{prefix_hf}.feed_forward.output_dense.weight", f"{prefix_fs}.fc2.weight")
put(f"{prefix_hf}.feed_forward.output_dense.bias", f"{prefix_fs}.fc2.bias")
put(f"{prefix_hf}.final_layer_norm.weight", f"{prefix_fs}.final_layer_norm.weight")
put(f"{prefix_hf}.final_layer_norm.bias", f"{prefix_fs}.final_layer_norm.bias")
missing = sorted(set(hf_state) - set(mapped))
if missing:
raise ValueError(f"Unmapped Transformers keys: {missing}")
return mapped
def convert(checkpoint_path: Path, output_dir: Path) -> None:
checkpoint = torch.load(checkpoint_path, map_location="cpu", weights_only=False)
fairseq_state = checkpoint["model"]
config = HubertConfig(
hidden_dropout=0.0,
activation_dropout=0.0,
attention_dropout=0.0,
feat_proj_dropout=0.0,
final_dropout=0.0,
mask_time_prob=0.0,
layerdrop=0.0,
)
model = HubertModel(config)
mapped = map_state_dict(fairseq_state, model.state_dict())
model.load_state_dict(mapped, strict=True)
model.eval()
output_dir.mkdir(parents=True, exist_ok=True)
model.save_pretrained(output_dir)
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--input", default="assets/hubert/hubert_base.pt")
parser.add_argument("--output", default="assets/hubert/hubert_base_transformers")
args = parser.parse_args()
convert(resolve_path(args.input), resolve_path(args.output))
print(f"saved Transformers HuBERT to {args.output}")
if __name__ == "__main__":
main()