Implemente um classifacor Naive Bayes para o problema de predizer a qualidade de um carro. Para este fim, utilizaremos um conjunto de dados referente a qualidade de carros, disponível no UCI. Este dataset de carros possui as seguintes features e classe:
Attributos
Classes
Crie uma versão de sua implementação usando as funções disponíveis na biblioteca SciKitLearn para o Naive Bayes (veja aqui)
Analise a acurácia dos dois algoritmos e discuta a sua solução.
In [533]:
#Bibliotecas
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
In [534]:
#Lendo o arquivo com os dados
df = pd.read_csv('carData.csv', header=None)
df.columns = ['buying', 'maint','doors','persons','lug_boot','safety','class']
df.head()
Out[534]:
In [535]:
#Separa em conjunto de treino (70%) e teste (30%)
df_train, df_test = train_test_split(df,test_size=0.3)
#Faz uma copia dos mesmos dados para usar na questão 2
df_train_sci = df_train.copy()
df_test_sci = df_test.copy()
In [536]:
#separa conjunto de teste em features e classe e calcula o tamanho total de treino
df = df_train
totalSize = len(df)
df_test_X = df_test.iloc[:,0:-1]
df_test_y = df_test['class']
In [537]:
#separa os dados por classe
for class_value in df['class'].unique():
class_set = df[df['class'] == class_value]
In [538]:
#Calcula a frequencia de cada atributo de cada feature em cada classe
#frequency = [[[[k,len(df[df['class'] == i].index & df[df[j] == k].index)] for k in df[j].unique()] for j in df.columns[:-1]] for i in df['class'].unique()]
frequency = [[[len(df[df['class'] == i].index & df[df[j] == k].index) for k in df[j].unique()] for j in df.columns[:-1]] for i in df['class'].unique()]
frequency
Out[538]:
In [539]:
frequency_test = [[[k for k in df[j].unique()] for j in df.columns[:-1]] for i in df['class'].unique()]
In [540]:
#Monta tabela de indices para enumerar cada atributo
columns = ['buying', 'maint','doors','persons','lug_boot','safety']
matrixA={}
p = 0;
for j in columns:
matrixA[j] = frequency_test[0][p]
p += 1
matrixA['class'] = list(df['class'].unique())
frequency_label = pd.DataFrame.from_dict(matrixA, orient='index')
frequency_label
Out[540]:
In [541]:
#(frequency_label.loc['buying'] == 'vhigh').argmax()
#sum(frequency[3][0][:] + frequency[2][0][:] + frequency[1][0][:] + frequency[0][0][:])
#frequency[3][0][:]
In [542]:
#Calcula a probabilidade de cada feature
likelihood_feature = [[(frequency[0][index][((frequency_label.loc[j] == k).argmax())] + frequency[1][index][((frequency_label.loc[j] == k).argmax())] + frequency[2][index][((frequency_label.loc[j] == k).argmax())] + frequency[3][index][((frequency_label.loc[j] == k).argmax())])/totalSize for k in df[j].unique()] for index,j in enumerate(df.columns[:-1])]
likelihood_feature
Out[542]:
In [543]:
#Calcula a probabilidade de cada classe
likelihood_class = [(sum(df['class'] == h))/totalSize for h in df['class'].unique()]
likelihood_class
Out[543]:
In [544]:
#Calcula a probabilidade de cada atributo em cada feature em cada classe
frequency_prob = [[[len(df[df['class'] == i].index & df[df[j] == k].index)/sum(df['class'] == i) for k in df[j].unique()] for j in df.columns[:-1]] for i in df['class'].unique()]
frequency_prob
Out[544]:
In [545]:
#data = df_test.iloc[0]
#data = data[:-1] #esclui a classe
#df_test.iloc[0]
In [546]:
#feature_index = [(frequency_label.loc[i] == data.loc[i]).argmax() for i in data.index]
#feature_index
In [547]:
#Faz a predição do conjunto de teste
df_test = df_test_X
predicts = ["" for x in range(len(df_test))]
k = 0;
for row in range(len(df_test)):
data = df_test.iloc[row]
feature_index = [(frequency_label.loc[h] == data.loc[h]).argmax() for h in data.index]
class_chance = np.zeros(len(likelihood_class))
for i,prob_class in enumerate(likelihood_class):
prob = 1;
for j,feature in enumerate(feature_index):
prob *= frequency_prob[i][j][feature]
class_chance[i] = (prob * prob_class) #/ likelihood_feature
predicts[k] = [frequency_label.loc['class'][class_chance.argmax()]]
k += 1
In [548]:
result_y = np.squeeze(np.asarray(predicts))
correct_values = np.sum(result_y == df_test_y.values)
In [549]:
correct_pct = correct_values / len(df_test_y)
print('Porcentagem de acerto = {0}'.format(correct_pct))
In [550]:
print("\nClassification Report:")
print(classification_report(y_true=df_test_y, y_pred=result_y, target_names=["unacc", "acc", "good", "vgood"]))
In [551]:
from sklearn.naive_bayes import MultinomialNB, GaussianNB
from sklearn.preprocessing import LabelEncoder
In [552]:
#Transforma os valores string em númericos
#Na minha versão foi feito o tratamento de todos os valores sem precisar converter, porém
#a função do scikit não é adaptado para valores strings
for i in range(0, df_train_sci.shape[1]):
df_train_sci.iloc[:,i] = LabelEncoder().fit_transform(df_train_sci.iloc[:,i])
for i in range(0, df_test_sci.shape[1]):
df_test_sci.iloc[:,i] = LabelEncoder().fit_transform(df_test_sci.iloc[:,i])
#Separa em features e classes
train_X_sci = df_train_sci.iloc[:,:-1]
test_X_sci = df_test_sci.iloc[:,:-1]
train_y_sci = df_train_sci.iloc[:,-1]
test_y_sci = df_test_sci.iloc[:,-1]
In [553]:
#Faz o fit e o predict com a implementação do scikit
nb = GaussianNB()
nb.fit(train_X_sci, train_y_sci)
result_y_sci = nb.predict(test_X_sci)
correct_pct_sci = accuracy_score(test_y_sci, result_y_sci)
In [554]:
print('Porcentagem de acerto = {0}'.format(correct_pct_sci))
In [555]:
print("\nClassification Report:")
print(classification_report(y_true=test_y_sci, y_pred=result_y_sci, target_names=["unacc", "acc", "good", "vgood"]))
In [556]:
print('Resultado no meu algoritmo: {0}'.format(correct_pct))
print('Resultado no algoritmo do sklearn: {0}'.format(correct_pct_sci))
Na primeira etapa são somadas as quantidades de ocorrências de cada atributo. Em seguida é criado um mapa de índices para ser possível de identificar os valores de probabilidades armazenados, de acordo com os labels string. Então é calculado a probabilidade de cada feature e posteriormente a probabilidade de cada classe, e a probabilidade de cada atributo que foi calculado na etapa inicial.
Com todas as probabilidades a etapa de predição consiste, basicamente, em fazer o seguinte cálculo de probabilidades, já armazenadas.
$$ v_{NB} = argmax(P(v_{j}) \prod_{i} P(A_{i}|v_{j})) $$Algumas observações podem ser feitas sobre resultados. O algoritmo do sklearn apresentou um desempenho abaixo do algoritmo implementado. O resultado dos dois algoritmos para o mesmo conjunto de dados, podem ser observados acima. Isso pode ter acontecido devido a transformação dos dados em numéricos para o algoritmo do sklearn, podendo ter sido realizado alguma medida, em que os dados foram mal ponderados pelos valores assumidos. Já no algoritmo implementado, no qual as probabilidades foram calculadas corretamente, e sem alterar os valores das features, o resultado sempre ficou próximo dos 85%. Considerando que um resultado razoável para esta base é de 70% a 76%, o resultado ficou bem acima do esperado.
É possível observar que as classes 'vgood' e 'acc' tiveram uma taxa de acerto baixa, principalmente no algoritmo do sklearn, isso deve ter acontecido devido ao desbalanceamento entre as classes, e pelo algoritmo utilizar apenas dados de probabilidades. Algumas classes podem apresentar valores de características bem representativos e já outras nem tanto. Fazendo com que as predições caiam nas classes bem representadas.