TrackChain / ml /scripts /run.sh
PRiyanshu0-1's picture
Add ml/ codebase and configs
2adb41a verified
Raw History Blame Contribute Delete
12.1 kB
#!/usr/bin/env bash
# =============================================================================
# TrackChain Master ML Training Orchestrator
# Trains all 5 Phase 2 models in dependency order with SMART checkpointing.
#
# Usage:
# chmod +x ml/scripts/run.sh
# ./ml/scripts/run.sh # Runs ONLY missing/failed steps
# ./ml/scripts/run.sh --force # Re-runs EVERYTHING (overwrites)
# ./ml/scripts/run.sh --clean # Wipes all checkpoints
# ./ml/scripts/run.sh --skip-yolo --skip-patchcore # Skips specific steps
#
# Output:
# - Trained weights in artifacts/checkpoints/
# - ONNX/INT8 exports in artifacts/exports/
# - Training logs in artifacts/logs/
# =============================================================================
set -euo pipefail
# --- Configuration -----------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
LOG_DIR="$REPO_ROOT/artifacts/logs"
CHECKPOINT_DIR="$REPO_ROOT/artifacts/checkpoints"
EXPORT_DIR="$REPO_ROOT/artifacts/exports"
DATA_ROOT="$REPO_ROOT/data"
export PYTHONPATH="$REPO_ROOT:${PYTHONPATH:-}"
EPOCHS_YOLO=${EPOCHS_YOLO:-50}
EPOCHS_BILSTM=${EPOCHS_BILSTM:-20}
EPOCHS_VAE=${EPOCHS_VAE:-30}
BATCH_SIZE=${BATCH_SIZE:-16}
# Flags
FORCE=false
CLEAN=false
SKIP_YOLO=false
SKIP_PATCHCORE=false
SKIP_BILSTM=false
SKIP_VAE=false
# Auto-detect CUDA GPU
if python -c "import torch; exit(0 if torch.cuda.is_available() else 1)" 2>/dev/null; then
DEFAULT_DEVICE="0"
DEVICE_INFO=$(python -c "import torch; print(f'CUDA GPU 0 ({torch.cuda.get_device_name(0)})')")
else
DEFAULT_DEVICE="cpu"
DEVICE_INFO="CPU (No CUDA device found)"
fi
TRAIN_DEVICE=${TRAIN_DEVICE:-"$DEFAULT_DEVICE"}
# --- Color output ------------------------------------------------------------
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; CYAN='\033[0;36m'; NC='\033[0m'
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
err() { echo -e "${RED}[ERROR]${NC} $*"; }
header(){ echo -e "\n${BLUE}═══════════════════════════════════════════════════${NC}";
echo -e "${BLUE} $*${NC}";
echo -e "${BLUE}═══════════════════════════════════════════════════${NC}"; }
# --- Argument parsing --------------------------------------------------------
while [[ $# -gt 0 ]]; do
case "$1" in
--epochs-yolo) EPOCHS_YOLO="$2"; shift 2;;
--epochs-bilstm) EPOCHS_BILSTM="$2"; shift 2;;
--epochs-vae) EPOCHS_VAE="$2"; shift 2;;
--batch) BATCH_SIZE="$2"; shift 2;;
--force) FORCE=true; shift;;
--clean) CLEAN=true; shift;;
--skip-yolo) SKIP_YOLO=true; shift;;
--skip-patchcore) SKIP_PATCHCORE=true; shift;;
--skip-bilstm) SKIP_BILSTM=true; shift;;
--skip-vae) SKIP_VAE=true; shift;;
*) err "Unknown option: $1"; exit 1;;
esac
done
# --- Setup & Clean Logic -----------------------------------------------------
cd "$REPO_ROOT"
mkdir -p "$LOG_DIR" "$CHECKPOINT_DIR/vision" "$CHECKPOINT_DIR/geometry" "$EXPORT_DIR"
if [[ "$CLEAN" == true ]]; then
warn "Cleaning all checkpoints, exports, and logs..."
find "$CHECKPOINT_DIR" -name "*.done" -type f -delete
find "$EXPORT_DIR" -name "*.done" -type f -delete
ok "Clean complete. Re-run without --clean to start training."
exit 0
fi
START_TIME=$(date +%s)
header "TrackChain Phase 2 — Master ML Training Pipeline"
info "Repo root: $REPO_ROOT"
info "Compute Device: $DEVICE_INFO"
info "YOLO epochs: $EPOCHS_YOLO"
info "Bi-LSTM epochs: $EPOCHS_BILSTM"
info "VAE epochs: $EPOCHS_VAE"
info "Batch size: $BATCH_SIZE"
info "Force retrain: $FORCE"
# --- Helper: checkpoint-aware run --------------------------------------------
run_step() {
local step_name="$1"
local checkpoint="$2"
shift 2
local cmd=("$@")
# SMART CHECKPOINTING: Skip if .done file exists and --force is not used
if [[ "$FORCE" == false && -f "$checkpoint" ]]; then
info "[$step_name] Already completed (checkpoint found). Skipping."
return 0
fi
info "[$step_name] Starting..."
# Temporarily disable exit-on-error to capture the exact exit code of the piped command
set +e
"${cmd[@]}" 2>&1 | tee "$LOG_DIR/${step_name}.log"
local status=${PIPESTATUS[0]}
set -e
if [[ $status -eq 0 ]]; then
touch "$checkpoint"
ok "[$step_name] Completed successfully"
else
err "[$step_name] FAILED (exit code $status). See log: $LOG_DIR/${step_name}.log"
exit 1
fi
}
# =============================================================================
# STEP 1: Data Verification & Generation
# =============================================================================
header "STEP 1/7: Data Verification & Generation"
if ! python -c "import os
has_yolo = os.path.exists('$DATA_ROOT/external/rail_defects_expanded/train/images') or os.path.exists('$DATA_ROOT/external/rail_defects/train/images')
has_patch = os.path.exists('$DATA_ROOT/external/rail_normal_expanded/train/good') or os.path.exists('$DATA_ROOT/external/rail_normal_only/train/good')
assert has_yolo, 'YOLO dataset missing'
assert has_patch, 'PatchCore dataset missing'
print('[OK] Vision datasets verified')" 2>/dev/null; then
err "Vision datasets not found. Ensure '$DATA_ROOT/external/rail_defects' and '$DATA_ROOT/external/rail_normal_only' exist."
exit 1
fi
run_step "generate_trc" \
"$CHECKPOINT_DIR/.trc_data.done" \
python ml/scripts/generate_trc_telemetry.py \
--mode telemetry \
--output "$DATA_ROOT/processed/synthetic_trc_run_001.csv"
run_step "generate_geometry" \
"$CHECKPOINT_DIR/.geometry_data.done" \
python ml/scripts/generate_trc_telemetry.py \
--mode geometry_sequences --num_samples 5000 \
--output "$DATA_ROOT/processed/geometry_sequences/"
run_step "generate_normal" \
"$CHECKPOINT_DIR/.normal_data.done" \
python ml/scripts/generate_trc_telemetry.py \
--mode normal_sequences --num_samples 3000 \
--output "$DATA_ROOT/processed/normal_sequences/"
# =============================================================================
# STEP 2: YOLO Training (Phase 2.1)
# =============================================================================
header "STEP 2/7: YOLOv8n Visual Defect Detector"
YOLO_DATA="$DATA_ROOT/external/rail_defects/data.yaml"
if [[ -f "$DATA_ROOT/external/rail_defects_expanded/data.yaml" ]]; then
YOLO_DATA="$DATA_ROOT/external/rail_defects_expanded/data.yaml"
fi
if [[ "$SKIP_YOLO" == true ]]; then
warn "Skipping YOLO (--skip-yolo)"
else
run_step "train_yolo" \
"$CHECKPOINT_DIR/vision/.yolo_train.done" \
python ml/scripts/train_detector.py \
--data "$YOLO_DATA" \
--epochs "$EPOCHS_YOLO" \
--batch "$BATCH_SIZE" \
--device "$TRAIN_DEVICE"
run_step "export_yolo_onnx" \
"$EXPORT_DIR/.yolo_onnx.done" \
python ml/inference/exporters.py \
--model "$CHECKPOINT_DIR/vision/yolov8n_rail_best.pt" \
--outdir "$EXPORT_DIR" \
--format onnx
run_step "export_yolo_int8" \
"$EXPORT_DIR/.yolo_int8.done" \
python ml/inference/exporters.py \
--model "$CHECKPOINT_DIR/vision/yolov8n_rail_best.pt" \
--outdir "$EXPORT_DIR" \
--format int8
fi
# =============================================================================
# STEP 3: PatchCore Memory Bank (Phase 2.2)
# =============================================================================
header "STEP 3/7: PatchCore Visual Anomaly Detector"
PATCHCORE_DATA="$DATA_ROOT/external/rail_normal_only"
if [[ -d "$DATA_ROOT/external/rail_normal_expanded/train/good" ]]; then
PATCHCORE_DATA="$DATA_ROOT/external/rail_normal_expanded"
info "Using expanded PatchCore dataset (800+ images)"
fi
if [[ "$SKIP_PATCHCORE" == true ]]; then
warn "Skipping PatchCore (--skip-patchcore)"
else
run_step "train_patchcore" \
"$CHECKPOINT_DIR/vision/.patchcore_train.done" \
python ml/scripts/train_anomaly.py \
--data-path "$PATCHCORE_DATA" \
--coreset_ratio 0.10 \
--fpr_target 0.01 \
--device "$TRAIN_DEVICE"
fi
# =============================================================================
# STEP 4: Physics Verification (Phase 2.3) — no training
# =============================================================================
header "STEP 4/7: EN 13848 Physics Verification"
if [[ "$FORCE" == false && -f "$CHECKPOINT_DIR/geometry/.physics_verify.done" ]]; then
info "[physics_verify] Already completed. Skipping."
else
python -c "
import pandas as pd
from ml.models.geometry.physics_detector import EN13848PhysicsThresholdDetector
det = EN13848PhysicsThresholdDetector()
df = pd.read_csv('$DATA_ROOT/processed/synthetic_trc_run_001.csv')
signals = det.predict(df)
fired = signals[0].fired
score = signals[0].value
print(f'Physics fired: {fired}, score: {score:.3f}')
assert fired and abs(score - 0.625) < 0.05, 'Physics math failed!'
"
touch "$CHECKPOINT_DIR/geometry/.physics_verify.done"
ok "[physics_verify] EN 13848 math verified"
fi
# =============================================================================
# STEP 5: Bi-LSTM Training (Phase 2.4) - ENHANCED
# =============================================================================
header "STEP 5/7: Bi-LSTM Geometry Fault Classifier (Enhanced)"
if [[ "$SKIP_BILSTM" == true ]]; then
warn "Skipping Bi-LSTM (--skip-bilstm)"
else
run_step "train_bilstm" \
"$CHECKPOINT_DIR/geometry/.bilstm_train.done" \
python ml/scripts/train_fault_classifier_enhanced.py \
--epochs "$EPOCHS_BILSTM" \
--hidden-size 128 \
--num-layers 3 \
--batch_size 128 \
--lr 0.0005 \
--label-smoothing 0.1 \
--dropout 0.4 \
--data-path "$DATA_ROOT/processed/geometry_sequences/" \
--save-path "$CHECKPOINT_DIR/geometry/bilstm_fault_typing_enhanced.pt"
fi
# =============================================================================
# STEP 6: Seq-VAE Training (Phase 2.5) - FIXED AND ENHANCED
# =============================================================================
header "STEP 6/7: Sequence VAE Novel Geometry Detector (Enhanced)"
if [[ "$SKIP_VAE" == true ]]; then
warn "Skipping Seq-VAE (--skip-vae)"
else
run_step "train_vae" \
"$CHECKPOINT_DIR/geometry/.vae_train.done" \
python ml/scripts/train_sequence_vae_enhanced.py \
--epochs "$EPOCHS_VAE" \
--beta 0.01 \
--latent-dim 16 \
--batch-size 64 \
--lr 0.001 \
--kl-annealing \
--annealing-epochs 10 \
--data-path "$DATA_ROOT/processed/normal_sequences/" \
--save-path "$CHECKPOINT_DIR/geometry/sequence_vae_enhanced.pt"
fi
# =============================================================================
# STEP 7: Final Test Suite
# =============================================================================
header "STEP 7/7: Full Test Suite Verification"
run_step "full_test_suite" \
"$CHECKPOINT_DIR/.tests.done" \
bash ml/scripts/test.sh
ELAPSED=$(( $(date +%s) - START_TIME ))
echo ""
header "TrackChain Phase 2 — Training Pipeline Complete in ${ELAPSED}s"
ok "Checkpoints: $CHECKPOINT_DIR/"
ok "Exports: $EXPORT_DIR/"
ok "Logs: $LOG_DIR/"
echo ""
ok "Next step: Run ./ml/scripts/calibrate.sh to fit calibration parameters."