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")
la source
Réponses:
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 règles de notation .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 .
la source