Building and Deploying an Image Classifier: Doors vs Windows


In this post, I share how I built my first deep learning project—a simple door vs window image classifier—from collecting data to training a model and finally deploying it online with Gradio and Hugging Face Spaces.
It was my first time experiencing the full end‑to‑end cycle:
Collecting and downloading images
Training a model
Testing it
And deploying it as an interactive web app
Tools and Libraries Used
Before setting up the environment, here’s a quick look at the tools I used for this project:
Python 3.10 – I specifically chose Python 3.10 because some libraries (like fastai) did not yet support 3.11 or higher on Windows.
Jupyter Notebook – An interactive coding environment for writing and running code step by step.
Fastai – High‑level deep learning tools built on top of PyTorch that make it easy and fast to train and fine‑tune models (I used it here to work with a pre‑trained model).
ResNet34 – A popular convolutional neural network architecture, pre‑trained on ImageNet, that served as the backbone for my classifier.
DuckDuckGo Search API – Used to automatically download images for the dataset (doors and windows) directly from the web.
Gradio – A Python library for creating simple web interfaces to interact with models.
Hugging Face Spaces – A free hosting platform where I deployed my trained model with a Gradio interface.
Setting Up the Environment
Before starting with the code, here’s what I did to set up my system on Windows:
Installed Python 3.10
Downloaded Python from python.org and made sure it worked from the command line.Added Python and Pip to PATH
Using Windows PowerShell, I added environment variables so thatpython
andpip
could be run anywhere.Installed Jupyter Notebook
pip install notebook jupyter notebook
This launched Jupyter in my browser, where I wrote and ran all my code step by step.
Notebook Steps
1. Install dependencies
!pip install -q fastai duckduckgo-search requests pillow
import os
2 .Download images
from duckduckgo_search import DDGS
import requests
from pathlib import Path
from PIL import Image
from io import BytesIO
# Configure
categories = ["door", "window"]
output_dir = Path("house")
output_dir.mkdir(exist_ok=True)
ddgs = DDGS()
def download_images(query, max_images=20):
category_dir = output_dir / query
category_dir.mkdir(exist_ok=True)
count = 0
for result in ddgs.images(query, max_results=max_images):
url = result["image"]
try:
resp = requests.get(url, timeout=10)
resp.raise_for_status()
img = Image.open(BytesIO(resp.content)).convert("RGB")
img.save(category_dir / f"{query}_{count}.jpg")
count += 1
print(f"Saved: {query}_{count}.jpg")
except Exception as e:
print(f"Failed {url} -> {e}")
for cat in categories:
download_images(cat, max_images=30)
Creates a folder called house
Downloads ~30 images each for doors and windows using DuckDuckGo image search
Saves them into house/door
and house/window
folders
3.Remove broken images
from fastai.vision.all import verify_images, get_image_files
failed = verify_images(get_image_files(base_path))
failed.map(Path.unlink) # Deletes them
This uses fastai.verify_images
to remove corrupted images that can’t be opened.
4.Build and train the model
from fastai.vision.all import *
from pathlib import Path
import pathlib
pathlib.PosixPath = pathlib.WindowsPath
# Use relative path (no C:/... absolute path)
dls = ImageDataLoaders.from_folder(
Path('house'),
valid_pct=0.1,
item_tfms=Resize(224),
bs=8
)
learn = vision_learner(dls, resnet34, metrics=accuracy)
learn.fine_tune(5)
This is the core training step:
Loads your dataset (with 10% used for validation)
Uses
resnet34
(a pre-trained convolutional neural network)Fine-tunes it for 5 epochs to classify images as door or window
5. Test the model on a new image
from fastai.vision.all import *
img = PILImage.create('house_test/test4.jpg')
pred_class, pred_idx, probs = learn.predict(img)
print(f"Prediction: {pred_class}, Confidence: {probs[pred_idx]:.2f}")
This loads a new image and prints the predicted class (door or window) with confidence.
6.Save the trained model (weights only)
# Save only the trained model weights
learn.save('stage-1')
print("Saved model weights as stage-1.pth")
Normally, learn.export()
is the common way to save a Fastai model.
It creates a .pkl
file that bundles:
The model architecture
The trained weights
The preprocessing steps
so you can directly load it anywhere with load_learner()
.
However, when I tried to deploy on Hugging Face,
I kept hitting Windows path errors caused by the .pkl
.
To avoid these issues, I skipped export entirely and instead used:
learn.save
('stage-1')
, which only stores the weights (stage-1.pth
)During deployment, I recreate the learner architecture in
app.py
and then load these weights instead.
After Training – Preparing for Deployment
Once training is done you will see a models/
folder created automatically by fastai.
Inside it, you now have:
models/
stage-1.pth
This stage-1.pth
file contains the trained weights of your model.
With this file ready, we can now move on to building a simple web app so we can interact with the model visually and prepare it for deployment.
Creating a Web App with Gradio
What is Gradio?
Gradio is a Python library that makes it easy to wrap any machine learning model in a web-based interface.It allows you to:
Upload an image (or other data)
Get predictions instantly
Use the same interface locally or on Hugging Face Spaces for public access
Installing Gradio
Before creating the app, make sure Gradio is installed.
If you already have fastai and torch installed from the training steps, simply run:
pip install gradio
Project Structure
We’ll organize our files so it works both locally and when deployed to Hugging Face:
bashCopyEditmy_app/
│
├── app.py # Gradio app
├── requirements.txt # Dependencies list
└── models/
└── stage-1.pth # The trained model weights
app.py: Loads the model weights and launches the Gradio interface.
requirements.txt: Will contain the list of libraries Hugging Face needs to install (fastai, gradio, torch, torchvision).
models: Folder where your
stage-1.pth
from training goes.
Understanding app.py
(Gradio Interface)
The app.py
script does four main things:
Rebuilds a minimal DataLoaders and Learner
Since we only saved weights (stage-1.pth
), the code first creates a dummyDataLoaders
object with the same class labels (['door', 'window']
) and architecture (resnet34
), then loads the saved weights:pythonCopyEditdls.vocab = ['door','window'] learn = vision_learner(dls, resnet34, metrics=accuracy, loss_func=CrossEntropyLossFlat()) learn.load('stage-1')
Defines a
predict
function
This function takes an uploaded image, resizes it, converts it to a tensor, runs it through the model, and returns the predicted label with confidence.Creates a Gradio interface
pythonCopyEditgr.Interface( fn=predict, inputs=gr.Image(type="pil"), outputs=gr.Label(), title="Door vs Window Classifier" ).launch()
Launches a local web app
Runningpython
app.py
will start a local Gradio server with a browser link where you can test predictions interactively.
Deploying to Hugging Face Spaces
After confirming that the Gradio app runs locally, the next step is to make it available online so anyone can try it instantly.
For this, I used Hugging Face Spaces – a free service that hosts machine learning demos and automatically runs your Gradio app.
Prerequisites
Before deploying, make sure:
Git is installed on your local machine (or use GitHub Desktop).
You have a Hugging Face account.
(Optional but recommended) Set up SSH keys so you can push to Hugging Face without typing your password every time:
Go to your Hugging Face account → Settings → Access Tokens / SSH Keys
Add your public SSH key (generated using
ssh-keygen
).
Steps to Deploy
Create a new Space
Go to Hugging Face Spaces
Click Create new Space, give it a name, choose Gradio as the SDK, and pick public/private.
Clone the Space
Copy the Git URL from the Space page and run:git clone https://huggingface.co/spaces/<username>/<space_name>
or, if using SSH:
git clone git@hf.co:spaces/<username>/<space_name>
Copy your app files
Into the cloned folder, copy:app.py requirements.txt models/stage-1.pth
Commit and push
git add . git commit -m "Initial commit" git push
Au**tomatic build and launch**
Hugging Face will install the requirements, build your Space, and launch the Gradio app.
After a few minutes, your project will be live at a shareable URL.
Key Learnings
Collecting a small dataset from the web
Fine‑tuning a pre‑trained ResNet34 with fastai
Saving model weights (
.pth
) for portabilityBuilding a Gradio interface
Deploying on Hugging Face Spaces
Conclusion
This project took me end‑to‑end: from images to a live AI app.
What started as a simple “door vs window” idea became a great lesson in training, debugging, saving, and deployment.
Try the live demo here:
Door vs Window Classifier
Subscribe to my newsletter
Read articles from Krupa Sawant directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
