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ómetrosdef rule(km): if km < 100: return 1 if km < 250: return 2 return 3right = 0for 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)}")pedido 2: la regla dice 1, tardó 2pedido 5: la regla dice 2, tardó 3pedido 6: la regla dice 2, tardó 3pedido 9: la regla dice 3, tardó 4pedido 10: la regla dice 3, tardó 4pedido 12: la regla dice 3, tardó 5la regla acierta 6 de 12Seis 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 cargadef 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 dayspedido 2: las reglas dicen 1, tardó 2pedido 12: las reglas dicen 4, tardó 5las reglas aciertan 10 de 12Diez 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íaORDERS = [ [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 unoDAYS = [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 − mindef 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 / kPaso a paso, en palabras
- 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.
- La distancia a cada pedido anterior. Cada diferencia se divide por el rango de su característica: los km no tapan a los kg.
- Luego ordena los pedidos por distancia: los más parecidos van primero.
- Toma los primeros k = 3: pedido 5, pedido 8, pedido 4. Los demás no cuentan.
- La predicción es el promedio de sus días: (3 + 3 + 2) / 3 = 2,7 días.
- 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 / kEl 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")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íasEl 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}")Con k = 1 la predicción son los días del más cercano: pedido 4, 2 días. Con k = 3 es el promedio de 2, 2 y 3: 7 / 3 = 2,33, impreso con una cifra decimal como 2.3.
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")140 km, 10.0 kg, 30 paquetes: 2.7 días90 km, 1.0 kg, 20 paquetes: 1.3 días350 km, 4.0 kg, 40 paquetes: 4.0 díasQué 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 / La suma es sobre k pedidos, así que se divide por k. Si la divides por el número de todos los pedidos, la predicción queda mucho menor que la real.
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 diferencia no se divide por el rango. Los kilómetros están en cientos, los kilogramos y los paquetes en una y dos cifras, así que la distancia casi solo depende de los kilómetros. El peso del paquete y el mensajero ocupado no pesan nada. Con / RANGE[j] (en C# / range[j]) cada característica queda en la misma escala.
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, kgORDERS = [ [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 empaquetarMINUTES = [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.
4 artículos, 1.5 kg: 6.7 min11 artículos, 10.0 kg: 20.7 minMá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
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: Parámetros y return · Lo que comparten todos los objetos: atributos de clase · Lambdas · Ordenamiento estable y el ordenamiento incorporado
- Continúa en: Regresión lineal · Descenso del gradiente · Clasificación: fraude o no · Evaluación: la exactitud miente
- Ejercicios sobre el tema: Cómo aprende un modelo