Poids aléatoire de la forêt et de la classe

11

Question en une phrase: quelqu'un sait-il comment déterminer les bons poids de classe pour une forêt aléatoire?

Explication: je joue avec des jeux de données déséquilibrés. Je veux utiliser le Rpackage randomForestpour former un modèle sur un ensemble de données très asymétrique avec seulement de petits exemples positifs et de nombreux exemples négatifs. Je sais, il existe d'autres méthodes et au final je vais les utiliser mais pour des raisons techniques, la construction d'une forêt aléatoire est une étape intermédiaire. J'ai donc joué avec le paramètre classwt. Je mets en place un ensemble de données très artificiel de 5000 exemples négatifs sur le disque de rayon 2, puis j'échantillon 100 exemples positifs sur le disque de rayon 1. Ce que je soupçonne, c'est que

1) sans pondération de classe, le modèle devient «dégénéré», c'est-à-dire prédit FALSEpartout.

2) avec une pondération de classe équitable, je verrai un «point vert» au milieu, c'est-à-dire qu'il prédira le disque de rayon 1 comme TRUEs'il y avait des exemples négatifs.

Voici à quoi ressemblent les données:

entrez la description de l'image ici

C'est ce qui se passe sans pondération: (appel: randomForest(x = train[, .(x,y)],y = as.factor(train$z),ntree = 50))

entrez la description de l'image ici

Pour vérifier, j'ai également essayé ce qui se passe lorsque j'équilibre violemment l'ensemble de données en sous-échantillonnant la classe négative afin que la relation soit à nouveau 1: 1. Cela me donne le résultat attendu:

entrez la description de l'image ici

Cependant, lorsque je calcule un modèle avec une pondération de classe de 'FALSE' = 1, 'TRUE' = 50 (c'est une pondération équitable car il y a 50 fois plus de négatifs que de positifs), alors j'obtiens ceci:

entrez la description de l'image ici

Ce n'est que lorsque j'ai défini les poids sur une valeur étrange comme «FALSE» = 0,05 et «TRUE» = 500000 que j'obtiens des résultats judicieux:

entrez la description de l'image ici

Et cela est assez instable, c'est-à-dire que changer le poids «FAUX» à 0,01 fait dégénérer à nouveau le modèle (c'est-à-dire qu'il prédit TRUEpartout).

Question: Quelqu'un sait-il comment déterminer les bons poids de classe pour une forêt aléatoire?

Code 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
la source
si sampsize fonctionne, pourquoi ne pas l'utiliser? J'ai également trouvé que sampsize fonctionnait mieux pour résoudre ce problème, comme d'autres. Voir également une excellente réponse ici stats.stackexchange.com/questions/157714/…
katya
daply est en plyr, vous devriez appeler cela.
EngrStudent
classwt Prieurs des classes. Pas besoin d'en ajouter un. Ignoré pour la régression.
Diego
@Diego: Ce paramètre semble être très problématique, je ne comprends pas comment ce paramètre se comporte (voir les commentaires dans la question!) ...
Fabian Werner

Réponses:

2

N'utilisez pas de coupure stricte pour classer une appartenance matérielle et n'utilisez pas de KPI qui dépendent d'une telle prédiction d'appartenance matérielle. Au lieu de cela, travaillez avec une prédiction probabiliste, utilisez predict(..., type="prob")-les et évaluez-les en utilisant .

Ce fil de discussion précédent devrait être utile: pourquoi la précision n'est-elle pas la meilleure mesure pour évaluer les modèles de classification? Sans surprise, je pense que ma réponse serait particulièrement utile (désolé pour l'impudeur), tout comme une réponse précédente .

Stephan Kolassa
la source
1
c'est le sujet dont vous avez parlé aujourd'hui, non? Je vais chercher cela dans mon travail :-)
Jonas Heidelberg