Losowe Wagi leśne i klasowe

11

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 Rpakietu 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 FALSEwszędzie.

2) przy uczciwej wadze klasy zobaczę „zieloną kropkę” pośrodku, tzn. Przewidzi dysk o promieniu 1, TRUEjakkolwiek istnieją przykłady negatywne.

Tak wyglądają dane:

wprowadź opis zdjęcia tutaj

Tak dzieje się bez ważenia: (połączenie to randomForest(x = train[, .(x,y)],y = as.factor(train$z),ntree = 50):)

wprowadź opis zdjęcia tutaj

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:

wprowadź opis zdjęcia tutaj

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:

wprowadź opis zdjęcia tutaj

Dopiero gdy ustawię wagi na jakąś dziwną wartość, np. „FAŁSZ” = 0,05 i „PRAWDA” = 500000, otrzymam sensowne wyniki:

wprowadź opis zdjęcia tutaj

Jest to dość niestabilne, tzn. Zmiana masy „FALSE” na 0,01 powoduje, że model ponownie się degeneruje (tzn. TRUEWszę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")
Fabian Werner
źródło
jeśli sampsize działa, dlaczego tego nie użyć? Odkryłem również, że sampsize działa lepiej, aby rozwiązać ten problem, podobnie jak inni. Zobacz także świetną odpowiedź tutaj stats.stackexchange.com/questions/157714/…
katya
daply jest w plyr, powinieneś to nazwać.
EngrStudent
classwt Priory klas. Nie trzeba sumować do jednego. Zignorowano dla regresji.
Diego
@Diego: Parametr ten wydaje się być bardzo problematyczne, ja nie rozumiem, jak zachowuje się ten parametr (patrz: komentarze w pytaniu!) ...
Fabian Werner

Odpowiedzi:

2

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 .

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ź .

Stephan Kolassa
źródło
1
to jest temat, o którym dziś mówiłeś, prawda? Poszukam tego w mojej pracy :-)
Jonas Heidelberg