01La versión corta
BoxBox resuelve un problema de clasificación supervisada. Cada ejemplo es un piloto en una carrera, y el modelo responde: ¿cuál es la probabilidad de que este piloto gane, de que termine 2.º o 3.º, o de que quede fuera del podio?
Piénsalo como una función pura f(variables) → [p_victoria, p_podio, p_resto] cuyos parámetros no escribimos a mano: se ajustan mirando 3854 ejemplos reales (de 2018-R1 a 2026-R18, 191 carreras). Entrenar es eso: buscar los parámetros que hacen que la función acierte lo mejor posible con los datos que ya conocemos, con la esperanza de que también acierte con los que no conoce.
Esa última frase es todo el oficio: un modelo que solo acierta en los datos con los que se entrenó no sirve para nada. Casi todo lo que verás abajo (cómo se construyen las variables, cómo se valida, por qué el modelo es pequeño) existe para que el modelo generalice a carreras que no ha visto.
02El pipeline, de punta a punta
Si vienes del desarrollo de software, un sistema de ML se parece más a un build que a una feature: hay una fase offline que produce un artefacto (el modelo entrenado) y una fase online que lo carga y responde peticiones. El código de cada paso vive en el monorepo: boxbox-ml/ (datos y entrenamiento), backend/ (API) y frontend/ (esta web).
- 1Extraer
boxbox_ml.extractResultados y clasificación de cada GP desde Jolpica (el sucesor de la API Ergast) y el clima de las dos primeras horas de carrera desde Open-Meteo. Se guarda en Parquet, una fila por piloto y carrera.
- 2Variables
03Los datos
Una fila del dataset es un piloto en un Gran Premio: posición de salida, posición en la clasificación del sábado, distancia a la pole en %, equipo, circuito, clima y cómo terminó. En total hay 3854 filas de 191 carreras.
Usamos las temporadas desde 2018. Primero probamos solo desde 2022, cuando cambió el reglamento técnico (coches de efecto suelo), porque en ML más datos no siempre es mejor: datos de un mundo distinto al que vas a predecir pueden enseñar cosas que ya no son ciertas (distribution shift). En vez de suponerlo, lo medimos con la misma validación carrera a carrera: añadir 2018-2021 bajó el log loss de 0,2915 a 0,2788 y subió el acierto del favorito del 53 % al 62 %. Con tan pocos ganadores, los años extra pesaron más que el cambio de reglamento, así que se quedaron.
El clima histórico sale del reanálisis ERA5, una reconstrucción hora a hora de la atmósfera, promediado en las dos horas desde la salida. Para las carreras futuras se usa el pronóstico, con el mismo código de agregación. Si llueve más de 1 mm en esa ventana, la carrera cuenta como mojada. Ese umbral se eligió mirando casos reales: Suzuka 2025 fue seca y ERA5 marcaba 0,5 mm.
04Qué predecimos exactamente
La variable objetivo (target o label) es lo que el modelo aprende a predecir. Podríamos predecir la posición exacta (1, 2, 3… 22), pero eso es muy ruidoso: la diferencia entre acabar 14.º o 15.º depende de cosas casi aleatorias y no nos interesa. Por eso agrupamos el resultado en tres clases:
- Clase 0 · VictoriaTerminó 1.º. Aproximadamente 1 de cada 22 filas.
- Clase 1 · Podio sin ganarTerminó 2.º o 3.º. Unas 2 de cada 22.
- Clase 2 · RestoTodo lo demás, abandonos incluidos.
El modelo devuelve tres probabilidades que suman 1 para cada piloto. La probabilidad de podio es simplemente p(victoria) + p(2.º–3.º), así que nunca puede ser menor que la de ganar. Si entrenáramos dos modelos separados (uno para ganar y otro para podio), podrían contradecirse.
05Variables: donde se gana o se pierde todo
Una variable (feature) es una columna numérica que el modelo puede usar para decidir. El algoritmo solo ve números: no sabe qué es un piloto ni qué es Mónaco. La ingeniería de variables consiste en traducir el conocimiento del dominio a columnas útiles. En proyectos con pocos datos, como este, suele importar más que elegir el algoritmo.
Estas son las 18 variables del modelo, agrupadas por la pregunta que responden:
¿Dónde sale?
- Posición de salida
grid - La variable más fuerte, con diferencia. Los que salen desde el pit lane se colocan detrás del último.
- Posición en clasificación
quali_position - Suele coincidir con la de salida, salvo que haya penalizaciones. Cuando no coinciden, delata a un coche rápido que sale atrás.
- Distancia a la pole (%)
quali_gap_pct - Cuánto más lento fue en clasificación, en porcentaje. Separa una pole por 0,05 % de otra por 0,5 %.
06El algoritmo: XGBoost
El modelo es un ensamble de árboles de decisión entrenado con gradient boosting (librería XGBoost). Es el estándar para datos tabulares, es decir, filas y columnas como una hoja de cálculo. En este tipo de problemas suele ganar a las redes neuronales, sobre todo con pocos datos.
Un árbol de decisión es un montón de if/else aprendidos de los datos. El algoritmo prueba cortes como «¿grid ≤ 2?» o «¿lluvia = 1?» y se queda con los que mejor separan a los ganadores del resto. Un árbol solo es interpretable pero torpe.
si grid ≤ 2:
si team_avg_points_5 > 30: sumar +1,2 a «victoria»
si no: sumar +0,4 a «victoria»
si no:
si rainfall = 1: sumar +0,1 a «victoria»
si no: sumar −0,9 a «victoria»Boosting significa entrenar los árboles en secuencia, cada uno pequeño y centrado en corregir los errores que dejaron los anteriores. Se parece a refactorizar por iteraciones: el primer árbol capta lo grueso (salir delante ayuda), el siguiente se fija en los casos que el primero falló (ese que salía 6.º y ganó con lluvia), y así sucesivamente. La predicción final es la de todos los árboles, convertida en tres probabilidades con una .
07Validar sin hacer trampa
Para saber si un modelo funciona hay que probarlo con datos que no vio al entrenar. La receta de manual es mezclar las filas y separar un 20 % al azar para probar. Aquí sería un error grave: el modelo entrenaría con carreras de 2026 para «predecir» carreras de 2024. Eso es ver el futuro, y el futuro (qué coche es rápido este año) es justo lo que queremos predecir.
Con datos que dependen del tiempo, la validación tiene que respetar el orden. Usamos un backtest walk-forward carrera a carrera, que reproduce exactamente cómo se usaría el modelo en la vida real:
- 1Antes de cada GP de las tres últimas temporadas, se entrena un modelo nuevo solo con las carreras anteriores.
- 2Ese modelo predice la parrilla completa del GP.
- 3Se guarda la predicción y se compara con el resultado real.
- 4
08Cómo medimos si acierta
No hay una métrica única: cada una responde a una pregunta distinta. Estas son las del backtest (66 carreras), comparadas con una línea base.
Log loss
menor es mejorMide si las probabilidades son buenas, no solo si el orden lo es. Castiga muchísimo estar muy seguro y equivocarse. Si dices 95 % y no gana, la penalización es enorme; si dices 30 % y no gana, apenas cuenta. Es la métrica que optimiza el entrenamiento.
09De probabilidades sueltas a una pizarra coherente
El modelo puntúa a cada piloto por separado: para él, cada fila es independiente. Por eso, si sumas la probabilidad de victoria de los 22 pilotos, no te sale exactamente 100 %. Puede salir 93 % o 108 %, y eso no tiene sentido en una carrera con un solo ganador.
La API lo corrige después de predecir: reescala las probabilidades de victoria para que sumen 100 % y las de podio para que sumen 300 % (hay tres plazas). Lo hace respetando dos reglas: ninguna probabilidad de podio puede pasar del 100 % y ninguna puede ser menor que la de ganar. Si un piloto se pasaría del 100 %, se queda en el tope y el resto se reparte entre los demás (lo que se llama water-filling).
Es un paso pequeño, pero ilustra algo importante: la salida cruda de un modelo casi nunca es lo que enseñas al usuario. Siempre hay una capa de lógica de producto encima.
10Qué variables pesan más
XGBoost lleva la cuenta de cuánto mejoró la predicción cada vez que usó una variable para cortar un árbol. A eso se le llama ganancia (gain). Repartida en porcentajes, da una idea de en qué se fija el modelo desplegado:
- Posición en clasificación
quali_position35 % - Posición de salida
grid29 % - Puntos medios (últimas 5)
drv_avg_points_55,5 % - Puntos del equipo (últimas 5)
team_avg_points_5
11En producción
El modelo entrenado es un fichero model.joblib de unos 300 KB, versionado en el repositorio. Guarda los árboles, la lista de circuitos conocidos y el histórico de resultados que hace falta para calcular la forma reciente. La API (FastAPI, en Render) lo carga una vez al arrancar y desde ahí cada predicción tarda milisegundos.
El calendario se consulta a Jolpica y el pronóstico de cada carrera, a Open-Meteo. Ambos se guardan una hora en memoria, para no depender de que esos servicios respondan en cada visita. El pronóstico llega solo cuando faltan 14 días o menos para la carrera; antes, el modelo trabaja sin clima y asume pista seca.
Cada predicción que se pide queda registrada en Postgres (Neon) con la versión del modelo. Eso permitirá más adelante medir si el modelo empeora con el tiempo (model drift), que en F1 pasa en cuanto un equipo trae mejoras al coche.
12Reentreno automático después de cada GP
Un modelo de F1 caduca rápido. Las variables de forma (puntos de las últimas 5 carreras, posición media, abandonos…) solo están al día si el modelo conoce la última carrera, y cada fin de semana trae algo nuevo: un coche que mejora, un piloto que pierde ritmo. Si no se reentrena, el modelo sigue prediciendo con el mundo de hace semanas.
Por eso el reentreno está automatizado con GitHub Actions, el CI del propio repositorio, en .github/workflows/retrain.yml. Es exactamente lo que harías a mano, pero ejecutado por una máquina y con controles que impiden desplegar un modelo peor.
13Limitaciones honestas
- Pocos datos. Unas 2.000 filas y apenas un centenar de ganadores. Cada victoria rara (un piloto que gana desde la 9.ª posición con lluvia) pesa mucho y es difícil distinguir el patrón del ruido.
- Le falta medio deporte. Sin telemetría, ritmo de carrera, estrategia, neumáticos ni safety cars, el modelo no puede ver muchas de las cosas que deciden una carrera.
- El mundo cambia. El reglamento de 2026 cambió los coches, y la forma de los equipos varía con cada actualización técnica. Pesar más las carreras recientes, o detectar estos cambios automáticamente, es un siguiente paso natural.
- La parrilla la pones tú. Antes de la clasificación, el modelo usa el orden de salida de la última carrera como suposición. Su calidad depende de que esa parrilla se parezca a la real.
- Las probabilidades son probabilidades. Un 40 % significa que, en situaciones parecidas, ese piloto gana unas 4 de cada 10 veces. Que pierda hoy no prueba que el modelo esté mal, y que gane tampoco prueba que esté bien. Para juzgarlo hay que mirar muchas carreras: para eso está el Historial.
14Glosario rápido
- Feature / variable
- Una columna de entrada que el modelo usa para decidir.
- Target / label
- Lo que se quiere predecir; aquí, victoria, podio o resto.
- Entrenamiento
- Ajustar los parámetros del modelo para que acierte con datos ya conocidos.
- Hiperparámetro
- Configuración del algoritmo que se fija antes de entrenar (profundidad de los árboles, ritmo de aprendizaje…).
- Overfitting
- Memorizar el ruido de los datos de entrenamiento: va bien en pruebas y mal con datos nuevos.
- Data leakage
- Que se cuele en el entrenamiento información que no estaría disponible al predecir.
- Walk-forward
- Validación que respeta el tiempo: predecir cada momento solo con lo anterior.
- Baseline
- Una solución simple contra la que comparar; si no la superas, el modelo sobra.
- Calibración
- Que un 30 % del modelo ocurra de verdad alrededor del 30 % de las veces.
- Early stopping
- Dejar de añadir árboles cuando el error en datos reservados deja de bajar.