Lesson 17 of 52· Random search: por qué casi siempre gana
En Andina Stream tienes que ajustar el modelo que predice qué suscriptores van a cancelar. Hay tiempo para 24 entrenamientos con validación cruzada y ni uno más: es lo que cabe antes de la reunión del viernes.
3 × 2 × 2 × 2 = 24 combinaciones. Exhaustiva, simétrica, elegante.Mismo modelo, mismo dataset, mismo protocolo de validación, mismo coste. En la mayoría de los casos reales, el equipo B llega más lejos. Y no es suerte: es geometría.
Esta lección no tiene una sola llamada a RandomizedSearchCV (eso viene en la siguiente). Su objetivo es que entiendas por qué el muestreo aleatorio gana, con el número exacto en la mano, para que puedas defender la decisión ante alguien que insiste en "probémoslo todo". Al terminar sabrás calcular, antes de lanzar nada, cuántos ensayos aleatorios necesitas para tener una probabilidad concreta de caer en la zona buena del espacio.
Una rejilla (grid) es un producto cartesiano: eliges valores para cada uno de los hiperparámetros y evalúas todas las combinaciones. El presupuesto total es
Dado un presupuesto fijo , el número de valores distintos que llegas a probar en cada dimensión es:
Con dos hiperparámetros, : 100 entrenamientos exploran solo 10 valores de learning_rate. Con cuatro, la cosa se pone peor:
| Presupuesto | ||||
|---|---|---|---|---|
| 16 | 4 valores/eje | ~2.5 | 2 | ~1.7 |
| 64 | 8 | 4 | ~2.8 | ~2.3 |
| 243 | ~15.6 | ~6.2 | ~3.9 | 3 |
| 1024 | 32 | ~10 | ~5.7 | 4 |
El muestreo aleatorio, con el mismo presupuesto , prueba valores distintos en cada dimensión (cada punto trae su propio valor de cada hiperparámetro). Con y : la rejilla ve como mucho 3 tasas de aprendizaje; el azar ve 24.
La rejilla reparte su presupuesto de forma redundante: repite el mismo valor de
learning_rateuna y otra vez con distintos acompañantes. El azar no repite nunca.
Mirados de frente, los dos paneles parecen igual de razonables: 16 puntos repartidos por el cuadrado. La diferencia aparece cuando proyectas los puntos sobre el eje que de verdad importa — es decir, cuando te preguntas cuántos valores distintos del hiperparámetro relevante llegué a probar.
Ahí está el truco. La fila de la rejilla parece tener 4 marcas porque los 16 puntos se apilan de cuatro en cuatro sobre exactamente los mismos 4 valores. La fila aleatoria tiene 16 marcas distintas. Pagaste 16 entrenamientos en ambos casos, pero uno te compró cuatro veces más resolución en el eje que decide el resultado.
El argumento anterior sería una curiosidad si todos los hiperparámetros importaran por igual. La observación empírica clave —popularizada por Bergstra y Bengio en Random Search for Hyper-Parameter Optimization (JMLR, 2012)— es que no lo hacen:
En la mayoría de los espacios de búsqueda reales, la métrica depende fuertemente de uno o dos hiperparámetros y es casi plana en los demás. La dimensionalidad efectiva es mucho menor que la dimensionalidad nominal .
En un modelo de boosting sobre el dataset de cancelación de Andina Stream, learning_rate y la complejidad del árbol (max_depth / num_leaves) mueven la aguja; colsample_bytree entre 0.8 y 1.0 apenas la roza. Pero no sabes de antemano cuál es cuál: si lo supieras, ya habrías terminado.
Y aquí las dos estrategias se separan del todo:
| Rejilla | Aleatorio | |
|---|---|---|
| Valores distintos por dimensión con presupuesto | ||
| Depende de (dimensiones totales) | Sí, exponencialmente | No |
| Depende de (las que importan) | Sí | Sí |
| Coste de añadir un hiperparámetro irrelevante | Multiplica el presupuesto por | Cero |
Esa última fila es la razón práctica por la que el azar gana. Añadir a la rejilla un hiperparámetro que resulta ser irrelevante te multiplica la factura sin comprarte nada. Añadirlo a una búsqueda aleatoria de ensayos no cambia el coste: sigues haciendo entrenamientos, simplemente cada uno lleva también un valor aleatorio de esa dimensión inútil, y tu resolución en las dimensiones útiles no se degrada.
Una búsqueda aleatoria de 60 ensayos sobre 8 hiperparámetros, de los cuales solo 2 importan, se comporta —en lo que respecta a esos 2— como una búsqueda de 60 puntos bien repartidos en un plano. La rejilla equivalente sobre 8 ejes con solo 2 valores cada uno ya cuesta entrenamientos y sigue probando dos tasas de aprendizaje.
Con un presupuesto de 81 entrenamientos sobre un espacio de 4 hiperparámetros, calcula cuántos valores distintos por dimensión llega a probar cada estrategia. Completa las dos variables para que el programa imprima exactamente:
Rejilla: 3 valores distintos por dimension
Aleatorio: 81 valores distintos por dimension# Presupuesto fijo: 81 entrenamientos con validacion cruzada.
# Espacio de busqueda: 4 hiperparametros.
presupuesto = 81
dimensiones = 4
# TODO: cuantos valores DISTINTOS por dimension prueba una rejilla balanceada?
# (pista: es la raiz d-esima del presupuesto; usa round() para evitar 2.9999...)
valores_rejilla = 0
# TODO: cuantos valores DISTINTOS por dimension prueba el muestreo aleatorio?
valores_aleatorio = 0
print(f"Rejilla: {valores_rejilla} valores distintos por dimension")
print(f"Aleatorio: {valores_aleatorio} valores distintos por dimension")Hasta aquí el argumento es cualitativo. Ahora el número que hace que esto sea utilizable en el trabajo real.
Supón que defines un espacio de búsqueda y que el 5 % mejor de ese espacio (medido por volumen) contiene configuraciones que te sirven: no la óptima absoluta, sino "suficientemente buena". Si sacas puntos al azar de forma independiente y uniforme:
y por tanto la probabilidad de que al menos uno acierte es
Despejando para una confianza objetivo :
Lo asombroso de esta fórmula es lo que NO aparece en ella: la dimensión . Da igual que tu espacio tenga 2 o 20 hiperparámetros; si la región buena ocupa el 5 % del volumen, 60 puntos aleatorios la alcanzan con ~95 % de probabilidad. La rejilla, en cambio, paga la maldición de la dimensionalidad en el exponente.
Mueve el deslizador y observa cómo la probabilidad se dispara al principio y luego se aplana: los primeros 30 ensayos compran casi todo, los siguientes 30 compran los últimos puntos porcentuales.
| Ensayos | con | con |
|---|---|---|
| 10 | 40 % | 10 % |
| 20 | 64 % | 18 % |
| 30 | 79 % | 26 % |
| 60 | 95 % | 45 % |
| 100 | 99 % | 63 % |
| 300 | >99.9 % | 95 % |
La regla de bolsillo: ~60 ensayos aleatorios ≈ 95 % de probabilidad de tocar el 5 % superior. Es el número que justifica un presupuesto ante tu jefe sin agitar las manos.
learning_rate = 0.02 y tu rango empieza en 0.1, ninguna cantidad de azar te salvará. El muestreo aleatorio es robusto a rangos amplios, no a rangos equivocados.learning_rate uniformemente en pone el 99 % de tus puntos por encima de 0.003. Para hiperparámetros que se piensan en órdenes de magnitud hay que muestrear en escala logarítmica (lo verás en la siguiente lección); si no, tu "5 % del volumen" no es el 5 % que te interesa.Sí sirve, en cuatro situaciones concretas — y conviene decirlo con precisión, porque "random search siempre gana" es un eslogan, no una regla:
criterion ∈ {gini, entropy} × class_weight ∈ {None, balanced} son 4 combinaciones. Probarlas todas es lo correcto; muestrearlas al azar sería absurdo.random_state fijo, tu búsqueda no es reproducible y el "ganador" puede cambiar entre corridas (sección 12).Antes de pasar a RandomizedSearchCV en la siguiente lección, responde con código a la pregunta que de verdad tendrás que responder en el trabajo: ¿cuántos ensayos aleatorios necesito?
Implementa la fórmula de cobertura, evalúala para 20 y 60 ensayos, y calcula el número mínimo de ensayos que garantiza al menos un 95 % de probabilidad de caer en el 5 % superior del espacio.
Completa prob_exito con la fórmula y calcula n_minimo, el menor número entero de ensayos con el que para . El programa debe imprimir exactamente:
N=20: 64%
N=60: 95%
N minimo: 59import math
q = 0.05 # fraccion superior del espacio que consideramos "buena"
def prob_exito(n, q=0.05):
"""P(al menos uno de n puntos aleatorios cae en la fraccion q superior)."""
# TODO: implementa la formula de la leccion
return 0.0
for n in (20, 60):
print(f"N={n}: {prob_exito(n) * 100:.0f}%")
# TODO: menor entero n tal que prob_exito(n) >= 0.95
# (despeja n con logaritmos y redondea hacia arriba con math.ceil)
n_minimo = 0
print(f"N minimo: {n_minimo}")$35.10 USD
$39.00 USD10% off
E2E auto promo · optimizacion
Offer ends on September 4, 2026