smart_ai_hack / app.py
tejas2110's picture
changes
79bf915 verified
# app.py
# This is the file Hugging Face looks for automatically.
# It runs your Gradio interface and exposes the API.
import gradio as gr
import numpy as np
import pandas as pd
import joblib
import os
# ── Load all models (Hugging Face runs this once on startup) ──────────────────
print("Loading models...")
reg_model = joblib.load("model1_regression.pkl")
cls_model = joblib.load("model1_classifier.pkl")
m1_features = joblib.load("model1_features.pkl")
forecast_6h = joblib.load("model2_forecast_6h.pkl")
forecast_12h = joblib.load("model2_forecast_12h.pkl")
m2_features = joblib.load("model2_features.pkl")
print("All models loaded.")
STATUS_NAMES = {0: "🟒 GREEN β€” Empty", 1: "🟑 YELLOW β€” Filling", 2: "πŸ”΄ RED β€” Full"}
STATUS_ACTION = {
0: "No action needed",
1: "Monitor β€” schedule collection soon",
2: "DISPATCH TRUCK IMMEDIATELY"
}
# ── Shared feature builder ────────────────────────────────────────────────────
def build_row(ultrasonic, weight, fill_now, hour_of_day, day_of_week,
bin_type, location, zone,
fill_rate_1h=0, fill_rate_3h=0, fill_rate_6h=0,
rolling_fill_3h=None, rolling_fill_6h=None,
rolling_fill_12h=None, rolling_fill_24h=None,
hours_since_collection=24, week_number=0):
rolling_fill_3h = rolling_fill_3h or fill_now
rolling_fill_6h = rolling_fill_6h or fill_now
rolling_fill_12h = rolling_fill_12h or fill_now
rolling_fill_24h = rolling_fill_24h or fill_now
hours_to_full = max(0, min(72,
(80 - fill_now) / fill_rate_1h if fill_rate_1h > 0.01 else 72
))
fill_acceleration = fill_rate_1h - (fill_rate_3h / 3 if fill_rate_3h else 0)
return {
"ultrasonic": ultrasonic,
"weight": weight,
"sensor_ratio": weight / (ultrasonic + 1e-5),
"fill_percent": fill_now,
"fill_rate_1h": fill_rate_1h,
"fill_rate_3h": fill_rate_3h,
"fill_rate_6h": fill_rate_6h,
"fill_rate_12h": 0,
"fill_acceleration": fill_acceleration,
"rolling_fill_3h": rolling_fill_3h,
"rolling_fill_6h": rolling_fill_6h,
"rolling_fill_12h": rolling_fill_12h,
"rolling_fill_24h": rolling_fill_24h,
"rolling_weight_3h": weight,
"hour_of_day": hour_of_day,
"day_of_week": day_of_week,
"week_number": week_number,
"is_weekend": int(day_of_week >= 5),
"is_rush_hour": int(hour_of_day in [7,8,9,12,13,17,18,19,20]),
"is_night": int(hour_of_day in [0,1,2,3,4,5]),
"sin_hour": np.sin(2 * np.pi * hour_of_day / 24),
"cos_hour": np.cos(2 * np.pi * hour_of_day / 24),
"sin_day": np.sin(2 * np.pi * day_of_week / 7),
"cos_day": np.cos(2 * np.pi * day_of_week / 7),
"hours_since_collection": hours_since_collection,
"hours_to_full": hours_to_full,
"bin_type_residential":int(bin_type == "residential"),
"bin_type_commercial": int(bin_type == "commercial"),
"location_urban": int(location == "urban"),
"location_suburban": int(location == "suburban"),
"location_mall": int(location == "mall"),
"zone_north": int(zone == "north"),
"zone_south": int(zone == "south"),
"zone_east": int(zone == "east"),
"zone_west": int(zone == "west"),
"zone_central": int(zone == "central"),
}
def safe_predict(model, features, row_dict):
df = pd.DataFrame([row_dict])
for col in features:
if col not in df.columns:
df[col] = 0
return model.predict(df[features])
# ── PREDICTION FUNCTION 1: Current fill status ────────────────────────────────
def predict_current_status(
ultrasonic, weight, fill_now,
hour_of_day, day_of_week,
bin_type, location, zone,
fill_rate_1h, hours_since_collection
):
try:
row = build_row(
ultrasonic=ultrasonic, weight=weight, fill_now=fill_now,
hour_of_day=int(hour_of_day), day_of_week=int(day_of_week),
bin_type=bin_type, location=location, zone=zone,
fill_rate_1h=fill_rate_1h,
hours_since_collection=int(hours_since_collection)
)
fill_pred = float(np.clip(safe_predict(reg_model, m1_features, row)[0], 0, 100))
status_idx = int(safe_predict(cls_model, m1_features, row)[0])
proba = cls_model.predict_proba(
pd.DataFrame([row])[[f for f in m1_features if f in row or True]]
)[0]
# Fix proba dataframe
df_row = pd.DataFrame([row])
for col in m1_features:
if col not in df_row.columns:
df_row[col] = 0
proba = cls_model.predict_proba(df_row[m1_features])[0]
confidence = float(proba.max()) * 100
result = f"""
## πŸ“Š Current Bin Status
| Metric | Value |
|--------|-------|
| **Predicted Fill** | {fill_pred:.1f}% |
| **Status** | {STATUS_NAMES[status_idx]} |
| **Action** | {STATUS_ACTION[status_idx]} |
| **Confidence** | {confidence:.1f}% |
### Probability Breakdown
- 🟒 GREEN (Empty): {proba[0]*100:.1f}%
- 🟑 YELLOW (Filling): {proba[1]*100:.1f}%
- πŸ”΄ RED (Full): {proba[2]*100:.1f}%
{"⚠️ **DISPATCH TRUCK NOW**" if status_idx == 2 else "βœ… No immediate action required"}
"""
return result
except Exception as e:
return f"❌ Error: {str(e)}"
# ── PREDICTION FUNCTION 2: Flow forecast ─────────────────────────────────────
def predict_forecast(
ultrasonic, weight, fill_now,
hour_of_day, day_of_week,
bin_type, location, zone,
fill_rate_1h, fill_rate_3h
):
try:
row = build_row(
ultrasonic=ultrasonic, weight=weight, fill_now=fill_now,
hour_of_day=int(hour_of_day), day_of_week=int(day_of_week),
bin_type=bin_type, location=location, zone=zone,
fill_rate_1h=fill_rate_1h, fill_rate_3h=fill_rate_3h
)
df_row = pd.DataFrame([row])
for col in m2_features:
if col not in df_row.columns:
df_row[col] = 0
df_row = df_row[m2_features]
p6h = float(np.clip(forecast_6h.predict(df_row)[0], 0, 100))
p12h = float(np.clip(forecast_12h.predict(df_row)[0], 0, 100))
# Urgency
if p6h >= 80:
urgency = "πŸ”΄ HIGH β€” Collect within 6 hours"
elif p12h >= 80:
urgency = "🟑 MEDIUM β€” Collect within 12 hours"
else:
urgency = "🟒 LOW β€” No collection needed soon"
bar_now = "β–ˆ" * int(fill_now / 5) + "β–‘" * (20 - int(fill_now / 5))
bar_6h = "β–ˆ" * int(p6h / 5) + "β–‘" * (20 - int(p6h / 5))
bar_12h = "β–ˆ" * int(p12h / 5) + "β–‘" * (20 - int(p12h / 5))
result = f"""
## πŸ“ˆ Fill Level Forecast
| Time | Predicted Fill | Bar |
|------|---------------|-----|
| **Now** | {fill_now:.1f}% | `{bar_now}` |
| **+6 hours** | {p6h:.1f}% | `{bar_6h}` |
| **+12 hours** | {p12h:.1f}% | `{bar_12h}` |
### πŸš› Collection Urgency
**{urgency}**
### For Route Optimization
```
bin_fill_now: {fill_now:.1f}%
bin_fill_6h: {p6h:.1f}%
bin_fill_12h: {p12h:.1f}%
dispatch_urgent: {str(p6h >= 80).lower()}
```
"""
return result
except Exception as e:
return f"❌ Error: {str(e)}"
# ── BUILD GRADIO UI ────────────────────────────────────────────────────────────
with gr.Blocks(title="Smart Bin AI") as demo:
gr.Markdown("""
# πŸ—‘οΈ Smart Bin AI β€” Waste Management Intelligence
**Model 1:** Predict current fill level (GREEN / YELLOW / RED)
**Model 2:** Forecast fill level 6h and 12h into the future
> Built with Random Forest + Gradient Boosting on 72,000 sensor readings
""")
# ── TAB 1: Current Status ─────────────────────────────────────────────────
with gr.Tab("πŸ“‘ Current Fill Status (Model 1)"):
gr.Markdown("### Enter live sensor readings to get current bin status")
with gr.Row():
with gr.Column():
ultra1 = gr.Slider(4, 65, value=30, label="Ultrasonic Distance (cm) β€” lower = more full")
weight1 = gr.Slider(0, 36, value=15, label="Weight (kg)")
fill1 = gr.Slider(0, 100, value=50, label="Current Fill % (from previous reading)")
rate1 = gr.Slider(0, 10, value=1.0, label="Fill Rate per hour (%/hr)", step=0.1)
hours_sc = gr.Slider(0, 72, value=24, label="Hours Since Last Collection")
with gr.Column():
hour1 = gr.Slider(0, 23, value=12, label="Hour of Day (0–23)", step=1)
day1 = gr.Slider(0, 6, value=1, label="Day of Week (0=Mon, 6=Sun)", step=1)
btype1 = gr.Dropdown(["residential", "commercial"], value="commercial", label="Bin Type")
loc1 = gr.Dropdown(["urban", "suburban", "mall"], value="urban", label="Location")
zone1 = gr.Dropdown(["north","south","east","west","central"], value="central", label="Zone")
btn1 = gr.Button("πŸ” Predict Current Status", variant="primary", size="lg")
output1 = gr.Markdown()
btn1.click(
fn=predict_current_status,
inputs=[ultra1, weight1, fill1, hour1, day1, btype1, loc1, zone1, rate1, hours_sc],
outputs=output1
)
gr.Examples(
examples=[
[55, 2.5, 8, 9, 1, "residential", "suburban", "north", 0.3, 48],
[32, 15, 50, 13, 2, "commercial", "urban", "central", 2.0, 24],
[8, 30, 90, 18, 4, "commercial", "urban", "central", 4.0, 6],
],
inputs=[ultra1, weight1, fill1, hour1, day1, btype1, loc1, zone1, rate1, hours_sc],
label="Try these examples"
)
# ── TAB 2: Flow Forecast ──────────────────────────────────────────────────
with gr.Tab("πŸ“ˆ Fill Forecast (Model 2)"):
gr.Markdown("### Predict how full this bin will be in 6 and 12 hours")
with gr.Row():
with gr.Column():
ultra2 = gr.Slider(4, 65, value=30, label="Ultrasonic Distance (cm)")
weight2 = gr.Slider(0, 36, value=15, label="Weight (kg)")
fill2 = gr.Slider(0, 100, value=50, label="Current Fill %")
rate1h = gr.Slider(0, 10, value=1.5, label="Fill Rate last 1h (%/hr)", step=0.1)
rate3h = gr.Slider(0, 30, value=4.0, label="Fill Rate last 3h (total)", step=0.1)
with gr.Column():
hour2 = gr.Slider(0, 23, value=12, label="Hour of Day", step=1)
day2 = gr.Slider(0, 6, value=1, label="Day of Week", step=1)
btype2 = gr.Dropdown(["residential","commercial"], value="commercial", label="Bin Type")
loc2 = gr.Dropdown(["urban","suburban","mall"], value="urban", label="Location")
zone2 = gr.Dropdown(["north","south","east","west","central"], value="central", label="Zone")
btn2 = gr.Button("πŸ“ˆ Forecast Fill Level", variant="primary", size="lg")
output2 = gr.Markdown()
btn2.click(
fn=predict_forecast,
inputs=[ultra2, weight2, fill2, hour2, day2, btype2, loc2, zone2, rate1h, rate3h],
outputs=output2
)
gr.Examples(
examples=[
[52, 4, 15, 7, 1, "residential", "suburban", "north", 0.3, 0.8],
[30, 16, 55, 11, 2, "commercial", "urban", "central", 2.8, 7.5],
[12, 27, 78, 17, 4, "commercial", "urban", "central", 3.5, 9.0],
],
inputs=[ultra2, weight2, fill2, hour2, day2, btype2, loc2, zone2, rate1h, rate3h],
label="Try these examples"
)
# ── TAB 3: API Docs for Node.js ───────────────────────────────────────────
with gr.Tab("πŸ”Œ Node.js API Docs"):
gr.Markdown("""
## Using This As a Node.js API
Hugging Face Spaces exposes your Gradio app as a REST API automatically.
Install the client: `npm install @gradio/client`
### Call Model 1 (Current Status)
```javascript
const { Client } = require("@gradio/client");
async function getBinStatus(sensorData) {
const client = await Client.connect("YOUR_HF_USERNAME/smart-bin-ai");
const result = await client.predict("/predict_current_status", {
ultrasonic: sensorData.ultrasonic,
weight: sensorData.weight,
fill_now: sensorData.fill_percent,
hour_of_day: new Date().getHours(),
day_of_week: new Date().getDay(),
bin_type: sensorData.bin_type || "commercial",
location: sensorData.location || "urban",
zone: sensorData.zone || "central",
fill_rate_1h: sensorData.fill_rate || 0,
hours_since_collection: sensorData.hours_since_collection || 24,
});
return result.data;
}
```
### Call Model 2 (Forecast)
```javascript
async function getForecast(sensorData) {
const client = await Client.connect("YOUR_HF_USERNAME/smart-bin-ai");
const result = await client.predict("/predict_forecast", {
ultrasonic: sensorData.ultrasonic,
weight: sensorData.weight,
fill_now: sensorData.fill_percent,
hour_of_day: new Date().getHours(),
day_of_week: new Date().getDay(),
bin_type: sensorData.bin_type || "commercial",
location: sensorData.location || "urban",
zone: sensorData.zone || "central",
fill_rate_1h: sensorData.fill_rate_1h || 0,
fill_rate_3h: sensorData.fill_rate_3h || 0,
});
return result.data;
}
```
### Replace `YOUR_HF_USERNAME` with your actual Hugging Face username.
### Replace `smart-bin-ai` with your Space name if you chose a different one.
""")
gr.Markdown("""
---
Built for Smart Waste Management | Random Forest (Model 1) + Gradient Boosting (Model 2)
| 72,000 training rows | GroupKFold validated
""")
# Launch
demo.launch(server_name="0.0.0.0", server_port=7860)