Deploy a Machine Learning Model with Docker

Deploying a Machine Learning model is the last mile problem of AI, and solving it is one of the most valuable skills you can learn. You must have heard about Docker. In this article, I’ll guide you through the process of taking a trained model, wrapping it in a robust API, and then containerize and deploy the Machine Learning model with Docker.

Deploy a Machine Learning Model with Docker: Our Strategy

Before we dive into the code, let’s understand our toolkit and strategy:

  1. We will train a Machine Learning model using Scikit-learn. It will be our brain.
  2. Next, we will use FastAPI. It will create a web API so other applications can communicate with our model using standard HTTP requests, the language of the internet.
  3. Next, we will create a Dockerfile. It will package our model, our API, and all their dependencies into a single, isolated unit that can run anywhere.

In simple terms, we’re taking the model, giving it a voice with FastAPI, and then packaging it in a self-sufficient container with Docker, so anyone can use it.

Step 1: Train and Save Your Model

Create a file named model.py inside a directory (in my case, I am naming my directory “dock”).

This is the part you’re likely most familiar with. The goal here isn’t just to train a model, but to serialize it, saving its learned state to a file:

# model.py

from sklearn.ensemble import GradientBoostingRegressor
from sklearn.datasets import fetch_california_housing
import joblib

def train_and_save_model():    
    print("Loading data and training the model...")

    # 1. Load the California Housing dataset
    housing = fetch_california_housing()
    X, y = housing.data, housing.target

    # 2. Initialize and train the model
    # We're using Gradient Boosting, a powerful ensemble method for regression.
    # The parameters here are just examples; in a real project, you'd tune them.
    model = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1, max_depth=3)
    model.fit(X, y)

    # 3. Save the trained model to a file
    joblib.dump(model, 'housing_model.pkl')

    print("Model trained and saved as housing_model.pkl!")
    print("Feature names:", housing.feature_names) # To help us with testing later

if __name__ == "__main__":
    train_and_save_model()

The key here is joblib.dump(model, ‘housing_model.pkl’). We use joblib (or pickle) to convert our trained Python object into a byte stream that can be saved to a file. This file, housing_model.pkl, now contains all the knowledge our model gained during training. We only need to run this script once. The resulting .pkl file is the asset we’ll deploy.

Step 2: Build the API Endpoint

Now, create a file named main.py. Now we need a way for the outside world to send data to our model and get a prediction back.

This is where FastAPI comes in. It’s incredibly fast and creates interactive documentation for us automatically:

# main.py

from fastapi import FastAPI
import joblib
from pydantic import BaseModel
import numpy as np

# 1. Create a FastAPI app instance
app = FastAPI(title="California Housing Price Predictor")

# 2. Load the trained model
model = joblib.load('housing_model.pkl')

# 3. Define the request body structure using Pydantic
# These are the features our model was trained on.
class HousingFeatures(BaseModel):
    MedInc: float
    HouseAge: float
    AveRooms: float
    AveBedrms: float
    Population: float
    AveOccup: float
    Latitude: float
    Longitude: float

# 4. Define the prediction endpoint
@app.post("/predict", tags=["Predictions"])
async def predict_price(features: HousingFeatures):
    
    # Convert the input data from the request into a NumPy array
    # The order of features must be the same as during training!
    data = np.array([[
        features.MedInc,
        features.HouseAge,
        features.AveRooms,
        features.AveBedrms,
        features.Population,
        features.AveOccup,
        features.Latitude,
        features.Longitude
    ]])

    # Make a prediction
    # The output is a price in units of 100,000 USD
    prediction = model.predict(data)[0]

    # Return the prediction
    return {"predicted_median_value": f"${prediction * 100000:.2f}"}

# Root endpoint
@app.get("/", tags=["General"])
async def read_root():
    return {"message": "Welcome to the California Housing Price Predictor API!"}

Here’s what we are doing in this Python file:

  1. model = joblib.load(…): We load our saved model into memory the moment the API starts.
  2. class HousingFeatures(BaseModel): Here, we’re creating a strict data contract. Our API will automatically validate incoming requests.
  3. @app.post(“/predict”): This decorator tells FastAPI that any POST request to the /predict URL should be handled by the predict_price function.

The function takes the validated features, converts them into the exact NumPy array format our Scikit-Learn model expects, calls model.predict(), and returns the result as a clean JSON object.

Before moving forward, make sure to create a text file named requirements.txt. Write this information in that file:

fastapi
uvicorn[standard]
scikit-learn
joblib
numpy

Step 3: Containerize with Docker

Now, make sure you have Docker installed on your system. You can download it from here.

Once you have installed Docker, create a file named “Dockerfile” (no extension needed). This is where the real deployment happens. A Dockerfile is a set of instructions for building a Docker image. It’s like a recipe for creating our self-contained application environment.

Write this in your Dockerfile:

# Dockerfile

# 1. Use an official Python runtime as a parent image
FROM python:3.9-slim

# 2. Set the working directory inside the container
WORKDIR /app

# 3. Copy the dependencies file and install them
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 4. Copy the rest of the application code into the container
COPY . .

# 5. Expose the port the app runs on
EXPOSE 80

# 6. Define the command to run the application
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]

Notice we copy requirements.txt and run pip install before copying the rest of our code. Docker builds in layers. If our Python code (main.py, model.py) changes but our dependencies (requirements.txt) don’t, Docker will reuse the already-installed layer, making our subsequent builds much faster!

Now, before moving forward, make sure your directory looks like this:

directory of my project on deploying a machine learning model with docker

Final Step: Build and Run

With these four files in a single directory, you are ready to launch. Open your terminal in that directory and run this command:

docker build -t dock .

This command reads your Dockerfile and builds the self-contained image. We’ll tag (-t) it with the name dock. Here, dock is the name of my directory.

That’s it! Your machine learning model is now a live, running API. You can also see it in your Docker application. It will look like this:

model running in docker container

You can now open your browser and navigate to http://localhost:8081/docs. You’ll see FastAPI’s beautiful, interactive API documentation, where you can even test your prediction endpoint directly. It will look like this:

Deployment using Docker
Output using Docker

Final Words

You’ve just gone from being a Machine Learning Engineer to an architect. You’ve taken a static model and transformed it into a dynamic, reliable, and distributable service. This process, Train, Serialize, Build the API, Containerize, is the fundamental workflow of modern MLOps.

I hope you liked this article on how to containerize and deploy a Machine Learning model with Docker. Feel free to ask valuable questions in the comments section below. You can follow me on Instagram for many more resources.

Aman Kharwal
Aman Kharwal

AI/ML Engineer | Published Author. My aim is to decode data science for the real world in the most simple words.

Articles: 2090

Leave a Reply

Discover more from AmanXai by Aman Kharwal

Subscribe now to keep reading and get access to the full archive.

Continue reading