Files
rustytorch/crates/models/rtx-csm/examples/wavlm_sv_convert.rs
T
osobhandClaude Opus 4.7 d1dba7a05c rtx-csm: WavLM-SV converter + end-to-end speaker similarity
Phase 5c — pure-Rust converter for microsoft/wavlm-base-plus-sv with
auto-detecting weight_norm merger; verified on real 100M-param weights:
load + embed + cosine-similarity round-trip works on Metal.

- wavlm_sv_convert.rs: candle_core::pickle reads pytorch_model.bin
  directly. merge_weight_norm_auto picks the kept dim from g's shape
  (dim=0 for AudioSeal SEANet, dim=2 for WavLM pos_conv_embed). Skips
  classifier.*/objective.* (train-only AMSoftmax head).
- examples/wavlm_sv_convert: HF download + convert CLI. Verified output:
  1 weight_norm pair merged + 261 passthrough + 3 skipped = 262 tensors.
- examples/wavlm_sv_demo: load + embed pair of WAVs + cosine similarity.
- examples/audioseal_inspect: gains --which wavlm-sv variant for key
  discovery.
- hub.rs: REPO_WAVLM_SV + resolve_wavlm_sv() helper.
- wavlm_sv::XVectorHead bug fix: layer_weights is top-level, not nested
  under prefix.

Verified end-to-end on Metal: cosine sim 0.9985 on same-speaker pair
(CSM vs CSM-watermarked, 10s @ 24 kHz resampled to 16 kHz). Numerical
parity vs HF reference is Phase 5d.

3 converter tests + 12 wavlm_sv tests; 78 lib tests total green.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-25 19:57:52 -07:00

44 lines
1.3 KiB
Rust

//! Convert `microsoft/wavlm-base-plus-sv/pytorch_model.bin` → flat
//! safetensors with weight_norm merged.
//!
//! Usage:
//! ```
//! cargo run -p rtx-csm --release --example wavlm_sv_convert -- \
//! --out /tmp/wavlm_sv.safetensors
//! ```
use anyhow::{Context, Result};
use clap::Parser;
use rtx_csm::{hub, wavlm_sv_convert};
use std::path::PathBuf;
#[derive(Debug, Parser)]
#[command(name = "wavlm_sv_convert")]
struct Cli {
/// Optional override; defaults to HF-fetched pytorch_model.bin.
#[arg(long)]
input: Option<PathBuf>,
/// Output safetensors.
#[arg(long)]
out: PathBuf,
}
fn main() -> Result<()> {
tracing_subscriber::fmt().init();
let cli = Cli::parse();
let input = match cli.input {
Some(p) => p,
None => hub::resolve_wavlm_sv().context("resolve_wavlm_sv")?,
};
tracing::info!("converting {} -> {}", input.display(), cli.out.display());
let report = wavlm_sv_convert::convert_pth(&input, &cli.out)?;
println!(
"merged {} weight_norm pairs, {} passthrough, {} skipped (classifier/objective), {} total tensors written",
report.merged_weight_norm_pairs,
report.passthrough_tensors,
report.skipped_tensors,
report.total_tensors_written
);
Ok(())
}