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 R
package randomForest
pour 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 FALSE
partout.
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 TRUE
s'il y avait des exemples négatifs.
Voici à quoi ressemblent les données:
C'est ce qui se passe sans pondération: (appel: randomForest(x = train[, .(x,y)],y = as.factor(train$z),ntree = 50)
)
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:
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:
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:
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 TRUE
partout).
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")