Official Task Variations (2/3): Make Scoring more Composable - #113
Official Task Variations (2/3): Make Scoring more Composable#113kargibora wants to merge 6 commits into
Conversation
| @@ -0,0 +1,143 @@ | |||
| """PairScore temperature calibration against human arena preferences.""" | |||
There was a problem hiding this comment.
This script should be self-contained. It was sitting on the runner.py which was making it harder to read; so its a simple refactor and not a new capability. It's only purpose is to use some rows to calculate a temperature.
| prefs, our_model_is_position_a, opponent_models, strict=True | ||
| ): | ||
| if _is_nan_pref(pref) or pref == 0.5: | ||
| if _is_nan_pref(pref): |
There was a problem hiding this comment.
Making nan pref's tie does not make sense. Tie is basically a indicator that both models perform same. ELO calculation for example makes these model ELO's closer to each other if they are tie. Thus it should be marked as NaN correctly.
| logger = get_logger(__name__) | ||
|
|
||
|
|
||
| class EloReport(Report): |
There was a problem hiding this comment.
Moved into report.py. Metric reporting is now contained within the metric, so they are re-usable. Report is just collection of key:values which we want to add to the result.
| # rebuilt from the (revision-pinned) arena, not persisted per run. | ||
| df_arena = arena_anchor_battles(df_arena_all) | ||
|
|
||
| df_results = pd.concat([df_llm_judge, df_arena], ignore_index=True) |
There was a problem hiding this comment.
No need for all the calibration code within the runner.
| battle_counts[row["model_b"]] = battle_counts.get(row["model_b"], 0) + 1 | ||
|
|
||
| pref_col = "pref" if use_soft else "pref_hard" | ||
| bootstrap_ratings: list[dict[str, float]] = [] |
There was a problem hiding this comment.
These are metrics. Not related to the runner.py. Thus they are moved into the classes which is going to use, in this case, BradleyTerryMetric which is basically ELO.
| ] | ||
| elo_mean = ( | ||
| float(np.mean(model_rating_values)) if model_rating_values else float("nan") | ||
| focal_is_a = pd.Series(our_model_is_position_a, dtype="bool") |
There was a problem hiding this comment.
Normalizes some column. I want to make the boundaries more clear so datasets are interchangable at the core (basically arena and pairwise pipelines almost same. The dataset includes a prompt and two responses, in arena it is between each model and in pairwise, it is between some baseline and other models. For now as we already normalized ELO datasets, this will allow us to use pairwise metrics easily (lenght contorlled etc).
| ) | ||
| elo_std = ( | ||
| float(np.std(model_rating_values)) if model_rating_values else float("nan") | ||
| metric_results = calculate_metrics( |
There was a problem hiding this comment.
Pay-off. This will handle any metrics. No need to change anything in the pipeline when we add newer metric.
| baseline_model: str | None = None | ||
| soft: bool = True | ||
|
|
||
| def __post_init__(self) -> None: |
There was a problem hiding this comment.
This allow us to construct the class without setting some default values in the init.
| return bool(positive.max() <= negative.min() or negative.max() <= positive.min()) | ||
|
|
||
|
|
||
| def _fit_length_model( |
There was a problem hiding this comment.
Estimate the effect of the length differences in predicting the winrate.
| "judge_max_out_tokens", | ||
| *instructions_df.columns, | ||
| } | ||
| missing_groups = sorted( |
There was a problem hiding this comment.
Allow us to group_by orientation for example (shows normal and swapped winrates), or any category/lang exist in the task (allow us to also report winrates per lang easily).
| ) | ||
|
|
||
|
|
||
| class Report(BaseModel, abc.ABC): |
fb02860 to
cdee3d8
Compare
cdee3d8 to
6bb0a65
Compare
| """One named calculation over a battle dataframe.""" | ||
|
|
||
| metric: str = Field(min_length=1) | ||
| group_by: tuple[str, ...] = () |
There was a problem hiding this comment.
group_by: [category, turn] normally means one grouping by the (category, turn) unique combination, however the executor calculates a separate breakdown for each field. Should we rename this to something like breakdown_by so isn't confused?
ErlisLushtaku
left a comment
There was a problem hiding this comment.
Just a small concern regarding the semantics of group_by.
f4d9e77 to
162bf13
Compare
Problem
Pairwise, Elo, and MT-Bench used separate scoring and reporting paths. Metric parameters were also split between task definitions and runner code. Adding a metric could therefore require changes in several pipelines.
Elo also handled fitting, bootstrap aggregation, calibration, and rendering inside its runner. This made the scoring boundary difficult to reuse.
Changes
calculate(battles)andrender(result)methods.available_metrics(),build_metric(),build_metrics(),calculate_metrics(), andrender_metrics().MetricSpecandScoringSpectask configuration.MetricSpec.parameters.judgearena/reports.py.judgearena/benchmarks/elo/calibration.py.Parsed preferences and Soft Elo
Soft Elo uses the continuous preference returned by the configured parser. Verdict, token-logprob, and custom parser preferences are not replaced with PairScore parsing.
Temperature adjustment and calibration remain specific to
PairScore. PairScore calibration reads the raw A/B scores stored inJudgeAnnotation.parsed.The scoring path is:
Metrics consume the numeric
prefcolumn. Structured parser evidence remains on the judge annotation and can be added to battle rows later when a metric has a defined use for it.Task configuration
Metrics and their parameters are selected in task YAML:
Runners use the same execution path:
Runtime-only values, such as a random number generator, remain explicit and are not serialized as metric configuration.
A new metric needs a configured metric class, its calculation and rendering methods, one internal registry entry, and a task YAML entry. It does not need a separate scoring implementation in each runner.
Tests
The tests cover shared metric construction, configuration validation, pairwise and MT-Bench battle rows, hard and soft Bradley-Terry behavior, PairScore calibration, generic continuous parser preferences, report rendering, and runtime dependencies.
Result:
312 passed.Estimated production changes
The new implementation is mainly the composable metric framework and length-controlled win-rate calculation.
Most of the movement comes from taking calibration, Bradley–Terry calculation, and report rendering out of the Elo runner. These were moved into dedicated metric, calibration, and report modules. The runner changes are therefore mostly extraction and integration rather than new calculation code.