rtx-csm: emotional_speech_guide — CREMA-D vs RAVDESS firdhokk verdict

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]>
This commit is contained in:
osobh
2026-04-30 00:01:02 -07:00
co-authored by Claude Opus 4.7
parent f4d8268381
commit a5cedfb46a
69 changed files with 1026 additions and 734 deletions
+15 -14
View File
@@ -29,8 +29,8 @@
//! enough of the PyTorch pickle format to extract a `state_dict`-style nested
//! `Dict`. So we don't need a Python step in the conversion pipeline.
use anyhow::{anyhow, Context, Result};
use candle_core::{pickle, safetensors as ct_safetensors, DType, Device, Tensor};
use anyhow::{Context, Result, anyhow};
use candle_core::{DType, Device, Tensor, pickle, safetensors as ct_safetensors};
use std::collections::HashMap;
use std::path::Path;
@@ -71,9 +71,9 @@ pub fn convert_pth(
let weight_v = v_tensors
.remove(&stem)
.expect("weight_v stem present after sort");
let weight_g = g_tensors.remove(&stem).ok_or_else(|| {
anyhow!("orphan weight_v at {stem} (no matching weight_g entry)")
})?;
let weight_g = g_tensors
.remove(&stem)
.ok_or_else(|| anyhow!("orphan weight_v at {stem} (no matching weight_g entry)"))?;
let merged = merge_weight_norm(&weight_v, &weight_g)
.with_context(|| format!("merging weight_norm at {stem}"))?;
out_map.insert(format!("{stem}.weight"), merged);
@@ -82,7 +82,9 @@ pub fn convert_pth(
if !g_tensors.is_empty() {
let orphans: Vec<_> = g_tensors.keys().cloned().collect();
return Err(anyhow!("orphan weight_g entries (no matching weight_v): {orphans:?}"));
return Err(anyhow!(
"orphan weight_g entries (no matching weight_v): {orphans:?}"
));
}
let pass_count = passthrough.len();
@@ -167,12 +169,7 @@ mod tests {
fn weight_norm_merge_matches_manual() {
// Reproduce PyTorch weight_norm formula on a small (2, 3) tensor.
let device = Device::Cpu;
let v = Tensor::from_slice(
&[1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0],
(2, 3),
&device,
)
.unwrap();
let v = Tensor::from_slice(&[1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0], (2, 3), &device).unwrap();
let g = Tensor::from_slice(&[2.0f32, 3.0], (2, 1), &device).unwrap();
let merged = merge_weight_norm(&v, &g).unwrap();
let merged: Vec<f32> = merged.flatten_all().unwrap().to_vec1().unwrap();
@@ -181,8 +178,12 @@ mod tests {
let n0 = (1.0f32 + 4.0 + 9.0).sqrt();
let n1 = (16.0f32 + 25.0 + 36.0).sqrt();
let expected = vec![
2.0 * 1.0 / n0, 2.0 * 2.0 / n0, 2.0 * 3.0 / n0,
3.0 * 4.0 / n1, 3.0 * 5.0 / n1, 3.0 * 6.0 / n1,
2.0 * 1.0 / n0,
2.0 * 2.0 / n0,
2.0 * 3.0 / n0,
3.0 * 4.0 / n1,
3.0 * 5.0 / n1,
3.0 * 6.0 / n1,
];
for (a, b) in merged.iter().zip(expected.iter()) {
assert!((a - b).abs() < 1e-6, "merge mismatch: got {a}, want {b}");