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

scikit-learn & statsmodels - which R-squared is correct?

我想为未来选择最好的算法。我找到了一些解决方案,但我不明白哪个 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

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

正如您所说,the Wikipedia article 也指出,“r 平方”或“R 平方”有多种定义。但是,常见的都有属性,范围从01。它们通常是正的,从名称的“平方”部分可以清楚地看出。 (有关此一般规则的例外情况,请参阅维基百科文章。)

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

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

Best possible score is 1.0 and it can be negative (because the model can be arbitrarily worse)

文档引出的Wikipedia article指出

values of R2 outside the range 0 to 1 can occur when the model fits the data worse than a horizontal hyperplane. This would occur when the wrong model was chosen, or nonsensical constraints were applied by mistake.

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

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

尽管 scikit-learn 的 LinearRegression()(即您的第一个 R 平方)默认安装 fit_intercept=Truedocs), this is not the case with statsmodels' OLS (your 2nd R-squared); quoting from the docs:

An intercept is not included by default and should be added by the user. See 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 平方与之前的值相同

那么,当我们“不小心”忘记考虑 statsmodels OLS 没有截距的事实时会发生什么?让我们看看:

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

好吧,0.80 的 R 平方确实与模型 截距返回的 0.16 相差甚远,并且可以说这正是您的案例.

到目前为止一切顺利,我可以轻松地在这里完成答案;但是这个和谐的世界确实有一点崩溃了:让我们看看当我们在没有截距的情况下拟合两个模型并且使用我们没有人为添加任何截距的初始数据 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 thread(无补救关闭),确认情况确实是这样。

[UPDATE 2021 年 12 月:进行更详细和深入的调查,并解释为什么在这种特殊情况下两个分数不同(即两个模型都没有截距) ), 参见 作者 Flavia]

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


那么,为什么 scikit-learn 不仅在这种情况下(诚然 edge)失败了,而且即使事实出现在 Github 问题中,它实际上也是如此被冷漠对待? (另请注意,在上述主题中回复的 scikit-learn 核心开发人员随便承认“我对统计信息不是很熟悉”...)。

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

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

In particular when using a test set, it's a bit unclear to me what the R^2 means.

我当然同意。

至于上面讨论的边缘情况(包括或不包括截距项?),我怀疑这听起来对现代深度学习从业者来说真的无关紧要,在现代深度学习实践者中,截距(偏置参数)的等价物总是包含在神经网络模型中的默认值...

请参阅交叉验证问题 Difference between statsmodel OLS and scikit linear regression for a more detailed discussion along these last lines. The discussion (and links) in Is R-squared Useless? 中已接受(并高度赞成)的答案,由伟大的统计学家 Cosma Shalizi 的一些相关(负面)评论引发,也很有启发性,强烈推荐。