【问题标题】:How to write custom F1 score metric in light gbm python in Multiclass classification如何在多类分类中的轻型 gbm python 中编写自定义 F1 分数指标
【发布时间】:2018-12-10 20:34:33
【问题描述】:

有人可以帮我如何在 python 中为多类分类编写自定义 F1 分数???

编辑:我正在编辑问题以更好地了解我想要做什么

这是我针对 5 个类的多类问题的自定义 eval f1 分数指标的函数。

def evalerror(preds, dtrain):
    labels = dtrain.get_label()
    preds = preds.reshape(-1, 5)
    preds = preds.argmax(axis = 1)
    f_score = f1_score(preds, labels, average = 'weighted')
    return 'f1_score', f_score, True

注意:我正在重塑的原因是验证真值的长度为 252705,而 preds 是长度为 1263525 的数组,是实际值的 5 倍。原因是 LGB 为每个预测输出每个类别的概率。

下面我将训练和验证数据转换为 LGB 可以接受的格式。

dtrain = lgb.Dataset(train_X, label= train_Y, free_raw_data = False)
dvalid = lgb.Dataset(valid_X, label= valid_Y, free_raw_data = False, 
                     reference= dtrain)

下面是我适合训练数据的 LGB 模型。如您所见,我已将evalerror 自定义函数传递给我的模型feval 以及验证数据dvalid,我希望在训练时查看其f1 分数。 我正在训练模型进行 10 次迭代。

evals_result = {}
num_round = 10
lgb_model = lgb.train(params, 
                      dtrain, 
                      num_round, 
                      valid_sets = dvalid, 
                      feval = evalerror,
                      evals_result = evals_result)

当模型接受 10 轮训练时,验证集上每次迭代的 F1 分数显示在下方,这是不正确的,因为我得到了 0.18 左右。

[1]     valid_0's multi_logloss: 1.46839        valid_0's f1_score: 0.183719
[2]     valid_0's multi_logloss: 1.35684        valid_0's f1_score: 0.183842
[3]     valid_0's multi_logloss: 1.26527        valid_0's f1_score: 0.183853
[4]     valid_0's multi_logloss: 1.18799        valid_0's f1_score: 0.183909
[5]     valid_0's multi_logloss: 1.12187        valid_0's f1_score: 0.187206
[6]     valid_0's multi_logloss: 1.06452        valid_0's f1_score: 0.187503
[7]     valid_0's multi_logloss: 1.01437        valid_0's f1_score: 0.187327
[8]     valid_0's multi_logloss: 0.97037        valid_0's f1_score: 0.187511
[9]     valid_0's multi_logloss: 0.931498       valid_0's f1_score: 0.186957
[10]    valid_0's multi_logloss: 0.896877       valid_0's f1_score: 0.18751

但是一旦模型被训练了 10 次迭代,我就会运行下面的代码来预测相同的验证集。

lgb_prediction = lgb_model.predict(valid_X)
lgb_prediction = lgb_prediction.argmax(axis = 1)
lgb_F1 = f1_score(lgb_prediction, valid_Y, average = 'weighted')
print("The Light GBM F1 is", lgb_F1)

The Light GBM F1 is 0.743250263548

注意:我没有像在自定义函数中那样重新整形,因为lgb_model.predict() 输出一个 numpy 数组 (252705, 5) 另请注意,我传递的是valid_X 而不是dvalid,因为在预测时我们将不得不传递原始格式而不是像我们传递lgb.train() 那样的稀疏格式

当我在同一个验证数据集上进行预测时,我得到的 F1 分数为 0.743250263548,这已经足够好了。所以我期望的是第 10 次迭代的验证 F1 分数,而训练应该与我在训练模型后预测的相同。

有人可以帮我解决我做错的事情吗?谢谢

【问题讨论】:

  • 需要更多细节。 preds是什么形状,dtrain是什么,你为什么要重塑preds?当你说你得到 0.74 分数时,你如何计算 f1 分数?如答案所述,您使用的 y_true 和 y_pred 参数错误。
  • Y_true 的形状是 252705,Y_pred 是 1263525(252705 * 5) 因为这是一个 5 类问题,每个数据点的输出是 5 类的概率。我已经通过 preds.reshape(-1, 5) 进行了整形,它输出了一个形状为 numpy 的数组 (252705, 5)。我采用 argmax,它将给出每条记录的最大概率。Y_pred 的最终 o/p 将具有与 Y_true (252705) 相同的形状。当我说我得到 0.74 时,我执行此 pred = lgb_model.predict(valid_X) pred = pred.argmax(axis = 1) 第一行预测概率和 o/p 形状为 (252705, 5)。第二个取最大概率。我将事实和 pred 传递给 f1_score()
  • 那为什么不把pred(你从lgb_model.predict(valid_X)计算出来)发送给函数。正如你所说,model.predict() 的 pred 的输出已经是(252705, 5),那么新的形状1263525 是从哪里来的?
  • model.predict() 是在我构建模型进行几次迭代并在测试集上预测我得到形状 (252705, 5) 之后。我正在编写的函数是在构建建模时查看 f1 分数,它为每次迭代输出一个长度为 1263525 的数组。所以我不得不像上面解释的那样重塑为 (252705, 5)
  • 在一个github问题中指出了一个答案:github.com/Microsoft/LightGBM/issues/…

标签: python scikit-learn lightgbm


【解决方案1】:
sklearn.metrics.f1_score(y_true, y_pred, labels=None, pos_label=1, average=’binary’, sample_weight=None)[source]

所以根据这个你应该更正:

#f1_score(labels , preds)
def evalerror(preds, dtrain):
    labels = dtrain.get_label()
    preds = preds.reshape(-1, 5)
    preds = preds.argmax(axis = 1)
    f_score = f1_score(labels , preds,  average = 'weighted')
    return 'f1_score', f_score, True

【讨论】:

  • 我不认为交换 pos 参数会有很大的不同。但只是为了再次确认我按照你的建议做了但这是迭代结果几乎相同[2] valid_0's multi_logloss: 1.35684 valid_0's f1_score: 0.200796 [3] valid_0's multi_logloss: 1.26527 valid_0's f1_score: 0.200777 但预测结果给出了 0.7436534383
【解决方案2】:

我遇到了同样的问题。

Lgb 预测以扁平数组的形式输出。

通过检查,我发现它是这样的:

样本a 到类i 的概率位于

num_classes*(a-1) + i位置

你的代码应该是这样的:

    def evalerror(preds, dtrain):

        labels = dtrain.get_label()
        preds = preds.reshape(5, -1).T
        preds = preds.argmax(axis = 1)
        f_score = f1_score(labels , preds,  average = 'weighted')
        return 'f1_score', f_score, True

【讨论】:

  • 非常感谢。我以错误的方式重塑了概率。
猜你喜欢
  • 2018-03-25
  • 2020-05-04
  • 2019-12-04
  • 1970-01-01
  • 1970-01-01
  • 2016-08-05
  • 2022-09-23
  • 2019-04-01
  • 2016-01-24
相关资源
最近更新 更多