8-gen bench (4 emotions × 2 corpora) at seed=42 against firdhokk Whisper-LV3: target RAVDESS CREMA-D happy happy (0.999) ✓ happy (0.999) ✓ angry neutral (0.92) sad (0.99) fearful happy (0.998) fearful (0.984) ✓ sad angry (0.99) fearful (0.99) CREMA-D 2/4 vs RAVDESS 1/4. Larger / more naturalistic corpus produces more class-pure fearful direction. Neither corpus solves angry or sad — recipe shifts into 'vague expressivity' rather than class-specific corners. Practical: prefer CREMA-D when available; A/B both per emotion if class precision matters. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
92 lines
3.1 KiB
Rust
92 lines
3.1 KiB
Rust
//! Phase 8.4 spike — download Moonshine-tiny from HuggingFace and dump
|
|
//! the safetensors layout. Used to design the candle module shape.
|
|
//!
|
|
//! Architecture (from HF config.json):
|
|
//! encoder-decoder transformer, hidden_size=288, intermediate=1152,
|
|
//! 6 enc + 6 dec layers, 8 heads (head_dim=36 + pad to 40),
|
|
//! partial_rotary_factor=0.9, vocab=32768, max_pos=194, audio input
|
|
//! is raw waveform (no mel-spec preprocessing).
|
|
//!
|
|
//! Usage:
|
|
//! ```bash
|
|
//! cargo run -p rtx-csm --release --example moonshine_inspect
|
|
//! ```
|
|
|
|
use anyhow::{Context, Result};
|
|
use hf_hub::api::sync::Api;
|
|
|
|
fn main() -> Result<()> {
|
|
let api = Api::new().context("hf_hub init")?;
|
|
let repo = api.model("UsefulSensors/moonshine-tiny".to_string());
|
|
let weights_path = repo
|
|
.get("model.safetensors")
|
|
.context("download model.safetensors")?;
|
|
println!("downloaded: {}", weights_path.display());
|
|
let bytes = std::fs::read(&weights_path)?;
|
|
println!(
|
|
"file size: {} bytes ({:.1} MB)",
|
|
bytes.len(),
|
|
bytes.len() as f64 / 1_000_000.0
|
|
);
|
|
|
|
// Parse the safetensors header to enumerate tensor names + shapes.
|
|
let st = safetensors::SafeTensors::deserialize(&bytes).context("parse safetensors header")?;
|
|
let names = st.names();
|
|
println!("\n=== {} tensors ===", names.len());
|
|
|
|
// Group by prefix so the layered structure is visible.
|
|
let mut grouped: std::collections::BTreeMap<String, Vec<(String, Vec<usize>, &str)>> =
|
|
Default::default();
|
|
for name in names {
|
|
let tensor = st.tensor(name)?;
|
|
let prefix = name.split('.').take(2).collect::<Vec<_>>().join(".");
|
|
let dtype = match tensor.dtype() {
|
|
safetensors::Dtype::F32 => "f32",
|
|
safetensors::Dtype::F16 => "f16",
|
|
safetensors::Dtype::BF16 => "bf16",
|
|
other => match other {
|
|
_ => "?",
|
|
},
|
|
};
|
|
grouped
|
|
.entry(prefix)
|
|
.or_default()
|
|
.push((name.to_string(), tensor.shape().to_vec(), dtype));
|
|
}
|
|
|
|
for (prefix, items) in grouped.iter() {
|
|
println!("\n[{prefix}] ({} tensors)", items.len());
|
|
for (name, shape, dtype) in items.iter().take(8) {
|
|
println!(" {name:<60} {dtype} {shape:?}");
|
|
}
|
|
if items.len() > 8 {
|
|
println!(" ... {} more", items.len() - 8);
|
|
}
|
|
|
|
// Special: dump everything matching `conv` since we need
|
|
// exact strides for the audio stem.
|
|
for (name, shape, dtype) in items.iter().filter(|(n, _, _)| n.contains("conv")) {
|
|
if !items.iter().take(8).any(|(n2, _, _)| n2 == name) {
|
|
println!(" {name:<60} {dtype} {shape:?} (conv detail)");
|
|
}
|
|
}
|
|
}
|
|
|
|
let total_params: usize = st
|
|
.names()
|
|
.iter()
|
|
.map(|n| {
|
|
st.tensor(n)
|
|
.map(|t| t.shape().iter().product::<usize>())
|
|
.unwrap_or(0)
|
|
})
|
|
.sum();
|
|
println!(
|
|
"\ntotal parameters: {} ({:.1} M)",
|
|
total_params,
|
|
total_params as f64 / 1_000_000.0
|
|
);
|
|
|
|
Ok(())
|
|
}
|