1 - Introduccion

Volver al Inicio





El siguiente trabajo consiste en realizar un modelo que clasifique lo mejor posible nuestros datos de hongos en venenosos y no venenosos. Luego del análisis exploratorio y limpieza de datos se va a entrenar un modelo de Knn Vecinos mas Cercanos que nos sirva para clasificar y poder predecir en base a nuevas features.



Librerías

Para utilizar código Python en R Markdown se usa la librería reticulate.



library(reticulate)

Sys.setenv(RETICULATE_PYTHON = 'C:/ProgramData/Anaconda3/python.exe')

use_python('C:/ProgramData/Anaconda3/python.exe')
import numpy as np
import pandas as pd
import seaborn as sb
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score, recall_score, precision_score, f1_score
from sklearn.model_selection import train_test_split, cross_val_score, KFold
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')

def colored(r, g, b, text):
        return "\033[38;2;{};{};{}m{} \033[38;2;255;255;255m".format(r, g, b, text)


2 - Descripcion del Modelo

Volver al Inicio



KNN es uno de los algoritmos más sencillos de machine learning. La famosa frase “dime con quién andas y te diré quién eres” tal vez sea la mejor explicación informal del algoritmo. Básicamente, KNN se encarga de memorizar la ubicación de cada muestra del conjunto de entrenamiento de acuerdo a los valores de sus features. Cuando recibe un dato nuevo, lo ubica en la posición del espacio que le corresponde según sus características y encuentra los k vecinos más cercanos (k es un hiperparámetro del modelo que define cuántos vecinos se van a considerar al momento de hacer las predicciones). Estos vecinos o puntos próximos son las muestras del conjunto de entrenamiento que resultan más similares a la observación que queremos clasificar. Una vez que se identificaron los k vecinos más cercanos, cada uno de ellos aporta un “voto” a la clase a la que corresponde. La predicción queda determinada por la clase mayoritaria entre los k vecinos más cercanos.

\[ p(y = c|x, D, k) = \frac{1}{k} \sum_{i \in N_k(x, D)} I(y_i = c) \] donde \(x\) es el vector de features del dato a predecir, \(D\) es el conjunto de datos de entrenamiento, k es la cantidad de vecinos a evaluar en \(D\), \(N_k(x, D)\) son los índices de los k vecinos más cercanos e \(I(y_i = c)\) es una función indicadora que se define como

\(I(e)=\left\{ \begin{array}{ll} 1\ si\ e\ es\ cierta\\ 0\ si\ e\ es\ falsa\end{array} \right.\)

La clase con mayor probabilidad es la que será predicha.



3 - Limpieza de Datos

Volver al Inicio



data_mush = 'secondary_data.csv'
data_hongo = pd.read_csv(data_mush, sep=';')
data_hongo.info()
## <class 'pandas.core.frame.DataFrame'>
## RangeIndex: 61069 entries, 0 to 61068
## Data columns (total 21 columns):
##  #   Column                Non-Null Count  Dtype  
## ---  ------                --------------  -----  
##  0   class                 61069 non-null  object 
##  1   cap-diameter          61069 non-null  float64
##  2   cap-shape             61069 non-null  object 
##  3   cap-surface           46949 non-null  object 
##  4   cap-color             61069 non-null  object 
##  5   does-bruise-or-bleed  61069 non-null  object 
##  6   gill-attachment       51185 non-null  object 
##  7   gill-spacing          36006 non-null  object 
##  8   gill-color            61069 non-null  object 
##  9   stem-height           61069 non-null  float64
##  10  stem-width            61069 non-null  float64
##  11  stem-root             9531 non-null   object 
##  12  stem-surface          22945 non-null  object 
##  13  stem-color            61069 non-null  object 
##  14  veil-type             3177 non-null   object 
##  15  veil-color            7413 non-null   object 
##  16  has-ring              61069 non-null  object 
##  17  ring-type             58598 non-null  object 
##  18  spore-print-color     6354 non-null   object 
##  19  habitat               61069 non-null  object 
##  20  season                61069 non-null  object 
## dtypes: float64(3), object(18)
## memory usage: 9.8+ MB
data_hongo['has-ring'] = data_hongo['has-ring'].replace('f',0)
data_hongo['has-ring'] = data_hongo['has-ring'].replace('t',1)
data_hongo['has-ring'].value_counts()
## 0    45890
## 1    15179
## Name: has-ring, dtype: int64
data_hongo.isnull().sum()
## class                       0
## cap-diameter                0
## cap-shape                   0
## cap-surface             14120
## cap-color                   0
## does-bruise-or-bleed        0
## gill-attachment          9884
## gill-spacing            25063
## gill-color                  0
## stem-height                 0
## stem-width                  0
## stem-root               51538
## stem-surface            38124
## stem-color                  0
## veil-type               57892
## veil-color              53656
## has-ring                    0
## ring-type                2471
## spore-print-color       54715
## habitat                     0
## season                      0
## dtype: int64
lista = data_hongo.isnull().sum()
values = []
for i in range(len(lista)):
    if data_hongo.isnull().sum()[i] > 0:
        value = data_hongo.isnull().sum().index[i]
        values.append(value)
data_hongo.drop(columns = values, inplace = True)


4 - Visualizaciones

Volver al Inicio



from matplotlib import pyplot as plt
plt.figure(figsize=(6,6))
mush_size = data_hongo['class'].value_counts().values.tolist()
mush_types = data_hongo['class'].value_counts().axes[0].tolist()
mush_labels = 'E', 'P'
colors = ['#2ea1ff', '#ff2e93']


import plotly.express as px

fig = px.scatter_3d(data_hongo, 
                    title = "3D Plot",
                    x = 'cap-diameter', 
                    y = 'stem-height', 
                    z = 'cap-shape', 
                    color = data_hongo['class'].astype('str'))
fig.update_layout(
                  width=600,
                  height=500)
#fig.show()


5 - Modelo

Volver al Inicio



data_hongo = pd.get_dummies(data_hongo, drop_first=True)
data_hongo.rename(columns={'class_p': 'class'}, inplace = True)
X = data_hongo.drop(columns=['class'])
y = data_hongo['class']

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state = 0)

Para poder comprender qué ocurre con el dilema entre sesgo y varianza conforme varía k, conviene pensar en las situaciones extremas:

• De mínima, el valor de k no puede ser inferior a 1, puesto que de ser igual a 0 no habría ningún vecino contra el que comparar a los datos nuevos (tampoco tendría mucho sentido hablar de identificar los -2 vecinos más cercanos, por ejemplo). Cuando k vale 1, el modelo va a estar “pegándose” demasiado a cada punto del set de entrenamiento, lo que lleva a una pérdida considerable de generalidad. Las “islas” azules dentro del área naranja que se observan en la primera figura son una clara representación de esta situación. Por esto, solemos asociar valores demasiado bajos de k con una alta varianza del modelo.

• De máxima, k no puede ser mayor al número se samples del conjunto de entrenamiento, ya que estaríamos pidiéndole al modelo que evalúe más puntos de los que conoce. Si k = n, el modelo sólo aprende a predecir la clase mayoritaria en el training set, de ahí que en la última figura de la grilla observemos que el área azul cubre todo el gráfico. Por esto, valores demasiado altos de k suelen asociarse a un alto sesgo del modelo. Hallar un valor adecuado de k es un aspecto crucial al momento de trabajar con KNN. Para ello, será necesario trabajar con un esquema de cross-validation que nos permita elegir un k óptimo en base a múltiples pruebas.

kf = KFold(n_splits = 5, shuffle = True, random_state = 12)

k_range = list(range(2,15))
scores = []
try:
    for i in k_range:
        knn = KNeighborsClassifier(n_neighbors = i)
        cv_scores = cross_val_score(knn, X_train, y_train, cv = kf)
        scores.append(np.mean(cv_scores))
except Exception as e:
    print(e)
    print(colored(238, 75, 43, '\nPide otra version de numpy para este algoritmo'))

knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train, y_train)
## KNeighborsClassifier(n_neighbors=3)
accuracy_score(y_test, knn.predict(X_test))
## 0.9910924810060256
from sklearn.metrics import confusion_matrix

y_pred = knn.predict(X_test)

confusion = confusion_matrix(y_test, y_pred)

def get_metrics(y_test, y_pred, model):
    accuracy = accuracy_score(y_test, y_pred).round(3)
    recall   = recall_score(y_test, y_pred).round(3)
    precision= precision_score(y_test, y_pred).round(3)
    f_1      = f1_score(y_test, y_pred).round(3)
    metrics  = f"\n\nAccuracy:  {accuracy}\nRecall:    {recall}\nPrecision: {precision}\nf1 Score:  {f_1}."
    return print(colored(0, 75, 43, 'Modelo: ' + model), colored(238, 75, 43, metrics))

get_metrics(y_test, y_pred, 'Knn')
## Modelo: Knn  
## 
## Accuracy:  0.991
## Recall:    0.994
## Precision: 0.99
## f1 Score:  0.992. 


6 - Conclusiones

Volver al Inicio



El modelo Knn tiene un accuracy del 99 %. Se comprobó que el accuracy de los datos de entrenamiento como testeo es casi el mismo, por lo tanto, podemos concluir que no solo es un buen modelo sino también no se encuentra sobre ajustado.

Este modelo nos sirve para clasificar y predecir en base a futuras features que tengamos sobre hongos.