actRecog / src /streamlit_app.py
Fola-lad's picture
expose model load error in UI for diagnosis
b77223e
Raw
History Blame
7.96 kB
import streamlit as st
import numpy as np
import pandas as pd
# ── Constants ──────────────────────────────────────────────────────────────
LABEL_MAP = {
0: "WALKING",
1: "WALKING_UPSTAIRS",
2: "WALKING_DOWNSTAIRS",
3: "SITTING",
4: "STANDING",
5: "LAYING",
}
EXPLANATIONS = {
"LAYING": "Minimal movement detected across all axes with low acceleration magnitude β€” consistent with a stationary horizontal posture.",
"SITTING": "Low dynamic acceleration with a stable gravity component suggests a stationary upright posture with little body movement.",
"STANDING": "Similar to sitting but with slight postural micro-movements. This class is often the hardest to distinguish from sitting.",
"WALKING": "Rhythmic periodic acceleration with peaks on the vertical axis β€” consistent with level walking at normal cadence.",
"WALKING_DOWNSTAIRS": "Downward gravitational shift with higher impact peaks characteristic of descending a staircase.",
"WALKING_UPSTAIRS": "Elevated vertical acceleration effort with upward body displacement β€” consistent with climbing stairs.",
}
# ── Model loader ────────────────────────────────────────────────────────────
@st.cache_resource
def load_model():
try:
import tensorflow as tf
from model_def import FeedForwardNetwork
model = tf.keras.models.load_model(
"model.keras",
custom_objects={"FeedForwardNetwork": FeedForwardNetwork},
)
return model, "ready"
except Exception as e:
return None, f"error: {e}"
# ── Page config ─────────────────────────────────────────────────────────────
st.set_page_config(
page_title="Human Activity Recognition",
page_icon="πŸƒ",
layout="centered"
)
st.title("Human Activity Recognition")
st.markdown(
"Deep learning classifier trained on 561 smartphone sensor features "
"from the [UCI HAR dataset](https://www.kaggle.com/datasets/uciml/human-activity-recognition-with-smartphones). "
"Classifies six daily activities from accelerometer and gyroscope readings."
)
# ── Sidebar ──────────────────────────────────────────────────────────────────
with st.sidebar:
st.header("About")
st.markdown("""
**Dataset:** UCI Human Activity Recognition
**Subjects:** 30 volunteers aged 19–48
**Sensor:** Samsung Galaxy S II (waist-mounted)
**Sampling rate:** 50Hz
**Features:** 561 time + frequency domain features
**Classes:** 6 activities of daily living
""")
st.markdown("---")
st.markdown("**Model performance on test set**")
st.metric("Architecture", "FFN 512β†’256β†’128")
st.metric("Status", "FFN live Β· CNN pending")
st.markdown("---")
st.caption("DAT606 Group Assignment Β· Pan-Atlantic University")
# ── Model status ─────────────────────────────────────────────────────────────
model, model_status = load_model()
if model_status != "ready":
st.warning(f"Model not loaded β€” {model_status}")
# ── Tabs ─────────────────────────────────────────────────────────────────────
tab1, tab2 = st.tabs(["Select a Sample", "Upload Phyphox CSV"])
# ── Tab 1: Sample selector ───────────────────────────────────────────────────
with tab1:
st.subheader("Select a pre-loaded test sample")
st.caption(
"Each sample is one 2.56-second window of sensor data "
"from a test subject the model has never seen during training."
)
try:
samples_df = pd.read_csv("data/samples.csv")
feature_cols = [
c for c in samples_df.columns
if c not in ["Activity", "subject"]
]
sample_labels = [
f"Sample {i+1} β€” {row['Activity']}"
for i, (_, row) in enumerate(samples_df.iterrows())
]
selected = st.selectbox("Choose a sample:", sample_labels)
selected_idx = sample_labels.index(selected)
selected_row = samples_df.iloc[selected_idx]
true_label = selected_row["Activity"]
feature_vector = selected_row[feature_cols].values.astype(np.float32)
col1, col2 = st.columns(2)
with col1:
st.metric("True Activity", true_label)
with col2:
st.metric("Feature count", len(feature_vector))
if st.button("Classify this sample", type="primary"):
if model_status == "no_model":
st.error("Model not loaded β€” cannot predict yet.")
else:
arr = feature_vector.reshape(1, -1)
probs = model.predict(arr, verbose=0)[0]
pred_idx = int(np.argmax(probs))
pred_label = LABEL_MAP[pred_idx]
confidence = float(probs[pred_idx]) * 100
correct = pred_label == true_label
st.markdown("---")
st.subheader("Result")
if correct:
st.success(
f"**{pred_label}** Β· {confidence:.1f}% confidence Β· βœ“ Correct"
)
else:
st.error(
f"**{pred_label}** Β· {confidence:.1f}% confidence Β· "
f"βœ— Incorrect (true: {true_label})"
)
st.markdown(f"_{EXPLANATIONS[pred_label]}_")
st.markdown("**Confidence across all classes**")
chart_data = pd.DataFrame({
"Confidence (%)": [
float(probs[i]) * 100 for i in range(6)
]
}, index=[LABEL_MAP[i] for i in range(6)])
st.bar_chart(chart_data)
except FileNotFoundError:
st.error("Sample data file not found. Add `data/samples.csv` to the repo.")
# ── Tab 2: Phyphox upload (placeholder) ─────────────────────────────────────
with tab2:
st.subheader("Upload Phyphox sensor recording")
st.markdown("""
**How to record your own data:**
1. Install [Phyphox](https://phyphox.org/) on your phone
2. Open the **Acceleration (without g)** and **Gyroscope** experiments
3. Record at least 3 seconds of a single activity
4. Export as CSV and upload below
""")
uploaded_file = st.file_uploader(
"Upload Phyphox CSV export",
type=["csv"],
help="Export from Phyphox as CSV β€” must contain accelerometer and gyroscope columns"
)
if uploaded_file is not None:
st.info(
"Phyphox pipeline coming soon. "
"Feature extraction from raw sensor readings "
"(filtering β†’ jerk β†’ FFT β†’ 561 features) is under development."
)
try:
preview = pd.read_csv(uploaded_file)
st.markdown("**File preview:**")
st.dataframe(preview.head(10))
st.caption(
f"{len(preview)} rows Β· {len(preview.columns)} columns detected"
)
except Exception as e:
st.error(f"Could not read file: {e}")