--- 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: ```r 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: ```sql 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: ```SQL 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 ```r 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 ```r 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 ```r source("04_test_client.R") ``` ### 4. Jalankan Monitoring Dashboard ```r shiny::runApp("05_monitoring_dashboard.R") ``` Dashboard akan berjalan di `http://localhost:7667` ## 🔌 API Usage Custom Batch Prediction (Mengirim JSON Fitur & Auto-Log) ```bash 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: ```json { "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 ```bash # 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 ```bash docker-compose up -d ``` Untuk stop: ```bash 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 ```r # 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: ```r 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: ```r #* @filter auth function(req, res) { if (is.null(req$HTTP_AUTHORIZATION)) { res$status <- 401 return(list(error = "Unauthorized")) } plumber::forward() } ``` ### 2. Rate Limiting ```r #* @filter ratelimit function(req, res) { # Implement rate limiting logic plumber::forward() } ``` ### 3. Logging Gunakan logger package: ```r library(logger) log_info("Prediction request from {req$REMOTE_ADDR}") ``` ### 4. Error Handling ```r 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) ```r library(pins) board <- board_s3( bucket = "my-models", region = "us-east-1" ) vetiver_pin_write(board, v) ``` ### RStudio Connect ```r board <- board_connect( server = "https://connect.example.com", key = Sys.getenv("CONNECT_API_KEY") ) vetiver_pin_write(board, v) ``` ### Azure Blob ```r board <- board_azure( container = "models", account = "myaccount", key = Sys.getenv("AZURE_STORAGE_KEY") ) ``` ## 🐛 Troubleshooting ### Model tidak load ```r # 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 ```r # 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 ```r # Adjust threshold threshold <- 3 # More conservative # Or increase window size window <- 500 ``` ## 📚 Resources - [Vetiver Documentation](https://vetiver.rstudio.com/) - [Pins Documentation](https://pins.rstudio.com/) - [Plumber Documentation](https://www.rplumber.io/) - [Tidymodels](https://www.tidymodels.org/) ## 🤝 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