Source code for mllm_shap.value.wer

"""Word error rate against a reference text as a coalition value.

@domain value
@see [[knowledge/architecture/value]]
@tested-by [[tests/value/test_wer.py]]
"""

import re
from typing import TYPE_CHECKING, ClassVar

from ..protocols.backend import GenerationResult
from ..protocols.plugin import PluginOptions

if TYPE_CHECKING:
    from ..plugins.context import PluginContext

_WORD_RE = re.compile(r"[\w']+")


[docs] def word_error_rate(reference: str, hypothesis: str) -> float: """Return word-level edit distance over the reference's word count. Words are lowercased runs of letters, digits, ``_`` and ``'``, so case and punctuation do not count. An empty reference gives 0 for an empty hypothesis and 1 otherwise. """ ref = _WORD_RE.findall(reference.lower()) hyp = _WORD_RE.findall(hypothesis.lower()) if not ref: return 0.0 if not hyp else 1.0 previous = list(range(len(hyp) + 1)) for i, word in enumerate(ref, start=1): current = [i] for j, other in enumerate(hyp, start=1): current.append( min( previous[j] + 1, current[j - 1] + 1, previous[j - 1] + (word != other), ) ) previous = current return previous[-1] / len(ref)
[docs] class ReferenceWerOptions(PluginOptions): """Options of the ``reference_wer`` value function.""" reference: str | None = None """Target text, such as a transcript; ``None`` uses the full coalition's answer."""
[docs] class ReferenceWerValue: """Score ``1 - min(1, WER)`` of a coalition's answer against a reference. With ``reference`` set, ``v(S)`` measures how correct the answer is (a transcription task); without it, how close it stays to the full answer. Values lie in ``[0, 1]``. """ options_model: ClassVar[type[PluginOptions]] = ReferenceWerOptions """Options accepted from ``explanation.plugin_options``.""" def __init__(self, context: "PluginContext | None" = None) -> None: """Read ``reference`` from the plugin options in ``context``.""" options = ReferenceWerOptions() if context is not None: options = ReferenceWerOptions.model_validate(context.options) self.reference = options.reference """Target text; ``None`` scores against the base generation.""" @property def name(self) -> str: """Return the registered value-function identifier.""" return "reference_wer"
[docs] def score(self, base: GenerationResult, candidate: GenerationResult) -> float: """Return ``1 - min(1, WER)`` of ``candidate`` against the target.""" target = base.text if self.reference is None else self.reference return 1.0 - min(1.0, word_error_rate(target, candidate.text))