Estoy trabajando en un conjunto de datos con etiquetas binarias altamente desequilibradas, donde el número de etiquetas verdaderas es solo del 7% de todo el conjunto de datos. Pero alguna combinación de características podría producir un número de unidades superior al promedio en un subconjunto.
Por ejemplo, tenemos el siguiente conjunto de datos con una sola característica (color):
180 muestras rojas - 0
20 muestras rojas - 1
300 muestras verdes - 0
100 muestras verdes - 1
Podemos construir un árbol de decisión simple:
(color)
red / \ green
P(1 | red) = 0.1 P(1 | green) = 0.25
P (1) = 0.2 para el conjunto de datos general
Si ejecuto XGBoost en este conjunto de datos, puede predecir probabilidades no mayores que 0.25. Lo que significa que si tomo una decisión en el umbral de 0.5:
- 0 - P <0.5
- 1 - P> = 0.5
Entonces siempre obtendré todas las muestras etiquetadas como ceros . Espero haber descrito claramente el problema.
Ahora, en el conjunto de datos inicial obtengo el siguiente gráfico (umbral en el eje x):
Tener un máximo de f1_score en el umbral = 0.1. Ahora tengo dos preguntas:
- ¿Debería incluso usar f1_score para un conjunto de datos de tal estructura?
- ¿Es siempre razonable usar un umbral de 0.5 para asignar probabilidades a las etiquetas cuando se usa XGBoost para la clasificación binaria?
Actualizar. Veo que el tema atrae algo de interés. A continuación se muestra el código de Python para reproducir el experimento rojo / verde usando XGBoost. Realmente genera las 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))))
Salida:
P(1 | red), predicted: [ 0.1073855]
P(1 | green), predicted: [ 0.24398108]
fuente
xgboost
admite pesos de clase, el OP debería jugar con ellos si él no está satisfecho con la métrica que quiera maximizar.n_samples / (n_classes * np.bincount(y))
. Esto evita que el clasificador otorgue más peso a las clases más pobladas.