Slide 1

Slide 1 text

Django for Data Science Will Vincent DjangoCon Europe (April 2025) Deploying Machine Learning Models with Django

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

Image: AlmaBetter.com Image: AlmaBetter.com

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

What is Data Science?

Slide 6

Slide 6 text

What do Data Scientists Do?

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

1,000,000,000,000,000 1 Quadrillion 10^15 Tokens

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

Train a Model

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

Jupyter Notebook - Setup

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

Iris Dataset

Slide 16

Slide 16 text

Iris Dataset

Slide 17

Slide 17 text

requirements.txt pandas~=2.2.3 scikit-learn~=1.6.1

Slide 18

Slide 18 text

Jupyter Notebook # Load libraries import pandas as pd from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score from sklearn.svm import SVC from joblib import dump, load # Load dataset df = pd.read_csv("data/iris.csv") # Extract features (X) and labels (y) X = df[["SepalLengthCm", "SepalWidthCm", "PetalLengthCm", "PetalWidthCm"]].values y = df["Species"].values # Split into training and testing data X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.20, random_state=1 )

Slide 19

Slide 19 text

Jupyter Notebook # Train the model model = SVC(gamma="auto") model.fit(X_train, y_train) # Make predictions on validation dataset predictions = model.predict(X_test) # Evaluate model accuracy accuracy = accuracy_score(y_test, predictions) print(f"Model Accuracy: {accuracy:.2f}") # Print accuracy score # Save model using joblib dump(model, "models/iris.joblib") # Load model using joblib model = load("models/iris.joblib")

Slide 20

Slide 20 text

# Get input from the user try: sepal_length = float(input("Enter SepalLengthCm: ")) sepal_width = float(input("Enter SepalWidthCm: ")) petal_length = float(input("Enter PetalLengthCm: ")) petal_width = float(input("Enter PetalWidthCm: ")) except ValueError: print("Invalid input. Please enter numeric values.") exit() # Make a prediction input_data = [[sepal_length, sepal_width, petal_length, petal_width]] result = model.predict(input_data) print("Prediction:", result[0]) # Iris Classification with SVM

Slide 21

Slide 21 text

Live Demo?

Slide 22

Slide 22 text

Visualize # requirements.txt pandas~=2.2.3 scikit-learn~=1.6.1 seaborn~=0.13.2 matplotlib~=3.10.0 # Import Seaborn and Matplotlib for visualizations import seaborn as sns import matplotlib.pyplot as plt # Pairplot to visualize relationships between features sns.pairplot(df, hue="Species", diag_kind="kde") plt.show()

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

Django Project

Slide 26

Slide 26 text

Gameplan ● Create new Django project ● Load joblib file ● Forms for users to enter predictions -> see results ● Store user info in the database ● Deployment?

Slide 27

Slide 27 text

DjangoForDataScience.com

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

├── django_project │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── manage.py ├── predict │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py └── templates

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

├── django_project │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── iris.joblib # hi there! ├── manage.py ├── predict │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py └── templates

Slide 33

Slide 33 text

URLs # django_project/urls.py from django.contrib import admin from django.urls import path, include # new urlpatterns = [ path("admin/", admin.site.urls), path("", include("predict.urls")), # new ] # predict/urls.py from django.urls import path from .views import predict urlpatterns = [ path("", predict, name="predict"),

Slide 34

Slide 34 text

View + Template # predict/views.py from django.shortcuts import render def predict(request): return render(request, "predict.html") Hello

Slide 35

Slide 35 text

Runserver (again)

Slide 36

Slide 36 text

# predict/views.py import joblib import numpy as np from django.shortcuts import render # Load the trained model model = joblib.load("iris.joblib") def predict(request): prediction = None if request.method == "POST": try: # Get input values from the form sepal_length = float(request.POST.get("sepal_length")) sepal_width = float(request.POST.get("sepal_width")) petal_length = float(request.POST.get("petal_length")) petal_width = float(request.POST.get("petal_width")) # Make a prediction features = np.array( [[sepal_length, sepal_width, petal_length, petal_width]] ) prediction = model.predict(features)[0] except Exception as e: prediction = f"Error: {str(e)}" return render(request, "predict.html", {"prediction": prediction})

Slide 37

Slide 37 text

Iris Prediction body { font-family: Arial, sans-serif; max-width: 400px; margin: 50px auto; text-align: center; } form { display: flex; flex-direction: column; gap: 10px; } input, button { padding: 10px; font-size: 16px; } button { cursor: pointer; background-color: #4CAF50; color: white; border: none; }

Slide 38

Slide 38 text

...

Iris Species Predictor

{% csrf_token %} Predict {% if prediction %}

Prediction: {{ prediction }}

{% endif %}

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

Store User Inputs # predict/views.py ... def predict(request): prediction = None form_data = {} # To store user inputs if request.method == "POST": try: # Store form inputs form_data = { "sepal_length": request.POST.get("sepal_length"), "sepal_width": request.POST.get("sepal_width"), "petal_length": request.POST.get("petal_length"), "petal_width": request.POST.get("petal_width"), } # Convert inputs to float and make a prediction features = np.array([[float(v) for v in form_data.values()]]) prediction = model.predict(features)[0] except Exception as e: prediction = f"Error: {str(e)}" return render( request, "predict.html", {"prediction": prediction, "form_data": form_data}

Slide 42

Slide 42 text

Display User Inputs {% if prediction %}

Prediction: {{ prediction }}

Inputs:

  • Sepal Length: {{ form_data.sepal_length }}
  • Sepal Width: {{ form_data.sepal_width }}
  • Petal Length: {{ form_data.petal_length }}
  • Petal Width: {{ form_data.petal_width }}
] {% endif %}

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

Add a Model to Store Predictions # predict/models.py from django.db import models class IrisPrediction(models.Model): sepal_length = models.FloatField() sepal_width = models.FloatField() petal_length = models.FloatField() petal_width = models.FloatField() prediction = models.CharField(max_length=100) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"{self.prediction} ({self.created_at.strftime('%Y-%m-%d %H:%M:%S')})"

Slide 45

Slide 45 text

# predict/views.py import joblib import numpy as np from django.shortcuts import render from .models import IrisPrediction. # new # Load the trained model model = joblib.load("iris.joblib") def predict(request): prediction = None form_data = {} if request.method == "POST": try: ... # Save prediction to database IrisPrediction.objects.create( sepal_length=form_data["sepal_length"], sepal_width=form_data["sepal_width"], petal_length=form_data["petal_length"], petal_width=form_data["petal_width"], prediction=prediction, )

Slide 46

Slide 46 text

Update Admin # predict/admin.py from django.contrib import admin from .models import IrisPrediction admin.site.register(IrisPrediction)

Slide 47

Slide 47 text

Update Admin

Slide 48

Slide 48 text

Deployment...

Slide 49

Slide 49 text

Deployment Checklist ● configure static files and install WhiteNoise ● add environment variables with environs ● create a .env file and update the .gitignore file ● update DEBUG, ALLOWED_HOSTS, SECRET_KEY, and CSRF_TRUSTED_ORIGINS ● update DATABASES to run PostgreSQL in production and install psycopg ● install Gunicorn as a production WSGI server ● create a Procfile ● update the requirements.txt file ● create a new Heroku project, push the code to it, and start a dyno web process

Slide 50

Slide 50 text

Takeaways ✅ Django is great for deploying ML models (web UI + APIs) ✅ Iris dataset is simple but powerful for learning ML deployment ✅ Try deploying a real-world ML model

Slide 51

Slide 51 text

Questions? @wsvincent.bsky.social @wsvincent -- django_irisml & iris_ml fosstodon.org/@wsvincent @william-s-vincent