ML Atlas

Machine learning desde cero · Lección 1.4 · 5 de 33 lecciones listas · 12 min

Vecinos más cercanos: el modelo más simple

En pocas palabras

kNN no aprende ninguna regla: un caso nuevo recibe la etiqueta de la mayoría de sus k ejemplos más cercanos. k fija las fronteras; las unidades, la cercanía.

Calentamiento Dos preguntas de lecciones anteriores. Un minuto, y no bloquea la lección.
De la lección 1.3: La referencia: ¿con qué comparas tu modelo? Un modelo de regresión tiene un error de 4096 y la media, de 5076. ¿Cuánto mejor que la referencia es el modelo?

Ver la respuesta

Respuesta correcta: Un 19,3 %, porque 1 − 4096 / 5076 ≈ 0,193, que no es mucho

Se compara con la referencia: la recta con el IMC reduce el error un 19,3 %, el modelo con 10 medidas un 44,3 %.

De la lección 1.2: Datos, características, etiquetas y particiones ¿Por qué el conjunto de prueba debe quedar intacto hasta el final?

Ver la respuesta

Respuesta correcta: Porque su puntuación solo vale si ninguna decisión se tomó mirándolo

En cuanto el conjunto de prueba ha influido en una elección, su puntuación deja de ser un sustituto honesto de los datos futuros.

La idea más simple para un modelo es esta: los casos parecidos tienen respuestas parecidas. Para reconocer un pingüino nuevo, busca los k pingüinos de aprendizaje que más se le parecen y pregunta de qué especie son. Es el método de los k vecinos más cercanos (en inglés, k-nearest neighbours, kNN). Volvemos a los pingüinos de la lección 0.1, esta vez descritos solo con dos medidas: la longitud del pico y la masa corporal.

Predice y comprueba Con k = 1 cada pingüino de aprendizaje es su propio vecino más cercano, así que el modelo los conoce todos sin un solo error. ¿Qué pasa con los pingüinos que nunca ha visto?
¿Qué tan seguro estás?

Experimento interactivo

Este experimento interactivo funciona en un navegador con JavaScript activado. El texto que lo rodea describe lo que muestra.

Ver la respuesta

Respuesta correcta: Acierta menos, en promedio menos que kNN preguntando a 9 vecinos

En los pingüinos de aprendizaje k = 1 obtiene un 100 %, en los de prueba un 94,2 % en la partición 1. En promedio sobre 100 particiones, k = 1 acierta un 92,2 % y k = 9 un 94,2 %; k = 9 gana en 78 particiones y pierde en 9. Un solo vecino copia cada excepción; el voto de 9 vecinos la promedia.

Prueba esto

  • El lab empieza en unidades brutas, milímetros y gramos, con k = 1. Cambia a unidades estandarizadas: ¿qué pasa con las fronteras y con el resultado?
  • Todavía con k = 1, mira las fronteras entre especies: ¿dónde están deshilachadas?
  • Sube k. ¿Con qué k se suavizan las fronteras y con cuál baja claramente el resultado en la prueba?
  • Compara la última línea, la media de 20 particiones, para k = 1 y k = 9.

Cuando lo hayas comprobado en el lab, pongamos nombre a lo que ves.

La idea en pocas palabras

kNN no construye ninguna regla. «Aprender» es guardar los 239 pingüinos de aprendizaje. Todo el trabajo ocurre al llegar una pregunta: para un pingüino nuevo, el modelo calcula la distancia a cada uno de los guardados, toma los k más cercanos y deja decidir a la mayoría. El fondo del lab es justo esa respuesta en cada punto del gráfico.

El número k fija cuánto se fía el modelo de los ejemplos sueltos. Con k = 1 la frontera rodea a cada pingüino, incluidas las excepciones que están entre otra especie. Con un k grande, por ejemplo 101 de 239, vota casi medio conjunto, las fronteras se suavizan y la especie más rara, Chinstrap, pierde votaciones: en la partición 1 la exactitud baja al 83,5 %. Un buen k está en medio, y se elige con datos, no a ojo.

El segundo mando se ve menos: las unidades. La distancia suma la diferencia en la longitud del pico y la diferencia en la masa. Los picos van de 32,1 a 59,6 mm, y la masa de 2700 a 6300 g. Sin reescalar, una diferencia de 100 g pesa lo mismo que 100 mm de pico, así que casi solo la masa decide quién está cerca. Estandarizar convierte cada medida en un número de desviaciones estándar respecto a la media, y las dos medidas pasan a pesar lo mismo.

Jak to policzono
  • Datos: Palmer Penguins (Gorman, Williams y Fraser, 2014; paquete palmerpenguins: Horst, Hill y Gorman, 2020), licencia CC0. 342 pingüinos con todas las medidas, 3 especies.
  • Partición: 70/30, estratificada; la partición 1 es train_test_split(test_size=0.3, stratify=y, random_state=1) en scikit-learn, la misma que en la lección 0.1. 239 pingüinos para aprender, 103 en la prueba.
  • Modelo: kNN con distancia euclídea, un voto por vecino; un empate lo gana la especie que va primero por orden alfabético (como en scikit-learn).
  • Estandarización: la media y la desviación estándar salen solo de los pingüinos de aprendizaje.

Por qué funciona

kNN funciona cuando estar cerca en las medidas significa parecerse en la respuesta. En los pingüinos se cumple: las especies difieren en su constitución, así que pingüinos con pico y masa parecidos suelen ser de la misma especie. Por eso dos medidas ya dan un 94,2 % frente al 43,7 % de adivinar «siempre Adelie».

Un k pequeño y uno grande estropean el resultado desde lados opuestos. Con k = 1 el modelo es muy sensible al azar: un pingüino de aprendizaje atípico cambia la respuesta en todo su entorno. El 100 % en aprendizaje no dice nada aquí, porque cada pingüino se encuentra a sí mismo. Con un k muy grande el modelo deja de ver la forma de los datos y se acerca a «la especie más común por aquí». Es el mismo equilibrio entre ajustar y generalizar que vuelve en el módulo 3.

Las unidades importan tanto porque kNN no sabe qué medida es importante: solo calcula distancias. En unidades brutas, con k = 5, acierta el 77,7 % de los pingüinos de prueba; tras estandarizar, el 94,2 %. Con las cuatro medidas, con la masa en gramos, la diferencia es aún mayor: 76,7 % sin escalar y 96,1 % tras estandarizar, y en promedio sobre 100 particiones 76,7 % y 98,9 %.

Error frecuente

¿Verdadero o falso?: «El k más pequeño es el mejor, porque el modelo se ajusta perfectamente a los datos». Falso. Con k = 1 el modelo obtiene un 100 % en los pingüinos de aprendizaje, porque cada uno es su propio vecino, pero con pingüinos nuevos pierde en promedio frente a k = 9: 92,2 % frente a 94,2 % en 100 particiones. Un ajuste perfecto a los datos de aprendizaje es memorizar también las excepciones. Elige k por el resultado en datos que el modelo no ha visto.

Dónde falla la analogía: los «vecinos» de kNN no son los pingüinos que están a tu lado en la playa, sino los de medidas parecidas. Y quién se parece depende de las unidades que elijas.

Test

Tres preguntas para comprobar lo aprendido. Elige una respuesta o despliega la solución bajo cada pregunta. Con el teclado: las flechas eligen, Enter o Espacio confirma.

1. ¿Cómo responde kNN a una pregunta sobre un caso nuevo?
Ver la respuesta

Respuesta correcta: Toma los k ejemplos de aprendizaje más cercanos y elige su mayoría

kNN no tiene regla: votan los k ejemplos guardados más cercanos. El fondo del lab muestra el resultado de esa votación en cada punto.

2. kNN con k = 1 obtiene un 100 % en los datos de aprendizaje. ¿Qué dice eso de los datos nuevos?
Ver la respuesta

Respuesta correcta: Casi nada, porque cada ejemplo de aprendizaje es su propio vecino

Con k = 1 un ejemplo de aprendizaje siempre se encuentra a sí mismo. Solo la prueba habla de los datos nuevos: en promedio 92,2 % con k = 1 y 94,2 % con k = 9.

3. ¿Por qué suelen estandarizarse las medidas antes de kNN?
Ver la respuesta

Respuesta correcta: Porque una medida en unidades grandes, como gramos, tapa a las demás

La distancia suma las diferencias de las medidas. La masa en miles de gramos tapa el pico en milímetros: con cuatro medidas, 76,7 % sin escalar y 96,1 % tras estandarizar.

Ideas clave

  • kNN no construye ninguna regla: un caso nuevo recibe el voto mayoritario de sus k ejemplos de aprendizaje más cercanos.
  • Un k pequeño sigue cada excepción y uno grande suaviza las fronteras hasta adivinar; k se elige con datos que el modelo no ha visto.
  • El 100 % en aprendizaje con k = 1 no significa nada, porque cada ejemplo es su propio vecino.
  • kNN calcula distancias, así que las unidades deciden el resultado: estandariza las medidas con los datos de aprendizaje.

Más a fondo: fórmula, código, práctica

Las matemáticas

La distancia euclídea entre los pingüinos a y b descritos por dos medidas es d(a, b) = √((a₁ − b₁)² + (a₂ − b₂)²). El modelo ordena los pingüinos de aprendizaje por d y toma los k primeros; la respuesta es la especie con más votos entre ellos.

Estandarizar convierte una medida x en z = (x − μ) / σ, donde μ y σ son la media y la desviación estándar de esa medida en los datos de aprendizaje. Sin ello, una diferencia de 100 g en la masa suma 100² = 10000 a d², y una diferencia de 10 mm en el pico solo 10² = 100. Tras estandarizar, cada medida se cuenta en desviaciones estándar y las dos pesan lo mismo.

Con k = 1 cada ejemplo de aprendizaje está a distancia 0 de sí mismo, así que la exactitud en aprendizaje es del 100 %, siempre que no haya dos ejemplos con etiquetas distintas y medidas idénticas.

En código (Python)

El mismo experimento en scikit-learn: las dos medidas del lab en unidades brutas y estandarizadas para cada k, k = 1 en los pingüinos de aprendizaje, k = 1, 9 y 101 en 100 particiones y las cuatro medidas con k = 5. El notebook enlazado bajo el resultado descarga los datos por sí solo.

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

df = pd.read_csv("data/raw/penguins.csv")
cols4 = ["bill_length_mm", "bill_depth_mm", "flipper_length_mm", "body_mass_g"]
d = df.dropna(subset=cols4)  # los 342 pingüinos con las cuatro medidas
y = d["species"]
K = [1, 3, 5, 9, 15, 25, 51, 101]
def knn(k, scale):
    model = KNeighborsClassifier(n_neighbors=k)
    return make_pipeline(StandardScaler(), model) if scale else model  # el escalador aprende media y desviación solo con la parte de aprendizaje
def score(cols, k, scale, seed):
    Xtr, Xte, ytr, yte = train_test_split(d[cols], y, test_size=0.3, stratify=y, random_state=seed)
    m = knn(k, scale).fit(Xtr, ytr)
    return m.score(Xte, yte), m.score(Xtr, ytr)
lab = ["bill_length_mm", "body_mass_g"]  # las dos medidas del lab: milímetros y gramos
for scale in (False, True):
    print(("standardized:" if scale else "raw mm and g:"), " ".join(f"k={k} {score(lab, k, scale, 1)[0]:.3f}" for k in K))
print(f"k=1 on the learning penguins: {score(lab, 1, True, 1)[1]:.3f}")
runs = {k: np.array([score(lab, k, True, s)[0] for s in range(100)]) for k in (1, 9, 101)}
print(f"100 splits, standardized: k=1 {runs[1].mean():.3f}, k=9 {runs[9].mean():.3f}, k=101 {runs[101].mean():.3f}; k=9 beats k=1 in {(runs[9] > runs[1]).sum()}, loses in {(runs[9] < runs[1]).sum()}")
four = [[score(cols4, 5, scale, s)[0] for s in range(100)] for scale in (False, True)]
print(f"all 4 measurements, k=5: raw {four[0][1]:.3f}, standardized {four[1][1]:.3f} on split 1; mean of 100 splits {np.mean(four[0]):.3f} and {np.mean(four[1]):.3f}")

Resultado

raw mm and g: k=1 0.835 k=3 0.748 k=5 0.777 k=9 0.757 k=15 0.777 k=25 0.748 k=51 0.738 k=101 0.738
standardized: k=1 0.942 k=3 0.922 k=5 0.942 k=9 0.942 k=15 0.942 k=25 0.942 k=51 0.942 k=101 0.835
k=1 on the learning penguins: 1.000
100 splits, standardized: k=1 0.922, k=9 0.942, k=101 0.856; k=9 beats k=1 in 78, loses in 9
all 4 measurements, k=5: raw 0.767, standardized 0.961 on split 1; mean of 100 splits 0.767 and 0.989

En la partición 1 los resultados son los mismos que en el lab. El lab promedia sus propias 20 particiones y este código 100 particiones de scikit-learn, así que las medias difieren un poco, pero la dirección es la misma: en promedio k = 9 supera a k = 1.

Para profesionales
  • Escala dentro de un pipeline: make_pipeline(StandardScaler(), KNeighborsClassifier()). Un escalador ajustado con todo el conjunto antes de la partición filtra información de la prueba al aprendizaje.
  • Elige k con validación cruzada, por ejemplo GridSearchCV sobre n_neighbors; con dos clases, un k impar evita empates.
  • weights="distance" da más peso a los vecinos más cercanos; suele ayudar con un k grande.
  • kNN responde despacio con muchos datos, porque calcula la distancia a cada ejemplo, y se debilita con muchas medidas, cuando todas las distancias se parecen.
  • Las medidas irrelevantes para la tarea también entran en la distancia y estropean el vecindario; elige las variables antes de usar kNN.

Profundiza en el Atlas

Entradas de la enciclopedia sobre las ideas de esta lección: