Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Build an image classifier with fastai, export it for inference, wrap it in a Gradio upload-and-predict app, then deploy the demo to Hugging Face Spaces. This guide uses a folder-based custom dataset and a pretrained ResNet; it also shows how to evaluate the model, test the exported file, and avoid common deployment and security problems. A public demo is not automatically suitable for confidential images or production workloads.

What an image classifier does

An image classifier assigns an image to one of a set of labels defined before training. A binary classifier chooses between two labels; a multiclass classifier chooses one label from three or more. For example, a cat-versus-dog model is binary, while a model that chooses cat, dog, or rabbit is multiclass.

Use multilabel classification when one image can legitimately have several labels at once, such as a photograph tagged both “beach” and “sunset.” A standard multiclass classifier assumes the labels are mutually exclusive.

Classification predicts what is present in the image as a whole. Object detection locates individual objects with boxes; segmentation assigns labels to pixels; image similarity or search retrieves images resembling a query. Choose the task that matches the result you need.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A classifier can be confidently wrong. Its output scores describe the model’s relative preference among its labels, not guaranteed correctness. Reliability depends on representative images, correct labels, a sound train/validation split, and evaluation against the conditions in which the app will be used.

Why build it with fastai?

fastai provides a high-level API over PyTorch for common vision workflows: loading and transforming images, using pretrained models for transfer learning, training, prediction, and interpretation. Its reusable pattern is to create DataLoaders, create a Learner, fit the model, then make predictions. See the fastai documentation and computer-vision quick start.

That convenience does not remove the need for careful dataset design or evaluation. Use fastai when transfer learning and its standard data and training abstractions fit the task. Consider raw PyTorch when the model, training loop, deployment format, or distributed setup needs more control.

Install the environment

Use a virtual environment so the project’s packages do not interfere with other Python projects. The following is a starting point, not a version-pinned reproduction; fastai, PyTorch, torchvision, and Gradio compatibility can change.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
python -m venv .venv
source .venv/bin/activate        # macOS/Linux
# .venvScriptsactivate         # Windows

python -m pip install --upgrade pip
pip install fastai gradio pillow

For GPU training, install the PyTorch build appropriate for your operating system and CUDA setup first, then install fastai. The fastai installation documentation recommends this order. A GPU is not required for every small training run or for a modest inference demo.

Record the Python version and installed packages while developing:

python --version
pip freeze > requirements-lock.txt

Use a simpler requirements.txt for deployment, containing the runtime dependencies:

fastai
gradio
pillow

Once you have run and tested the project, replace broad package names with the exact versions that worked, for example fastai==…, gradio==…, and pillow==…. Do not treat untested pins as known-compatible versions.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Prepare the dataset

For a beginner-friendly custom dataset, place images in one directory per class. Here is a three-class example:

data/
├── cats/
│   ├── cat001.jpg
│   └── cat002.jpg
├── dogs/
│   ├── dog001.jpg
│   └── dog002.jpg
└── rabbits/
    ├── rabbit001.jpg
    └── rabbit002.jpg

Before training, check that paths and file extensions are expected, images can be opened, and each folder contains only its intended class. Keep class names stable and human-readable. Display a sample from each class and verify the labels rather than assuming a directory or filename convention is correct.

Prevent data leakage when creating validation data. Near-duplicates should not land on opposite sides of the split. If images come from the same video, person, patient, product, or session, keep related images together where possible; otherwise the validation score may reward recognition of a source or subject rather than generalization. Document the dataset license and confirm you have the right to use the images.

For a reproducible example, fastai’s official quick start uses the Oxford-IIIT Pet Dataset, which contains 7,349 images across 37 breeds. The walkthrough below follows its cat-versus-not-cat labeling pattern; a custom directory dataset can use the same training concepts with a folder-based data loader.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Create the data loaders and train

This example uses the current-style vision_learner API with a pretrained ResNet-34 backbone. The official quick-start page also contains an older cnn_learner example; do not mix API examples blindly across fastai versions. Check the API available in the version installed in your environment.

from fastai.vision.all import *

path = untar_data(URLs.PETS) / "images"

def is_cat(filename):
    return filename.name[0].isupper()

dls = ImageDataLoaders.from_name_func(
    path,
    get_image_files(path),
    valid_pct=0.2,
    seed=42,
    label_func=is_cat,
    item_tfms=Resize(224),
)

learn = vision_learner(
    dls,
    resnet34,
    metrics=error_rate,
)

learn.fine_tune(1)
  • ImageDataLoaders builds the training and validation loaders from image files and labels.
  • valid_pct=0.2 reserves 20% of the data for validation; seed=42 makes the random split reproducible for this setup.
  • Resize(224) standardizes image dimensions for the model.
  • vision_learner creates a vision learner with a pretrained backbone. resnet34 is the selected architecture, not a universal best choice.
  • error_rate reports the fraction of incorrect predictions.
  • fine_tune(1) runs one fine-tuning epoch in this example. It is not a recommendation that one epoch is sufficient for another dataset.

Inspect validation loss and metrics as training proceeds. If you run out of memory, try a smaller batch size or image size, or a smaller backbone. More epochs are not automatically better: watch for validation performance that stops improving or deteriorates.

Evaluate before serving predictions

Overall accuracy or error rate is only a starting point. Review which classes are confused, how often each class is missed, and whether the errors matter equally. A confusion matrix and examples of high-loss predictions help expose issues that a single score can hide.

interp = ClassificationInterpretation.from_learner(learn)
interp.plot_confusion_matrix()
interp.plot_top_losses(9, figsize=(12, 12))

Inspect per-class precision and recall as well, particularly when class counts are imbalanced. Review false positives and false negatives, then test images from the intended real-world environment. A strong score on a small validation set may be misleading if the set contains duplicates, shares subjects or backgrounds with training data, or does not represent deployment conditions. Use ImageClassifierCleaner as an aid to review possible labeling problems, not as an automatic reason to delete difficult examples.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Export the learner and test inference

Export an inference-oriented learner, then load it in a separate process. The exported learner does not include the training items and optimizer state.

learn.export("export.pkl")
from fastai.vision.all import *

learn_inf = load_learner("export.pkl", cpu=True)

img = PILImage.create("test-image.jpg")
pred, pred_idx, probabilities = learn_inf.predict(img)

print("Prediction:", pred)
print("Index:", pred_idx)
print("Confidence:", float(probabilities[pred_idx]))

for label, probability in zip(learn_inf.dls.vocab, probabilities):
    print(label, float(probability))

The result includes the predicted label, its index, and a probability for each vocabulary label. These probabilities are not calibrated certainty unless calibration has been evaluated. Test several known examples, including examples that are difficult or unlike the training images.

learn.save(...) is for saving model weights and optimizer state for resuming or reconstructing training; learn.export(...) creates a learner artifact for inference. For custom model, transform, loss, or data code, ensure the required functions and modules are importable in the deployment environment. The Learner documentation describes export and loading requirements.

Security: load_learner uses Python pickle. Loading a maliciously crafted file can execute code, so load only an artifact you created or obtained from a fully trusted source. If your workflow only needs model weights, consult fastai’s safer loading alternatives, including Learner.load, in the Learner documentation.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Build the Gradio app

Save this as app.py beside export.pkl. The learner loads once when the process starts, rather than once per upload.

import gradio as gr
from fastai.vision.all import *

learn_inf = load_learner("export.pkl", cpu=True)

def classify_image(image):
    if image is None:
        raise gr.Error("Upload an image to get a prediction.")

    image = image.convert("RGB")
    _, _, probabilities = learn_inf.predict(PILImage.create(image))

    return {
        str(label): float(probability)
        for label, probability in zip(learn_inf.dls.vocab, probabilities)
    }

demo = gr.Interface(
    fn=classify_image,
    inputs=gr.Image(type="pil"),
    outputs=gr.Label(num_top_classes=3),
    title="Image Classifier",
    description="Upload an image to see the model's top predictions.",
)

if __name__ == "__main__":
    demo.launch()

gr.Image(type="pil") supplies a PIL image to the function. The returned dictionary maps labels to scores, and gr.Label displays the leading predictions. Component signatures can vary by Gradio version, so validate this code against the version recorded in your deployment requirements. Test it locally with python app.py and try known examples before publishing.

This minimal example relies on Gradio’s image component for input handling. For an app accepting arbitrary files or larger uploads, add explicit file-type and size limits, handle corrupt images, and return a useful validation message. Do not retrain the model in response to a user request.

Deploy the app to Hugging Face Spaces

Keep the application files together in a small project directory:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
image-classifier/
├── app.py
├── export.pkl
├── requirements.txt
└── README.md

From the application directory, run:

gradio deploy

Gradio’s deployment guide describes how this command gathers app metadata, uploads relevant files, and launches the app on Spaces. Follow its prompts, then inspect the Space build logs and test the resulting app. Alternatively, create a Space with the Gradio SDK selected and upload the app, learner artifact, and requirements file manually.

Spaces rebuild when repository changes are pushed. Public Spaces expose the app and its source code to the internet and allow cloning. Hugging Face supports public, protected, and private visibility; protected visibility requires an eligible paid plan. Review the Spaces overview for current visibility and runtime details.

  • The default CPU environment may be adequate for a modest, one-image-at-a-time classifier, but measure the exported model under your expected request pattern.
  • Space disk is not persistent by default; do not rely on local files written at runtime being retained.
  • Store tokens and other credentials in Space settings as secrets, not in app.py or a public repository.
  • A large learner file can lengthen builds and cold starts. Check logs when a build succeeds but startup fails.
  • A Space is an interactive demo host, not automatically an authenticated, rate-limited production API.

Free resources and hardware availability depend on Space type, account status, quotas, and resource use. ZeroGPU has account and quota conditions; consult the current ZeroGPU documentation and Hugging Face pricing rather than assuming a particular resource is always available without charge.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Publish the model separately if useful

The Hugging Face Hub model repository and a Space solve different problems: the repository versions and shares the model, while the Space runs the user interface. Publishing either one does not require publishing the other.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from huggingface_hub import push_to_hub_fastai

push_to_hub_fastai(
    learner=learn,
    repo_id="YOUR_USERNAME/YOUR_MODEL_NAME",
)

To retrieve a published learner:

from huggingface_hub import from_pretrained_fastai

learn_inf = from_pretrained_fastai(
    "YOUR_USERNAME/YOUR_MODEL_NAME"
)

See fastai’s Hub integration documentation and Hugging Face’s fastai guide for current integration details. Review the model card and repository contents before publishing; do not include sensitive training data or metadata.

Choose an interface and hosting approach

Option Best fit Trade-off
Gradio A focused model demo, image upload, and direct Spaces deployment. Not a substitute for authentication, rate limits, or production API operations.
Streamlit A broader Python data app with multiple controls, charts, tables, or explanatory pages. Less direct when the goal is a compact upload-and-predict model widget. See Streamlit deployment options.
FastAPI A backend for programmatic clients needing request validation, authentication, rate limiting, or observability. Requires a separate interface and operational setup rather than a ready-made demo UI.

Likewise, a CPU is a reasonable first inference target for a small model and low request volume. Consider GPU or dedicated inference infrastructure for larger models, batch processing, many concurrent users, or measured latency requirements. Benchmark the actual artifact and traffic pattern before choosing hardware.

Improve the demo without overstating it

You can add clearer class descriptions, example images, and a threshold-based “uncertain” response. A threshold is useful only if it is evaluated on representative data: softmax scores are not inherently calibrated, and a model forced to choose a class may still be wrong on unfamiliar images. An abstention option should make clear that it signals low confidence according to a chosen rule, not proof that an image is outside the model’s scope.

For a production service, separately plan authentication, rate limiting, privacy and retention, monitoring, model versioning, and deployment reliability. A public educational demo should not invite confidential, medical, biometric, or proprietary images unless access, handling, consent, and applicable obligations have been addressed. Use a dedicated API or cloud architecture when those controls or predictable service behavior are requirements.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Troubleshoot common failures

Images fail to open or labels look wrong

  • Check that paths resolve and get_image_files(path) returns the expected number of files.
  • Scan for corrupt images, unsupported formats, or incomplete downloads; repair or remove files that cannot be decoded.
  • Print the vocabulary and inspect labeled examples from every class. Check case-sensitive filename rules, mixed-content folders, hidden files, and unrelated images.

Validation performance seems implausibly high

  • Look for duplicates or related images split across training and validation.
  • Check whether filenames, backgrounds, or capture sources leak the answer.
  • Create group- or source-based splits and evaluate on an external test set representative of deployment images.

The model predicts one class repeatedly

  • Inspect class counts for imbalance and verify labels manually.
  • Review the confusion matrix and compare training, validation, and deployment preprocessing.
  • Test the exported learner locally on known examples to separate training problems from app-loading or input problems.

Exported learner fails to load

  • If an error names a custom function, put shared custom code in an importable module and import it in both training and deployment.
  • Use compatible Python, fastai, and PyTorch versions; retain environment metadata and re-export after major dependency changes.
  • Check the path and filename of export.pkl, and never solve a load error by downloading an untrusted pickle.

The Space builds but crashes or feels slow

  • Inspect Space logs, package versions, import paths, and CPU/GPU device assumptions.
  • Load the learner at startup once, reduce image size or use a smaller backbone if appropriate, and avoid repeated downloads.
  • Measure performance before moving to paid hardware or a dedicated service; investigate a different architecture only when the observed workload requires it.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.