1 - Introduccion
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
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
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
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
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')## [38;2;0;75;43mModelo: Knn [38;2;255;255;255m [38;2;238;75;43m
##
## Accuracy: 0.991
## Recall: 0.994
## Precision: 0.99
## f1 Score: 0.992. [38;2;255;255;255m
6 - Conclusiones
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.