Skip to content

Add zero-fraction inference metric#1323

Open
mcgibbon wants to merge 4 commits into
mainfrom
feature/dry-fraction-aggregator
Open

Add zero-fraction inference metric#1323
mcgibbon wants to merge 4 commits into
mainfrom
feature/dry-fraction-aggregator

Conversation

@mcgibbon

@mcgibbon mcgibbon commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Adds a default-off inference metric reporting, per variable, the area-weighted fraction of cells whose value is <= threshold (default 0) for the prediction, plus its difference from the target. For precipitation (PRATEsfc) with the default threshold this is the fraction of exactly-zero (dry) cells — a scalar the existing RMSE/bias/histogram metrics do not surface directly, useful for diagnosing a model's dry-cell / drizzle behaviour against the target distribution.

This is the metric used in the keep_gradient_through_clamps precipitation validation (companion to #1322); opening as a draft to decide whether the zero-fraction diagnostic is worth keeping in main as a general-purpose metric.

Changes:

  • fme.ace.aggregator.inference.zero_fraction.ZeroFractionMetricConfig / ZeroFractionAggregator: new default-off metric. Configurable variables (required when enabled — raises in __post_init__ otherwise), threshold, per_variable_threshold (per-variable override of the global threshold), and name (default zero_threshold_fraction). Log keys are <name>/gen/<var> and <name>/gen_minus_target/<var> (the standalone target fraction is computed internally for the difference but not logged).

  • fme.ace.aggregator.inference.main: register zero_fraction on InferenceEvaluatorAggregatorConfig (opt-in).

  • Tests added

  • If dependencies changed, "deps only" image rebuilt and "latest_deps_only_image.txt" file updated

Add a default-off DryFractionMetricConfig / DryFractionAggregator that
reports the area-weighted fraction of cells <= a threshold (default 0,
i.e. exactly-dry) per variable, for prediction, target, and their
difference. For precipitation this is the dry-cell fraction, which the
existing RMSE/bias/histogram metrics do not surface directly. Wired into
InferenceEvaluatorAggregatorConfig as an opt-in metric.


@dataclasses.dataclass
class DryFractionMetricConfig:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call this ZeroFractionMetricConfig, or not DryFraction. Update all documentation you've added accordingly.

Parameters:
variables: when set, only compute the metric for these variables
(typically ``["PRATEsfc"]``).
threshold: a cell counts as "dry" when its value is ``<= threshold``.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add per_variable_threshold, dict[str, float] = (field defaulting to dict) to be able to specify the threshold per-variable.

strict: raise if the metric can't be built.
"""

variables: list[str] | None = None

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default the list of variables to an empty list, the user should specify the variables they want. Raise an exception if enabled=True but no variables are given, in the post-init.


variables: list[str] | None = None
threshold: float = 0.0
name: str = "dry_fraction"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
name: str = "dry_fraction"
name: str = "zero_threshold_fraction"

…re variables when enabled

- Rename DryFraction* -> ZeroFraction* (file, config, aggregator, metric
  name default 'zero_threshold_fraction', config field 'zero_fraction'),
  updating docstrings.
- Add per_variable_threshold (dict[str, float]) overriding the global
  threshold per variable.
- Default variables to an empty list and raise in __post_init__ if the
  metric is enabled with no variables specified.
- Drop the redundant metric-name component from the log keys (the
  framework already prefixes with the metric name), so keys are
  '<metric>/gen/<var>' rather than the previously doubled form.
@mcgibbon

Copy link
Copy Markdown
Contributor Author

Thanks — addressed in the latest commit:

  • Rename to ZeroFraction: dry_fraction.pyzero_fraction.py, DryFractionMetricConfig/DryFractionAggregatorZeroFractionMetricConfig/ZeroFractionAggregator, config field dry_fractionzero_fraction, and docstrings updated. name default is now "zero_threshold_fraction".
  • per_variable_threshold: dict[str, float]: added (defaults to {}); overrides the global threshold for the named variables, with a test covering the override + fallback.
  • variables required when enabled: default is now an empty list, and __post_init__ raises if enabled=True with no variables given (test added).
  • While renaming I also dropped the redundant metric-name component the aggregator was prepending to its log keys — the framework already prefixes with the metric name (label=), which was producing a doubled …/dry_fraction/dry_fraction/…. Keys are now <metric>/gen/<var> etc.

All aggregator + inference-main tests pass; pre-commit (ruff/mypy) clean.

@mcgibbon mcgibbon changed the title Add dry-fraction inference metric Add zero-fraction inference metric Jun 26, 2026
"""

variables: list[str] = dataclasses.field(default_factory=list)
threshold: float = 0.0

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question for reviewer: should we call this eps?

Comment on lines +131 to +132
for name, value in target_means.items():
logs[f"target/{name}"] = value

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the target value, just keep gen/ and gen_minus_target/.

Report only gen/ and gen_minus_target/ per variable; the target fraction
is still computed internally for the difference but no longer logged on
its own. Update docstrings and tests accordingly.
@mcgibbon mcgibbon marked this pull request as ready for review June 26, 2026 20:59
- Export ZeroFractionMetricConfig from fme.ace (it is in the MetricConfig
  union, so the symbol-import and metric-documentation tests require it).
- Add its autoclass entry to docs/evaluator_config.rst.
- Regenerate docs/default-aggregator-config.yaml for the new zero_fraction
  default block.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant