128 votos

¿Son problemáticos los conjuntos de datos desequilibrados y (cómo) ayuda el sobremuestreo?

TL;DR

Ver título.


Motivación

Espero una respuesta canónica en la línea de "(1) No, (2) No aplicable, porque (1)", con la que podamos cerrar muchos preguntas erróneas sobre conjuntos de datos desequilibrados y sobremuestreo. Me alegraría mucho que se demostrara que estoy equivocado en mis ideas preconcebidas. Fabulosas recompensas esperan al intrépido contestador.


Mi argumento

Me desconciertan las muchas preguntas que recibimos en el clases desequilibradas etiqueta. Las clases desequilibradas parecen ser evidentemente mala . Y sobremuestreo la(s) clase(s) minoritaria(s) se considera tan evidente que ayuda a resolver los problemas evidentes. Muchas preguntas que llevan ambas etiquetas proceden a preguntar cómo realizar el sobremuestreo en alguna situación específica.

No entiendo qué problema plantean las clases desequilibradas ni cómo se supone que el sobremuestreo resuelve estos problemas.

En mi opinión, los datos desequilibrados no suponen ningún problema. Hay que modelar las probabilidades de pertenencia a una clase, y éstas pueden ser pequeñas. Mientras sean correctas, no hay ningún problema. Por supuesto, no hay que utilizar la precisión como un KPI que hay que maximizar en un problema de clasificación. O calcular umbrales de clasificación . En su lugar, se debe evaluar la calidad de toda la distribución predictiva utilizando una reglas de puntuación . Tetlock's Superprevisión sirve como una maravillosa y muy legible introducción a la predicción de clases desequilibradas, aunque esto no se mencione explícitamente en el libro.


Relacionado

La discusión en los comentarios ha sacado a relucir una serie de temas relacionados.

Respuesta de IcannotFixThis parece suponer (1) que el KPI que intentamos maximizar es la precisión, y (2) que la precisión es un KPI apropiado para la evaluación del modelo de clasificación. No lo es. Esta puede ser una de las claves de todo el debate.

Respuesta de AdamO se centra en la baja precisión de las estimaciones de los factores no equilibrados. Se trata, por supuesto, de una preocupación válida y probablemente la respuesta a mi pregunta principal. Pero el sobremuestreo no ayuda en este caso, al igual que no podemos obtener estimaciones más precisas en cualquier regresión corriente simplemente duplicando cada observación diez veces.


Resumen

Aparentemente, los hilos anteriores pueden resumirse de la siguiente manera.

  • Las clases raras (tanto en el resultado como en los predictores) son un problema, porque las estimaciones de los parámetros y las predicciones tienen una alta varianza/baja precisión. Esto no puede abordarse mediante un sobremuestreo. (En el sentido de que siempre es mejor obtener más datos que representante de la población, y el muestreo selectivo inducirá un sesgo según mis simulaciones y las de otros).
  • Las clases raras son un "problema" si evaluamos nuestro modelo por su precisión. Pero la precisión no es una buena medida para evaluar los modelos de clasificación . (He pensado en incluir la precisión en mis simulaciones, pero entonces habría tenido que establecer un umbral de clasificación, que es un pregunta equivocada y la pregunta ya es lo suficientemente larga).

Un ejemplo

Hagamos una simulación para ilustrar. En concreto, simularemos diez predictores, de los cuales sólo uno tiene realmente un impacto en un resultado raro. Veremos dos algoritmos que pueden utilizarse para la clasificación probabilística: regresión logística y bosques aleatorios .

En cada caso, aplicaremos el modelo al conjunto de datos completo o a uno equilibrado sobremuestreado, que contiene todas las instancias de la clase rara y el mismo número de muestras de la clase mayoritaria (por lo que el conjunto de datos sobremuestreado es más pequeño que el conjunto de datos completo).

Para la regresión logística, evaluaremos si cada modelo recupera realmente los coeficientes originales utilizados para generar los datos. Además, para ambos métodos, calcularemos las predicciones probabilísticas de pertenencia a una clase y las evaluaremos con datos retenidos generados mediante el mismo proceso de generación de datos que los datos de entrenamiento originales. Se evaluará si las predicciones coinciden realmente con los resultados mediante el método Puntuación de Brier uno de los más comunes reglas de puntuación adecuadas .

Haremos 100 simulaciones. (Aumentar esto sólo hace que los gráficos de los granos sean más estrechos y que la simulación dure más que una taza de café). Cada simulación contiene $n=10,000$ muestras. Los predictores forman un $10,000\times 10$ con entradas distribuidas uniformemente en $[0,1]$ . Sólo el primer predictor tiene realmente un impacto; la verdadera DGP es

$$ \text{logit}(p_i) = -7+5x_{i1}. $$

Esto hace que las incidencias simuladas para la clase minoritaria TRUE estén entre el 2 y el 3%:

training_incidence

Vamos a realizar las simulaciones. Al introducir el conjunto de datos en una regresión logística, obtenemos (sin sorpresa) estimaciones insesgadas de los parámetros (los verdaderos valores de los parámetros están indicados por los diamantes rojos):

logistic_coefficients

Sin embargo, si introducimos el conjunto de datos sobremuestreados en la regresión logística, el parámetro de intercepción está muy sesgado :

logistic_coefficients_oversampled

Comparemos las puntuaciones de Brier entre los modelos ajustados a los conjuntos de datos "brutos" y a los sobremuestreados, tanto para la regresión logística como para el bosque aleatorio. Recuerde que más pequeño es mejor:

logistic_regression_Brier_scores

Random_Forest_Brier_score

En cada caso, las distribuciones predictivas derivadas del conjunto de datos completo son mucho mejores que las derivadas de uno sobremuestreado.

Concluyo que las clases desequilibradas no son un problema, y que el sobremuestreo no alivia este no-problema, sino que introduce gratuitamente sesgos y peores predicciones.

¿Dónde está mi error?


Una advertencia

Concederé gustosamente que el sobremuestreo tiene una aplicación: si

  1. estamos ante un resultado raro, y
  2. evaluación de la resultado es fácil o barato, pero
  3. evaluar la predictores es difícil o caro

Un buen ejemplo sería estudios de asociación del genoma completo (GWAS) de enfermedades raras. Comprobar si una persona padece una determinada enfermedad puede ser mucho más fácil que realizar el genotipo de su sangre. (He participado en algunos GWAS de TEPT .) Si los presupuestos son limitados, puede tener sentido hacer una selección basada en el resultado y asegurarse de que hay "suficientes" casos más raros en la muestra.

Sin embargo, hay que sopesar el ahorro monetario frente a las pérdidas ilustradas anteriormente, y lo que quiero decir es que las preguntas sobre los conjuntos de datos desequilibrados en CV no mencionan tal compensación, sino que tratan las clases desequilibradas como un mal evidente, completamente aparte de cualquier coste de la recogida de muestras .


Código R

    library(randomForest)
    library(beanplot)

    nn_train <- nn_test <- 1e4
    n_sims <- 1e2

    true_coefficients <- c(-7, 5, rep(0, 9))

    incidence_train <- rep(NA, n_sims)
    model_logistic_coefficients <- 
         model_logistic_oversampled_coefficients <- 
         matrix(NA, nrow=n_sims, ncol=length(true_coefficients))

    brier_score_logistic <- brier_score_logistic_oversampled <- 
      brier_score_randomForest <- 
    brier_score_randomForest_oversampled <- 
      rep(NA, n_sims)

    pb <- winProgressBar(max=n_sims)
    for ( ii in 1:n_sims ) {
        setWinProgressBar(pb,ii,paste(ii,"of",n_sims))
        set.seed(ii)
        while ( TRUE ) {    # make sure we even have the minority 
                            # class
            predictors_train <- matrix(
              runif(nn_train*(length(true_coefficients) - 1)), 
                  nrow=nn_train)
            logit_train <- 
             cbind(1, predictors_train)%*%true_coefficients
            probability_train <- 1/(1+exp(-logit_train))
            outcome_train <- factor(runif(nn_train) <= 
                     probability_train)
            if ( sum(incidence_train[ii] <- 
               sum(outcome_train==TRUE))>0 ) break
        }
        dataset_train <- data.frame(outcome=outcome_train, 
                          predictors_train)

        index <- c(which(outcome_train==TRUE),  
          sample(which(outcome_train==FALSE),   
                sum(outcome_train==TRUE)))

        model_logistic <- glm(outcome~., dataset_train, 
                    family="binomial")
        model_logistic_oversampled <- glm(outcome~., 
              dataset_train[index, ], family="binomial")

        model_logistic_coefficients[ii, ] <- 
               coefficients(model_logistic)
        model_logistic_oversampled_coefficients[ii, ] <- 
          coefficients(model_logistic_oversampled)

        model_randomForest <- randomForest(outcome~., dataset_train)
        model_randomForest_oversampled <- 
          randomForest(outcome~., dataset_train, subset=index)

        predictors_test <- matrix(runif(nn_test * 
            (length(true_coefficients) - 1)), nrow=nn_test)
        logit_test <- cbind(1, predictors_test)%*%true_coefficients
        probability_test <- 1/(1+exp(-logit_test))
        outcome_test <- factor(runif(nn_test)<=probability_test)
        dataset_test <- data.frame(outcome=outcome_test, 
                         predictors_test)

        prediction_logistic <- predict(model_logistic, dataset_test, 
                                        type="response")
        brier_score_logistic[ii] <- mean((prediction_logistic - 
               (outcome_test==TRUE))^2)

        prediction_logistic_oversampled <-      
               predict(model_logistic_oversampled, dataset_test, 
                        type="response")
        brier_score_logistic_oversampled[ii] <- 
          mean((prediction_logistic_oversampled - 
                (outcome_test==TRUE))^2)

        prediction_randomForest <- predict(model_randomForest, 
            dataset_test, type="prob")
        brier_score_randomForest[ii] <-
          mean((prediction_randomForest[,2]-(outcome_test==TRUE))^2)

        prediction_randomForest_oversampled <-   
                         predict(model_randomForest_oversampled, 
                                  dataset_test, type="prob")
        brier_score_randomForest_oversampled[ii] <- 
          mean((prediction_randomForest_oversampled[, 2] - 
                (outcome_test==TRUE))^2)
    }
    close(pb)

    hist(incidence_train, breaks=seq(min(incidence_train)-.5, 
            max(incidence_train) + .5),
      col="lightgray",
      main=paste("Minority class incidence out of", 
                    nn_train,"training samples"), xlab="")

    ylim <- range(c(model_logistic_coefficients, 
                   model_logistic_oversampled_coefficients))
    beanplot(data.frame(model_logistic_coefficients),
      what=c(0,1,0,0), col="lightgray", xaxt="n", ylim=ylim,
      main="Logistic regression: estimated coefficients")
    axis(1, at=seq_along(true_coefficients),
      c("Intercept", paste("Predictor", 1:(length(true_coefficients) 
             - 1))), las=3)
    points(true_coefficients, pch=23, bg="red")

    beanplot(data.frame(model_logistic_oversampled_coefficients),
      what=c(0, 1, 0, 0), col="lightgray", xaxt="n", ylim=ylim,
      main="Logistic regression (oversampled): estimated 
              coefficients")
    axis(1, at=seq_along(true_coefficients),
      c("Intercept", paste("Predictor", 1:(length(true_coefficients) 
             - 1))), las=3)
    points(true_coefficients, pch=23, bg="red")

    beanplot(data.frame(Raw=brier_score_logistic, 
            Oversampled=brier_score_logistic_oversampled),
      what=c(0,1,0,0), col="lightgray", main="Logistic regression: 
             Brier scores")
    beanplot(data.frame(Raw=brier_score_randomForest, 
      Oversampled=brier_score_randomForest_oversampled),
      what=c(0,1,0,0), col="lightgray", 
              main="Random Forest: Brier scores")

1 votos

Tengo más o menos la misma pregunta: stats.stackexchange.com/questions/285231/ ¡!

9 votos

También he realizado la misma simulación con una selección aún mayor de modelos y una gama más amplia de probabilidades de clase a priori, y he observado los mismos resultados. Además, si mide el AUC de sus modelos, observará que todos son iguales, independientemente del equilibrio de clases de sus datos de entrenamiento. Me pregunto cuál es el origen de esta amplia concepción sobre los males del equilibrio de clases, de dónde viene, cómo hemos llegado a este punto.

0 votos

¡Genial, gracias! Supongo que no busqué lo suficiente...

33voto

John Richardson Puntos 1197

Me gustaría empezar apoyando una afirmación de la pregunta:

... lo que quiero decir es que las preguntas sobre conjuntos de datos no equilibrados en CV no no mencionan tal compensación, sino que tratan las clases desequilibradas como un como un mal evidente, sin tener en cuenta los costes de la recogida de muestras. de la muestra.

Yo también tengo la misma preocupación, mis preguntas aquí y aquí tienen la intención de invitar a la contraprueba de que es un "mal evidente" la falta de respuestas (incluso con una recompensa) sugiere que no lo es. Muchas publicaciones en blogs y artículos académicos tampoco lo dejan claro. Clasificadores puede tienen un problema con los conjuntos de datos desequilibrados, pero sólo cuando el conjunto de datos es muy pequeño, por lo que mi respuesta se refiere a casos excepcionales, y no justifica el remuestreo del conjunto de datos en general.

Allí es un problema de desequilibrio de clases, pero no es causado por el desequilibrio por sí mismo sino porque hay muy pocos ejemplos de la clase minoritaria para describir adecuadamente su distribución estadística. Como se menciona en la pregunta, esto significa que las estimaciones de los parámetros pueden tener una alta varianza, lo cual es cierto, pero eso puede dar lugar a un sesgo a favor de la clase mayoritaria (en lugar de afectar a ambas clases por igual). En el caso de la regresión logística, esto es discutido por King y Zeng,

3 Gary King y Langche Zeng. 2001. "Regresión logística en datos de eventos raros". Political Analysis, 9, Pp. 137-163. https://j.mp/2oSEnmf

[En mis experimentos he descubierto que a veces puede haber un sesgo a favor de la clase minoritaria, pero eso se debe a un sobreajuste salvaje en el que el solapamiento de clases desaparece debido al muestreo aleatorio, así que eso no cuenta realmente y la regularización (bayesiana) debería solucionarlo].

Lo bueno es que el MLE es asintóticamente insesgado, por lo que podemos esperar que este sesgo contra la clase minoritaria desaparezca a medida que aumente el tamaño total del conjunto de datos, independientemente del desequilibrio.

Como se trata de un problema de estimación, cualquier cosa que dificulte la estimación (por ejemplo, la alta dimensionalidad) parece que empeorará el problema del desequilibrio de clases.

Tenga en cuenta que los clasificadores probabilísticos (como la regresión logística) y las reglas de puntuación apropiadas no resolverán este problema, ya que "los procedimientos estadísticos populares, como la regresión logística, pueden subestimar fuertemente la probabilidad de eventos raros" 3 . Esto significa que sus estimaciones de probabilidad no estarán bien calibradas, por lo que tendrá que hacer cosas como ajustar el umbral (lo que equivale a volver a muestrear o ponderar los datos).

Así, si observamos un modelo de regresión logística con 10.000 muestras, no deberíamos esperar ver un problema de desequilibrio, ya que añadir más datos tiende a solucionar la mayoría de los problemas de estimación.

Así que un desequilibrio puede ser problemático, si tiene un desequilibrio extremo y el conjunto de datos es pequeño (y/o de alta dimensión, etc.), pero en ese caso puede ser difícil hacer mucho al respecto (ya que no tiene suficientes datos para estimar cuán grande es la corrección del muestreo necesaria para corregir el sesgo). Si tiene muchos datos, la única razón para volver a muestrear es que las frecuencias de las clases operativas sean diferentes a las del conjunto de entrenamiento o que los costes de la clasificación errónea sean diferentes, etc. (si cualquiera de las dos cosas es desconocida o variable, realmente debería utilizar un clasificador probabilístico).

Esto es más que nada un resumen, espero poder añadir más cosas más adelante.

0 votos

Gracias, espero con impaciencia su ampliación. Si he entendido bien, el problema de desequilibrio de clases que se observa es la alta varianza de las estimaciones de los parámetros, ¿verdad? Me parece que el sobremuestreo, etc., no solucionaría este problema, ¿correcto?

0 votos

@StephanKolassa En principio se puede, pero hay que saber qué cantidad de sobremuestreo aplicar (o, lo que es lo mismo, reponderación o ajuste del umbral), lo cual va a ser difícil si no se dispone de datos suficientes para estimar el modelo en primer lugar. King adopta el enfoque del ajuste del umbral, ya que puede aproximarse analíticamente para la regresión logística, pero no está claro que tenga una gran utilidad práctica.

0 votos

@StephanKolassa Creo que no he dado una respuesta muy directa a tu pregunta. Creo que lo que ocurre es que la varianza en las estimaciones de los parámetros provoca el sesgo indebido contra la clase minoritaria debido a la estructura del problema. Reducir la varianza debería reducir el sesgo, pero un nuevo muestreo (o mejor una nueva ponderación de los datos en la pérdida) puede abordar el sesgo directamente. Sin embargo, será difícil estimar cuánta regularización o remuestreo/reponderación se necesita en la práctica.

11voto

Adam Check Puntos 106

En general, estoy de acuerdo con su premisa de que existe una fijación excesiva por equilibrar las clases, y que normalmente no es necesario hacerlo. Tus ejemplos de cuándo es apropiado hacerlo son buenos.

Sin embargo, no estoy de acuerdo con su afirmación:

Concluyo que las clases desequilibradas no son un problema, y que el sobremuestreo no alivia este no-problema, sino que introduce gratuitamente sesgos y peores predicciones.

El problema de sus predicciones no es el procedimiento de sobremuestreo, sino que no corrige el hecho de que el porcentaje de positivos en la regresión "sobremuestreada" (50/50) es del 50%, mientras que en los datos se acerca al 2%.

Siguiendo a King y Zeng ("Logistic Regression in Rare Events Data", 2001, Análisis político , PDF aquí ), que la tasa de base de población viene dada por $\tau$ . Estimamos que $\tau$ como la proporción de positivos en la muestra de entrenamiento: $$ \tau = \frac{1}{N}\sum_{i=1}^N y_i $$ Y que $\bar{y}$ es la proporción de positivos en el conjunto sobremuestreado, $\bar{y}=0.5$ . Esto es así por construcción, ya que se utiliza una muestra equilibrada 50/50 en la regresión sobremuestreada.

A continuación, tras utilizar el predict comando para generar probabilidades previstas $P(y|x,d)$ ajustamos estas probabilidades utilizando la fórmula de King y Zeng, apéndice B.2 para hallar la probabilidad bajo la tasa base de población. Esta probabilidad viene dada por $P(y=1|x,d)A_1B$ . En el caso de dos clases: $$ P(y=1|x,d)A_1B = \frac{P(y=1|x,d) \frac{\tau}{\bar{y}}}{P(y=1|x,d) \frac{\tau}{\bar{y}} + P(y=0|x,d) \frac{1-\tau}{1-\bar{y}}} $$ Desde $\bar{y}=0.5$ esto se simplifica a: $$ P(y=1|x,d)A_1B = \frac{P(y=1|x,d) \tau}{P(y=1|x,d) \tau + P(y=0|x,d) (1-\tau)} $$

Modificando su código en los lugares pertinentes, ahora tenemos puntuaciones Brier muy similares entre los dos enfoques, a pesar de que la muestra de entrenamiento sobremuestreada utiliza un orden de magnitud menos de datos que la muestra de entrenamiento bruta (en la mayoría de los casos, aproximadamente 450 puntos de datos frente a 10.000).

Así pues, en este estudio de Monte Carlo, vemos que equilibrar la muestra de entrenamiento no perjudica la precisión predictiva (según la puntuación de Brier), pero tampoco proporciona ningún aumento significativo de la precisión. El único beneficio de equilibrar la muestra de entrenamiento en esta aplicación concreta es reducir la carga computacional de estimar el predictor binario. En el caso que nos ocupa, sólo necesitamos ~450 puntos de datos en lugar de 10.000. La reducción de la carga computacional sería mucho mayor si se tratara de millones de observaciones en los datos brutos.

bean plot of brier scores from logistic regression showing roughly equivalent brier scores between raw training sample and over-sampled training sample

bean plot of brier scores from random forest showing roughly equivalent brier scores between raw training sample and over-sampled training sample

A continuación se muestra el código modificado:

library(randomForest)
library(beanplot)

nn_train <- nn_test <- 1e4
n_sims <- 1e2

true_coefficients <- c(-7, 5, rep(0, 9))

incidence_train <- rep(NA, n_sims)
model_logistic_coefficients <- 
  model_logistic_oversampled_coefficients <- 
  matrix(NA, nrow=n_sims, ncol=length(true_coefficients))

brier_score_logistic <- brier_score_logistic_oversampled <- 
  brier_score_randomForest <- 
  brier_score_randomForest_oversampled <- 
  rep(NA, n_sims)

pb <- txtProgressBar(max=n_sims)
for ( ii in 1:n_sims ) {
  setTxtProgressBar(pb,ii,paste(ii,"of",n_sims))
  set.seed(ii)
  while ( TRUE ) {    # make sure we even have the minority 
    # class
    predictors_train <- matrix(
      runif(nn_train*(length(true_coefficients) - 1)), 
      nrow=nn_train)
    logit_train <- 
      cbind(1, predictors_train)%*%true_coefficients
    probability_train <- 1/(1+exp(-logit_train))
    outcome_train <- factor(runif(nn_train) <= 
                              probability_train)
    if ( sum(incidence_train[ii] <- 
             sum(outcome_train==TRUE))>0 ) break
  }
  dataset_train <- data.frame(outcome=outcome_train, 
                              predictors_train)

  index <- c(which(outcome_train==TRUE),  
             sample(which(outcome_train==FALSE),   
                    sum(outcome_train==TRUE)))

  model_logistic <- glm(outcome~., dataset_train, 
                        family="binomial")
  model_logistic_oversampled <- glm(outcome~., 
                                    dataset_train[index, ], family="binomial")

  model_logistic_coefficients[ii, ] <- 
    coefficients(model_logistic)
  model_logistic_oversampled_coefficients[ii, ] <- 
    coefficients(model_logistic_oversampled)

  model_randomForest <- randomForest(outcome~., dataset_train)
  model_randomForest_oversampled <- 
    randomForest(outcome~., dataset_train, subset=index)

  predictors_test <- matrix(runif(nn_test * 
                                    (length(true_coefficients) - 1)), nrow=nn_test)
  logit_test <- cbind(1, predictors_test)%*%true_coefficients
  probability_test <- 1/(1+exp(-logit_test))
  outcome_test <- factor(runif(nn_test)<=probability_test)
  dataset_test <- data.frame(outcome=outcome_test, 
                             predictors_test)

  prediction_logistic <- predict(model_logistic, dataset_test, 
                                 type="response")
  brier_score_logistic[ii] <- mean((prediction_logistic - 
                                      (outcome_test==TRUE))^2)

  prediction_logistic_oversampled <-      
    predict(model_logistic_oversampled, dataset_test, 
            type="response")

  # Adjust probabilities based on appendix B.2 in King and Zeng (2001)
  p1_tau1 = prediction_logistic_oversampled*(incidence_train[ii]/nn_train)
  p0_tau0 = (1-prediction_logistic_oversampled)*(1-incidence_train[ii]/nn_train)
  prediction_logistic_oversampled_adj <- p1_tau1/(p1_tau1+p0_tau0)

  brier_score_logistic_oversampled[ii] <- 
    mean((prediction_logistic_oversampled_adj - 
            (outcome_test==TRUE))^2)

  prediction_randomForest <- predict(model_randomForest, 
                                     dataset_test, type="prob")
  brier_score_randomForest[ii] <-
    mean((prediction_randomForest[,2]-(outcome_test==TRUE))^2)

  prediction_randomForest_oversampled <-   
    predict(model_randomForest_oversampled, 
            dataset_test, type="prob")

  # Adjust probabilities based on appendix B.2 in King and Zeng (2001)
  p1_tau1 = prediction_randomForest_oversampled*(incidence_train[ii]/nn_train)
  p0_tau0 = (1-prediction_randomForest_oversampled)*(1-incidence_train[ii]/nn_train)
  prediction_randomForest_oversampled_adj <- p1_tau1/(p1_tau1+p0_tau0)

  brier_score_randomForest_oversampled[ii] <- 
    mean((prediction_randomForest_oversampled_adj[, 2] - 
            (outcome_test==TRUE))^2)
}
close(pb)

hist(incidence_train, breaks=seq(min(incidence_train)-.5, 
                                 max(incidence_train) + .5),
     col="lightgray",
     main=paste("Minority class incidence out of", 
                nn_train,"training samples"), xlab="")

ylim <- range(c(model_logistic_coefficients, 
                model_logistic_oversampled_coefficients))
beanplot(data.frame(model_logistic_coefficients),
         what=c(0,1,0,0), col="lightgray", xaxt="n", ylim=ylim,
         main="Logistic regression: estimated coefficients")
axis(1, at=seq_along(true_coefficients),
     c("Intercept", paste("Predictor", 1:(length(true_coefficients) 
                                          - 1))), las=3)
points(true_coefficients, pch=23, bg="red")

beanplot(data.frame(model_logistic_oversampled_coefficients),
         what=c(0, 1, 0, 0), col="lightgray", xaxt="n", ylim=ylim,
         main="Logistic regression (oversampled): estimated 
              coefficients")
axis(1, at=seq_along(true_coefficients),
     c("Intercept", paste("Predictor", 1:(length(true_coefficients) 
                                          - 1))), las=3)
points(true_coefficients, pch=23, bg="red")

beanplot(data.frame(Raw=brier_score_logistic, 
                    Oversampled=brier_score_logistic_oversampled),
         what=c(0,1,0,0), col="lightgray", main="Logistic regression: 
             Brier scores")
beanplot(data.frame(Raw=brier_score_randomForest, 
                    Oversampled=brier_score_randomForest_oversampled),
         what=c(0,1,0,0), col="lightgray", 
         main="Random Forest: Brier scores")

2voto

JeeyCi Puntos 1

Estoy totalmente de acuerdo con una respuesta marcada "No hay problema de desequilibrio per se" - el problema es la falta de datos: cuando la clase minoritaria no forma una distribución gaussiana y, por lo tanto, la clase minoritaria sigue siendo un valor atípico en la distribución general == este es el problema para los métodos no supervisados.

Pero si se realiza un aprendizaje supervisado, conociendo las clases objetivo y sus características (rasgos) de antemano, los datos desequilibrados no podrían ser un problema SI se tratan correctamente. -- al menos cada submuestra Cross-Validated (por ejemplo en conjunto bagging & boosting) deben incluir representantes de ambos ( fuente ) para una aproximación completa del límite de decisión

Principal problema de la clase minoritaria : "el modelo no puede modelar bien el límite de estas regiones de baja densidad durante el aprendizaje, lo que da lugar a ambigüedad y escasa generalización ". Solución se encontró en (1) el método de aprendizaje semisupervisado, que genera pseudoetiquetas en datos no etiquetados y luego entrena conjuntamente, O (2) (cuando hay desequilibrio extremo, como en medicina) preentrenamiento autosupervisado con posterior entrenamiento principal.

Así pues, se puede reducir el problema del desequilibrio mediante la lógica de programación o (mejor) aumentando el número de muestras == incluso sin sobremuestreo (inyección artificial de muestras falsas de clases minoritarias).

P.D. algunos consejos :

primero - "Exactamente igual que deberíamos hacer la selección de características dentro del bucle de validación cruzada, también deberíamos sobremuestrear dentro del bucle". ( fuente )

segundo "El reequilibrio sólo tiene sentido en el conjunto de entrenamiento, para evitar que el clasificador clasifique simple e ingenuamente todas las instancias como negativas para una precisión percibida del 99%".

tercera "Cuando se comparan dos clasificadores binarios, el AUC es uno de los criterios que no debe dejarse engañar por el desequilibrio de los datos".

adelante como alternativa - Función de Coste/Pérdida Ponderada - "Gracias al Sklearn, existe un parámetro incorporado llamado class_weight en la mayoría de los algoritmos ML que ayuda a equilibrar la contribución de cada clase". - p.ej. pérdida de entropía cruzada sigmoidal ponderada para CLF binario

quinto ver 8º en el enlace - "sé creativo"

sexto Desplazamiento del umbral y búsqueda del valor óptimo a partir de una cuadrícula

1voto

codeNewbie Puntos 21

Edita para resumir los siguientes argumentos y simulaciones:

Propongo que el equilibrio mediante sobremuestreo/ submuestreo o pesos de clase es una ventaja durante el entrenamiento de modelos de descenso de gradiente que utilizan procedimientos de muestreo durante el entrenamiento (es decir, submuestreo, bootstraping, minibatches, etc., como se utilizan, por ejemplo, en las redes neuronales y el refuerzo de gradiente). Propongo que esto se debe a una mejor relación señal/ruido del gradiente de la función de pérdida que se explica por:

  1. Señal mejorada (mayor gradiente de la función de pérdida, como sugiere la primera simulación)
  2. Reducción del ruido del gradiente debido al muestreo en un entorno equilibrado frente al fuertemente desequilibrado (como lo corrobora la segunda simulación).

Respuesta original: Para exponer mi punto de vista, he modificado su código para incluir un modelo "0" (o de referencia) para cada ejecución, en el que se elimina la primera columna de predictores, conservando sólo los 9 predictores restantes que no tienen relación con el resultado (código completo a continuación). Al final calculo las puntuaciones Brier para los modelos logístico y randomForest y comparo las diferencias con el modelo completo. El código completo está más abajo. Cuando ahora comparo el cambio en la puntuación Brier de los modelos "0" con los modelos originales completos (que incluyen el predictor 1) observo:

>     round( quantile( (brier_score_logistic - brier_score_logistic_0)/brier_score_logistic_0), 3)
0%    25%    50%    75%   100% 
-0.048 -0.038 -0.035 -0.032 -0.020 
>     round( quantile( (brier_score_logistic_oversampled - brier_score_logistic_oversampled_0)/brier_score_logistic_oversampled_0),3)
0%    25%    50%    75%   100% 
-0.323 -0.258 -0.241 -0.216 -0.130 
>     round( quantile( (brier_score_randomForest - brier_score_randomForest_0)/brier_score_randomForest_0), 3)
0%    25%    50%    75%   100% 
-0.050 -0.037 -0.032 -0.026 -0.009 
>     round( quantile( (brier_score_randomForest_oversampled - brier_score_randomForest_oversampled_0)/brier_score_randomForest_oversampled_0), 3)
0%    25%    50%    75%   100% 
-0.306 -0.272 -0.255 -0.233 -0.152 

Lo que parece claro es que, para el mismo predictor, el cambio relativo en la puntuación Brier salta de una mediana de alrededor de 0,035 en un entorno desequilibrado a 0,241 en un entorno equilibrado, lo que da un gradiente aproximadamente 7 veces mayor para un modelo predictivo frente a una línea de base. Además, si nos fijamos en las puntuaciones Brier absolutas, el modelo de referencia en un entorno desequilibrado funciona mucho mejor que el modelo completo en un entorno equilibrado:

>     round( quantile(brier_score_logistic_0), 5)
0%     25%     50%     75%    100% 
0.02050 0.02363 0.02450 0.02545 0.02753 
>     round( quantile(brier_score_logistic_oversampled), 5)
0%     25%     50%     75%    100% 
0.17576 0.18842 0.19294 0.19916 0.23089 

Por lo tanto, concluir que un Brier más pequeño es mejor per se conducirá a conclusiones erróneas si, por ejemplo, se comparan conjuntos de datos con diferentes prevalencias de predictores o resultados.

En general, me parecen dos ventajas/problemas:

  1. Equilibrar los conjuntos de datos parece conseguir un gradiente más alto, lo que debería ser beneficioso para el entrenamiento de algoritmos de descenso de gradiente (xgboost, redes neuronales). En mi experiencia, si no se equilibra, la red neuronal podría aprender a adivinar la clase con mayor probabilidad sin aprender ninguna característica de los datos si el conjunto de datos está demasiado desequilibrado.
  2. La comparabilidad entre diferentes estudios/poblaciones de pacientes/biomarcadores puede beneficiarse de medidas que sean menos sensibles a los cambios en la prevalencia, como el AUC o el índice C, o quizá un Brier estratificado. Como muestra el ejemplo, un fuerte desequilibrio disminuye la diferencia entre un modelo de referencia y un modelo predictivo. Este trabajo va en una dirección similar: ieeexplore.ieee.org/document/6413859

Edita: Para continuar con la discusión en los comentarios, que se refiere en parte al error debido al muestreo para un modelo entrenado en un conjunto de datos desequilibrado frente a uno equilibrado, he utilizado una segunda pequeña modificación del script (completo versión 2 del nuevo script más abajo). En esta modificación, los conjuntos de datos para la prueba de los modelos predictivos originales se realiza en un conjunto de prueba, mientras que los modelos "0" se prueban en un "conjunto_de_prueba_nuevo" separado, que se genera utilizando el mismo código. Esto representa una nueva muestra de la misma población o un nuevo "lote" o "minilote" o subconjunto de los datos utilizados para entrenar los modelos con el descenso de gradiente. Ahora el "gradiente" del Brier de un modelo no predictivo a uno predictivo parece bastante revelador:

>     round( quantile( (brier_score_logistic - brier_score_logistic_0)/brier_score_logistic_0), 3)
0%    25%    50%    75%   100% 
-0.221 -0.100 -0.052  0.019  0.131 
>     round( quantile( (brier_score_logistic_oversampled - brier_score_logistic_oversampled_0)/brier_score_logistic_oversampled_0),3)
0%    25%    50%    75%   100% 
-0.318 -0.258 -0.242 -0.215 -0.135 
>     
  >     round( quantile( (brier_score_randomForest - brier_score_randomForest_0)/brier_score_randomForest_0), 3)
0%    25%    50%    75%   100% 
-0.213 -0.092 -0.046  0.020  0.127 
>     round( quantile( (brier_score_randomForest_oversampled - brier_score_randomForest_oversampled_0)/brier_score_randomForest_oversampled_0), 3)
0%    25%    50%    75%   100% 
-0.304 -0.273 -0.255 -0.232 -0.155 
>     round( mean(brier_score_logistic>brier_score_logistic_0), 3)
[1] 0.31
>     round( mean(brier_score_randomForest>brier_score_randomForest_0), 3)
[1] 0.33

Así pues, ahora, en el 31-33% de las simulaciones de modelos desequilibrados, la puntuación Brier del modelo "0" es "mejor" (menor) que la puntuación del modelo predictivo, ¡a pesar de un tamaño de muestra de 10.000! Mientras que para los modelos entrenados con datos equilibrados, el gradiente del Brier está sistemáticamente en la dirección correcta (modelos predictivos inferiores a los modelos "0"). Me parece que esto se debe claramente a la variabilidad del muestreo en el entorno desequilibrado, donde incluso pequeñas variaciones (observación individual) dan lugar a una variabilidad mucho mayor en el rendimiento (como se ha observado anteriormente, el Brier general se ve más afectado por la prevalencia que por los predictores reales cuando se entrena en un conjunto de datos desequilibrado). Como se discute más adelante espero que esto puede afectar fuertemente a cualquier enfoque de muestreo durante el entrenamiento de descenso de gradiente (minibatch, submuestreo, etc.), mientras que cuando se utiliza exactamente el mismo conjunto de datos durante cada época el efecto puede ser menos prominente.

La versión modificada del código de OP:

library(randomForest)
library(beanplot)

nn_train <- nn_test <- 1e4
n_sims <- 1e2

true_coefficients <- c(-7, 5, rep(0, 9))

incidence_train <- rep(NA, n_sims)
model_logistic_coefficients <- 
  model_logistic_oversampled_coefficients <- 
  matrix(NA, nrow=n_sims, ncol=length(true_coefficients))

brier_score_logistic <- brier_score_logistic_oversampled <- 
  brier_score_logistic_0 <-
  brier_score_logistic_oversampled_0 <- 
  brier_score_randomForest <- 
  brier_score_randomForest_oversampled <- 
  brier_score_randomForest_0 <- 
  brier_score_randomForest_oversampled_0 <- 
  rep(NA, n_sims)

#pb <- winProgressBar(max=n_sims)
for ( ii in 1:n_sims ) {
  print(ii)#setWinProgressBar(pb,ii,paste(ii,"of",n_sims))
  set.seed(ii)
  while ( TRUE ) {    # make sure we even have the minority 
    # class
    predictors_train <- matrix(
      runif(nn_train*(length(true_coefficients) - 1)), 
      nrow=nn_train)
    logit_train <- 
      cbind(1, predictors_train)%*%true_coefficients
    probability_train <- 1/(1+exp(-logit_train))
    outcome_train <- factor(runif(nn_train) <= 
                              probability_train)
    if ( sum(incidence_train[ii] <- 
             sum(outcome_train==TRUE))>0 ) break
  }
  dataset_train <- data.frame(outcome=outcome_train, 
                              predictors_train)

  index <- c(which(outcome_train==TRUE),  
             sample(which(outcome_train==FALSE),   
                    sum(outcome_train==TRUE)))

  model_logistic <- glm(outcome~., dataset_train, 
                        family="binomial")
  model_logistic_0 <- glm(outcome~., dataset_train[,-2], 
                        family="binomial")
  model_logistic_oversampled <- glm(outcome~., 
                                    dataset_train[index, ], family="binomial")
  model_logistic_oversampled_0 <- glm(outcome~., 
                                    dataset_train[index, -2], family="binomial")

  model_logistic_coefficients[ii, ] <- 
    coefficients(model_logistic)
  model_logistic_oversampled_coefficients[ii, ] <- 
    coefficients(model_logistic_oversampled)

  model_randomForest <- randomForest(outcome~., dataset_train)
  model_randomForest_0 <- randomForest(outcome~., dataset_train[,-2])

  model_randomForest_oversampled <- 
    randomForest(outcome~., dataset_train, subset=index)
  model_randomForest_oversampled_0 <- 
    randomForest(outcome~., dataset_train[,-2], subset=index)

  predictors_test <- matrix(runif(nn_test * 
                                    (length(true_coefficients) - 1)), nrow=nn_test)
  logit_test <- cbind(1, predictors_test)%*%true_coefficients
  probability_test <- 1/(1+exp(-logit_test))
  outcome_test <- factor(runif(nn_test)<=probability_test)
  dataset_test <- data.frame(outcome=outcome_test, 
                             predictors_test)

  prediction_logistic <- predict(model_logistic, dataset_test, 
                                 type="response")
  brier_score_logistic[ii] <- mean((prediction_logistic - 
                                      (outcome_test==TRUE))^2)
  prediction_logistic_0 <- predict(model_logistic_0, dataset_test[,-2], 
                                 type="response")
  brier_score_logistic_0[ii] <- mean((prediction_logistic_0 - 
                                      (outcome_test==TRUE))^2)

  prediction_logistic_oversampled <-      
    predict(model_logistic_oversampled, dataset_test, 
            type="response")
  brier_score_logistic_oversampled[ii] <- 
    mean((prediction_logistic_oversampled - 
            (outcome_test==TRUE))^2)
  prediction_logistic_oversampled_0 <-      
    predict(model_logistic_oversampled_0, dataset_test[,-2], 
            type="response")
  brier_score_logistic_oversampled_0[ii] <- 
    mean((prediction_logistic_oversampled_0 - 
            (outcome_test==TRUE))^2)

  prediction_randomForest <- predict(model_randomForest, 
                                     dataset_test, type="prob")
  brier_score_randomForest[ii] <-
    mean((prediction_randomForest[,2]-(outcome_test==TRUE))^2)

  prediction_randomForest_0 <- predict(model_randomForest_0, 
                                     dataset_test[,-2], type="prob")
  brier_score_randomForest_0[ii] <-
    mean((prediction_randomForest_0[,2]-(outcome_test==TRUE))^2)

  prediction_randomForest_oversampled <-   
    predict(model_randomForest_oversampled, 
            dataset_test, type="prob")
  brier_score_randomForest_oversampled[ii] <- 
    mean((prediction_randomForest_oversampled[, 2] - 
            (outcome_test==TRUE))^2)

  prediction_randomForest_oversampled_0 <-   
    predict(model_randomForest_oversampled_0, 
            dataset_test, type="prob")
  brier_score_randomForest_oversampled_0[ii] <- 
    mean((prediction_randomForest_oversampled_0[, 2] - 
            (outcome_test==TRUE))^2)
}
#close(pb)

quantile( (brier_score_logistic - brier_score_logistic_0)/brier_score_logistic_0)
quantile( (brier_score_logistic_oversampled - brier_score_logistic_oversampled_0)/brier_score_logistic_oversampled_0)

quantile( (brier_score_randomForest - brier_score_randomForest_0)/brier_score_randomForest_0)
quantile( (brier_score_randomForest_oversampled - brier_score_randomForest_oversampled_0)/brier_score_randomForest_oversampled_0)

Versión 2:

library(randomForest)
library(beanplot)

nn_train <- nn_test <- 1e4
n_sims <- 1e2

true_coefficients <- c(-7, 5, rep(0, 9))

incidence_train <- rep(NA, n_sims)
model_logistic_coefficients <- 
  model_logistic_oversampled_coefficients <- 
  matrix(NA, nrow=n_sims, ncol=length(true_coefficients))

brier_score_logistic <- brier_score_logistic_oversampled <- 
  brier_score_logistic_0 <-
  brier_score_logistic_oversampled_0 <- 
  brier_score_randomForest <- 
  brier_score_randomForest_oversampled <- 
  brier_score_randomForest_0 <- 
  brier_score_randomForest_oversampled_0 <- 
  rep(NA, n_sims)

#pb <- winProgressBar(max=n_sims)
for ( ii in 1:n_sims ) {
  print(ii)#setWinProgressBar(pb,ii,paste(ii,"of",n_sims))
  set.seed(ii)
  while ( TRUE ) {    # make sure we even have the minority 
    # class
    predictors_train <- matrix(
      runif(nn_train*(length(true_coefficients) - 1)), 
      nrow=nn_train)
    logit_train <- 
      cbind(1, predictors_train)%*%true_coefficients
    probability_train <- 1/(1+exp(-logit_train))
    outcome_train <- factor(runif(nn_train) <= 
                              probability_train)
    if ( sum(incidence_train[ii] <- 
             sum(outcome_train==TRUE))>0 ) break
  }
  dataset_train <- data.frame(outcome=outcome_train, 
                              predictors_train)

  index <- c(which(outcome_train==TRUE),  
             sample(which(outcome_train==FALSE),   
                    sum(outcome_train==TRUE)))

  model_logistic <- glm(outcome~., dataset_train, 
                        family="binomial")
  model_logistic_0 <- glm(outcome~., dataset_train[,-2], 
                        family="binomial")
  model_logistic_oversampled <- glm(outcome~., 
                                    dataset_train[index, ], family="binomial")
  model_logistic_oversampled_0 <- glm(outcome~., 
                                    dataset_train[index, -2], family="binomial")

  model_logistic_coefficients[ii, ] <- 
    coefficients(model_logistic)
  model_logistic_oversampled_coefficients[ii, ] <- 
    coefficients(model_logistic_oversampled)

  model_randomForest <- randomForest(outcome~., dataset_train)
  model_randomForest_0 <- randomForest(outcome~., dataset_train[,-2])

  model_randomForest_oversampled <- 
    randomForest(outcome~., dataset_train, subset=index)
  model_randomForest_oversampled_0 <- 
    randomForest(outcome~., dataset_train[,-2], subset=index)

  predictors_test <- matrix(runif(nn_test * 
                                    (length(true_coefficients) - 1)), nrow=nn_test)
  logit_test <- cbind(1, predictors_test)%*%true_coefficients
  probability_test <- 1/(1+exp(-logit_test))
  outcome_test <- factor(runif(nn_test)<=probability_test)
  dataset_test <- data.frame(outcome=outcome_test, 
                             predictors_test)

  prediction_logistic <- predict(model_logistic, dataset_test, 
                                 type="response")
  brier_score_logistic[ii] <- mean((prediction_logistic - 
                                      (outcome_test==TRUE))^2)

  prediction_logistic_oversampled <-      
    predict(model_logistic_oversampled, dataset_test, 
            type="response")
  brier_score_logistic_oversampled[ii] <- 
    mean((prediction_logistic_oversampled - 
            (outcome_test==TRUE))^2)

  prediction_randomForest <- predict(model_randomForest, 
                                     dataset_test, type="prob")
  brier_score_randomForest[ii] <-
    mean((prediction_randomForest[,2]-(outcome_test==TRUE))^2)

  prediction_randomForest_oversampled <-   
    predict(model_randomForest_oversampled, 
            dataset_test, type="prob")
  brier_score_randomForest_oversampled[ii] <- 
    mean((prediction_randomForest_oversampled[, 2] - 
            (outcome_test==TRUE))^2)

  #sampling another testing dataset for "0" model
  predictors_test <- matrix(runif(nn_test * 
                                    (length(true_coefficients) - 1)), nrow=nn_test)
  logit_test <- cbind(1, predictors_test)%*%true_coefficients
  probability_test <- 1/(1+exp(-logit_test))
  outcome_test <- factor(runif(nn_test)<=probability_test)
  dataset_test_new <- data.frame(outcome=outcome_test, 
                             predictors_test)

  prediction_logistic_0 <- predict(model_logistic_0, dataset_test_new[,-2], 
                                   type="response")
  brier_score_logistic_0[ii] <- mean((prediction_logistic_0 - 
                                        (outcome_test==TRUE))^2)
  prediction_logistic_oversampled_0 <-      
    predict(model_logistic_oversampled_0, dataset_test_new[,-2], 
            type="response")
  brier_score_logistic_oversampled_0[ii] <- 
    mean((prediction_logistic_oversampled_0 - 
            (outcome_test==TRUE))^2)

  prediction_randomForest_0 <- predict(model_randomForest_0, 
                                       dataset_test_new[,-2], type="prob")
  brier_score_randomForest_0[ii] <-
    mean((prediction_randomForest_0[,2]-(outcome_test==TRUE))^2)

  prediction_randomForest_oversampled_0 <-   
    predict(model_randomForest_oversampled_0, 
            dataset_test_new, type="prob")
  brier_score_randomForest_oversampled_0[ii] <- 
    mean((prediction_randomForest_oversampled_0[, 2] - 
            (outcome_test==TRUE))^2)
}
#close(pb)

round( quantile( (brier_score_logistic - brier_score_logistic_0)/brier_score_logistic_0), 3)
round( quantile( (brier_score_logistic_oversampled - brier_score_logistic_oversampled_0)/brier_score_logistic_oversampled_0),3)

round( quantile( (brier_score_randomForest - brier_score_randomForest_0)/brier_score_randomForest_0), 3)
round( quantile( (brier_score_randomForest_oversampled - brier_score_randomForest_oversampled_0)/brier_score_randomForest_oversampled_0), 3)

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X