O XGBoost lida com multicolinearidade sozinho?

23

Atualmente, estou usando o XGBoost em um conjunto de dados com 21 recursos (selecionados da lista de cerca de 150 recursos), depois os codifiquei com um hot-code para obter ~ 98 recursos. Alguns desses 98 recursos são um pouco redundantes, por exemplo: uma variável (recurso) também aparece como BA eCBA .CA

Minhas perguntas são:

  • Como ( se? ) As Árvores de Decisão Impulsionadas lidam com multicolinearidade?
  • Como a existência da multicolinearidade afetaria a previsão se não fosse tratada?

Pelo que entendi, o modelo está aprendendo mais de uma árvore e a previsão final é baseada em algo como uma "soma ponderada" das previsões individuais. Portanto, se isso estiver correto, as Boosted Decision Trees devem poder lidar com a co-dependência entre variáveis.

Além disso, em uma observação relacionada - como o objeto de importância variável no XGBoost funciona?

user140323
fonte
Entendo que as árvores podem lidar com multicolinearidade. Mas e o XGBoost baseado em regressão? Ele também pode lidar com multicolinearidade? > As árvores de decisão são por natureza imunes à multicolinearidade. Por exemplo, se você possui 2 recursos correlacionados a 99%, ao decidir sobre uma divisão, a árvore escolherá apenas um deles. Outros modelos, como a regressão logística, usariam os dois recursos. >> Como as árvores potencializadas usam árvores de decisão individuais, elas também não são afetadas pela multicolinearidade. No entanto, é uma boa prática> remover todos os recursos redundantes de qualquer conjunto de dados usado para tra
Jay Saxena

Respostas:

27

As árvores de decisão são, por natureza, imunes à multicolinearidade. Por exemplo, se você tiver 2 recursos que são 99% correlacionados, ao decidir sobre uma divisão, a árvore escolherá apenas um deles. Outros modelos, como a regressão logística, usariam os dois recursos.

Como as árvores reforçadas usam árvores de decisão individuais, elas também não são afetadas pela multicolinearidade. No entanto, é uma boa prática remover todos os recursos redundantes de qualquer conjunto de dados usado para treinamento, independentemente do algoritmo do modelo. No seu caso, como você está obtendo novos recursos, você pode usar essa abordagem, avaliar a importância de cada recurso e reter apenas os melhores para o seu modelo final.

A matriz de importância de um modelo xgboost é na verdade um objeto data.table com a primeira coluna listando os nomes de todos os recursos realmente usados ​​nas árvores aumentadas. A segunda coluna é a métrica Ganho, que implica a contribuição relativa do recurso correspondente ao modelo calculado, levando-se a contribuição de cada recurso para cada árvore no modelo. Um valor mais alto dessa métrica quando comparado a outro recurso implica que é mais importante para gerar uma previsão.

Sandeep S. Sandhu
fonte
7

Fiquei curioso sobre isso e fiz alguns testes.

Treinei um modelo no conjunto de dados de diamantes e observei que a variável "x" é a mais importante para prever se o preço de um diamante é maior que um determinado limite. Em seguida, adicionei várias colunas altamente correlacionadas com x, executei o mesmo modelo e observei os mesmos valores.

Parece que quando a correlação entre duas colunas é 1, o xgboost remove a coluna extra antes de calcular o modelo, portanto a importância não é afetada. No entanto, quando você adiciona uma coluna parcialmente correlacionada a outra, portanto, com um coeficiente mais baixo, a importância da variável original x é reduzida.

Por exemplo, se eu adicionar uma variável xy = x + y, a importância de x e y diminui. Da mesma forma, a importância de x diminui se eu adicionar novas variáveis ​​com r = 0,4, 0,5 ou 0,6, embora apenas um pouco.

Eu acho que a colinearidade não é um problema para aumentar quando você calcula a precisão do modelo, porque a árvore de decisão não se importa com qual das variáveis ​​é usada. No entanto, isso pode afetar a importância das variáveis, porque a remoção de uma das duas variáveis ​​correlacionadas não afeta muito a precisão do modelo, uma vez que a outra contém informações semelhantes.

library(tidyverse)
library(xgboost)

evaluate_model = function(dataset) {
    print("Correlation matrix")
    dataset %>% select(-cut, -color, -clarity, -price) %>% cor %>% print

    print("running model")
    diamond.model = xgboost(
        data=dataset %>% select(-cut, -color, -clarity, -price) %>% as.matrix, 
        label=dataset$price > 400, 
        max.depth=15, nrounds=30, nthread=2, objective = "binary:logistic",
        verbose=F
        )

    print("Importance matrix")
    importance_matrix <- xgb.importance(model = diamond.model)
    importance_matrix %>% print
    xgb.plot.importance(importance_matrix)
    }

> diamonds %>% head
carat   cut color   clarity depth   table   price   x   y   z
0.23    Ideal   E   SI2 61.5    55  326 3.95    3.98    2.43
0.21    Premium E   SI1 59.8    61  326 3.89    3.84    2.31
0.23    Good    E   VS1 56.9    65  327 4.05    4.07    2.31
0.29    Premium I   VS2 62.4    58  334 4.20    4.23    2.63
0.31    Good    J   SI2 63.3    58  335 4.34    4.35    2.75
0.24    Very Good   J   VVS2    62.8    57  336 3.94    3.96    2.48

Avalie um modelo nos dados de diamantes

Prevemos se o preço é superior a 400, considerando todas as variáveis ​​numéricas disponíveis (quilate, profundidade, tabela, x, y, x)

Observe que x é a variável mais importante, com uma pontuação de ganho de importância de 0,375954.

evaluate_model(diamonds)
    [1] "Correlation matrix"
               carat       depth      table           x           y          z
    carat 1.00000000  0.02822431  0.1816175  0.97509423  0.95172220 0.95338738
    depth 0.02822431  1.00000000 -0.2957785 -0.02528925 -0.02934067 0.09492388
    table 0.18161755 -0.29577852  1.0000000  0.19534428  0.18376015 0.15092869
    x     0.97509423 -0.02528925  0.1953443  1.00000000  0.97470148 0.97077180
    y     0.95172220 -0.02934067  0.1837601  0.97470148  1.00000000 0.95200572
    z     0.95338738  0.09492388  0.1509287  0.97077180  0.95200572 1.00000000
    [1] "running model"
    [1] "Importance matrix"
       Feature       Gain      Cover  Frequency
    1:       x 0.37595419 0.54788335 0.19607102
    2:   carat 0.19699839 0.18015576 0.04873442
    3:   depth 0.15358261 0.08780079 0.27767284
    4:       y 0.11645929 0.06527969 0.18813751
    5:   table 0.09447853 0.05037063 0.17151492
    6:       z 0.06252699 0.06850978 0.11786929

Modelo treinado em diamantes, adicionando uma variável com r = 1 ex

Aqui, adicionamos uma nova coluna, que, no entanto, não adiciona nenhuma informação nova, pois está perfeitamente correlacionada com x.

Observe que essa nova variável não está presente na saída. Parece que o xgboost remove automaticamente variáveis ​​perfeitamente correlacionadas antes de iniciar o cálculo. O ganho de importância de x é o mesmo, 0,3759.

diamonds_xx = diamonds %>%
    mutate(xx = x + runif(1, -1, 1))
evaluate_model(diamonds_xx)
[1] "Correlation matrix"
           carat       depth      table           x           y          z
carat 1.00000000  0.02822431  0.1816175  0.97509423  0.95172220 0.95338738
depth 0.02822431  1.00000000 -0.2957785 -0.02528925 -0.02934067 0.09492388
table 0.18161755 -0.29577852  1.0000000  0.19534428  0.18376015 0.15092869
x     0.97509423 -0.02528925  0.1953443  1.00000000  0.97470148 0.97077180
y     0.95172220 -0.02934067  0.1837601  0.97470148  1.00000000 0.95200572
z     0.95338738  0.09492388  0.1509287  0.97077180  0.95200572 1.00000000
xx    0.97509423 -0.02528925  0.1953443  1.00000000  0.97470148 0.97077180
               xx
carat  0.97509423
depth -0.02528925
table  0.19534428
x      1.00000000
y      0.97470148
z      0.97077180
xx     1.00000000
[1] "running model"
[1] "Importance matrix"
   Feature       Gain      Cover  Frequency
1:       x 0.37595419 0.54788335 0.19607102
2:   carat 0.19699839 0.18015576 0.04873442
3:   depth 0.15358261 0.08780079 0.27767284
4:       y 0.11645929 0.06527969 0.18813751
5:   table 0.09447853 0.05037063 0.17151492
6:       z 0.06252699 0.06850978 0.11786929

Modelo treinado em diamantes, adicionando uma coluna para x + y

Nós adicionamos uma nova coluna xy = x + y. Isso está parcialmente correlacionado com ambos x e y.

Observe que a importância de xey é ligeiramente reduzida, passando de 0,3759 para 0,3592 para x e de 0,116 para 0,079 para y.

diamonds_xy = diamonds %>%
    mutate(xy=x+y)
evaluate_model(diamonds_xy)

[1] "Correlation matrix"
           carat       depth      table           x           y          z
carat 1.00000000  0.02822431  0.1816175  0.97509423  0.95172220 0.95338738
depth 0.02822431  1.00000000 -0.2957785 -0.02528925 -0.02934067 0.09492388
table 0.18161755 -0.29577852  1.0000000  0.19534428  0.18376015 0.15092869
x     0.97509423 -0.02528925  0.1953443  1.00000000  0.97470148 0.97077180
y     0.95172220 -0.02934067  0.1837601  0.97470148  1.00000000 0.95200572
z     0.95338738  0.09492388  0.1509287  0.97077180  0.95200572 1.00000000
xy    0.96945349 -0.02750770  0.1907100  0.99354016  0.99376929 0.96744200
              xy
carat  0.9694535
depth -0.0275077
table  0.1907100
x      0.9935402
y      0.9937693
z      0.9674420
xy     1.0000000
[1] "running model"
[1] "Importance matrix"
   Feature       Gain      Cover  Frequency
1:       x 0.35927767 0.52924339 0.15952849
2:   carat 0.17881931 0.18472506 0.04793713
3:   depth 0.14353540 0.07482622 0.24990177
4:   table 0.09202059 0.04714548 0.16267191
5:      xy 0.08203819 0.04706267 0.13555992
6:       y 0.07956856 0.05284980 0.13595285
7:       z 0.06474029 0.06414738 0.10844794

Modelo treinado em dados de diamantes, modificado adicionando colunas redundantes

Adicionamos três novas colunas que estão correlacionadas com x (r = 0,4, 0,5 e 0,6) e vemos o que acontece.

Observe que a importância de x é reduzida, passando de 0,3759 para 0,279.

#' given a vector of values (e.g. diamonds$x), calculate three new vectors correlated to it
#' 
#' Source: https://stat.ethz.ch/pipermail/r-help/2007-April/128938.html
calculate_correlated_vars = function(x1) {

    # create the initial x variable
    #x1 <- diamonds$x

    # x2, x3, and x4 in a matrix, these will be modified to meet the criteria
    x234 <- scale(matrix( rnorm(nrow(diamonds) * 3), ncol=3 ))

    # put all into 1 matrix for simplicity
    x1234 <- cbind(scale(x1),x234)

    # find the current correlation matrix
    c1 <- var(x1234)

    # cholesky decomposition to get independence
    chol1 <- solve(chol(c1))

    newx <-  x1234 %*% chol1 

    # check that we have independence and x1 unchanged
    zapsmall(cor(newx))
    all.equal( x1234[,1], newx[,1] )

    # create new correlation structure (zeros can be replaced with other r vals)
    newc <- matrix( 
    c(1  , 0.4, 0.5, 0.6, 
      0.4, 1  , 0  , 0  ,
      0.5, 0  , 1  , 0  ,
      0.6, 0  , 0  , 1  ), ncol=4 )

    # check that it is positive definite
    eigen(newc)

    chol2 <- chol(newc)

    finalx <- newx %*% chol2 * sd(x1) + mean(x1)

    # verify success
    mean(x1)
    colMeans(finalx)

    sd(x1)
    apply(finalx, 2, sd)

    zapsmall(cor(finalx))
    #pairs(finalx)

    all.equal(x1, finalx[,1])
    finalx
}
finalx = calculate_correlated_vars(diamonds$x)
diamonds_cor = diamonds
diamonds_cor$x5 = finalx[,2]
diamonds_cor$x6 = finalx[,3]
diamonds_cor$x7 = finalx[,4]
evaluate_model(diamonds_cor)
[1] "Correlation matrix"
           carat        depth       table           x           y          z
carat 1.00000000  0.028224314  0.18161755  0.97509423  0.95172220 0.95338738
depth 0.02822431  1.000000000 -0.29577852 -0.02528925 -0.02934067 0.09492388
table 0.18161755 -0.295778522  1.00000000  0.19534428  0.18376015 0.15092869
x     0.97509423 -0.025289247  0.19534428  1.00000000  0.97470148 0.97077180
y     0.95172220 -0.029340671  0.18376015  0.97470148  1.00000000 0.95200572
z     0.95338738  0.094923882  0.15092869  0.97077180  0.95200572 1.00000000
x5    0.39031255 -0.007507604  0.07338484  0.40000000  0.38959178 0.38734145
x6    0.48879000 -0.016481580  0.09931705  0.50000000  0.48835896 0.48487442
x7    0.58412252 -0.013772440  0.11822089  0.60000000  0.58408881 0.58297414
                 x5            x6            x7
carat  3.903125e-01  4.887900e-01  5.841225e-01
depth -7.507604e-03 -1.648158e-02 -1.377244e-02
table  7.338484e-02  9.931705e-02  1.182209e-01
x      4.000000e-01  5.000000e-01  6.000000e-01
y      3.895918e-01  4.883590e-01  5.840888e-01
z      3.873415e-01  4.848744e-01  5.829741e-01
x5     1.000000e+00  5.925447e-17  8.529781e-17
x6     5.925447e-17  1.000000e+00  6.683397e-17
x7     8.529781e-17  6.683397e-17  1.000000e+00
[1] "running model"
[1] "Importance matrix"
   Feature       Gain      Cover  Frequency
1:       x 0.27947762 0.51343709 0.09748172
2:   carat 0.13556427 0.17401365 0.02680747
3:      x5 0.13369515 0.05267688 0.18155971
4:      x6 0.12968400 0.04804315 0.19821284
5:      x7 0.10600238 0.05148826 0.16450041
6:   depth 0.07087679 0.04485760 0.11251015
7:       y 0.06050565 0.03896716 0.08245329
8:   table 0.04577057 0.03135677 0.07554833
9:       z 0.03842355 0.04515944 0.06092608
dalloliogm
fonte
6

Há uma resposta de Tianqi Chen (2018).

Essa diferença tem impacto em um caso de canto na análise de importância de recursos: os recursos correlatos. Imagine dois recursos perfeitamente correlacionados, o recurso A e o recurso B. Para uma árvore específica, se o algoritmo precisar de um deles, ele escolherá aleatoriamente (verdadeiro em boosting e Random Forests ™).

No entanto, no Random Forests ™, essa escolha aleatória será feita para cada árvore, porque cada árvore é independente das outras. Portanto, aproximadamente, dependendo de seus parâmetros, 50% das árvores escolherão o recurso A e os outros 50% escolherão o recurso B. Portanto, a importância das informações contidas em A e B (que é a mesma, porque estão perfeitamente correlacionadas ) é diluído em A e B. Portanto, você não saberá facilmente que essas informações são importantes para prever o que você deseja prever! É ainda pior quando você tem 10 recursos correlatos ...

Ao aumentar, quando um algoritmo aprende um vínculo específico entre recurso e resultado, ele tenta não se concentrar novamente (em teoria, é o que acontece, a realidade nem sempre é tão simples). Portanto, toda a importância estará no recurso A ou no recurso B (mas não nos dois). Você saberá que um recurso tem um papel importante no link entre as observações e o rótulo. Ainda depende de você procurar os recursos correlatos para aquele detectado como importante se precisar conhecer todos eles.

Para resumir, o Xgboost não usa aleatoriamente os recursos correlatos em cada árvore, que modelo de floresta aleatória sofre com essa situação.

Referência :

Tianqi Chen, Michaël Benesty, Tong He. 2018. “Entenda seu conjunto de dados com o Xgboost.” Https://cran.r-project.org/web/packages/xgboost/vignettes/discoverYourData.html#numeric-vs-categorical-variables .

Jiaxiang
fonte
2

Uma observação sobre a resposta de Sandeep: Supondo que dois de seus recursos sejam altamente colineares (digamos, 99% do tempo), de fato, apenas um recurso é selecionado em cada divisão, mas para a próxima divisão, o xgb pode selecionar o outro recurso. Portanto, o ranking de recursos xgb provavelmente classificará os dois recursos colineares igualmente. Sem algum conhecimento prévio ou outro processamento de recurso, você quase não tem meios desta classificação fornecida para detectar que os dois recursos são colineares.

Agora, quanto à importância relativa que gera o xgboost, ele deve ser muito semelhante (ou talvez exatamente semelhante) ao ranking de árvores impulsionadas pelo gradiente sklearn. Veja aqui as explicações.

PSAfrance
fonte