AI Engineer World's Fair 2024
Covalent Launch: The GPU Cheatcode: Fine-tune 20 Llama Models in 5 Minutes
Read the talk
From a Python Notebook to GPU Training and Model Deployment
Covalent’s launch demo follows locally written Python through remote GPU execution, model evaluation, CPU-based selection, and deployment behind a text-generation endpoint.
From a talk by Santosh Radha
Before you start: Familiarity with Python functions, decorators, and the distinction between model training and inference will help you follow the workflow.
Send locally written Python to remote compute
How do you fine-tune a collection of models from Python without first managing Docker containers or a Kubernetes cluster? Santosh Radha opens with that problem. The launch title names twenty Llama models, but he calls twenty arbitrary and describes an ambition to support hundreds. The five minutes refer to his presentation budget; the demonstration does not establish a timed twenty-model training result. He points viewers to a QR code for the accompanying code and examples, then introduces Covalent as an open-source/open-core product that sends locally written Python to a chosen compute backend.
The starting point is a function in a local Jupyter notebook. Add a decorator specifying the resources it needs, then press Shift+Enter. Radha’s first illustrative request selects an H100, 36 GB of memory, and a maximum runtime of two days. The function executes on the remote GPU backend, and its result comes back to the notebook. The memory quantities here and later are stated resource requests; the talk does not establish that they describe GPU VRAM.
The execution destination depends on how Covalent is used:
| Mode | Where the code runs |
|---|---|
| Open source | Your own attached compute |
| Hosted cloud | Covalent’s GPU cluster |
| Bring your own infrastructure | Your cloud or on-premises systems, orchestrated through Covalent |
Radha describes hosted billing in terms of GPU execution time: a five-minute H100 run incurs five minutes of usage, while a ten-second run incurs ten seconds. The local programming interface stays the same idea across these arrangements: specify compute, send the function, receive its result.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Function jobs and inference services
A job is a single function with an execution destination. Its decorator describes the compute requirement; the backend runs the function and returns a Python object. Radha describes the charge as belonging to that function’s execution. This makes the function boundary both a programming boundary and a resource-allocation boundary.
An inference service adds a different lifecycle. Define an initializer, define a Python handler for an endpoint such as /generate, and call cc.deploy from the notebook. Instead of only receiving a function result, the developer receives an API endpoint. In the launch presentation, Radha describes that endpoint as supporting scale-to-zero and scaling as requests arrive. These are the service capabilities described in the recording, rather than guarantees established here for a current SDK version.
Radha then gives three examples of custom scaling policies:
- Scheduled capacity: increase to ten GPUs at nine o’clock each day.
- Utilization trigger: scale when GPU utilization reaches 80%.
- Request trigger: scale when the request count reaches 1,000.
He also describes configurable authentication. The developer supplies Python functions and service policy; the platform handles the infrastructure work without requiring the developer to write Docker or Kubernetes configuration.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
A broader set of Python workloads
Before moving into the fine-tuning code, Radha points to the example library. His examples range from real-time time-series analysis and inverted transformers for forecasting—which he describes as state of the art—to large language model serving and an entire AI model foundry built with Python. Fine-tuning is the small demonstration of a broader capability: coordinating compute for several kinds of workloads through the same language used to implement them.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Give each task the resources it needs
The code demonstration begins with ordinary training and evaluation functions in a local Python package. A training task wraps the fine-tuning function: it accepts a model and data, then returns a fine-tuned model. Radha assigns that task 24 CPU cores, one H100 GPU, 48 GB of memory, and an eighteen-hour maximum runtime. The resource specification surrounds the training function rather than changing its model-and-data interface.
Once training finishes, an evaluation task accepts the trained model and measures its accuracy. The workflow then ranks the candidates and picks the best-performing model for deployment. The selection task runs on a CPU machine: there is no reason to reserve a GPU merely to sort model scores. Resource placement follows the work each function performs.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Connect training to deployment
The selected model becomes an inference service. An initialization decorator marks the service setup, and a /generate endpoint generates text and returns a prediction. This completes the deployment target before the demonstration connects the individual tasks into a workflow.
The workflow itself uses a familiar Python structure: loop over candidate models, fine-tune each one, evaluate the result, and collect model–accuracy pairs. Sort those pairs, then deploy the winner. The orchestration logic can be expressed with the task functions passed explicitly:
python
def train_select_deploy(
models, data, fine_tune_task, evaluate_task,
select_task, deploy_task,
):
scored_models = []
for model in models:
trained_model = fine_tune_task(model, data)
accuracy = evaluate_task(trained_model)
scored_models.append((trained_model, accuracy))
best_model = select_task(scored_models)
return deploy_task(best_model)
def select_best(scored_models):
ranked = sorted(
scored_models,
key=lambda item: item[1],
reverse=True,
)
return ranked[0][0]
Here, select_task represents the CPU task wrapping select_best; the other task arguments represent the training, evaluation, and deployment boundaries. The collected pairs preserve the association between each trained model and its score, so selection returns the model that should be deployed.
A single-line dispatch submits the workflow to Covalent and creates a job in its application. Each function runs on the device assigned to it. The frontend then lets Radha inspect an individual evaluation step and the machine allocated to that step, connecting the Python task boundary to an actual execution record.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Inspect the runs behind the workflow
Radha closes by reporting two runtime and cost examples from the application:
| Run | Reported device | Reported runtime | Reported cost |
|---|---|---|---|
| Evaluation | L14 | Six minutes | $0.87 |
| Another model run; task unspecified | V100 | Six minutes | $0.11 |
The first device name is transcribed as L14; its accelerator identity remains unclear. The second run’s task is not specified, and the presentation does not supply the model sizes, datasets, accuracy definition, or billing conditions needed to compare the two costs. These are reported individual runs, not a controlled accelerator comparison.
The completed path is nevertheless concrete: locally written Python defines training, evaluation, selection, and deployment; each function receives its own compute assignment; and the selected model becomes a service. Radha ends with that training-to-deployment workflow completed without the developer managing Docker or Kubernetes.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Resources
From the talk
Open-source Python workflow orchestration with executor plugins for cloud and on-premises compute.
Further reading
An inverted Transformer architecture that applies attention across variates for multivariate time-series forecasting.
Read the complete timestamped transcript
- 0:00
[on hold music]
- 0:13
So the talk is actually going to be about, um, uh, how you run things extremely easy directly from Python. And the example that I'm going to show you here is obviously I just have five minutes in on my end, but I'm going to try my best to showcase how you can fine-tune pretty much, twenty is an arbitrary
- 0:29
number here, but hundreds of models that you can do right from Python without needing anything like Kubernetes, Docker, or anything on your side in. Uh, so before that, you can find the talk and the actual code for what I'm gonna do in this QR code, and you'll find a lot more interesting examples over there to try out
- 0:45
and run as well. Um, okay. So what do we do? Um, so Covalent is an open source/open core, uh, product on its end. And what we do is we help people write Python locally and ship the code to any kind of compute backend that you need to send it to.
- 1:03
So what that means is, hey, you have a Python function that you want to run on a GPU, um, in your local laptop, open up a Jupyter Notebook, add a single decorator on top to say, "Hey, I want to run this on H100 with thirty-six gigs of memory for two days maximum time limit," and press Shift+Enter in
- 1:19
your Jupyter Notebook, and that's it. The code gets shipped to a backend in a GPU, and you get back the result on your side in. In the open source case, it sends it to your own compute.
- 1:28
You can attach your own compute cluster, and it runs over there. In the cloud case, it runs in our GPU cluster, and you just pay for the GPU time that it runs in.
- 1:36
So it runs for five minutes, you pay for five minutes of H100. It runs for ten seconds, you pay for ten seconds of H100s on your side in. You can also bring your own compute and attach to us, and we'll help you orchestrate the entire compute that you're handling it on your side, be it your own cloud
- 1:49
or on-prem systems or whatever it is on your end in.
- 1:53
Okay. So, uh, Covalent basically has a bunch of parameters that you define in. You can submit in jobs, which are called single functions. So essentially all you need to do is, as I said, add a single decorator on top and say, what is the compute that you need to ship it to?
- 2:07
It goes there, it runs, and you get back the Python object back, and you just pay for the function that you are running in.
- 2:13
We also let you run inferences, and again, it's completely Pythonic. You don't Dockerize, you don't run Kubernetes cluster, you don't do anything. You just say, "Hey, I have an initializer function, and I have a-- I need an endpoint called /generate."
- 2:27
And you define your Python functions. You click a single cc.deploy command, uh, in your Jupyter Notebook. The entire service gets shipped to us, and we scale. You get back an API endpoint that scales to zero or scales in its requests as and when your new request comes in.
- 2:41
You can define your custom auto-scaling mechanism. Like, "Hey, I want to auto-scale it to ten GPUs exactly at nine o'clock every day," or, "I wanna auto-scale whenever my GPU utilization hits in eighty percent," or, "I wanna auto-scale whenever the number of requests I get in is thousand."
- 2:56
Uh, so you can define whatever auto-scaling you want. You can define authentication and everything, and everything happens in the background for you. You don't even touch a single code of Kubernetes or Docker or anything on your side in.
- 3:07
And the talk I'm gonna give in is a very tiny example, um, of what we do from our side. But if you go to this link in, there's a whole host of examples, uh, that you can run in right from real-time time series analysis to, uh, you know, using inverter transformers for time series, which is like a
- 3:25
state-of-the-art, uh, time series transformer on its end, uh, running in large systems, um, large language models on your serving, uh, systems, and even building an entire AI model foundry out of our just pure Pythonic code, uh, on your side in.
- 3:40
So without further ado, I'll quickly run through the code example of how you do essentially fine-tune a bunch of huge set of models, uh, directly just from Python on your end.
- 3:51
And I'll also show you how it looks like, uh, from the front-end side as well. So, um, it's rather simple. All you do is I have written a bunch of, uh, normal Pythonic training functions in my local package called fine-tune and evaluate on our end.
- 4:06
And what we are going to do is, hey, I'm going to define a Python task, which essentially calls in my fine-tune function, which is going to accept a model and data and return back a fine-tune model.
- 4:15
So this is a simple Python function, and I'm gonna say, hey, I want to run it on a twenty-four core CPU with one GPU in it of type H100 with forty-eight gigs of memory, and going to give a max limit of eighteen hours on it.
- 4:27
And then I'm gonna say, hey, I'm gonna-- Once the model is done, I'm gonna accept the model and then evaluate its accuracy on its end. And finally, I'm going to just sort the model among all the best models and then pick the best model in it.
- 4:39
And I want this to run on a CPU-based machine. I don't wanna waste GPU for my sorting or whatever I'm gonna do in on my end. And finally, I'm gonna deploy the best model that I figured, um, in its-- that has performed well on my end.
- 4:51
And this is like in a simple decorator to add to say, "Hey, this is my initialization service, and I'm gonna create an endpoint called /generate. Um, and I'm going to generate the text and give back the prediction."
- 5:04
And finally, this is where the magic happens. To tie together all of these things, what I do is I'm gonna create a workflow where I'm pretty much just going to simply loop over a bunch of models to fine-tune, call the fine-tune function, evaluate the task and get the accuracy, make a list of all the models and accuracy,
- 5:20
sort the best models, and then deploy the model from my end. And this is completely Pythonic. And once you dispatch this to our server, which is essentially calling a single line over here, what you will go back and see is a new job that creates in our application, and all of the functions that you called will run
- 5:39
in the respective devices that you just defined. So for instance, here is, uh, one of the evaluation step that ran in, and it has its own machine that we ran in.
- 5:49
It ran in L14. It ran for six minutes, and you get back just eighty-seven cents to evaluate your model in. Another model ran in V100 on its end, and it ran for six minutes again.
- 6:02
It costed eleven cents to do it in. And in total, you finally have deployed, tri-- fine-tuned, untrained completely in Python without needing anything like Docker or Kubernetes on your end.
- 6:13
And we have a booth over there. Do visit us, and we can have more chat over there. Thank you, guys. [outro music]