Estou trabalhando em um conjunto de dados com rótulos binários altamente desequilibrado, em que o número de rótulos verdadeiros fica a apenas 7% de todo o conjunto de dados. Mas alguma combinação de recursos pode gerar um número acima da média de um em um subconjunto.
Por exemplo, temos o seguinte conjunto de dados com um único recurso (cor):
180 amostras vermelhas - 0
20 amostras vermelhas - 1
300 amostras verdes - 0
100 amostras verdes - 1
Podemos construir uma árvore de decisão simples:
(color)
red / \ green
P(1 | red) = 0.1 P(1 | green) = 0.25
P (1) = 0,2 para o conjunto de dados geral
Se eu executar o XGBoost neste conjunto de dados, ele poderá prever probabilidades não maiores que 0,25. O que significa que, se eu tomar uma decisão no limite de 0,5:
- 0 - P <0,5
- 1 - P> = 0,5
Então sempre receberei todas as amostras rotuladas como zeros . Espero que eu tenha descrito claramente o problema.
Agora, no conjunto de dados inicial, estou obtendo o seguinte gráfico (limite no eixo x):
Ter o máximo de f1_score no limite = 0,1. Agora eu tenho duas perguntas:
- devo usar f1_score para um conjunto de dados dessa estrutura?
- é sempre razoável usar o limite de 0,5 para mapear probabilidades para rótulos ao usar o XGBoost para classificação binária?
Atualizar. Vejo que esse tópico atrai algum interesse. Abaixo está o código Python para reproduzir o experimento em vermelho / verde usando o XGBoost. Na verdade, gera as probabilidades esperadas:
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))))
Resultado:
P(1 | red), predicted: [ 0.1073855]
P(1 | green), predicted: [ 0.24398108]
fonte
xgboost
suporta pesos de classe, o OP deve jogar com aqueles se ele está insatisfeito com qualquer métrica que ele queira maximizar.n_samples / (n_classes * np.bincount(y))
. Isso evita que o classificador dê mais peso a classes mais populosas.