CARRO: Seleção do melhor preditor para divisão quando ganhos na diminuição da impureza são iguais?


8

Minha pergunta lida com árvores de classificação . Considere o seguinte exemplo do conjunto de dados Iris:

insira a descrição da imagem aqui

Desejo selecionar manualmente o melhor preditor para a primeira divisão. De acordo com o algoritmo CART, o melhor recurso para fazer uma divisão é aquele que maximiza a diminuição da impureza da partição, também chamada de ganho de Gini:

GiniGain(N,X)=Gini(N)|N1||N|Gini(N1)|N2||N|Gini(N1)

XNN1N2N|.|

Gini(N)=1k=1Kpk2K

Agora, como fazer uma divisão com base na largura da pétala (eixo 1) e no comprimento da pétala (eixo 2) produz a mesma partição (todas as flores de Setosa são separadas das não-Setosa), as pontuações do GinGain serão exatamente as mesmas para cada preditor. Então, como o algoritmo CART decide qual é o melhor?

rpartGiniGain

Tópico relacionado, mas sem a resposta para minha pergunta.

Tópico relacionado sem qualquer resposta.

Respostas:


8

Confesso que sou um intérprete medíocre de código c e esse código antigo não é fácil de usar. Dito isto, passei pelo código fonte e fiz essas observações, o que me deixa bastante seguro em dizer: "o rpart escolhe literalmente a primeira e a melhor coluna variável". Como as colunas 1 e 2 produzem divisões inferiores, petal.length será a primeira variável dividida, porque essa coluna é anterior à petal.width em data.frame / matrix. Por fim, mostro isso invertendo a ordem das colunas para que petal.with seja a primeira variável dividida.

No arquivo de origem c "bsplit.c" no código-fonte do rpart , cito a linha 38:

 * test out the variables 1 at at time
me->primary = (pSplit) NULL;
for (i = 0; i < rp.nvar; i++) {

... assim, iterando em um loop for a partir de i = 1 para rp.nvar, uma função de perda será chamada para varrer todas as divisões por uma variável, dentro de gini.c para a linha "divisão não categórica" ​​230, a divisão mais encontrada é atualizado se uma nova divisão for melhor. (Também pode ser uma função de perda definida pelo usuário)

if (temp < best) {
        best = temp;
        where = i;
        direction = lmean < rmean ? LEFT : RIGHT;
}

e última linha 323, a melhoria para melhor divisão por uma variável é calculada ...

*improve = total_ss - best

... de volta ao bsplit.c a melhoria é verificada se for maior do que a vista anteriormente e atualizada apenas se for maior.

if (improve > rp.iscale)
rp.iscale = improve;        /* largest seen so far */

Minha impressão é que o primeiro e o melhor (dos possíveis empates serão escolhidos), porque somente se o novo ponto de interrupção tiver uma pontuação melhor, ele será salvo. Isso diz respeito ao primeiro melhor ponto de interrupção encontrado e à primeira melhor variável encontrada. Os pontos de interrupção parecem não ser verificados simplesmente da esquerda para a direita no gini.c, portanto, o primeiro ponto de interrupção encontrado pode ser difícil de prever. Mas as variáveis ​​são muito previsíveis varridas da primeira coluna para a última coluna.

Esse comportamento é diferente da implementação randomForest, na qual classTree.c é usada a seguinte solução:

/* Break ties at random: */
if (crit == critmax) {
    if (unif_rand() < 1.0 / ntie) {
        *bestSplit = j;
        critmax = crit;
        *splitVar = mvar;
    }
    ntie++;
}

Por fim, confirmo esse comportamento lançando as colunas de íris, de modo que petal.width seja escolhido primeiro

library(rpart)
data(iris)
iris = iris[,5:1]  #flip/flop", invert order of columns columns
obj = rpart(Species~.,data=iris)
print(obj) #now petal width is first split 


1) root 150 100 setosa (0.33333333 0.33333333 0.33333333)  
  2) Petal.Width< 0.8 50   0 setosa (1.00000000 0.00000000 0.00000000) *
  3) Petal.Width>=0.8 100  50 versicolor (0.00000000 0.50000000 0.50000000)  
    6) Petal.Width< 1.75 54   5 versicolor (0.00000000 0.90740741 0.09259259) *
    7) Petal.Width>=1.75 46   1 virginica (0.00000000 0.02173913 0.97826087) *

... e volte novamente

iris = iris[,5:1]  #flop/flip", revert order of columns columns
obj = rpart(Species~.,data=iris)
print(obj) #now petal length is first split 
1) root 150 100 setosa (0.33333333 0.33333333 0.33333333)  
  2) Petal.Length< 2.45 50   0 setosa (1.00000000 0.00000000 0.00000000) *
  3) Petal.Length>=2.45 100  50 versicolor (0.00000000 0.50000000 0.50000000)  
    6) Petal.Width< 1.75 54   5 versicolor (0.00000000 0.90740741 0.09259259) *
    7) Petal.Width>=1.75 46   1 virginica (0.00000000 0.02173913 0.97826087) *

Muito obrigado pela sua resposta. Portanto, quando mais de um preditor corresponde à divisão ideal, o primeiro é selecionado, isso não tem nada a ver com margens. Afinal, faz sentido.
Antoine

Foi divertido para descobrir :) Eu acho que as margens não são originalmente implementado em muitos modelos de árvores como splits binárias são nativamente não paramétrica
Soren Havelund Welling

pode ser útil mencionar que o código-fonte para o rpart também pode ser obtido no console do R via untar(download.packages(pkgs = "rpart",destdir = ".",type = "source")[,2])e, em seguida, abrir a srcpasta no diretório de trabalho atual (deste thread do SO ). Em seguida, o código para uma função específica pode ser visualizado com o Notepad ++ .
Antoine

E o algoritmo para quando a divisão não leva mais a nenhuma melhoria para todos os nós, certo?
Antoine

Sim. em partition.c linha 80 isch: "Isso é bastante raro - mas não consegui encontrar uma divisão que valha a pena" ... disse a função recursiva representada. A seguir, o nó é selado e o algoritmo recursivo retorna ao nó anterior chamando return (0).
Soren Havelund Welling
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.