Download ml/model_predictor.py from xplorers/UPDRS_API: direct link, hf CLI and curl.
- Browser
- Download file 2.92 kB
-
https://huggingface.co/spaces/xplorers/UPDRS_API/resolve/main/ml/model_predictor.py
- Command line
-
hf download hf://spaces/xplorers/UPDRS_API/ml/model_predictor.py
-
curl -L -o model_predictor.py https://huggingface.co/spaces/xplorers/UPDRS_API/resolve/main/ml/model_predictor.py
2.92 kB
| import joblib | |
| import os | |
| import numpy as np | |
| import pandas as pd | |
| from huggingface_hub import hf_hub_download | |
| MODEL_REPO = "xplorers/parkinsons-updrs-model" | |
| MODEL_DIR = "/tmp/models" | |
| # In-memory cache — models are loaded once at startup, reused for every request | |
| _cache = {} | |
| def _download_and_cache(): | |
| """Download models from HF Hub and cache in memory. Runs once at startup.""" | |
| if _cache: | |
| return # already loaded | |
| os.makedirs(MODEL_DIR, exist_ok=True) | |
| filenames = ["ensemble_model.pkl", "feature_names.pkl", "scaler.pkl"] | |
| for filename in filenames: | |
| hf_hub_download( | |
| repo_id=MODEL_REPO, | |
| filename=filename, | |
| repo_type="model", | |
| local_dir=MODEL_DIR, | |
| token=os.getenv("HF_TOKEN"), # needed if your HF model repo is private | |
| ) | |
| _cache["model"] = joblib.load(os.path.join(MODEL_DIR, "ensemble_model.pkl")) | |
| _cache["feature_names"] = joblib.load(os.path.join(MODEL_DIR, "feature_names.pkl")) | |
| _cache["scaler"] = joblib.load(os.path.join(MODEL_DIR, "scaler.pkl")) | |
| print("Models loaded into memory from HF Hub.") | |
| def predict_parkinson(features: dict) -> float: | |
| """ | |
| Predict Parkinson's motor UPDRS score from patient features. | |
| Uses cached models — no disk read on each request. | |
| """ | |
| try: | |
| model = _cache["model"] | |
| feature_names = _cache["feature_names"] | |
| scaler = _cache["scaler"] | |
| print(f"Expected features: {feature_names}") | |
| print(f"Received features: {list(features.keys())}") | |
| missing_features = [name for name in feature_names if name not in features] | |
| if missing_features: | |
| raise ValueError(f"Missing required features: {missing_features}") | |
| for name in feature_names: | |
| value = features[name] | |
| if pd.isna(value) or np.isinf(value): | |
| print(f"Warning: Feature '{name}' has invalid value: {value}, replacing with 0.0") | |
| features[name] = 0.0 | |
| input_values = [features[name] for name in feature_names] | |
| input_df = pd.DataFrame([input_values], columns=feature_names) | |
| scaled_features = scaler.transform(input_df) | |
| updrs_prediction = model.predict(scaled_features)[0] | |
| return float(updrs_prediction) | |
| except KeyError: | |
| raise RuntimeError("Models not loaded. Ensure startup lifespan ran successfully.") | |
| except Exception as e: | |
| raise Exception(f"Prediction error: {e}") | |
| def get_required_features(): | |
| try: | |
| return list(_cache["feature_names"]) | |
| except KeyError: | |
| return [ | |
| 'age', 'sex', 'test_time', 'Jitter(%)', 'Jitter(Abs)', 'Jitter:RAP', | |
| 'Jitter:PPQ5', 'Jitter:DDP', 'Shimmer', 'Shimmer(dB)', 'Shimmer:APQ3', | |
| 'Shimmer:APQ5', 'Shimmer:APQ11', 'Shimmer:DDA', 'NHR', 'HNR', | |
| 'RPDE', 'DFA', 'PPE' | |
| ] |