Почему среднее уменьшение джини в случайном лесу зависит от численности популяции? - PullRequest
1 голос
/ 07 марта 2019

Я использую R-пакет randomForest, и чтобы понять важность переменных, мы можем исследовать varImpPlot, который показывает среднее уменьшение Джини. Я подробно изучил Случайный Лес и хорошо знаю, как эта модель работает в деталях, я не могу полностью понять, как рассчитывается среднее уменьшение Джини, или, скорее, почему оно зависит от численности населения.

enter image description here

Когда мы вычислили индекс Джини, мы можем агрегировать среднее уменьшение Джини по следующей формуле (деленной на количество деревьев):

enter image description here

Я понимаю, что при большем населении в каждом дереве будет больше расщеплений, но разве в среднем эти расщепления не должны приводить к очень небольшому уменьшению индекса Джини?

Вот пример кода, показывающий, что я имею в виду (как и ожидалось, количество деревьев не влияет на среднее уменьшение Джини, но популяция оказывает огромное влияние и кажется более или менее линейной с размером популяции):

install.packages("randomForest")
library(randomForest)

set.seed(1)
a <- as.factor(c(rep(1, 20), rep(0, 30)))
b <- c(rnorm(20, 5, 2), rnorm(30, 4, 1))
c <- c(rnorm(25, 0, 1), rnorm(25, 1, 2))
data <- data.frame(a = a, b = b, c = c)

rf <- randomForest(data = data, a ~ b + c, importance = T, ntree = 300)
varImpPlot(rf)


a2 <- as.factor(c(rep(1, 200), rep(0, 300)))
b2 <- c(rnorm(200, 5, 2), rnorm(300, 4, 1))
c2 <- c(rnorm(250, 0, 1), rnorm(250, 1, 2))
data2 <- data.frame(a2 = a2, b2 = b2, c2 = c2)

rf2 <- randomForest(data = data2, a2 ~ b2 + c2, importance = T, ntree = 
300)
varImpPlot(rf2)


a3 <- as.factor(c(rep(1, 2000), rep(0, 3000)))
b3 <- c(rnorm(2000, 5, 2), rnorm(3000, 4, 1))
c3 <- c(rnorm(2500, 0, 1), rnorm(2500, 1, 2))
data3 <- data.frame(a3 = a3, b3 = b3, c3 = c3)

rf3 <- randomForest(data = data3, a3 ~ b3 + c3, importance = T, ntree = 
300)
varImpPlot(rf3)

В результате на следующих графиках мы видим, что ось x увеличивается примерно в 10 раз при каждом увеличении населения:

enter image description here

enter image description here

enter image description here

Я предполагаю, что существует вес, основанный на количестве людей в каждом проведенном разделении. То есть разделение, которое выполняется в первых узлах, которое разделяет 1000 людей весом тяжелее, чем разделение, которое проводится далее по дереву, скажем, 10 люди, я не могу найти это ни в одной литературе, хотя, поскольку кажется, что все расчеты сделаны с учетом доли населения, а не абсолютных чисел.

Что мне не хватает?

1 Ответ

1 голос
/ 08 марта 2019

Ваше предположение верно.

Вы записали определение примеси Джини для одного сплита. Деревья в случайном лесу обычно делятся несколько раз. Более высокие узлы имеют больше выборок и интуитивно более «нечисты». Таким образом, формула среднего уменьшения Джини учитывает размеры узлов.

Так что вместо

Delta i(tau) = i(tau) - (n_l/n) i(tau_l) - (n_r/n) i(tau_r)

уменьшение примеси рассчитывается как

Delta i(tau) = n i(tau) - n_l i(tau_l) - n_r i(tau_r)

То есть взвешивает примеси в необработанных количествах, а не в пропорциях.

Алгоритм продолжает разбивать дерево до максимально возможного размера (если не указаны аргументы nodesize или maxnodes). Таким образом, функция может быть выбрана для критерия разделения несколько раз. Его общая важность - сумма Delta s на этих расколах. Это вычисление важности для одного дерева. Наконец, значения усредняются по всем деревьям в лесу.

Покажем это на очень надуманном примере.

library("randomForest")
#> randomForest 4.6-14
#> Type rfNews() to see new features/changes/bug fixes.
set.seed(1)

n <- 1000
# There are three classes in equal proportions
a <- rep(c(-10,0,10), each = n)
# One feature is useless
b <- rnorm(3*n)
# The other feature is highly predictive but we need at least two splits
c <- rnorm(3*n, a)
data <- data.frame(a = as.factor(a), b = b, c = c)

# First let's do just one split, i.e., ask for just two terminal nodes

# Expected MeanDecreaseGini:
# With one split the best we can do is separate one class from the other two
3000*(2/3) - 1000*0 - 2000*(1/2)
#> [1] 1000

# Actual MeanDecreaseGini
rf3 <- randomForest(data = data, a ~ b + c, importance = TRUE,
                    ntree = 1000, mtry = 2, maxnodes = 2)
rf3$importance[, "MeanDecreaseGini"]
#>        b        c 
#>    0.000 1008.754


# Next let's do two splits; this is enough to separate classes perfectly

# Expected MeanDecreaseGini:
3000*(2/3) - 1000*0 - 2000*(1/2)  +   2000*(1/2) - 1000*0 - 1000*0
#> [1] 2000

# Actual MeanDecreaseGini
rf3 <- randomForest(data = data, a ~ b + c, importance = TRUE,
                    ntree = 1000, mtry = 2, maxnodes = 3)
rf3$importance[, "MeanDecreaseGini"]
#>        b        c 
#>    0.000 1999.333

Создано в 2019-03-08 пакетом Представить (v0.2.1)

PS: Хорошо знать, как значения вычисляются по критерию Джини. Но прочитайте эту статью для объяснения, почему вы должны вместо этого использовать значения перестановки: https://explained.ai/rf-importance/index.html

...