RandomForest y pesos de clase

11

Pregunta en una oración: ¿Alguien sabe cómo determinar los buenos pesos de clase para un bosque aleatorio?

Explicación: estoy jugando con conjuntos de datos desequilibrados. Quiero usar el Rpaquete randomForestpara entrenar un modelo en un conjunto de datos muy sesgado con solo pocos ejemplos positivos y muchos ejemplos negativos. Lo sé, hay otros métodos y al final los utilizaré, pero por razones técnicas, construir un bosque aleatorio es un paso intermedio. Así que jugué con el parámetro classwt. Estoy configurando un conjunto de datos muy artificial de 5000 ejemplos negativos en el disco con radio 2 y luego muestro 100 ejemplos positivos en el disco con radio 1. Lo que sospecho es que

1) sin ponderación de clase, el modelo se vuelve 'degenerado', es decir, predice en FALSEtodas partes.

2) con una ponderación de clase justa veré un 'punto verde' en el medio, es decir, predecirá el disco con radio 1 como TRUEsi hubiera ejemplos negativos.

Así es como se ven los datos:

ingrese la descripción de la imagen aquí

Esto es lo que sucede sin ponderación: (llamada es: randomForest(x = train[, .(x,y)],y = as.factor(train$z),ntree = 50))

ingrese la descripción de la imagen aquí

Para verificar, también probé lo que sucede cuando balanceo violentamente el conjunto de datos reduciendo el muestreo de la clase negativa para que la relación sea 1: 1 nuevamente. Esto me da el resultado esperado:

ingrese la descripción de la imagen aquí

Sin embargo, cuando calculo un modelo con una ponderación de clase de 'FALSO' = 1, 'VERDADERO' = 50 (esta es una ponderación justa ya que hay 50 veces más negativos que positivos), entonces obtengo esto:

ingrese la descripción de la imagen aquí

Solo cuando establezco los pesos en algún valor extraño como 'FALSO' = 0.05 y 'VERDADERO' = 500000, obtengo resultados sensatos:

ingrese la descripción de la imagen aquí

Y esto es bastante inestable, es decir, cambiar el peso 'FALSO' a 0.01 hace que el modelo se degenere nuevamente (es decir, predice en TRUEtodas partes).

Pregunta: ¿Alguien sabe cómo determinar los buenos pesos de clase para un bosque aleatorio?

Código R:

library(plot3D)
library(data.table)
library(randomForest)
set.seed(1234)
amountPos = 100
amountNeg = 5000

# positives
r = runif(amountPos, 0, 1)
phi = runif(amountPos, 0, 2*pi)
x = r*cos(phi)
y = r*sin(phi)
z = rep(T, length(x))
pos = data.table(x = x, y = y, z = z)

# negatives
r = runif(amountNeg, 0, 2)
phi = runif(amountNeg, 0, 2*pi)
x = r*cos(phi)
y = r*sin(phi)
z = rep(F, length(x))
neg = data.table(x = x, y = y, z = z)

train = rbind(pos, neg)

# draw train set, verify that everything looks ok
plot(train[z == F]$x, train[z == F]$y, col="red")
points(train[z == T]$x, train[z == T]$y, col="green")
# looks ok to me :-)

Color.interpolateColor = function(fromColor, toColor, amountColors = 50) {
  from_rgb = col2rgb(fromColor)
  to_rgb = col2rgb(toColor)

  from_r = from_rgb[1,1]
  from_g = from_rgb[2,1]
  from_b = from_rgb[3,1]

  to_r = to_rgb[1,1]
  to_g = to_rgb[2,1]
  to_b = to_rgb[3,1]

  r = seq(from_r, to_r, length.out = amountColors)
  g = seq(from_g, to_g, length.out = amountColors)
  b = seq(from_b, to_b, length.out = amountColors)

  return(rgb(r, g, b, maxColorValue = 255))
}
DataTable.crossJoin = function(X,Y) {
  stopifnot(is.data.table(X),is.data.table(Y))
  k = NULL
  X = X[, c(k=1, .SD)]
  setkey(X, k)
  Y = Y[, c(k=1, .SD)]
  setkey(Y, k)
  res = Y[X, allow.cartesian=TRUE][, k := NULL]
  X = X[, k := NULL]
  Y = Y[, k := NULL]
  return(res)
}

drawPredictionAreaSimple = function(model) {
  widthOfSquares = 0.1
  from = -2
  to = 2

  xTable = data.table(x = seq(from=from+widthOfSquares/2,to=to-widthOfSquares/2,by = widthOfSquares))
  yTable = data.table(y = seq(from=from+widthOfSquares/2,to=to-widthOfSquares/2,by = widthOfSquares))
  predictionTable = DataTable.crossJoin(xTable, yTable)
  pred = predict(model, predictionTable)
  res = rep(NA, length(pred))
  res[pred == "FALSE"] = 0
  res[pred == "TRUE"] = 1
  pred = res
  predictionTable = predictionTable[, PREDICTION := pred]
  #predictionTable = predictionTable[y == -1 & x == -1, PREDICTION := 0.99]
  col = Color.interpolateColor("red", "green")

  input = matrix(c(predictionTable$x, predictionTable$y), nrow = 2, byrow = T)
  m = daply(predictionTable, .(x, y), function(x) x$PREDICTION)
  image2D(z = m, x = sort(unique(predictionTable$x)), y = sort(unique(predictionTable$y)), col = col, zlim = c(0,1))
}


rfModel = randomForest(x = train[, .(x,y)],y = as.factor(train$z),ntree = 50)
rfModelBalanced = randomForest(x = train[, .(x,y)],y = as.factor(train$z),ntree = 50, classwt = c("FALSE" = 1, "TRUE" = 50))
rfModelBalancedWeird = randomForest(x = train[, .(x,y)],y = as.factor(train$z),ntree = 50, classwt = c("FALSE" = 0.05, "TRUE" = 500000))


drawPredictionAreaSimple(rfModel)
title("unbalanced")
drawPredictionAreaSimple(rfModelBalanced)
title("balanced with weights")
pos = train[z == T]
neg = train[z == F]
neg = neg[sample.int(neg[, .N], size = 100, replace = FALSE)]
trainSampled = rbind(pos, neg)
rfModelBalancedSampling = randomForest(x = trainSampled[, .(x,y)],y = as.factor(trainSampled$z),ntree = 50)
drawPredictionAreaSimple(rfModelBalancedSampling)
title("balanced with sampling")


drawPredictionAreaSimple(rfModelBalancedWeird)
title("balanced with weird weights")
Fabian Werner
fuente
si sampsize funciona, ¿por qué no usar eso? También encontré que Sampsize funciona mejor para resolver esto, al igual que otros. También vea una gran respuesta aquí stats.stackexchange.com/questions/157714/…
katya
daply está en plyr, deberías llamarlo así.
EngrStudent
classwt Priors de las clases. No es necesario sumar uno. Ignorado para la regresión.
Diego
@Diego: Este parámetro parece ser muy problemático, no entiendo cómo se comporta este parámetro (¡vea los comentarios en la pregunta!) ...
Fabian Werner

Respuestas:

2

No use un límite estricto para clasificar una membresía dura, y no use KPI que dependan de una predicción de membresía tan dura. En cambio, trabaje con una predicción probabilística, utilícela predict(..., type="prob")y evalúelas utilizando las adecuadas .

Este hilo anterior debería ser útil: ¿por qué la precisión no es la mejor medida para evaluar los modelos de clasificación? Como era de esperar, creo que mi respuesta sería particularmente útil (perdón por la desvergüenza), al igual que una respuesta anterior mía .

Stephan Kolassa
fuente
1
Este es el tema del que hablaste hoy, ¿verdad? Buscaré esto en mi trabajo :-)
Jonas Heidelberg