mpg-highway-api / README.md
aephidayatuloh
fix: add YAML frontmatter to README for Hugging Face configuration
ddd42b4
metadata
title: MPG Highway API
emoji: πŸš€
colorFrom: green
colorTo: blue
sdk: docker
app_port: 7860

πŸš€ MPG Highway API: R MLOps dengan Vetiver, Pins, dan Plumber

REST API menggunakan R Plumber untuk prediksi efisiensi bahan bakar.

Sistem deployment dan monitoring untuk model machine learning R menggunakan tidymodels, vetiver, pins, dan plumber. Proyek ini adalah sistem monitoring performa model Machine Learning (menggunakan dataset mpg untuk memprediksi efisiensi bahan bakar) secara real-time. Proyek ini mencakup API inferensi, pencatatan log data batch ke database, serta visualisasi data drift dan performa menggunakan Shiny dan echarts4r.

βœ… Status Proyek: Migrasi database ke PostgreSQL (Supabase) Selesai & Stabil.

πŸ“‹ Fitur Utama

  • βœ… Model Versioning: Menggunakan Vetiver dan Pins untuk manajemen siklus hidup model.
  • βœ… Cloud Logging: Pencatatan log prediksi otomatis ke Supabase Cloud.
  • βœ… Actuals Ingestion: Sinkronisasi data aktual lapangan dengan log prediksi menggunakan Composite Primary Key.
  • βœ… Secure Credentials: Manajemen akses database yang aman menggunakan file .env dan paket dotenv.
  • βœ… Monitoring Dashboard: Shiny dashboard yang menarik data secara real-time langsung dari PostgreSQL.
  • βœ… PostgreSQL Optimized: Implementasi kueri yang dioptimalkan untuk dialek Postgres (ON CONFLICT, $1 placeholders).

πŸ› οΈ Requirements

Proyek ini menggunakan renv untuk manajemen package. Sebelum memulai, pastikan Anda telah merestorasi environment dengan membuka R di folder ini dan menjalankan:

renv::restore()

πŸ“ Struktur File

.
β”œβ”€β”€ 01_train_and_version.R      # Training dan versioning model
β”œβ”€β”€ 02_api_plumber.R            # API dengan monitoring & DB Logging
β”œβ”€β”€ 03_run_server.R             # Jalankan API Server local
β”œβ”€β”€ 04_test_client.R            # Test API Server local
β”œβ”€β”€ 05_monitoring_dashboard.R   # Shiny dashboard monitoring
β”œβ”€β”€ 07_ab_testing.R             # Review champion dan challenger model
β”œβ”€β”€ 08_ingest_actuals.R         # Script untuk memasukkan data aktual
β”œβ”€β”€ simulasi_prediksi.R         # Script untuk memasukkan log data prediksi
β”œβ”€β”€ Makefile                    # Otomatisasi perintah terminal
β”œβ”€β”€ Dockerfile                  # Docker image
β”œβ”€β”€ docker-compose.yml          # Docker orchestration
└── models/                     # Model storage & train stats

πŸ—„οΈ Skema Database (Supabase SQL)

Jalankan kueri ini di SQL Editor Supabase Anda untuk menyiapkan tabel di skema mlops:

CREATE SCHEMA IF NOT EXISTS mlops;

-- Tabel untuk Log Prediksi
CREATE TABLE mlops.predictions (
    request_id TEXT,
    row_id TEXT,
    variant TEXT,
    version TEXT,
    input_features JSONB,
    predicted_value NUMERIC,
    status TEXT,
    error_message TEXT,
    timestamp TIMESTAMPTZ DEFAULT NOW(),
    PRIMARY KEY (request_id, row_id)
);

-- Tabel untuk Data Aktual
CREATE TABLE mlops.actuals (
    request_id TEXT,
    row_id TEXT,
    actual_value NUMERIC,
    logged_at TIMESTAMPTZ DEFAULT NOW(),
    PRIMARY KEY (request_id, row_id)
);

⚠️ Catatan Penting Implementasi

1. Dialek PostgreSQL vs SQLite

Jangan gunakan sintaks SQLite seperti INSERT OR IGNORE. Proyek ini menggunakan standar PostgreSQL:

  • Placeholder: Menggunakan $1, $2, ... (bukan ?).
  • Conflict Handling: Menggunakan ON CONFLICT (request_id, row_id) DO NOTHING.

2. Database Connection

Koneksi database dipusatkan di 00_db_helper.R. Selalu gunakan DBI::dbExecute(con, "SET search_path TO mlops") setelah membuka koneksi untuk memastikan kueri mengarah ke skema yang benar.

3. Permission

Jika terjadi error Permission Denied, pastikan user database memiliki hak akses penuh ke skema mlops:

GRANT USAGE ON SCHEMA mlops TO authenticated;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA mlops TO authenticated;

πŸš€ Quick Start

1. Training dan Versioning Model

source("01_train_and_version.R")

Output:

  • Model tersimpan di models/ dengan versioning
  • Training statistics untuk drift detection
  • Metadata model (RMSE, RΒ², dll)

2. Jalankan API Server

source("03_run_server.R")

API akan berjalan di http://localhost:8000 dengan endpoints:

  • GET /ping - Health check
  • GET /metadata - Model metadata
  • GET /prototype - Akses data prototype
  • POST /predict - Standard prediction
  • POST /predict_custom - Custom post-prediction

3. Test API

source("04_test_client.R")

4. Jalankan Monitoring Dashboard

shiny::runApp("05_monitoring_dashboard.R")

Dashboard akan berjalan di http://localhost:7667

πŸ”Œ API Usage

Custom Batch Prediction (Mengirim JSON Fitur & Auto-Log)

curl -X POST "http://localhost:8000/predict_custom" \
  -H "Content-Type: application/json" \
  -d '[
      {
        "row_id": "client_id_1",
        "displ": 2.4,
        "year": 2008,
        "cyl": 4,
        "class": "compact"
      },
      {"row_id":"client_id_A", "displ":2.0,"year":2008,"cyl":4,"class":"compact"},
      {"row_id":"client_id_B", "displ":5.0,"year":2008,"cyl":8,"class":"suv"}
    ]'

Response:

{
  "request_id": "REQ-260403101334-2334",
  "results": [
    {
      "row_id": "client_id_1",
      "prediction": 29.3853
    },
    {
      "row_id": "client_id_A",
      "prediction": 29.1273
    },
    {
      "row_id": "client_id_B",
      "prediction": 17.4506
    }
  ]
}

πŸ“Š Monitoring Dashboard

Dashboard menyediakan:

  1. Performance Tab

    • Trend prediksi over time
    • Distribusi prediksi
    • Performance metrics
  2. Data Drift Tab

    • Z-score chart untuk setiap feature
    • Drift detection table
    • Alert jika ada drift
  3. Prediction Logs Tab

    • Tabel semua prediksi
    • Filter dan search
  4. Feature Distributions Tab

    • Perbandingan distribusi training vs production
    • Untuk setiap feature

🐳 Docker Deployment

Build dan Run

# Build image
docker build -t ml-api .

# Run container
docker run -p 8000:8000 -v $(pwd)/models:/app/models -v $(pwd)/logs:/app/logs ml-api

Menggunakan Docker Compose

docker-compose up -d

Untuk stop:

docker-compose down

πŸ” Drift Detection

Sistem mendeteksi drift dengan cara:

  1. Statistical Drift: Membandingkan distribusi feature production vs training menggunakan Z-score
  2. Threshold: Z-score > 2 dianggap sebagai drift
  3. Features Monitored: Semua numerical features (displ, year, cyl)

Formula Z-score:

z = |recent_mean - train_mean| / train_sd

πŸ“ˆ Performance Monitoring

Metrics yang dimonitor:

  • Prediction Statistics: mean, median, SD, min, max
  • Input Statistics: distribusi input features
  • Volume: jumlah prediksi per window
  • Trend: perubahan prediksi over time

πŸ”„ Model Update Workflow

  1. Train model baru dengan data terbaru
  2. Version model dengan vetiver
  3. Pin model baru ke board
  4. Restart API server
  5. API otomatis load model versi terbaru
  6. Monitor performance dan drift
# Update model
source("01_train_and_version.R")

# Restart API (model akan auto-load versi terbaru)
source("03_run_server.R")

🎯 Best Practices

1. Monitoring Window Size

  • Small window (50-100): Deteksi drift lebih cepat, tapi lebih sensitif
  • Large window (500-1000): Lebih stabil, tapi deteksi drift lebih lambat

2. Drift Threshold

  • Threshold = 2: Standard (95% confidence)
  • Threshold = 3: Lebih konservatif (99.7% confidence)

3. Log Management

Pembersihan Log Berkala (Pindah ke SQLite) Karena data log saat ini disimpan di SQLite, Anda dapat membersihkan log lama (misal menyisakan 10.000 data terakhir) dengan kueri SQL berikut agar database tidak terlalu bengkak:

con <- DBI::dbConnect(RSQLite::SQLite(), "logs/model_monitoring.sqlite")
DBI::dbExecute(con, "
  DELETE FROM predictions 
  WHERE id NOT IN (SELECT id FROM predictions ORDER BY timestamp DESC LIMIT 10000)
")
DBI::dbDisconnect(con)

4. Model Retraining Triggers

Retrain model jika:

  • Drift terdeteksi pada multiple features
  • Performance degradation signifikan
  • Data pattern berubah
  • Scheduled retraining (e.g., monthly)

πŸ” Production Considerations

1. Authentication

Tambahkan authentication di Plumber:

#* @filter auth
function(req, res) {
  if (is.null(req$HTTP_AUTHORIZATION)) {
    res$status <- 401
    return(list(error = "Unauthorized"))
  }
  plumber::forward()
}

2. Rate Limiting

#* @filter ratelimit
function(req, res) {
  # Implement rate limiting logic
  plumber::forward()
}

3. Logging

Gunakan logger package:

library(logger)
log_info("Prediction request from {req$REMOTE_ADDR}")

4. Error Handling

tryCatch({
  prediction <- predict(v, input_data)
}, error = function(e) {
  log_error("Prediction error: {e$message}")
  return(list(error = "Prediction failed"))
})

πŸ“ Advanced Pins Boards

S3 Board (AWS)

library(pins)
board <- board_s3(
  bucket = "my-models",
  region = "us-east-1"
)

vetiver_pin_write(board, v)

RStudio Connect

board <- board_connect(
  server = "https://connect.example.com",
  key = Sys.getenv("CONNECT_API_KEY")
)

vetiver_pin_write(board, v)

Azure Blob

board <- board_azure(
  container = "models",
  account = "myaccount",
  key = Sys.getenv("AZURE_STORAGE_KEY")
)

πŸ› Troubleshooting

Model tidak load

# Check pins board
board <- board_folder("models")
pin_list(board)
pin_versions(board, "mpg_highway_model")

# Read model manually
v <- vetiver_pin_read(board, "mpg_highway_model")

API error 500

# Check logs
source("02_api_plumber.R")
api %>% 
  pr_run(
    host = "0.0.0.0", 
    port = 8000, 
    debug = TRUE)  # Enable debug mode

Drift false positives

# Adjust threshold
threshold <- 3  # More conservative

# Or increase window size
window <- 500

πŸ“š Resources

🀝 Contributing

Silakan customize sesuai kebutuhan project Anda:

  1. Ganti dataset dengan data Anda
  2. Modifikasi recipe dan model sesuai use case
  3. Tambahkan metrics monitoring spesifik
  4. Implementasi alerting (email, Slack, dll)

πŸ“„ License

MIT License - feel free to use and modify