Todas las semanas escogemos un proyecto de la vida real para que construyas tu portafolio y te prepares para conseguir un trabajo. Todos nuestros proyectos están construidos con ChatGPT como co-pilot!
Únete al retoUn podcast de cultura tecnológica donde aprenderás a luchar contra los enemigos que te bloquean en tu camino para convertirte en un profesional exitoso en tecnología.
Escuchar el podcastA continuación veremos cómo podemos implementar este modelo en Python. Para ello, utilizaremos la librería scikit-learn
.
import pandas as pd
from sklearn.model_selection import train_test_split
total_data = pd.read_csv("https://raw.githubusercontent.com/4GeeksAcademy/machine-learning-content/master/assets/clean_iris.csv")
X = total_data.drop("specie", axis = 1)
y = total_data["specie"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)
X_train.head()
El conjunto train lo utilizaremos para entrenar el modelo, mientras que con el test lo evaluaremos para medir su grado de efectividad.
Para asegurar el correcto funcionamiento de este modelo y evitar errores, la estandarización de los datos es necesaria ya que si las características no están en la misma escala, aquellas con magnitudes más grandes pueden dominar la distancia y afectar el resultado del algoritmo.
Por ejemplo, si tenemos dos características: la edad (con valores entre 0 y 100) y el ingreso anual (con valores entre 0 y 100.000), la diferencia de escala entre ambas variables podría hacer que el ingreso anual tenga un impacto desproporcionado en la distancia ignorando la importancia de la edad.
Estandarizar los datos ayuda a que todas las características contribuyan de manera equitativa a la distancia, lo que puede mejorar el rendimiento del algoritmo KNN. La elección entre normalización y estandarización Min-Max dependerá del comportamiento de las variables y de cómo afectan al rendimiento del modelo. Si tenemos características con diferentes escalas y rango, Min-Max es la mejor alternativa. Si por el contrario tienen la misma escala o similar, la normalización es la más apropiada.
A continuación visualizaremos la relación entre las variables del dataset (hemos escogido tres para realizar un gráfico 3D, ya que no podemos coger más y graficarlas; no existen los gráficos 4D):
# Añadimos el nombre de la especie para el plot
total_data["specie"] = total_data["specie"].map({0: "setosa", 1: "versicolor", 2: "virginica"})
import plotly.express as px
fig = px.scatter_3d(total_data, x = "petal width (cm)", y = "petal length (cm)", z = "sepal width (cm)", color = "specie", width = 1000, height = 500,
size = total_data["petal length (cm)"].abs(), color_discrete_sequence=["#E58139", "#39E581", "#8139E5"])
camera = dict(
up = dict(x = 1, y = 3.5, z = 0),
eye = dict(x = 2, y = 0, z = 0)
)
fig.update_layout(scene_camera = camera)
fig.show()
El gráfico 3D permite analizar la separación y distribución de las clases en función de la combinatoria de las 3 variables. Claramente se observa que dado un punto y las variables predictoras seríamos capaz de, aprovechando la potencia y capacidad predictora de un modelo KNN, de realizar una precisión adecuada.
Para más información, podríamos calcular un diagrama de puntos para la relación entre las variables dos a dos (esto tendría que hacerse en el EDA):
import matplotlib.pyplot as plt
import seaborn as sns
fig, axis = plt.subplots(2, 3, figsize = (15, 7))
palette = ["#E58139", "#39E581", "#8139E5"]
sns.scatterplot(ax = axis[0, 0], data = total_data, x = "sepal length (cm)", y = "sepal width (cm)", hue = "specie", palette = palette)
sns.scatterplot(ax = axis[0, 1], data = total_data, x = "sepal length (cm)", y = "petal length (cm)", hue = "specie", palette = palette)
sns.scatterplot(ax = axis[0, 2], data = total_data, x = "sepal length (cm)", y = "petal width (cm)", hue = "specie", palette = palette)
sns.scatterplot(ax = axis[1, 0], data = total_data, x = "sepal width (cm)", y = "petal length (cm)", hue = "specie", palette = palette)
sns.scatterplot(ax = axis[1, 1], data = total_data, x = "sepal width (cm)", y = "petal width (cm)", hue = "specie", palette = palette)
sns.scatterplot(ax = axis[1, 2], data = total_data, x = "petal length (cm)", y = "petal width (cm)", hue = "specie", palette = palette)
plt.tight_layout()
plt.show()
Comparando las predictoras una a una (para que sea más gráfico y explícito) se observa mejor la separación en función de los valores de las clases. Por lo tanto, el modelo KNN es también muy apropiado para resolver el problema.
from sklearn.neighbors import KNeighborsClassifier
model = KNeighborsClassifier()
model.fit(X_train, y_train)
El tiempo de entrenamiento de un modelo dependerá, en primer lugar, del tamaño del conjunto de datos (instancias y características), y también de la tipología de modelo y su configuración.
Una vez se ha entrenado el modelo, se puede utilizar para predecir con el conjunto de datos de prueba.
y_pred = model.predict(X_test)
y_pred
Con los datos en crudo es muy complicado saber si el modelo está acertando o no. Para ello, debemos compararlo con la realidad. Existe una gran cantidad de métricas para medir la efectividad de un modelo a la hora de predecir, entre ellas la precisión (accuracy), que es la fracción de predicciones que el modelo realizó correctamente.
from sklearn.metrics import accuracy_score
accuracy_score(y_test, y_pred)
El modelo es perfecto!
Una vez tenemos el modelo que estábamos buscando (presumiblemente tras la optimización de hiperparámetros), para poder utilizarlo a futuro es necesario almacenarlo en nuestro directorio, junto a la semilla.
from pickle import dump
dump(model, open("knn_classifier_default.sav", "wb"))
Añadir un nombre explicativo al modelo es vital, ya que en el caso de perder el código que lo ha generado sabremos qué configuración tiene (en este caso decimos default
porque no hemos personalizado ninguno de los hiperparámetros del modelo, hemos dejado los que tiene por defecto la función).
Para ejemplificar la implementación de un algoritmo KNN mostraremos cómo generar un conjunto de datos que satisfaga nuestras necesidades.
import pandas as pd
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
X, y = make_regression(n_samples = 1000, n_features = 4, noise = 1, random_state = 42)
X = pd.DataFrame(X, columns = ["Var1", "Var2", "Var3", "Var4"])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)
X_train.head()
El conjunto train lo utilizaremos para entrenar el modelo, mientras que con el test lo evaluaremos para medir su grado de efectividad. Dividiremos también las predictoras de las características.
En la regresión también es necesario estandarizar los datos. En este caso, ya nos vienen estandarizados. Tal y como hemos hecho anteriormente, dibujaremos de nuevo el gráfico 3D y las relaciones una a una de las características del dataset generado artificialmente:
import plotly.express as px
total_data = X.copy()
total_data["target"] = y
fig = px.scatter_3d(total_data, x = "Var1", y = "Var2", z = "Var3", color = "target", width = 1000, height = 500,
size = total_data["Var4"].abs())
camera = dict(
up = dict(x = 1, y = 3.5, z = 0),
eye = dict(x = 2, y = 0, z = 0)
)
fig.update_layout(scene_camera = camera)
fig.show()
import matplotlib.pyplot as plt
import seaborn as sns
fig, axis = plt.subplots(2, 3, figsize = (15, 7))
palette = sns.color_palette("gnuplot2_r", as_cmap=True)
sns.scatterplot(ax = axis[0, 0], data = total_data, x = "Var1", y = "Var2", hue = "target", palette = palette)
sns.scatterplot(ax = axis[0, 1], data = total_data, x = "Var1", y = "Var3", hue = "target", palette = palette)
sns.scatterplot(ax = axis[0, 2], data = total_data, x = "Var1", y = "Var4", hue = "target", palette = palette)
sns.scatterplot(ax = axis[1, 0], data = total_data, x = "Var2", y = "Var3", hue = "target", palette = palette)
sns.scatterplot(ax = axis[1, 1], data = total_data, x = "Var2", y = "Var4", hue = "target", palette = palette)
sns.scatterplot(ax = axis[1, 2], data = total_data, x = "Var3", y = "Var4", hue = "target", palette = palette)
plt.tight_layout()
plt.show()
Vemos que para la mayoría de variables se establece cierto patrón diferenciador y que la regresión puede arrojar buenos resultados.
from sklearn.neighbors import KNeighborsRegressor
model = KNeighborsRegressor()
model.fit(X_train, y_train)
Una vez se ha entrenado el modelo, se puede utilizar para predecir con el conjunto de datos de prueba.
y_pred = model.predict(X_test)
y_pred
Para calcular la efectividad del modelo utilizaremos el error cuadrático medio (MSE):
from sklearn.metrics import mean_squared_error, r2_score
print(f"Error cuadrático medio: {mean_squared_error(y_test, y_pred)}")
print(f"Coefficient of determination: {r2_score(y_test, y_pred)}")
El modelo está muy cerca de ser perfecto.
Una vez tenemos el modelo que estábamos buscando (presumiblemente tras la optimización de hiperparámetros), para poder utilizarlo a futuro es necesario almacenarlo en nuestro directorio, junto a la semilla.
dump(model, open("knn_regressor_default.sav", "wb"))
Añadir un nombre explicativo al modelo es vital, ya que en el caso de perder el código que lo ha generado sabremos qué configuración tiene (en este caso decimos default
porque no hemos personalizado ninguno de los hiperparámetros del modelo, hemos dejado los que tiene por defecto la función).