Monday, September 7, 2026
HomeData ScienceModel Deployment with Streamlit – Build ML Web Apps in Python 2026

Model Deployment with Streamlit – Build ML Web Apps in Python 2026

Table of Content

Streamlit turns Python scripts into interactive web apps in minutes — no HTML, CSS, or JavaScript required. For data scientists, it is the fastest way to deploy a model demo, build an internal analytics tool, or share exploratory analysis interactively. This guide builds a complete ML web app from scratch and deploys it to the cloud.

Getting Started

pip install streamlit scikit-learn pandas plotly

# Run any script as a Streamlit app
streamlit run app.py
# Opens automatically at http://localhost:8501
# app.py — your first Streamlit app
import streamlit as st
import pandas as pd
import numpy as np

st.set_page_config(page_title='DataExpertise ML App',
                   page_icon='📊', layout='wide')

st.title('📊 Data Science Dashboard')
st.markdown('An interactive ML app built with Streamlit')

# Sidebar for controls
with st.sidebar:
    st.header('⚙️ Settings')
    n_samples = st.slider('Sample size', 100, 5000, 1000)
    noise     = st.slider('Noise level', 0.0, 2.0, 0.5)

# Main content
col1, col2, col3 = st.columns(3)
col1.metric('Total Samples', f'{n_samples:,}', delta='+100 vs last')
col2.metric('Accuracy', '94.2%', delta='+2.1%')
col3.metric('AUC-ROC', '0.97', delta='+0.03')

# Data display
df = pd.DataFrame(np.random.randn(n_samples, 3),
                  columns=['Feature A', 'Feature B', 'Target'])
st.dataframe(df.head(20), use_container_width=True)

st.download_button('Download Data', df.to_csv(index=False),
                   'data.csv', 'text/csv')

Building a Complete ML App

a computer screen with a logo on it
Photo by Lautaro Andreani on Unsplash
import streamlit as st
import pandas as pd
import numpy as np
import joblib
import plotly.express as px
import plotly.graph_objects as go
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix
import io

# ── Train and cache model ─────────────────────────────────────
@st.cache_resource   # cached across sessions — only trains once
def load_model():
    data    = load_iris(as_frame=True)
    X, y    = data.data, data.target
    X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=42)
    model   = RandomForestClassifier(n_estimators=100, random_state=42)
    model.fit(X_tr, y_tr)
    return model, X_te, y_te, data.target_names

@st.cache_data   # cache data computations
def get_feature_importance(model, feature_names):
    return pd.DataFrame({
        'Feature':    feature_names,
        'Importance': model.feature_importances_
    }).sort_values('Importance', ascending=False)

model, X_te, y_te, class_names = load_model()

# ── Page layout ───────────────────────────────────────────────
st.set_page_config(page_title='Iris Classifier', layout='wide')
st.title('🌸 Iris Flower Classifier')

tab1, tab2, tab3 = st.tabs(['🔮 Predict', '📊 Model Performance', '📁 Batch Predict'])

# ── Tab 1: Single prediction ──────────────────────────────────
with tab1:
    st.subheader('Enter flower measurements')
    col1, col2 = st.columns(2)

    with col1:
        sepal_length = st.number_input('Sepal Length (cm)', 4.0, 8.0, 5.4)
        sepal_width  = st.number_input('Sepal Width (cm)',  2.0, 5.0, 3.4)
    with col2:
        petal_length = st.number_input('Petal Length (cm)', 1.0, 7.0, 1.3)
        petal_width  = st.number_input('Petal Width (cm)',  0.1, 3.0, 0.2)

    if st.button('🔮 Predict Species', type='primary'):
        features = np.array([[sepal_length, sepal_width,
                               petal_length, petal_width]])
        pred     = model.predict(features)[0]
        proba    = model.predict_proba(features)[0]

        st.success(f'**Predicted Species: {class_names[pred]}**')

        # Probability chart
        fig = px.bar(x=class_names, y=proba,
                     labels={'x': 'Species', 'y': 'Probability'},
                     color=proba, color_continuous_scale='viridis',
                     title='Prediction Confidence')
        fig.update_layout(showlegend=False)
        st.plotly_chart(fig, use_container_width=True)

# ── Tab 2: Model performance ──────────────────────────────────
with tab2:
    col1, col2 = st.columns(2)

    with col1:
        st.subheader('Feature Importance')
        feat_df = get_feature_importance(model, load_iris().feature_names)
        fig = px.bar(feat_df, x='Importance', y='Feature', orientation='h',
                     color='Importance', color_continuous_scale='blues')
        st.plotly_chart(fig, use_container_width=True)

    with col2:
        st.subheader('Confusion Matrix')
        y_pred = model.predict(X_te)
        cm     = confusion_matrix(y_te, y_pred)
        fig    = px.imshow(cm, text_auto=True, x=class_names, y=class_names,
                           color_continuous_scale='blues',
                           labels={'x': 'Predicted', 'y': 'Actual'})
        st.plotly_chart(fig, use_container_width=True)

    st.subheader('Classification Report')
    report = classification_report(y_te, y_pred,
                                   target_names=class_names, output_dict=True)
    st.dataframe(pd.DataFrame(report).T.round(3), use_container_width=True)

# ── Tab 3: Batch prediction ───────────────────────────────────
with tab3:
    st.subheader('Upload CSV for Batch Predictions')
    st.info('CSV must have columns: sepal length (cm), sepal width (cm), '
            'petal length (cm), petal width (cm)')

    uploaded = st.file_uploader('Choose a CSV file', type='csv')

    if uploaded:
        df_upload = pd.read_csv(uploaded)
        st.write(f'Loaded {len(df_upload)} rows')
        st.dataframe(df_upload.head(), use_container_width=True)

        if st.button('Run Batch Prediction', type='primary'):
            preds = model.predict(df_upload)
            probs = model.predict_proba(df_upload)
            df_upload['Predicted Species'] = [class_names[p] for p in preds]
            df_upload['Confidence']        = probs.max(axis=1).round(4)

            st.success(f'Predictions complete for {len(df_upload)} rows')
            st.dataframe(df_upload, use_container_width=True)

            csv = df_upload.to_csv(index=False)
            st.download_button('Download Predictions', csv,
                               'predictions.csv', 'text/csv')

Session State for Interactivity

import streamlit as st

# Session state persists values across reruns
if 'history' not in st.session_state:
    st.session_state.history = []

if 'counter' not in st.session_state:
    st.session_state.counter = 0

if st.button('Add Prediction to History'):
    st.session_state.history.append({'id': st.session_state.counter,
                                      'result': 'setosa'})
    st.session_state.counter += 1

if st.session_state.history:
    st.write(f'History ({len(st.session_state.history)} predictions):')
    st.dataframe(pd.DataFrame(st.session_state.history))

if st.button('Clear History'):
    st.session_state.history = []
    st.rerun()

Deploying to Streamlit Community Cloud

3D render of cloud computing concept
Photo by Growtika on Unsplash
# 1. Push your code to GitHub
# Make sure your repo includes:
# - app.py
# - requirements.txt

# requirements.txt
streamlit>=1.35.0
scikit-learn>=1.5.0
pandas>=2.2.0
numpy>=1.26.0
plotly>=5.22.0
joblib>=1.4.0

# 2. Go to share.streamlit.io
# 3. Click "New app"
# 4. Select your GitHub repo and branch
# 5. Set main file path to app.py
# 6. Click Deploy — live in 2 minutes, free

Performance Tips

Use @st.cache_resource for models and database connections (cached once, shared across all users). Use @st.cache_data for data loading and computation (cached per unique inputs). Avoid reloading data on every user interaction — cache it. Use st.spinner() for long operations to show progress. Use st.empty() for dynamically updating content without page refresh. Limit plotly figures to under 10,000 data points for fast rendering.

Conclusion

Streamlit removes the barrier between a working Python model and a shareable web app. In a single afternoon you can go from a trained scikit-learn model to a deployed interactive app accessible to anyone with a browser. The caching system (cache_resource and cache_data) handles performance. Session state handles interactivity. And Streamlit Community Cloud makes deployment free. For data scientists who want their work to actually get used — Streamlit is the fastest path from insight to impact.

Leave feedback about this

  • Rating

Durgesh Kekare
Durgesh Kekarehttps://www.dataexpertise.in
Durgesh Kekare is a data science educator and founder of DataExpertise.in. With expertise in Python, machine learning, and analytics, he helps 10,000+ learners break into data careers.

Latest Posts

List of Categories