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

Mer*_*nık 1 python machine-learning linear-regression scikit-learn statsmodels

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

为此,我将数据分为两个作为测试和训练,我在下面打印了两个不同的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
Run Code Online (Sandbox Code Playgroud)

第一个R-Squared结果是-4.28.
第二个R-Squared结果是0.84

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

des*_*aut 9

可以说,在这种情况下真正的挑战是确保你比较苹果和苹果.在你的情况下,似乎你没有.我们最好的朋友总是相关的文档,结合简单的经验.所以...

虽然LinearRegression()默认情况下scikit-learn (即你的第一个R平方)与fit_intercept=True(docs)一致,但statsmodels的情况并非如此OLS(你的第二个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
Run Code Online (Sandbox Code Playgroud)

出于所有实际目的,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
Run Code Online (Sandbox Code Playgroud)

同样,R平方与先前的值相同.

那么,当我们"意外地"忘记考虑到statsmodel OLS没有拦截的事实时会发生什么呢?让我们来看看:

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

嗯,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
Run Code Online (Sandbox Code Playgroud)

噢...!有没有搞错??

似乎scikit-earn在计算时r2_score总是假设一个拦截,要么明确地在模型中(fit_intercept=True),要么隐含在数据中(我们X_X上面生成的方式,使用statsmodels' add_constant); 在网上挖掘一点点显示一个Github线程(没有补救措施关闭),确认情况确实如此.

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


那么,为什么scikit-learn不仅在这样一个(不可否认的边缘)案件中失败,而且即使在Github问题中出现这个事实,它实际上也是漠不关心的?(另请注意,在上述帖子中回复的scikit-learn核心开发人员随便承认" 我不是非常熟悉统计数据 "......).

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

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

特别是在使用测试集时,我对R ^ 2的含义有点不清楚.

我当然同意.

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

请参阅交叉验证问题中的接受(和高度投票)答案statsmodel OLS和scikit线性回归之间的差异,以便在这些最后几行进行更详细的讨论......