MLflow Part 4: Interacting with the Server Client! | by David Hundley | Towards Data Science

Hello there all! Long time, no see, right? My previous post in this series was published all the way back in October 2020, which is coincidentally the time when a ton of new video games rolled out. Because I write these posts in my free time, I clearly was using my free time for other things. 😂
This will be the fourth and likely final post in our MLflow 101 series. It’s been a great ride, but all good things must come to an end. If you would like to catch up on the previous posts, please check them out at the following links:
Part 1: Getting Started with MLflow!
Part 2: Deploying a Tracking Server to Minikube!
Part 3: Logging Models to a Tracking Server
If you would like to follow along with the code shown in this post, you can find it at my personal GitHub here.
To briefly recap where we left off from the last post, we had finally logged our first model into the MLflow tracking server we deployed out onto Kubernetes (Minikube). I intentionally logged the model as three different versions, and if you open them up in the MLflow server interface, this is what you might recall seeing:
And as another quick refresher, the diagram below shows the architecture for how we have this server deployed. In short, we have a Postgres instance housing the metadata about our models, and we have a Minio server housing the model artifacts in an AWS S3-like interface. Remember, Minio is intentionally set up to emulate S3. This will be important to remember further on down in the post.
Okay, so let’s dive into some new stuff now! As great as interacting with the MLflow UI is, it’s better if we can programmatically interact with the server client for consistency’s sake. There are a ton of cool things you can do with the Python tracking client, and you can learn all about them via MLflow’s documentation. For this post, we’ll cover just a few things.
First, we need to connect to the client itself. In order to do this, you’ll need to recall the URI we created for our tracking server back in Part 2 of this series. In my case, my client’s URI is http://mlflow-server.local. Instantiating the MLflow client in Python is actually super simple. This is what that syntax looks like:
# Importing our required librariesimport mlflowimport mlflow.sklearnimport pandas as pdimport os# Setting the MLflow clientclient = mlflow.tracking.MlflowClient(tracking_uri = 'http://mlflow-server.local')
Before moving forward, we have to do one other special thing with some environment variables given that we are using the Minio artifact store. Because Minio emulates AWS and S3, it’s going to look for those same environment variables that AWS would look for if we were legitimately using AWS. You can export those environment variables from the command line, or if you’d like to do it right in your Python code, all you have to do is this:
# Setting the requried environment variablesos.environ['MLFLOW_S3_ENDPOINT_URL'] = 'http://mlflow-minio.local/'os.environ['AWS_ACCESS_KEY_ID'] = 'minio'os.environ['AWS_SECRET_ACCESS_KEY'] = 'minio123'
Client instantiated: check! Environment variables: check! We’re ready to roll, folks! Let’s start off with some simple stuff. Truth be told, I don’t know what real world use case you’d want to execute the following commands, but it’s nice to know they’re there. The first is a very simple list of all the model experiments we’ve created thus far, and the second shows all the versions of a particular model given it’s name. Here is the syntax, and down below shows the output of that syntax:
# Listing the MLflow experimentsclient.list_experiments()# Getting the model versions for the wine-pyfile-modelresults = client.search_model_versions("name='wine-pyfile-model'")for res in results:    print(f'name={res.name}; version={res.version}')
For the client.list_experiments() function, you can see we have one experiment and other details about that experiment, including where the artifacts are stored in “S3” (aka Minio). In the next function, we do a search using the “wine-pyfile-model” name (as we completed in Part 3), and it shows that I have three different versions of that model in MLflow.
“Okay,” you might be wondering, “when are we going to do something really useful?” Here’s where I think MLflow’s client interaction shows the most potential: model deployment. Now, for those of you familiar with MLflow, you’ll recognize that this is not the recommended way to deploy models with MLflow. MLflow actually does have another recommended way to deploy models, but truth be told... I’m not wild about it. In my opinion, I think it’s too rigid. It follows a very precise syntax and way to package things, and I just found it to be too much of a hassle to be useful.
Instead of doing things the way MLflow would encourage them, I’m going to show you my preferred route using the same MLflow client we’ve been working with. In this pattern, we can use the MLflow client to find the precise location of our model artifact within “S3” (Minio) and directly load the model for usage within our Python file. In this example, I’m going to grab Version 2 of our model just to show we can grab whatever version we like. Here’s the syntax for using the client to both find and load the model:
# Getting the URI for version 2 of the Wine modeluri = (client.get_model_version_download_uri(name = 'wine-pyfile-model', version='2')) + '/'print(uri)# Loading the model using the URI abovemodel = mlflow.sklearn.load_model(model_uri = uri)
And that’s it! You now have your model loaded into memory, and you can use this for inference from here on out. I’m not going to show it in this post, but I have successfully deployed a Flask API to Kubernetes that does an upfront load of the model directly from MLflow and holds it in memory for all incoming inference requests.
As a simple means to prove to you that this works, I’ll run this model against the same training data that we trained the model on originally. In the screenshot below, you can see that I’ve loaded the dataset, dropped the predictor value, and ran the remaining columns through the model. It produces inference outputs like a dream!
Theoretically speaking, if you have all the proper security and networking measures in place, this means you can effectively deploy your model to any platform. For example, if I train all my models locally in Kubernetes and want to use them in AWS, GCP, or Azure, all I’d need to do is make a call out to Kubernetes and load in that model on the fly. Goodbye, vendor lock-in!
And that wraps up this series on MLflow! Apologies again that this post was super delayed. I hope you found a lot of value in this series, and I learned a lot myself by writing these posts. I haven’t yet decided what my next series of posts will be, so if you would like to see something covered, please reach out to me with your input. Thanks again for reading!