szili2011's picture
Update app.py
0164c97 verified
raw
history blame
4.42 kB
# app.py - FINAL POLISHED version with forgiving input clamping
import gradio as gr
import joblib
import pandas as pd
import os
import subprocess
# --- STEP 1: Download Model Files ---
MODEL_URL = "https://huggingface.co/szili2011/ai-house-price-predictor/resolve/main/housing_model.joblib"
COLUMNS_URL = "https://huggingface.co/szili2011/ai-house-price-predictor/resolve/main/model_columns.joblib"
MODEL_LOCAL_PATH = "housing_model.joblib"
COLUMNS_LOCAL_PATH = "model_columns.joblib"
if not os.path.exists(MODEL_LOCAL_PATH):
print("--- Downloading Model Files ---")
subprocess.run(["wget", "-O", MODEL_LOCAL_PATH, MODEL_URL])
subprocess.run(["wget", "-O", COLUMNS_LOCAL_PATH, COLUMNS_URL])
print("--- Download Complete ---")
# --- STEP 2: Load Model and Columns ---
try:
model = joblib.load(MODEL_LOCAL_PATH)
model_columns = joblib.load(COLUMNS_LOCAL_PATH)
print("✅ Model and columns loaded successfully.")
model_loaded_successfully = True
except Exception as e:
print(f"❌ CRITICAL ERROR during model loading: {e}")
model_loaded_successfully = False
# --- Core prediction function with the new, forgiving clamping logic ---
def predict_price(sqft, bedrooms, house_age, condition, year_sold, interest_rate, region, sub_type, style, has_garage, has_pool):
if not model_loaded_successfully:
raise gr.Error("Model is not loaded. Please check the Space logs for errors.")
# --- NEW: Forgiving Input Clamping ---
# Instead of raising an error, we gently guide extreme inputs back into a reasonable range.
# The min() function takes the smaller of two numbers, the max() function takes the larger.
# This "clamps" the value between a minimum and a maximum.
# Clamp square footage between 300 and 30,000
sqft_clamped = max(300, min(sqft, 30000))
# Clamp bedrooms between 1 and 20
bedrooms_clamped = max(1, min(bedrooms, 20))
# Clamp house age between 0 and 200
house_age_clamped = max(0, min(house_age, 200))
# Clamp year sold between 2000 and 2030
year_sold_clamped = max(2000, min(year_sold, 2030))
# Clamp interest rate between 0 and 25
interest_rate_clamped = max(0.0, min(interest_rate, 25.0))
# We now use these "safe" clamped values for the prediction.
input_data = {
'SquareFootage': sqft_clamped,
'Bedrooms': bedrooms_clamped,
'HouseAge': house_age_clamped,
'PropertyCondition': condition,
'HasGarage': has_garage,
'HasPool': has_pool,
'YearSold': year_sold_clamped,
'InterestRate': interest_rate_clamped,
'Region': region,
'SubType': sub_type,
'ArchitecturalStyle': style
}
input_df = pd.DataFrame([input_data])
input_processed = pd.get_dummies(input_df)
final_input = input_processed.reindex(columns=model_columns, fill_value=0)
predicted_price = model.predict(final_input)[0]
return f"${predicted_price:,.0f}"
# --- Gradio Interface with UI limits ---
# The UI limits still provide a good first line of guidance for the user.
demo = gr.Interface(
fn=predict_price,
inputs=[
gr.Number(label="Square Footage", value=2500, minimum=300, maximum=30000),
gr.Number(label="Bedrooms", value=4, minimum=1, maximum=20),
gr.Number(label="House Age (years)", value=15, minimum=0, maximum=200),
gr.Slider(label="Property Condition", minimum=1, maximum=10, step=1, value=8),
gr.Number(label="Year Sold", value=2024, minimum=2000, maximum=2030),
gr.Number(label="Interest Rate (%)", value=5.5, minimum=0, maximum=25),
gr.Radio(['Sunbelt', 'Pacific Northwest', 'Rust Belt', 'New England', 'Mountain West'], label="Region", value="Sunbelt"),
gr.Radio(['Urban', 'Suburban', 'Rural', 'Historic District'], label="Sub-Type", value="Suburban"),
gr.Radio(['Modern', 'Ranch', 'Colonial', 'Craftsman', 'Victorian'], label="Architectural Style", value="Colonial"),
gr.Checkbox(label="Has Garage?", value=True),
gr.Checkbox(label="Has Pool?", value=False)
],
outputs=gr.Textbox(label="Predicted Price"),
title="AI House Price Predictor",
description="Describe a property, and our AI will estimate its market value. The model is robust and will provide estimates even for extreme values by capping them to its known limits."
)
# Launch the app
demo.launch()