Pytanie w jednym zdaniu: Czy ktoś wie, jak ustalić dobre wagi klas dla losowego lasu?
Objaśnienie: Bawię się niezrównoważonymi zestawami danych. Chcę użyć tego R
pakietu randomForest
, aby wyszkolić model na bardzo wypaczonym zbiorze danych z niewielkimi pozytywnymi przykładami i wieloma negatywnymi przykładami. Wiem, że istnieją inne metody i na koniec z nich skorzystam, ale z przyczyn technicznych budowanie losowego lasu jest krokiem pośrednim. Więc bawiłem się parametrem classwt
. Tworzę bardzo sztuczny zbiór danych 5000 negatywnych przykładów na dysku o promieniu 2, a następnie próbkuję 100 pozytywnych przykładów na dysku o promieniu 1. Podejrzewam, że
1) bez ważenia klas model staje się „zdegenerowany”, tj. Przewiduje FALSE
wszędzie.
2) przy uczciwej wadze klasy zobaczę „zieloną kropkę” pośrodku, tzn. Przewidzi dysk o promieniu 1, TRUE
jakkolwiek istnieją przykłady negatywne.
Tak wyglądają dane:
Tak dzieje się bez ważenia: (połączenie to randomForest(x = train[, .(x,y)],y = as.factor(train$z),ntree = 50)
:)
Do sprawdzania próbowałem również tego, co się dzieje, gdy gwałtownie zbalansuję zestaw danych, próbkując w dół próbkę klasy ujemnej, tak aby relacja znów wynosiła 1: 1. To daje mi oczekiwany wynik:
Jednak gdy obliczam model z wagą klasy „FAŁSZ” = 1, „PRAWDA” = 50 (jest to odpowiednia waga, ponieważ istnieje 50 razy więcej negatywów niż pozytywów), otrzymuję to:
Dopiero gdy ustawię wagi na jakąś dziwną wartość, np. „FAŁSZ” = 0,05 i „PRAWDA” = 500000, otrzymam sensowne wyniki:
Jest to dość niestabilne, tzn. Zmiana masy „FALSE” na 0,01 powoduje, że model ponownie się degeneruje (tzn. TRUE
Wszędzie przewiduje ).
Pytanie: Czy ktoś wie, jak ustalić dobre wagi klas dla losowego lasu?
Kod 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")
źródło
Odpowiedzi:
Nie używaj twardego odcięcia do klasyfikowania twardego członkostwa i nie używaj kluczowych wskaźników wydajności, które zależą od tak trudnych prognoz członkostwa. Zamiast tego pracuj z probabilistyczną prognozą, używając
predict(..., type="prob")
i oceniaj je, stosując odpowiednie reguły punktacji .Ten wcześniejszy wątek powinien być pomocny: Dlaczego dokładność nie jest najlepszym miernikiem do oceny modeli klasyfikacji? Nic dziwnego, że wierzę, że moja odpowiedź byłaby szczególnie pomocna (przepraszam za bezwstyd), podobnie jak moja wcześniejsza odpowiedź .
źródło