ffasr / ffas.py
whojavumusic's picture
new scoring method
1db043b
Raw
History Blame Contribute Delete
2.35 kB
"""Far-Field ASR Score (FFAS): harmonic mean of smooth per-condition accuracies."""
from __future__ import annotations
from typing import Optional, Sequence
def ffas_harmonic(values: Sequence[float], weights: Optional[Sequence[float]] = None) -> float:
"""
Weighted harmonic mean of positive smooth scores in (0, 1]; higher is better.
Used for per-scenario accuracies and optional speed/RTF terms.
"""
if not values:
raise ValueError("values must contain at least one entry")
if any(v <= 0 for v in values):
raise ValueError("values must be positive")
if weights is None:
weights = [1.0] * len(values)
elif len(weights) != len(values):
raise ValueError("weights and values must have the same length")
weighted_recip_sum = sum(w / v for w, v in zip(weights, values))
return sum(weights) / weighted_recip_sum
def smooth_speed_from_rtf(rtf: float) -> float:
"""Map RTF (audio sec / infer sec, higher = faster) to a smooth score in (0, 1]."""
if rtf < 0 or rtf != rtf: # NaN
raise ValueError("RTF must be a non-negative finite number")
r = float(rtf)
return r / (1.0 + r)
def ffas(wers: Sequence[float], weights: Optional[Sequence[float]] = None) -> float:
"""
Far-Field ASR Score (FFAS): harmonic mean of smooth accuracies.
Each WER is mapped to accuracy via A = 1 / (1 + WER), bounded in (0, 1].
Handles WER > 1 gracefully (never collapses to 0). The final score is
the (weighted) harmonic mean of accuracies across all conditions.
Args:
wers: WER values for each condition (non-negative).
weights: Optional weights, same length as wers. Defaults to equal.
Returns:
FFAS score in (0, 1]. Higher is better.
"""
if not wers:
raise ValueError("wers must contain at least one value")
if any(w < 0 for w in wers):
raise ValueError("WER values must be non-negative")
accs = [1.0 / (1.0 + wer) for wer in wers]
return ffas_harmonic(accs, weights)
def robustness(wer_anechoic: float, wer_degraded: float) -> float:
"""
Companion metric: how gracefully the model degrades.
Returns A_degraded / A_anechoic. Closer to 1.0 = more robust.
"""
a_an = 1.0 / (1.0 + wer_anechoic)
a_deg = 1.0 / (1.0 + wer_degraded)
return a_deg / a_an