可以说,在这种情况下,真正的挑战是确保将苹果与苹果进行比较。在你的情况下,你似乎没有。我们最好的朋友总是相关的文档,结合简单的实验。所以...
虽然 scikit-learn 的 LinearRegression()(即您的第一个 R 平方)默认适合 fit_intercept=True (docs),但 不是 statsmodels 的 OLS 的情况(你的第二个 R 平方);引用docs:
默认情况下不包含拦截,应由用户添加。见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-squared 值相同。
让我们更进一步,尝试一个没有拦截的 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) 还是隐式在数据中(我们生成的方式X_ 来自上述X,使用statsmodels'add_constant);在网上挖掘了一下,发现Github thread(没有补救措施就关闭了),确认情况确实如此。
[更新 2021 年 12 月:如需更详细和深入的调查和解释为什么在这种特殊情况下两个分数不同(即两个模型都没有截距),请参阅 @987654324 @弗拉维亚]
让我澄清一下,我上面描述的差异与您的问题无关:在您的情况下,真正的问题是您实际上是在比较苹果(具有截距的模型)和橙子(没有截距的模型)。
那么,为什么 scikit-learn 不仅在这种(诚然 edge)情况下失败了,而且即使事实出现在 Github 问题中,它实际上也被冷漠? (另请注意,在上述线程中回复的 scikit-learn 核心开发人员漫不经心地承认“我对统计数据不是很熟悉”......)。
答案有点超出编码问题,例如 SO 主要涉及的问题,但在这里可能值得详细说明。
可以说,原因是整个 R 平方概念实际上直接来自统计世界,其中强调解释性模型,它在机器学习环境中几乎没有用处,其中重点明显是预测模型;至少 AFAIK,除了一些非常入门的课程之外,我从来没有(我的意思是 从来没有 ...)看到 R 平方用于任何类型的性能评估的预测建模问题;流行的机器学习介绍,例如 Andrew Ng 在 Coursera 的 Machine Learning,甚至懒得提及它也不是偶然的。并且,正如上面的 Github 线程中所述(强调添加):
特别是在使用 test 集时,我有点不清楚 R^2 的含义。
我当然同意。
至于上面讨论的边缘情况(包括或不包括截距项?),我怀疑这听起来与现代深度学习从业者无关,在现代深度学习从业者中,截距(偏置参数)的等价物总是默认包含在神经网络模型...
请参阅交叉验证问题Difference between statsmodel OLS and scikit linear regression 中已接受(且高度赞成)的答案,以了解有关最后几行的更详细讨论。 Is R-squared Useless? 中的讨论(和链接)由伟大的统计学家 Cosma Shalizi 的一些相关(负面)言论引发,也很有启发性,强烈推荐。