【问题标题】:scikit-learn & statsmodels - which R-squared is correct?scikit-learn & statsmodels - 哪个 R 平方是正确的?
【发布时间】:2019-07-03 23:30:09
【问题描述】:

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

第一个 R 平方结果是 -4.28。
第二个 R 平方结果是 0.84

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

【问题讨论】:

    标签: python machine-learning scikit-learn linear-regression statsmodels


    【解决方案1】:

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

    虽然 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 的一些相关(负面)言论引发,也很有启发性,强烈推荐。

    【讨论】:

    • 这真的很有帮助。非常感谢
    • 这不是很清楚:如果你预测测试集的平均值,R2 为零(或者如果你使用在训练集上估计的平均值,它接近于零),如果你更糟,它低于零与预测平均值相比,如果您做出完美的预测,那就是一个。所以有点可以解释。它也与规模无关,因此可以跨数据集聚合。但我同意,我从未见过它在实践中被使用过。
    【解决方案2】:

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

    最好的分数是 1.0,它可以是负数(因为模型可以任意变坏)

    文档指向的Wikipedia article 指出

    当模型拟合数据比水平超平面差时,R2 值可能会超出 0 到 1 的范围。选择错误的模型时会发生这种情况,或者错误地应用了荒谬的约束。

    因此,您的 r2_score 为负这一事实可能比您以另一种方式计算的 R^2 统计量相对较好(但不是很好)更为重要。如果第一个分数表明您的模型选择很差,那么第二个统计量很可能只是过拟合的产物。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 维基百科文章指出有多种定义,其中一些采用负值。说第一个 R 平方根本不是一个真正的 R 平方是以一种 Wikipedia 文章没有的方式站在一边(尽管我倾向于同意你的观点,即任何称为 R 平方的东西都不是positive 是错误的名称,但这是该领域的术语)。但你是对的,只有第二个是真正标准的,所以 +1
      • @JohnColeman:我试图简要说明一些 R 平方定义会导致我的 cmets 和我的链接产生负值。正如您所说,我确实认为这些定义是非标准的。您的回答很好地涵盖了其他定义,并提供了必要且有用的上下文,因此为您 +1。
      • 可以说,使用第二个,因为它看起来更好是一个幼稚且糟糕的建议;甚至维基百科的文章也明确提到 R 平方可以是负数。这里真正的问题似乎是 OP 试图将苹果与橙子进行比较(即有和没有截距的模型); (-1)来自我,当然愿意纠正它,以防答案被编辑......
      猜你喜欢
      • 2017-12-19
      • 2021-04-25
      • 2021-05-29
      • 2018-06-05
      • 2020-09-12
      • 2021-09-14
      • 2014-10-01
      相关资源
      最近更新 更多