scikit-learn & statsmodels - 哪个 R-squared 是正确的?



我想为将来选择最好的算法。我找到了一些解决方案,但我不明白哪个 R 平方值是正确的。

为此,我将数据分为测试和训练两部分,并在下面打印了两个不同的 R 平方值。

import statsmodels.api as sm
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
lineer = LinearRegression()
lineer.fit(x_train,y_train)
lineerPredict = lineer.predict(x_test)
scoreLineer = r2_score(y_test, lineerPredict)  # First R-Squared
model = sm.OLS(lineerPredict, y_test)
print(model.fit().summary()) # Second R-Squared

第一个 R 平方结果为 -4.28。
第二个 R 平方结果为 0.84

但我不明白哪个值是正确的。

可以说,在这种情况下,真正的挑战是确保将苹果与苹果进行比较。就您而言,您似乎没有。我们最好的朋友总是相关的文档,结合简单的实验。所以。。。

虽然scikit-learn的LinearRegression()(即你的第1个R平方)默认用fit_intercept=True(docs)拟合,但statsmodels的OLS(你的第2个R平方)并非如此;引用自文档:

默认情况下不包括拦截,应由用户添加。请参阅statsmodels.tools.add_constant

记住这个重要的细节,让我们用虚拟数据运行一些简单的实验:

import numpy as np
import statsmodels.api as sm
from sklearn.metrics import r2_score
from sklearn.linear_model import LinearRegression
# dummy data:
y = np.array([1,3,4,5,2,3,4])
X = np.array(range(1,8)).reshape(-1,1) # reshape to column
# scikit-learn:
lr = LinearRegression()
lr.fit(X,y)
# LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None,
#     normalize=False)
lr.score(X,y)
# 0.16118421052631582
y_pred=lr.predict(X)
r2_score(y, y_pred)
# 0.16118421052631582

# statsmodels
# first artificially add intercept to X, as advised in the docs:
X_ = sm.add_constant(X)
model = sm.OLS(y,X_) # X_ here
results = model.fit()
results.rsquared
# 0.16118421052631593

出于所有实际目的,scikit-learn和statsmodels生成的R平方的两个值是相同的

让我们更进一步,尝试一个没有拦截的scikit-learn模型,但是我们使用人工"截获"的数据X_我们已经构建用于statsmodels:

lr2 = LinearRegression(fit_intercept=False)
lr2.fit(X_,y) # X_ here
# LinearRegression(copy_X=True, fit_intercept=False, n_jobs=None,
#         normalize=False)
lr2.score(X_, y)
# 0.16118421052631593
y_pred2 = lr2.predict(X_)
r2_score(y, y_pred2)
# 0.16118421052631593

同样,R 平方与前面的值相同

那么,当我们"不小心"忘记解释统计模型OLS在没有拦截的情况下拟合的事实时会发生什么?我看看:

model3 = sm.OLS(y,X) # X here, i.e. no intercept
results3 = model2.fit()
results3.rsquared
# 0.8058035714285714

好吧,0.80 的 R 平方确实与具有截距的模型返回的 0.16R 平方相去甚远,可以说这正是您案例中发生的事情。

到目前为止一切顺利,我可以很容易地在这里完成答案;但这个和谐的世界确实有一点崩溃了:让我们看看当我们拟合两个模型而不进行拦截时会发生什么,并且初始数据X我们没有人为地添加任何拦截。我们已经拟合了上面的OLS模型,并得到了 0.80 的 R 平方;那么来自scikit-learn的类似模型呢?

# scikit-learn
lr3 = LinearRegression(fit_intercept=False)
lr3.fit(X,y) # X here
lr3.score(X,y)
# -0.4309210526315792
y_pred3 = lr3.predict(X)
r2_score(y, y_pred3)
# -0.4309210526315792

哎呀...!什么鬼??

似乎scikit-earn在计算r2_score时总是假设截距,无论是在模型中显式地(fit_intercept=True)还是隐式地在数据中(我们使用statsmodels的add_constant从上面的X生成X_的方式);在网上挖掘一点揭示了一个Github线程(在没有补救措施的情况下关闭),在那里确认情况确实是这样的。

[2021 年 12 月更新:有关在这种特殊情况下两个分数不同的更详细和深入的调查和解释(即两个模型都没有截距),请参阅 Flavia 的这个很棒的答案]

让我澄清一下,我上面描述的差异与您的问题无关:在您的情况下,真正的问题是您实际上是在比较苹果(带有拦截的模型)和橙子(没有拦截的模型)。


那么,为什么scikit-learn不仅在这种(公认的边缘)案例中失败,而且即使事实出现在Github问题中,它实际上也被冷对待?(另请注意,在上述线程中回复的scikit-learn核心开发人员随口承认">我对统计数据不是很熟悉"......

答案有点超出编码问题,例如SO主要涉及的问题,但可能值得在这里详细说明一下。

可以说,原因是整个R平方概念实际上直接来自统计学世界,其中重点是解释模型,并且在机器学习环境中几乎没有用处,其中重点显然是预测模型;至少AFAIK,除了一些非常入门的课程之外,我从来没有(我的意思是从来没有)。...)看到一个预测建模问题,其中 R 平方用于任何类型的性能评估;流行的机器学习介绍,如Andrew Ng在Coursera的机器学习,甚至懒得提及它,这也不是偶然的。而且,正如上面的 Github 线程中所述(强调添加):

特别是在使用测试集时,我有点不清楚 R^2 是什么意思。

我当然同意这一点。

至于上面讨论的边缘情况(包括或不包括拦截项?),我怀疑这听起来与现代深度学习从业者无关,其中神经网络模型中默认总是包含截距(偏差参数)的等效项......

请参阅交叉验证问题统计模型OLS和scikit线性回归之间的差异中的已接受(和高度赞成)答案,以更详细地讨论最后几行。由伟大的统计学家Cosma Shalizi的一些相关(负面)评论引发的R平方无用?中的讨论(和链接)也具有启发性并强烈推荐。

您似乎正在使用sklearn.metrics_r2_score。文件指出

最佳可能分数是 1.0,它可以是负数(因为模型可以任意差)

文档指向的维基百科文章指出,

当模型拟合的数据比水平超平面差时,可能会出现 0 到 1 范围之外的 R2 值。当选择了错误的模型,或者错误地应用了荒谬的约束时,就会发生这种情况。

出于这个原因,你有这样一个负r2_score的事实可能比你有一个相对好(但不是很好)的R^2统计量以另一种方式计算要重要得多。如果第一个分数表明您的模型选择不佳,则第二个统计量可能只是过度拟合的伪影。

正如您所注意到的,正如维基百科文章所指出的,"r 平方"或"R 平方"有多种定义。 但是,常见的都具有从01.它们通常是积极的,从名称的"平方"部分可以清楚地看出。(有关此一般规则的例外情况,请参阅维基百科文章。

您的"第一个 R 平方结果"是-4.28,它不在01之间,甚至不是正数。因此,它根本不是真正的"R 平方"。因此,请使用在正确范围内的"第二 R 平方结果"。

相关内容

  • 没有找到相关文章

最新更新