Czy XGBoost sam radzi sobie z wielokoliniowością?

23

Obecnie używam XGBoost na zestawie danych z 21 funkcjami (wybranymi z listy około 150 funkcji), a następnie zakodowałem je jednym kodem, aby uzyskać ~ 98 funkcji. Kilka z tych 98 funkcji jest nieco redundantnych, na przykład: zmienna (funkcja) pojawia się również jako i .B.ZA C.bZAdoZA

Moje pytania to:

  • W jaki sposób ( jeśli? ) Boosted drzew decyzyjnych radzi sobie z wielokoliniowością?
  • W jaki sposób istnienie wielokoliniowości wpłynęłoby na przewidywanie, jeśli nie zostanie ono obsłużone?

Z tego, co rozumiem, model uczy się więcej niż jednego drzewa, a ostateczna prognoza oparta jest na czymś w rodzaju „ważonej sumy” indywidualnych prognoz. Więc jeśli jest to poprawne, to drzewka decyzyjne wzmocnione powinny być w stanie obsłużyć współzależność między zmiennymi.

Również w powiązanej notatce - jak działa obiekt o zmiennej ważności w XGBoost?

użytkownik140323
źródło
Rozumiem, że drzewa potrafią obsłużyć wielokoliniowość. Ale co z XGBoost opartym na regresji? Czy poradzi sobie również z wielokolinearnością? > Drzewa decyzyjne są z natury odporne na wielokolinearność. Na przykład, jeśli masz 2 funkcje, które są w 99% skorelowane, kiedy> decydujesz o podziale, drzewo wybierze tylko jedną z nich. Inne> modele, takie jak regresja logistyczna, wykorzystałyby obie funkcje. >> Ponieważ drzewa wzmocnione używają pojedynczych drzew decyzyjnych, również nie ma na nie wpływu wiele kolinearności. Jednak dobrą praktyką jest> usuwanie wszelkich zbędnych funkcji z dowolnego zestawu danych wykorzystywanego do tra
Jay Saxena

Odpowiedzi:

27

Drzewa decyzyjne są z natury odporne na wielokulturowość. Na przykład, jeśli masz 2 funkcje, które są w 99% skorelowane, przy podejmowaniu decyzji o podziale drzewo wybierze tylko jedną z nich. Inne modele, takie jak regresja logistyczna, wykorzystywałyby obie funkcje.

Ponieważ wzmocnione drzewa używają pojedynczych drzew decyzyjnych, nie ma na nie wpływu również wielokoliniowość. Dobrą praktyką jest jednak usuwanie wszelkich zbędnych funkcji z dowolnego zestawu danych wykorzystywanego do szkolenia, niezależnie od algorytmu modelu. W twoim przypadku, ponieważ czerpiesz nowe funkcje, możesz użyć tego podejścia, ocenić znaczenie każdej funkcji i zachować tylko najlepsze funkcje dla ostatecznego modelu.

Matryca ważności modelu xgboost jest w rzeczywistości obiektem data.table z pierwszą kolumną zawierającą nazwy wszystkich funkcji faktycznie używanych w drzewach wzmocnionych. Druga kolumna to metryka Zysk, która implikuje względny wkład odpowiedniej operacji do modelu obliczony na podstawie udziału każdej operacji dla każdego drzewa w modelu. Wyższa wartość tej metryki w porównaniu z inną funkcją oznacza, że ​​jest ona ważniejsza dla generowania prognozy.

Sandeep S. Sandhu
źródło
7

Byłem tego ciekawy i przeprowadziłem kilka testów.

Przeszkoliłem model zestawu danych o diamentach i zauważyłem, że zmienna „x” jest najważniejsza do przewidzenia, czy cena diamentu jest wyższa niż określony próg. Następnie dodałem wiele kolumn silnie skorelowanych z x, uruchomiłem ten sam model i zaobserwowałem te same wartości.

Wydaje się, że gdy korelacja między dwiema kolumnami wynosi 1, xgboost usuwa dodatkową kolumnę przed obliczeniem modelu, więc nie ma to wpływu na ważność. Jednak po dodaniu kolumny częściowo skorelowanej z inną, a więc o niższym współczynniku, ważność oryginalnej zmiennej x jest zmniejszana.

Na przykład, jeśli dodam zmienną xy = x + y, znaczenie zarówno x, jak i y maleje. Podobnie znaczenie x maleje, jeśli dodam nowe zmienne o wartości r = 0,4, 0,5 lub 0,6, choć tylko odrobinę.

Myślę, że kolinearność nie stanowi problemu dla wzmocnienia, gdy obliczasz dokładność modelu, ponieważ drzewo decyzyjne nie dba o to, która ze zmiennych jest używana. Może to jednak wpłynąć na ważność zmiennych, ponieważ usunięcie jednej z dwóch skorelowanych zmiennych nie ma dużego wpływu na dokładność modelu, biorąc pod uwagę, że druga zawiera podobne informacje.

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

Oceń model na podstawie danych diamentów

Przewidujemy, czy cena jest wyższa niż 400, biorąc pod uwagę wszystkie dostępne zmienne numeryczne (karat, głębokość, tabela, x, y, x)

Zauważ, że x jest najważniejszą zmienną, z wynikiem przyrostu ważności wynoszącym 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

Model trenowany na diamentach, dodając zmienną o wartości r = 1 do x

Tutaj dodajemy nową kolumnę, która jednak nie dodaje żadnych nowych informacji, ponieważ jest doskonale skorelowana z x.

Zauważ, że ta nowa zmienna nie jest obecna na wyjściu. Wydaje się, że xgboost automatycznie usuwa doskonale skorelowane zmienne przed rozpoczęciem obliczeń. Wzrost znaczenia x jest taki sam, 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

Model trenowany na Diamentach, dodając kolumnę dla x + y

Dodajemy nową kolumnę xy = x + y. Jest to częściowo skorelowane zarówno z x, jak i y.

Zauważ, że znaczenie xiy jest nieco zmniejszone, z 0,3759 do 0,3592 dla xi od 0,116 do 0,079 dla 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

Model wytrenowany na danych diamentów, zmodyfikowany przez dodanie zbędnych kolumn

Dodajemy trzy nowe kolumny, które są skorelowane z x (r = 0,4, 0,5 i 0,6) i widzimy, co się stanie.

Zauważ, że znaczenie x zmniejsza się, z 0,3759 do 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
źródło
6

Istnieje odpowiedź Tianqi Chen (2018).

Ta różnica ma wpływ na przypadek narożny w analizie ważności cech: cechy skorelowane. Wyobraź sobie dwie cechy doskonale skorelowane, cechę A i cechę B. Dla jednego określonego drzewa, jeśli algorytm potrzebuje jednej z nich, wybierze losowo (prawda zarówno w przypadku wzmocnienia, jak i losowych lasów ™).

Jednak w Random Forests ™ ten losowy wybór zostanie dokonany dla każdego drzewa, ponieważ każde drzewo jest niezależne od innych. Dlatego w przybliżeniu, w zależności od parametrów, 50% drzew wybierze cechę A, a pozostałe 50% wybierze cechę B. Zatem znaczenie informacji zawartych w A i B (to jest to samo, ponieważ są one doskonale skorelowane ) jest rozcieńczony w A i B. Więc nie będziesz łatwo wiedzieć, że ta informacja jest ważna, aby przewidzieć, co chcesz przewidzieć! Gorzej, gdy masz 10 skorelowanych funkcji…

Podczas wzmacniania, gdy algorytm nauczy się konkretnego powiązania między cechą a wynikiem, spróbuje nie skupiać się na nim (teoretycznie tak się dzieje, rzeczywistość nie zawsze jest taka prosta). Dlatego całe znaczenie będzie dotyczyło funkcji A lub funkcji B (ale nie obu). Dowiesz się, że jedna cecha odgrywa ważną rolę w powiązaniu między obserwacjami a etykietą. W dalszym ciągu od Ciebie zależy wyszukanie skorelowanych funkcji z funkcją uznaną za ważną, jeśli chcesz poznać je wszystkie.

Podsumowując, Xgboost nie używa losowo skorelowanych funkcji w każdym drzewie, na który losowy model lasu cierpi z powodu takiej sytuacji.

Odniesienie :

Tianqi Chen, Michaël Benesty, Tong He. 2018. „Zrozum swój zestaw danych dzięki Xgboost.” Https://cran.r-project.org/web/packages/xgboost/vignettes/discoverYourData.html#numeric-vs-categorical-variables .

Jiaxiang
źródło
2

Uwaga na odpowiedź Sandeepa: Zakładając, że 2 twoje cechy są wysoce kolinearne (powiedzmy równe 99% czasu) Rzeczywiście, tylko jeden element jest wybierany przy każdym podziale, ale dla następnego podziału xgb może wybrać inną cechę. Dlatego ranking funkcji xgb prawdopodobnie uszereguje 2 cechy kolinearne jednakowo. Bez pewnej wcześniejszej wiedzy lub innego przetwarzania funkcji, z prawie tego nie ma możliwości wykrycia, że ​​2 funkcje są współliniowe.

Teraz, jeśli chodzi o względne znaczenie, które generuje xgboost, powinno ono być bardzo podobne (lub może dokładnie podobne) do rankingu drzewa wzmocnionego gradientem sklearn. Zobacz tutaj wyjaśnienia.

PSAfrance
źródło