You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

sklearn r2_score与PyTorch MSELoss计算R2得分存在差异的原因?

sklearn r2_score与PyTorch MSELoss计算R²得分的差异原因及解决方法

发现sklearn的r2_score()函数返回的R²得分,与通过PyTorch的MSELoss()结合statistics.variance()计算得到的结果存在细微差异,且sklearn的结果始终略低。以下是复现差异的代码:

from sklearn.metrics import r2_score
from torch.nn import MSELoss
import statistics 
import random
import torch
import numpy as np 

actuals = random.sample(range(1, 50), 40)

preds = []
for value in actuals:
    pred = value * 0.70
    preds.append(pred)

loss = MSELoss()
mse = loss(torch.tensor(preds), torch.tensor(actuals))

r2 = 1 - mse / statistics.variance(actuals)
score = r2_score(actuals, preds)

print(f'R2 Score using (PyTorch) MSELoss: {r2}')
print(f'R2 Score using (sklearn) r2_score: {score}')

示例输出:

R2 Score using (PyTorch) MSELoss: 0.6261289715766907
R2 Score using (sklearn) r2_score: 0.6165425269729996

差异原因

这和张量输入无关,核心是样本方差与总体方差的计算区别:

  • statistics.variance()计算的是样本方差,分母为n-1(n是样本量);
  • PyTorch的MSELoss默认计算的是平均MSE,分母为n;
  • sklearn的r2_score严格遵循R²的定义:$R^2 = 1 - \frac{残差平方和}{总平方和}$,其中总平方和等于n * 总体方差(分母为n),残差平方和等于n * 平均MSE。

你的计算中,用了样本方差(分母n-1)除以平均MSE(分母n),导致最终结果被放大,公式推导如下:
$$
R^2_{你的计算} = 1 - \frac{\frac{1}{n}\sum(y-\hat{y})2}{\frac{1}{n-1}\sum(y-\bar{y})2} = 1 - \frac{n-1}{n} \cdot \frac{\sum(y-\hat{y})2}{\sum(y-\bar{y})2}
$$
而sklearn的计算是:
$$
R^2_{sklearn} = 1 - \frac{\sum(y-\hat{y})2}{\sum(y-\bar{y})2}
$$
两者的差异正好是$\frac{n-1}{n}$的比例,用你的示例数值验证:$(1 - 0.6261) * \frac{40}{39} ≈ 0.3835$,$1 - 0.3835 = 0.6165$,完全匹配sklearn的结果。

解决方法

只需让MSE和方差的计算分母保持一致即可:

方法1:使用总体方差代替样本方差

用np.var(actuals, ddof=0)计算总体方差(分母为n),和MSELoss的平均损失匹配:

from sklearn.metrics import r2_score
from torch.nn import MSELoss
import numpy as np
import random
import torch

actuals = random.sample(range(1, 50), 40)
preds = [val * 0.70 for val in actuals]

loss = MSELoss()
mse = loss(torch.tensor(preds), torch.tensor(actuals))
# 使用总体方差(ddof=0)
r2 = 1 - mse / np.var(actuals, ddof=0)
score = r2_score(actuals, preds)

print(f'R2 Score using (PyTorch) MSELoss + 总体方差: {r2}')
print(f'R2 Score using (sklearn) r2_score: {score}')

方法2:调整MSE计算为除以n-1

将PyTorch的MSELoss设置为求和模式,再除以n-1,和样本方差匹配:

from sklearn.metrics import r2_score
from torch.nn import MSELoss
import statistics
import random
import torch

actuals = random.sample(range(1, 50), 40)
preds = [val * 0.70 for val in actuals]

loss = MSELoss(reduction='sum')
mse_sum = loss(torch.tensor(preds), torch.tensor(actuals))
# MSE除以n-1
mse = mse_sum / (len(actuals)-1)
r2 = 1 - mse / statistics.variance(actuals)
score = r2_score(actuals, preds)

print(f'R2 Score using (PyTorch) MSELoss(求和)+样本方差: {r2}')
print(f'R2 Score using (sklearn) r2_score: {score}')

两种方法都能让PyTorch计算的R²和sklearn结果完全一致。


内容的提问来源于stack exchange,提问作者Pau Vila Soler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 00:52:30