scikit-learn与statsmodels – 哪个R平方值是正确的?

我想为未来选择最佳算法。我找到了一些解决方案,但我没有理解哪个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_constantX生成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平方是否无用?中也很有启发性,强烈推荐。

Related Posts

使用LSTM在Python中预测未来值

这段代码可以预测指定股票的当前日期之前的值,但不能预测…

如何在gensim的word2vec模型中查找双词组的相似性

我有一个word2vec模型,假设我使用的是googl…

dask_xgboost.predict 可以工作但无法显示 – 数据必须是一维的

我试图使用 XGBoost 创建模型。 看起来我成功地…

ML Tuning – Cross Validation in Spark

我在https://spark.apache.org/…

如何在React JS中使用fetch从REST API获取预测

我正在开发一个应用程序,其中Flask REST AP…

如何分析ML.NET中多类分类预测得分数组?

我在ML.NET中创建了一个多类分类项目。该项目可以对…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注