Productionize a Machine Learning Model with a Django API | by GreekDataGuy | Towards Data Science

Previously, I wrote a tutorial on productionizing an ML model with flask.
Flask is lighter weight, but if we needed more functionality, django comes with that included.
I built an API that makes a prediction via sklearn model. Below is my step-by-step code.
This tutorial contains 3 parts:1. Training the ML model2. Building the django application3. Testing the API
This first part can be done in a jupyter notebook.
This is not a tutorial on machine learning. So we’ll train a model on fictional data. That said, it will function like any other sklearn model you could train.
Our model will detect if an animal is a dog, based on the noise the animal makes.
Create fictional data! Within each inner list, the 1st index is the sound of an animal, the 2nd index is a boolean label indicating if the animal is a dog.
data = [    ['woof', 1],    ['bark', 1],    ['ruff', 1],    ['bowwow', 1],    ['roar', 0],    ['bah', 0],    ['meow', 0],    ['ribbit', 0],    ['moo', 0],    ['yip', 0],    ['pika', 0]]
Convert above into lists of features and labels.
X = []y = []for i in data:    X.append( i[0] )    y.append( i[1] )
Fit a vectorizer and transform the features.
from sklearn.feature_extraction.text import CountVectorizervectorizer = CountVectorizer()X_vectorized = vectorizer.fit_transform(X)
Train a linear regression.
from sklearn.linear_model import LinearRegressionimport numpy as npregressor = LinearRegression()regressor.fit(X_vectorized, y)
Now test it out on a few examples.
test_feature = vectorizer.transform(['woof'])prediction = regressor.predict(test_feature)print(prediction)test_feature = vectorizer.transform(['ribbit'])prediction = regressor.predict(test_feature)print(prediction)test_feature = vectorizer.transform(['meoww'])prediction = regressor.predict(test_feature)print(prediction)#=> [1.]#=> [0.]#=> [0.36363636]
Perfect.
Pickle our models into a byte stream so we can store them in the app.
import picklepickl = {    'vectorizer': vectorizer,    'regressor': regressor}pickle.dump( pickl, open( 'models' + ".p", "wb" ) )
Open the command line to the directory where you store your django projects. Create a directory for this application and cd into it.
mkdir DjangoMLAPI && cd DjangoMLAPI
Create a virtual environment and install the required packages.
python3 -m venv envsource env/bin/activatepip install django djangorestframework sklearn numpy
Now create a django project, the directory containing all the code we’re working on. This would also include database configuration and application settings if we needed them.
While a “project” actually represents the application we’re building, django uses the term “app” to refer to a package in a project. Our main package will be called api.
django-admin startproject api
This generates a bunch of boilerplate code required to power our project. It will look like the file tree on the left.
The outer /api is just a folder that contains all our project code.
The inner /api is the main python package for our project.
Next, we’ll generate an “app” inside our project. This will power the machine learning behind our API.
We’ll name this predictor.
cd apipython manage.py startapp predictor
The full directory will now look like the file tree on the left.
What we added here is a folder called /predictor with a number of files inside.
Several of these files aren’t even needed for our use case. We’ll get back to deleting them at the end of the tutorial.
apps.py is where we’ll define our config class. This is code that will only run once (instead of on every request) so we’ll eventually place the code to load our models there.
views.py will contain code that runs on every request. So we put vectorization and regression logic there.
Now let’s add this app to toINSTALLED_APPS. Open /api/api/settings.py and add 'predictor' to INSTALLED_APPS. It should look like below.
INSTALLED_APPS = [    'django.contrib.admin',    'django.contrib.auth',    'django.contrib.contenttypes',    'django.contrib.sessions',    'django.contrib.messages',    'django.contrib.staticfiles',    'predictor']
Now create a folder called /models inside /predictor. Move your trained pickled models into this directory.
Note that in a real production environment, I’d decouple this from the app (possibly with S3) so we don’t need to re-deploy the app every time we update the model. But here we’ll just include the model in the app.
Add this line to settings as well. We’ll use this to load our model.
MODELS = os.path.join(BASE_DIR, 'predictor/models')
Now write the code that loads our models when the application starts. Inside /api/predictor/apps.py use this code.
from django.apps import AppConfigfrom django.conf import settingsimport osimport pickleclass PredictorConfig(AppConfig):    # create path to models    path = os.path.join(settings.MODELS, 'models.p')     # load models into separate variables    # these will be accessible via this class    with open(path, 'rb') as pickled:       data = pickle.load(pickled)    regressor = data['regressor']    vectorizer = data['vectorizer']
Now create a view that supports our regression logic. Open /api/predictor/views.py and update with this code.
from django.shortcuts import renderfrom .apps import PredictorConfigfrom django.http import JsonResponsefrom rest_framework.views import APIViewclass call_model(APIView):    def get(self,request):        if request.method == 'GET':            # get sound from request            sound = request.GET.get('sound')               # vectorize sound            vector = PredictorConfig.vectorizer.transform([sound])            # predict based on vector            prediction = PredictorConfig.regressor.predict(vector)[0]            # build response            response = {'dog': prediction}            # return response            return JsonResponse(response)
Set up routing, mapping URLs to views in /api/api/urls.py .
from django.urls import pathfrom predictor import viewsurlpatterns = [    path('classify/', views.call_model.as_view())]
You can delete the following files because we don’t need them.
api/predictor/tests.pyapi/predictor/models.pyapi/predictor/admin.py
Now start the server.
python manage.py runserver
And make a couple curl requests to test it out. You could also directly input the URLs into the browser.
curl -X GET http://127.0.0.1:8000/classify/?sound=meow#=> {"dog": 0.0}curl -X GET http://127.0.0.1:8000/classify/?sound=woof#=> {"dog": 1.0}
Easy peazy! It’s working! A number close to 1 indicates it’s a dog and a number close to 0 indicates it’s not a dog.
There we have it. A django API that loads and runs a trained machine learning model!