Downloads · 30 days
0
matbee/whisper-to-vad
whisper-to-vad is a audio classification model from matbee. Use it for the audio classification task on the model card, and read the license before you ship it in a product. It is set up for onnx. The card lists the license as mit.
A lightweight regressor that maps Whisper-large-v3-turbo encoder hidden states to continuous Valence / Arousal / Dominance coordinates in [-1, +1]^3.
Downloads · 30 days
0
Access
Public
Updated Oct 15, 2026
Repo size
1.4 MB
Likes
0
Public
Click a slice to open those files.
.data1.4 MB · 96%
From the Hugging Face model README
A lightweight regressor that maps Whisper-large-v3-turbo encoder hidden
states to continuous Valence / Arousal / Dominance coordinates in
[-1, +1]^3.
Trained on CREMA-D
under the Open Database License. Used as an external V/A/D auditor in
the remotemedia-sdk
affect-pipeline projects (LLM activation steering, audio→blendshape
diffusion).
ridge_l12_neutral, cross-corpus model (2026-10-15)ridge.onnx was trained and tested on CREMA-D only. On speech from other corpora it gets arousal
roughly right, but scores everything, including happy speech, as strongly negative valence (bias ≈ −0.4).
ridge_l12_neutral fixes that. It reads an earlier encoder layer and is applied to embeddings minus
the speaker's own calm-voice baseline.
Scores below are on corpora excluded from training (leave-one-corpus-out, held-out speakers), with a 5-utterance calm warm-up. CCC is the primary metric: it penalises the offsets that Pearson r ignores.
| held-out corpus | model | V CCC | A CCC | D CCC | AUC happy>neutral (valence) | neutral clips' mean V |
|---|---|---|---|---|---|---|
| RAVDESS (eval only, never trained on) | ridge.onnx | 0.24 | 0.37 | 0.32 | 0.47 | −0.45 |
ridge_l12_neutral | 0.66 | 0.59 | 0.65 | 0.91 | −0.08 | |
| JL-Corpus (model trained without JL) | ridge.onnx | 0.40 | 0.55 | 0.36 | 0.91 | −0.21 |
ridge_l12_neutral recipe | 0.54 | 0.73 | 0.47 | 0.99 | −0.07 |
| file | what |
|---|---|
ridge_l12_neutral.onnx | Ridge, same I/O as ridge.onnx: whisper_embed (N, 1280) → vad (N, 3) |
ridge_l12_neutral.json | Runtime contract (layer, normalisation, baseline gate) + provenance |
ridge_l12_neutral.population_neutral.npy | (1280,) float32: population calm-voice embedding, used before the speaker's own is known |
ridge.onnx)hidden_states[-12], i.e. after block 21 of 32),
mean-pooled over the processor's padded 30 s window, exactly like ridge.onnx's layer −2 input.reference = (population + Σ accepted) / (1 + n_accepted).
import json, numpy as np, onnxruntime as ort, torch, librosa
from transformers import AutoModel, AutoProcessor
cfg = json.load(open("ridge_l12_neutral.json"))["runtime"]
processor = AutoProcessor.from_pretrained("openai/whisper-large-v3-turbo")
encoder = AutoModel.from_pretrained("openai/whisper-large-v3-turbo").get_encoder().eval()
sess = ort.InferenceSession("ridge_l12_neutral.onnx", providers=["CPUExecutionProvider"])
population = np.load(cfg["population_baseline"])
accepted = [] # this speaker's low-arousal utterance embeddings
def predict(path):
audio, _ = librosa.load(path, sr=16000)
inputs = processor(audio, sampling_rate=16000, return_tensors="pt")
with torch.no_grad():
hidden = encoder(**inputs, output_hidden_states=True).hidden_states[cfg["whisper_layer"]]
embed = hidden.mean(dim=1)[0].numpy().astype(np.float32)
reference = (population + np.sum(accepted, axis=0)) / (1 + len(accepted))
vad = sess.run(["vad"], {"whisper_embed": (embed - reference)[None].astype(np.float32)})[0][0]
if abs(vad[1]) < 0.3:
accepted.append(embed)
return vad, len(accepted) >= 2 # (valence, arousal, dominance), warmed up?
remotemedia-sdk's ProsodyVad reads ridge_l12_neutral.json and does all of this itself.
W/b, so the ONNX is still
MatMul + Add.ridge.onnx.tools/affect_calibration/ in remotemedia-sdk
(11_cross_corpus_eval.py, 12_vad_sweep.py, 13_online_baseline_sim.py,
notes/whisper-to-vad-eval-2026-10-15.md).Labels are acted categories mapped to fixed V/A/D points, not dimensional ratings, and every corpus here is acted or synthetic. Expect lower numbers on spontaneous conversation.
| Variant | File | Test Pearson r (V/A/D) | Notes |
|---|---|---|---|
| Ridge ⭐ | ridge.onnx | 0.58 / 0.66 / 0.69 | Recommended. Linear projection, ~15 KB, faster. |
| MLP | mlp.onnx + mlp.onnx.data | 0.28 / 0.62 / 0.55 | Reference. 2-layer MLP, ~1.4 MB external weights. |
Ridge wins on every axis on the test split. The MLP is shipped only as
a comparison baseline; production use should default to ridge.onnx.
Both models share the same ONNX I/O:
| Tensor | Name | Shape | dtype | Description |
|---|---|---|---|---|
| input | whisper_embed | (batch, 1280) | float32 | Mean-pooled Whisper encoder hidden state |
| output | vad | (batch, 3) | float32 | [valence, arousal, dominance] ∈ [-1, +1]^3 |
Critical: input is per-clip, not per-frame. Pool Whisper hidden states over time (mean across the time axis) before feeding to this regressor.
import numpy as np
import onnxruntime as ort
from transformers import AutoModel, AutoProcessor
# 1. Load Whisper encoder + this regressor
processor = AutoProcessor.from_pretrained("openai/whisper-large-v3-turbo")
whisper = AutoModel.from_pretrained("openai/whisper-large-v3-turbo").get_encoder().eval()
sess = ort.InferenceSession("ridge.onnx", providers=["CPUExecutionProvider"])
# 2. Extract hidden states from layer -2 (penultimate); mean-pool over time
import torch, librosa
audio, _ = librosa.load("clip.wav", sr=16000)
inputs = processor(audio, sampling_rate=16000, return_tensors="pt")
with torch.no_grad():
out = whisper(**inputs, output_hidden_states=True)
embed = out.hidden_states[-2].mean(dim=1).numpy().astype(np.float32) # (1, 1280)
# 3. Predict V/A/D
vad = sess.run(["vad"], {"whisper_embed": embed})[0] # (1, 3)
print(f"valence={vad[0,0]:+.2f} arousal={vad[0,1]:+.2f} dominance={vad[0,2]:+.2f}")
W ∈ R^(1280 × 3) + bias. Closed-form ridge
regression with α=1.0, no regularization on bias. Total params: 3,843.Linear(1280, 256) → GELU → Linear(256, 3),
AdamW + cosine schedule, 100 epochs. Total params: 328,963.emotion_to_vad.py).{
"dataset": "crema_d",
"whisper_model": "openai/whisper-large-v3-turbo",
"whisper_layer": -2,
"regressor": "ridge",
"n_train": 1391,
"n_val": 305,
"n_test": 304,
"speaker_disjoint": true,
"axes": [
"valence",
"arousal",
"dominance"
],
"val": {
"n": 305,
"rmse": {
"valence": 0.41578346490859985,
"arousal": 0.27047833800315857,
"dominance": 0.2509686052799225
},
"mae": {
"valence": 0.3144280016422272,
"arousal": 0.21582330763339996,
"dominance": 0.20134007930755615
},
"pearson_r": {
"valence": 0.6142750533217433,
"arousal": 0.6183148626337297,
"dominance": 0.7671747949052007
}
},
"test": {
"n": 304,
"rmse": {
"valence": 0.4229687750339508,
"arousal": 0.2829131484031677,
"dominance": 0.2870493531227112
},
"mae": {
"valence": 0.3345738351345062,
"arousal": 0.22188791632652283,
"dominance": 0.2326992005109787
},
"pearson_r": {
"valence": 0.5816889655848607,
"arousal": 0.6575878679123864,
"dominance": 0.6881453235310694
}
},
"dry_run": false
}
{
"dataset": "crema_d",
"whisper_model": "openai/whisper-large-v3-turbo",
"whisper_layer": -2,
"regressor": "mlp",
"n_train": 1391,
"n_val": 305,
"n_test": 304,
"speaker_disjoint": true,
"axes": [
"valence",
"arousal",
"dominance"
],
"val": {
"n": 305,
"rmse": {
"valence": 0.4990813732147217,
"arousal": 0.2946903705596924,
"dominance": 0.299040824174881
},
"mae": {
"valence": 0.42277732491493225,
"arousal": 0.24514958262443542,
"dominance": 0.25509873032569885
},
"pearson_r": {
"valence": 0.339805625682777,
"arousal": 0.518995070692627,
"dominance": 0.672127424657303
}
},
"test": {
"n": 304,
"rmse": {
"valence": 0.5018822550773621,
"arousal": 0.3011777400970459,
"dominance": 0.32821124792099
},
"mae": {
"valence": 0.4377283751964569,
"arousal": 0.24337027966976166,
"dominance": 0.28178495168685913
},
"pearson_r": {
"valence": 0.2784075767971956,
"arousal": 0.6199177507366342,
"dominance": 0.5484364257265157
}
},
"dry_run": false
}
MIT (this model). Upstream licenses:
ridge_l12_neutral only: JL-Corpus (CC0, per the Kaggle upload by co-author Li Tian) and
EmoVoice-DB (MIT; its audio was generated with OpenAI GPT-4o-audio).If you publish work using this regressor, cite CREMA-D + the Whisper paper:
@article{cao2014cremad,
title={CREMA-D: Crowd-sourced Emotional Multimodal Actors Dataset},
author={Cao, Houwei and Cooper, David G and Keutmann, Michael K and Gur, Ruben C and Nenkova, Ani and Verma, Ragini},
journal={IEEE Transactions on Affective Computing},
year={2014}
}
@inproceedings{radford2023whisper,
title={Robust Speech Recognition via Large-Scale Weak Supervision},
author={Radford, Alec and Kim, Jong Wook and Xu, Tao and Brockman, Greg and McLeavey, Christine and Sutskever, Ilya},
booktitle={ICML},
year={2023}
}