import gradio as gr import pandas as pd import numpy as np import pickle import json import tensorflow as tf from tensorflow.keras.models import model_from_json import plotly.graph_objects as go from plotly.subplots import make_subplots import os # Set environment variable to avoid oneDNN warnings os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0' # Load model artifacts def load_model_artifacts(): try: # Load from the same directory where training code saved artifacts with open('model_architecture.json', 'r') as json_file: model_json = json_file.read() model = model_from_json(model_json) model.load_weights('final_model.h5') with open('scaler.pkl', 'rb') as f: scaler = pickle.load(f) with open('metadata.json', 'r') as f: metadata = json.load(f) return model, scaler, metadata except Exception as e: raise Exception(f"Error loading model artifacts: {str(e)}") # Initialize model components try: model, scaler, metadata = load_model_artifacts() feature_names = metadata['feature_names'] # Get feature names from metadata print(f"✅ Model loaded successfully with features: {feature_names}") except Exception as e: print(f"❌ Error loading model: {e}") model, scaler, metadata = None, None, {} feature_names = ['Feature_1', 'Feature_2'] # Fallback if metadata not available def predict_student_eligibility(*args): try: if model is None or scaler is None: return "Model not loaded", "N/A", "N/A", create_error_plot() # Create input dictionary with correct feature names input_data = {feature_names[i]: args[i] for i in range(len(feature_names))} input_df = pd.DataFrame([input_data]) # Ensure columns are in correct order input_df = input_df[feature_names] # Scale and reshape input input_scaled = scaler.transform(input_df) input_reshaped = input_scaled.reshape(input_scaled.shape[0], input_scaled.shape[1], 1) probability = float(model.predict(input_reshaped)[0][0]) prediction = "Eligible" if probability > 0.5 else "Not Eligible" confidence = abs(probability - 0.5) * 2 fig = create_prediction_viz(probability, prediction, input_data) return prediction, f"{probability:.4f}", f"{confidence:.4f}", fig except Exception as e: return f"Error: {str(e)}", "N/A", "N/A", create_error_plot() def create_error_plot(): fig = go.Figure() fig.add_annotation( text="Model not available or error occurred", xref="paper", yref="paper", x=0.5, y=0.5, xanchor='center', yanchor='middle', showarrow=False, font=dict(size=20) ) fig.update_layout( xaxis={'visible': False}, yaxis={'visible': False}, height=400 ) return fig def create_prediction_viz(probability, prediction, input_data): try: fig = make_subplots( rows=2, cols=2, subplot_titles=('Prediction Probability', 'Confidence Meter', 'Input Features', 'Probability Distribution'), specs=[[{"type": "indicator"}, {"type": "indicator"}], [{"type": "bar"}, {"type": "scatter"}]] ) # Prediction probability gauge fig.add_trace( go.Indicator( mode="gauge+number", value=probability, title={'text': "Eligibility Probability"}, gauge={ 'axis': {'range': [None, 1]}, 'bar': {'color': "darkblue"}, 'steps': [ {'range': [0, 0.5], 'color': "lightcoral"}, {'range': [0.5, 1], 'color': "lightgreen"} ], 'threshold': { 'line': {'color': "red", 'width': 4}, 'thickness': 0.75, 'value': 0.5 } } ), row=1, col=1 ) # Confidence meter confidence = abs(probability - 0.5) * 2 fig.add_trace( go.Indicator( mode="gauge+number", value=confidence, title={'text': "Prediction Confidence"}, gauge={ 'axis': {'range': [None, 1]}, 'bar': {'color': "orange"}, 'steps': [ {'range': [0, 0.3], 'color': "lightcoral"}, {'range': [0.3, 0.7], 'color': "lightyellow"}, {'range': [0.7, 1], 'color': "lightgreen"} ] } ), row=1, col=2 ) # Input features bar chart features = list(input_data.keys()) values = list(input_data.values()) fig.add_trace( go.Bar( x=features, y=values, name="Input Values", marker_color="skyblue", text=values, textposition='auto' ), row=2, col=1 ) # Probability distribution fig.add_trace( go.Scatter( x=[0, 1], y=[probability, probability], mode='lines+markers', name="Probability", line=dict(color="red", width=3), marker=dict(size=10) ), row=2, col=2 ) fig.update_layout( height=800, showlegend=False, title_text="Student Eligibility Prediction Dashboard", title_x=0.5, margin=dict(l=50, r=50, t=100, b=50) ) # Update x-axis for probability plot fig.update_xaxes(title_text="", row=2, col=2, range=[-0.1, 1.1]) fig.update_yaxes(title_text="Probability", row=2, col=2, range=[0, 1]) return fig except Exception as e: return create_error_plot() def batch_predict(file): try: if model is None or scaler is None: return "Model not loaded. Please check if all model files are uploaded.", None if file is None: return "Please upload a CSV file.", None df = pd.read_csv(file) # Check for required features missing_features = set(feature_names) - set(df.columns) if missing_features: return f"Missing features: {', '.join(missing_features)}", None # Ensure correct column order df_features = df[feature_names] df_scaled = scaler.transform(df_features) df_reshaped = df_scaled.reshape(df_scaled.shape[0], df_scaled.shape[1], 1) probabilities = model.predict(df_reshaped).flatten() predictions = ["Eligible" if p > 0.5 else "Not Eligible" for p in probabilities] results_df = df.copy() results_df['Probability'] = probabilities results_df['Prediction'] = predictions results_df['Confidence'] = np.abs(probabilities - 0.5) * 2 output_file = "batch_predictions.csv" results_df.to_csv(output_file, index=False) eligible_count = predictions.count('Eligible') not_eligible_count = predictions.count('Not Eligible') summary = f"""Batch Prediction Summary: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 📊 Total predictions: {len(results_df)} ✅ Eligible: {eligible_count} ({eligible_count / len(predictions) * 100:.1f}%) ❌ Not Eligible: {not_eligible_count} ({not_eligible_count / len(predictions) * 100:.1f}%) 📈 Average Probability: {np.mean(probabilities):.4f} 🎯 Average Confidence: {np.mean(np.abs(probabilities - 0.5) * 2):.4f} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Results saved to: {output_file} """ return summary, output_file except Exception as e: return f"Error processing file: {str(e)}", None # Gradio UI with gr.Blocks(theme=gr.themes.Soft()) as demo: gr.Markdown("# 🎓 Student Eligibility Prediction") gr.Markdown("This app predicts student eligibility based on academic performance metrics.") with gr.Tabs(): with gr.Tab("📝 Single Prediction"): with gr.Row(): with gr.Column(): inputs = [gr.Number(label=feature, value=75) for feature in feature_names] predict_btn = gr.Button("Predict", variant="primary") with gr.Column(): prediction = gr.Textbox(label="Prediction") probability = gr.Textbox(label="Probability") confidence = gr.Textbox(label="Confidence") plot = gr.Plot() predict_btn.click( predict_student_eligibility, inputs=inputs, outputs=[prediction, probability, confidence, plot] ) with gr.Tab("📁 Batch Prediction"): gr.Markdown("Upload a CSV file with student data to get batch predictions.") with gr.Row(): with gr.Column(): file_input = gr.File( label="Upload CSV", file_types=[".csv"], type="filepath" ) batch_btn = gr.Button("Process Batch", variant="primary") with gr.Column(): batch_output = gr.Textbox(label="Results", lines=10) download = gr.File(label="Download Predictions") batch_btn.click( batch_predict, inputs=file_input, outputs=[batch_output, download] ) # Footer gr.Markdown("---") gr.Markdown("> Note: This model was trained on student eligibility data. Ensure your input features match the training data format.") # Launch app if __name__ == "__main__": demo.launch()