Skip to content

Fix numerical instability in PearsonCorrelation - #3830

Open
RangeshPandianPT wants to merge 1 commit into
pytorch:masterfrom
RangeshPandianPT:fix-pearson-correlation
Open

RangeshPandianPT wants to merge 1 commit into
pytorch:masterfrom
RangeshPandianPT:fix-pearson-correlation

Conversation

@RangeshPandianPT

Copy link
Copy Markdown

Fixes #3662

Description

Fixes numerical instability in PearsonCorrelation caused by
catastrophic cancellation in the variance and covariance calculations.

The previous implementation accumulated raw sums and squares, which
can become numerically unstable for inputs with large offsets.

This change uses batch-level mean, variance, and covariance statistics
combined with Chan's merge algorithm to maintain numerical stability
across multiple update() calls and distributed processes.

Changes

  • Replace raw sum and square accumulation with numerically stable statistics.
  • Use Chan's algorithm to merge statistics across multiple updates.
  • Use stable distributed aggregation with idist.all_gather().
  • Add regression coverage for large-offset values.
  • Add tests for multiple updates and different batch sizes.
  • Add coverage for zero-variance inputs.

Testing

Ran:

pytest tests/ignite/metrics/regression/test_pearson_correlation.py

Result:

  • 30 tests passed.
  • 26 tests skipped due to unavailable hardware configurations.
  • Distributed tests could not execute successfully in the local Windows environment because the required PyTorch distributed/libuv backend is unavailable.

The distributed test setup error is environment-specific and does not represent a test assertion failure.

Checklist

  • New tests are added for the bug fix.
  • Existing tests continue to pass.
  • No unrelated files are modified.

Copilot AI lite review requested due to automatic review settings August 27, 2026 20:26
@github-actions github-actions Bot added the module: metrics Metrics module label Aug 27, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates ignite.metrics.regression.PearsonCorrelation to avoid catastrophic cancellation by switching from raw sum/sum-of-squares accumulation to numerically stable streaming mean/variance/covariance (Chan-style merge), and adds/updates tests to cover the regression scenario.

Changes:

  • Replaced naive variance/covariance computation with stable batch statistics merged across update() calls.
  • Implemented distributed aggregation via idist.all_gather() and state merging.
  • Added regression/edge-case tests and updated accumulator-focused tests to reflect the new internal state.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
ignite/metrics/regression/pearson_correlation.py Switches to stable mean/variance/covariance accumulation and adds distributed merge logic in compute().
tests/ignite/metrics/regression/test_pearson_correlation.py Adds a large-offset regression test and updates internal-accumulator assertions to match the new implementation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +95 to +99
mean_x_B = y_pred.mean(dtype=self._double_dtype)
mean_y_B = y.mean(dtype=self._double_dtype)

y_pred_d = y_pred.to(self._double_dtype)
y_d = y.to(self._double_dtype)
Comment on lines +126 to 129
def compute(self) -> float:
if self._num_examples == 0:
raise NotComputableError("PearsonCorrelation must have at least one example before it can be computed.")

Comment on lines +102 to +124
offset = 1e8
y_true = torch.tensor([1., 2., 3., 4., 5.], dtype=torch.float64) + offset
y_pred = torch.tensor([1.1, 2.1, 3.1, 4.1, 5.1], dtype=torch.float64) + offset

# Test single batch
m = PearsonCorrelation(device=available_device)
m.update((y_pred, y_true))
assert m.compute() == pytest.approx(1.0, rel=1e-4)

# Test multiple update calls
# Test multiple update calls with batch size 1
m.reset()
for yp, yt in zip(y_pred, y_true):
m.update((yp.unsqueeze(0), yt.unsqueeze(0)))

assert m.compute() == pytest.approx(1.0, rel=1e-4)

# Test different batch sizes
m.reset()
m.update((y_pred[:2], y_true[:2]))
m.update((y_pred[2:], y_true[2:]))

assert m.compute() == pytest.approx(1.0, rel=1e-4)
Comment on lines +165 to +168
total_mean_x += delta_x * (n_B / total_n)
total_mean_y += delta_y * (n_B / total_n)
else:
total_n = self._num_examples
Comment on lines +126 to +130
# Test zero variance edge case (constant inputs)
m.reset()
y_true_zero_var = torch.tensor([1., 1., 1., 1., 1.], dtype=torch.float64) + offset
y_pred_zero_var = torch.tensor([2., 2., 2., 2., 2.], dtype=torch.float64) + offset

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

module: metrics Metrics module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Numerical instability in PearsonCorrelation due to naive variance formula

2 participants