R: implementando meu próprio algoritmo de aumento de gradiente

10

Estou tentando escrever meu próprio algoritmo de aumento de gradiente. Eu entendo que existem pacotes como gbme, xgboost,mas eu queria entender como o algoritmo funciona escrevendo meus próprios.

Estou usando o irisconjunto de dados e meu resultado é Sepal.Length(contínuo). Minha função de perda é mean(1/2*(y-yhat)^2)(basicamente o erro quadrático médio com 1/2 na frente), então meu gradiente correspondente é apenas o residual y - yhat. Estou inicializando as previsões em 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)
}

Com isso, divido o irisconjunto de dados em um conjunto de dados de treinamento e teste e ajustei meu modelo a ele.

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)

Agora eu calculo os valores previstos de my.model. Pois my.model, os valores ajustados são 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

Eu tenho algumas perguntas

  1. Meu algoritmo de aumento de gradiente parece certo?
  2. Eu calculei os valores previstos yhats.mymodcorretamente?
YQW
fonte

Respostas:

0
  1. Sim, isso parece correto. Em cada etapa, você está se ajustando aos psuedo-resíduos, que são calculados como derivado da perda em relação ao ajuste. Você derivou corretamente esse gradiente no início da sua pergunta e até se preocupou em acertar o fator 2.
  2. Isso também parece correto. Você está agregando os modelos, ponderados pela taxa de aprendizado, exatamente como você fez durante o treinamento.

Mas, para abordar algo que não foi perguntado, notei que sua configuração de treinamento tem algumas peculiaridades.

  • O irisconjunto de dados é dividido igualmente entre três espécies (setosa, versicolor, virginica) e estas são adjacentes nos dados. Seus dados de treinamento possuem toda a setosa e versicolor, enquanto o conjunto de testes possui todos os exemplos de virginica. Não há sobreposição, o que levará a problemas fora da amostra. É preferível equilibrar seus conjuntos de treinamento e teste para evitar isso.
  • A combinação de taxa de aprendizado e contagem de modelos me parece muito baixa. O ajuste converge como (1-lr)^n. Com lr = 1e-3e n = 1000você só pode modelar 63,2% da magnitude dos dados. Ou seja, mesmo que todo modelo preveja cada amostra corretamente, você estimaria 63,2% do valor correto. A inicialização do ajuste com uma média, em vez de 0s, ajudaria desde então o efeito é uma regressão à média em vez de apenas um arrasto.
mcskinner
fonte
Obrigado por seus comentários. Você poderia expandir por que o "ajuste converge como (1-lr) ^ n"? Qual é a lógica por trás disso?
YQW
É porque fit <- fit + learning.rate * prediction, onde predictionestá o residual target - fit. Então fit <- fit + lr * (target - fit)ou fit <- fit * (1 - lr) + target * lr. Esta é apenas uma média móvel exponencial. Segundo a Wikipedia , "o peso omitido pela interrupção após k termos está (1-α)^kfora do peso total" ( αé a taxa de aprendizado e ké n). Você está começando com uma estimativa de 0 em vez da média, portanto, esse peso omitido sai diretamente da previsão.
mcskinner 26/04