If you’ve worked in a data science or machine learning position, you know that deploying machine learning models isn’t always easy or straightforward. You often need to answer questions like, “Where will the model live?,” “How often will it be run?,” and “How well do you have to maintain it?”

Originally, you might have deployed models to run in batches or with an API call. However, this would often require the setup of an entire API system, which could be a hassle to develop. As a machine learning engineer, you likely had to work with either a DevOps team or software engineers in order to get your whole model deployed, costing you valuable time and forcing your model to become stuck behind other team processes.

With the development of serverless computing, you can avoid much of this technical infrastructure development, as teams can now deploy their machine learning models via serverless technology.

Developing Your First Serverless Machine Learning Model

In this blog post, we’ll use scikit-learn to develop a model. For those unfamiliar with it, scikit-learn is arguably one of the more popular machine learning libraries in Python. This is due to the fact that it not only contains many different options for model development but also many other utility functions that can help ensure your data is set up correctly. These functions include the train_test_split function, which allows you to easily partition your data into testing and training data sets, and LabelEncoder, which allows you to transform categorical data into numbers, making the development of models far easier.

For the time being, we’ll use the linear regression model, as our focus is more on the deployment of the model and less on the model itself. Linear regression is perfect as an example since there are a lot of data sets you can easily use to test your model. Once you understand how to deploy this model, you can try out more complex versions as well. 

Setting Up Your First Machine Learning Function

For this model, you’ll be using the vehicle price data set from Kaggle.com.

This data set lists the purchase and selling price in lacs, so don’t be surprised that cars are going for 5.5 lacs.

But before we dive into things, let’s take a look at the data in general. With the code below, you can file and output the data so that you know what’s going on. The describe function will put out the mean, min, max, and other data points that you might be interested in getting familiar with: 

```
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))
        data=pd.read_csv(os.path.join(dirname, filename))   
data.describe()
 ```

Now, before getting too far into things, you might be interested in looking into the data a bit to better help you develop your model in the future.

One easy place to start is to check out the various aggregations of the data to see if there are any high-level trends, as shown below:

```
data.groupby(‘Seller_Type’)['Selling_Price'].mean()
data.groupby(['Seller_Type','Fuel_Type'])['Selling_Price'].mean()
```

For example, if you look at the average price by Seller_Type, you can see that Dealers have a much higher average. Many things could be driving this value, but it’s an interesting point.

setting a serverless machine learning function

Figure 1: Average selling price of vehicles based on seller type (Source: Jupyter Notebook output)

Similarly, if we look at Transmission and Fuel_Type, you see that automatic vehicles that are diesel-based have the highest price on average. The count of rows is not included here, so this could be throwing the numbers off. For example, if there is only one automatic transmission car, then the average would be based on that one car. But this is just to help you understand the data.

Figure 2: Average selling price of vehicles based on transmission and fuel type (Source: Jupyter Notebook output)

Model Development

To start the model development, you’ll need to split the data up into training and testing sets as well as X and Y variables, as seen below, and use the LabelEncoder function to transform categorical data into numerical data. You’ll also be normalizing the data as part of its processing. 

For now, normalizing the data is used to set up all the data to be from 0-1. This is due to the fact that when you start comparing some of these variables, they are all so different in what they represent. For example, some of these data points represent years, others represent categorical data like transmission type, and still, others represent miles on the car. All of these values are very different, but by normalizing them, you simply range them from 0-1.

Normalizing the data is demonstrated in the Python code below:

from sklearn.feature_selection import f_regression
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
number = LabelEncoder()
x['Transmission']=number.fit_transform(x['Transmission'].astype('str'))
x['Seller_Type']=number.fit_transform(x['Seller_Type'].astype('str'))
x['Fuel_Type']=number.fit_transform(x['Fuel_Type'].astype('str'))
train, test = train_test_split(x, test_size=0.2)
y_test=test.Selling_Price
x_test=test.loc[:,'Kms_Driven':'Owner']
x_test.insert(2,'Year',test.Year)
y_train=train.Selling_Price
x_train=train.loc[:,'Kms_Driven':'Owner']
x_train.insert(2,'Year',train.Year)
from sklearn import preprocessing
x = x_train.values #returns a numpy array
min_max_scaler = preprocessing.MinMaxScaler()
x_scaled = min_max_scaler.fit_transform(x)
x_train = pd.DataFrame(x_scaled)
x = x_test.values #returns a numpy array
min_max_scaler = preprocessing.MinMaxScaler()
x_scaled = min_max_scaler.fit_transform(x)
x_test = pd.DataFrame(x_scaled)

With all of your data set up, you can now develop your model by creating a function to develop a Pipeline via a linear regression model. This is denoted by the f_regression setting.

from sklearn.metrics import mean_squared_error
def create_model():
    model=Pipeline([
        ('scaler',StandardScaler()),
        ('selector',SelectKBest(score_func=f_regression,k=4)),
        ('svr',SVR(C=10,epsilon=0.1,gamma=1.0,kernel='rbf'))
    ])
    return model
model=create_model()
model=create_model()
model.fit(x_train,y_train)
y_pred_test=model.predict(x_test)
print(mean_squared_error(y_test,y_pred_test))

Once you’ve run the model, you can test the output against your test set as depicted in the “print” statement above. One way to do this is by using the mean squared error function, but there are lots of methods you should use when testing your machine learning model.

Each model might have different goals and room for error. Beyond that, each specific business objective might allow for different types of errors. In some cases, you might be OK with some false positives, while in other cases, you might be looking to reduce them.  

For linear regression, you can look at this article to learn more about training and testing.

With your model designed, you can now work on deploying it. The question is, how do you save it? Here, you can use the Joblib library to help run your pipeline. Joblib tools allow the user to develop lightweight workflows in Python. Similar to the one you developed in the model above, you can essentially save the pipeline, compress it to be loaded, and call it later on:

import time
import joblib
model_name='model_1x.joblib' 
joblib.dump(model,model_name, compress=False)

Developing a Predictive Function

Now that the model is ready to go, you can develop a predictive function. In order to do this, you need to take the saved model and load it into your future Lambda function. 

Using the Joblib library, import the model you created, then use it to set up the end-point. In this case, you’ll call the end-point “predict,” i.e., a function that takes an event you can connect to later on using the command line.

The predict then gets all the parameters for the serverless model and outputs the response of said model back to you, as shown below: 

import json
import joblib
model_name ='model_1x.joblib'
model=joblib.load(model_name)
def predict(event, context):
    body={'message':'OK'}
    params = event['queryStringParameters']
    Kms_Driven =float(params['Kms_Driven'])
    Fuel_Type =float(params['Fuel_Type'])
    Year =float(params['Year'])
    Seller_Type =float(params['Seller_Type'])
    Transmission =float(params['Transmission'])
    Owner =float(params['Owner'])
    inputVector =[Kms_Driven,Fuel_Type,Year,Seller_Type,Transmission,Owner]
    data=[inputVector]
    print(data[0])
    predictPrice = model.predict((data))[0]
    predictPrice = round(predictPrice,1)
    body['predictPrice'] = predictPrice
    response={
        'StatusCode':200,
        'body':json.dumps(body),
        'headers':{'Access-Control-Allow-Orgin','*'}
    }
    return response

This will be the eventual model that you’ll push to Lambda. 

Almost Ready To Deploy

We are almost done developing and deploying your first predictive function. To summarize, in Part 1 of our Serverless Machine Learning series, we have gone through the basic steps of developing a model and setting it up to be called by future predictive functions.

In Part 2 of the series, we will outline how you can deploy your predictive model to AWS. Once you’ve done this, you will be able to call the function from anywhere with the correct authorization and without any infrastructure development. All in all, serverless machine learning models can not only simplify your development but also reduce costs.

Read More:

Considerations for the Beginner Serverless Developer

Serverless CI/CD: Best Practices

The Most Popular Serverless Deployment Tools

The 5 Best Use Cases for the Serverless Beginner