11 · Pipelines reproducibles con DVC
Un montón de scripts que ejecutas en el orden correcto de memoria no es reproducible. Un `dvc.yaml` declara las etapas — prepare → train → evaluate — con sus comandos, dependencias y salidas, así que `dvc repro` re-ejecuta SOLO las etapas cuyas entradas cambiaron, y `dvc dag` dibuja todo el grafo.
Un `dvc.yaml` convierte scripts ad-hoc en un PIPELINE declarado: cada ETAPA lista su `cmd`, sus `deps` (archivos/parámetros de entrada) y sus `outs` (archivos producidos). DVC hace un hash de cada dependencia, así que `dvc repro` re-ejecuta SOLO las etapas cuyas entradas realmente cambiaron (saltándose el resto), `dvc dag` dibuja el grafo de dependencias, y todo el pipeline se vuelve un build reproducible, cacheado y rastreado por versión.
Sin esto:
Sin un pipeline declarado, reproducir un resultado significa recordar qué scripts ejecutar en qué orden — y re-ejecutar todo desde cero cada vez, incluso las etapas lentas cuyas entradas nunca cambiaron.
Versionar los datos (lección 10) resuelve qué datos usó un modelo. Pero el camino de los datos crudos a un modelo entrenado suele ser varios pasos — limpiar los datos, crear características, entrenar, evaluar — y unirlos a mano es frágil. "Corre prepare.py, luego train.py, luego evaluate.py, pero solo re-corre prepare si cambiaste los datos crudos...". Ese orden vive en tu cabeza y en un README, y se rompe en cuanto alguien olvida un paso o re-corre el entrenamiento lento cuando solo cambió la evaluación.
Los pipelines de DVC hacen ese camino declarativo. Describes tu flujo de trabajo en un archivo dvc.yaml como un conjunto de etapas (stages), y cada etapa declara tres cosas:
cmd— el comando a ejecutar (p. ej.python src/train.py).deps— las entradas de las que depende esta etapa: archivos de datos de entrada, el propio script, yparams(valores con nombre de unparams.yaml, comotrain.max_depth).outs— las salidas que produce esta etapa: un dataset limpio, un archivo de modelo, un JSON de métricas. DVC rastrea estas salidas de la misma forma direccionada por contenido con que rastrea los archivos dedvc add.
Una vez declaradas las etapas, DVC entiende el grafo de dependencias: train depende de la salida de prepare; evaluate depende de la salida de train. Ese grafo es un DAG (grafo acíclico dirigido), y dvc dag te lo dibuja.
La gran ventaja es dvc repro. Recorre el DAG y, para cada etapa, hace un hash de las dependencias de esa etapa y las compara con la última corrida. Si las entradas de una etapa no cambiaron, DVC la salta y reutiliza la salida cacheada. Solo las etapas cuyas dependencias realmente cambiaron — y las etapas aguas abajo de ellas — se re-ejecutan. ¿Ajustaste solo evaluate.py? DVC re-ejecuta solo evaluate, reutilizando el costoso modelo entrenado. ¿Cambiaste un parámetro de entrenamiento en params.yaml? Re-ejecuta train y evaluate pero salta prepare. Así es exactamente como funciona una herramienta de build como make, pero impulsada por hash de contenido y consciente de los datos.
Así que un pipeline de DVC te da tres cosas que una carpeta de scripts nunca puede: un único comando para reproducir todo (dvc repro), ejecución incremental (solo lo que cambió se re-ejecuta), y un grafo rastreado por versión e inspeccionable de todo el flujo de trabajo. DVC no se ejecuta en el navegador, así que el dvc.yaml de abajo es solo para leer — pero su estructura (cmd / deps / outs por etapa) es la idea entera.
Un `dvc.yaml` con tres etapas (prepare → train → evaluate), cada una declarando `cmd`, `deps`, `outs` (y `params`/`metrics`); las dependencias forman un DAG para que `dvc repro` re-ejecute solo las etapas cambiadas.
¿Por qué un pipeline `dvc.yaml` declarado supera a ejecutar scripts ad-hoc a mano?
- Un `dvc.yaml` declara el flujo de trabajo como etapas, cada una con un `cmd`, sus `deps` (entradas/params) y sus `outs` (archivos producidos) — convirtiendo un montón de scripts en un pipeline rastreado por versión.
- Las deps/outs forman un DAG; `dvc repro` re-ejecuta SOLO las etapas cuyas entradas (con hash) cambiaron (más las etapas aguas abajo), reutilizando las salidas cacheadas del resto.
- `dvc dag` dibuja el grafo y `dvc metrics show` rastrea las métricas entre versiones — así el pipeline es reproducible con un comando e inspeccionable, como `make` para ML.
Los repos de ML en producción incluyen un `dvc.yaml` (o un DAG de Airflow/Kubeflow) para que el camino datos→modelo sea un comando reproducible, re-ejecutando solo lo que cambió — la base que luego CI automatiza en cada commit.
Si lo quitas: Reproducir un modelo significaría recordar un orden de scripts no documentado y re-ejecutar todo desde cero cada vez — lento, propenso a errores e imposible de delegar limpiamente.