Je travaille sur un ensemble de données à étiquetage binaire très déséquilibré, où le nombre de véritables étiquettes est à seulement 7% de l'ensemble de données. Mais une combinaison de fonctionnalités pourrait produire un nombre supérieur à la moyenne de celles d'un sous-ensemble.
Par exemple, nous avons le jeu de données suivant avec une seule entité (couleur):
180 échantillons rouges - 0
20 échantillons rouges - 1
300 échantillons verts - 0
100 échantillons verts - 1
Nous pouvons construire un arbre de décision simple:
(color)
red / \ green
P(1 | red) = 0.1 P(1 | green) = 0.25
P (1) = 0,2 pour l'ensemble de données global
Si j'exécute XGBoost sur cet ensemble de données, il peut prédire des probabilités ne dépassant pas 0,25. Ce qui signifie que si je prends une décision au seuil de 0,5:
- 0 - P <0,5
- 1 - P> = 0,5
Ensuite, j'obtiendrai toujours tous les échantillons étiquetés comme des zéros . J'espère que j'ai clairement décrit le problème.
Maintenant, sur l'ensemble de données initial, j'obtiens le tracé suivant (seuil sur l'axe des x):
Avoir un maximum de f1_score au seuil = 0,1. Maintenant, j'ai deux questions:
- dois-je même utiliser f1_score pour un ensemble de données d'une telle structure?
- est-il toujours raisonnable d'utiliser un seuil de 0,5 pour mapper les probabilités aux étiquettes lors de l'utilisation de XGBoost pour la classification binaire?
Mise à jour. Je vois que ce sujet suscite un certain intérêt. Ci-dessous se trouve le code Python pour reproduire l'expérience rouge / vert en utilisant XGBoost. Il génère en fait les probabilités attendues:
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
import numpy as np
X0_0 = np.zeros(180) # red - 0
Y0_0 = np.zeros(180)
X0_1 = np.zeros(20) # red - 1
Y0_1 = np.ones(20)
X1_0 = np.ones(300) # green - 0
Y1_0 = np.zeros(300)
X1_1 = np.ones(100) # green - 1
Y1_1 = np.ones(100)
X = np.concatenate((X0_0, X0_1, X1_0, Y1_1))
Y = np.concatenate((Y0_0, Y0_1, Y1_0, Y1_1))
# reshaping into 2-dim array
X = X.reshape(-1, 1)
import xgboost as xgb
xgb_dmat = xgb.DMatrix(X_train, label=y_train)
param = {'max_depth': 1,
'eta': 0.01,
'objective': 'binary:logistic',
'eval_metric': 'error',
'nthread': 4}
model = xgb.train(param, xg_mat, 400)
X0_sample = np.array([[0]])
X1_sample = np.array([[1]])
print('P(1 | red), predicted: ' + str(model.predict(xgb.DMatrix(X0_sample))))
print('P(1 | green), predicted: ' + str(model.predict(xgb.DMatrix(X1_sample))))
Production:
P(1 | red), predicted: [ 0.1073855]
P(1 | green), predicted: [ 0.24398108]
la source
xgboost
prend en charge les poids de classe, l'OP devrait jouer avec ceux-ci s'il n'est pas satisfait de la métrique qu'il souhaite maximiser.n_samples / (n_classes * np.bincount(y))
. Cela évite au classificateur de donner plus de poids aux classes plus peuplées.