Разница в реализации бинарных разбиений в деревьях решений

12

Мне интересно узнать о практической реализации бинарного разбиения в дереве решений - поскольку оно относится к уровням категориального предиктора .Xj

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

Скажем, переменная X принимает уровни {A,B,C,D,E}. В образце, возможно {A,B,C,D}, присутствуют только уровни . Затем, когда результирующее дерево используется для прогнозирования, может присутствовать полный набор.

Продолжая этот пример, скажем, что дерево разбивается на X и отправляет {A,B}влево и {C,D}вправо. Я ожидаю, что логика двоичного разбиения скажет, когда столкнется с новыми данными: «Если X имеет значение A или B, отправьте влево, в противном случае отправьте этот случай вправо». В некоторых реализациях кажется, что «если X имеет значение A или B, отправьте влево, если X имеет значение C или D отправьте вправо». Когда этот случай принимает значение E, алгоритм выходит из строя.

Каков «правильный» способ обработки двоичного разбиения? Кажется, гораздо более надежный способ реализуется часто, но не всегда (см. Rpart ниже).

Вот пара примеров:

Rpart не работает, остальные в порядке.

#test trees and missing values

summary(solder)
table(solder$PadType)

# create train and validation
set.seed(12345)
t_rows<-sample(1:nrow(solder),size=360, replace=FALSE)
train_solder<-solder[t_rows,]
val_solder<-solder[-t_rows,]

#look at PadType
table(train_solder$PadType)
table(val_solder$PadType)
#set a bunch to missing
levels(train_solder$PadType)[train_solder$PadType %in% c('L8','L9','W4','W9')] <- 'MISSING'


#Fit several trees, may have to play with the parameters to get them to split on the variable

####RPART
mod_rpart<-rpart(Solder~PadType,data=train_solder)
predict(mod_rpart,val_solder)
#Error in model.frame.default(Terms, newdata, na.action = na.action, xlev = attr(object,  : 
#factor 'PadType' has new level(s) D6, L6, L7, L8, L9, W4

####TREE
mod_tree<-tree(Solder~PadType,data=train_solder,split="gini")
predict(mod_tree,val_solder) #works fine

####ctree
mod_ctree<-ctree(Solder~PadType,data=train_solder,control = ctree_control(mincriterion = 0.05))
predict(mod_ctree,val_solder) #works fine
B_Miner
источник

Ответы:

9

Фактически есть два типа факторов - упорядоченные (например, Tiny <Small <Medium <Big <Huge) и неупорядоченные (Cucumber, Carrot, Fennel, Aubergine).
Первый класс - это то же самое, что и непрерывные классы - здесь проще проверить все сводки, нет проблем с расширением списка уровней.
Для второго класса вы должны создать набор элементов, которые будут направлены в одну ветвь, а остальные - в другую - в этом случае вы можете:

  1. ошибка выброса
  2. предположим, что невидимый класс переходит в вашу любимую ветку
  3. трактуйте это как NA и выбирайте ветку более-менее случайным образом.

12#categories-1-1яя

Я бы сказал, что самая разумная идея - заставить пользователя определить полный набор факторов (например, R делает это органично, сохраняя уровни с помощью операций подмножества) и использовать опцию 1. для не объявленных уровней и опцию 2. для объявленных. , Вариант 3. может иметь смысл, если у вас уже есть некоторая инфраструктура обработки NA.

*) Существует также побочная стратегия, заключающаяся в некотором нетривиальном перекодировании уровней в числа, как, например, кодирование Бреймана, - но это порождает еще больше проблем.


источник
1
Вы говорите, что ctree или tree в моем примере фактически рассматривают этот неупорядоченный фактор как упорядоченный фактор и таким образом отправляют его в ветвь «0»?
B_Miner
@mbq Не могли бы вы объяснить, почему общее количество способов, которыми вы можете выполнить разбиения, составляет 2 ^ (# категорий + 1) - 2. Я не совсем понимаю, почему часть "-2".
honeybadger
Хм, кажется, я облажался с этой формулой; есть как 2 ^ n n-битных слов, но мы не считаем слова a и ~ a, поэтому 2 ^ (n-1), и нам не нравятся сплиты, которые вообще не проливаются, поэтому 2 ^ (n-1) -1 (другими словами, мы считаем от 1). Тогда n = 1 является частным случаем.