Alberi decisionale in crescita

Come tutti i modelli di machine learning supervisionati, gli alberi decisionali vengono addestrati per spiegare al meglio un insieme di esempi di addestramento. L'addestramento ottimale di un albero decisionale è un problema NP-difficile. Pertanto, l'addestramento viene generalmente eseguito utilizzando le heurismi, ovvero un algoritmo di apprendimento facile da creare che genera un albero decisionale non ottimale, ma quasi ottimale.

La maggior parte degli algoritmi utilizzati per addestrare gli alberi decisionali funziona con una strategia di suddivisione e conquista avida. L'algoritmo inizia creando un singolo nodo (la radice) e aumenta in modo ricorsivo e avido l'albero decisionale.

In ogni nodo, tutte le possibili condizioni vengono valutate e assegnate un punteggio. L'algoritmo seleziona la condizione "migliore", ovvero quella con il punteggio più alto. Per il momento, tieni presente che il punteggio è una metrica correlata all'attività e che le condizioni vengono selezionate per massimizzare questa metrica.

Ad esempio, nel set di dati Palmer Penguins (utilizzato per gli esempi di codice più avanti in questo corso), la maggior parte dei pinguini Adelie e Barboni ha un becco lungo più di 16 mm, mentre la maggior parte dei pinguini Gentoo ha un becco più piccolo. Pertanto, la condizione bill_length_mm ≥ 16 genera previsioni quasi perfette per ipinguini di Adelia, ma non riesce a distinguere trapinguini di Adelia e pinguini antartici. L'algoritmo probabilmente sceglierà questa condizione.

Una condizione che genera due foglie. La condizione è "bill_length_mm >= 16".
In caso affermativo, la piuma è "Adelie o Chinstrap".  In caso contrario, la foglia è "Gentoo".

Figura 7. Il primo passo per far crescere un albero.

 

L'algoritmo viene quindi ripetuto in modo ricorsivo e indipendente su entrambi i nodi secondari. Quando non vengono trovate condizioni soddisfacenti, il nodo diventa una foglia. La previsione dell'elemento finale viene determinata come il valore dell'etichetta più rappresentativo negli esempi.

L'algoritmo è il seguente:

def train_decision_tree(training_examples):
  root = create_root() # Create a decision tree with a single empty root.
  grow_tree(root, training_examples) # Grow the root node.
  return root

def grow_tree(node, examples):
  condition = find_best_condition(examples) # Find the best condition.

  if condition is None:
    # No satisfying conditions were found, therefore the grow of the branch stops.
    set_leaf_prediction(node, examples)
    return

  # Create two childrens for the node.
  positive_child, negative_child = split_node(node, condition)

  # List the training examples used by each children.
  negative_examples = [example for example in examples if not condition(example)]
  positive_examples = [example for example in examples if condition(example)]

  # Continue the growth of the children.
  grow_tree(negative_child, negative_examples)
  grow_tree(positive_child, positive_examples)

Analizziamo più in dettaglio i passaggi per l'addestramento di un determinato albero decisionale.

Passaggio 1: crea un utente principale:

Un nodo con un punto interrogativo.

Passaggio 2: sviluppa il nodo 1. È stata trovata la condizione "x1 ≥ 1". Vengono creati due nodi secondari:

Un nodo principale
   che rimanda a due nodi non definiti.

Passaggio 3: espandi il nodo 2. Non sono state trovate condizioni soddisfacenti. Quindi, trasforma il nodo in una foglia:

Un nodo principale che rimanda a due nodi non definiti.

Passaggio 4: espandi il nodo 3. È stata trovata la condizione "x2 ≥ 0,5". Vengono creati due nodi secondari.

Un nodo principale, una condizione e tre foglie.

Esistono altri metodi per sviluppare alberi decisionali. Un'alternativa molto utilizzata è ottimizzare i nodi a livello globale anziché utilizzare una strategia di suddivisione e conquista.

Codice YDF
In YDF, per impostazione predefinita gli alberi decisionali vengono sviluppati con l'algoritmo "divide et impera" descritto sopra. In alternativa, puoi attivare la crescita globale con il parametro growing_strategy="BEST_FIRST_GLOBAL". Per ulteriori dettagli, consulta la documentazione.

A seconda del numero e del tipo di elementi di input, il numero di condizioni possibili per un determinato nodo può essere enorme, in genere infinito. Ad esempio, data una condizione di soglia $\mathrm{feature}_i \geq t$, la combinazione di tutti i possibili valori di soglia per $t \in \mathbb{R}$ è infinita.

La routine responsabile della ricerca della condizione migliore è chiamata splitter. Poiché devono testare molte condizioni possibili, gli splitter sono il collo di bottiglia durante l'addestramento di un albero decisionale.

Il punteggio massimizzato dallo splitter dipende dall'attività. Ad esempio:

  • Il guadagno informativo e l'indice di Gini (entrambi trattati in seguito) vengono comunemente utilizzati per la classificazione.
  • Lo scarto quadratico medio è comunemente utilizzato per la regressione.

Esistono molti algoritmi di suddivisione, ognuno con un supporto diverso per:

  • Il tipo di caratteristiche, ad esempio numeriche, categoriche, di testo
  • L'attività, ad esempio classificazione binaria, classificazione multiclasse, regressione
  • Il tipo di condizione, ad esempio condizione di soglia, condizione in-set, condizione obliqua
  • I criteri di regolarizzazione, ad esempio gli split esatti o approssimativi per le condizioni di soglia

Inoltre, esistono varianti dello splitter equivalenti con compromessi diversi per quanto riguarda l'utilizzo della memoria, l'utilizzo della CPU, la distribuzione del calcolo e così via.

Codice YDF
In YDF, gli splitter vengono selezionati implicitamente dal tipo della funzionalità rilevato automaticamente (o specificato manualmente), dagli iperparametri e dalla velocità stimata di ciascun splitter (durante l'addestramento, YDF esegue un piccolo modello per prevedere la velocità di ciascun splitter candidato).