# machine-learning-en-python
Machine Learning en Python
Volver a la portada del cursoCategoría de Clases con KNN
Sección 3 · Modelos Supervisados Básicos
Categoría de clases con KNN
PyShop quiere saber qué clientes contratarían el envío premium para ofrecérselo a ellos y no a todos. Ya no se predice un número, como el gasto anual, sino una categoría: 0 o 1. Eso es clasificación, y K vecinos más cercanos (KNN) es el clasificador más fácil de razonar: para decidir sobre un cliente nuevo, busca a los clientes más parecidos que ya conoce y copia lo que hicieron ellos.
La idea: votan los vecinos
KNN no aprende coeficientes. Al llamar a fit solo guarda los datos de entrenamiento. Al predecir, calcula la distancia del cliente nuevo a cada cliente guardado, toma los k más cercanos y devuelve la clase mayoritaria entre ellos. Con k=5, si tres de los cinco vecinos son premium, la predicción es premium y predict_proba da 0.6.
La distancia por defecto en scikit-learn es la euclidiana, sobre todas las variables a la vez:
$$d(a, b) = \sqrt{\sum_{j=1}^{p} (a_j - b_j)^2}$$
En el curso se usan 7 variables por cliente: edad, antigüedad, visitas al mes, minutos por sesión, compras en 12 meses, ticket promedio y gasto anual. Los 1000 clientes limpios se parten 80/20 con stratify=y, para que entrenamiento y prueba conserven la misma proporción de premium (0.362).
Primero, la línea base
Antes de entrenar nada, pregúntate cuánto acierta un modelo tonto. En prueba hay 128 clientes no premium y 72 premium. Predecir «no premium» para todos acierta 128 de 200: 0.64 de exactitud. Cualquier modelo que no supere ese número no aporta nada, aunque 0.64 suene a algo.
Escalar no es opcional
El gasto anual va de 0 a unos 20 000 pesos; las visitas al mes, de 0.5 a 12.7. En la fórmula de la distancia, una diferencia de 1000 pesos pesa lo mismo que mil visitas. Sin escalar, el vecino «más cercano» es simplemente el que gasta parecido, y las otras seis variables casi no cuentan.
StandardScaler resta la media y divide entre la desviación estándar de cada columna, así que todas quedan en la misma escala. Con k=5, KNN sin escalar da 0.81 en prueba y con escalado sube a 0.83. El escalador va dentro de un pipeline para que la media y la desviación se calculen solo con el entrenamiento:
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
VARS = ["edad", "antiguedad_meses", "visitas_mes", "minutos_sesion",
"compras_12m", "ticket_promedio", "gasto_anual"]
X, y = df[VARS], df["premium"]
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
knn = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=5))
knn.fit(Xtr, ytr)
print(round(knn.score(Xte, yte), 3)) # 0.83
Elegir k
k controla qué tan local es la decisión. Esta es la exactitud con escalado para varios valores:
| k | Entrenamiento | Prueba |
|---|---|---|
| 1 | 1.000 | 0.795 |
| 3 | 0.877 | 0.790 |
| 5 | 0.863 | 0.830 |
| 11 | 0.854 | 0.840 |
| 21 | 0.830 | 0.855 |
| 51 | 0.815 | 0.845 |
Con k=1, cada cliente de entrenamiento es su propio vecino y el acierto es perfecto: el modelo memoriza, incluido el ruido. Al subir k, la frontera entre clases se suaviza, el entrenamiento baja y la prueba sube hasta 0.855 con k=21. Con k=51 empieza a promediar clientes que ya no se parecen y la prueba cae a 0.845.
Un aviso: esta tabla usa el conjunto de prueba para comparar. Si eliges k así, el 0.855 deja de ser una estimación honesta. La forma correcta, con validación cruzada y GridSearchCV, se ve en la sección de optimización; ahí la búsqueda también elige k=21.
La matriz de confusión
La exactitud resume todo en un número y oculta qué tipo de error se comete. La matriz de confusión del modelo con k=5 lo muestra (filas: clase real; columnas: predicha):
from sklearn.metrics import confusion_matrix
print(confusion_matrix(yte, knn.predict(Xte)))
# [[115 13]
# [ 21 51]]
Acierta 115 no premium y 51 premium: 166 de 200, el 0.83. Pero de los 72 premium reales detecta 51 y se le escapan 21. Dos métricas lo cuentan mejor:
- Precisión = 51 / (51 + 13) = 0.797: de los que marca como premium, ocho de cada diez lo son.
- Recall = 51 / (51 + 21) = 0.708: de los premium reales, encuentra siete de cada diez.
Cuál importa más depende del negocio. Si ofrecer el envío a quien no lo quiere cuesta poco, el error caro son los 21 premium no detectados, y conviene mirar el recall.
Exactitud con clases desbalanceadas
Aquí las clases están 64/36 y la exactitud todavía dice algo. Con 98/2, un modelo que nunca predice la clase minoritaria tendría 0.98 de exactitud y no serviría para nada. Por eso, en clasificación, compara siempre contra la línea base y revisa la matriz de confusión antes de celebrar un número.
Trampas comunes
- No escalar. La variable con números más grandes decide sola la distancia.
- Escalar antes de partir. Ajustar el
StandardScalercon todos los datos filtra información del test; usa un pipeline. - Elegir k mirando el test. El resultado queda inflado; usa validación cruzada.
- Reportar solo la exactitud. Sin línea base ni matriz de confusión, 0.83 no dice si el modelo sirve.
Cierre
Cuando entrenes un clasificador, escribe tres números antes de mirar la exactitud: la línea base, cuántos casos positivos hay en prueba y cuántos detecta el modelo. Si el modelo no mejora claramente la línea base en la clase que te importa, todavía no está listo.