ALGORITMO DE CLASSIFICAÇÃO
Árvores de decisão estão por toda secção no aprendizagem de máquina, adoradas por sua saída intuitiva. Quem não patroa um fluxograma simples de “se-então”? Apesar de sua popularidade, é surpreendente o quão reptador é encontrar uma explicação clara e passo a passo de uma vez que as Árvores de Decisão funcionam. (Na verdade, estou envergonhado pelo tempo que levei para realmente entender uma vez que o algoritmo funciona.)
Logo, nesta estudo, vou focar nos fundamentos da construção de árvores. Vamos desempacotar exatamente o que está acontecendo em cada nó e por quê, da raiz às folhas finais (com visuais, é evidente).
Um classificador de Árvore de Decisão cria uma árvore invertida para fazer previsões, começando no topo com uma pergunta sobre um recurso importante em seus dados, logo ramifica-se com base nas respostas. Conforme você segue essas ramificações para grave, cada paragem faz outra pergunta, estreitando as possibilidades. Esse jogo de perguntas e respostas continua até você chegar ao fundo — um nó folha — onde você obtém sua previsão ou classificação final.
Ao longo deste item, usaremos oriente conjunto de dados de golfe sintético (inspirado em [1]) uma vez que exemplo. Oriente conjunto de dados prevê se uma pessoa jogará golfe com base nas condições climáticas.
# Import libraries
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import pandas as pd
import numpy as np# Load data
dataset_dict = {
'Outlook': ['sunny', 'sunny', 'overcast', 'rainy', 'rainy', 'rainy', 'overcast', 'sunny', 'sunny', 'rainy', 'sunny', 'overcast', 'overcast', 'rainy', 'sunny', 'overcast', 'rainy', 'sunny', 'sunny', 'rainy', 'overcast', 'rainy', 'sunny', 'overcast', 'sunny', 'overcast', 'rainy', 'overcast'],
'Temperature': [85.0, 80.0, 83.0, 70.0, 68.0, 65.0, 64.0, 72.0, 69.0, 75.0, 75.0, 72.0, 81.0, 71.0, 81.0, 74.0, 76.0, 78.0, 82.0, 67.0, 85.0, 73.0, 88.0, 77.0, 79.0, 80.0, 66.0, 84.0],
'Humidity': [85.0, 90.0, 78.0, 96.0, 80.0, 70.0, 65.0, 95.0, 70.0, 80.0, 70.0, 90.0, 75.0, 80.0, 88.0, 92.0, 85.0, 75.0, 92.0, 90.0, 85.0, 88.0, 65.0, 70.0, 60.0, 95.0, 70.0, 78.0],
'Wind': [False, True, False, False, False, True, True, False, False, False, True, True, False, True, True, False, False, True, False, True, True, False, True, False, False, True, False, False],
'Play': ['No', 'No', 'Yes', 'Yes', 'Yes', 'No', 'Yes', 'No', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'No', 'No', 'Yes', 'Yes', Sua visita nos ajuda a continuar oferecendo o melhor para você! 'No', 'No', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'No', 'Yes']
}
df = pd.DataFrame(dataset_dict)
# Preprocess data
df = pd.get_dummies(df, columns=['Outlook'], prefix='', prefix_sep='', dtype=int)
df['Wind'] = df['Wind'].astype(int)
df['Play'] = (df['Play'] == 'Yes').astype(int)
# Reorder the columns
df = df[['sunny', 'overcast', 'rainy', 'Temperature', 'Humidity', 'Wind', 'Play']]
# Prepare features and target
X, y = df.drop(columns='Play'), df['Play']
# Split data
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.5, shuffle=False)
# Display results
print(pd.concat([X_train, y_train], axis=1), 'n')
print(pd.concat([X_test, y_test], axis=1))
O classificador Decision Tree opera dividindo recursivamente os dados com base nos recursos mais informativos. Veja uma vez que funciona:
- Comece com todo o conjunto de dados no nó raiz.
- Selecione o melhor recurso para dividir os dados (com base em medidas uma vez que impureza de Gini).
- Crie nós filhos para cada valor provável do recurso selecionado.
- Repita as etapas 2–3 para cada nó fruto até que um critério de paragem seja atendido (por exemplo, profundidade máxima atingida, amostras mínimas por folha ou nós de folha puros).
- Atribua a classe majoritária a cada nó folha.
Sua visita nos ajuda a continuar oferecendo o melhor para você! no np gu nq b hx py ns nt ia pz nv nw nx qa nz oa ob qb od oe of qc oh oi oj gn bk">No scikit-learn, o algoritmo de árvore de decisão é chamado CART (Classification and Regression Trees). Ele constrói árvores binárias e normalmente segue estas etapas:
- Comece com todas as amostras de treinamento no nó raiz.
2. Para cada recurso:
a. Classifique os valores dos recursos.
b. Considere todos os limites possíveis entre valores adjacentes uma vez que potenciais pontos de partilha.
def potential_split_points(attr_name, attr_values):
sorted_attr = np.sort(attr_values)
unique_values = np.unique(sorted_attr)
split_points = [(unique_values[i] + unique_values[i+1]) / 2 for i in range(len(unique_values) - 1)]
return {attr_name: split_points}# Calculate and display potential split points for all columns
for column in X_train.columns:
splits = potential_split_points(column, X_train[column])
for attr, points in splits.items():
print(f"{attr:11}: {points}")
3. Para cada ponto de partilha potencial:
a. Calcule a impureza (por exemplo, impureza de Gini) do nó atual.
b. Calcule a média ponderada das impurezas.
def gini_impurity(y):
p = np.bincount(y) / len(y)
return 1 - np.sum(p**2)def weighted_average_impurity(y, split_index):
n = len(y)
left_impurity = gini_impurity(y[:split_index])
right_impurity = gini_impurity(y[split_index:])
return (split_index * left_impurity + (n - split_index) * right_impurity) / n
# Sort 'sunny' feature and corresponding labels
sunny = X_train['sunny']
sorted_indices = np.argsort(sunny)
sorted_sunny = sunny.iloc[sorted_indices]
sorted_labels = y_train.iloc[sorted_indices]
# Find split índice for 0.5
split_index = np.searchsorted(sorted_sunny, 0.5, side='right')
# Calculate impurity
impurity = weighted_average_impurity(sorted_labels, split_index)
print(f"Weighted average impurity for 'sunny' at split point 0.5: {impurity:.3f}")
4. Depois de calcular todas as impurezas para todos os recursos e pontos de partilha, escolha o menor.
def calculate_split_impurities(X, y):
split_data = []for feature in X.columns:
sorted_indices = np.argsort(X[feature])
sorted_feature = X[feature].iloc[sorted_indices]
sorted_y = y.iloc[sorted_indices]
unique_values = sorted_feature.unique()
split_points = (unique_values[1:] + unique_values[:-1]) / 2
for split in split_points:
split_index = np.searchsorted(sorted_feature, split, side='right')
impurity = weighted_average_impurity(sorted_y, split_index)
split_data.append({
'feature': feature,
'split_point': split,
'weighted_avg_impurity': impurity
})
return pd.DataFrame(split_data)
# Calculate split impurities for all features
calculate_split_impurities(X_train, y_train).round(3)
5. Crie dois nós filhos com base no recurso escolhido e no ponto de partilha:
– Rebento esquerdo: amostras com valor de propriedade <= ponto de partilha
– Rebento recta: amostras com valor de propriedade > ponto de partilha
6. Repita recursivamente as etapas 2–5 para cada nó fruto. Você também pode parar até que um critério de paragem seja atendido (por exemplo, profundidade máxima atingida, número mínimo de amostras por nó folha ou subtracção mínima de impurezas).
# Calculate split impurities forselected índice
selected_index = [4,8,3,13,7,9,10] # Change it depending on which indices you want to check
calculate_split_impurities(X_train.iloc[selected_index], y_train.iloc[selected_index]).round(3)
from sklearn.tree import DecisionTreeClassifier# The whole Training Phase above is done inside sklearn like this
dt_clf = DecisionTreeClassifier()
dt_clf.fit(X_train, y_train)
Árvore Final Completa
O rótulo de classe de um nó folha é a classe majoritária das amostras de treinamento que alcançaram esse nó.
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
# Plot the decision tree
plt.figure(figsize=(20, 10))
plot_tree(dt_clf, filled=True, feature_names=X.columns, class_names=['Not Play', 'Play'])
plt.show()
Veja uma vez que o processo de previsão funciona depois que a árvore de decisão é treinada:
- Comece no nó raiz da árvore de decisão treinada.
- Avalie o recurso e a requisito de partilha no nó atual.
- Repita o passo 2 em cada nó subsequente até chegar a um nó folha.
- O rótulo de classe do nó folha se torna a previsão para a novidade instância.
# Make predictions
y_pred = dt_clf.predict(X_test)
print(y_pred)
# Evaluate the classifier
print(f"Accuracy: {accuracy_score(y_test, y_pred)}")
Árvores de decisão têm vários parâmetros importantes que controlam seu prolongamento e complicação:
1 . Profundidade Máxima: Isso define a profundidade máxima da árvore, o que pode ser uma utensílio valiosa para evitar overfitting.
👍 Dica útil: Considere estrear com uma árvore rasa (talvez de 3 a 5 níveis de profundidade) e aumentar gradualmente a profundidade.
2. Amostras mínimas divididas: Oriente parâmetro determina o número mínimo de amostras necessárias para dividir um nó interno.
👍 Dica útil: Definir isso para um valor mais cume (muro de 5–10% dos seus dados de treinamento) pode ajudar a evitar que a árvore crie muitas divisões pequenas e específicas que podem não ser muito generalizadas para novos dados.
3. Amostras mínimas de folhas: Isso especifica o número mínimo de amostras necessárias em um nó folha.
👍 Dica útil: Escolha um valor que garanta que cada folha represente um subconjunto significativo dos seus dados (aproximadamente 1–5% dos seus dados de treinamento). Isso pode ajudar a evitar previsões excessivamente específicas.
4. Critério: A função usada para medir a qualidade de uma partilha (geralmente “gini” para impureza de Gini ou “entropia” para lucro de informação).
👍 Dica útil: Embora Gini seja geralmente mais simples e rápido de calcular, a entropia geralmente tem melhor desempenho para problemas multiclasse. Dito isso, eles frequentemente dão resultados semelhantes.
Porquê qualquer algoritmo em aprendizagem de máquina, as Árvores de Decisão têm seus pontos fortes e limitações.
Prós:
- Interpretabilidade: Fácil de entender e visualizar o processo de tomada de decisão.
- Sem dimensionamento de recursos: Pode manipular dados numéricos e categóricos sem normalização.
- Lida com relacionamentos não lineares: Pode conquistar padrões complexos nos dados.
- Preço do recurso: Fornece uma indicação clara de quais recursos são mais importantes para a previsão.
Contras:
- Sobreajuste: Propenso a gerar árvores excessivamente complexas que não generalizam muito, mormente com pequenos conjuntos de dados.
- Instabilidade:Pequenas alterações nos dados podem resultar na geração de uma árvore completamente dissemelhante.
- Tendencioso com conjuntos de dados desequilibrados:Pode ser tendencioso em relação às classes dominantes.
- Incapacidade de extrapolar: Não é provável fazer previsões além do pausa dos dados de treinamento.
Em nosso exemplo de golfe, uma Árvore de Decisão pode gerar regras muito precisas e interpretáveis para sentenciar se jogar golfe com base nas condições climáticas. No entanto, ela pode se ajustar demais a combinações específicas de condições se não for podada corretamente ou se o conjunto de dados for pequeno.
Classificadores de Árvores de Decisão são uma ótima utensílio para resolver muitos tipos de problemas em aprendizagem de máquina. Eles são fáceis de entender, podem mourejar com dados complexos e nos mostram uma vez que eles tomam decisões. Isso os torna úteis em muitas áreas, de negócios a medicina. Embora Árvores de Decisão sejam poderosas e interpretáveis, elas são frequentemente usadas uma vez que blocos de construção para métodos de conjunto mais avançados, uma vez que Random Forests ou Gradient Boosting Machines.
# Import libraries
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from sklearn.tree import plot_tree, DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score# Load data
dataset_dict = {
'Outlook': ['sunny', 'sunny', 'overcast', 'rainy', 'rainy', 'rainy', 'overcast', 'sunny', 'sunny', 'rainy', 'sunny', 'overcast', 'overcast', 'rainy', 'sunny', 'overcast', 'rainy', 'sunny', 'sunny', 'rainy', 'overcast', 'rainy', 'sunny', 'overcast', 'sunny', 'overcast', 'rainy', 'overcast'],
'Temperature': [85.0, 80.0, 83.0, 70.0, 68.0, 65.0, 64.0, 72.0, 69.0, 75.0, 75.0, 72.0, 81.0, 71.0, 81.0, 74.0, 76.0, 78.0, 82.0, 67.0, 85.0, 73.0, 88.0, 77.0, 79.0, 80.0, 66.0, 84.0],
'Humidity': [85.0, 90.0, 78.0, 96.0, 80.0, 70.0, 65.0, 95.0, 70.0, 80.0, 70.0, 90.0, 75.0, 80.0, 88.0, 92.0, 85.0, 75.0, 92.0, 90.0, 85.0, 88.0, 65.0, 70.0, 60.0, 95.0, 70.0, 78.0],
'Wind': [False, True, False, False, False, True, True, False, False, False, True, True, False, True, True, False, False, True, False, True, True, False, True, False, False, True, False, False],
'Play': ['No', 'No', 'Yes', 'Yes', 'Yes', 'No', 'Yes', 'No', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'No', 'No', 'Yes', 'Yes', 'No', 'No', 'No', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'No', 'Yes']
}
df = pd.DataFrame(dataset_dict)
# Prepare data
df = pd.get_dummies(df, columns=['Outlook'], prefix='', prefix_sep='', dtype=int)
df['Wind'] = df['Wind'].astype(int)
df['Play'] = (df['Play'] == 'Yes').astype(int)
# Split data
X, y = df.drop(columns='Play'), df['Play']
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.5, shuffle=False)
# Train model
dt_clf = DecisionTreeClassifier(
max_depth=None, # Maximum depth of the tree
min_samples_split=2, # Minimum number of samples required to split an internal node
min_samples_leaf=1, # Minimum number of samples required to be at a leaf node
criterion='gini' # Function to measure the quality of a split
)
dt_clf.fit(X_train, y_train)
# Make predictions
y_pred = dt_clf.predict(X_test)
# Evaluate model
print(f"Accuracy: {accuracy_score(y_test, y_pred)}")
# Visualize tree
plt.figure(figsize=(20, 10))
plot_tree(dt_clf, filled=True, feature_names=X.columns,
class_names=['Not Play', 'Play'], impurity=False)
plt.show()