Skip to main content

raterkit

Reliability, rogue-rater, drift and leakage diagnostics for labelled data.

ci PyPI DOI License: MIT

Every benchmark score, every fine-tune, every "our judge agrees with humans 92% of the time" bottoms out in a pile of human labels — produced by raters who may have been careless, biased, tired by the end of the queue, or graded against gold that was quietly wrong. None of that is visible in accuracy numbers computed on top. raterkit reads the raw ratings and measures each of these failure modes directly, with a confidence interval on every number and a flag only when the evidence clears a stated bar.

The audit in one table

Eight synthetic annotation projects, seven implanted defects. Bold values are flags; each lands on the row where its defect was implanted, and the clean row carries none.

project implanted defect alpha rogue biased drift gap leaks stale gold flags
clean nothing 0.80 0 0 +0.00 0 0.0% none
careless-crowd every rater at 55% accuracy 0.18 0 0 -0.02 0 0.7% reliability
one-spammer one rater answers uniformly at random 0.72 1 0 +0.05 0 0.0% rogue raters
leaning-rater one rater always takes the same side of ambiguous items 0.65 0 1 +0.05 0 - rater bias
tiring-crowd competence decays 0.97 to 0.73 over the queue 0.64 0 0 +0.25 0 0.0% drift
leaky-split 12 test items duplicate a train item's text 0.80 0 0 +0.00 12 0.0% split leakage
stale-gold 8% of gold labels are wrong 0.80 0 0 +0.00 0 6.7% stale gold
easy-but-skewed 94% of items are one class; raters 90% accurate 0.34 0 0 +0.12 0 0.0% reliability

The row to sit with is easy-but-skewed: raters are 90% accurate and agree with each other 81% of the time, yet alpha is 0.34 — far below the 0.667 floor. With 94% of items in one class, raters agree that often by accident; the skew, not the raters, is doing the agreeing. Raw agreement percentages cannot be compared across datasets with different label distributions, which is why every agreement number here is chance-corrected.

rater forest

depth curve

make demo regenerates the table, the full report and the figures from fixed seeds; CI rebuilds them from pinned dependencies and fails if a committed number differs from what the code produces.

211,225 real ratings, audited

examples/goemotions/ audits the raw crowd ratings behind GoEmotions — 211,225 ratings by 82 raters over 58,011 Reddit comments, 28 binary emotion labels each. Committed results: examples/goemotions/results/goemotions.md.

  • 27 of 28 emotions sit below the 0.667 reliability floor — the whole 95% CI, not just the point estimate. Only gratitude (alpha 0.72) passes; the median emotion manages 0.23, and neutral itself scores 0.26.
  • Rater quality spans 0.11 to 0.36 (rater-vs-rest alpha across the 67 raters with 300+ ratings) — the noisiest raters agree with their co-raters at a third of the rate the best ones do, and 20 of 67 sit below 0.20.
  • The rater count was never enough for the task. By Spearman-Brown, the observed reliability would need ~13 raters per item to yield 0.8-reliable aggregated labels; the dataset averages 3.6, which projects to 0.52.
  • 86% of multi-rated comments are contested on at least one emotion, yet raters marked only 1.6% of ratings "very unclear" — disagreement without awareness of disagreement.

None of this says GoEmotions is a scandal — fine-grained emotion is genuinely hard, and the released splits aggregate and filter these raw ratings. It says the raw layer under a famous benchmark fails the standard reliability bar almost everywhere, measurably and reproducibly.

Install

pip install raterkit

Python 3.11+. Runtime dependencies: numpy, pydantic, matplotlib.

Quickstart

Log one JSON object per rating — who labeled what, and optionally when, in which split, and the item text:

{"item_id": "c41", "rater_id": "r07", "label": "toxic", "t": 1718040000, "split": "train", "text": "..."}
from raterkit import load_ratings, load_gold, run_audit

audit = run_audit(load_ratings("ratings.jsonl"), gold=load_gold("gold.jsonl"))
for flag in audit.flags:
    print(flag.name, "--", flag.detail)

Or from the shell, with an exit code CI can gate on:

raterkit report ratings.jsonl --gold gold.jsonl --out audit --fail-on-flags

An optional config sets the measurement level (nominal, ordinal, interval), the label order, and the target reliability. Alongside report.md the audit writes report.json for pipelines that gate on specific numbers. Any check that could not run is listed with the exact fields to log to enable it — a skip describes the log file, not the raters.

An LLM judge is just another rater: give its verdicts a rater_id and the same audit reports how it agrees with the humans, whether its label distribution diverges, and whether it drifts — no special casing needed.

What it checks

check question it answers needs
reliability is agreement above the chance-corrected floor? 2+ ratings on some items
rogue raters does any rater agree with the rest credibly less than the pool? 30+ pairs per rater
rater bias does any rater use the labels differently (marginal shift)? 30+ ratings per rater
drift did agreement change between early and late work? t per rating
split leakage does any duplicated text span train/test? text + split
stale gold which gold labels does a unanimous crowd contradict? a gold file
ambiguity how many items are genuinely contested? 2+ ratings on some items
prevalence is raw agreement inflated by class skew? always runs
rating depth how many raters per item does your target reliability need? always runs

The last three are descriptive and never flag: they explain the numbers above them.

How the numbers are defended

  • Cross-checked implementations. Krippendorff's alpha (nominal, ordinal and interval, with missing data) matches the reference krippendorff package to 1e-10; Cohen's and Fleiss' kappa match statsmodels; the chi-square survival function matches scipy to 1e-10; Benjamini-Hochberg reproduces statsmodels decisions exactly. The library itself depends on none of them.
  • Validation by implantation. The synthetic projects have defect dials — a spammer, a leaning rater, a competence drift, a gold error rate — and the tests require each probe to recover its dial, stay monotone in it, and stay silent on the clean project.
  • Uncertainty on every number. Band probes trigger only when the whole 95% bootstrap CI leaves the innocent band; the bootstrap resamples items (never single ratings), so dependence within an item is respected.
  • Drift-checked results. The demo table, report and README numbers are regenerated by CI from pinned dependencies and diffed against the committed copy.

Design notes

  • Chance correction is not optional. The easy-but-skewed demo row and the kappa-paradox literature both show raw agreement rewarding class skew. All agreement numbers are Krippendorff's alpha, which handles missing ratings, any number of raters, and nominal/ordinal/interval labels in one framework — and reduces to the familiar coefficients where they overlap.
  • Innocent bands, not point nulls. With enough data any rater's marginal "significantly" differs from the pool's while meaning nothing. Band probes state the interval a healthy project could occupy (bias within 0.10 total variation, drift within ±0.10 alpha) and flag only when the whole CI leaves it. Exact procedures use conventions: the 0.667 floor, zero tolerance for cross-split duplicates. All constants live in one place (probes.py) and are easy to disagree with.
  • Raters are compared to the pool, not to a standard. A rater is rogue when their CI sits below the pool's CI — a claim about this project, not about an absolute bar their domain may not share.
  • Ambiguity is measured, not punished. Contested items are reported descriptively: persistent disagreement can be signal about the task, not noise from the raters, and deserves a number rather than a flag.
  • Deterministic to the digit. Seeded PCG64 everywhere, no BLAS in any statistic's path: the same ratings file gives the same report on any machine.

Limitations

  • An audit is bounded by what was logged. No timestamps, no drift check; no text or splits, no leakage check; no gold, no gold check.
  • The innocent bands and the 0.667 floor are conventions, not derivations; fields differ, and the constants are one file away.
  • Spearman-Brown projections assume added raters behave like existing ones — an optimistic bound, stated as such in the report.
  • The rogue-rater comparison needs overlap; raters who never co-rate an item with others cannot be tested and are counted as untested, not innocent.
  • Implantation shows the probes detect the mechanisms simulated; real annotation projects can fail in ways not simulated here.
  • The GoEmotions audit measures the raw rating layer; the released benchmark splits aggregate and filter it, so per-label alpha here is not a direct statement about scores computed on those splits.

Related work

The failure modes measured here are the standard ones from the agreement and annotation-quality literature: agreement coefficients and their proper use (Artstein & Poesio, 2008; Krippendorff, Content Analysis), interpretation benchmarks (Landis & Koch, 1977), the kappa paradox under skewed prevalence (Feinstein & Cicchetti, 1990), crowd label quality (Snow et al., 2008), learning rater competence (Raykar et al., 2010), annotation error detection (Klie et al., 2023), and human label variation as signal (Plank, 2022). The case study audits the GoEmotions raw ratings (Demszky et al., 2020). raterkit's contribution is packaging the checks as one auditable tool with uncertainty on every number and validation by implantation.

Companion projects: judgekit audits LLM judges the way this audits human raters; judgepanel estimates how accurate a panel of judges is when no gold labels exist at all; abkit applies the same audit-before-you-trust pattern to A/B-test readouts; trajectory-judge measures what outcome-only judges miss on agent trajectories; arenakit audits the pairwise leaderboards those labels and judgements end up building.

Citation

Releases are archived on Zenodo. Cite the concept DOI 10.5281/zenodo.21810143, which always resolves to the latest version; structured metadata is in CITATION.cff.

@software{mohammadi_raterkit,
  author  = {Mohammadi, Hadi},
  title   = {raterkit: audit a labeled dataset before you trust it},
  url     = {https://github.com/mohammadi-hadi/raterkit},
  doi     = {10.5281/zenodo.21810143},
  version = {0.1.0},
  year    = {2026}
}

License

MIT

Release files for raterkit 0.1.1

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

Source distribution (sdist)

Source distribution for raterkit 0.1.1
File Size Uploaded
raterkit-0.1.1.tar.gz 208.9 kB Details

Built distribution (wheel)

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

Total release size: 236.9 kB

Release files / raterkit-0.1.1.tar.gz

Download URL raterkit-0.1.1.tar.gz
Size 208.9 kB
Tags Source
SHA-256 checksum
How to use checksums
5c159bfca8b11fcee1f0801bd4d2e312f66953f814a3c44a2639b95b2a1e1499
BLAKE2b-256 checksum
How to use checksums
3b42a86bec84e1923596bc5af6a1aacfa5381a2febb890be27180d0c95ae6ada
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 7, 2026.

Transparency log

Release files / raterkit-0.1.1-py3-none-any.whl

Download URL raterkit-0.1.1-py3-none-any.whl
Size 28.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
84888af386c4aa3b21f69fea159439f584d7fd385c7cc1f2c41b12ea9f354c45
BLAKE2b-256 checksum
How to use checksums
7bdb75321f716374e97aefaf8ce761e6ca39d894e15c5cfea4378abe71811bad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 7, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

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