SGSoundGuard

Pipeline CT/CD

Continuous Training y Continuous Deployment con Step Functions, evaluación cross-fold y gate de promoción.

Por qué CT/CD aquí es distinto

SoundGuard no usa un modelo entrenable tradicional (no hay gradient descent, no hay epochs, no hay loss curves). El "modelo" es la combinación de tres elementos:

  1. Embeddings: vectores de 1024 dims generados por Bedrock Nova.
  2. Índice vectorial: S3 Vectors con embeddings + metadata.
  3. Clasificador: k-NN con un valor fijo de k.

Reentrenar = reconstruir el índice con nuevos datos o distintos parámetros. Desplegar = promover un índice validado a producción.

Esto no es menos válido que un pipeline de deep learning. Es un patrón distinto: instance-based learning con model store vectorial.

State machine

S3 Trigger (nuevo clip en soundguard-dev-audio)


[Step Functions: soundguard-dev-ct-pipeline]

    ├─[Lambda: detect_new_clips]      ¿qué falta en el índice?

    ├─[Lambda: embed_batch]           Bedrock Nova → S3 Vectors staging

    ├─[Lambda: evaluate_and_decide]   evaluación cross-fold contra producción

    └─[Choice: accuracy ≥ 0.90?]
        ├── YES ─▶ promote_index.py  swap atómico de alias
        └── NO  ─▶ SNS alert + rollback staging

Lambdas del pipeline

LambdaResponsabilidadCódigo
detect_new_clipsLista S3, hace diff contra el índice y devuelve los faltantes.src/pipeline/detect_new_clips.py
embed_batchPara cada clip, llama Bedrock y hace put_vectors en staging.src/pipeline/embed_batch.py
evaluate_and_decideCorre evaluación cross-fold y decide promover o rechazar.src/pipeline/evaluate_and_decide.py
promote_indexSwap atómico del alias soundguard-dev-production.src/pipeline/promote_index.py

CD Gate

El gate compara el índice de staging contra el índice de producción usando un fold distinto del que se entrenó:

EscenarioAccuracyF1 macroDecisión
LOO: fold 2 vs fold 289.00%0.8888REJECT (evaluación interna del fold)
Cross-fold: fold 2 vs fold 190.00%0.8949PASS (queries staging vs índice prod)

Cambio clave: el CD gate usa evaluación cross-fold (evaluate_cross_fold.py) en lugar de LOO. Esto simula el escenario real: "¿cómo se clasifican los nuevos clips usando el índice de producción existente?"

Por qué funciona: el índice de producción (fold 1) tiene 400 vectores diversos que representan bien las clases. Los clips de fold 2 encuentran mejores vecinos allí que entre ellos mismos.

Promoción atómica

def promote(staging_index: str, production_alias: str, threshold: float = 0.90):
    metrics = evaluate(staging_index)
    if metrics["accuracy"] >= threshold:
        swap_alias(production_alias, staging_index)
        log_to_mlflow(metrics, stage="production")
        return "promoted"
    log_to_mlflow(metrics, stage="rejected")
    return "rejected"

El swap del alias es atómico (cambia el env var VECTOR_INDEX de la Lambda). No hay redeploy. Rollback toma segundos: revertir el env var.

Experiment tracking con MLflow

TipoValor
Hyperparameterk11
Datasetfold 1, fold 2, fold 1+2
Embedding configdim=1024, purpose=CLASSIFICATION
Métricasaccuracy, F1 macro, F1 weighted, F1 por clase
Artefactosconfusion_matrix.png, metrics.json

Comando local:

for k in 1 3 5 7 9 11; do
  python -m src.evaluation.evaluate_knn --fold 1 --k "$k" --mlflow
done
mlflow ui --backend-store-uri "file://$(pwd)/mlruns"

Decisiones arquitectónicas

PreguntaDecisiónPor qué
MLflow o W&BMLflowOpen-source, corre local, sin signup.
Step Functions o Airflow/PrefectStep FunctionsNativo AWS, sin scheduler propio.
Sobrescribir índice o stagingStagingA/B testing y rollback instantáneo.
Apuntar Lambda a nombre fijo o aliasAliasSwap atómico, sin redeploy.

Riesgos conocidos

RiesgoMitigación
Throttling de Bedrock en pipeline paraleloRate limit en embed_batch, batch size pequeño, retries con backoff.
Costo del grid search~$12 USD por 11 corridas × 400 clips. Cubierto por créditos académicos.
Step Functions sin trigger nativo de S3S3 Event Notification → Lambda → StartExecution API.

On this page