R: Implementacja własnego algorytmu zwiększania gradientu

10

Próbuję napisać własny algorytm zwiększania gradientu. Rozumiem, że są istniejące pakiety, jak gbmi xgboost,, ale chciałam zrozumieć, jak działa algorytm pisząc własną rękę.

Korzystam z iriszestawu danych, a mój wynik jest Sepal.Length(ciągły). Moja funkcja straty to mean(1/2*(y-yhat)^2)(w zasadzie średni błąd kwadratu z 1/2 z przodu), więc mój odpowiedni gradient jest tylko resztkowy y - yhat. Inicjuję prognozy na 0.

library(rpart)
data(iris)

#Define gradient
grad.fun <- function(y, yhat) {return(y - yhat)}

mod <- list()

grad_boost <- function(data, learning.rate, M, grad.fun) {
  # Initialize fit to be 0
  fit <- rep(0, nrow(data))
  grad <- grad.fun(y = data$Sepal.Length, yhat = fit)

  # Initialize model
  mod[[1]] <- fit

  # Loop over a total of M iterations
  for(i in 1:M){

    # Fit base learner (tree) to the gradient
    tmp <- data$Sepal.Length
    data$Sepal.Length <- grad
    base_learner <- rpart(Sepal.Length ~ ., data = data, control = ("maxdepth = 2"))
    data$Sepal.Length <- tmp

    # Fitted values by fitting current model
    fit <- fit + learning.rate * as.vector(predict(base_learner, newdata = data))

    # Update gradient
    grad <- grad.fun(y = data$Sepal.Length, yhat = fit)

    # Store current model (index is i + 1 because i = 1 contain the initialized estiamtes)
    mod[[i + 1]] <- base_learner

  }
  return(mod)
}

Dzięki temu podzieliłem iriszestaw danych na zestaw danych szkoleniowych i testowych i dopasowałem do niego mój model.

train.dat <- iris[1:100, ]
test.dat <- iris[101:150, ]
learning.rate <- 0.001
M = 1000
my.model <- grad_boost(data = train.dat, learning.rate = learning.rate, M = M, grad.fun = grad.fun)

Teraz obliczam przewidywane wartości na podstawie my.model. Dla my.modeldopasowanych wartości są 0 (vector of initial estimates) + learning.rate * predictions from tree 1 + learning rate * predictions from tree 2 + ... + learning.rate * predictions from tree M.

yhats.mymod <- apply(sapply(2:length(my.model), function(x) learning.rate * predict(my.model[[x]], newdata = test.dat)), 1, sum)

# Calculate RMSE
> sqrt(mean((test.dat$Sepal.Length - yhats.mymod)^2))
[1] 2.612972

mam parę pytań

  1. Czy mój algorytm zwiększania gradientu wygląda poprawnie?
  2. Czy yhats.mymodpoprawnie wyliczyłem przewidywane wartości ?
YQW
źródło

Odpowiedzi:

0
  1. Tak, to wygląda poprawnie. Na każdym etapie dopasowujesz się do reszty psuedo, które są obliczane jako pochodna straty w odniesieniu do dopasowania. Poprawnie wyprowadziłeś ten gradient na początku pytania, a nawet starałeś się uzyskać współczynnik 2 w prawo.
  2. To również wygląda poprawnie. Agregujesz według modeli, ważonych według współczynnika uczenia się, tak jak podczas treningu.

Ale aby odpowiedzieć na coś, o co nie pytano, zauważyłem, że twoje ustawienie treningu ma kilka dziwactw.

  • Zestaw irisdanych jest równo podzielony na 3 gatunki (setosa, versicolor, virginica) i te sąsiadują ze sobą w danych. Twoje dane treningowe zawierają wszystkie kolory setosa i versicolor, a zestaw testowy zawiera wszystkie przykłady virginica. Nie ma nakładania się, co doprowadzi do problemów poza próbą. Lepiej jest zrównoważyć zestawy treningowe i testowe, aby tego uniknąć.
  • Połączenie szybkości uczenia się i liczby modeli wydaje mi się zbyt niskie. Dopasowanie jest zbieżne jak (1-lr)^n. Za pomocą lr = 1e-3i n = 1000można modelować tylko 63,2% wielkości danych. Oznacza to, że nawet jeśli każdy model prawidłowo przewiduje każdą próbkę, oszacowałbyś 63,2% prawidłowej wartości. Inicjowanie dopasowania za pomocą średniej zamiast zera pomógłoby od tego czasu efektem jest regresja do średniej zamiast tylko przeciągania.
Mcskinner
źródło
Dziękuję za twoje komentarze. Czy możesz wyjaśnić, dlaczego „dopasowanie jest zbieżne jako (1-lr) ^ n”? Jakie jest uzasadnienie tego?
YQW
Jest tak fit <- fit + learning.rate * prediction, ponieważ gdzie predictionjest resztkowe target - fit. Więc fit <- fit + lr * (target - fit)lub fit <- fit * (1 - lr) + target * lr. To tylko wykładnicza średnia ruchoma. Według Wikipedii „waga pomijana przez zatrzymanie po tym, jak k terminów jest (1-α)^kpoza całkowitą wagą” ( αto wskaźnik uczenia się i kwynosi n). Zaczynasz z oszacowaniem 0 zamiast średniej, więc ta pominięta waga pochodzi bezpośrednio z prognozy.
mcskinner,