Skip to main content
BeWER

Python Versions Coverage PyPI License


⚠️ Important: Bewer is pre-1.0 and under active development. The API may change. Breaking changes are signalled by a minor version bump (e.g. 0.2.x → 0.3.0); patch releases contain only fixes and backwards-compatible additions. Pin with bewer~=0.Y.0 (pip) or ^0.Y.0 (Poetry).

Bewer is an evaluation and analysis framework for automatic speech recognition in Python. It defines a flexible approach for configuring and customizing speech recognition evaluation. The hierarchical structure, going from the dataset level down to individual tokens, makes it easy to inspect individual examples and understand evaluation results. The built-in preprocessing pipeline and metrics collection are designed to cover all conventional use cases and then some, while still being fully extensible.

Contents | Installation | Quickstart | Core Concepts | Metrics Catalog |

Installation

pip install bewer

Quickstart

from bewer import Dataset

# Create an evaluation dataset
dataset = Dataset(language="en")

# Load data
dataset.load_csv(
    "data.csv",
    ref_col="reference",
    hyp_col="hypothesis",
)

# Compute a metric
wer = dataset.metrics.wer()
print(f"{wer.short_name_base}: {wer.value:.2%}")
WER: 12.34%

Core Concepts

Hierarchy

In bewer, evaluation is centered around the Dataset object, which implements a hierarchical structure, from a collection of reference-hypothesis pairs to individual tokens.

dataset = Dataset(language="en")            # Create a dataset ...
dataset.add(ref="one two", hyp="want to")   # ... and populate it

# Climb down the hierarchy:
for example in dataset:                     # Dataset -> Example
    print(example)
    text = example.ref                      # Example -> Text
    print(text)
    for token in text.tokens:               # Text -> Token
        print(token)
Example(ref="one two", hyp="want to")
Text("one two")
Token("one")
Token("two")

Preprocessing

Text preprocessing is a central part of speech recognition evaluation, and typically requires specific considerations for different languages, domains, or tasks. The bewer preprocessing pipeline runs in three stages:

  • Standardization runs on the whole string, before tokenization. It irons out inconsistencies that would otherwise affect how the text is split. The default standardizer applies Unicode NFC and unifies apostrophe, hyphen, and slash variants.
  • Tokenization is regex-based, to track token boundaries in the standardized text. While not essential for metric computation, it supports richer post-hoc analysis.
  • Normalization runs per token, after tokenization. At this point, token boundaries are fixed. It typically handles lowercasing, removing diacritics, and other language-specific adjustments.

The result of each stage is accessible from the corresponding objects.

text.raw                      # str: the original input
text.standardized             # str: after the standardizer
text.tokens.standardized      # list[str]: the tokens after standardization
text.tokens.normalized        # list[str]: the tokens after normalization

Pre-defined pipeline components can be found under dataset.pipelines.

print(dataset.pipelines)
Pipelines(
    standardizers: default
    tokenizers:    default, with_punctuation, key_term, orthographically_complex_term
    normalizers:   default, cased
)

Each component is named and defined as part of the dataset's configuration. Typically, it is sufficient to just choose the language, and the configuration for that language is applied automatically, but you can adapt and extend the pipeline as needed.

# TODO: Example of customizing the pipeline for a dataset

Any component can be switched with the set_pipeline context manager. The English key_term tokenizer, for instance, splits on apostrophes as well as hyphens and slashes, so that a term is still matched when it appears in the possessive form. In contrast, the default tokenizer does not.

from bewer import set_pipeline

dataset = Dataset(language="en")
dataset.add(ref="Jane Doe's", hyp="Jane Doe's")
text = dataset[0].ref

print(text.tokens.standardized)

with set_pipeline(tokenizer="key_term"):
    print(text.tokens.standardized)
['Jane', "Doe's"]
['Jane', 'Doe', 's']

In practice you will rarely need to adjust the pipeline manually. As we will see next, metrics carry the pipeline they run under, so a key-term metric is already defined to use the key_term tokenizer.

Metrics

Every metric in bewer is registered under one or more names, which makes it accessible from a dataset's metrics collection.

wer = dataset.metrics.wer()

All metrics accept the preprocessing components standardizer, tokenizer and normalizer as keyword arguments, allowing you to override the defaults they were registered with. Some metrics also accept arguments specific to their computation.

# Override the pipeline components a metric runs under (see Vocabularies below
# for how to define a key-term vocabulary):
ktf = dataset.metrics.ktf(
    tokenizer="key_term",
    vocab="medical",
)

Call dataset.metrics.list_metrics() to get an overview of available metrics and their parameters or see the metrics catalog below.

You can also register new variants of existing metrics or your own custom metrics.

from bewer.metrics import METRIC_REGISTRY, WER

# Register a custom metric in the global registry
METRIC_REGISTRY.register_metric(WER, name="my_wer", tokenizer="key_term")

# Access it from the dataset's metrics collection
my_wer = dataset.metrics.my_wer()

Every metric exposes a main value alongside the constituents it was computed from. By convention, the main value is simply value for numeric metrics and alignment for alignments.

print(wer.metric_values())
print(f"{wer.value:.2%} = {wer.num_edits}/{wer.ref_length}")
{'main': 'value', 'other': ['num_edits', 'ref_length']}
12.50% = 1/8

Most metrics are defined by aggregating over example-level values. This structure is reflected in bewer, which also exposes example-level metrics accessible through the example objects or directly from the metric object itself.

wer = dataset.metrics.wer()

assert wer[0] is dataset[0].metrics.wer()
assert wer.num_edits == sum(ex_wer.num_edits for ex_wer in wer)

Vocabularies

Key-term metrics measure performance on a pre-defined subset of terms (medical, financial, acronyms, etc.). To use them, build a Vocabulary and attach it to the dataset.

from bewer import Vocabulary

dataset = Dataset(language="en")
dataset.add(
    ref="the patient has diabetes and high blood sugar",
    hyp="the patient has diabetis and high blood sugar",
)

# Add key terms from a Python list
vocab = Vocabulary(name="medical")
vocab.add_terms(["diabetes", "blood sugar"])
dataset.add_vocabulary(vocab)

# Compute key-term recall by referencing the vocabulary name
ktr = dataset.metrics.ktr(vocab="medical")
print(f"{ktr.long_name_base}: {ktr.value:.2%}")
Key-Term Recall: 50.00%

You can also load line-separated key terms directly from a file (add_file) or write a custom function (Dataset -> Iterable[str]) to extract key terms from the dataset (add_extractor).

Alignments

Alignments produce an example-level text-to-text alignment as their primary output, rather than a dataset-level numeric score. An Alignment is a sequence of Op objects, each representing a match, substitution, insertion, or deletion between hypothesis and reference. Edit counts are available as supportive values.

dataset = Dataset(language="en")
dataset.add(
    ref="an example with different types of errors",
    hyp="an odd example with diff types errors",
)

# Access alignments at the example level
example = dataset[0]
alignment = example.metrics.levenshtein().alignment

# Access pre-computed edit operation counts
assert alignment.num_edits + alignment.num_matches == len(alignment)
assert alignment.num_edits >= alignment.num_substitutions

# Print a color-coded two-row alignment in the console
alignment.display()
A color-coded two-row alignment, showing an insertion, a substitution and a deletion

Lazy Computation and Caching

Metric values and pipeline attributes are computed lazily. The dependencies between the pipeline stages are tracked to make sure that the full pipeline is considered when deciding which computations are necessary. Once a metric is requested from the metrics collection, it is cached and reused for subsequent requests with matching parameters or by other metrics that depend on it.

wer = dataset.metrics.wer() # already uses the "default" normalizer
assert wer is dataset.metrics.wer(normalizer="default")

Metrics Catalog

Type Accessor
General purpose
Word Error Rate General wer >
Character Error Rate General cer >
Punctuation Error Rate General per >
Hallucination metrics
Insertion Rate Hallucination insertion_rate >
Key-term metrics
Key-Term Recall Key-term ktr >
Key-Term Precision Key-term ktp >
Key-Term F-Score Key-term ktf >
Key-Term Error Rate Key-term kter >
Key-Term False-Positive Rate Key-term ktfpr >
Key-Term Character Error Rate Key-term ktcer >
Relaxed Key-Term Recall Key-term rktr >
Orthographically complex terms
Orthographically Complex Term Recall Key-term orthographically_complex_term_recall >
Orthographically Complex Term Precision Key-term orthographically_complex_term_precision >
Orthographically Complex Term F-Score Key-term orthographically_complex_term_fscore >
Alignments
Levenshtein Alignment Alignment levenshtein >
Error Alignment Alignment error_align >
Dataset statistics
Dataset Summary Summary summary >

Metadata

Release files for bewer 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bewer 0.1.0
File Size Uploaded
bewer-0.1.0.tar.gz 73.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bewer 0.1.0
File Interpreter ABI Platform
bewer-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 176.1 kB

Release files / bewer-0.1.0.tar.gz

Download URL bewer-0.1.0.tar.gz
Size 73.5 kB
Tags Source
SHA-256 checksum
How to use checksums
1950c9bf226e0fa87f910726785681f2236a77487a13e7e7b49cf9ded23fa6d3
BLAKE2b-256 checksum
How to use checksums
a1b58bb0e00572435359a3ad578df2237f32f02a8a05168bb504a7e67274db3a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / bewer-0.1.0-py3-none-any.whl

Download URL bewer-0.1.0-py3-none-any.whl
Size 102.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
96f145efd207152c3a5b76c736488a4e6d8d2f6f1722fefd95ce94d94fb0889b
BLAKE2b-256 checksum
How to use checksums
d109aa57ba42440506a7578fa242b04f6fd8895a9af2962751bcd5598f3eaf46
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page