Skip to content

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

@Prathamesh8989

Description

@Prathamesh8989

The current implementation of PearsonCorrelation in ignite.metrics.regression uses the naive "sum of squares" algorithm to compute variance and covariance:

[
Var(X) = E[X^2] - (E[X])^2
]

Although mathematically valid, this approach is numerically unstable when the input values have large magnitudes relative to their variance.

This leads to catastrophic cancellation, where two very large numbers (E[X^2] and (E[X])^2) are subtracted, causing loss of precision in float32. As a result, the metric can produce incorrect results, such as returning 0.0 when the true correlation is ≈ 0.707.


Steps to Reproduce

import torch
from ignite.metrics.regression import PearsonCorrelation
import numpy as np

# Magnitude that triggers precision loss in float32
offset = 1e8 

y_true = torch.tensor([1.0, 2.0, 3.0, 4.0, 5.0]) + offset
y_pred = torch.tensor([1.1, 2.1, 3.1, 4.1, 5.1]) + offset

metric = PearsonCorrelation()
metric.update((y_pred, y_true))
ignite_res = metric.compute()

# Ground truth using PyTorch's stable corrcoef
combined = torch.stack([y_pred, y_true])
torch_res = torch.corrcoef(combined)[0, 1].item()

print(f"Offset used: {offset}")
print(f"Ignite Result: {ignite_res}")
print(f"Torch Ground Truth: {torch_res}")

actual output

Offset used: 100000000.0
Ignite Result: 0.0
Torch Ground Truth: 0.7071067690849304

Expected Behavior

The Pearson correlation metric should be invariant to constant offsets in the input data.

Therefore, the result should be consistent with numerically stable implementations such as:

  • torch.corrcoef
  • scipy.stats.pearsonr

Expected Result

≈ 0.7071067

Proposed Fix

To improve numerical stability, the implementation can be updated as follows:

Use a Numerically Stable Online Algorithm

Implement Welford’s Online Algorithm (or a similar one-pass algorithm) to compute:

  • Mean
  • Variance
  • Covariance

incrementally.


Use Higher Precision Accumulators

Maintain internal accumulators using torch.float64 to prevent precision loss during batch updates.


Ensure Compatibility with Ignite Metrics

The updated implementation should still support batch-wise streaming updates used by Ignite metrics.


Benefit

These changes will prevent catastrophic cancellation and ensure correct results even when the data has:

  • Large offsets
  • High magnitude values

Hi @vfdev-5, I would like to work on a PR to resolve this issue if this looks good to you!

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions