This article was published as a part of the Data Science Blogathon.
Deploying is perhaps the second most crucial step in the complete product development life cycle. Deploying models let other members of your organization consume what you have created. For starters, it could appear daunting, but with the right tools, things can be smooth.
In this article, we will create a spam classifier model with pandas, and Scikit learns and serve it with Fast API. Next up, we will containerize our web API with Docker. And finally, deploy it using AWS ECS.
If you are not already familiar with Fast API, then go through this article for a basic understanding of Fast API components such as path parameters, query parameters, etc.
Here’s what we will cover in this article,
In between these topics, we will also cover the fundamentals of each process so as not to make you feel overwhelmed.
So, let’s get started.
The spam classification model classifies different SMS as spam or ham. As you can see, it is a binary classification problem for which you can use any classification model. We will keep the model simple without getting into NLP.
First, create a python virtual environment by typing the following commands in shell.
python -m venv spam_ham_api
We named the virtual environment spam_ham_api.
Install necessary libraries. For this project, we will only need Pandas, Sklearn, Uvicorn, Fastapi, and Joblib.
pip install pandas sklearn uvicorn fastapi joblib
Now, create a python file for model creation.
Import dataset to python environment.
import pandas as pd
#load dataset df = pd.read_csv('path/spam.csv') df.drop(['Unnamed: 2', 'Unnamed: 3', 'Unnamed: 4'], axis=1, inplace=True)
The next step is to clean the data of unnecessary characters, punctuations, and stop-words.
import regex as re from sklearn.feature_extraction.text import ENGLISH_STOP_WORDS def cleanText(text): text = re.sub(r"[W_]",' ',text.lower()).replace('-','') words = [word for word in text.split() if word not in ENGLISH_STOP_WORDS] new_text = " ".join(words) return new_text
We will now import all the libraries and methods we will be using.
from sklearn.pipeline import Pipeline from sklearn.feature_extraction.text import TfidfVectorizer from xgboost import XGBRFClassifier from sklearn.linear_model import LogisticRegression from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import train_test_split
Create TF-IDF vectorizer and Label encoder instances.
tfidf = TfidfVectorizer() encode = LabelEncoder() df['v1'] = encode.fit_transform(df['v1'])
Clean texts
X = df['v2'].apply(cleanText) y = df['v1']
Split train and test data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=39)
If you wish to use multiple models to find the best model, you can do this by using GridsearchCV and Pipeline methods. But first, you will have to create a switcher class.
#switcher class for different estimators class my_classifier(BaseEstimator,): def __init__(self, estimator=None): self.estimator = estimator def fit(self, X, y=None): self.estimator.fit(X,y) return self def predict(self, X, y=None): return self.estimator.predict(X,y) def predict_proba(self, X): return self.estimator.predict_proba(X) def score(self, X, y): return self.estimator.score(X, y)
Specify the parameters you wish to pass for each model.
parameters = [ {'clf':[LogisticRegression(max_iter=1000)], }, {'clf':[RandomForestClassifier()], }, { 'clf':[DecisionTreeClassifier()], }, { 'clf':[XGBRFClassifier()] }, ]
Here, we haven’t specified any other parameters for tuning models. But you can do this by passing clf__paramName in each dictionary above. Visit this article for implementations.
Now create a pipeline,
make_pipeline = Pipeline([('tfidf', tfidf), ('clf', my_classifier())])
Fit model
grid = GridSearchCV(make_pipeline, parameters, cv=5) grid.fit(X_train,y_train)
Predict and accuracy score
y_pred = grid.predict(X_test) accuracy = accuracy_score(y_test, y_pred)
Print outcome
print(f'Accuracy score is {accuracy*100}')
Now, the only thing left is to save the model. So, we will not be executing the process again and again. There are two ways one can do this, either by using pickle or Joblib. We will use joblib.
filename = 'spam_model' from joblib import dump dump(grid, filename)
It will save the model in the same directory as this Python file.
Full code for the model.py
So, the model creation is complete. We shall move on to API creation.
Alright, we are all set to build an API for our model.
Create another python file in the same directory. Import libraries you will use.
from fastapi import FastAPI from joblib import load import regex as re from sklearn.feature_extraction.text import ENGLISH_STOP_WORDS
Initiate the app
app = FastAPI()
Load the model you saved earlier
filename = 'spam_model' model = load(filename)
Define a function for cleaning text
def cleanText(text): text = re.sub(r"[W_]",' ',text.lower()).replace('-','') words = [word for word in text.split() if word not in ENGLISH_STOP_WORDS] new_text = " ".join(words) return new_text
Define a function for classification
def classify(model,text): text = cleanText(text) prediction = model.predict([text])[0] res = 'Ham' if prediction == 0 else 'spam' spam_prob = model.predict_proba([text])[0][1] return {'label': res, 'spam_probability': float(spam_prob)}
Path operation
@app.get('/')
def get_root(): return {'message': 'Welcome to the SMS spam detection API'}
Whenever you run the API, you will receive this dictionary.
@app.get('/spam_detection_path/{message}')
async def detect_spam_path(message: str):
return classify(model, message)
The API will return the label (spam or ham) with spam probability in percentages when you input a message.
Let’s check if the API is working or not.
uvicorn model_API:app --reload
By default, the API will be available on your localhost:8000 port.
Head over to localhost:8000/docs for interactive Sagger GUI.
Complete code for our API
We have finished our API creation. Next, we will see how we can create containers from the docker file for our API.
So, what is Docker? It is one of those words you must have heard from almost anyone in software development. So, Docker is an open-source tool that lets us run software independently. It packages the software into a standardized unit called containers. These containers have everything libraries, code, and system tools necessary for running. It is one of the crucial tools for microservices architecture. Docker can significantly lower the time to scale and upgrade software. It also ensures the software will run similarly on every device.
So, to create a container, we need a Dockerfile. A docker file is a text document that specifies the command line commands to create a docker image.
Note: An image is a file system representing a packaged application with all the dependencies to run separately.
Now, let’s create a docker file for our API.
Assuming you are still in a virtual environment, type
pip freeze > requirements.txt
It will create a requirements text file in the current directory with all the packages you have installed in your virtual environment with their versions.
Now create a text file without a .txt extension and name it Dockerfile. Paste the below commands in your Dockerfile.
FROM python:3.9 # WORKDIR /code # COPY ./requirements.txt /code/requirements.txt # RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt # COPY ./model_API.py /code/model_API.py # COPY ./app/spam_model /code/spam_model # COPY ./spamClassify.py /code/spamClassify.py # CMD ["uvicorn", "model_API:app", "--host", "0.0.0.0"]
Let’s break apart each line.
Now run
docker build -t imageName
The building process will start, and it might take a few moments, depending on internet connections.
Now, run the following commands to create a container
docker run -d --name conatinerName -p 8000:8080 imageName
To view your running containers,
docker container ls
To view all of them,
docker container ls -a
It will show you all the container’s names, IDs, port mappings etc.
For debugging your container, always check its logs.
docker logs containerName
So, here you can see our API is running at 0.0.0.0:8000. Visit the address or run the following code to see if it is running.
curl -v 0.0.0.0:8000
It should return the message of our API.
The Docker hub is a registry for storing container images. The best thing about it is it’s free of cost for casual users. Though for private repositories, you have to shell out a few bucks. There are several alternatives for the docker registry, like AWS ECR, Azure Container Registry, etc. If your entire operation is on AWS or Azure, you may opt for their respective registries, but we will use Docker Hub for this article.
In this section, we will learn how to push container images to Docker Hub from the command line.
First, go to Docker Hub’s official site, create an account, and create a repository to store your images. Then get back to the command line interface.
Now, we need to give a tag to the image we just created. Remember the tag you will
docker tag imageName dockerHubName/repository Name:tag
Check if your image is listed or not.
docker images
Alternatively, you can also build images with the appropriate names and tags.
docker build -t dockerhubname/repositoryname:tag
Log in to your Docker Hub account.
docker login
Type in your username and passwords, and you are now good to go.
Push your image to the hub
docker push dockerhubname/repositoryname:tag
Wait for some time. Depending on the image size and internet connection, it might take a while.
The next step is to deploy our spam detection app on AWS ECS from the docker hub registry.
First, log in to your AWS account.
Search ECS and click on “get started.”
Configure the docker container
In the next step, change the name and add an application load balancer. And click Next.
In this step, name the cluster and leave everything as it is. Then next, review your changes and click on create.
Wait for a few minutes until the launch status is complete.
Go to clusters -> tasks, wait for a few minutes “PENDING” status will turn to “RUNNING.” This implies your app is up and running.
To get the IP address of your app, click on the task link, and you will see the public IP under the network section. Copy and paste it into the browser and plug your port at the end.
For me, it was http://13.232.54.11:8000/. It should show our API message.
Plug /docs at the end of the link to go to Swagger Ui and see if our app is running.
Make sure after completing all the tasks, you delete the resources as it will incur charges, and you would not want that.
Deploying is important, and ECS makes it easier to run containerized applications. In this article, we went from creating a Machine Learning app with Fast API to deploying it on AWS ECS.
Here are some key takeaways from the article.
So, this was it. I hope you liked the article.
The media shown in this article is not owned by Analytics Vidhya and is used at the Author’s discretion.