Ir al contenido
wannacode

Qué es el aprendizaje automático

En resumen: El aprendizaje automático es una forma de que el programa saque la regla por sí solo a partir de ejemplos (características y una etiqueta), en lugar de que un programador la escriba a mano con ifs.

Problema

Cuántos días tardará el pedido

La página del producto tiene que decir «llega en 2 días». Un colega escribe la regla según los kilómetros: hasta 100 km es 1 día, hasta 250 km son 2 días, y más allá, 3. Luego la ejecuta sobre 12 pedidos anteriores cuyo tiempo de viaje conocemos:

# la regla del colega: según los kilómetros
def rule(km):
if km < 100:
return 1
if km < 250:
return 2
return 3
right = 0
for i, (order, days) in enumerate(zip(ORDERS, DAYS)):
guess = rule(order[0])
if guess == days:
right += 1
else:
print(f"pedido {i + 1}: la regla dice {guess}, tardó {days}")
print(f"la regla acierta {right} de {len(ORDERS)}")
Salida
pedido 2: la regla dice 1, tardó 2
pedido 5: la regla dice 2, tardó 3
pedido 6: la regla dice 2, tardó 3
pedido 9: la regla dice 3, tardó 4
pedido 10: la regla dice 3, tardó 4
pedido 12: la regla dice 3, tardó 5
la regla acierta 6 de 12

Seis de doce. El colega añade dos reglas: un paquete pesado (más de 10 kg) tarda un día más, y también un mensajero ocupado (más de 35 paquetes ese día). El mismo bucle, con la regla nueva:

# dos reglas más: un paquete pesado y un mensajero con mucha carga
def rule(km, kg, parcels):
days = 1 if km < 100 else 2 if km < 250 else 3
if kg > 10:
days += 1
if parcels > 35:
days += 1
return days
Salida
pedido 2: las reglas dicen 1, tardó 2
pedido 12: las reglas dicen 4, tardó 5
las reglas aciertan 10 de 12

Diez de doce, pero con tres números nuevos puestos a ojo: 10 kg, 35 paquetes y «un día más». ¿Por qué no 8 kg? ¿Por qué +1 y no +0,5? Y el mes que viene la tienda cambia de mensajero, y todos los números vuelven a fallar. Con 50 000 pedidos y diez cosas que importan, nadie puede ajustarlos a mano.

La idea

La regla a partir de los ejemplos

Dale la vuelta. En lugar de escribir la regla y comprobarla con los pedidos anteriores, dale al programa los pedidos anteriores y deja que encuentre la regla por sí solo. Esto se llama aprendizaje automático: un programa que deduce la regla de los ejemplos en lugar de que alguien la escriba.

Cada ejemplo tiene dos partes:

  • características: lo que sabes del pedido antes de que salga. Aquí son tres: la distancia, el peso y cuántos paquetes tiene el mensajero ese día;
  • etiqueta: la respuesta que quieres adivinar. Aquí es el número de días que tardó el pedido.
# pedidos anteriores: km, kg, paquetes del mensajero ese día
ORDERS = [
[15, 1.2, 18], [40, 0.8, 35], [60, 3.5, 20],
[120, 2.0, 22], [150, 12.0, 25], [180, 1.5, 40],
[220, 0.5, 15], [250, 8.0, 30], [300, 2.5, 45],
[320, 18.0, 20], [380, 1.0, 28], [400, 6.0, 42],
]
# cuántos días tardó cada uno
DAYS = [1, 2, 1, 2, 3, 3, 2, 3, 4, 4, 3, 5]

Lo que obtienes de los ejemplos y da una respuesta para un pedido nuevo se llama modelo. Hay muchos tipos de modelos. Empezamos por el más simple, donde puedes ver cada número.

La idea

Los vecinos más cercanos

¿Cómo adivinarías? Buscarías pedidos anteriores que se parezcan al nuevo: una distancia parecida, un peso parecido, una carga parecida. Si tardaron 3 días, el nuevo probablemente tardará lo mismo.

Eso es un modelo: k vecinos más cercanos (k-NN). Guarda todos los pedidos anteriores. Para un pedido nuevo busca los k más cercanos y devuelve el promedio de sus días. Aquí k = 3.

«Cerca» significa una distancia pequeña entre dos pedidos. La diferencia de cada característica se eleva al cuadrado, se suman los cuadrados y se saca la raíz cuadrada de la suma, como la distancia entre dos puntos en un mapa.

Hay una trampa. Los kilómetros están en cientos, los kilogramos en una cifra. Si sumas las diferencias tal cual, 10 kg pesan lo mismo que 10 km, que es nada, y los kilómetros deciden solos. Por eso cada diferencia se divide por el rango de su característica (el valor mayor menos el menor en los datos). Así cada característica queda en una escala: 1 significa «de un extremo al otro».

# cuán separados están los valores de cada característica: max − min
def ranges(rows):
return [max(row[j] for row in rows) - min(row[j] for row in rows)
for j in range(len(rows[0]))]
def distance(a, b):
total = 0
for j in range(len(a)):
d = (a[j] - b[j]) / RANGE[j]
total += d * d
return math.sqrt(total)
math.sqrt es la raíz cuadrada, por eso el programa empieza con import math.

Míralo

Los tres más cercanos al pedido nuevo

def nearest(order):
dist = [distance(order, row) for row in ORDERS]
return sorted(range(len(ORDERS)),
key=lambda i: dist[i])
def predict(order, k):
near = nearest(order)
total = 0
for i in near[:k]:
total += DAYS[i]
return total / k
Paso a paso, en palabras
  1. Un pedido nuevo: 140 km, 10 kg, 30 paquetes del mensajero. Abajo están los 12 pedidos anteriores y cuántos días tardó cada uno.
  2. La distancia a cada pedido anterior. Cada diferencia se divide por el rango de su característica: los km no tapan a los kg.
  3. Luego ordena los pedidos por distancia: los más parecidos van primero.
  4. Toma los primeros k = 3: pedido 5, pedido 8, pedido 4. Los demás no cuentan.
  5. La predicción es el promedio de sus días: (3 + 3 + 2) / 3 = 2,7 días.
  6. Nadie escribió una regla. La respuesta viene de los pedidos anteriores que más se parecen al nuevo.

El modelo mide la distancia a cada pedido anterior, los ordena y toma los primeros k:

def nearest(order):
dist = [distance(order, row) for row in ORDERS]
return sorted(range(len(ORDERS)),
key=lambda i: dist[i])
def predict(order, k):
near = nearest(order)
total = 0
for i in near[:k]:
total += DAYS[i]
return total / k

El pedido nuevo es de 140 km, 10 kg y 30 paquetes para el mensajero:

order = [140, 10.0, 30]
print(f"los 3 más cercanos a {order[0]:.0f} km, {order[1]:.1f} kg, {order[2]:.0f} paquetes:")
for i in nearest(order)[:3]:
o = ORDERS[i]
print(f" pedido {i + 1}: {o[0]:.0f} km, {o[1]:.1f} kg, {o[2]:.0f} paquetes → {DAYS[i]} días "
f"(distancia {distance(order, o):.2f})")
print(f"predicción: {predict(order, 3):.1f} días")
Salida
los 3 más cercanos a 140 km, 10.0 kg, 30 paquetes:
pedido 5: 150 km, 12.0 kg, 25 paquetes → 3 días (distancia 0.20)
pedido 8: 250 km, 8.0 kg, 30 paquetes → 3 días (distancia 0.31)
pedido 4: 120 km, 2.0 kg, 22 paquetes → 2 días (distancia 0.53)
predicción: 2.7 días

El más cercano es el paquete pesado de 150 km. El pedido 8 está 100 km más lejos, pero es pesado y de un día igual de ocupado, así que queda segundo. El pedido ligero de 120 km es el tercero. El promedio de 3, 3 y 2 días es 2,7.

Pruébalo

Predice la salida

Para un pedido de 230 km, 3 kg y 24 paquetes, los tres más cercanos son el pedido 4 (2 días), el pedido 7 (2 días) y el pedido 8 (3 días), en ese orden. ¿Qué imprimirá el programa?

order = [230, 3.0, 24]
print(f"{predict(order, 1):.1f} {predict(order, 3):.1f}")
Respuesta

La idea

Una predicción para pedidos nuevos

El modelo está listo en cuanto tiene los datos. Pedidos nuevos:

fresh = [[140, 10.0, 30], [90, 1.0, 20], [350, 4.0, 40]]
for order in fresh:
print(f"{order[0]:.0f} km, {order[1]:.1f} kg, {order[2]:.0f} paquetes: {predict(order, 3):.1f} días")
Salida
140 km, 10.0 kg, 30 paquetes: 2.7 días
90 km, 1.0 kg, 20 paquetes: 1.3 días
350 km, 4.0 kg, 40 paquetes: 4.0 días

Qué tan bien adivina, estos tres números no lo dicen. La comprobación honesta se hace con pedidos que el modelo no ha visto, y está en la lección sobre la evaluación.

¿Cómo eliges k? Con k = 1 la predicción son los días de un único pedido, y puede ser una excepción: un festivo, un paquete perdido. Con k igual a todos los pedidos, la predicción es el promedio de todo, y el modelo deja de distinguir 15 km de 400 km. Normalmente pruebas unos cuantos valores y te quedas con el que menos se equivoca con pedidos no vistos.

Pruébalo

Completa

La predicción es el promedio de los días de los k más cercanos. ¿Por qué se divide la suma?

total = 0
for i in near[:k]:
    total += DAYS[i]
return total / 

Pruébalo

Encuentra el error

Un colega reescribe la distancia. El programa funciona, pero los vecinos del pedido de 140 km y 10 kg ahora son los pedidos 5, 4, 6: el pedido pesado 8 del mismo día ocupado ha salido, y un paquete ligero ha ocupado su lugar. Para el pedido de 90 km la predicción pasa a ser 1.7 días en lugar de 1.3. ¿En qué línea está el error?

Elige la línea con el error.

La idea

Cuándo el aprendizaje automático es la herramienta equivocada

Un modelo es una regla que nadie escribió. Eso es a la vez su fuerza y su coste.

  • La regla es conocida y estable. El envío cuesta 4,90 € hasta 2 kg, 6,90 € por encima de 2 kg, gratis desde 50 €. Eso es un if, no un modelo. Un modelo la aprendería solo de forma aproximada y a veces se equivocaría por unos céntimos.
  • Necesitas una respuesta exacta o una explicación. Impuestos, un descuento de un contrato, el derecho de devolución. «Normalmente es así» no basta ahí, y «lo decidió el modelo» no es una explicación para el cliente.
  • No hay datos. Una tienda nueva sin pedidos anteriores no tiene de qué aprender. Entonces una regla a ojo es un comienzo honesto, y los datos llegan después.
  • El mundo ha cambiado. El modelo solo conoce el pasado. Un mensajero nuevo, festivos, una huelga: las predicciones basadas en los pedidos viejos mienten hasta que el modelo se entrena de nuevo.
  • Un error sale caro. Un modelo que falla en 1 de cada 20 casos sirve para «llega en 2 días» y no sirve para «dejar pasar el pago». Ahí hay una regla o una persona al lado.

El aprendizaje automático es para los casos en que la regla tiene demasiadas partes para que una persona la escriba y hay muchos ejemplos.

Más precisamente, para curiosos

Lo que cuesta una predicción. k-NN no aprende nada de antemano: para cada pedido nuevo compara con todos los anteriores y los ordena. Con n pedidos son al menos n distancias por predicción. Con 12 pedidos no es nada, con 10 millones es lento. Por eso las bibliotecas reales usan estructuras como un árbol k-d, y con muchas características buscan de forma aproximada. Qué significa «crece con n» está en la lección sobre Big-O.

La misma búsqueda, pero para texto. La búsqueda por significado en bases de datos es k vecinos más cercanos sobre vectores: la lección sobre vectores del curso de SQL.

Dividir por el rango se llama normalización min-max. Otra forma es restar la media y dividir por la desviación estándar. Por qué la escala de las características importa también para otros modelos lo verás en la lección sobre el descenso del gradiente.

Las herramientas reales. Aquí todo son bucles y arreglos, para que veas cada número. En la práctica el aprendizaje automático se hace sobre todo en Python: NumPy para arreglos, scikit-learn para los modelos clásicos (allí k-NN es KNeighborsRegressor), PyTorch para redes neuronales. C# tiene ML.NET, Java tiene DJL y Tribuo, JavaScript tiene TensorFlow.js y ONNX Runtime. Las ideas son las mismas.

Entrenar modelos y ejecutarlos en producción (servidores de entrenamiento, despliegue, vigilar si un modelo se queda viejo) es infraestructura y se enseña en el curso de DevOps ZeroToOps.

Ejercicio

Cuántos minutos tarda el empaquetado

Resuelve la tarea en tu editor.

El almacén quiere saber cuántos minutos tardará en empaquetarse un pedido, para planificar al personal. Las características son el número de artículos y el peso total, y la etiqueta son los minutos. Aquí tienes diez pedidos anteriores:

# pedidos anteriores: cantidad de artículos, kg
ORDERS = [
[1, 0.5], [2, 1.0], [3, 4.0], [5, 2.0], [6, 8.0],
[8, 3.0], [10, 12.0], [12, 5.0], [15, 9.0], [20, 6.0],
]
# minutos para empaquetar
MINUTES = [4, 6, 9, 10, 15, 14, 22, 18, 25, 28]

Escribe k vecinos más cercanos con k = 3 y división por el rango, y predice dos pedidos nuevos: 4 artículos y 1,5 kg, y 11 artículos y 10 kg.

Salida
4 artículos, 1.5 kg: 6.7 min
11 artículos, 10.0 kg: 20.7 min

Más tareas

  • Sin el rango. Quita la división por el rango y mira qué vecinos salen. ¿Qué característica decide ahora?
  • Pesos. Dale un peso a los vecinos: el más cercano cuenta más (por ejemplo 1 / distancia). ¿Cambia la predicción?

Test

Ponte a prueba

1. La tienda entrena un modelo que dice cuántos días tardará un pedido. ¿Cuál es la etiqueta?
2. ¿Por qué la distancia divide cada diferencia por el rango de su característica?
3. ¿Qué hace k vecinos más cercanos cuando llega un pedido nuevo?
4. El envío cuesta 4,90 € hasta 2 kg y 6,90 € por encima de 2 kg, y es gratis desde 50 €. ¿Cómo lo calculas?
5. ¿Por qué k = 1 es arriesgado?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·