Spaces:
Sleeping
Sleeping
Initial commit: end-to-end MLOps pipeline project
Browse filesIncludes Dagster orchestration, MLflow tracking, Evidently monitoring,
FastAPI serving, and Gradio frontend. Adds .gitignore for Python/MLOps
artifacts (venv, mlruns, model binaries, generated reports).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- .gitignore +58 -0
- .python-version +1 -0
- README.md +182 -0
- app.py +162 -0
- data/raw/Computer_Durability.csv +1001 -0
- data/raw/Computer_Durability_Plus.csv +0 -0
- models/best_model_info.json +22 -0
- models/feature_meta.json +12 -0
- models/optuna_trials.csv +31 -0
- pyproject.toml +47 -0
- scripts/run_pipeline.py +70 -0
- scripts/synthesize_data.py +140 -0
- serving/__init__.py +0 -0
- serving/api.py +152 -0
- src/__init__.py +0 -0
- src/mlops_pipeline/__init__.py +0 -0
- src/mlops_pipeline/assets/__init__.py +0 -0
- src/mlops_pipeline/assets/data_assets.py +120 -0
- src/mlops_pipeline/assets/evaluation_assets.py +128 -0
- src/mlops_pipeline/assets/training_assets.py +232 -0
- src/mlops_pipeline/config.py +30 -0
- src/mlops_pipeline/definitions.py +20 -0
- src/mlops_pipeline/resources.py +19 -0
- uv.lock +0 -0
.gitignore
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Python
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*.pyo
|
| 5 |
+
*.pyd
|
| 6 |
+
*.so
|
| 7 |
+
*.egg
|
| 8 |
+
*.egg-info/
|
| 9 |
+
dist/
|
| 10 |
+
build/
|
| 11 |
+
*.whl
|
| 12 |
+
|
| 13 |
+
# Virtual environment (uv)
|
| 14 |
+
.venv/
|
| 15 |
+
venv/
|
| 16 |
+
env/
|
| 17 |
+
|
| 18 |
+
# Environment variables
|
| 19 |
+
.env
|
| 20 |
+
.env.*
|
| 21 |
+
!.env.example
|
| 22 |
+
|
| 23 |
+
# Jupyter
|
| 24 |
+
.ipynb_checkpoints/
|
| 25 |
+
*.ipynb~
|
| 26 |
+
|
| 27 |
+
# MLflow experiment tracking
|
| 28 |
+
mlruns/
|
| 29 |
+
mlartifacts/
|
| 30 |
+
|
| 31 |
+
# Trained model artifacts (track metadata/JSON, not binaries)
|
| 32 |
+
models/*.joblib
|
| 33 |
+
models/*.pkl
|
| 34 |
+
models/*.h5
|
| 35 |
+
models/*.pt
|
| 36 |
+
models/*.onnx
|
| 37 |
+
|
| 38 |
+
# Generated reports (Evidently)
|
| 39 |
+
reports/
|
| 40 |
+
|
| 41 |
+
# Dagster storage
|
| 42 |
+
storage/
|
| 43 |
+
$DAGSTER_HOME/
|
| 44 |
+
.dagster/
|
| 45 |
+
|
| 46 |
+
# Data files at root level only (duplicates of data/ directory)
|
| 47 |
+
/Computer_Durability.csv
|
| 48 |
+
/Computer_Durability_Plus.csv
|
| 49 |
+
|
| 50 |
+
# OS
|
| 51 |
+
.DS_Store
|
| 52 |
+
Thumbs.db
|
| 53 |
+
|
| 54 |
+
# IDE
|
| 55 |
+
.vscode/
|
| 56 |
+
.idea/
|
| 57 |
+
*.swp
|
| 58 |
+
*.swo
|
.python-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
3.12
|
README.md
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
---
|
| 2 |
title: MLOps End2end MSDS2026
|
| 3 |
emoji: π
|
|
@@ -11,3 +12,184 @@ pinned: false
|
|
| 11 |
---
|
| 12 |
|
| 13 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<<<<<<< HEAD
|
| 2 |
---
|
| 3 |
title: MLOps End2end MSDS2026
|
| 4 |
emoji: π
|
|
|
|
| 12 |
---
|
| 13 |
|
| 14 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
| 15 |
+
=======
|
| 16 |
+
# MLOps End-to-End Pipeline β Computer Durability Classifier
|
| 17 |
+
|
| 18 |
+
Predicts whether a computer **needs replacement** using a full MLOps stack:
|
| 19 |
+
|
| 20 |
+
| Component | Role |
|
| 21 |
+
|---|---|
|
| 22 |
+
| **Dagster** | Workflow orchestration (asset-based DAG) |
|
| 23 |
+
| **MLflow** | Experiment tracking + Model Registry |
|
| 24 |
+
| **Evidently** | Data drift detection + quality monitoring |
|
| 25 |
+
| **FastAPI** | REST prediction endpoint |
|
| 26 |
+
| **Gradio** | Interactive demo UI (HF Spacesβready) |
|
| 27 |
+
|
| 28 |
+
---
|
| 29 |
+
|
| 30 |
+
## Dataset
|
| 31 |
+
|
| 32 |
+
| File | Rows | Description |
|
| 33 |
+
|---|---|---|
|
| 34 |
+
| `Computer_Durability.csv` | 999 | Original dataset |
|
| 35 |
+
| `Computer_Durability_Plus.csv` | 2,999 | Original + 2,000 synthetic rows with mild drift |
|
| 36 |
+
|
| 37 |
+
**Features:** Hours Used Per Day Β· Cost Β· User Age Β· Primary Usage Β· Brand Β· Computer Age (Months)
|
| 38 |
+
**Target:** `Needs Replacement` (binary, ~5β6% positive)
|
| 39 |
+
|
| 40 |
+
**Drift injected:** synthetic cohort has +2h/day average usage and β$3k average cost β detectable by Evidently's drift report.
|
| 41 |
+
|
| 42 |
+
---
|
| 43 |
+
|
| 44 |
+
## Setup
|
| 45 |
+
|
| 46 |
+
```bash
|
| 47 |
+
# 1. Create venv with Python 3.12 and install all dependencies
|
| 48 |
+
uv venv --python 3.12
|
| 49 |
+
uv pip install -e ".[dev]"
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
---
|
| 53 |
+
|
| 54 |
+
## Running the Pipeline
|
| 55 |
+
|
| 56 |
+
### Option A β Programmatic (script)
|
| 57 |
+
|
| 58 |
+
```bash
|
| 59 |
+
uv run python scripts/run_pipeline.py
|
| 60 |
+
```
|
| 61 |
+
|
| 62 |
+
Runs all 9 Dagster assets in order. Produces:
|
| 63 |
+
- `models/` β trained RF + XGBoost models, scaler, Optuna trial CSV
|
| 64 |
+
- `reports/` β Evidently HTML reports (data quality, drift, classification)
|
| 65 |
+
- `mlruns/` β MLflow experiment + model registry
|
| 66 |
+
|
| 67 |
+
### Option B β Dagster Web UI
|
| 68 |
+
|
| 69 |
+
```bash
|
| 70 |
+
uv run dagster dev
|
| 71 |
+
# Opens http://localhost:3000
|
| 72 |
+
# Navigate to Assets β Materialize All
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
### Data synthesis only
|
| 76 |
+
|
| 77 |
+
```bash
|
| 78 |
+
uv run python scripts/synthesize_data.py
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
---
|
| 82 |
+
|
| 83 |
+
## Model Serving
|
| 84 |
+
|
| 85 |
+
### Start FastAPI
|
| 86 |
+
|
| 87 |
+
```bash
|
| 88 |
+
uv run uvicorn serving.api:app --host 0.0.0.0 --port 8000 --reload
|
| 89 |
+
```
|
| 90 |
+
|
| 91 |
+
Endpoints:
|
| 92 |
+
- `GET /health` β liveness check
|
| 93 |
+
- `GET /info` β model version + metrics
|
| 94 |
+
- `POST /predict` β single prediction
|
| 95 |
+
- `POST /predict/batch` β batch predictions
|
| 96 |
+
|
| 97 |
+
Example:
|
| 98 |
+
```bash
|
| 99 |
+
curl -X POST http://localhost:8000/predict \
|
| 100 |
+
-H "Content-Type: application/json" \
|
| 101 |
+
-d '{
|
| 102 |
+
"hours_used_per_day": 20.0,
|
| 103 |
+
"cost": 12000,
|
| 104 |
+
"user_age": 50,
|
| 105 |
+
"primary_usage": 1,
|
| 106 |
+
"brand": 2,
|
| 107 |
+
"computer_age_months": 48
|
| 108 |
+
}'
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
### Start Gradio Demo
|
| 112 |
+
|
| 113 |
+
```bash
|
| 114 |
+
# In a second terminal (API must be running on port 8000)
|
| 115 |
+
uv run python app.py
|
| 116 |
+
# Opens http://localhost:7860
|
| 117 |
+
```
|
| 118 |
+
|
| 119 |
+
### MLflow UI
|
| 120 |
+
|
| 121 |
+
```bash
|
| 122 |
+
uv run mlflow ui --backend-store-uri mlruns/
|
| 123 |
+
# Opens http://localhost:5000
|
| 124 |
+
```
|
| 125 |
+
|
| 126 |
+
---
|
| 127 |
+
|
| 128 |
+
## Project Structure
|
| 129 |
+
|
| 130 |
+
```
|
| 131 |
+
βββ Computer_Durability.csv # Original 999-row dataset
|
| 132 |
+
βββ Computer_Durability_Plus.csv # Augmented 2,999-row dataset (generated)
|
| 133 |
+
βββ app.py # Gradio frontend (HF Spacesβready)
|
| 134 |
+
βββ pyproject.toml # uv/pip project + dependencies
|
| 135 |
+
βββ .python-version # Python 3.12
|
| 136 |
+
β
|
| 137 |
+
βββ src/mlops_pipeline/
|
| 138 |
+
β βββ config.py # Paths, column names, MLflow settings
|
| 139 |
+
β βββ resources.py # Dagster MLflow resource
|
| 140 |
+
β βββ definitions.py # Dagster Definitions (asset wiring)
|
| 141 |
+
β βββ assets/
|
| 142 |
+
β βββ data_assets.py # raw_data, augmented_data, train_test_split
|
| 143 |
+
β βββ training_assets.py # baseline_rf_model, tuned_xgb_model, best_model_info
|
| 144 |
+
β βββ evaluation_assets.py # data_quality_report, data_drift_report, model_eval_report
|
| 145 |
+
β
|
| 146 |
+
βββ serving/
|
| 147 |
+
β βββ api.py # FastAPI prediction server
|
| 148 |
+
β
|
| 149 |
+
βββ scripts/
|
| 150 |
+
β βββ synthesize_data.py # Generates Computer_Durability_Plus.csv
|
| 151 |
+
β βββ run_pipeline.py # Runs all Dagster assets programmatically
|
| 152 |
+
β
|
| 153 |
+
βββ models/ # Trained model artifacts (generated)
|
| 154 |
+
βββ reports/ # Evidently HTML reports (generated)
|
| 155 |
+
βββ mlruns/ # MLflow tracking store (generated)
|
| 156 |
+
βββ data/raw/ # Raw CSV copies
|
| 157 |
+
```
|
| 158 |
+
|
| 159 |
+
---
|
| 160 |
+
|
| 161 |
+
## Pipeline Asset DAG
|
| 162 |
+
|
| 163 |
+
```
|
| 164 |
+
raw_data βββββββββββββββββββββββββββββββββββββββββββ
|
| 165 |
+
β
|
| 166 |
+
augmented_data βββΊ train_test_split_asset βββΊ baseline_rf_model βββΊ best_model_info βββΊ model_eval_report
|
| 167 |
+
β β
|
| 168 |
+
ββββββββββββββββββββΊ tuned_xgb_model βββββββββββββββ
|
| 169 |
+
β
|
| 170 |
+
augmented_data βββββββββββββββββββββββββββββββββββββββββββββββΊ data_quality_report
|
| 171 |
+
data_drift_report
|
| 172 |
+
```
|
| 173 |
+
|
| 174 |
+
---
|
| 175 |
+
|
| 176 |
+
## Results (actual run)
|
| 177 |
+
|
| 178 |
+
| Model | ROC-AUC | Avg Precision | F1 |
|
| 179 |
+
|---|---|---|---|
|
| 180 |
+
| RandomForest (baseline) | 0.827 | 0.246 | 0.314 |
|
| 181 |
+
| **XGBoost + Optuna (winner)** | **0.841** | **0.289** | **0.376** |
|
| 182 |
+
|
| 183 |
+
**Champion model:** `ComputerDurabilityClassifier v1 @champion` in MLflow Registry
|
| 184 |
+
|
| 185 |
+
High Avg Precision (~0.29) on a 6% positive-rate dataset is meaningful β random baseline would score 0.06.
|
| 186 |
+
|
| 187 |
+
---
|
| 188 |
+
|
| 189 |
+
## HF Spaces Deployment
|
| 190 |
+
|
| 191 |
+
The `app.py` at the project root is already structured for Hugging Face Spaces:
|
| 192 |
+
1. Push the repo to HF
|
| 193 |
+
2. Set `HF_API_URL` as a Space secret pointing to your deployed FastAPI instance
|
| 194 |
+
3. Spaces will auto-launch `app.py`
|
| 195 |
+
>>>>>>> c1e8f69 (Initial commit: end-to-end MLOps pipeline project)
|
app.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Gradio demo app β HF Spaces compatible (place this file at repo root).
|
| 3 |
+
|
| 4 |
+
In local mode it hits the FastAPI server at API_URL.
|
| 5 |
+
For HF Spaces, set the HF_API_URL secret or update API_URL to your deployed endpoint.
|
| 6 |
+
|
| 7 |
+
Start locally:
|
| 8 |
+
# Terminal 1: uv run uvicorn serving.api:app --port 8000
|
| 9 |
+
# Terminal 2: uv run python app.py
|
| 10 |
+
"""
|
| 11 |
+
from __future__ import annotations
|
| 12 |
+
|
| 13 |
+
import os
|
| 14 |
+
|
| 15 |
+
import gradio as gr
|
| 16 |
+
import requests
|
| 17 |
+
|
| 18 |
+
API_URL = os.getenv("HF_API_URL", "http://localhost:8000")
|
| 19 |
+
|
| 20 |
+
PRIMARY_USAGE_MAP = {
|
| 21 |
+
"Work / Productivity (1)": 1,
|
| 22 |
+
"Gaming (2)": 2,
|
| 23 |
+
"Study / Education (3)": 3,
|
| 24 |
+
"Personal / Entertainment (4)": 4,
|
| 25 |
+
}
|
| 26 |
+
BRAND_MAP = {
|
| 27 |
+
"Brand A (1)": 1,
|
| 28 |
+
"Brand B (2)": 2,
|
| 29 |
+
"Brand C (3)": 3,
|
| 30 |
+
"Brand D (4)": 4,
|
| 31 |
+
"Brand E (5)": 5,
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def predict(
|
| 36 |
+
hours_per_day: float,
|
| 37 |
+
cost: float,
|
| 38 |
+
user_age: float,
|
| 39 |
+
primary_usage: str,
|
| 40 |
+
brand: str,
|
| 41 |
+
computer_age_months: float,
|
| 42 |
+
) -> tuple[str, str]:
|
| 43 |
+
payload = {
|
| 44 |
+
"hours_used_per_day": hours_per_day,
|
| 45 |
+
"cost": cost,
|
| 46 |
+
"user_age": user_age,
|
| 47 |
+
"primary_usage": PRIMARY_USAGE_MAP[primary_usage],
|
| 48 |
+
"brand": BRAND_MAP[brand],
|
| 49 |
+
"computer_age_months": computer_age_months,
|
| 50 |
+
}
|
| 51 |
+
try:
|
| 52 |
+
resp = requests.post(f"{API_URL}/predict", json=payload, timeout=10)
|
| 53 |
+
resp.raise_for_status()
|
| 54 |
+
data = resp.json()
|
| 55 |
+
except requests.exceptions.ConnectionError:
|
| 56 |
+
return (
|
| 57 |
+
"β Cannot connect to the prediction API.",
|
| 58 |
+
f"Make sure the FastAPI server is running at {API_URL}\n\n"
|
| 59 |
+
"Run: uv run uvicorn serving.api:app --port 8000",
|
| 60 |
+
)
|
| 61 |
+
except Exception as exc:
|
| 62 |
+
return "β Error", str(exc)
|
| 63 |
+
|
| 64 |
+
prob = data["probability"]
|
| 65 |
+
label = data["needs_replacement"]
|
| 66 |
+
version = data.get("model_version", "N/A")
|
| 67 |
+
|
| 68 |
+
verdict = "π΄ **Replacement Recommended**" if label else "π’ **No Replacement Needed**"
|
| 69 |
+
details = (
|
| 70 |
+
f"**Replacement Probability:** {prob:.1%}\n\n"
|
| 71 |
+
f"**Decision Threshold:** 50%\n\n"
|
| 72 |
+
f"**Model:** {version}\n\n"
|
| 73 |
+
f"---\n"
|
| 74 |
+
f"*Inputs:* {hours_per_day:.1f}h/day Β· ${cost:,.0f} Β· "
|
| 75 |
+
f"{user_age:.0f}yo Β· {computer_age_months:.0f} months old"
|
| 76 |
+
)
|
| 77 |
+
return verdict, details
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def build_demo() -> gr.Blocks:
|
| 81 |
+
with gr.Blocks(
|
| 82 |
+
title="Computer Durability Predictor",
|
| 83 |
+
theme=gr.themes.Soft(),
|
| 84 |
+
) as demo:
|
| 85 |
+
gr.Markdown(
|
| 86 |
+
"""
|
| 87 |
+
# π» Computer Durability Predictor
|
| 88 |
+
**MLOps End-to-End Pipeline** Β· Dagster Β· MLflow Β· Evidently Β· FastAPI Β· Gradio
|
| 89 |
+
|
| 90 |
+
Predict whether a computer **needs replacement** based on its usage profile.
|
| 91 |
+
The model was trained on the Computer Durability dataset using
|
| 92 |
+
**XGBoost + Optuna** hyperparameter tuning and tracked with **MLflow**.
|
| 93 |
+
"""
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
with gr.Row():
|
| 97 |
+
with gr.Column(scale=1):
|
| 98 |
+
gr.Markdown("### Usage & Hardware")
|
| 99 |
+
hours = gr.Slider(
|
| 100 |
+
label="Hours Used Per Day",
|
| 101 |
+
minimum=1.0, maximum=24.0, step=0.5, value=10.0,
|
| 102 |
+
)
|
| 103 |
+
cost = gr.Slider(
|
| 104 |
+
label="Computer Cost (USD)",
|
| 105 |
+
minimum=5000, maximum=50000, step=500, value=30000,
|
| 106 |
+
)
|
| 107 |
+
comp_age = gr.Slider(
|
| 108 |
+
label="Computer Age (Months)",
|
| 109 |
+
minimum=1, maximum=60, step=1, value=24,
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
with gr.Column(scale=1):
|
| 113 |
+
gr.Markdown("### User Profile")
|
| 114 |
+
user_age = gr.Slider(
|
| 115 |
+
label="User Age (Years)",
|
| 116 |
+
minimum=8, maximum=65, step=1, value=35,
|
| 117 |
+
)
|
| 118 |
+
usage_type = gr.Dropdown(
|
| 119 |
+
label="Primary Usage",
|
| 120 |
+
choices=list(PRIMARY_USAGE_MAP.keys()),
|
| 121 |
+
value="Work / Productivity (1)",
|
| 122 |
+
)
|
| 123 |
+
brand = gr.Dropdown(
|
| 124 |
+
label="Brand",
|
| 125 |
+
choices=list(BRAND_MAP.keys()),
|
| 126 |
+
value="Brand C (3)",
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
predict_btn = gr.Button("Predict", variant="primary", size="lg")
|
| 130 |
+
|
| 131 |
+
with gr.Row():
|
| 132 |
+
verdict_out = gr.Markdown(label="Verdict")
|
| 133 |
+
details_out = gr.Markdown(label="Details")
|
| 134 |
+
|
| 135 |
+
predict_btn.click(
|
| 136 |
+
fn=predict,
|
| 137 |
+
inputs=[hours, cost, user_age, usage_type, brand, comp_age],
|
| 138 |
+
outputs=[verdict_out, details_out],
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
gr.Markdown(
|
| 142 |
+
"""
|
| 143 |
+
---
|
| 144 |
+
### Architecture
|
| 145 |
+
```
|
| 146 |
+
CSV Data β Dagster Pipeline β MLflow (tracking + registry)
|
| 147 |
+
β Evidently (drift + quality reports)
|
| 148 |
+
β FastAPI β Gradio (this app)
|
| 149 |
+
```
|
| 150 |
+
**Key signals driving replacement probability:**
|
| 151 |
+
- Hours/day > 12 β risk increases significantly
|
| 152 |
+
- Cost < $20k β cheaper machines fail more often
|
| 153 |
+
- Older users (>40) β slight additional risk
|
| 154 |
+
"""
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
return demo
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
if __name__ == "__main__":
|
| 161 |
+
demo = build_demo()
|
| 162 |
+
demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
|
data/raw/Computer_Durability.csv
ADDED
|
@@ -0,0 +1,1001 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ο»ΏHours Used Per Day,Cost,User Age,Needs Replacement,Primary Usage,Brand,Computer Age (Months)
|
| 2 |
+
16.89428369,35266.8221,22.91722397,0,3,2,17.58335924
|
| 3 |
+
7.247563169,45843.37058,22.07779155,0,3,3,40.4131477
|
| 4 |
+
5.384026161,45981.07025,59.65651109,0,3,4,11.42425654
|
| 5 |
+
5.246546519,20297.32086,22.22413339,0,4,5,35.2334834
|
| 6 |
+
8.86847528,43341.38919,23.50113439,0,3,2,54.3380642
|
| 7 |
+
10.88902525,33822.00801,51.28570096,0,1,4,19.89068888
|
| 8 |
+
20.11183093,49665.21624,33.63517102,0,2,3,40.54498459
|
| 9 |
+
3.75749515,34496.96228,52.27250175,0,1,2,12.08631174
|
| 10 |
+
17.90329449,49606.87836,11.72587098,0,1,2,37.86292852
|
| 11 |
+
13.22346303,42637.08084,35.79155804,0,4,4,20.85057076
|
| 12 |
+
7.622742882,27458.14654,9.91597521,0,3,2,13.46128726
|
| 13 |
+
12.33485044,36151.84938,11.5712326,0,2,5,19.47774099
|
| 14 |
+
7.898667605,49764.06918,59.66693484,0,1,5,11.55162304
|
| 15 |
+
14.70988497,45725.91823,15.93698615,0,3,4,54.21305036
|
| 16 |
+
10.97841023,22921.00681,38.34797889,0,3,3,24.43402629
|
| 17 |
+
4.781327169,41191.1599,31.43244935,0,4,4,15.47147899
|
| 18 |
+
3.681410715,30367.38758,27.7985696,0,4,5,27.45606262
|
| 19 |
+
13.57400069,20209.46786,59.2905007,0,4,1,19.91163384
|
| 20 |
+
21.75549977,32104.42907,9.243933616,0,2,1,44.92073597
|
| 21 |
+
8.911026791,27042.63265,45.83601211,0,1,2,58.3019748
|
| 22 |
+
17.85900247,44566.07812,62.91348275,0,4,2,26.84398575
|
| 23 |
+
16.14921116,30119.91827,39.92958646,0,4,1,51.8009706
|
| 24 |
+
22.42576303,44825.71082,61.39888034,0,4,5,34.38592134
|
| 25 |
+
19.8825634,20017.93817,10.97869912,0,1,2,10.27690424
|
| 26 |
+
14.03192095,29234.5745,31.87121919,0,1,4,41.74674178
|
| 27 |
+
16.11170146,27234.95643,22.82899408,0,4,4,31.29263533
|
| 28 |
+
21.66288873,27007.02083,49.656795,0,4,5,39.38578487
|
| 29 |
+
10.18893205,22045.59859,63.93393416,0,3,2,53.49155734
|
| 30 |
+
8.516278235,25613.72811,22.62221356,0,4,4,26.98563967
|
| 31 |
+
1.248654924,36666.4871,45.28795228,0,2,2,54.16947977
|
| 32 |
+
20.01067471,28740.83219,19.29156507,0,2,2,45.28947184
|
| 33 |
+
19.42255611,32521.84347,40.22382451,0,4,4,29.82769063
|
| 34 |
+
3.402453916,31306.00641,34.44415174,0,1,1,36.590106
|
| 35 |
+
14.26771977,46277.14775,63.40430379,0,3,4,20.20928682
|
| 36 |
+
11.67157906,47158.96997,42.68605454,0,1,5,59.03224068
|
| 37 |
+
3.730309197,34788.70915,27.92186321,0,4,3,40.37371862
|
| 38 |
+
23.55306922,29904.67851,14.50345974,0,2,4,3.586964297
|
| 39 |
+
5.938005412,23204.02112,16.62106924,0,3,2,20.27069244
|
| 40 |
+
2.546386142,48602.08638,20.84306501,0,1,2,37.77555159
|
| 41 |
+
14.68148973,21806.80835,22.3050997,0,1,4,4.32366339
|
| 42 |
+
17.99393357,28929.50833,56.4851153,0,4,3,2.409167288
|
| 43 |
+
1.746199259,28941.98604,39.98969893,0,3,4,27.55143013
|
| 44 |
+
16.15050569,29612.03858,37.83328195,0,2,3,20.6287206
|
| 45 |
+
13.24271315,24928.6053,14.5418257,0,4,3,26.85961787
|
| 46 |
+
3.375095204,43494.84293,57.02796252,0,3,5,16.49890528
|
| 47 |
+
4.96804917,23674.93586,49.20041528,0,1,2,10.71834707
|
| 48 |
+
14.06340884,39047.19977,11.85709663,0,4,2,41.65945806
|
| 49 |
+
7.816225111,23715.3808,48.34660054,0,1,3,31.35969586
|
| 50 |
+
22.58126176,22116.06913,38.98167839,0,1,2,12.80909846
|
| 51 |
+
19.83102528,47282.88511,12.65834471,0,4,1,19.85577615
|
| 52 |
+
23.62204575,25703.86299,34.12313657,0,4,3,35.91666655
|
| 53 |
+
6.972429296,44637.50464,35.62768837,0,4,2,5.261531548
|
| 54 |
+
23.30364355,36338.92613,17.44914879,0,4,1,9.699727547
|
| 55 |
+
10.91837901,22335.69559,61.90479438,0,4,5,5.321490642
|
| 56 |
+
22.17731082,16907.3274,56.44859617,1,1,2,48.92672263
|
| 57 |
+
8.992932292,48663.44574,46.13427323,0,2,1,45.39214476
|
| 58 |
+
2.15803912,27408.83501,34.35084698,0,2,4,45.44546298
|
| 59 |
+
2.213600931,45789.36036,31.47063587,0,4,4,43.26319527
|
| 60 |
+
16.91625889,25064.30017,45.10548763,0,1,1,40.49374
|
| 61 |
+
11.53236687,45618.28646,39.08961656,0,2,3,29.14252301
|
| 62 |
+
6.209997399,20396.86439,11.54956704,0,2,3,38.89441165
|
| 63 |
+
15.12131705,35353.4137,37.21265098,0,2,2,3.263331732
|
| 64 |
+
6.81573672,43083.27738,53.96500618,0,1,4,37.66640094
|
| 65 |
+
14.28553035,48043.86552,34.17667302,0,4,3,46.89478463
|
| 66 |
+
9.261801181,47276.06409,10.96152492,0,4,3,30.52114326
|
| 67 |
+
19.94170121,44588.74609,52.81785736,0,4,4,22.09328638
|
| 68 |
+
19.8760117,46645.74049,19.47773558,0,3,5,17.36817735
|
| 69 |
+
11.97905916,25976.59058,22.74138759,0,3,3,38.92532096
|
| 70 |
+
9.068839515,28525.52887,17.38826215,0,3,1,41.87527072
|
| 71 |
+
9.338875321,28669.05575,26.8222587,0,1,2,52.38708839
|
| 72 |
+
19.53143409,31311.61793,51.13483582,0,2,3,21.19771087
|
| 73 |
+
8.548128104,31799.62011,37.60499399,0,2,3,46.41656614
|
| 74 |
+
5.808365595,36336.65974,19.67823263,0,1,5,34.11999363
|
| 75 |
+
2.947831863,24796.3929,58.03631445,0,1,1,53.08735295
|
| 76 |
+
23.36593755,13302.91308,58.13616573,1,4,2,32.14091439
|
| 77 |
+
11.73394178,40737.39991,57.62297023,0,4,3,31.54015988
|
| 78 |
+
12.08903828,25026.99618,21.61138405,0,3,2,35.09474781
|
| 79 |
+
20.34090034,29309.25446,33.72064262,0,3,4,45.20960595
|
| 80 |
+
6.088490018,35100.35392,64.1444104,0,1,3,57.91673825
|
| 81 |
+
9.762799294,43893.21685,52.00471115,0,3,3,5.340237899
|
| 82 |
+
19.59333199,42184.077,9.54854301,0,2,4,4.152641541
|
| 83 |
+
19.94334305,39366.03069,11.7166617,0,1,5,56.31546991
|
| 84 |
+
9.846054748,24673.19782,34.44408848,0,1,3,32.26266191
|
| 85 |
+
11.55985254,36411.36037,59.82555173,0,2,2,37.27927786
|
| 86 |
+
7.97557549,22623.39505,38.70600248,0,4,1,29.46299249
|
| 87 |
+
22.4487622,31320.8953,36.37531296,0,4,2,6.525789704
|
| 88 |
+
3.728806612,32405.49591,14.0120009,0,2,5,46.31727333
|
| 89 |
+
22.61826141,13443.84468,45.43646597,1,2,1,45.08625406
|
| 90 |
+
22.47623673,21194.34318,54.85988015,0,3,2,19.39049597
|
| 91 |
+
16.72892839,28219.64298,29.68394043,0,1,2,19.06811547
|
| 92 |
+
20.86880371,20701.27842,52.2098755,1,3,4,50.43264147
|
| 93 |
+
13.22615951,48669.48402,62.97516927,0,2,5,5.269078207
|
| 94 |
+
1.751158398,48608.97823,19.61468817,0,3,4,52.98520375
|
| 95 |
+
11.93871241,30569.81163,37.82978898,0,2,1,16.1430397
|
| 96 |
+
15.32957616,8914.252802,24.36686361,1,3,4,35.98610012
|
| 97 |
+
10.37820642,21293.00327,53.19270188,0,3,5,56.22365656
|
| 98 |
+
4.708032675,25372.84818,40.92282186,0,4,3,41.00922759
|
| 99 |
+
16.09742088,31766.5338,44.1711978,0,2,3,25.45135894
|
| 100 |
+
23.33647087,48433.39094,53.4811071,0,3,1,41.68251833
|
| 101 |
+
13.93034265,24235.97191,30.57031694,0,1,3,21.29479854
|
| 102 |
+
17.43932536,42905.47531,60.1601339,0,3,4,17.35824656
|
| 103 |
+
2.574360409,23260.47618,38.38264543,0,3,1,59.8043134
|
| 104 |
+
10.6063409,49437.72732,17.0034249,0,1,5,45.29662364
|
| 105 |
+
3.676245159,31448.49497,47.66624974,0,2,1,39.03847867
|
| 106 |
+
15.09430422,43257.03617,53.21589697,0,4,1,21.96208895
|
| 107 |
+
22.58508964,30277.73248,26.05541559,0,4,2,3.90048892
|
| 108 |
+
16.22088785,38830.06631,56.85921765,0,4,2,2.832355686
|
| 109 |
+
2.766470111,25760.48254,59.65016552,0,1,1,33.69669407
|
| 110 |
+
9.157322715,24380.63058,23.78355578,0,3,3,45.95993054
|
| 111 |
+
13.66525773,48141.92419,64.06072394,0,2,2,32.70537224
|
| 112 |
+
10.27377839,47529.41834,16.0205571,0,3,5,50.60631924
|
| 113 |
+
20.17381224,46624.06821,19.5148931,0,4,4,2.972165453
|
| 114 |
+
19.73743311,38763.0035,18.50081584,0,3,2,22.63837251
|
| 115 |
+
15.07211346,44248.04634,58.95741347,0,1,1,14.98715308
|
| 116 |
+
9.589532738,22071.10811,45.29467548,0,1,3,27.38910512
|
| 117 |
+
6.872736597,30043.67126,16.66994414,0,2,4,5.270207198
|
| 118 |
+
3.435109319,29411.09374,33.09843485,0,2,2,57.29376685
|
| 119 |
+
9.16264672,39069.35966,43.07198743,0,4,3,14.17206866
|
| 120 |
+
10.5029679,30678.7332,12.75745284,0,2,3,55.41615875
|
| 121 |
+
16.54392728,24455.20345,58.297733,0,3,3,26.12769615
|
| 122 |
+
16.13767596,37894.01226,53.80540152,0,1,1,59.14592386
|
| 123 |
+
2.612487212,44431.51762,36.79678609,0,1,5,33.68944462
|
| 124 |
+
10.09096425,31463.70483,63.12621707,0,1,3,55.91913899
|
| 125 |
+
5.180913946,23968.06145,31.81237582,0,2,4,1.315590333
|
| 126 |
+
4.606692222,25900.64592,64.09428819,0,1,2,45.14608956
|
| 127 |
+
20.02794944,27124.49916,46.07143993,0,1,4,54.27998853
|
| 128 |
+
1.964049984,28996.92779,44.17626258,0,1,1,9.243513452
|
| 129 |
+
10.64120615,39283.85212,17.45942903,0,4,1,6.671425252
|
| 130 |
+
4.868684083,38028.36454,58.26988222,0,4,4,26.04978382
|
| 131 |
+
21.22459496,40882.39585,32.36691221,0,2,5,56.08705546
|
| 132 |
+
13.81193181,30875.05954,17.24730413,0,1,1,33.00080705
|
| 133 |
+
6.321754637,44719.92956,8.718628639,0,2,1,32.11993136
|
| 134 |
+
12.5558276,26122.62983,39.90607397,0,1,4,9.32644098
|
| 135 |
+
17.82418627,34071.22243,38.06177846,0,1,1,1.046581154
|
| 136 |
+
23.03671421,44028.16165,49.00315648,0,1,5,48.79826535
|
| 137 |
+
6.070258098,25404.75943,58.74470902,0,2,5,6.166899216
|
| 138 |
+
21.38218076,23295.89341,12.52617532,0,2,5,38.39713931
|
| 139 |
+
22.48899463,46510.51903,49.69529995,0,1,2,54.96237082
|
| 140 |
+
22.07767252,30287.88988,18.68248081,0,3,5,13.31046288
|
| 141 |
+
15.61453021,40516.10375,56.91609203,0,2,5,40.25109734
|
| 142 |
+
15.48587505,20760.45813,54.68662029,0,4,2,5.643216026
|
| 143 |
+
10.27384456,40701.40665,38.82524403,0,2,3,36.62155349
|
| 144 |
+
18.30515129,29483.03935,48.48383952,0,1,1,10.67197154
|
| 145 |
+
13.21976242,21672.22561,25.91795645,0,3,5,44.42126007
|
| 146 |
+
16.58107564,38294.38285,34.85657024,0,1,1,18.77264639
|
| 147 |
+
10.85453179,27224.11448,54.83330317,0,4,4,28.34006952
|
| 148 |
+
17.82441867,31604.21726,34.17811624,0,2,1,36.36640198
|
| 149 |
+
19.9628449,22417.42437,28.39450112,0,2,2,11.10513123
|
| 150 |
+
4.368033902,24414.89753,36.17010949,0,1,1,44.7566313
|
| 151 |
+
20.15738346,28939.31706,55.21020739,0,1,1,18.8557801
|
| 152 |
+
13.42682023,22692.78502,27.1068587,0,4,1,40.07993973
|
| 153 |
+
20.42731591,47324.78794,17.90441081,0,3,3,10.33869999
|
| 154 |
+
10.91993925,20181.4113,48.58479916,0,4,3,41.59894189
|
| 155 |
+
9.717931164,34515.80657,55.08075812,0,3,4,8.563096296
|
| 156 |
+
22.04639685,23697.42374,13.73633628,0,2,2,26.09603328
|
| 157 |
+
6.776893154,29230.37268,21.67282443,0,3,1,15.58515835
|
| 158 |
+
20.41688348,47781.78679,16.09240342,0,3,1,45.15413972
|
| 159 |
+
12.13247317,24850.8366,27.83266046,0,2,1,16.46006527
|
| 160 |
+
12.82419518,48974.96959,33.6700058,0,2,1,53.02132108
|
| 161 |
+
8.094770474,26173.83131,50.68310121,0,2,3,53.61739247
|
| 162 |
+
14.17997061,42246.98325,45.11541234,0,4,4,12.5900376
|
| 163 |
+
8.479857514,36400.31795,43.39292835,0,2,5,37.21691076
|
| 164 |
+
15.37939251,21405.06638,28.08537196,1,1,2,6.175340861
|
| 165 |
+
1.903196528,46534.36183,55.96251053,0,2,1,24.10068888
|
| 166 |
+
7.255967923,36619.79767,34.86336854,0,2,4,43.08598204
|
| 167 |
+
1.146717998,36036.77657,63.80898147,0,2,5,39.90597156
|
| 168 |
+
23.5021601,25863.74001,44.14610188,0,3,4,37.85733949
|
| 169 |
+
23.21218583,24986.88086,15.19709027,0,4,3,17.66991096
|
| 170 |
+
10.08857184,47494.67453,46.54212104,0,2,5,23.30347202
|
| 171 |
+
17.75252965,29835.14597,26.53092888,0,4,4,7.073822537
|
| 172 |
+
8.953540263,31162.46765,47.12065021,0,1,3,43.42234641
|
| 173 |
+
16.43514261,42794.70975,11.96954545,0,1,4,30.72943901
|
| 174 |
+
19.52489099,30164.70447,17.96824192,0,2,4,42.81717231
|
| 175 |
+
22.77539363,48561.8734,56.77702376,0,1,5,15.68766796
|
| 176 |
+
10.20488234,42137.83307,20.94923475,0,4,3,20.14185549
|
| 177 |
+
19.01585361,37922.87728,55.71134414,0,3,1,20.7187049
|
| 178 |
+
23.56032385,17974.94228,23.91873704,1,2,2,2.518802175
|
| 179 |
+
7.116354365,46426.85482,44.64426887,0,1,5,21.1218245
|
| 180 |
+
23.77764299,21488.17725,47.56658269,0,4,3,48.39496984
|
| 181 |
+
1.592700772,27711.88077,37.22132093,0,4,4,53.94061125
|
| 182 |
+
14.88116255,33951.09003,25.40270857,0,1,3,31.57488062
|
| 183 |
+
16.16863758,26809.35618,20.12072945,0,4,2,27.87460002
|
| 184 |
+
16.82504781,42317.15562,9.89179321,0,4,2,2.818652503
|
| 185 |
+
3.768914312,47576.24093,25.32494487,0,2,1,52.87305592
|
| 186 |
+
22.58618502,32713.1036,45.23027906,0,4,1,1.767602592
|
| 187 |
+
5.167215418,30263.978,61.48337465,0,1,2,56.74025473
|
| 188 |
+
15.32332827,48846.52196,57.65865549,0,2,5,29.86375867
|
| 189 |
+
6.12154982,33345.57298,51.66569226,0,1,5,21.17413512
|
| 190 |
+
8.065977591,34671.45201,52.94149857,0,3,4,55.52888766
|
| 191 |
+
12.12341395,13379.39548,45.9041395,1,1,1,2.439068593
|
| 192 |
+
13.56854646,30549.49617,22.83635088,0,1,4,35.3040688
|
| 193 |
+
10.59544863,47286.45419,59.71011675,0,2,1,33.38701895
|
| 194 |
+
4.685188822,37068.6645,46.23174178,0,4,2,13.93990367
|
| 195 |
+
4.931537256,42865.23762,39.94511597,0,1,5,26.30435845
|
| 196 |
+
10.61733013,44354.20155,14.32639975,0,2,5,39.48357635
|
| 197 |
+
20.59259968,14160.57233,33.482166,1,3,2,43.90074062
|
| 198 |
+
18.4175085,30255.47126,34.24042146,0,2,4,1.042294664
|
| 199 |
+
21.65318688,33010.82994,57.28012574,0,2,4,56.27435238
|
| 200 |
+
2.934936232,45454.15323,39.15894855,0,4,3,10.70699697
|
| 201 |
+
10.04138987,30897.66055,29.68283129,0,1,1,14.78124562
|
| 202 |
+
3.305647966,43818.054,63.67761873,0,3,4,16.5449333
|
| 203 |
+
1.381627055,48229.76968,14.31107382,0,2,2,3.231388057
|
| 204 |
+
16.2195297,38696.31119,32.08526041,0,1,4,8.260075292
|
| 205 |
+
14.85633717,42248.81862,10.39540648,0,1,5,59.19464545
|
| 206 |
+
4.749439618,44152.91936,50.17453488,0,2,1,19.59778751
|
| 207 |
+
20.47495395,10132.94414,60.33036348,1,1,3,7.80276433
|
| 208 |
+
6.377695161,45351.18947,23.96212778,0,2,1,7.312484409
|
| 209 |
+
1.545278334,31851.60014,56.92547558,0,2,5,43.44813809
|
| 210 |
+
20.20378573,21537.90378,24.65643914,0,2,5,51.41955118
|
| 211 |
+
19.47227449,20042.28665,59.91389921,1,3,5,16.16018567
|
| 212 |
+
23.41788371,32056.28814,50.97582851,0,2,5,2.233661228
|
| 213 |
+
4.115569346,40212.68689,53.87968628,0,4,2,14.16231109
|
| 214 |
+
6.323929917,33510.51157,9.026373669,0,1,3,4.334425757
|
| 215 |
+
20.97341905,42468.89453,62.88019035,0,3,5,7.100301261
|
| 216 |
+
22.30866735,30709.47288,49.42024655,0,1,5,26.45897907
|
| 217 |
+
10.6514298,39042.32423,25.37081244,0,3,4,14.56518388
|
| 218 |
+
2.164316862,25592.59415,55.2754959,0,4,3,31.44080683
|
| 219 |
+
1.906012175,42481.0831,24.04670831,0,3,1,15.95613144
|
| 220 |
+
14.20084692,36370.87906,57.74696886,0,3,3,56.49170077
|
| 221 |
+
10.0365875,26121.93166,14.41708225,0,3,3,54.45069411
|
| 222 |
+
1.659005834,28757.3352,48.11010072,0,1,3,34.4415602
|
| 223 |
+
14.41480502,42458.26126,38.81981317,0,2,5,21.91101553
|
| 224 |
+
1.259958342,34478.03707,13.5024648,0,1,5,23.48766604
|
| 225 |
+
19.10955504,26480.94321,21.78794821,0,2,2,25.29928471
|
| 226 |
+
8.047954922,49018.13571,8.707030068,0,1,1,43.92718001
|
| 227 |
+
1.925934069,41453.75059,34.71977403,0,4,4,27.64670074
|
| 228 |
+
14.53210108,48714.04498,25.17211986,0,4,3,51.54706684
|
| 229 |
+
10.14674153,32813.15136,42.1063766,0,3,1,56.70159237
|
| 230 |
+
23.39740951,34253.01022,24.94255504,0,4,1,20.82082305
|
| 231 |
+
13.51656924,46938.65649,25.09538936,0,3,2,34.0809744
|
| 232 |
+
7.330501164,35751.48021,50.36201233,0,2,4,6.608335673
|
| 233 |
+
17.31709389,41218.36736,10.74414165,0,4,3,12.82767382
|
| 234 |
+
23.88130615,15257.49485,59.46501473,1,2,4,25.39448024
|
| 235 |
+
7.244316058,27565.18632,56.57904084,0,1,2,13.16596024
|
| 236 |
+
21.7975957,33401.79406,46.06486694,0,2,5,52.71706896
|
| 237 |
+
9.618605322,23703.49597,41.8136295,0,1,1,10.33888911
|
| 238 |
+
15.74255323,11417.09085,58.86124239,1,2,3,27.88398847
|
| 239 |
+
13.64697694,26179.40108,18.56382023,0,2,5,36.73625519
|
| 240 |
+
2.163181388,20028.30246,12.50124224,0,4,2,41.81997058
|
| 241 |
+
10.80078847,21177.95155,21.65207968,0,2,4,23.60109281
|
| 242 |
+
20.14436141,49318.5147,53.29096189,0,4,1,52.27684505
|
| 243 |
+
19.52992252,27270.61085,9.976205203,0,4,3,34.53082991
|
| 244 |
+
6.159326854,39899.32129,41.2199896,0,3,5,26.93095905
|
| 245 |
+
6.199818633,45172.99096,64.73993842,0,3,5,4.967197409
|
| 246 |
+
16.07966237,16921.00232,56.77467735,1,1,1,28.41667864
|
| 247 |
+
19.79802218,36519.81378,37.72240428,0,2,4,49.42341461
|
| 248 |
+
22.39916366,24594.335,11.62753183,0,1,2,37.25792008
|
| 249 |
+
3.193301189,41843.49312,55.38829032,0,2,4,50.09758477
|
| 250 |
+
11.35134871,38007.5423,42.14177497,0,3,4,30.14668857
|
| 251 |
+
8.761448416,41937.51768,14.55118083,0,3,4,7.989240601
|
| 252 |
+
21.02583448,43092.39869,13.34986523,0,2,1,1.804750904
|
| 253 |
+
2.921073038,49262.31103,59.84872871,0,2,1,18.88995326
|
| 254 |
+
5.855896679,37206.8984,46.14441499,0,3,2,42.18143782
|
| 255 |
+
18.30677176,30249.23338,55.26934756,0,3,4,47.62533433
|
| 256 |
+
2.179758527,39453.93671,58.10179732,0,1,1,18.44941724
|
| 257 |
+
12.94727284,24914.68904,40.59102407,1,3,2,30.24358133
|
| 258 |
+
12.32831376,22053.22128,37.49444205,0,2,2,25.84601536
|
| 259 |
+
11.1684618,46921.91955,32.53436239,0,1,4,25.22084428
|
| 260 |
+
8.691227117,23581.43034,26.0659555,0,2,3,14.64957408
|
| 261 |
+
10.07516325,29835.28514,32.77196977,0,4,4,9.045895197
|
| 262 |
+
13.1886335,44472.36142,52.11114038,0,1,3,12.60462494
|
| 263 |
+
4.711449243,37919.37135,42.30963557,0,2,2,4.264610843
|
| 264 |
+
14.1559052,31809.06561,58.87382753,0,3,1,12.68971377
|
| 265 |
+
19.52494357,34203.06978,33.27266082,0,4,5,1.54285296
|
| 266 |
+
18.48370139,45636.4218,42.60410599,0,2,2,38.00824564
|
| 267 |
+
4.539697808,30200.13158,43.98453053,0,1,1,19.66614904
|
| 268 |
+
4.432737804,46089.49054,41.72673825,0,2,2,58.41553797
|
| 269 |
+
7.168010458,22644.03293,48.05012507,0,4,4,12.429306
|
| 270 |
+
9.304718706,43303.95312,21.5337077,0,1,3,21.40388565
|
| 271 |
+
10.39447837,45426.42899,37.20473532,0,4,2,22.11262836
|
| 272 |
+
16.63303599,25454.52959,13.94081376,0,2,1,1.498638268
|
| 273 |
+
2.30364994,32910.39597,29.91714568,0,3,4,50.51326923
|
| 274 |
+
1.797472197,24964.99324,35.79702291,0,3,5,2.887673149
|
| 275 |
+
10.01394295,41198.12387,45.17678279,0,2,4,56.47061025
|
| 276 |
+
17.03476492,36060.43304,62.18027,0,2,3,53.63238671
|
| 277 |
+
5.449012888,39059.7004,42.23711079,0,2,4,32.59579305
|
| 278 |
+
15.75460307,25894.60901,50.38485322,0,1,5,33.07213892
|
| 279 |
+
6.976047173,26352.37586,36.85716539,0,4,1,39.57289745
|
| 280 |
+
21.3799805,21244.91977,44.14392979,0,4,2,23.8359507
|
| 281 |
+
21.60086872,29662.02606,12.04313632,0,4,3,39.18611472
|
| 282 |
+
7.837605698,36792.33984,22.50032011,0,2,5,3.641334933
|
| 283 |
+
6.289856341,45738.04742,28.62562631,0,4,3,4.188810014
|
| 284 |
+
10.45999136,40007.82342,34.93212124,0,2,4,27.97659476
|
| 285 |
+
6.532226459,33061.8897,10.60197849,0,1,3,33.42771577
|
| 286 |
+
16.4648284,48593.60343,15.98137356,0,3,5,53.38124078
|
| 287 |
+
19.99948774,41577.35602,23.77841554,0,2,2,7.174977401
|
| 288 |
+
16.48111901,47904.95609,63.37736338,0,2,2,49.5362277
|
| 289 |
+
17.31357764,13705.24559,26.8867794,1,3,3,13.3927929
|
| 290 |
+
19.96006029,35827.6392,35.47634031,0,3,1,24.25093526
|
| 291 |
+
15.13258231,23413.82802,19.17756963,1,1,3,19.67954365
|
| 292 |
+
10.13081973,27767.12824,42.81446405,0,4,4,24.6091611
|
| 293 |
+
4.595290326,21584.80059,23.99894328,0,2,2,9.879234386
|
| 294 |
+
17.97287179,41782.54554,19.79857697,0,4,2,30.71063439
|
| 295 |
+
9.290913618,23639.08994,37.44463982,0,2,2,48.2969128
|
| 296 |
+
16.43922898,29083.25385,8.313830552,0,1,1,27.05050661
|
| 297 |
+
7.224810666,35974.12724,8.436849861,0,3,1,29.94620098
|
| 298 |
+
2.868281601,36932.49763,20.48692212,0,1,5,51.07728753
|
| 299 |
+
23.8293813,38017.49591,10.09311766,0,3,3,42.65742876
|
| 300 |
+
4.592634682,24987.65991,14.15746799,0,4,3,29.9368101
|
| 301 |
+
23.73368286,31392.12214,27.31505701,0,4,4,16.20404324
|
| 302 |
+
23.47743845,38515.82681,53.74738374,0,1,2,53.21067806
|
| 303 |
+
19.25781639,49094.56594,40.60677267,0,3,1,35.0442037
|
| 304 |
+
16.16672813,41828.93413,37.22206038,0,1,4,14.047709
|
| 305 |
+
14.28956216,47678.11271,24.72886292,0,3,4,16.87806541
|
| 306 |
+
20.92033554,42866.36175,61.10996201,0,3,1,48.38485225
|
| 307 |
+
7.657108997,37751.50907,30.62985735,0,4,3,3.604588163
|
| 308 |
+
11.75666789,25760.70107,12.96428771,0,4,2,59.53343903
|
| 309 |
+
15.2459693,40000.56956,43.17279142,0,3,3,8.690749103
|
| 310 |
+
10.45738094,38701.38698,14.48878843,0,3,4,47.968043
|
| 311 |
+
10.83218844,38074.07511,27.67771914,0,3,2,24.29174369
|
| 312 |
+
8.596547353,34693.57604,36.92248051,0,4,5,30.04075481
|
| 313 |
+
13.97733178,35859.69812,57.83068391,0,4,4,52.95838407
|
| 314 |
+
20.56321414,30033.27581,36.13215508,0,2,3,25.36176019
|
| 315 |
+
5.635152694,35579.38078,48.02874966,0,3,3,32.71213147
|
| 316 |
+
22.49195989,25926.67717,64.59055982,0,2,3,3.908680082
|
| 317 |
+
16.84901604,44155.23192,15.49487983,0,3,1,1.138500958
|
| 318 |
+
19.93528389,25573.30864,23.65969695,0,4,4,15.44319251
|
| 319 |
+
13.79238593,22548.9887,30.49083631,0,2,2,41.15440248
|
| 320 |
+
18.92888413,33087.38079,32.04420618,0,2,3,8.154628708
|
| 321 |
+
1.372609139,39749.22319,31.42820129,0,1,3,7.711955569
|
| 322 |
+
19.82274977,33131.50475,59.73382715,0,3,1,25.24667587
|
| 323 |
+
1.923193759,28302.62396,48.69986073,0,4,2,39.22238528
|
| 324 |
+
21.46800766,36745.13552,42.6505972,0,1,4,43.01224754
|
| 325 |
+
23.81514319,30410.07006,25.63424492,0,1,5,11.52858977
|
| 326 |
+
7.76355163,47986.0307,54.9560519,0,3,1,5.695700695
|
| 327 |
+
5.837326795,47679.35419,62.43844915,0,1,4,56.483425
|
| 328 |
+
18.60335737,35071.1546,54.80830211,0,3,5,16.37931657
|
| 329 |
+
16.12064043,11900.3368,8.089210996,1,1,3,51.34744336
|
| 330 |
+
6.819605841,29836.51982,44.2748781,0,3,3,51.94599558
|
| 331 |
+
20.90793481,42113.97242,10.91471674,0,4,2,46.57883291
|
| 332 |
+
3.36537962,21101.3883,22.68361156,0,3,5,10.96118279
|
| 333 |
+
3.896969517,34243.07018,11.39290792,0,3,3,49.4862861
|
| 334 |
+
23.52048577,30089.70183,42.41634082,0,2,3,46.385783
|
| 335 |
+
16.49829466,47616.07655,47.13562507,0,4,2,36.20665642
|
| 336 |
+
20.47840616,20360.09082,14.52579537,0,4,4,20.89492007
|
| 337 |
+
8.459575679,36584.44627,29.87881633,0,4,3,16.48800765
|
| 338 |
+
16.55912624,42219.90743,34.00548101,0,1,1,8.191463513
|
| 339 |
+
14.66756203,34550.53963,29.03602224,0,2,1,31.07566695
|
| 340 |
+
14.87245276,22562.0909,14.89842503,0,4,4,35.53039166
|
| 341 |
+
16.69824657,49173.8417,31.88002404,0,1,5,22.20100044
|
| 342 |
+
14.23326098,35540.31294,50.81714873,0,1,2,29.04618499
|
| 343 |
+
10.86843449,38425.58733,12.04743688,0,1,1,22.44735298
|
| 344 |
+
7.346221971,27098.29348,12.5702608,0,2,1,38.81935738
|
| 345 |
+
18.67737061,34504.94255,28.22196363,0,1,4,54.17429326
|
| 346 |
+
6.20426589,32874.48476,61.67836556,0,2,5,10.67129546
|
| 347 |
+
16.92414904,22246.87526,46.10864086,0,3,5,23.50311978
|
| 348 |
+
6.366654921,23184.81061,46.68418753,0,1,4,9.311247633
|
| 349 |
+
15.38259391,45124.17559,28.62943948,0,4,1,37.87168245
|
| 350 |
+
18.18280014,27194.59857,41.8386651,0,1,2,44.00336006
|
| 351 |
+
6.030419445,25848.74814,8.57747749,0,3,1,55.98285122
|
| 352 |
+
2.37869592,35147.34354,44.25747465,0,3,5,17.45344365
|
| 353 |
+
4.012396089,43062.41394,60.05735601,0,1,1,57.06246115
|
| 354 |
+
14.93793025,21858.39556,42.9166877,0,3,2,4.732497637
|
| 355 |
+
20.53726734,37304.23759,57.80081983,0,2,3,36.57781794
|
| 356 |
+
2.034926888,23576.31329,49.26646416,0,2,1,4.370830441
|
| 357 |
+
17.88547896,21093.83152,14.87182974,0,1,4,53.73267708
|
| 358 |
+
8.850328857,21582.31856,59.43983852,0,2,3,32.25873437
|
| 359 |
+
12.00615251,45024.92325,11.78729913,0,2,4,40.4915527
|
| 360 |
+
22.36376965,23539.17451,38.43614594,0,4,4,50.06286163
|
| 361 |
+
8.635344609,21354.54093,16.10181606,0,1,4,9.868326727
|
| 362 |
+
11.70274948,33136.99661,8.667276812,0,3,1,3.276902154
|
| 363 |
+
1.314695539,45319.80416,32.05595283,0,1,5,52.85398761
|
| 364 |
+
2.87677056,27872.25129,24.81732034,0,2,3,19.58106042
|
| 365 |
+
6.947606596,32651.37853,35.70202815,0,4,2,8.609755568
|
| 366 |
+
1.640601539,21197.67598,40.90039354,0,1,5,28.63921299
|
| 367 |
+
15.5216325,33472.35542,10.49312712,0,1,1,36.30005847
|
| 368 |
+
10.80716221,37328.39242,15.01118782,0,2,4,2.827265602
|
| 369 |
+
13.60733966,37127.21161,39.8426166,0,2,2,17.21884384
|
| 370 |
+
5.016885276,29972.80077,27.56053913,0,1,3,2.935192582
|
| 371 |
+
7.806436677,29465.99542,49.56288879,0,2,5,25.79978796
|
| 372 |
+
16.26796292,23198.70857,45.18062397,0,2,5,53.81090171
|
| 373 |
+
23.20196264,31003.94962,56.19944869,0,2,2,58.97649228
|
| 374 |
+
2.159585793,22984.84007,47.47209112,0,2,1,38.58580566
|
| 375 |
+
21.47883938,42997.87834,32.50607141,0,2,3,10.53662393
|
| 376 |
+
14.26826763,48988.22893,46.35909748,0,4,2,54.44511401
|
| 377 |
+
13.97015004,49102.2337,23.69672693,0,2,4,31.45862157
|
| 378 |
+
12.50640631,45952.13298,25.45986943,0,3,3,10.07120941
|
| 379 |
+
2.596063976,38023.76448,52.97215412,0,1,5,55.3818735
|
| 380 |
+
3.06716759,41025.97092,33.44591243,0,4,2,45.69067399
|
| 381 |
+
14.82097613,28556.17408,53.50767941,0,4,5,44.17061109
|
| 382 |
+
8.842047143,38923.11261,54.8780753,0,2,5,42.84166152
|
| 383 |
+
22.09716553,33669.48648,56.88105325,0,1,5,12.82004944
|
| 384 |
+
10.35158236,34460.52019,60.24820844,0,3,1,13.44939481
|
| 385 |
+
4.29547355,42733.29681,32.56615757,0,2,5,41.60928691
|
| 386 |
+
17.43853317,28457.55067,26.17540864,0,1,4,13.96364226
|
| 387 |
+
7.747128693,29541.18835,41.18532856,0,2,4,26.88163011
|
| 388 |
+
13.08581707,47739.25373,29.15682814,0,2,3,47.13810771
|
| 389 |
+
17.05015407,21686.72885,42.26122751,0,2,5,47.25319857
|
| 390 |
+
21.70607456,34569.02895,48.21839417,0,2,5,27.74244458
|
| 391 |
+
19.22039748,47384.02401,47.23908605,0,2,2,47.73920636
|
| 392 |
+
22.01486148,6094.654138,29.34951456,1,2,1,50.47868598
|
| 393 |
+
16.55630456,38310.89023,17.5110315,0,2,3,39.63176933
|
| 394 |
+
16.6315632,36392.21223,32.54017229,0,3,5,14.47712529
|
| 395 |
+
22.75626681,27492.32538,16.12786621,0,4,2,17.16669504
|
| 396 |
+
7.803954242,30298.26467,58.73553101,0,3,1,19.57562817
|
| 397 |
+
1.025578167,45561.45247,27.71501526,0,2,3,7.891442356
|
| 398 |
+
7.254954866,36776.54395,16.80420559,0,2,3,17.1164176
|
| 399 |
+
6.011501731,35442.26581,9.450522315,0,3,5,13.36163423
|
| 400 |
+
16.22219546,22987.39141,44.81187602,0,3,3,27.86439624
|
| 401 |
+
15.57536679,20480.84469,44.30343209,0,4,2,54.59781532
|
| 402 |
+
14.6496118,48910.55566,27.4144375,0,3,3,7.164338688
|
| 403 |
+
1.373599194,31309.58502,12.08757766,0,4,4,43.49580751
|
| 404 |
+
17.76189525,38096.39186,31.34884701,0,2,2,41.54969726
|
| 405 |
+
8.441199487,22391.4522,25.73939722,0,1,1,6.649497721
|
| 406 |
+
16.29628775,40493.42857,46.59587646,0,1,1,55.43177189
|
| 407 |
+
13.80130386,47977.00126,42.52941722,0,1,5,34.53985993
|
| 408 |
+
8.882811285,37269.95759,28.78184251,0,1,5,22.45980575
|
| 409 |
+
4.095254143,23783.45967,20.41987559,0,2,4,45.63577643
|
| 410 |
+
3.171634646,37451.64582,64.31805007,0,1,1,16.18456205
|
| 411 |
+
20.12492166,42990.41639,33.87809241,0,3,1,41.91708039
|
| 412 |
+
22.12314347,32192.4961,47.23163144,0,3,1,3.342961247
|
| 413 |
+
15.94512508,46395.13244,16.01151915,0,1,1,50.52507422
|
| 414 |
+
3.378984642,39460.4886,35.67861288,0,3,3,27.44207997
|
| 415 |
+
10.24662599,46977.46755,9.569593033,0,4,2,38.56488195
|
| 416 |
+
23.22581416,9084.842127,36.81094172,1,2,2,14.05303137
|
| 417 |
+
17.7656016,38705.15934,62.94899221,0,1,4,40.94734905
|
| 418 |
+
18.93588033,23938.63916,29.89978264,0,1,3,46.56217628
|
| 419 |
+
3.719028313,29290.75133,10.22277272,0,2,1,28.91957803
|
| 420 |
+
1.000706533,26053.84845,9.764480082,0,3,1,37.44588852
|
| 421 |
+
17.37915206,47264.79493,30.11524532,0,2,5,27.92322951
|
| 422 |
+
9.201712048,42812.00202,17.12142138,0,2,5,53.75139079
|
| 423 |
+
6.853070627,40291.95107,9.331069491,0,3,4,30.37428165
|
| 424 |
+
1.296641805,29028.98959,51.104176,0,2,1,50.66671383
|
| 425 |
+
13.42688636,25521.34316,34.13581151,0,3,4,48.27977144
|
| 426 |
+
20.57646691,42693.53264,24.486952,0,3,3,25.21978446
|
| 427 |
+
23.02552786,34224.73838,59.30474143,0,2,2,14.57498502
|
| 428 |
+
14.22879686,13373.90287,14.61992073,1,4,1,24.95884582
|
| 429 |
+
14.01213386,26773.68656,62.48737325,0,3,3,49.74682517
|
| 430 |
+
12.83261054,38498.65934,25.89610289,0,2,1,14.43031961
|
| 431 |
+
2.956296866,21210.01556,58.63900932,0,3,5,31.94174441
|
| 432 |
+
13.62407444,29785.10762,42.36774396,0,4,4,48.55970813
|
| 433 |
+
9.739518816,34056.70984,55.12231951,0,4,5,21.85517103
|
| 434 |
+
14.96411146,24438.75002,64.0887503,0,1,2,38.89036858
|
| 435 |
+
9.940706001,49539.63433,24.43910996,0,3,5,15.49226287
|
| 436 |
+
6.526879839,26272.92094,62.78472267,0,3,1,7.447487277
|
| 437 |
+
3.18420133,23910.5939,30.20046801,0,2,5,47.86866928
|
| 438 |
+
8.243124226,26106.04622,29.97417857,0,3,1,6.839148781
|
| 439 |
+
3.222831189,43071.83399,27.40206494,0,1,2,17.01150231
|
| 440 |
+
5.068629869,32765.16978,38.86023362,0,4,5,51.4514559
|
| 441 |
+
23.70873165,36962.47819,16.78626746,0,4,4,21.44168794
|
| 442 |
+
11.21438181,20630.27471,39.56155467,0,1,4,58.40967562
|
| 443 |
+
13.23305063,21032.93926,38.87717431,0,3,4,58.80432575
|
| 444 |
+
21.09322616,33374.76649,51.42840332,0,3,5,16.82828521
|
| 445 |
+
23.89808471,44499.6573,55.51804783,0,1,5,3.417715113
|
| 446 |
+
14.40243233,46557.16636,33.10515697,0,2,5,42.59873377
|
| 447 |
+
19.68689788,22601.19703,25.23299521,0,4,1,27.60820366
|
| 448 |
+
8.51964356,36150.89306,22.78208915,0,1,2,6.944417794
|
| 449 |
+
8.032111021,47396.89059,19.09355869,0,1,4,42.74384632
|
| 450 |
+
10.26218558,31639.18263,11.28591219,0,3,1,2.839319638
|
| 451 |
+
16.46766704,44886.97494,27.51953479,0,4,4,52.34160474
|
| 452 |
+
16.69040885,24010.86963,23.40411845,0,3,1,9.180499218
|
| 453 |
+
8.250506997,48061.83469,63.08579099,0,1,5,8.511400846
|
| 454 |
+
4.063828255,42542.69953,39.7894031,0,2,4,11.36466558
|
| 455 |
+
15.5451139,48240.03178,27.79479122,0,4,5,57.65571515
|
| 456 |
+
3.95149483,40298.51701,41.08665326,0,1,4,41.89699225
|
| 457 |
+
14.30532769,30902.11796,15.93421198,0,1,3,48.28092059
|
| 458 |
+
16.94870895,48145.43518,33.3104937,0,2,1,24.46046416
|
| 459 |
+
17.11406442,28267.00329,43.69535632,0,1,3,20.84245092
|
| 460 |
+
18.32985855,29965.17566,35.86717641,0,1,1,14.8345564
|
| 461 |
+
21.0757667,41024.25254,30.90139164,0,4,2,29.35823367
|
| 462 |
+
12.49905682,42959.48899,64.64935817,0,4,4,33.55789528
|
| 463 |
+
17.79573136,47882.10452,58.17794144,0,4,2,38.27471578
|
| 464 |
+
15.24097291,26144.07227,43.53413962,0,3,2,16.16999174
|
| 465 |
+
5.292718615,43939.94106,40.45450959,0,2,1,56.97059077
|
| 466 |
+
1.590861832,42156.3816,43.37764735,0,4,3,11.01575139
|
| 467 |
+
7.544451871,21913.83669,19.47479015,0,3,5,57.97353731
|
| 468 |
+
11.18977525,31616.69424,30.52285137,0,3,1,1.876511228
|
| 469 |
+
15.19343672,28482.96371,10.24919337,0,2,5,29.38641582
|
| 470 |
+
16.47010718,9392.698572,35.10612414,1,2,2,20.66870245
|
| 471 |
+
20.5490646,29134.71273,38.95240129,0,1,5,27.49828142
|
| 472 |
+
5.513651484,49488.40639,20.98173496,0,3,3,34.66921016
|
| 473 |
+
3.887645168,39283.3224,62.94964429,0,4,1,26.69976032
|
| 474 |
+
23.16003879,41530.28473,59.83891282,0,2,3,25.47596417
|
| 475 |
+
3.485024866,49298.52912,49.16216442,0,3,3,44.23068151
|
| 476 |
+
12.00529461,31316.05347,38.40303784,0,4,1,41.46670023
|
| 477 |
+
14.46796576,44055.20505,57.5882421,0,3,3,50.16300564
|
| 478 |
+
13.45161168,33035.9289,15.4471836,0,2,4,15.74757989
|
| 479 |
+
2.978739612,46086.01015,53.05905827,0,1,4,36.96218208
|
| 480 |
+
2.311619022,25419.72624,15.1147864,0,3,4,35.52649234
|
| 481 |
+
3.426118832,48430.58531,53.27000777,0,4,5,22.81453084
|
| 482 |
+
14.46851673,26563.15125,23.71875139,0,2,3,29.67739497
|
| 483 |
+
13.51993242,29786.61521,57.99416312,0,1,1,19.51314646
|
| 484 |
+
6.376038217,42685.76702,61.81039935,0,4,1,9.368819459
|
| 485 |
+
15.66880526,31812.65425,16.48137026,0,4,5,13.61427143
|
| 486 |
+
19.86155255,38246.3413,34.37250949,0,4,1,7.225305138
|
| 487 |
+
1.956316076,33337.20503,63.91628056,0,3,4,41.30110494
|
| 488 |
+
23.14356675,5004.540764,35.55422385,1,2,3,39.98291619
|
| 489 |
+
12.74170153,23300.03075,57.22220878,1,4,1,57.08577592
|
| 490 |
+
12.4569887,42271.09811,41.55769676,0,3,1,44.05102641
|
| 491 |
+
16.85413948,26849.93757,29.39380475,0,2,2,4.070704487
|
| 492 |
+
6.803174323,21753.73962,24.28966266,0,2,2,53.17046526
|
| 493 |
+
8.078730822,28997.88192,19.58371619,0,3,2,51.89783461
|
| 494 |
+
15.11367177,34229.25995,51.42249691,0,3,3,27.15589893
|
| 495 |
+
21.65251175,25040.60157,30.03280889,0,4,4,35.56591998
|
| 496 |
+
19.62826167,30643.91869,37.14270135,0,2,4,58.58862162
|
| 497 |
+
14.41199504,31999.49055,36.06255236,0,1,4,32.40595747
|
| 498 |
+
17.79509951,21721.75531,40.90490467,0,3,3,54.41398787
|
| 499 |
+
9.390916815,37499.21745,57.3378973,0,2,3,5.899286711
|
| 500 |
+
15.74220715,46530.30284,63.90214258,0,2,2,12.27206025
|
| 501 |
+
11.7221766,24552.28739,31.23229989,0,4,1,27.00454166
|
| 502 |
+
5.368531139,37934.2404,55.16857856,0,1,5,35.50065169
|
| 503 |
+
17.1399803,39942.17615,51.5780843,0,4,5,49.15744326
|
| 504 |
+
13.79868666,32580.04041,40.69115023,0,1,1,59.30746479
|
| 505 |
+
9.248296385,41032.92762,62.49468719,0,1,2,23.94223829
|
| 506 |
+
21.96080137,32321.9463,19.42704739,0,3,1,3.797522532
|
| 507 |
+
1.48148471,35139.11864,14.22805999,0,2,1,48.26911869
|
| 508 |
+
8.262782479,20220.59134,56.67583212,0,2,5,36.65846243
|
| 509 |
+
2.308272149,40751.60342,33.03153955,0,2,2,12.71402867
|
| 510 |
+
18.63012827,39840.28037,56.27722469,0,1,3,30.19487894
|
| 511 |
+
17.14847144,21152.02632,58.9061417,0,2,5,44.79630449
|
| 512 |
+
8.621101031,31047.50322,11.56008284,0,3,3,46.45429544
|
| 513 |
+
16.54024675,49370.95522,58.35749149,0,1,2,30.47748631
|
| 514 |
+
10.1065562,32622.09688,33.55418616,0,1,1,19.00765728
|
| 515 |
+
18.3938372,35058.36215,37.09465187,0,1,4,58.45081234
|
| 516 |
+
11.44622424,47297.90474,43.71577313,0,1,4,40.13839405
|
| 517 |
+
13.10832634,21877.58822,60.80380858,1,3,2,25.08601364
|
| 518 |
+
10.48243374,41517.85628,9.089617382,0,2,4,17.54733023
|
| 519 |
+
22.49878806,37462.10393,35.18007769,0,3,3,44.135076
|
| 520 |
+
6.770219297,43934.14044,47.20015642,0,2,4,15.28882329
|
| 521 |
+
3.77028882,45930.98799,49.19429659,0,3,2,11.13088935
|
| 522 |
+
14.4444916,33706.64215,47.4743486,0,4,2,32.26019945
|
| 523 |
+
23.29440168,34253.25095,15.66280238,0,4,3,18.10925617
|
| 524 |
+
9.690301916,31581.52807,25.06554854,0,4,4,38.10540355
|
| 525 |
+
2.426845563,49001.1842,28.44678495,0,4,4,7.958381818
|
| 526 |
+
8.804021148,40914.98763,53.85292084,0,1,1,39.64152517
|
| 527 |
+
12.63642805,22496.69199,23.88931273,0,1,5,17.71449515
|
| 528 |
+
4.724229153,45888.7887,20.01018294,0,3,2,1.014333132
|
| 529 |
+
16.13635102,34416.79823,62.57453709,0,3,1,8.489227403
|
| 530 |
+
23.94823595,22062.61719,8.505207251,0,4,2,56.08330729
|
| 531 |
+
13.95349187,19845.84463,64.87578877,1,2,1,52.17392371
|
| 532 |
+
11.39000795,36491.51765,46.57881403,0,4,5,14.78293289
|
| 533 |
+
9.158756805,32514.92837,55.22275007,0,3,1,46.92394237
|
| 534 |
+
10.21619584,46332.48215,24.79330559,0,4,2,36.905724
|
| 535 |
+
3.648362103,26119.83811,8.815970827,0,3,4,58.28337798
|
| 536 |
+
21.31529283,44793.93828,50.05856256,0,4,2,23.04273391
|
| 537 |
+
10.54099008,36730.66074,55.54624203,0,2,3,41.20617614
|
| 538 |
+
9.907311463,21638.3091,50.20713952,0,4,3,4.748607623
|
| 539 |
+
16.17287628,49155.35904,16.14246638,0,2,3,24.37859346
|
| 540 |
+
11.16676868,35378.45732,50.94539157,0,2,3,57.86524201
|
| 541 |
+
15.90936978,28726.30563,51.82859858,0,3,5,32.79987159
|
| 542 |
+
2.413264673,34082.20526,45.53680939,0,2,4,17.9848907
|
| 543 |
+
19.71563691,45735.73351,51.66860817,0,1,4,8.095986825
|
| 544 |
+
22.63159092,29147.09373,56.21752206,0,1,2,42.50817207
|
| 545 |
+
15.92160388,25780.29189,42.97543451,0,1,1,39.04650656
|
| 546 |
+
19.14255401,8356.291913,13.0504687,1,3,2,23.0623841
|
| 547 |
+
22.95501636,23872.13741,35.79478515,0,1,2,57.10553976
|
| 548 |
+
4.482943231,28935.78195,12.42601208,0,3,2,2.309965966
|
| 549 |
+
11.98057236,43401.44261,31.22995277,0,3,2,18.91970725
|
| 550 |
+
14.84155488,34148.79096,31.20506988,0,1,4,55.34309969
|
| 551 |
+
14.37994289,26824.44811,11.76256112,0,2,1,19.93519086
|
| 552 |
+
10.23362092,24985.54999,27.88277044,0,4,2,15.8829172
|
| 553 |
+
8.775941415,29981.8843,14.32689163,0,3,4,32.51900419
|
| 554 |
+
3.916952519,48197.73119,54.06940697,0,1,2,49.60430325
|
| 555 |
+
13.80566725,7407.271524,62.01821748,1,3,3,7.085658892
|
| 556 |
+
10.01007636,30290.80138,12.12203909,0,2,2,56.63822187
|
| 557 |
+
4.741198747,49643.72228,62.44156682,0,2,2,47.90929999
|
| 558 |
+
17.89041056,34556.91367,37.78686616,0,1,5,11.69376508
|
| 559 |
+
5.803111516,25374.91755,25.07524364,0,3,3,43.00846971
|
| 560 |
+
2.347002022,46734.20275,12.38113829,0,4,1,23.71256464
|
| 561 |
+
13.70442117,33810.66119,36.53558328,0,4,2,24.73538601
|
| 562 |
+
14.69028385,41290.14031,53.28738604,0,3,1,47.21411545
|
| 563 |
+
21.05400041,49511.3475,48.3039292,0,3,3,57.80430367
|
| 564 |
+
14.27057709,21505.26051,10.86288265,0,3,2,8.472696498
|
| 565 |
+
8.946059636,22900.83679,12.15540462,0,1,5,25.81718398
|
| 566 |
+
19.46927328,41613.70224,30.96377711,0,2,4,29.37555488
|
| 567 |
+
13.42641454,28454.7501,24.83155816,0,4,5,23.2023972
|
| 568 |
+
2.823446547,25529.04191,21.24590641,0,1,5,22.4281583
|
| 569 |
+
14.01456289,21180.85639,24.01725644,0,3,4,4.083851386
|
| 570 |
+
21.88442654,20655.40136,53.79851637,0,3,4,41.44677861
|
| 571 |
+
10.11519039,43234.56809,60.96599906,0,2,4,26.73665061
|
| 572 |
+
7.815493444,24033.00282,31.09085359,0,3,2,12.49283436
|
| 573 |
+
4.299797511,34031.61848,59.64832712,0,1,1,49.14597664
|
| 574 |
+
4.483247005,38176.90446,26.32525497,0,1,1,48.35108414
|
| 575 |
+
10.95374414,32850.55402,35.15690822,0,2,4,29.69710606
|
| 576 |
+
14.70063596,20813.19957,20.88365288,0,1,5,18.60858901
|
| 577 |
+
2.854154319,43505.04508,44.5071344,0,4,3,38.64747082
|
| 578 |
+
22.61007755,20315.39674,63.80192356,0,1,4,18.00097601
|
| 579 |
+
18.3680732,42856.69683,42.39910647,0,1,5,14.92446789
|
| 580 |
+
14.49235012,46799.13481,28.39540279,0,1,5,44.10036459
|
| 581 |
+
20.05408943,32985.19763,44.92559442,0,4,2,3.379189144
|
| 582 |
+
2.844295407,36066.27419,15.00647867,0,3,2,6.591339791
|
| 583 |
+
11.96729419,34309.50178,58.65356758,0,1,1,1.400502267
|
| 584 |
+
15.49214192,31676.25856,36.67578519,0,2,1,10.023648
|
| 585 |
+
20.05901684,46622.73606,33.6129353,0,4,3,5.070410514
|
| 586 |
+
19.01979988,46818.00532,41.39429294,0,2,3,59.7523397
|
| 587 |
+
7.372592743,30074.49094,43.61268018,0,1,3,18.03978823
|
| 588 |
+
22.6402439,38452.09262,12.09122095,0,2,3,9.806812747
|
| 589 |
+
3.859954353,47807.22686,46.90918161,0,1,5,15.21877922
|
| 590 |
+
21.12807323,48861.75497,21.79010578,0,4,5,56.23140872
|
| 591 |
+
23.33162574,35268.77121,48.69530005,0,2,1,31.68945316
|
| 592 |
+
5.074531034,33584.74416,54.88448328,0,2,2,22.52426768
|
| 593 |
+
17.60751948,28360.82948,53.82563493,0,1,2,52.53009602
|
| 594 |
+
1.926566932,48766.1562,39.49255514,0,1,4,24.30442596
|
| 595 |
+
10.32472498,24284.42913,37.64968386,0,3,5,20.76550563
|
| 596 |
+
12.87451588,37394.88728,16.1439298,0,1,3,13.22966883
|
| 597 |
+
14.3548402,28476.50803,52.19473057,0,1,3,23.29001261
|
| 598 |
+
22.52655671,28370.24871,23.47033469,0,3,4,19.684377
|
| 599 |
+
16.46176581,32149.05762,36.3116391,0,1,2,40.9609336
|
| 600 |
+
12.06939288,49963.78807,24.2036233,0,1,3,18.08537958
|
| 601 |
+
19.62252583,21648.98921,15.62821669,0,3,2,35.05478504
|
| 602 |
+
22.86225488,37356.25455,43.88478872,0,4,4,4.887637737
|
| 603 |
+
1.50752142,22477.16474,11.09692598,0,4,2,12.28606025
|
| 604 |
+
23.59798926,24635.6249,50.67277835,0,1,2,25.03691103
|
| 605 |
+
2.981039198,33186.88825,26.10244733,0,1,2,56.28000598
|
| 606 |
+
19.73089592,35313.80394,8.007677501,0,3,3,23.25786716
|
| 607 |
+
7.447699309,27763.77225,37.13436094,0,3,1,40.18037315
|
| 608 |
+
4.040132128,29493.71393,10.67055879,0,4,5,16.05014612
|
| 609 |
+
11.11667971,22609.01061,23.7416648,0,2,2,8.494980982
|
| 610 |
+
15.81100133,29096.59352,48.29765978,0,1,2,4.453659405
|
| 611 |
+
9.761892436,48996.75318,11.57330843,0,2,2,19.01939641
|
| 612 |
+
2.255004391,48341.22145,55.84229406,0,2,1,10.30200058
|
| 613 |
+
14.78760621,30046.3487,8.217735592,0,3,4,11.26661643
|
| 614 |
+
21.75527535,37125.61809,22.06896054,0,1,3,45.88479919
|
| 615 |
+
9.813953417,47287.86173,50.2315312,0,1,1,1.934446186
|
| 616 |
+
5.975653886,20185.51468,26.02739853,0,3,2,33.91079742
|
| 617 |
+
11.17927896,44320.50711,13.80786612,0,2,1,23.38822146
|
| 618 |
+
2.112342354,48451.08658,28.53333337,0,3,1,30.88644999
|
| 619 |
+
19.87121444,22739.77203,23.41241732,0,4,1,34.206984
|
| 620 |
+
20.00583865,24843.43058,56.03457842,0,1,4,19.48869397
|
| 621 |
+
14.52401066,48822.38908,25.8608313,0,4,5,18.41142275
|
| 622 |
+
9.121366446,36126.20431,52.96915089,0,3,3,44.27070911
|
| 623 |
+
19.39501738,20425.89903,58.83671225,0,1,4,52.4053423
|
| 624 |
+
13.75780157,27728.95996,32.72759292,0,4,4,12.36231632
|
| 625 |
+
19.9964431,33498.31023,59.8667592,0,4,5,6.759506872
|
| 626 |
+
15.52355223,40787.26733,29.50712908,0,2,4,53.96995258
|
| 627 |
+
19.04132588,41994.81134,62.95246974,0,1,1,21.64383236
|
| 628 |
+
14.76694884,49862.64539,13.0895029,0,4,1,33.52772605
|
| 629 |
+
10.52524457,28625.33598,47.16009819,0,4,2,22.21304553
|
| 630 |
+
23.03275478,36146.60306,36.14740031,0,2,4,8.422337844
|
| 631 |
+
13.44561797,35999.70644,30.09598191,0,4,2,35.77056705
|
| 632 |
+
14.91705893,32169.35778,44.06457709,0,4,3,31.98331025
|
| 633 |
+
6.071215707,25074.85682,48.11985422,0,4,1,12.97661999
|
| 634 |
+
15.38996816,49323.6969,8.24870637,0,2,3,27.66027363
|
| 635 |
+
23.61691658,6352.094989,17.51632606,1,4,5,48.01990274
|
| 636 |
+
14.14697594,45599.5156,48.64362489,0,4,1,35.83514008
|
| 637 |
+
5.251283153,34949.99085,45.98396857,0,1,3,6.718122297
|
| 638 |
+
2.370503239,38097.36268,63.06470779,0,1,5,33.33326834
|
| 639 |
+
14.90026435,26817.83161,51.37915404,0,2,3,42.46155966
|
| 640 |
+
18.5649222,36313.61211,62.19404434,0,3,5,47.76950094
|
| 641 |
+
13.03823153,40849.10399,48.04480859,0,1,5,38.11793714
|
| 642 |
+
6.215770382,35455.01085,24.98897589,0,2,1,10.00683171
|
| 643 |
+
16.33000546,49708.55512,14.00580057,0,4,2,42.90022273
|
| 644 |
+
2.842431712,38340.24969,52.56396021,0,4,5,40.96316892
|
| 645 |
+
11.16217521,31129.21138,44.71650562,0,2,5,2.441186093
|
| 646 |
+
4.757435443,43164.56083,10.74660271,0,4,1,25.29311239
|
| 647 |
+
17.36057433,19480.58378,28.52287656,1,4,2,20.64406457
|
| 648 |
+
5.242558174,25113.87535,62.53758948,0,3,4,40.49531179
|
| 649 |
+
5.652760831,31535.47441,36.52286531,0,4,4,13.75201293
|
| 650 |
+
9.894262312,30408.76851,32.66556075,0,1,2,35.86144346
|
| 651 |
+
2.176454729,32954.21285,34.08887749,0,4,3,54.87915175
|
| 652 |
+
10.14924914,43453.39478,19.90631527,0,2,1,48.07033996
|
| 653 |
+
12.76927128,34525.19096,29.01630874,0,3,2,55.36600644
|
| 654 |
+
12.11576855,43820.43351,29.07935559,0,4,4,58.73185205
|
| 655 |
+
9.804034309,37294.66709,10.98423615,0,2,5,1.629537493
|
| 656 |
+
18.01821552,23576.15921,51.75135483,1,1,4,37.74173531
|
| 657 |
+
20.30037199,30262.08304,31.74075262,0,4,5,25.35752447
|
| 658 |
+
4.342513926,44299.36231,54.86425355,0,3,1,51.73141493
|
| 659 |
+
12.63102511,27148.69012,56.46983606,0,4,5,15.85706742
|
| 660 |
+
2.42695259,26731.48939,20.08366112,0,2,2,58.88073986
|
| 661 |
+
2.636580614,35135.08772,45.46914848,0,3,5,37.2087702
|
| 662 |
+
14.19891454,27825.47738,34.92006284,0,4,3,47.81870673
|
| 663 |
+
14.01842377,20272.61105,58.16893406,0,1,2,6.050373617
|
| 664 |
+
21.1947708,39448.92582,20.29721871,0,3,1,11.8240152
|
| 665 |
+
13.83109454,35332.81464,46.63535242,0,1,2,45.74581671
|
| 666 |
+
23.07519347,33365.53443,42.64187097,0,2,3,2.2972048
|
| 667 |
+
2.119981513,49009.05544,24.83218422,0,4,1,49.19580117
|
| 668 |
+
3.25417675,37135.97552,15.78625151,0,1,4,1.822992103
|
| 669 |
+
2.013514816,45902.94733,45.14346635,0,2,3,39.7700115
|
| 670 |
+
5.261367558,24183.50709,50.10005511,0,4,1,22.54054129
|
| 671 |
+
13.44921255,37076.19389,25.99075859,0,2,5,37.07819631
|
| 672 |
+
15.84816017,46039.3888,44.75545843,0,1,2,42.06585301
|
| 673 |
+
2.048458633,37493.17272,30.52246173,0,2,1,21.20603793
|
| 674 |
+
22.75733047,41540.42534,48.65279235,0,3,2,7.369784439
|
| 675 |
+
20.36980366,31161.30703,19.35527067,0,4,5,5.109106197
|
| 676 |
+
7.82208709,22773.48551,58.74225515,0,2,1,35.74722533
|
| 677 |
+
2.775313195,33737.00885,24.382347,0,1,4,31.71866587
|
| 678 |
+
4.907402684,31834.38173,28.9638013,0,4,3,44.78985954
|
| 679 |
+
14.95990339,13610.71642,11.31124127,1,3,1,31.07439509
|
| 680 |
+
3.919721516,46011.80547,14.3561961,0,4,5,25.92430161
|
| 681 |
+
3.835526924,27319.85907,37.40412845,0,3,1,24.48714352
|
| 682 |
+
12.92691884,49002.02683,23.25278257,0,4,2,2.560367253
|
| 683 |
+
6.654491177,48744.14359,55.62237287,0,2,1,20.7978749
|
| 684 |
+
9.23721348,27428.94929,8.837523153,0,2,4,58.58518929
|
| 685 |
+
23.75862267,30441.55866,29.60836077,0,1,1,52.539779
|
| 686 |
+
16.73922773,46866.67223,27.22869617,0,3,3,20.43330596
|
| 687 |
+
22.82655534,48998.66663,9.101674962,0,1,1,35.6799289
|
| 688 |
+
4.278800766,33303.17309,15.08899874,0,2,2,50.99050799
|
| 689 |
+
9.789207921,29629.91137,31.57763247,0,3,1,27.03518587
|
| 690 |
+
13.75883148,35782.8204,36.09328131,0,1,4,2.059513961
|
| 691 |
+
2.765435423,40633.07619,31.04451559,0,1,5,5.396117247
|
| 692 |
+
1.096311203,28204.23546,38.26344767,0,1,1,31.11317056
|
| 693 |
+
16.41998968,42094.19524,41.92250547,0,1,1,58.19692316
|
| 694 |
+
15.76184889,21549.72071,8.565657804,0,1,3,41.89936299
|
| 695 |
+
10.44814263,21478.83478,34.45342409,0,3,4,5.184055295
|
| 696 |
+
12.26181375,31588.63115,62.91945673,0,1,5,12.17229167
|
| 697 |
+
10.55518901,30604.19576,37.58483997,0,3,2,46.50806545
|
| 698 |
+
1.552116031,29468.99646,46.61990925,0,1,1,50.64494584
|
| 699 |
+
8.713546943,34730.59288,25.7762399,0,2,5,37.44398059
|
| 700 |
+
5.072891874,29622.00747,52.11750855,0,2,2,39.02096701
|
| 701 |
+
3.263756349,25767.73755,52.05652447,0,1,4,2.671909573
|
| 702 |
+
23.00456998,29284.08647,37.71387472,0,4,4,49.88328532
|
| 703 |
+
14.78527368,49247.12818,63.63278793,0,2,5,47.50823728
|
| 704 |
+
17.75909454,32333.40251,15.15636224,0,3,2,16.2830735
|
| 705 |
+
8.253366095,33341.38331,8.966016899,0,1,4,42.81192648
|
| 706 |
+
10.04238398,37296.3497,51.89901807,0,3,5,13.67642178
|
| 707 |
+
6.44925186,49500.22305,54.00786563,0,4,4,3.379479502
|
| 708 |
+
3.230210432,27248.03829,14.85181206,0,1,4,12.86705601
|
| 709 |
+
5.117727661,40438.46332,23.13822148,0,2,4,11.53910373
|
| 710 |
+
19.34282304,41479.54081,9.000484712,0,3,3,20.92713181
|
| 711 |
+
16.58355352,38219.7325,24.71859081,0,2,2,25.68852033
|
| 712 |
+
17.65152303,7382.81344,52.06908681,1,2,5,31.0037946
|
| 713 |
+
13.57936415,22657.35019,37.52375008,0,1,5,29.12847865
|
| 714 |
+
11.92706708,40306.16827,27.84144881,0,3,2,16.96980175
|
| 715 |
+
19.94296359,15247.85641,29.18913981,1,4,2,2.721069446
|
| 716 |
+
22.21598852,21922.84133,8.077156666,0,2,2,36.70099571
|
| 717 |
+
2.672903799,37884.27949,25.08863007,0,1,2,24.34894496
|
| 718 |
+
7.453818704,42187.43532,44.84814827,0,1,1,18.62165729
|
| 719 |
+
21.01412564,11976.63815,63.5290833,1,4,5,52.14952153
|
| 720 |
+
9.048570632,26329.09672,56.28246714,0,1,1,45.05785516
|
| 721 |
+
18.99579368,45248.66023,9.344912985,0,2,4,51.60816735
|
| 722 |
+
23.82783626,20910.61417,59.21795447,0,1,4,30.20398489
|
| 723 |
+
6.535421839,45314.38591,52.64230956,0,3,5,8.724943875
|
| 724 |
+
21.10906869,33809.87152,52.48143904,0,3,4,42.07706473
|
| 725 |
+
20.10432349,26527.6382,34.10575826,0,2,2,13.15835302
|
| 726 |
+
6.167120146,24013.34435,30.6863017,0,3,1,27.03255048
|
| 727 |
+
10.18464934,48599.21503,25.26619654,0,4,2,4.835046548
|
| 728 |
+
10.42247599,22179.96763,11.74460696,0,1,4,49.69319574
|
| 729 |
+
23.49907538,25972.8026,21.00941572,0,2,1,8.430497822
|
| 730 |
+
5.160513732,30434.67042,22.05640719,0,4,2,54.28275292
|
| 731 |
+
19.38063334,38607.04547,35.59239297,0,1,5,51.98960553
|
| 732 |
+
8.686432426,36344.03256,50.60433118,0,4,2,28.4103831
|
| 733 |
+
17.81001795,23769.30477,35.00716022,0,2,4,44.91772405
|
| 734 |
+
10.6602531,28691.83951,11.29713362,0,2,1,37.54725936
|
| 735 |
+
14.30227455,27162.55313,62.59401866,0,4,5,48.3814629
|
| 736 |
+
20.15007079,26406.28563,61.73528619,0,1,1,26.47650095
|
| 737 |
+
19.49580799,32334.33009,52.75580396,0,3,5,47.36270057
|
| 738 |
+
20.92773322,41120.94358,64.50569039,0,3,1,28.28495221
|
| 739 |
+
2.371198483,26523.58797,39.03549297,0,4,4,56.51698148
|
| 740 |
+
16.91716272,31000.36639,62.87779596,0,2,5,29.69595395
|
| 741 |
+
4.219709665,31727.52281,12.30948732,0,1,3,28.29453704
|
| 742 |
+
10.56382706,49293.69588,28.84380265,0,1,1,47.31923983
|
| 743 |
+
13.61753355,48639.99071,20.84673228,0,2,1,49.47308381
|
| 744 |
+
10.26300815,44004.12748,19.1624858,0,3,2,33.71485774
|
| 745 |
+
12.95106854,27249.76762,16.02576809,0,1,5,11.608071
|
| 746 |
+
23.9201051,22041.11942,43.47757757,0,4,3,28.59773563
|
| 747 |
+
4.109998215,29507.12413,52.53534578,0,3,4,58.11627767
|
| 748 |
+
16.53548904,28003.83648,40.96300877,0,4,2,45.42618539
|
| 749 |
+
10.10802223,35177.774,16.37687445,0,3,3,48.85859363
|
| 750 |
+
4.059225375,43331.8373,54.23405489,0,2,5,2.93670443
|
| 751 |
+
4.650945558,45749.68817,44.24941831,0,4,3,28.6037083
|
| 752 |
+
22.83328144,45345.92256,30.12544525,0,4,1,39.15064689
|
| 753 |
+
21.23083442,30790.26684,46.42542685,0,2,1,49.97192833
|
| 754 |
+
21.86196509,40617.0303,22.81458382,0,4,2,3.907145561
|
| 755 |
+
23.80525384,29385.02593,27.67595588,0,3,5,51.29389425
|
| 756 |
+
5.79125397,21827.1663,60.29669306,0,1,5,33.35565527
|
| 757 |
+
9.175104759,41046.95797,24.55875682,0,1,3,12.64152426
|
| 758 |
+
16.39203769,20294.82658,34.78933129,0,2,2,19.71805777
|
| 759 |
+
12.12745842,32275.47762,58.73804483,0,3,5,17.63384355
|
| 760 |
+
10.62295927,29881.37808,48.34011697,0,4,1,32.32068302
|
| 761 |
+
9.229658735,40801.32616,11.54558389,0,4,3,4.049455115
|
| 762 |
+
14.65695453,42987.36418,16.39627352,0,1,4,10.35363335
|
| 763 |
+
14.23864886,29122.83908,8.446970674,0,4,1,48.94888956
|
| 764 |
+
4.713530183,44348.81043,43.96254204,0,1,2,8.823635454
|
| 765 |
+
11.85001626,28817.50282,33.51738525,0,2,5,14.7703799
|
| 766 |
+
13.72985085,33389.7908,15.6536665,0,4,1,27.74886573
|
| 767 |
+
14.11659959,41810.55581,62.6022449,0,3,4,31.5101324
|
| 768 |
+
5.832465437,40787.60688,38.19060426,0,4,4,46.0530183
|
| 769 |
+
18.07224202,49621.05417,21.78791776,0,1,2,27.39261317
|
| 770 |
+
1.579579181,26754.83073,36.53440356,0,2,1,47.60201264
|
| 771 |
+
9.161132677,39869.53741,46.73835897,0,1,3,37.48243212
|
| 772 |
+
18.94489092,38290.45725,12.34563542,0,4,1,7.672884626
|
| 773 |
+
13.96953718,47452.95363,23.65811746,0,1,4,10.22355487
|
| 774 |
+
7.006735948,24690.86425,53.99703298,0,1,2,26.7849898
|
| 775 |
+
16.98392174,42553.94491,34.20089761,0,2,2,24.41647419
|
| 776 |
+
14.04999172,37048.05849,39.1548971,0,3,3,44.86911191
|
| 777 |
+
19.31360302,28239.97609,32.67051213,0,4,2,35.26349125
|
| 778 |
+
17.90557928,34233.04742,10.50229673,0,2,2,23.89985178
|
| 779 |
+
15.03248221,30013.29791,17.44773745,0,3,2,11.07485672
|
| 780 |
+
12.22808653,37379.40208,33.39587341,0,1,2,14.38338904
|
| 781 |
+
4.06318889,46670.01832,19.92357153,0,3,2,34.95456009
|
| 782 |
+
7.010659411,37079.14953,10.84880463,0,1,4,25.98288949
|
| 783 |
+
10.63928788,38951.67255,56.08803085,0,1,1,31.38072302
|
| 784 |
+
14.7838836,35562.43541,63.92797416,0,2,5,11.06996868
|
| 785 |
+
12.81528067,21534.96519,53.20914382,0,1,2,40.11146889
|
| 786 |
+
7.628244097,26985.75671,56.66574054,0,2,5,51.29978571
|
| 787 |
+
1.148680229,34760.05188,21.79269643,0,3,5,16.2184309
|
| 788 |
+
12.4135021,29307.05851,62.7557376,0,3,4,29.99933932
|
| 789 |
+
7.569397737,25452.90793,19.22476515,0,1,2,58.55611241
|
| 790 |
+
17.89599092,35123.98526,62.23149919,0,2,5,20.66126269
|
| 791 |
+
1.550764101,29684.2938,64.70469976,0,1,2,36.10773435
|
| 792 |
+
14.45103966,27743.2293,48.56819993,0,2,3,6.321546312
|
| 793 |
+
21.04641699,12496.49849,63.92519271,1,2,3,36.75488515
|
| 794 |
+
22.65390746,33919.56252,40.46376744,0,2,3,6.288274625
|
| 795 |
+
5.011413144,23998.52774,22.79388164,0,3,5,57.59489068
|
| 796 |
+
11.84793829,47832.37199,32.90876548,0,2,3,43.22409647
|
| 797 |
+
3.102705812,46031.06294,41.83297332,0,3,4,47.5014275
|
| 798 |
+
15.39068398,41228.61757,12.16564904,0,2,4,42.54215729
|
| 799 |
+
13.66222865,23186.41483,43.47356608,0,3,5,35.79401313
|
| 800 |
+
18.93469335,17426.61248,63.9271367,1,4,2,27.54485408
|
| 801 |
+
10.36940706,43276.31763,18.83613604,0,3,1,51.64402941
|
| 802 |
+
12.99167149,44238.71984,53.17792163,0,3,2,57.45330439
|
| 803 |
+
21.62121714,43032.93853,59.75023125,0,1,2,30.4431342
|
| 804 |
+
5.514768953,23418.97661,61.79099079,0,2,2,55.9605679
|
| 805 |
+
22.8523173,19374.36227,62.72772945,1,4,2,35.41664169
|
| 806 |
+
1.518983769,39317.23871,37.72319941,0,1,1,32.76466703
|
| 807 |
+
21.80889417,7212.071977,63.70655076,1,2,3,27.81448124
|
| 808 |
+
20.81829235,45029.74387,51.16668181,0,4,2,13.34797793
|
| 809 |
+
14.27369289,37320.60204,17.21527187,0,4,4,1.219459988
|
| 810 |
+
21.48514448,39392.22126,35.18330607,0,4,5,15.24886738
|
| 811 |
+
14.72974014,33750.90438,48.944877,0,2,5,31.07366712
|
| 812 |
+
19.63370507,35861.2196,22.098398,0,4,4,23.41763718
|
| 813 |
+
10.88248736,21031.30575,44.51506546,0,4,2,27.46062845
|
| 814 |
+
18.24677842,39168.89066,45.99622731,0,1,1,58.85824495
|
| 815 |
+
21.99995374,22443.41639,17.2738854,0,3,3,21.66606987
|
| 816 |
+
14.15882817,33050.43783,40.20986281,0,2,5,27.67879342
|
| 817 |
+
5.158058463,28779.65293,51.98273393,0,3,3,8.87741753
|
| 818 |
+
7.182345146,36362.83851,36.43705288,0,1,1,3.829506214
|
| 819 |
+
5.568825269,24002.49007,8.689877656,0,3,5,46.74916136
|
| 820 |
+
6.677711635,40749.30206,8.515192738,0,4,3,34.67030733
|
| 821 |
+
8.047555335,43850.34611,28.35067261,0,4,1,45.88158356
|
| 822 |
+
13.7680771,24900.63349,60.7930619,0,2,3,9.841905134
|
| 823 |
+
14.52380748,23129.39262,21.03457644,0,2,2,34.7378975
|
| 824 |
+
10.80949419,24365.46448,44.15873027,0,2,1,15.96490599
|
| 825 |
+
15.15467794,46313.82686,20.65832463,0,1,3,14.4556472
|
| 826 |
+
2.876223289,35845.67785,26.33518941,0,2,2,1.680034829
|
| 827 |
+
3.019129452,34017.82988,56.33839911,0,1,2,8.690102691
|
| 828 |
+
4.952077588,23201.35457,49.54510221,0,4,1,19.24681858
|
| 829 |
+
12.91578245,39486.6802,13.43775881,0,3,4,7.895846875
|
| 830 |
+
5.92187761,46039.98776,32.4360152,0,4,5,59.16564798
|
| 831 |
+
7.51554128,44845.57608,9.662676528,0,4,1,19.20932626
|
| 832 |
+
21.08457738,5711.804616,35.41073836,1,4,2,8.689112855
|
| 833 |
+
10.20719523,38781.64127,45.75873867,0,2,2,12.74585646
|
| 834 |
+
19.67292085,22258.3615,14.75464955,0,2,1,35.67449047
|
| 835 |
+
1.318829126,23371.91082,24.46644152,0,2,4,4.778567908
|
| 836 |
+
15.93571287,21479.26961,30.67668765,0,3,5,51.88361368
|
| 837 |
+
16.38416021,27724.37649,60.41326841,0,2,3,57.72848107
|
| 838 |
+
19.36908295,24069.84401,64.61553588,0,1,1,15.55809782
|
| 839 |
+
22.4533176,27801.44121,10.55994404,0,4,5,27.55562085
|
| 840 |
+
1.463170526,31551.74167,51.37745164,0,3,5,2.305932611
|
| 841 |
+
12.66726626,24886.60605,29.18827178,1,3,2,38.80190326
|
| 842 |
+
4.537025227,48289.48352,30.37011662,0,3,3,25.42163743
|
| 843 |
+
21.38193976,38311.28179,50.99310348,0,3,1,16.9334495
|
| 844 |
+
11.54592693,45049.21357,60.35030663,0,3,2,13.20338953
|
| 845 |
+
13.9855847,41310.54502,62.20292989,0,1,2,39.58736158
|
| 846 |
+
12.08253555,9654.566978,40.8959874,1,4,4,25.55680483
|
| 847 |
+
16.24962569,23375.09392,28.35590608,0,3,5,11.59989805
|
| 848 |
+
16.6185319,42077.61219,52.89029789,0,4,1,41.65743581
|
| 849 |
+
22.43002928,28252.96645,22.30707885,0,2,1,34.3392647
|
| 850 |
+
23.9298366,39173.23541,40.15221964,0,3,1,51.66165124
|
| 851 |
+
17.5636216,30228.85921,28.43897231,0,3,2,7.931078034
|
| 852 |
+
7.606087812,49930.1679,45.4279712,0,4,4,47.35432951
|
| 853 |
+
21.23430525,27906.54438,21.70273803,0,3,3,40.73749042
|
| 854 |
+
2.132081034,28373.91412,18.92079791,0,3,2,7.785645273
|
| 855 |
+
6.314859616,47920.16767,60.33962794,0,3,3,41.99442306
|
| 856 |
+
21.6398715,20739.33214,13.80280993,0,1,4,27.05129004
|
| 857 |
+
6.892404832,40900.66964,36.83960074,0,1,5,56.75410235
|
| 858 |
+
6.074974371,41759.79172,20.58860863,0,3,1,9.355069686
|
| 859 |
+
4.176881742,45091.35732,10.21900791,0,1,3,38.92717006
|
| 860 |
+
20.75957043,49233.17932,10.05307264,0,2,2,23.53682268
|
| 861 |
+
12.50133996,49268.05757,17.98764101,0,3,3,57.45206556
|
| 862 |
+
16.52254367,21958.30273,57.40591021,0,2,5,20.46851882
|
| 863 |
+
6.498526834,44655.17968,24.10128841,0,4,1,57.76896866
|
| 864 |
+
18.43492968,39389.29801,62.17617272,0,2,3,54.18766521
|
| 865 |
+
18.48012753,39420.26403,41.1524707,0,4,5,16.95261088
|
| 866 |
+
8.189754573,23850.4978,32.88700882,0,3,2,48.40896483
|
| 867 |
+
10.44570056,39505.61499,41.06506358,0,4,4,59.74064791
|
| 868 |
+
5.822876381,49474.47742,37.45180796,0,4,2,15.90711713
|
| 869 |
+
23.46090354,47354.55709,51.25024646,0,2,1,26.76985299
|
| 870 |
+
15.84885927,33173.91982,24.10227576,0,2,2,31.69066346
|
| 871 |
+
22.49450926,21198.4392,28.12387232,0,2,2,18.40093701
|
| 872 |
+
7.977867503,30868.8047,58.96337524,0,3,2,1.900625539
|
| 873 |
+
18.983491,44791.70935,61.94802125,0,3,2,13.98762541
|
| 874 |
+
18.61474881,41044.97304,58.87581897,0,2,1,17.63427618
|
| 875 |
+
16.90807999,33780.30967,31.90853566,0,3,5,27.17238238
|
| 876 |
+
23.20969643,21653.04605,52.48083367,0,3,3,11.09504623
|
| 877 |
+
10.05177273,32806.84183,35.1511368,0,4,4,52.52200704
|
| 878 |
+
4.006498443,34070.74819,36.35977157,0,4,3,42.58975683
|
| 879 |
+
16.44930376,47856.66599,19.66676622,0,2,3,6.026828176
|
| 880 |
+
17.77412541,39232.58311,41.69442729,0,3,4,39.35545462
|
| 881 |
+
14.22639319,29349.5875,18.60974617,0,4,1,39.56573211
|
| 882 |
+
5.756811542,35759.54793,26.89918785,0,1,4,57.41704924
|
| 883 |
+
12.60248666,21981.65923,56.73672149,0,3,1,1.17823141
|
| 884 |
+
8.180363941,49177.04707,19.80332113,0,1,3,15.62801454
|
| 885 |
+
10.83173015,30801.79706,12.05602225,0,2,1,55.73969728
|
| 886 |
+
15.84002724,34336.02065,11.93343278,0,3,5,55.99590101
|
| 887 |
+
18.13993908,30658.51923,61.62471767,0,2,4,43.51459874
|
| 888 |
+
6.341167891,40475.3519,36.89446496,0,2,5,46.18003594
|
| 889 |
+
14.54936733,6076.610498,31.33648809,1,2,3,6.397494518
|
| 890 |
+
10.32248329,39866.96475,54.22007912,0,2,4,31.31867425
|
| 891 |
+
7.190716133,41459.66932,55.64207789,0,2,3,14.66339649
|
| 892 |
+
5.568527119,28044.29689,26.93488889,0,3,5,11.69186457
|
| 893 |
+
19.01790606,29470.05443,47.53622928,0,1,3,48.80789465
|
| 894 |
+
21.91281197,23757.53693,51.9540005,0,3,4,8.967250064
|
| 895 |
+
9.515340271,39665.29916,45.31508915,0,3,4,28.65044658
|
| 896 |
+
18.27941526,21193.91382,16.64091926,0,3,3,56.37611614
|
| 897 |
+
19.96228313,8797.506582,57.92533576,1,4,4,11.96409128
|
| 898 |
+
21.63089166,37930.47007,38.72838951,0,4,4,58.701612
|
| 899 |
+
20.46085684,46090.87655,24.1009283,0,3,3,59.66299116
|
| 900 |
+
5.204707936,46724.96445,32.23800657,0,1,5,33.78141213
|
| 901 |
+
22.99471327,33471.13281,10.14155215,0,3,4,11.03842167
|
| 902 |
+
9.66355813,33779.11065,15.28840767,0,2,4,19.65018839
|
| 903 |
+
8.802535207,22309.07549,51.63616478,0,4,4,26.04507713
|
| 904 |
+
2.435920527,40122.95076,8.000663181,0,4,4,19.25012924
|
| 905 |
+
12.18199404,44637.78261,31.74425222,0,3,3,17.55749921
|
| 906 |
+
3.50731316,27339.65164,37.78308032,0,3,5,58.36155284
|
| 907 |
+
6.716415881,32639.02645,11.11416194,0,1,5,10.04852482
|
| 908 |
+
8.289680301,45165.1801,63.46545529,0,1,1,44.26634661
|
| 909 |
+
19.60300544,46313.61403,20.88914388,0,2,4,1.024207077
|
| 910 |
+
21.13623896,27295.39941,25.33932702,0,3,4,36.17477749
|
| 911 |
+
7.956725302,36517.11904,25.3247232,0,3,1,55.04063723
|
| 912 |
+
20.75005679,25774.79452,21.13374974,0,3,5,17.98811155
|
| 913 |
+
20.71631156,23056.36292,8.084007859,0,4,2,11.94603254
|
| 914 |
+
18.58697186,47333.61504,49.57265326,0,2,2,39.4491079
|
| 915 |
+
4.373987622,44615.22513,63.11019349,0,1,5,43.04348303
|
| 916 |
+
16.87742548,42801.04705,20.78472855,0,4,4,41.59380062
|
| 917 |
+
22.73555349,21462.98956,45.79368993,0,4,2,19.83969592
|
| 918 |
+
16.69843101,45311.86378,50.28809064,0,4,3,15.87994173
|
| 919 |
+
8.627187529,33141.32228,56.36024661,0,4,3,57.3992092
|
| 920 |
+
12.5133243,30271.8537,32.08986423,0,2,1,55.47467099
|
| 921 |
+
15.851676,36394.29083,25.26706162,0,3,5,27.98167998
|
| 922 |
+
12.7431194,22740.59728,26.54182264,0,1,5,50.9254381
|
| 923 |
+
3.75891766,42699.39648,48.61941599,0,1,2,27.68708518
|
| 924 |
+
16.93436987,5779.156927,54.55642959,1,4,4,26.03895207
|
| 925 |
+
8.177473027,37473.05231,18.35199616,0,4,5,10.24901391
|
| 926 |
+
19.4551087,25597.70291,29.14363746,0,2,2,26.15795733
|
| 927 |
+
20.81261506,25646.96599,59.41059121,0,4,2,32.70759938
|
| 928 |
+
4.157900752,25527.45955,53.98153062,0,1,5,2.327755318
|
| 929 |
+
22.90489531,21710.07163,64.13692896,0,1,2,27.20266731
|
| 930 |
+
8.562920211,37633.95348,50.9921508,0,3,1,47.10001689
|
| 931 |
+
16.23321599,45210.44533,30.41212646,0,3,1,41.12271733
|
| 932 |
+
18.29197836,20524.94642,41.66635654,0,1,5,38.47426234
|
| 933 |
+
19.6747634,21470.13935,45.67781249,0,1,4,54.66377028
|
| 934 |
+
22.75224315,37183.1083,12.47198153,0,2,1,2.06388858
|
| 935 |
+
8.197149605,44584.87056,39.03632667,0,2,1,33.86580759
|
| 936 |
+
16.82887511,12836.21789,48.43128722,1,1,5,47.45358206
|
| 937 |
+
20.74564845,29802.16672,17.53731433,0,4,3,32.93878758
|
| 938 |
+
4.028798779,30488.43196,52.49600789,0,3,2,20.1098376
|
| 939 |
+
17.21594085,37890.39492,41.27504849,0,1,1,9.03878334
|
| 940 |
+
18.06548778,28226.38667,62.27664178,0,4,3,53.36622124
|
| 941 |
+
16.58763455,20798.25098,10.418071,0,1,4,28.77498115
|
| 942 |
+
6.803238931,42056.85554,23.12358984,0,3,3,52.80879662
|
| 943 |
+
21.07999238,38769.49739,42.28857112,0,3,5,35.56527522
|
| 944 |
+
4.883744658,37422.67997,24.903915,0,2,4,40.77733488
|
| 945 |
+
2.233600205,42250.52387,48.72217735,0,2,4,48.45140811
|
| 946 |
+
17.50169478,40256.92513,51.2633011,0,2,3,39.89017713
|
| 947 |
+
11.93594145,26176.45965,13.84340995,0,2,5,24.4852356
|
| 948 |
+
20.52925446,30574.43141,37.28970446,0,3,1,31.30436965
|
| 949 |
+
9.818134244,23783.30425,37.00676167,0,4,3,48.37968596
|
| 950 |
+
4.870407536,23895.7104,29.0490039,0,4,1,23.04536749
|
| 951 |
+
20.23817043,40285.87239,61.17671717,0,4,3,23.89916595
|
| 952 |
+
13.62492153,45758.00321,55.16786023,0,1,1,6.645716847
|
| 953 |
+
5.351503225,48881.97752,47.74093477,0,2,3,54.96048168
|
| 954 |
+
17.56883197,38850.41353,48.71661824,0,2,4,6.371142582
|
| 955 |
+
12.75561369,25523.0397,34.3178202,0,3,2,39.24434147
|
| 956 |
+
14.89919859,23169.51036,60.4966877,0,3,4,25.37847425
|
| 957 |
+
11.60066178,44385.52915,47.59193935,0,1,1,24.93154403
|
| 958 |
+
20.06030663,37362.69429,49.55192051,0,1,5,41.09355836
|
| 959 |
+
22.61504266,21048.03569,57.11638152,1,4,4,4.958530225
|
| 960 |
+
20.07994246,38548.55063,23.62208059,0,3,2,36.02864119
|
| 961 |
+
17.3159429,43443.86266,54.00304152,0,2,1,46.54987537
|
| 962 |
+
22.74675418,11824.889,19.12871437,1,4,4,49.86455272
|
| 963 |
+
3.42423168,41098.31683,27.68449128,0,2,3,11.06129297
|
| 964 |
+
22.91010317,13883.66814,27.12979589,1,4,5,53.94772395
|
| 965 |
+
20.29363882,45033.74759,63.7759517,0,2,1,39.71261942
|
| 966 |
+
16.43329855,21686.41716,56.82262175,0,1,4,53.02943325
|
| 967 |
+
17.03155582,42425.6191,47.96668211,0,4,1,29.63156231
|
| 968 |
+
9.348605329,45525.58272,49.44223541,0,2,3,32.97996736
|
| 969 |
+
21.22342979,28584.05127,40.03814928,0,2,2,55.22325834
|
| 970 |
+
17.68354681,39213.41822,61.98416803,0,3,4,54.09466306
|
| 971 |
+
3.58958073,29424.86192,36.28674891,0,4,1,27.44149153
|
| 972 |
+
10.37889479,48739.13199,29.68950946,0,2,1,17.19034527
|
| 973 |
+
21.38674833,21002.70717,17.29301421,0,3,1,24.19705564
|
| 974 |
+
2.085524139,37393.78531,52.81372218,0,2,2,23.87809538
|
| 975 |
+
23.11258225,24995.10641,49.86330413,0,4,1,17.92514562
|
| 976 |
+
16.62073907,41366.60616,29.9082372,0,1,5,16.84619666
|
| 977 |
+
15.52164749,25276.7085,9.436024596,0,4,5,51.80271325
|
| 978 |
+
7.222862899,27061.49786,55.82284768,0,4,1,5.12185332
|
| 979 |
+
6.722211141,34831.12433,8.650825057,0,2,5,40.97905974
|
| 980 |
+
22.66733905,47445.0485,48.11088744,0,2,4,11.19638304
|
| 981 |
+
7.860266447,26351.43116,63.30465525,0,4,5,21.32584771
|
| 982 |
+
15.76653692,18511.73547,32.9466957,1,3,1,4.631039014
|
| 983 |
+
18.83662182,24327.20063,21.39348408,0,3,3,16.92844457
|
| 984 |
+
10.04180136,42769.30496,48.17764757,0,3,4,46.3368432
|
| 985 |
+
4.202843628,35018.58597,54.57630409,0,2,2,53.21590415
|
| 986 |
+
1.246451847,47353.55337,39.14652802,0,2,1,14.07010196
|
| 987 |
+
13.51791875,24398.43671,63.12101069,0,4,2,28.34932252
|
| 988 |
+
23.44291283,39377.83679,10.94511713,0,1,3,28.74911883
|
| 989 |
+
9.490132458,26986.37438,36.7733491,0,2,2,52.54094304
|
| 990 |
+
9.215423682,24702.81286,48.951875,0,4,1,46.00514412
|
| 991 |
+
8.914107211,40502.88245,57.17050686,0,4,1,31.27155588
|
| 992 |
+
12.97250826,37810.5386,18.21756988,0,1,3,40.57408483
|
| 993 |
+
11.13529232,42644.82375,53.60019846,0,3,1,50.99394818
|
| 994 |
+
21.23803641,29283.87729,39.50430332,0,4,3,1.562798288
|
| 995 |
+
12.62697821,33439.14969,30.60355987,0,4,3,24.77051183
|
| 996 |
+
12.67843028,32164.15029,15.50775663,0,4,1,54.52734687
|
| 997 |
+
4.182546679,49522.68392,57.32185826,0,3,5,44.67846882
|
| 998 |
+
23.60300944,28767.4687,16.96457287,0,3,3,33.02764109
|
| 999 |
+
6.74937649,28844.51715,25.65790797,0,1,1,32.2490271
|
| 1000 |
+
2.687373552,30636.44626,24.53259532,0,2,5,21.51843576
|
| 1001 |
+
16.25236373,36937.82244,57.67059995,0,3,3,31.71071148
|
data/raw/Computer_Durability_Plus.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
models/best_model_info.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"winner": "XGBoost",
|
| 3 |
+
"run_id": "222e3e5354e24caa80b6addf170c7739",
|
| 4 |
+
"registry_version": 1,
|
| 5 |
+
"metrics": {
|
| 6 |
+
"roc_auc": 0.8413560591871138,
|
| 7 |
+
"avg_precision": 0.28931499498300856,
|
| 8 |
+
"f1": 0.3764705882352941
|
| 9 |
+
},
|
| 10 |
+
"all_metrics": {
|
| 11 |
+
"RandomForest": {
|
| 12 |
+
"roc_auc": 0.8269807079977524,
|
| 13 |
+
"avg_precision": 0.24554784524827208,
|
| 14 |
+
"f1": 0.3140495867768595
|
| 15 |
+
},
|
| 16 |
+
"XGBoost": {
|
| 17 |
+
"roc_auc": 0.8413560591871138,
|
| 18 |
+
"avg_precision": 0.28931499498300856,
|
| 19 |
+
"f1": 0.3764705882352941
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
}
|
models/feature_meta.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"feature_cols": [
|
| 3 |
+
"Hours Used Per Day",
|
| 4 |
+
"Cost",
|
| 5 |
+
"User Age",
|
| 6 |
+
"Primary Usage",
|
| 7 |
+
"Brand",
|
| 8 |
+
"Computer Age (Months)"
|
| 9 |
+
],
|
| 10 |
+
"target_col": "Needs Replacement",
|
| 11 |
+
"scaler_path": "/home/karl-joseph-cacao/Documents/MSDS2026/Term_04/MLOps_MSDS2026/MLOps_end2end_MSDS2026/models/scaler.joblib"
|
| 12 |
+
}
|
models/optuna_trials.csv
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
number,value,datetime_start,datetime_complete,duration,params_colsample_bytree,params_gamma,params_learning_rate,params_max_depth,params_min_child_weight,params_n_estimators,params_reg_alpha,params_reg_lambda,params_subsample,state
|
| 2 |
+
0,0.1898637299496884,2026-05-31 15:36:16.578732,2026-05-31 15:36:17.003259,0 days 00:00:00.424527,0.5780093202212182,0.2904180608409973,0.1205712628744377,10,2,250,0.08499808989182997,0.0006440507553993703,0.8394633936788146,COMPLETE
|
| 3 |
+
1,0.18258175254449607,2026-05-31 15:36:17.003313,2026-05-31 15:36:17.082029,0 days 00:00:00.078716,0.6061695553391381,0.9170225492671691,0.2708160864249968,3,2,383,2.716051144654844e-06,0.00015777981883364995,0.9329770563201687,COMPLETE
|
| 4 |
+
2,0.2369980882131971,2026-05-31 15:36:17.082077,2026-05-31 15:36:17.148082,0 days 00:00:00.066005,0.6460723242676091,2.28034992108518,0.08012737503998542,5,4,273,0.019116469627784252,3.9572205641009174e-07,0.6557975442608167,COMPLETE
|
| 5 |
+
3,0.23354413552023795,2026-05-31 15:36:17.148116,2026-05-31 15:36:17.260518,0 days 00:00:00.112402,0.5852620618436457,4.7444276862666666,0.011711509955524094,7,1,306,0.530953226900921,0.02932100047183291,0.8430179407605753,COMPLETE
|
| 6 |
+
4,0.23962087875157065,2026-05-31 15:36:17.260553,2026-05-31 15:36:17.316920,0 days 00:00:00.056367,0.5610191174223894,0.17194260557609198,0.1024932221692416,3,5,222,0.1881755597772026,1.1755466083160747e-06,0.7760609974958406,COMPLETE
|
| 7 |
+
5,0.28667717461871667,2026-05-31 15:36:17.316957,2026-05-31 15:36:17.421954,0 days 00:00:00.104997,0.5924272277627636,3.8756641168055728,0.05864129169696527,5,10,365,0.32808889626606236,0.14408501080722544,0.8186841117373118,COMPLETE
|
| 8 |
+
6,0.25540877113288846,2026-05-31 15:36:17.421987,2026-05-31 15:36:17.590549,0 days 00:00:00.168562,0.522613644455269,1.9433864484474102,0.01351182947645082,10,4,339,1.481809088646707e-06,0.04264813784432918,0.6783931449676581,COMPLETE
|
| 9 |
+
7,0.22473300885806227,2026-05-31 15:36:17.590588,2026-05-31 15:36:17.647970,0 days 00:00:00.057382,0.9010984903770198,4.9344346830025865,0.06333268775321842,5,1,243,0.015064619068942013,3.8879928024075543e-07,0.6563696899899051,COMPLETE
|
| 10 |
+
8,0.26037160449907365,2026-05-31 15:36:17.648004,2026-05-31 15:36:17.687360,0 days 00:00:00.039356,0.8856351733429728,1.7923286427213632,0.11069143219393454,9,1,102,8.451863533931625e-08,0.08032068562667222,0.8916028672163949,COMPLETE
|
| 11 |
+
9,0.2634981661447631,2026-05-31 15:36:17.687395,2026-05-31 15:36:17.796768,0 days 00:00:00.109373,0.6625916610133735,3.1877873567760657,0.012413189635294229,5,8,349,0.12522814303053625,5.994036749692399e-05,0.7243929286862649,COMPLETE
|
| 12 |
+
10,0.2631726307572251,2026-05-31 15:36:17.796802,2026-05-31 15:36:17.951005,0 days 00:00:00.154203,0.7640858921203038,3.651834476035047,0.03001215871999436,7,10,486,0.000708454929345622,0.5743386936170548,0.9729161367647149,COMPLETE
|
| 13 |
+
11,0.26603700011816767,2026-05-31 15:36:17.951047,2026-05-31 15:36:18.066038,0 days 00:00:00.114991,0.7171781358782056,3.428985852396359,0.03057610208137913,5,9,438,0.0007202349503619245,2.541136388519596e-05,0.7392194339652385,COMPLETE
|
| 14 |
+
12,0.26924362098676174,2026-05-31 15:36:18.066076,2026-05-31 15:36:18.180931,0 days 00:00:00.114855,0.7542782371321683,3.8010404681426615,0.03338034147970685,6,10,443,0.00027677678129142897,1.5223416071433098e-08,0.7458004804578023,COMPLETE
|
| 15 |
+
13,0.26031906342822275,2026-05-31 15:36:18.180969,2026-05-31 15:36:18.288627,0 days 00:00:00.107658,0.8160782391706531,4.133292234546421,0.03531369483454838,6,7,407,4.354583380764534e-05,1.887357440280478e-08,0.6037493867846113,COMPLETE
|
| 16 |
+
14,0.2496807049617076,2026-05-31 15:36:18.288666,2026-05-31 15:36:18.402156,0 days 00:00:00.113490,0.9966762831864868,2.7939929152165717,0.04815035925095873,6,10,492,0.0010872340147197433,0.004359299029555911,0.8038102230086545,COMPLETE
|
| 17 |
+
15,0.2791457576443677,2026-05-31 15:36:18.402191,2026-05-31 15:36:18.539029,0 days 00:00:00.136838,0.7242740284450381,4.109844361539446,0.02028573521720704,8,7,435,1.3307088829376484e-08,3.868159214922418e-08,0.8795233936488246,COMPLETE
|
| 18 |
+
16,0.2738912655179476,2026-05-31 15:36:18.539071,2026-05-31 15:36:18.674124,0 days 00:00:00.135053,0.6836874653341911,4.369794306410908,0.020397706135341725,8,7,383,1.6510195320654132e-08,5.251280804878214e-06,0.8920538105669723,COMPLETE
|
| 19 |
+
17,0.25417907776052284,2026-05-31 15:36:18.674167,2026-05-31 15:36:18.762207,0 days 00:00:00.088040,0.8132394567758081,2.838091932582077,0.018661763995585987,8,7,185,4.598257659375889e-06,0.9522581473958315,0.8749004528350904,COMPLETE
|
| 20 |
+
18,0.2356488662792346,2026-05-31 15:36:18.762243,2026-05-31 15:36:18.839684,0 days 00:00:00.077441,0.5094030062553871,4.353215554408531,0.04969388325976179,4,8,328,2.9904280092387287e-07,0.0013509039128217649,0.9694880010155159,COMPLETE
|
| 21 |
+
19,0.22775512996274952,2026-05-31 15:36:18.839723,2026-05-31 15:36:18.924300,0 days 00:00:00.084577,0.639553048746349,3.9411181890882356,0.1991266186754082,8,6,426,3.454555947497455e-05,5.246279583118166e-08,0.8109069179221944,COMPLETE
|
| 22 |
+
20,0.28931499498300856,2026-05-31 15:36:18.924341,2026-05-31 15:36:19.061684,0 days 00:00:00.137343,0.7020843771180812,3.176227806450042,0.02261987722445193,9,9,378,1.2387044364130546e-08,6.840793596052189e-06,0.9184453934740648,COMPLETE
|
| 23 |
+
21,0.28581974382822006,2026-05-31 15:36:19.061724,2026-05-31 15:36:19.257097,0 days 00:00:00.195373,0.7149872794581777,3.250454518489347,0.02032786129497231,9,9,377,1.3767884945287468e-08,5.30621871476353e-06,0.9253052475673965,COMPLETE
|
| 24 |
+
22,0.28920056877860617,2026-05-31 15:36:19.257139,2026-05-31 15:36:19.406564,0 days 00:00:00.149425,0.6883898835864328,3.115044878544297,0.023136549204546084,9,9,364,1.7566366830590038e-07,8.359410053058924e-06,0.938363077009676,COMPLETE
|
| 25 |
+
23,0.26653265451306396,2026-05-31 15:36:19.406602,2026-05-31 15:36:19.495439,0 days 00:00:00.088837,0.6160774117802493,2.8617253865724335,0.04000479078782372,9,9,296,1.7977504933100885e-07,1.7227894475773726e-05,0.9997173908335091,COMPLETE
|
| 26 |
+
24,0.26794141321867904,2026-05-31 15:36:19.495474,2026-05-31 15:36:19.588458,0 days 00:00:00.092984,0.8039619302221802,2.290670043607198,0.06487473530802493,10,8,356,6.037447074927862e-07,0.00023039318949668005,0.9225443295963883,COMPLETE
|
| 27 |
+
25,0.2665346426703458,2026-05-31 15:36:19.588498,2026-05-31 15:36:19.684495,0 days 00:00:00.095997,0.6821269497054445,1.4635679205345227,0.02604698774930044,4,9,314,1.140881083355269e-05,4.0565145398254975e-06,0.8465055785221199,COMPLETE
|
| 28 |
+
26,0.2820373576224597,2026-05-31 15:36:19.684531,2026-05-31 15:36:19.835644,0 days 00:00:00.151113,0.5361187515937964,3.410347487890499,0.01606989528103154,9,10,395,6.362577991228303e-08,0.004737188696616971,0.9403252601806452,COMPLETE
|
| 29 |
+
27,0.2714868590236465,2026-05-31 15:36:19.835683,2026-05-31 15:36:19.976217,0 days 00:00:00.140534,0.6149440016111111,2.558894363479297,0.024851203526807926,7,8,459,0.004920491332843422,8.99350063281423e-07,0.957152195047184,COMPLETE
|
| 30 |
+
28,0.25661956243485884,2026-05-31 15:36:19.976258,2026-05-31 15:36:20.070753,0 days 00:00:00.094495,0.6919072523191407,2.9963426885215694,0.04094162942045279,4,9,364,4.861837982039971e-08,3.375170910136555e-05,0.7803890844735994,COMPLETE
|
| 31 |
+
29,0.2158726038429907,2026-05-31 15:36:20.070791,2026-05-31 15:36:20.145960,0 days 00:00:00.075169,0.5554549411439629,1.0270497742181646,0.15921728208236582,10,6,280,7.26811739217153e-07,0.0006337665565356945,0.8557310125499158,COMPLETE
|
pyproject.toml
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[build-system]
|
| 2 |
+
requires = ["hatchling"]
|
| 3 |
+
build-backend = "hatchling.build"
|
| 4 |
+
|
| 5 |
+
[project]
|
| 6 |
+
name = "mlops-pipeline"
|
| 7 |
+
version = "0.1.0"
|
| 8 |
+
description = "End-to-end MLOps pipeline: Dagster + MLflow + Evidently + FastAPI + Gradio"
|
| 9 |
+
requires-python = ">=3.12,<3.13"
|
| 10 |
+
dependencies = [
|
| 11 |
+
# Orchestration
|
| 12 |
+
"dagster>=1.9.0",
|
| 13 |
+
"dagster-webserver>=1.9.0",
|
| 14 |
+
# ML
|
| 15 |
+
"scikit-learn>=1.5.0",
|
| 16 |
+
"xgboost>=2.1.0",
|
| 17 |
+
"optuna>=3.6.0",
|
| 18 |
+
"imbalanced-learn>=0.12.0",
|
| 19 |
+
# Experiment tracking
|
| 20 |
+
"mlflow>=2.15.0",
|
| 21 |
+
# Monitoring / drift
|
| 22 |
+
"evidently>=0.4.33",
|
| 23 |
+
# Serving
|
| 24 |
+
"fastapi>=0.112.0",
|
| 25 |
+
"uvicorn[standard]>=0.30.0",
|
| 26 |
+
"pydantic>=2.8.0",
|
| 27 |
+
# Frontend
|
| 28 |
+
"gradio>=4.42.0",
|
| 29 |
+
"requests>=2.32.0",
|
| 30 |
+
# Data
|
| 31 |
+
"pandas>=2.2.0",
|
| 32 |
+
"numpy>=1.26.0",
|
| 33 |
+
"scipy>=1.14.0",
|
| 34 |
+
]
|
| 35 |
+
|
| 36 |
+
[project.optional-dependencies]
|
| 37 |
+
dev = [
|
| 38 |
+
"pytest>=8.0.0",
|
| 39 |
+
"ipykernel>=6.29.0",
|
| 40 |
+
"jupyter>=1.0.0",
|
| 41 |
+
]
|
| 42 |
+
|
| 43 |
+
[tool.hatch.build.targets.wheel]
|
| 44 |
+
packages = ["src/mlops_pipeline"]
|
| 45 |
+
|
| 46 |
+
[tool.dagster]
|
| 47 |
+
module_name = "mlops_pipeline.definitions"
|
scripts/run_pipeline.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Run the full Dagster asset pipeline programmatically (no web UI needed).
|
| 3 |
+
Executes all assets in dependency order:
|
| 4 |
+
raw_data β augmented_data β train_test_split_asset
|
| 5 |
+
β baseline_rf_model + tuned_xgb_model β best_model_info
|
| 6 |
+
β data_quality_report + data_drift_report + model_eval_report
|
| 7 |
+
"""
|
| 8 |
+
import sys
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
|
| 11 |
+
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
| 12 |
+
|
| 13 |
+
from dagster import materialize
|
| 14 |
+
|
| 15 |
+
from mlops_pipeline.assets.data_assets import (
|
| 16 |
+
augmented_data,
|
| 17 |
+
raw_data,
|
| 18 |
+
train_test_split_asset,
|
| 19 |
+
)
|
| 20 |
+
from mlops_pipeline.assets.evaluation_assets import (
|
| 21 |
+
data_drift_report,
|
| 22 |
+
data_quality_report,
|
| 23 |
+
model_eval_report,
|
| 24 |
+
)
|
| 25 |
+
from mlops_pipeline.assets.training_assets import (
|
| 26 |
+
baseline_rf_model,
|
| 27 |
+
best_model_info,
|
| 28 |
+
tuned_xgb_model,
|
| 29 |
+
)
|
| 30 |
+
from mlops_pipeline.config import MLFLOW_EXPERIMENT, MLFLOW_TRACKING_URI
|
| 31 |
+
from mlops_pipeline.resources import MLflowResource
|
| 32 |
+
|
| 33 |
+
ASSETS = [
|
| 34 |
+
raw_data,
|
| 35 |
+
augmented_data,
|
| 36 |
+
train_test_split_asset,
|
| 37 |
+
baseline_rf_model,
|
| 38 |
+
tuned_xgb_model,
|
| 39 |
+
best_model_info,
|
| 40 |
+
data_quality_report,
|
| 41 |
+
data_drift_report,
|
| 42 |
+
model_eval_report,
|
| 43 |
+
]
|
| 44 |
+
|
| 45 |
+
if __name__ == "__main__":
|
| 46 |
+
print("=" * 60)
|
| 47 |
+
print("MLOps Pipeline: Computer Durability Classifier")
|
| 48 |
+
print("=" * 60)
|
| 49 |
+
|
| 50 |
+
result = materialize(
|
| 51 |
+
ASSETS,
|
| 52 |
+
resources={
|
| 53 |
+
"mlflow": MLflowResource(
|
| 54 |
+
tracking_uri=MLFLOW_TRACKING_URI,
|
| 55 |
+
experiment_name=MLFLOW_EXPERIMENT,
|
| 56 |
+
)
|
| 57 |
+
},
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
if result.success:
|
| 61 |
+
print("\nβ Pipeline completed successfully!")
|
| 62 |
+
print(f" MLflow UI: mlflow ui --backend-store-uri {MLFLOW_TRACKING_URI}")
|
| 63 |
+
print(f" Reports : reports/")
|
| 64 |
+
print(f" Models : models/")
|
| 65 |
+
print("\nTo serve the model:")
|
| 66 |
+
print(" uv run uvicorn serving.api:app --host 0.0.0.0 --port 8000")
|
| 67 |
+
print(" uv run python app.py")
|
| 68 |
+
else:
|
| 69 |
+
print("\nβ Pipeline failed. Check logs above.")
|
| 70 |
+
sys.exit(1)
|
scripts/synthesize_data.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Synthesize ~2000 additional rows for Computer_Durability_Plus.csv.
|
| 3 |
+
|
| 4 |
+
Distribution matches the original 999-row dataset but introduces mild drift
|
| 5 |
+
so Evidently can detect it:
|
| 6 |
+
- Hours Used Per Day shifted +2h (heavier usage in new cohort)
|
| 7 |
+
- Cost shifted -$3000 (cheaper machines in new cohort)
|
| 8 |
+
- Class rate ~7% positive (up from 5%), still realistic
|
| 9 |
+
|
| 10 |
+
The synthesized rows are appended to the original data and saved as
|
| 11 |
+
Computer_Durability_Plus.csv at the project root.
|
| 12 |
+
"""
|
| 13 |
+
from __future__ import annotations
|
| 14 |
+
|
| 15 |
+
import csv
|
| 16 |
+
import random
|
| 17 |
+
import math
|
| 18 |
+
from pathlib import Path
|
| 19 |
+
|
| 20 |
+
SEED = 42
|
| 21 |
+
N_SYNTH = 2000
|
| 22 |
+
ROOT = Path(__file__).parent.parent
|
| 23 |
+
|
| 24 |
+
# Original distribution parameters (derived from EDA)
|
| 25 |
+
ORIG = {
|
| 26 |
+
"hours_mean": 12.648, "hours_std": 6.558, "hours_min": 1.0, "hours_max": 24.0,
|
| 27 |
+
"cost_mean": 33789.0, "cost_std": 9647.0, "cost_min": 5000.0, "cost_max": 50000.0,
|
| 28 |
+
"age_mean": 36.637, "age_std": 16.560, "age_min": 8.0, "age_max": 65.0,
|
| 29 |
+
"comp_age_mean": 29.654, "comp_age_std": 16.862, "comp_age_min": 1.0, "comp_age_max": 60.0,
|
| 30 |
+
}
|
| 31 |
+
# Mild drift: heavier usage, cheaper machines
|
| 32 |
+
DRIFT = {
|
| 33 |
+
"hours_mean": 14.8, # +2.15h drift
|
| 34 |
+
"hours_std": 6.2,
|
| 35 |
+
"cost_mean": 30500.0, # -$3289 drift
|
| 36 |
+
"cost_std": 9200.0,
|
| 37 |
+
"age_mean": 36.637, # unchanged
|
| 38 |
+
"age_std": 16.560,
|
| 39 |
+
"comp_age_mean": 29.654,
|
| 40 |
+
"comp_age_std": 16.862,
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
HEADER = [
|
| 44 |
+
"Hours Used Per Day", "Cost", "User Age",
|
| 45 |
+
"Needs Replacement", "Primary Usage", "Brand", "Computer Age (Months)"
|
| 46 |
+
]
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def clamp(value: float, lo: float, hi: float) -> float:
|
| 50 |
+
return max(lo, min(hi, value))
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def box_muller(mean: float, std: float, rng: random.Random) -> float:
|
| 54 |
+
u1, u2 = rng.random(), rng.random()
|
| 55 |
+
z = math.sqrt(-2 * math.log(max(u1, 1e-12))) * math.cos(2 * math.pi * u2)
|
| 56 |
+
return mean + std * z
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def replacement_probability(hours: float, cost: float, user_age: float) -> float:
|
| 60 |
+
"""
|
| 61 |
+
Signal calibrated to match observed replacement rates:
|
| 62 |
+
<12h/day β ~0%, 12-16h β ~8%, >16h β ~11%
|
| 63 |
+
Cost modifier: cheaper machine β higher risk.
|
| 64 |
+
Age modifier: older users slightly more at risk.
|
| 65 |
+
Overall positive rate: ~5-7% (original ~5%, drift cohort ~7%).
|
| 66 |
+
"""
|
| 67 |
+
if hours < 12.0:
|
| 68 |
+
base = 0.002
|
| 69 |
+
elif hours < 16.0:
|
| 70 |
+
base = 0.07
|
| 71 |
+
else:
|
| 72 |
+
base = 0.11
|
| 73 |
+
# Cost: scale from 1.8 (cheapest, $5k) to 0.2 (most expensive, $50k)
|
| 74 |
+
cost_factor = 2.0 - 1.8 * (cost - 5000.0) / 45000.0
|
| 75 |
+
# User age: mild uplift for older users
|
| 76 |
+
age_factor = 1.0 + 0.01 * max(0.0, user_age - 40.0)
|
| 77 |
+
return min(base * cost_factor * age_factor, 0.85)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def synthesize(n: int, rng: random.Random) -> list[list]:
|
| 81 |
+
rows = []
|
| 82 |
+
for _ in range(n):
|
| 83 |
+
hours = clamp(box_muller(DRIFT["hours_mean"], DRIFT["hours_std"], rng), 1.0, 24.0)
|
| 84 |
+
cost = clamp(box_muller(DRIFT["cost_mean"], DRIFT["cost_std"], rng), 5000.0, 50000.0)
|
| 85 |
+
user_age = clamp(box_muller(DRIFT["age_mean"], DRIFT["age_std"], rng), 8.0, 65.0)
|
| 86 |
+
comp_age = clamp(box_muller(DRIFT["comp_age_mean"], DRIFT["comp_age_std"], rng), 1.0, 60.0)
|
| 87 |
+
primary_usage = rng.randint(1, 4)
|
| 88 |
+
brand = rng.randint(1, 5)
|
| 89 |
+
p_replace = replacement_probability(hours, cost, user_age)
|
| 90 |
+
needs_replacement = 1 if rng.random() < p_replace else 0
|
| 91 |
+
rows.append([
|
| 92 |
+
round(hours, 8), round(cost, 5), round(user_age, 8),
|
| 93 |
+
needs_replacement, primary_usage, brand, round(comp_age, 7)
|
| 94 |
+
])
|
| 95 |
+
return rows
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def main() -> None:
|
| 99 |
+
rng = random.Random(SEED)
|
| 100 |
+
|
| 101 |
+
src = ROOT / "Computer_Durability.csv"
|
| 102 |
+
dst = ROOT / "Computer_Durability_Plus.csv"
|
| 103 |
+
|
| 104 |
+
orig_rows = []
|
| 105 |
+
with src.open(encoding="utf-8-sig") as f:
|
| 106 |
+
reader = csv.reader(f)
|
| 107 |
+
next(reader) # skip header
|
| 108 |
+
for r in reader:
|
| 109 |
+
orig_rows.append([
|
| 110 |
+
float(r[0]), float(r[1]), float(r[2]),
|
| 111 |
+
int(r[3]), int(r[4]), int(r[5]), float(r[6])
|
| 112 |
+
])
|
| 113 |
+
|
| 114 |
+
synth_rows = synthesize(N_SYNTH, rng)
|
| 115 |
+
all_rows = orig_rows + synth_rows
|
| 116 |
+
|
| 117 |
+
pos = sum(1 for r in all_rows if r[3] == 1)
|
| 118 |
+
print(f"Original rows : {len(orig_rows)}")
|
| 119 |
+
print(f"Synthesized : {len(synth_rows)}")
|
| 120 |
+
print(f"Total rows : {len(all_rows)}")
|
| 121 |
+
print(f"Positive class: {pos} ({100*pos/len(all_rows):.1f}%)")
|
| 122 |
+
|
| 123 |
+
with dst.open("w", newline="", encoding="utf-8") as f:
|
| 124 |
+
writer = csv.writer(f)
|
| 125 |
+
writer.writerow(HEADER)
|
| 126 |
+
writer.writerows(all_rows)
|
| 127 |
+
|
| 128 |
+
print(f"Saved β {dst}")
|
| 129 |
+
|
| 130 |
+
# Also copy raw originals into data/raw/
|
| 131 |
+
raw_dir = ROOT / "data" / "raw"
|
| 132 |
+
raw_dir.mkdir(parents=True, exist_ok=True)
|
| 133 |
+
import shutil
|
| 134 |
+
shutil.copy(src, raw_dir / src.name)
|
| 135 |
+
shutil.copy(dst, raw_dir / dst.name)
|
| 136 |
+
print(f"Copied raw files β {raw_dir}")
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
if __name__ == "__main__":
|
| 140 |
+
main()
|
serving/__init__.py
ADDED
|
File without changes
|
serving/api.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
FastAPI model serving endpoint.
|
| 3 |
+
|
| 4 |
+
Loads the champion model from MLflow Model Registry (or falls back to the
|
| 5 |
+
locally saved joblib file) and serves predictions.
|
| 6 |
+
|
| 7 |
+
Start with:
|
| 8 |
+
uv run uvicorn serving.api:app --host 0.0.0.0 --port 8000 --reload
|
| 9 |
+
"""
|
| 10 |
+
from __future__ import annotations
|
| 11 |
+
|
| 12 |
+
import json
|
| 13 |
+
from pathlib import Path
|
| 14 |
+
from typing import Optional
|
| 15 |
+
|
| 16 |
+
import joblib
|
| 17 |
+
import mlflow
|
| 18 |
+
import numpy as np
|
| 19 |
+
from fastapi import FastAPI, HTTPException
|
| 20 |
+
from pydantic import BaseModel, Field
|
| 21 |
+
|
| 22 |
+
ROOT = Path(__file__).parent.parent
|
| 23 |
+
MODELS_DIR = ROOT / "models"
|
| 24 |
+
META_PATH = MODELS_DIR / "feature_meta.json"
|
| 25 |
+
INFO_PATH = MODELS_DIR / "best_model_info.json"
|
| 26 |
+
|
| 27 |
+
app = FastAPI(
|
| 28 |
+
title="Computer Durability Classifier",
|
| 29 |
+
description="Predicts whether a computer needs replacement based on usage patterns.",
|
| 30 |
+
version="1.0.0",
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
# ββ Model Loading βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 34 |
+
|
| 35 |
+
_model = None
|
| 36 |
+
_scaler = None
|
| 37 |
+
_feature_cols: list[str] = []
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def _load_artifacts() -> None:
|
| 41 |
+
global _model, _scaler, _feature_cols
|
| 42 |
+
|
| 43 |
+
if META_PATH.exists():
|
| 44 |
+
meta = json.loads(META_PATH.read_text())
|
| 45 |
+
_feature_cols = meta["feature_cols"]
|
| 46 |
+
_scaler = joblib.load(meta["scaler_path"])
|
| 47 |
+
else:
|
| 48 |
+
raise RuntimeError("feature_meta.json not found β run the Dagster pipeline first.")
|
| 49 |
+
|
| 50 |
+
if INFO_PATH.exists():
|
| 51 |
+
info = json.loads(INFO_PATH.read_text())
|
| 52 |
+
winner = info.get("winner", "XGBoost")
|
| 53 |
+
if winner == "XGBoost":
|
| 54 |
+
model_path = MODELS_DIR / "xgb_tuned.joblib"
|
| 55 |
+
else:
|
| 56 |
+
model_path = MODELS_DIR / "rf_baseline.joblib"
|
| 57 |
+
_model = joblib.load(model_path)
|
| 58 |
+
else:
|
| 59 |
+
raise RuntimeError("best_model_info.json not found β run the Dagster pipeline first.")
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
@app.on_event("startup")
|
| 63 |
+
def startup_event() -> None:
|
| 64 |
+
_load_artifacts()
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
# ββ Request / Response schemas ββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 68 |
+
|
| 69 |
+
class PredictionRequest(BaseModel):
|
| 70 |
+
hours_used_per_day: float = Field(..., ge=0.0, le=24.0, example=18.5)
|
| 71 |
+
cost: float = Field(..., ge=0.0, example=15000.0)
|
| 72 |
+
user_age: float = Field(..., ge=0.0, le=120.0, example=45.0)
|
| 73 |
+
primary_usage: int = Field(..., ge=1, le=4, example=2)
|
| 74 |
+
brand: int = Field(..., ge=1, le=5, example=3)
|
| 75 |
+
computer_age_months: float = Field(..., ge=0.0, example=36.0)
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
class PredictionResponse(BaseModel):
|
| 79 |
+
needs_replacement: bool
|
| 80 |
+
probability: float
|
| 81 |
+
model_version: Optional[str] = None
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
# ββ Endpoints βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 85 |
+
|
| 86 |
+
@app.get("/health")
|
| 87 |
+
def health() -> dict:
|
| 88 |
+
return {"status": "ok", "model_loaded": _model is not None}
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
@app.get("/info")
|
| 92 |
+
def model_info() -> dict:
|
| 93 |
+
if INFO_PATH.exists():
|
| 94 |
+
return json.loads(INFO_PATH.read_text())
|
| 95 |
+
return {"error": "model info not available"}
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
@app.post("/predict", response_model=PredictionResponse)
|
| 99 |
+
def predict(request: PredictionRequest) -> PredictionResponse:
|
| 100 |
+
if _model is None or _scaler is None:
|
| 101 |
+
raise HTTPException(status_code=503, detail="Model not loaded")
|
| 102 |
+
|
| 103 |
+
# Assemble feature vector in the exact column order
|
| 104 |
+
row = np.array([[
|
| 105 |
+
request.hours_used_per_day,
|
| 106 |
+
request.cost,
|
| 107 |
+
request.user_age,
|
| 108 |
+
request.primary_usage,
|
| 109 |
+
request.brand,
|
| 110 |
+
request.computer_age_months,
|
| 111 |
+
]])
|
| 112 |
+
row_scaled = _scaler.transform(row)
|
| 113 |
+
prob = float(_model.predict_proba(row_scaled)[0, 1])
|
| 114 |
+
label = prob >= 0.5
|
| 115 |
+
|
| 116 |
+
version = None
|
| 117 |
+
if INFO_PATH.exists():
|
| 118 |
+
info = json.loads(INFO_PATH.read_text())
|
| 119 |
+
version = f"{info.get('winner')} v{info.get('registry_version')}"
|
| 120 |
+
|
| 121 |
+
return PredictionResponse(
|
| 122 |
+
needs_replacement=label,
|
| 123 |
+
probability=round(prob, 4),
|
| 124 |
+
model_version=version,
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
@app.post("/predict/batch")
|
| 129 |
+
def predict_batch(requests: list[PredictionRequest]) -> list[PredictionResponse]:
|
| 130 |
+
if _model is None or _scaler is None:
|
| 131 |
+
raise HTTPException(status_code=503, detail="Model not loaded")
|
| 132 |
+
|
| 133 |
+
rows = np.array([
|
| 134 |
+
[r.hours_used_per_day, r.cost, r.user_age, r.primary_usage, r.brand, r.computer_age_months]
|
| 135 |
+
for r in requests
|
| 136 |
+
])
|
| 137 |
+
rows_scaled = _scaler.transform(rows)
|
| 138 |
+
probs = _model.predict_proba(rows_scaled)[:, 1]
|
| 139 |
+
|
| 140 |
+
version = None
|
| 141 |
+
if INFO_PATH.exists():
|
| 142 |
+
info = json.loads(INFO_PATH.read_text())
|
| 143 |
+
version = f"{info.get('winner')} v{info.get('registry_version')}"
|
| 144 |
+
|
| 145 |
+
return [
|
| 146 |
+
PredictionResponse(
|
| 147 |
+
needs_replacement=bool(p >= 0.5),
|
| 148 |
+
probability=round(float(p), 4),
|
| 149 |
+
model_version=version,
|
| 150 |
+
)
|
| 151 |
+
for p in probs
|
| 152 |
+
]
|
src/__init__.py
ADDED
|
File without changes
|
src/mlops_pipeline/__init__.py
ADDED
|
File without changes
|
src/mlops_pipeline/assets/__init__.py
ADDED
|
File without changes
|
src/mlops_pipeline/assets/data_assets.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Dagster data assets:
|
| 3 |
+
raw_data β load original 999-row CSV
|
| 4 |
+
augmented_data β load 3000-row Plus CSV (original + 2000 synthetic)
|
| 5 |
+
train_test_split β stratified split with SMOTE oversampling on train set
|
| 6 |
+
"""
|
| 7 |
+
import json
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
from typing import Any
|
| 10 |
+
|
| 11 |
+
import numpy as np
|
| 12 |
+
import pandas as pd
|
| 13 |
+
from dagster import AssetExecutionContext, asset
|
| 14 |
+
from imblearn.over_sampling import SMOTE
|
| 15 |
+
from sklearn.model_selection import train_test_split
|
| 16 |
+
from sklearn.preprocessing import StandardScaler
|
| 17 |
+
|
| 18 |
+
from mlops_pipeline.config import (
|
| 19 |
+
FEATURE_COLS,
|
| 20 |
+
MODELS_DIR,
|
| 21 |
+
ORIG_CSV,
|
| 22 |
+
PLUS_CSV,
|
| 23 |
+
TARGET_COL,
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
@asset(group_name="data")
|
| 28 |
+
def raw_data(context: AssetExecutionContext) -> pd.DataFrame:
|
| 29 |
+
"""Load the original 999-row Computer_Durability.csv."""
|
| 30 |
+
df = pd.read_csv(ORIG_CSV)
|
| 31 |
+
context.log.info(f"Loaded raw data: {df.shape}, positives={df[TARGET_COL].sum()}")
|
| 32 |
+
context.add_output_metadata(
|
| 33 |
+
{
|
| 34 |
+
"num_rows": int(len(df)),
|
| 35 |
+
"num_positives": int(df[TARGET_COL].sum()),
|
| 36 |
+
"positive_rate": float(df[TARGET_COL].mean()),
|
| 37 |
+
"columns": df.columns.tolist(),
|
| 38 |
+
}
|
| 39 |
+
)
|
| 40 |
+
return df
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
@asset(group_name="data")
|
| 44 |
+
def augmented_data(context: AssetExecutionContext) -> pd.DataFrame:
|
| 45 |
+
"""Load the 3000-row Computer_Durability_Plus.csv (original + 2000 synthetic)."""
|
| 46 |
+
df = pd.read_csv(PLUS_CSV)
|
| 47 |
+
context.log.info(f"Loaded augmented data: {df.shape}, positives={df[TARGET_COL].sum()}")
|
| 48 |
+
context.add_output_metadata(
|
| 49 |
+
{
|
| 50 |
+
"num_rows": int(len(df)),
|
| 51 |
+
"num_positives": int(df[TARGET_COL].sum()),
|
| 52 |
+
"positive_rate": float(df[TARGET_COL].mean()),
|
| 53 |
+
"synthetic_rows": int(len(df) - 999),
|
| 54 |
+
}
|
| 55 |
+
)
|
| 56 |
+
return df
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
@asset(group_name="data")
|
| 60 |
+
def train_test_split_asset(
|
| 61 |
+
context: AssetExecutionContext,
|
| 62 |
+
augmented_data: pd.DataFrame,
|
| 63 |
+
) -> dict[str, Any]:
|
| 64 |
+
"""
|
| 65 |
+
Stratified 80/20 split, then SMOTE on the training set.
|
| 66 |
+
Returns a dict with X_train, X_test, y_train, y_test as numpy arrays
|
| 67 |
+
plus the fitted StandardScaler.
|
| 68 |
+
"""
|
| 69 |
+
X = augmented_data[FEATURE_COLS].values
|
| 70 |
+
y = augmented_data[TARGET_COL].values
|
| 71 |
+
|
| 72 |
+
X_train, X_test, y_train, y_test = train_test_split(
|
| 73 |
+
X, y, test_size=0.2, random_state=42, stratify=y
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
# Scale numeric features (fit only on train)
|
| 77 |
+
scaler = StandardScaler()
|
| 78 |
+
X_train_scaled = scaler.fit_transform(X_train)
|
| 79 |
+
X_test_scaled = scaler.transform(X_test)
|
| 80 |
+
|
| 81 |
+
# SMOTE: oversample minority class on train set only
|
| 82 |
+
smote = SMOTE(random_state=42, k_neighbors=5)
|
| 83 |
+
X_train_res, y_train_res = smote.fit_resample(X_train_scaled, y_train)
|
| 84 |
+
|
| 85 |
+
context.log.info(
|
| 86 |
+
f"Train (after SMOTE): {X_train_res.shape}, pos={y_train_res.sum()}"
|
| 87 |
+
)
|
| 88 |
+
context.log.info(
|
| 89 |
+
f"Test (no SMOTE): {X_test_scaled.shape}, pos={y_test.sum()}"
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
# Persist scaler for serving
|
| 93 |
+
MODELS_DIR.mkdir(parents=True, exist_ok=True)
|
| 94 |
+
import joblib
|
| 95 |
+
joblib.dump(scaler, MODELS_DIR / "scaler.joblib")
|
| 96 |
+
|
| 97 |
+
# Persist feature metadata for serving
|
| 98 |
+
meta = {
|
| 99 |
+
"feature_cols": FEATURE_COLS,
|
| 100 |
+
"target_col": TARGET_COL,
|
| 101 |
+
"scaler_path": str(MODELS_DIR / "scaler.joblib"),
|
| 102 |
+
}
|
| 103 |
+
(MODELS_DIR / "feature_meta.json").write_text(json.dumps(meta, indent=2))
|
| 104 |
+
|
| 105 |
+
context.add_output_metadata(
|
| 106 |
+
{
|
| 107 |
+
"train_rows": int(len(X_train_res)),
|
| 108 |
+
"test_rows": int(len(X_test_scaled)),
|
| 109 |
+
"train_positives_after_smote": int(y_train_res.sum()),
|
| 110 |
+
"test_positives": int(y_test.sum()),
|
| 111 |
+
}
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
return {
|
| 115 |
+
"X_train": X_train_res,
|
| 116 |
+
"X_test": X_test_scaled,
|
| 117 |
+
"y_train": y_train_res,
|
| 118 |
+
"y_test": y_test,
|
| 119 |
+
"feature_cols": FEATURE_COLS,
|
| 120 |
+
}
|
src/mlops_pipeline/assets/evaluation_assets.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Dagster evaluation assets using Evidently 0.7+ API:
|
| 3 |
+
data_quality_report β DataSummaryPreset on augmented_data
|
| 4 |
+
data_drift_report β DataDriftPreset: original vs synthetic cohort
|
| 5 |
+
model_eval_report β ClassificationPreset on test predictions
|
| 6 |
+
All HTML reports saved to reports/.
|
| 7 |
+
"""
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
from typing import Any
|
| 10 |
+
|
| 11 |
+
import joblib
|
| 12 |
+
import numpy as np
|
| 13 |
+
import pandas as pd
|
| 14 |
+
from dagster import AssetExecutionContext, asset
|
| 15 |
+
from evidently import DataDefinition, Dataset
|
| 16 |
+
from evidently.core.datasets import BinaryClassification
|
| 17 |
+
from evidently.core.report import Report
|
| 18 |
+
from evidently.presets import ClassificationPreset, DataDriftPreset, DataSummaryPreset
|
| 19 |
+
|
| 20 |
+
from mlops_pipeline.config import (
|
| 21 |
+
FEATURE_COLS,
|
| 22 |
+
MODELS_DIR,
|
| 23 |
+
NUMERIC_COLS,
|
| 24 |
+
CATEGORICAL_COLS,
|
| 25 |
+
ORIG_CSV,
|
| 26 |
+
PLUS_CSV,
|
| 27 |
+
REPORTS_DIR,
|
| 28 |
+
TARGET_COL,
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def _save_report(snapshot, name: str) -> Path:
|
| 33 |
+
REPORTS_DIR.mkdir(parents=True, exist_ok=True)
|
| 34 |
+
out = REPORTS_DIR / f"{name}.html"
|
| 35 |
+
snapshot.save_html(str(out))
|
| 36 |
+
return out
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def _base_definition() -> DataDefinition:
|
| 40 |
+
return DataDefinition(
|
| 41 |
+
numerical_columns=NUMERIC_COLS,
|
| 42 |
+
categorical_columns=CATEGORICAL_COLS + [TARGET_COL],
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
@asset(group_name="evaluation")
|
| 47 |
+
def data_quality_report(
|
| 48 |
+
context: AssetExecutionContext,
|
| 49 |
+
augmented_data: pd.DataFrame,
|
| 50 |
+
) -> str:
|
| 51 |
+
"""Evidently data summary/quality report on the full augmented dataset."""
|
| 52 |
+
dd = _base_definition()
|
| 53 |
+
ds = Dataset.from_pandas(augmented_data, data_definition=dd)
|
| 54 |
+
report = Report([DataSummaryPreset()])
|
| 55 |
+
snapshot = report.run(ds, None)
|
| 56 |
+
out = _save_report(snapshot, "data_quality")
|
| 57 |
+
context.log.info(f"Data quality report β {out}")
|
| 58 |
+
context.add_output_metadata({"report_path": str(out)})
|
| 59 |
+
return str(out)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
@asset(group_name="evaluation")
|
| 63 |
+
def data_drift_report(
|
| 64 |
+
context: AssetExecutionContext,
|
| 65 |
+
) -> str:
|
| 66 |
+
"""Evidently drift report: original CSV (reference) vs 2000 synthetic rows (current)."""
|
| 67 |
+
orig = pd.read_csv(ORIG_CSV)
|
| 68 |
+
plus = pd.read_csv(PLUS_CSV)
|
| 69 |
+
synth = plus.iloc[len(orig):]
|
| 70 |
+
|
| 71 |
+
dd = _base_definition()
|
| 72 |
+
ref_ds = Dataset.from_pandas(orig, data_definition=dd)
|
| 73 |
+
cur_ds = Dataset.from_pandas(synth, data_definition=dd)
|
| 74 |
+
|
| 75 |
+
report = Report([DataDriftPreset()])
|
| 76 |
+
snapshot = report.run(cur_ds, ref_ds)
|
| 77 |
+
out = _save_report(snapshot, "data_drift")
|
| 78 |
+
context.log.info(f"Data drift report β {out}")
|
| 79 |
+
context.add_output_metadata({"report_path": str(out)})
|
| 80 |
+
return str(out)
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
@asset(group_name="evaluation")
|
| 84 |
+
def model_eval_report(
|
| 85 |
+
context: AssetExecutionContext,
|
| 86 |
+
train_test_split_asset: dict[str, Any],
|
| 87 |
+
best_model_info: dict[str, Any],
|
| 88 |
+
) -> str:
|
| 89 |
+
"""Evidently classification report on the test set using the best model."""
|
| 90 |
+
winner = best_model_info["winner"]
|
| 91 |
+
model_path = MODELS_DIR / ("xgb_tuned.joblib" if winner == "XGBoost" else "rf_baseline.joblib")
|
| 92 |
+
model = joblib.load(model_path)
|
| 93 |
+
|
| 94 |
+
X_test = train_test_split_asset["X_test"]
|
| 95 |
+
y_test = train_test_split_asset["y_test"]
|
| 96 |
+
|
| 97 |
+
y_prob = model.predict_proba(X_test)[:, 1]
|
| 98 |
+
y_pred = (y_prob >= 0.5).astype(int)
|
| 99 |
+
|
| 100 |
+
test_df = pd.DataFrame(X_test, columns=FEATURE_COLS)
|
| 101 |
+
test_df[TARGET_COL] = y_test
|
| 102 |
+
test_df["prediction_proba"] = y_prob
|
| 103 |
+
test_df["prediction_labels"] = y_pred
|
| 104 |
+
|
| 105 |
+
dd = DataDefinition(
|
| 106 |
+
numerical_columns=FEATURE_COLS,
|
| 107 |
+
classification=[
|
| 108 |
+
BinaryClassification(
|
| 109 |
+
target=TARGET_COL,
|
| 110 |
+
prediction_labels="prediction_labels",
|
| 111 |
+
prediction_probas="prediction_proba",
|
| 112 |
+
)
|
| 113 |
+
],
|
| 114 |
+
)
|
| 115 |
+
ds = Dataset.from_pandas(test_df, data_definition=dd)
|
| 116 |
+
report = Report([ClassificationPreset()])
|
| 117 |
+
snapshot = report.run(ds, None)
|
| 118 |
+
out = _save_report(snapshot, "model_evaluation")
|
| 119 |
+
|
| 120 |
+
context.log.info(f"Model eval report β {out}")
|
| 121 |
+
context.add_output_metadata(
|
| 122 |
+
{
|
| 123 |
+
"report_path": str(out),
|
| 124 |
+
"winner_model": winner,
|
| 125 |
+
"test_rows": len(y_test),
|
| 126 |
+
}
|
| 127 |
+
)
|
| 128 |
+
return str(out)
|
src/mlops_pipeline/assets/training_assets.py
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Dagster training assets:
|
| 3 |
+
baseline_rf_model β RandomForest with class_weight='balanced'
|
| 4 |
+
tuned_xgb_model β XGBoost tuned via Optuna (30 trials)
|
| 5 |
+
best_model_info β compare RF vs XGBoost, pick winner
|
| 6 |
+
All runs logged to MLflow.
|
| 7 |
+
"""
|
| 8 |
+
import json
|
| 9 |
+
from typing import Any
|
| 10 |
+
|
| 11 |
+
import mlflow
|
| 12 |
+
import mlflow.sklearn
|
| 13 |
+
import mlflow.xgboost
|
| 14 |
+
import numpy as np
|
| 15 |
+
import optuna
|
| 16 |
+
import xgboost as xgb
|
| 17 |
+
from dagster import AssetExecutionContext, asset
|
| 18 |
+
from sklearn.ensemble import RandomForestClassifier
|
| 19 |
+
from sklearn.metrics import (
|
| 20 |
+
average_precision_score,
|
| 21 |
+
f1_score,
|
| 22 |
+
roc_auc_score,
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
from mlops_pipeline.config import (
|
| 26 |
+
MLFLOW_EXPERIMENT,
|
| 27 |
+
MLFLOW_TRACKING_URI,
|
| 28 |
+
MODELS_DIR,
|
| 29 |
+
)
|
| 30 |
+
from mlops_pipeline.resources import MLflowResource
|
| 31 |
+
|
| 32 |
+
optuna.logging.set_verbosity(optuna.logging.WARNING)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def _eval_metrics(y_true: np.ndarray, y_prob: np.ndarray) -> dict[str, float]:
|
| 36 |
+
y_pred = (y_prob >= 0.5).astype(int)
|
| 37 |
+
return {
|
| 38 |
+
"roc_auc": float(roc_auc_score(y_true, y_prob)),
|
| 39 |
+
"avg_precision": float(average_precision_score(y_true, y_prob)),
|
| 40 |
+
"f1": float(f1_score(y_true, y_pred, zero_division=0)),
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
@asset(group_name="training", required_resource_keys={"mlflow"})
|
| 45 |
+
def baseline_rf_model(
|
| 46 |
+
context: AssetExecutionContext,
|
| 47 |
+
train_test_split_asset: dict[str, Any],
|
| 48 |
+
) -> dict[str, Any]:
|
| 49 |
+
"""Train a RandomForest baseline and log to MLflow."""
|
| 50 |
+
mlflow_res: MLflowResource = context.resources.mlflow
|
| 51 |
+
mlflow_res.set_experiment()
|
| 52 |
+
|
| 53 |
+
X_train = train_test_split_asset["X_train"]
|
| 54 |
+
y_train = train_test_split_asset["y_train"]
|
| 55 |
+
X_test = train_test_split_asset["X_test"]
|
| 56 |
+
y_test = train_test_split_asset["y_test"]
|
| 57 |
+
|
| 58 |
+
with mlflow.start_run(run_name="RandomForest-baseline") as run:
|
| 59 |
+
params = {
|
| 60 |
+
"n_estimators": 300,
|
| 61 |
+
"max_depth": 12,
|
| 62 |
+
"min_samples_leaf": 4,
|
| 63 |
+
"class_weight": "balanced",
|
| 64 |
+
"random_state": 42,
|
| 65 |
+
"n_jobs": -1,
|
| 66 |
+
}
|
| 67 |
+
mlflow.log_params(params)
|
| 68 |
+
|
| 69 |
+
rf = RandomForestClassifier(**params)
|
| 70 |
+
rf.fit(X_train, y_train)
|
| 71 |
+
|
| 72 |
+
y_prob = rf.predict_proba(X_test)[:, 1]
|
| 73 |
+
metrics = _eval_metrics(y_test, y_prob)
|
| 74 |
+
mlflow.log_metrics(metrics)
|
| 75 |
+
|
| 76 |
+
# Log model artifact
|
| 77 |
+
mlflow.sklearn.log_model(rf, artifact_path="model")
|
| 78 |
+
|
| 79 |
+
run_id = run.info.run_id
|
| 80 |
+
context.log.info(f"RF roc_auc={metrics['roc_auc']:.4f} avg_pr={metrics['avg_precision']:.4f}")
|
| 81 |
+
|
| 82 |
+
import joblib
|
| 83 |
+
MODELS_DIR.mkdir(parents=True, exist_ok=True)
|
| 84 |
+
joblib.dump(rf, MODELS_DIR / "rf_baseline.joblib")
|
| 85 |
+
|
| 86 |
+
context.add_output_metadata({"run_id": run_id, **metrics})
|
| 87 |
+
return {"model_name": "RandomForest", "run_id": run_id, "metrics": metrics}
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
@asset(group_name="training", required_resource_keys={"mlflow"})
|
| 91 |
+
def tuned_xgb_model(
|
| 92 |
+
context: AssetExecutionContext,
|
| 93 |
+
train_test_split_asset: dict[str, Any],
|
| 94 |
+
) -> dict[str, Any]:
|
| 95 |
+
"""XGBoost hyperparameter search via Optuna (30 trials), log best run to MLflow."""
|
| 96 |
+
mlflow_res: MLflowResource = context.resources.mlflow
|
| 97 |
+
mlflow_res.set_experiment()
|
| 98 |
+
|
| 99 |
+
X_train = train_test_split_asset["X_train"]
|
| 100 |
+
y_train = train_test_split_asset["y_train"]
|
| 101 |
+
X_test = train_test_split_asset["X_test"]
|
| 102 |
+
y_test = train_test_split_asset["y_test"]
|
| 103 |
+
|
| 104 |
+
scale_pos_weight = float((y_train == 0).sum()) / float((y_train == 1).sum())
|
| 105 |
+
|
| 106 |
+
def objective(trial: optuna.Trial) -> float:
|
| 107 |
+
params = {
|
| 108 |
+
"n_estimators": trial.suggest_int("n_estimators", 100, 500),
|
| 109 |
+
"max_depth": trial.suggest_int("max_depth", 3, 10),
|
| 110 |
+
"learning_rate": trial.suggest_float("learning_rate", 0.01, 0.3, log=True),
|
| 111 |
+
"subsample": trial.suggest_float("subsample", 0.6, 1.0),
|
| 112 |
+
"colsample_bytree": trial.suggest_float("colsample_bytree", 0.5, 1.0),
|
| 113 |
+
"min_child_weight": trial.suggest_int("min_child_weight", 1, 10),
|
| 114 |
+
"gamma": trial.suggest_float("gamma", 0.0, 5.0),
|
| 115 |
+
"reg_alpha": trial.suggest_float("reg_alpha", 1e-8, 1.0, log=True),
|
| 116 |
+
"reg_lambda": trial.suggest_float("reg_lambda", 1e-8, 1.0, log=True),
|
| 117 |
+
"scale_pos_weight": scale_pos_weight,
|
| 118 |
+
"eval_metric": "aucpr",
|
| 119 |
+
"random_state": 42,
|
| 120 |
+
"n_jobs": -1,
|
| 121 |
+
"use_label_encoder": False,
|
| 122 |
+
}
|
| 123 |
+
model = xgb.XGBClassifier(**params)
|
| 124 |
+
model.fit(X_train, y_train, eval_set=[(X_test, y_test)], verbose=False)
|
| 125 |
+
y_prob = model.predict_proba(X_test)[:, 1]
|
| 126 |
+
return average_precision_score(y_test, y_prob)
|
| 127 |
+
|
| 128 |
+
study = optuna.create_study(direction="maximize", sampler=optuna.samplers.TPESampler(seed=42))
|
| 129 |
+
study.optimize(objective, n_trials=30, show_progress_bar=False)
|
| 130 |
+
context.log.info(f"Optuna best avg_pr={study.best_value:.4f} in {len(study.trials)} trials")
|
| 131 |
+
|
| 132 |
+
# Retrain best params on full train set and log to MLflow
|
| 133 |
+
best_params = {
|
| 134 |
+
**study.best_params,
|
| 135 |
+
"scale_pos_weight": scale_pos_weight,
|
| 136 |
+
"eval_metric": "aucpr",
|
| 137 |
+
"random_state": 42,
|
| 138 |
+
"n_jobs": -1,
|
| 139 |
+
"use_label_encoder": False,
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
with mlflow.start_run(run_name="XGBoost-Optuna") as run:
|
| 143 |
+
mlflow.log_params(best_params)
|
| 144 |
+
mlflow.log_param("optuna_n_trials", len(study.trials))
|
| 145 |
+
|
| 146 |
+
xgb_model = xgb.XGBClassifier(**best_params)
|
| 147 |
+
xgb_model.fit(X_train, y_train)
|
| 148 |
+
|
| 149 |
+
y_prob = xgb_model.predict_proba(X_test)[:, 1]
|
| 150 |
+
metrics = _eval_metrics(y_test, y_prob)
|
| 151 |
+
mlflow.log_metrics(metrics)
|
| 152 |
+
|
| 153 |
+
mlflow.xgboost.log_model(xgb_model, artifact_path="model")
|
| 154 |
+
|
| 155 |
+
# Log Optuna study summary
|
| 156 |
+
trials_df = study.trials_dataframe()
|
| 157 |
+
trials_path = MODELS_DIR / "optuna_trials.csv"
|
| 158 |
+
MODELS_DIR.mkdir(parents=True, exist_ok=True)
|
| 159 |
+
trials_df.to_csv(trials_path, index=False)
|
| 160 |
+
mlflow.log_artifact(str(trials_path), artifact_path="optuna")
|
| 161 |
+
|
| 162 |
+
run_id = run.info.run_id
|
| 163 |
+
context.log.info(f"XGB roc_auc={metrics['roc_auc']:.4f} avg_pr={metrics['avg_precision']:.4f}")
|
| 164 |
+
|
| 165 |
+
import joblib
|
| 166 |
+
joblib.dump(xgb_model, MODELS_DIR / "xgb_tuned.joblib")
|
| 167 |
+
|
| 168 |
+
context.add_output_metadata({"run_id": run_id, **metrics})
|
| 169 |
+
return {"model_name": "XGBoost", "run_id": run_id, "metrics": metrics}
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
@asset(group_name="training", required_resource_keys={"mlflow"})
|
| 173 |
+
def best_model_info(
|
| 174 |
+
context: AssetExecutionContext,
|
| 175 |
+
baseline_rf_model: dict[str, Any],
|
| 176 |
+
tuned_xgb_model: dict[str, Any],
|
| 177 |
+
) -> dict[str, Any]:
|
| 178 |
+
"""Pick the best model by avg_precision and register it in MLflow Model Registry."""
|
| 179 |
+
from mlops_pipeline.config import MODEL_REGISTRY_NAME
|
| 180 |
+
|
| 181 |
+
candidates = [baseline_rf_model, tuned_xgb_model]
|
| 182 |
+
best = max(candidates, key=lambda m: m["metrics"]["avg_precision"])
|
| 183 |
+
|
| 184 |
+
context.log.info(
|
| 185 |
+
f"Winner: {best['model_name']} "
|
| 186 |
+
f"avg_pr={best['metrics']['avg_precision']:.4f} "
|
| 187 |
+
f"roc_auc={best['metrics']['roc_auc']:.4f}"
|
| 188 |
+
)
|
| 189 |
+
|
| 190 |
+
# Register in MLflow Model Registry
|
| 191 |
+
mlflow_res: MLflowResource = context.resources.mlflow
|
| 192 |
+
client = mlflow_res.get_client()
|
| 193 |
+
|
| 194 |
+
model_uri = f"runs:/{best['run_id']}/model"
|
| 195 |
+
try:
|
| 196 |
+
client.create_registered_model(MODEL_REGISTRY_NAME)
|
| 197 |
+
except Exception:
|
| 198 |
+
pass # already exists
|
| 199 |
+
|
| 200 |
+
mv = client.create_model_version(
|
| 201 |
+
name=MODEL_REGISTRY_NAME,
|
| 202 |
+
source=model_uri,
|
| 203 |
+
run_id=best["run_id"],
|
| 204 |
+
)
|
| 205 |
+
client.set_registered_model_alias(
|
| 206 |
+
name=MODEL_REGISTRY_NAME,
|
| 207 |
+
alias="champion",
|
| 208 |
+
version=mv.version,
|
| 209 |
+
)
|
| 210 |
+
context.log.info(f"Registered {MODEL_REGISTRY_NAME} v{mv.version} as @champion")
|
| 211 |
+
|
| 212 |
+
result = {
|
| 213 |
+
"winner": best["model_name"],
|
| 214 |
+
"run_id": best["run_id"],
|
| 215 |
+
"registry_version": mv.version,
|
| 216 |
+
"metrics": best["metrics"],
|
| 217 |
+
"all_metrics": {m["model_name"]: m["metrics"] for m in candidates},
|
| 218 |
+
}
|
| 219 |
+
context.add_output_metadata(
|
| 220 |
+
{
|
| 221 |
+
"winner": best["model_name"],
|
| 222 |
+
"avg_precision": best["metrics"]["avg_precision"],
|
| 223 |
+
"roc_auc": best["metrics"]["roc_auc"],
|
| 224 |
+
"registry_version": mv.version,
|
| 225 |
+
}
|
| 226 |
+
)
|
| 227 |
+
|
| 228 |
+
# Write summary JSON for serving
|
| 229 |
+
MODELS_DIR.mkdir(parents=True, exist_ok=True)
|
| 230 |
+
(MODELS_DIR / "best_model_info.json").write_text(json.dumps(result, indent=2))
|
| 231 |
+
|
| 232 |
+
return result
|
src/mlops_pipeline/config.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Central configuration: paths, column names, model registry settings."""
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
|
| 4 |
+
ROOT = Path(__file__).parent.parent.parent # project root: src/mlops_pipeline/ β src/ β root
|
| 5 |
+
|
| 6 |
+
# ββ Data ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 7 |
+
DATA_RAW_DIR = ROOT / "data" / "raw"
|
| 8 |
+
ORIG_CSV = ROOT / "Computer_Durability.csv"
|
| 9 |
+
PLUS_CSV = ROOT / "Computer_Durability_Plus.csv"
|
| 10 |
+
|
| 11 |
+
FEATURE_COLS = [
|
| 12 |
+
"Hours Used Per Day",
|
| 13 |
+
"Cost",
|
| 14 |
+
"User Age",
|
| 15 |
+
"Primary Usage",
|
| 16 |
+
"Brand",
|
| 17 |
+
"Computer Age (Months)",
|
| 18 |
+
]
|
| 19 |
+
TARGET_COL = "Needs Replacement"
|
| 20 |
+
CATEGORICAL_COLS = ["Primary Usage", "Brand"]
|
| 21 |
+
NUMERIC_COLS = [c for c in FEATURE_COLS if c not in CATEGORICAL_COLS]
|
| 22 |
+
|
| 23 |
+
# ββ MLflow ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 24 |
+
MLFLOW_TRACKING_URI = f"file://{ROOT / 'mlruns'}"
|
| 25 |
+
MLFLOW_EXPERIMENT = "computer-durability"
|
| 26 |
+
MODEL_REGISTRY_NAME = "ComputerDurabilityClassifier"
|
| 27 |
+
|
| 28 |
+
# ββ Outputs βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 29 |
+
REPORTS_DIR = ROOT / "reports"
|
| 30 |
+
MODELS_DIR = ROOT / "models"
|
src/mlops_pipeline/definitions.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Dagster Definitions: wires all assets and resources together."""
|
| 2 |
+
from dagster import Definitions, load_assets_from_modules
|
| 3 |
+
|
| 4 |
+
from mlops_pipeline.assets import data_assets, evaluation_assets, training_assets
|
| 5 |
+
from mlops_pipeline.config import MLFLOW_EXPERIMENT, MLFLOW_TRACKING_URI
|
| 6 |
+
from mlops_pipeline.resources import MLflowResource
|
| 7 |
+
|
| 8 |
+
all_assets = load_assets_from_modules(
|
| 9 |
+
[data_assets, training_assets, evaluation_assets]
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
defs = Definitions(
|
| 13 |
+
assets=all_assets,
|
| 14 |
+
resources={
|
| 15 |
+
"mlflow": MLflowResource(
|
| 16 |
+
tracking_uri=MLFLOW_TRACKING_URI,
|
| 17 |
+
experiment_name=MLFLOW_EXPERIMENT,
|
| 18 |
+
),
|
| 19 |
+
},
|
| 20 |
+
)
|
src/mlops_pipeline/resources.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Dagster resources: MLflow client wrapper."""
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
+
import mlflow
|
| 5 |
+
from dagster import ConfigurableResource
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class MLflowResource(ConfigurableResource):
|
| 9 |
+
tracking_uri: str
|
| 10 |
+
experiment_name: str
|
| 11 |
+
|
| 12 |
+
def get_client(self) -> mlflow.MlflowClient:
|
| 13 |
+
mlflow.set_tracking_uri(self.tracking_uri)
|
| 14 |
+
return mlflow.MlflowClient(tracking_uri=self.tracking_uri)
|
| 15 |
+
|
| 16 |
+
def set_experiment(self) -> str:
|
| 17 |
+
mlflow.set_tracking_uri(self.tracking_uri)
|
| 18 |
+
mlflow.set_experiment(self.experiment_name)
|
| 19 |
+
return mlflow.get_experiment_by_name(self.experiment_name).experiment_id
|
uv.lock
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|