Árvores de Classificação e Regressão

Introdução

Neste tutorial iremos apresentar as implementações dos algoritmos de Árvores de Classificação e Regressão que podem também ser chamados de CART. Este é um poderoso método de predição bastante popular. A árvore de decisão final explica exatamente o porquê de uma predição específica ter sido feita, tornando-a muito atrativa para o uso. Elas também servem de base para métodos mais avançados. Para implementar a árvore iremos utilizar funções das bibliotecas do Scikit-learn, do Pandas e do Numpy.

Árvores de classificação e regressão

São algoritmos que podem ser utilizados para problemas de modelagem preditiva classificativa ou regressiva. Vamos focar no uso do modelo CART(Árvores de Classificação e Regressão) para classificação nesse tutorial. A representação do método é uma árvore binária. Um nó representa uma única variável de entrada (X) e um ponto de divisão nessa variável, assumindo que é numérica. Os nós folhas (também chamados de nós terminais) de uma árvore contém uma variável de saída (Y) a qual é utilizada para fazer uma predição. Uma vez criada, uma árvore pode ser navegada com uma nova linha de dados seguindo cada ramo com as divisões até a última predição ser feita.

Banknote Dataset

Utilizaremos o conjunto de dados denominado BankNote Dataset disponível nesse link: https://goo.gl/rLPrHO. Esse dataset é utilizado para prever se uma cédula é autêntica dado o conjunto de medidas tiradas de uma fotografia. Esse problema é composto por 1372 observações de dados que foram extraídos de imagens que foram tiradas de cédulas genuínas e falsas. Foi utilizada uma ferramenta de transformação "wavelet"(função capaz de decompor e descrever ou representar outra função) para extrair essas informações da imagem. Os registros mostram a variância, a obliquidade e a curtose(é uma medida de forma que caracteriza o achatamento da curva da função) da imagem transformada pela ferramenta, além disso há a entropia da imagem. Cada registro há um valor de classe que indica se a cédula é autentica (0) ou não (1).

Passos do tutorial:

1. Carregar o dataset.
2. Dvidir o dataset.
3. Treinar o algoritmo.
4. Fazer uma predição e avaliar o algoritmo.
5. Avaliar o algortimo utilizando o K-fold Cross Validation

1. Carregar o dataset

Nesta etapa vamos utilizar a biblioteca Pandas. O Pandas é uma biblioteca do python que oferece suporte para operações de manipulação e análise de dados.

O primeiro passo é importar o pandas. Para ler o arquivo .csv que contém o dataset iremos utilizar a função read_csv. O primeiro parâmetro que passamos é o nome do arquivo que desejamos ler. Ao fazermos a atribuição "header= None" estamos informando a função que o nosso dataset não possui cabeçalho. O dataset será então retornamos como um DataFrame que será armazenado na variável data.


In [1]:
import pandas as pd
data = pd.read_csv('banknote.csv', header= None)

Utilizaremos a função head() para visualizar as primeiras linhas do dataframe.


In [3]:
data.head()


Out[3]:
0 1 2 3 4
0 3.62160 8.6661 -2.8073 -0.44699 0
1 4.54590 8.1674 -2.4586 -1.46210 0
2 3.86600 -2.6383 1.9242 0.10645 0
3 3.45660 9.5228 -4.0112 -3.59440 0
4 0.32924 -4.4552 4.5718 -0.98880 0

Se desejar visualizar todo o dataset executa a seguinte linha de código.


In [2]:
data


Out[2]:
0 1 2 3 4
0 3.621600 8.66610 -2.807300 -0.446990 0
1 4.545900 8.16740 -2.458600 -1.462100 0
2 3.866000 -2.63830 1.924200 0.106450 0
3 3.456600 9.52280 -4.011200 -3.594400 0
4 0.329240 -4.45520 4.571800 -0.988800 0
5 4.368400 9.67180 -3.960600 -3.162500 0
6 3.591200 3.01290 0.728880 0.564210 0
7 2.092200 -6.81000 8.463600 -0.602160 0
8 3.203200 5.75880 -0.753450 -0.612510 0
9 1.535600 9.17720 -2.271800 -0.735350 0
10 1.224700 8.77790 -2.213500 -0.806470 0
11 3.989900 -2.70660 2.394600 0.862910 0
12 1.899300 7.66250 0.153940 -3.110800 0
13 -1.576800 10.84300 2.546200 -2.936200 0
14 3.404000 8.72610 -2.991500 -0.572420 0
15 4.676500 -3.38950 3.489600 1.477100 0
16 2.671900 3.06460 0.371580 0.586190 0
17 0.803550 2.84730 4.343900 0.601700 0
18 1.447900 -4.87940 8.342800 -2.108600 0
19 5.242300 11.02720 -4.353000 -4.101300 0
20 5.786700 7.89020 -2.619600 -0.487080 0
21 0.329200 -4.45520 4.571800 -0.988800 0
22 3.936200 10.16220 -3.823500 -4.017200 0
23 0.935840 8.88550 -1.683100 -1.659900 0
24 4.433800 9.88700 -4.679500 -3.748300 0
25 0.705700 -5.49810 8.336800 -2.871500 0
26 1.143200 -3.74130 5.577700 -0.635780 0
27 -0.382140 8.39090 2.162400 -3.740500 0
28 6.563300 9.81870 -4.411300 -3.225800 0
29 4.890600 -3.35840 3.420200 1.090500 0
... ... ... ... ... ...
1342 -1.747900 -5.82300 5.869900 1.212000 1
1343 -0.959230 -6.71280 4.985700 0.328860 1
1344 1.345100 0.23589 -1.878500 1.325800 1
1345 2.227900 4.09510 -4.803700 -2.111200 1
1346 1.257200 4.87310 -5.286100 -5.874100 1
1347 -5.385700 9.12140 -0.419290 -5.918100 1
1348 -2.978600 2.34450 0.526670 -0.401730 1
1349 -1.585100 -2.15620 1.708200 0.901700 1
1350 -0.218880 -2.20380 -0.095400 0.564210 1
1351 1.318300 1.90170 -3.311100 0.065071 1
1352 1.489600 3.42880 -4.030900 -1.425900 1
1353 0.115920 3.22190 -3.430200 -2.845700 1
1354 -3.392400 3.35640 -0.720040 -3.523300 1
1355 -6.163200 8.70960 -0.216210 -3.634500 1
1356 -4.078600 2.92390 0.870260 -0.653890 1
1357 -2.589900 -0.39110 0.934520 0.429720 1
1358 -1.011600 -0.19038 -0.905970 0.003003 1
1359 0.066129 2.49140 -2.940100 -0.621560 1
1360 -0.247450 1.93680 -2.469700 -0.805180 1
1361 -1.573200 1.06360 -0.712320 -0.838800 1
1362 -2.166800 1.59330 0.045122 -1.678000 1
1363 -1.166700 -1.42370 2.924100 0.661190 1
1364 -2.839100 -6.63000 10.484900 -0.421130 1
1365 -4.504600 -5.81260 10.886700 -0.528460 1
1366 -2.410000 3.74330 -0.402150 -1.295300 1
1367 0.406140 1.34920 -1.450100 -0.559490 1
1368 -1.388700 -4.87730 6.477400 0.341790 1
1369 -3.750300 -13.45860 17.593200 -2.777100 1
1370 -3.563700 -8.38270 12.393000 -1.282300 1
1371 -2.541900 -0.65804 2.684200 1.195200 1

1372 rows × 5 columns


In [3]:
data.info()


<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1372 entries, 0 to 1371
Data columns (total 5 columns):
0    1372 non-null float64
1    1372 non-null float64
2    1372 non-null float64
3    1372 non-null float64
4    1372 non-null int64
dtypes: float64(4), int64(1)
memory usage: 53.7 KB

2. Dividir o dataset

Iremos agora utilizar a função iloc do pandas para dividir o nosso dataset. Em X iremos guardar os valores da coluna 0 até a coluna 3 do dataset, em Y iremos guardar os valores da última coluna, que correspondem aos valores de saída.


In [10]:
X = data.iloc[:,[0,1,2,3]]
Y = data.iloc[:,4]

O próximo passo é dividir o conjunto de dados, em dados de treino e de teste. Para isso usaremos a função train_test_split da biblioteca sklearn.


In [11]:
from sklearn.model_selection import train_test_split

Dividiremos os dados da seguinte forma: 80% dos dados serão utilizados para treino e 20% para teste. Os dados de treino serão então separados e guardados em duas variáveis: o X_train guarda os valores da coluna 0 até a 4 e o y_train guarda os valores de saída correspondente a cada linha do X_train. A divisão acontecerá da mesma forma entre o X_test e o y_test para os dados de treino.


In [12]:
X_train, X_test, y_train, y_test = train_test_split(X, Y, train_size=0.8, test_size= 0.2, random_state=1)

3. Treinar o algoritmo


In [13]:
from sklearn.tree import DecisionTreeClassifier

Vamos utilizar a função DecisionTreeClassifier para modelar. O parâmetro criterion define a função utilizada para medir a qualidade da divisão. No nosso caso definimos o critério com 'gini'.

O coeficiente é uma função de custo utilizada para avaliar divisões no dataset. Uma divisão neste envolve um atributo de entrada e um valor para este atributo. Uma separação perfeita resulta em um valor de Gini igual a 0, no pior caso, ou seja, no caso em que a separação resulta em 50/50, resulta em um valor de Gini igual a 0.5.

O parâmetro max_depth define a máxima profundidade de uma árvore: Isso é o número máximo de nós da raiz da árvore. Uma vez que a profundidade máxima é atingida devemos parar de adicionar novos nós. Árvores muito profundas são mais complexas e mais prováveis de superar os dados de treinamento.

O parâmetro min_samples_split define o número mínimo de registros nos nós: É o número mínimo de padrões de treinamento que um nó é responsável. Uma vez menor ou igual que o mínimo, devemos parar de dividir e adicionar novos nós. Nós que são responsaveis por poucos padrões de treinamento são muitos específicos.

Esta função aceita ainda outros parâmetros, mas para o nosso caso estes são suficientes.


In [14]:
model = DecisionTreeClassifier(criterion='gini', max_depth= 5, min_samples_split= 10)

Agora iremos treinar o nosso modelo, utilizando a função fit do sklearn, usaremos os conjuntos de dados de treino que tínhamos preparados anteriormente.


In [15]:
model.fit(X_train, y_train)


Out[15]:
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=5,
            max_features=None, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=10,
            min_weight_fraction_leaf=0.0, presort=False, random_state=None,
            splitter='best')

4. Fazer uma predição e avaliar o algoritmo

Vamos agora utilizar o nosso modelo para efetuar uma predição sobre o nosso dataset. Usaremos a função predict do sklearn e o nosso conjunto de dados X_test.


In [16]:
predicao = model.predict(X_test)

In [17]:
predicao


Out[17]:
array([1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0,
       1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1,
       0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1,
       0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0,
       1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1,
       0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1,
       0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0,
       1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1,
       0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0,
       1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0,
       1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0,
       1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0])

É natural querermos saber quão bom é o desempenho do modelo que desenvolvemos sobre o dataset. Para calcular a acurácia podemos utilizar a função score do sklearn.tree.


In [28]:
accuracy = model.score(X_test, y_test)*100

In [31]:
print('Accuracy: %s%%' % accuracy)


Accuracy: 97.0909090909%

5. Avaliar o algortimo utilizando o K-fold Cross Validation

Podemos também usar o método da validação cruzada com k-fold para avaliar o nosso algoritmo. Iremos utilizar as funções KFold e cross_val_score, ambas terão de ser importadas da biblioteca sklearn.model_selection.


In [35]:
from sklearn.model_selection import KFold, cross_val_score
import numpy as np

Utilizaremos 5 folds.


In [36]:
k_fold = KFold(n_splits=5)

In [37]:
scores = cross_val_score(model, X, Y, cv=k_fold, n_jobs=-1)

Podemos então ver o desempenho do algoritmo quando avaliado sobre cada um dos folds.


In [38]:
scores


Out[38]:
array([ 0.95272727,  0.98181818,  0.97810219,  0.91240876,  0.98175182])

Por fim podemos calcular a média dos scores utilizando a função mean da biblioteca numpy, que já foi importada acima.


In [41]:
media = np.mean(scores) * 100

In [42]:
print('Media: %s%%' % media)


Media: 96.1361645654%

FIM