Spaces:
Running
Running
| import gradio as gr | |
| import pandas as pd | |
| import numpy as np | |
| import pickle | |
| import os | |
| # Paths for HuggingFace deployment (running from root folder) | |
| MODEL_PATH = "A2/models/champion_model_final.pkl" | |
| DATA_PATH = "A2/A2_dataset.csv" | |
| model = None | |
| FEATURE_NAMES = None | |
| MODEL_METRICS = None | |
| def load_champion_model(): | |
| global model, FEATURE_NAMES, MODEL_METRICS | |
| possible_paths = [ | |
| MODEL_PATH, | |
| "A2/models/champion_model_final.pkl", | |
| "../A2/models/champion_model_final.pkl", | |
| ] | |
| for path in possible_paths: | |
| if os.path.exists(path): | |
| print(f"Loading champion model from {path}") | |
| with open(path, "rb") as f: | |
| artifact = pickle.load(f) | |
| model = artifact["model"] | |
| FEATURE_NAMES = artifact["feature_columns"] | |
| MODEL_METRICS = artifact.get("test_metrics", {}) | |
| print(f"model loaded successfully") | |
| print(f"Features: {len(FEATURE_NAMES)} columns") | |
| print(f"Test R2: {MODEL_METRICS.get('r2', 'N/A')}") | |
| return True | |
| print("champion model not found") | |
| return False | |
| load_champion_model() | |
| # prediction function | |
| def predict_score(*feature_values): | |
| if model is None: | |
| return "Error", "Model not loaded" | |
| # Convert inputs to dataframe with correct feature names | |
| features_df = pd.DataFrame([feature_values], columns=FEATURE_NAMES) | |
| raw_score = model.predict(features_df)[0] | |
| # score to valid range and change to % | |
| score = max(0, min(1, raw_score)) * 100 | |
| if score >= 80: | |
| interpretation = "Excellent, great squat form" | |
| elif score >= 60: | |
| interpretation = "Good, minor improvements needed" | |
| elif score >= 40: | |
| interpretation = "Average, a lot of areas to work on" | |
| else: | |
| interpretation = "Needs work, focus on proper form" | |
| # Create output | |
| r2 = MODEL_METRICS.get('r2', 'N/A') | |
| correlation = MODEL_METRICS.get('correlation', 'N/A') | |
| # Format metrics | |
| r2_str = f"{r2:.4f}" if isinstance(r2, (int, float)) else str(r2) | |
| corr_str = f"{correlation:.4f}" if isinstance(correlation, (int, float)) else str(correlation) | |
| details = f""" | |
| ### Prediction Details | |
| - **Raw Model Output:** {raw_score:.4f} | |
| - **Normalized Score:** {score:.1f}% | |
| - **Assessment:** {interpretation} | |
| ### Model Performance | |
| - **Test R-squared:** {r2_str} | |
| - **Test Correlation:** {corr_str} | |
| *Lower deviation values = better form* | |
| """ | |
| return f"{score:.1f}%", interpretation, details | |
| # load example for tesitng | |
| def load_example(): | |
| if FEATURE_NAMES is None: | |
| return [0.5] * 35 | |
| try: | |
| possible_paths = [ | |
| "Datasets_all/A2_dataset_80.csv", | |
| "A2/A2_dataset.csv", | |
| "../Datasets_all/A2_dataset_80.csv", | |
| ] | |
| df = None | |
| for path in possible_paths: | |
| if os.path.exists(path): | |
| df = pd.read_csv(path) | |
| print(f"Loaded dataset from {path}") | |
| break | |
| # Fallback to GitHub raw URL if no local file found | |
| if df is None: | |
| url = "https://raw.githubusercontent.com/othmanreem/Data-intensive-systems/main/Datasets_all/A2_dataset_80.csv" | |
| print(f"Loading dataset from {url}") | |
| df = pd.read_csv(url) | |
| # Get a random row with only the features we need | |
| available_features = [f for f in FEATURE_NAMES if f in df.columns] | |
| print(f"Found {len(available_features)} matching features") | |
| sample = df[available_features].sample(1).values[0] | |
| # Convert to float list to ensure proper types for Gradio sliders | |
| return [float(x) for x in sample] | |
| except Exception as e: | |
| print(f"Error loading example: {e}") | |
| import traceback | |
| traceback.print_exc() | |
| return [0.5] * len(FEATURE_NAMES) | |
| # create gradio interface | |
| def create_interface(): | |
| if FEATURE_NAMES is None: | |
| return gr.Interface( | |
| fn=lambda: "Model not loaded", | |
| inputs=[], | |
| outputs="text", | |
| title="Error: Model not loaded" | |
| ) | |
| # Create input sliders for features | |
| inputs = [] | |
| for name in FEATURE_NAMES: | |
| slider = gr.Slider( | |
| minimum=0, | |
| maximum=1, | |
| value=0.5, | |
| step=0.01, | |
| label=name.replace("_", " "), | |
| ) | |
| inputs.append(slider) | |
| # Build the interface | |
| description = """ | |
| ## Deep Squat Movement Assessment | |
| **How to use:** | |
| 1. Adjust the sliders to input deviation values (0 = no deviation, 1 = maximum deviation) | |
| 2. Click "Submit" to get your predicted score | |
| 3. Or click "Load Random Example" to test with real data | |
| **Score Interpretation:** | |
| - 80-100%: Excellent form | |
| - 60-79%: Good form | |
| - 40-59%: Average form | |
| - 0-39%: Needs improvement | |
| """ | |
| # features into categories | |
| angle_features = [n for n in FEATURE_NAMES if "Angle" in n] | |
| nasm_features = [n for n in FEATURE_NAMES if "NASM" in n] | |
| time_features = [n for n in FEATURE_NAMES if "Time" in n] | |
| # Get indices for each category | |
| angle_indices = [FEATURE_NAMES.index(f) for f in angle_features] | |
| nasm_indices = [FEATURE_NAMES.index(f) for f in nasm_features] | |
| time_indices = [FEATURE_NAMES.index(f) for f in time_features] | |
| # Create the main interface | |
| with gr.Blocks(title="Deep Squat Assessment") as demo: | |
| gr.Markdown("# Deep Squat Movement Assessment") | |
| gr.Markdown(description) | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| gr.Markdown("### Input Features") | |
| gr.Markdown(f"*{len(FEATURE_NAMES)} features loaded from champion model*") | |
| gr.Markdown("*Deviation values: 0 = perfect, 1 = maximum deviation*") | |
| with gr.Tabs(): | |
| with gr.TabItem(f"Angle Deviations ({len(angle_indices)})"): | |
| for idx in angle_indices: | |
| inputs[idx].render() | |
| with gr.TabItem(f"NASM Deviations ({len(nasm_indices)})"): | |
| for idx in nasm_indices: | |
| inputs[idx].render() | |
| with gr.TabItem(f"Time Deviations ({len(time_indices)})"): | |
| for idx in time_indices: | |
| inputs[idx].render() | |
| with gr.Column(scale=1): | |
| gr.Markdown("### Results") | |
| score_output = gr.Textbox(label="Predicted Score") | |
| interp_output = gr.Textbox(label="Assessment") | |
| details_output = gr.Markdown(label="Details") | |
| with gr.Row(): | |
| submit_btn = gr.Button("Submit", variant="primary") | |
| example_btn = gr.Button("Load Random Example") | |
| clear_btn = gr.Button("Clear") | |
| submit_btn.click( | |
| fn=predict_score, | |
| inputs=inputs, | |
| outputs=[score_output, interp_output, details_output], | |
| ) | |
| example_btn.click( | |
| fn=load_example, | |
| inputs=[], | |
| outputs=inputs | |
| ) | |
| clear_btn.click( | |
| fn=lambda: [0.5] * len(FEATURE_NAMES) + ["", "", ""], | |
| inputs=[], | |
| outputs=inputs + [score_output, interp_output, details_output], | |
| ) | |
| return demo | |
| # Create the interface | |
| demo = create_interface() | |
| if __name__ == "__main__": | |
| demo.launch( | |
| share=False, | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| ) | |