我想为未来选择最佳算法。我找到了一些解决方案,但我没有理解哪个R平方值是正确的。
为此,我将数据分为测试集和训练集,并在下面打印了两个不同的R平方值。
import statsmodels.api as smfrom sklearn.linear_model import LinearRegressionfrom sklearn.metrics import r2_scorelineer = LinearRegression()lineer.fit(x_train,y_train)lineerPredict = lineer.predict(x_test)scoreLineer = r2_score(y_test, lineerPredict) # First R-Squaredmodel = sm.OLS(lineerPredict, y_test)print(model.fit().summary()) # Second R-Squared
第一个R平方结果是-4.28。
第二个R平方结果是0.84
但我没有理解哪个值是正确的。
回答:
可以说,在这种情况下,真正的挑战是确保你比较的是同类事物。而在你的案例中,似乎并非如此。我们最好的朋友始终是相关文档,结合简单的实验。因此…
虽然scikit-learn的LinearRegression()
(即你的第一个R平方)默认情况下是通过fit_intercept=True
拟合的(文档),但statsmodels的OLS
(你的第二个R平方)却并非如此;引用自文档:
默认情况下不包含截距,用户应添加截距。请参见
statsmodels.tools.add_constant
。
牢记这一重要细节,让我们用虚拟数据进行一些简单的实验:
import numpy as npimport statsmodels.api as smfrom sklearn.metrics import r2_scorefrom 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.16118421052631582y_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_ hereresults = model.fit()results.rsquared# 0.16118421052631593
就所有实际用途而言,scikit-learn和statsmodels生成的这两个R平方值是相同的。
让我们更进一步,尝试一个没有截距的scikit-learn模型,但我们使用已经为statsmodels构建的带有“截距”的数据X_
:
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.16118421052631593y_pred2 = lr2.predict(X_)r2_score(y, y_pred2)# 0.16118421052631593
同样,R平方值与之前的值是相同的。
那么,当我们“意外地”忘记考虑statsmodels的OLS
是没有截距拟合的事实时,会发生什么?让我们看看:
model3 = sm.OLS(y,X) # X here, i.e. no interceptresults3 = model2.fit()results3.rsquared# 0.8058035714285714
嗯,0.80的R平方值确实与带有截距的模型返回的0.16相差甚远,可以说这正是你案例中发生的情况。
到目前为止,一切顺利,我可以很容易地在这里结束回答;但确实有一个点,这个和谐的世界会崩溃:让我们看看当我们拟合两个没有截距的模型并使用初始数据X
时会发生什么,我们没有在其中人为地添加任何截距。我们已经在上面拟合了OLS
模型,并得到了0.80的R平方值;那么scikit-learn的类似模型呢?
# scikit-learnlr3 = LinearRegression(fit_intercept=False)lr3.fit(X,y) # X herelr3.score(X,y)# -0.4309210526315792y_pred3 = lr3.predict(X)r2_score(y, y_pred3)# -0.4309210526315792
哦,不…!这是怎么回事?
看起来scikit-learn在计算r2_score
时,总是假设有一个截距,无论是在模型中明确(fit_intercept=True
)还是在数据中隐式(我们上面使用statsmodels的add_constant
从X
生成X_
的方式);在网上稍作研究就会发现一个Github讨论(未解决的问题已关闭),其中确认情况确实如此。
[更新 2021年12月:关于为什么在这特定情况下(即两个模型都没有拟合截距)两个分数不同的更详细和深入的调查和解释,请参见这个很棒的回答 by Flavia]
让我澄清,我上面描述的差异与你的问题毫无关系:在你的案例中,真正的问题是你在比较苹果(带截距的模型)和橘子(不带截距的模型)。
那么,为什么scikit-learn不仅在这种(可以说是边缘)情况下失败,而且当这一事实在Github问题中出现时,实际上被以冷漠对待?(还要注意,上述讨论中回复的scikit-learn核心开发者随便承认“我对统计不是很熟悉”…)。
答案有点超出了编码问题,就像SO主要讨论的那样,但这里可能值得详细说明一下。
可以说,原因是整个R平方的概念实际上直接来自统计学世界,那里重点在于解释性模型,而在机器学习环境中,它在预测模型中几乎没有用;至少据我所知,除了一些非常入门的课程外,我从未(我是说从未…)见过任何使用R平方来进行任何性能评估的预测建模问题;也不是偶然,流行的机器学习介绍,如Andrew Ng在Coursera上的机器学习课程,甚至不屑于提及它。而且,正如上述Github讨论中所指出的(强调增加):
特别是在使用测试集时,我有点不清楚R^2的含义是什么。
我当然同意这一点。
至于上面讨论的边缘情况(是否包括截距项?),我怀疑这对现代深度学习从业者来说听起来真的无关紧要,在神经网络模型中,截距的等价物(偏置参数)总是默认包含的…
参见Cross Validated问题中接受的(并高度投票的)回答statsmodel OLS与scikit线性回归的区别,以获得更多关于这些最后几点的详细讨论。由著名统计学家Cosma Shalizi的一些相关(负面)评论触发的讨论(和链接)在R平方是否无用?中也很有启发性,强烈推荐。