Introduction
Image classification is the hello-world of deep learning — and with MobileNetV2, a pretrained model from TensorFlow Hub, it fits in about 60 lines. Upload any photo and the app returns its top-5 guesses from 1,000 categories (dogs, guitars, coffee mugs) with confidence scores. No training, no GPUs, no dataset wrangling: transfer learning means someone else's millions of images are working for you.
This is the heavyweight sibling of the QR scanner — same upload-decode-display flow, but the "decode" step is a neural network forward pass.
Features
Prerequisites
pip install streamlit tensorflow-cpu pillow numpyStep 1: Create the Script
Save as classifier_app.py:
import streamlit as st
import numpy as np
from PIL import Image
st.set_page_config(page_title="Image Classifier", page_icon="🖼️")
st.title("🖼️ Image Classifier — MobileNetV2")
@st.cache_resource
def load_model():
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.applications.mobilenet_v2 import decode_predictions
model = MobileNetV2(weights="imagenet")
return model, decode_predictions
model, decode = load_model()
uploaded = st.file_uploader("Upload an image", type=["png", "jpg", "jpeg"])
min_conf = st.slider("Minimum confidence %", 0, 100, 5)
if uploaded:
img = Image.open(uploaded).convert("RGB")
st.image(img, caption="Your image", width=350)
if st.button("🔍 Classify", type="primary"):
with st.spinner("Thinking..."):
resized = img.resize((224, 224))
arr = np.array(resized, dtype=np.float32)
arr = np.expand_dims(arr, 0)
preds = model.predict(arr, verbose=0)
results = decode(preds, top=5)[0]
st.subheader("Predictions")
shown = 0
for _, name, score in results:
pct = score * 100
if pct >= min_conf:
st.markdown(f"**{name.replace('_', ' ').title()}**")
st.progress(score, text=f"{pct:.1f}%")
shown += 1
if shown == 0:
st.warning("Nothing above your confidence threshold.")Step 2: Run the App
streamlit run classifier_app.pyThe first run downloads the weights (one time, ~14MB). Try a pet photo, a kitchen object, a screenshot of a car — then try something deliberately weird and watch the model's confidence crumble.
How It Works
MobileNetV2 is a convolutional network trained on ImageNet: 1.2M images across 1,000 categories. Preprocessing is minimal but non-negotiable — resize to 224×224 (its trained input size) and scale values the way it expects. Every pixel becomes a float; expand_dims adds the batch dimension because Keras models expect shape (samples, 224, 224, 3).
The output is 1,000 logits converted to a probability distribution — one score per category summing to 1.0. decode_predictions maps indices to human names and returns the top-k. The confidence slider filters that list client-side; a 3% "sea slug" guess is rarely what the user wanted to see.
@st.cache_resource is the performance hero: model loading takes seconds and hundreds of MB, so it must happen once per process, not once per rerun. This is different from cache_data (which caches *values*) — cache_resource caches *objects* like models and connections, the same pattern NLTK's analyzer uses in the sentiment app.
Common Errors & Fixes
pillow-heif.MobileNetV2(weights="imagenet", alpha=0.35) for a 4× smaller variant.Key Concepts
What to Try Next
st.camera_input for live classification.tf-keras-vis).accept_multiple_files=True and a results table, like the file organizer.FAQ
Can I classify custom categories (my own products)?
Not with this model — it only knows ImageNet's 1,000 classes. For custom categories you'd retrain the final layer (fine-tuning) on your own labeled images; MobileNet is actually the standard base for that.
Why CPU instead of GPU?
MobileNet is deliberately small — CPU inference is under a second, which is plenty for an app. tensorflow-cpu also avoids a 2GB CUDA install.
Why 224×224 specifically?
It's the input size the architecture was designed and trained with. Feeding other sizes either errors out or forces a resize — the network's learned filters expect that resolution.