Scikit-learn: 由cross_val_score返回时,MSE为负

创建于 2013-09-12  ·  58评论  ·  资料来源: scikit-learn/scikit-learn

sklearn.cross_validation.cross_val_score返回的均方误差始终为负。 虽然这是一个经过设计的决策,以便可以在给定某些超参数的情况下最大程度地使用此函数的输出,但是当直接使用cross_val_score时,这非常令人困惑。 至少我问自己一个平方的平均值可能是负数,并认为cross_val_score无法正常工作或未使用提供的度量。 仅在研究了sklearn源代码后,我才意识到该标志被翻转了。

在scorer.py的make_scorer中提到了此行为,但是在cross_val_score中没有提及,我认为应该这样做,因为否则会使人们认为cross_val_score无法正常工作。

API Bug Documentation

最有用的评论

也许小结会解决问题

所有58条评论

你指的是

greater_is_better : boolean, default=True

Whether score_func is a score function (default), meaning high is good, 
or a loss function, meaning low is good. In the latter case, the scorer 
object will sign-flip the outcome of the score_func.

http://scikit-learn.org/stable/modules/generation/sklearn.metrics.make_scorer.html中
? (仅供参考)

我同意可以在cross_val_score文档中更加清楚

感谢您的举报

的确,在进行记分器重构时,我们忽略了该问题。 以下是非常违反直觉的:

>>> import numpy as np
>>> from sklearn.datasets import load_boston
>>> from sklearn.linear_model import RidgeCV
>>> from sklearn.cross_validation import cross_val_score

>>> boston = load_boston()
>>> np.mean(cross_val_score(RidgeCV(), boston.data, boston.target, scoring='mean_squared_error'))
-154.53681864311497

/ cc @larsmans

顺便说一句,我不同意这是一个文档问题。 cross_val_score应该返回带有与计分名称相匹配的符号的值。 理想情况下, GridSearchCV(*params).fit(X, y).best_score_应该保持一致。 否则,API将非常令人困惑。

我也同意,更改返回实际MSE而不更改符号的方法将是更好的选择。

计分器对象可以只存储greater_is_better标志,并且无论何时使用计分器,都可以在需要的情况下翻转符号,例如在GridSearchCV

我同意我们这里存在可用性问题,但是我不同意@ogrisel的解决方案,即我们应该

返回带有与评分名称匹配的符号的值

因为从长远来看,这是不可靠的破解。 如果有人用mse类的名称定义自定义计分器怎么办? 如果他们确实遵循命名模式,但将计分器包裹在更改名称的装饰器中怎么办?

计分器对象可以只存储Greater_is_better标志,并且无论何时使用计分器,都可以在需要的情况下翻转符号,例如在GridSearchCV中。

这是评分员最初在0.13和0.14版本之间进行开发时所做的事情,这使他们的定义变得更加困难。 这也使代码难以遵循,因为greater_is_better属性似乎在计分器代码中消失了,只是重新出现在网格搜索代码的中间。 需要一个特殊的Scorer类来完成一些理想的事情,一个简单的函数就可以完成。

我相信,如果我们要优化分数,那么分数应为_maximized_。 为了方便用户使用,我认为我们可以引入一个参数score_is_loss["auto", True, False] ,该参数仅更改得分的_display_并可以使用基于内置名称的启发式方法。

那是仓促的回应,因为我必须下车。 我所说的“显示”实际上是cross_val_score的返回值。 我认为计分员应该简单统一,算法应始终保持最大化。

这的确在内置和自定义评分器之间引入了不对称性。

Ping @GaelVaroquaux。

我喜欢score_is_loss解决方案,或有类似效果的解决方案。.与记分名称相匹配的符号更改似乎难以维护,可能会导致问题,因为@larsmans提到

结论是什么,我们应该寻求哪种解决方案? :)

@tdomhan @jaquesgrobler @larsmans您知道这是否也适用于r2吗? 我注意到的是, r2成绩被返回GridSearchCV也是大多是负面的ElasticNetLassoRidge

R²可以是正数,也可以是负数,而负数仅表示模型的性能非常差。

IIRC @GaelVaroquaux支持在greater_is_better=False时返回负数。

r2是一个得分函数(越大越好),因此,如果您的模型良好,则应该为正值-但这是为数不多的性能指标之一,实际上它可能是负值,意味着比0差。

在这个问题上有什么共识? 我认为cross_val_score是一种评估工具,而不是模型选择工具。 因此,它应该返回原始值。

我可以在PR#2759中进行修复,因为所做的更改使修复起来非常容易。 诀窍是不要先翻转符号,而要在进行网格搜索时访问计分器上的greater_is_better属性。

在这个问题上有什么共识? 我认为cross_val_score是
一种评估工具,而不是模型选择工具。 因此它应该返回
原始值。

特殊情况是行为的变化是软件问题的根源。

我只是认为我们应该在列表中将“ mse”重命名为“ negated_mse”
可接受的计分字符串。

如果有人用mse之类的名称定义自定义计分器怎么办? 如果他们确实遵循命名模式,但将计分器包裹在更改名称的装饰器中怎么办?

我不认为@ogrisel建议使用名称匹配,只是为了与原始指标保持一致。 如果我错了@ogrisel,请纠正我。

我只是认为我们应该在可接受的评分字符串列表中将“ mse”重命名为“ negated_mse”。

如果您不了解scikit-learn的内部知识,那是完全不直观的。 如果您必须像这样弯曲系统,我认为这表明存在设计问题。

如果您不了解scikit-learn的内部知识,那是完全不直观的。
如果您必须像这样弯曲系统,我认为这表明存在
设计问题。

我不同意。 人类通过很多先验知识来理解事物,
上下文。 它们几乎是系统的。 试图将其嵌入软件中
给出类似一组特殊情况的购物清单。 它不仅使
软件难以维护,但这也意味着没有
请记住,这些异常会导致令人惊讶的行为并编写越野车
使用该库的代码。

您要考虑什么特殊情况?

为了清楚起见,我认为存储在GridSearchCV对象中的交叉验证得分也应该是原始值(不带符号翻转)。

引入了AFAIK,翻转符号是为了使网格搜索实现更加简单,但不应影响可用性。

您要考虑什么特殊情况?

好吧,事实上,对于某些指标而言,更好的是,而对于另一些指标
相反。

AFAIK,翻转标志被引入以便进行网格搜索
实施稍微简单一点,但是不应该影响
可用性。

这不是关于网格搜索,而是关于关注点分离:分数
需要在不了解它们的情况下可用,否则代码
处理它们的特殊性将传播到整个代码库。 有
已经有很多计分代码。

但这在某种程度上将问题推迟到了用户代码上。 没有人愿意绘制“负MSE”图,因此用户将不得不在其代码中翻转符号。 这很不方便,尤其是对于多指标交叉验证报告(PR#2759),因为您需要单独处理每个指标。 我想知道我们能否同时兼顾两者:通用代码和直观结果。

但这在某种程度上将问题推迟到了用户代码上。 没人要
绘制“否定的MSE”,以便用户必须将标志翻转回他们的
码。

当然不是世界末日。 请注意,在阅读论文或
在看演示文稿时,我遇到了同样的问题:当图形不是
做得好,我花了一点时间和精力来尝试
判断更大还是更好。

这很不方便,尤其是对于多指标交叉验证
报告(PR#2759),因为您需要分别处理每个指标。

为什么。 如果您只是接受它总是越大越好,它会使
一切都变得容易,包括结果的解释。

我想知道我们是否可以兼得两者:通用代码和
直观的结果。

风险是拥有非常复杂的代码,这会使我们的维护速度变慢
和发展。 Scikit学习正在增加体重。

如果您只是接受它总是越大越好

她是这样说的 :)

更严重的是,我认为这使人们感到困惑的原因之一是因为cross_val_score的输出与指标不一致。 如果我们遵循您的逻辑,则sklearn.metrics中的所有指标都应遵循“越大越好”的原则。

她是这样说的 :)

好一个!

更严重的是,我认为这使人们感到困惑的原因之一是
cross_val_score的输出与指标不一致。 要是我们
遵循您的逻辑,sklearn.metrics中的所有指标都应遵循“更大
更好”。

同意这就是为什么我喜欢更改名称的想法:它会弹出
在人们的眼中。

更严重的是,我认为这使人们感到困惑的原因之一是因为cross_val_score的输出与指标不一致。

这反过来使scoring看起来比实际更神秘。

尝试进行线性回归时,今天在0.16.1中受到了限制。 虽然对于分类器而言,分数的迹象显然不再翻转了,但对于线性回归它仍然翻转了。 更令人困惑的是,LinearRegression.score()返回分数的非翻转版本。

我建议使所有这些保持一致,并返回线性模型的非符号翻转得分。

例:

from sklearn import linear_model
from sklearn.naive_bayes import GaussianNB
from sklearn import cross_validation
from sklearn import datasets
iris = datasets.load_iris()
nb = GaussianNB()
scores = cross_validation.cross_val_score(nb, iris.data, iris.target)
print("NB score:\t  %0.3f" % scores.mean() )

iris_reg_data = iris.data[:,:3]
iris_reg_target = iris.data[:,3]
lr = linear_model.LinearRegression()
scores = cross_validation.cross_val_score(lr, iris_reg_data, iris_reg_target)
print("LR score:\t %0.3f" % scores.mean() )

lrf = lr.fit(iris_reg_data, iris_reg_target)
score = lrf.score(iris_reg_data, iris_reg_target)
print("LR.score():\t  %0.3f" % score )

这给出:

NB score:     0.934    # sign is not flipped
LR score:    -0.755    # sign is flipped
LR.score():   0.938    # sign is not flipped

交叉验证会翻转模型的所有征兆,越大越好。 我仍然不同意这个决定。 我认为它的主要支持者是@GaelVaroquaux甚至@mblondel [我记得您重构

哦,没关系,所有讨论都在上面。
我觉得默认情况下,在mse和r2中翻转符号比较不直观:-/

@Huitzilo GaussianNB是分类器,并使用准确性作为默认计分器。 LinearRegression是一个回归器,并使用r2得分作为默认得分器。 第二个分数是负数,但请记住r2分数可以是负数。 此外,虹膜是一个多类数据集。 因此,目标是绝对的。 您不能使用回归器。

是的,我对发生的事情有些困惑,r2没有翻转……只有mse会。

也许解决整个问题的方法是将事物negmse重命名?

@mblondel当然你是对的,对不起。 我只是很快拍了个回归的例子,由于对虹膜数据的过分自信,我认为从其他角度预测特征4会起作用(R2为正)。 但是,它没有负R2。 这里没有翻转的迹象。 好。 我的错。

不过,我从cross_val_score获得的MSE中的符号仍然翻转。

也许只有我一个人,但是我发现这种不一致使我很困惑(这就是让我陷入这个问题的原因)。 为什么应将MSE翻转而不是R2?

也许只有我一个人,但是我发现这种不一致使我很困惑(这就是让我陷入这个问题的原因)。 为什么应将MSE翻转而不是R2?

因为分数的语义越高越好。 高MSE不好。

也许小结会解决问题

@amueller我同意,使符号在scoring参数的名称上显式翻转绝对可以避免混淆。

也许[1]中的文档甚至可以更明确地说明符号在某些分数上是如何翻转的。 就我而言,我需要快速的信息,只查看了3.1.1.1下的表格,但没有阅读文本(这说明了“越大越好”的原则)。 恕我直言,在3.1.1.1下的表中添加有关mse,中位数和均值绝对误差的注释,表明它们的取反,将对您有很大帮助,而无需更改实际代码。

[1] http://scikit-learn.org/stable/modules/model_evaluation.html#scoring -parameter

我遇到了一个非常有趣的案例:

from sklearn.cross_validation import cross_val_score
model = LinearRegression()
scores = cross_val_score(model, X, target, cv=2, scoring='r2')
scores

结果是

array([-0.17026282, -2.21315179])

对于同一数据集,以下代码

model = LinearRegression()
model.fit(X, target)
prediction = model.predict(X)
print r2_score(target, prediction)

产生合理的价值

0.353035789318

线性回归模型(带截距)的AFAIK无法获得R ^ 2> 1或R ^ 2 <0

因此,cv结果看起来不像带有翻转符号的R ^ 2。 我在某个时候错了吗?

r2可以为负(对于不良模型)。 不能大于1。

您可能过拟合了。 尝试:

from sklearn.cross_validation import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, target, test_size=0.2, random_state=0)
model = LinearRegression()
model.fit(X_train, y_train)
pred_train = model.predict(X_train)
print("train r2: %f" % r2_score(y_train, pred_train))

pred_test = model.predict(X_test)
print("test r2: %f" % r2_score(y_test, pred_test))

尝试使用用于控制随机拆分的random_state整数种子的其他值。

也许小结会解决问题

为“ neg_mse” +1(我认为下划线使内容更易读)。

这样可以解决所有问题吗? 还有其他分数更大不是更好吗?

有:

  • log_loss
  • mean_absolute_error
  • median_absolute_error

根据doc/modules/model_evaluation.rst ,这应该是全部。

我猜是hinge_loss吗?

在所有这些损失上加上neg_前缀会感到很尴尬。

一个想法是返回原始分数(不带符号翻转),而不是返回ndarray,我们返回一个类,该类使用诸如best()arg_best()best_sorted() 。 这样,结果就不足为奇了,我们有方便的方法来检索最佳结果。

没有铰链丢失的评分器(而且我从未见过将其用于评估)。

计分器不返回numpy数组,而是返回浮点数,对吗?
我们可以返回一个具有自定义“>”但看上去像个float的得分对象。
与以前的解决方案相比,这对我来说更让人为难,后者是用一个记号“ lower_is_better”标记记分器,然后在GridSearchCV中使用它。

cross_val_score返回一个数组。

实际上,由cross_val_score返回的分数通常不需要排序,只需取平均值即可。

另一个想法是一个加sorted方法_BaseScorer

my_scorer = make_scorer(my_metric, greater_is_better=False)
scores = my_scorer.sorted(scores)  # takes into account my_scorer._sign
best = scores[0]

cross_val_score返回一个数组,但计分员返回一个浮点数。 我觉得在cross_val_score具有特定的逻辑会很奇怪,因为您希望在GridSearchCV和所有其他CV对象中具有相同的行为。

您还需要一个argsort方法,因为在GridSearchCV中,您需要最佳分数和最佳索引。

如何通过scikit-learn实现“根据控制问题估计工人错误的均值和方差,然后在去除预测的估计偏差后计算加权平均值”?

IIRC我们在sprint中讨论了这一问题(去年夏天?!),并决定使用neg_mse (或者是neg-mse )并弃用所有我们现在带有负号的得分手/字符串。
这仍然是共识吗? 我们应该在0.18之前这样做。
@GaelVaroquaux @agramfort @jnothman @ogrisel @raghavrv

是的,我们同意neg_mse AFAIK

那是neg_mse

我们还需要:

  • neg_log_loss
  • neg_mean_absolute_error
  • neg_median_absolute_error

模型= Sequential()
keras.layers.Flatten()
model.add(密集(11,input_dim = 3,kernel_initializer = keras.initializers.he_normal(seed = 2),
kernel_regularizer = regularizers.l2(2)))
keras.layers.LeakyReLU(alpha = 0.1)
model.add(密集(8,kernel_initializer = keras.initializers.he_normal(seed = 2)))
keras.layers.LeakyReLU(alpha = 0.1)
model.add(密集(4,kernel_initializer = keras.initializers.he_normal(seed = 2)))
keras.layers.LeakyReLU(alpha = 0.1)
model.add(密集(1,kernel_initializer = keras.initializers.he_normal(seed = 2)))
keras.layers.LeakyReLU(alpha = 0.2)
adag = RMSprop(lr = 0.0002)
model.compile(loss = losses.mean_squared_error,
优化程序= adag

历史= model.fit(X_train,Y_train,epochs = 2000,
batch_size = 20,随机播放= True)

如何交叉验证以上代码? 我想在此使用一种交叉验证方法。

@shreyassks这不是您提出问题的正确地点,但我会检查一下: https : //keras.io/scikit-learn-api 。 用scikit-learn估算器包装您的网络,然后使用w / model_selection.cross_val_score

是。 我完全同意! Brier_score_loss也发生了这种情况,使用Brier_score_loss可以很好地工作,但是当它来自GridSearchCV时,会产生混淆,负的Brier_score_loss返回。 至少,最好输出类似以下内容,因为Brier_score_loss是损失(越低越好),此处的计分功能将符号翻转为负值。

这个想法是cross_val_score应该完全集中在结果的绝对值上。 据我所知,cross_val_score中为MSE(均方误差)获得的负号(-)的重要性尚未预定义。 让我们等待解决该问题的sklearn的更新版本。

对于回归用例:
model_score = cross_val_score(model,df_input,df_target,scoring ='neg_mean_squared_error',cv = 3)
我得到的价值观是:

SVR:
[-6.20938025 -1.397376 -1.94519]
-3.183982080147279

线性回归:
[-5.94898085 -9.30931808 -1.15760676]
-5.4719685646934275

套索:
[-7.22363814 -10.47734135 -2.20807684]
-6.6363521107522345

岭:
[-5.95990385 -4.17946756 -1.36885809]
-3.8360764993832004

那么哪一个最好呢?
SVR?

对于回归用例:
使用时我得到不同的结果
(1)得分='neg_mean_squared_error'的“ cross_val_score”

(2)对于相同的输入,当我使用“ GridSearchCV”并检查“ best_score_”时

对于回归模型,哪个更好?

  • “ cross_val_score”,得分='neg_mean_squared_error'
    (要么)
  • 使用“ GridSearchCV”并选中“ best_score_”

@pritishban
您正在询问使用问题。 问题跟踪器主要用于错误和新功能。 对于使用问题,建议尝试Stack OverflowMailing List

此页面是否有帮助?
0 / 5 - 0 等级