Optimizing Language Model Training: A Practical Guide to SLURM

Viktorciroski
10 min readNov 20, 2023

--

In the dynamic world of deep learning, pushing the boundaries of language models often bumps into the memory limits of individual GPUs, like the NVIDIA GeForce RTX 3090. With 24 GB of GDDR6X memory, it’s a powerhouse, but models such as Llama 2 can still stress these resources, causing headaches like memory overflow and lengthy training times. To tackle these challenges, many researchers and developers are turning to distributed learning systems, where the combined might of multiple GPUs can make a real difference. In this blog post, we’re taking a down-to-earth look at SLURM (Simple Linux Utility for Resource Management), exploring how it can help fine-tune large language models on distributed systems. It’s all about overcoming resource hurdles without the snobby jargon. Let’s dive in!

Training on a single GPU

Embarking on the journey of training powerful language models often starts with individual GPUs, providing a solid foundation for experimentation and model development. In this section, we’ll explore the process of training on a single GPU, specifically on Google Colab, leveraging the might of a Tesla T4 with CUDA version 12.0. However, as we delve into this realm, it’s not uncommon to encounter challenges. Imagine hitting a roadblock with an unexpected error, like the one below:

#Load Model
qlora_fine_tuning_config = yaml.safe_load(yaml_file)
model = LudwigModel(config=qlora_fine_tuning_config, logging_level=logging.INFO)

#Train Model
results = model.train(dataset=train_data)
print("Model Trained!")


— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — -
ImportError Traceback (most recent call last)
<ipython-input-14–66695e3ad8bb> in <cell line: 6>()
4
5 # Train Model
— → 6 results = model.train(dataset=train_data)
7 print(“Model Trained!”)

ImportError: Using `low_cpu_mem_usage=True` or a `device_map` requires
Accelerate: `pip install accelerate`

This error is a familiar stumbling block, hinting at the need for the Accelerate library. Before we proceed further, let’s explore if there’s a remedy for this error, and if not, we’ll navigate through the troubleshooting steps. Our goal is to make the training process on a single GPU as smooth as possible, laying the groundwork for understanding how SLURM can play a pivotal role in overcoming these challenges on distributed systems. Let’s begin the exploration!

Transitioning from the Tesla T4 to the A100 represents a significant leap in the world of GPU computing, particularly in the context of deep learning tasks. The A100, part of NVIDIA’s latest GPU architecture, brings a host of advantages, including increased computational performance, enhanced memory bandwidth, and a larger memory capacity. This shift is often motivated by the need to tackle more demanding workloads, especially when working with large language models like Llama 2.

The A100’s superior capabilities can alleviate memory constraints, a common hurdle when training intricate models, and reduce training times substantially. Its increased parallel processing capabilities and advanced Tensor Cores make it a powerhouse for handling complex neural network architectures.

However, while the advantages of a high-end GPU like the A100 are substantial, there are inherent limitations to what a single GPU can achieve, especially when tackling resource-intensive tasks. Recognizing this constraint, the appeal of distributed learning environments becomes evident. Transitioning from a single GPU setup to a cluster or distributed system empowers researchers to scale their computations horizontally, effectively distributing the workload across multiple GPUs.

It’s important to note that this post serves as an introduction to the concept of working with distributed systems, laying the foundation for understanding the benefits of scaling computations. In subsequent posts, we will delve deeper into practical aspects, providing insights into fully optimizing and utilizing the advantages offered by distributed systems. Specifically, we’ll explore advanced techniques, tools, and frameworks, with a focus on SLURM (Simple Linux Utility for Resource Management), to help users harness the full potential of distributed computing for their specific applications.

Stay tuned for in-depth discussions on fine-tuning distributed learning environments, overcoming challenges, and implementing best practices to achieve optimal performance. This series aims to equip you with the knowledge and skills needed to navigate the intricacies of distributed systems efficiently.

In contrast to platforms like Google Colab, where GPU access may be limited, a dedicated cluster offers greater control and customization of the hardware environment. Researchers can optimize resource utilization and overcome potential bottlenecks associated with single-GPU setups. The transition to the A100 coupled with distributed learning methods is a strategic move to unlock unparalleled computational power, addressing the evolving challenges of training advanced language models effectively. In this pursuit, SLURM emerges as a crucial tool, providing the means to manage resources efficiently in a distributed environment, enabling seamless collaboration between multiple GPUs and unlocking the full potential of the A100’s capabilities.

What is SLURM?

SLURM, an acronym for Simple Linux Utility for Resource Management, is an open-source, highly scalable cluster management and job scheduling system widely used in high-performance computing (HPC) environments. It plays a pivotal role in efficiently managing and allocating computing resources, making it an invaluable tool for researchers and organizations dealing with complex computational workloads.

The history of SLURM traces back to the early 2000s when it was developed by Lawrence Livermore National Laboratory (LLNL) to address the evolving challenges in the management of large-scale computing clusters. The primary motivation was to create a flexible and extensible resource manager that could adapt to the diverse needs of various HPC applications. Over the years, SLURM has evolved into a robust and versatile solution, with contributions from the broader HPC community and continuous development to meet the ever-growing demands of modern computing.

At its core, SLURM operates as a job scheduler and resource manager, responsible for efficiently allocating and managing resources such as CPUs, GPUs, and memory within a computing cluster. Users submit job requests to SLURM, specifying resource requirements and the computational tasks to be performed. SLURM then optimally schedules and allocates resources based on the availability and priority of requests. Its design promotes a modular and extensible architecture, allowing administrators to customize and adapt the system to their specific cluster configurations and usage patterns.

SLURM’s working mechanism involves a hierarchical structure with different daemons responsible for specific tasks. The slurmctld daemon manages the entire cluster, overseeing resource allocation and job scheduling. Compute nodes run the slurmd daemon, which communicates with slurmctld to report node status and receive job assignments. This distributed architecture ensures efficient communication and coordination across the entire cluster, enabling SLURM to handle complex workflows and diverse job requirements.

In summary, SLURM stands as a testament to the collaborative efforts of the HPC community in addressing the resource management challenges of large-scale computing environments. Its history showcases a commitment to adaptability and scalability, making it a trusted resource manager for organizations worldwide. The underlying architecture and working principles of SLURM make it a critical component in the orchestration of distributed computing tasks, providing a foundation for optimal resource utilization and job scheduling in HPC clusters.

Writing your first Bash Script –rewrite for our script

Step 1: Create a Bash Script File

Begin by creating a new file for your SLURM job script. In this example, we’ll name it `my_slurm_job.sh`.

touch my_slurm_job.sh

Step 2: Add SLURM Directives

Open the newly created script file (`my_slurm_job.sh`) in your preferred text editor and add the necessary SLURM directives. These directives guide the SLURM scheduler on how to handle your job.

#!/bin/bash
#SBATCH — job-name=my_job
#SBATCH — output=output.txt
#SBATCH — error=error.txt
#SBATCH — partition=your_partition
#SBATCH — nodes=1
#SBATCH — ntasks-per-node=1
#SBATCH — cpus-per-task=4
#SBATCH — time=01:00:00
#SBATCH — gres=gpu:1

- ` — job-name`: Specify a name for your job.

- ` — output` and ` — error`: Define file paths for standard output and error streams.

- ` — partition`: Specify the SLURM partition or queue you want to use.

- ` — nodes`, ` — ntasks-per-node`, ` — cpus-per-task`: Define the number of nodes, tasks per node, and CPUs per task.

- ` — time`: Set the maximum run time for your job.

  • ` — gres=gpu:1`: Request GPU resources

(modify according to your needs).

### **Step 3: Add Your Commands**

Below the SLURM directives, add the actual commands that SLURM should execute as part of your job.

load module XXX

# Your application commands go here
python my_script.py

TABLE ABOUT WHAT LOAD MODULE IS

Replace `python my_script.py` with the actual command(s) required for your job.

Step 4: Submit the Job

Save the script, make it executable, and submit it to the SLURM scheduler.

chmod +x my_slurm_job.sh
sbatch my_slurm_job.sh

The `chmod +x` command makes the script executable, and `sbatch` is used to submit the job script to SLURM.

Step 5: Monitor Job Status

Keep track of your job’s status using the `squeue` command.

squeue -u your_username

or

squeue --me

This command displays the status of your submitted job.

Congratulations! You’ve successfully written and submitted your first SLURM job script. Adjust the directives and commands based on your specific requirements and application.

Keeping an Eye on Your SLURM Job and Bailing Out If Needed

So, you’ve tossed your job into the SLURM queue, and now it’s playing the waiting game. Let’s talk about how to check up on it and, just in case, how to call it quits.

Spy on Your Job’s Status

To see what’s up with your job and where it stands in the queue, throw a glance using the `squeue` command:

squeue

This little trick spills the beans on all jobs in the queue, including yours. If you’re curious about the nitty-gritty details of a specific job — like what resources it’s got and its general mood — you can use `scontrol show job`:

scontrol show job [JOB_ID]

No worries, just replace `[JOB_ID]` with the actual job ID. You can snatch this ID from the `squeue`.

Hunt Down Jobs with a Specific Name

Now, if you’ve got a bunch of jobs with names that could be siblings, let’s say “my_job,” you can sift through the crowd using `grep`:

squeue | grep “my_job”

This command filters the queue chatter, spotlighting only the jobs rocking the specified name.

Pull the Plug on a Job

If a job is being a party pooper and you need to kick it out, play the boss with the `scancel` command, followed by the job ID:

scancel [JOB_ID]

Just plug in the actual job ID — no need for any fancy codes. You can fish out this ID from the `squeue` hitlist.

**Friendly Tip:** If you decide to tweak your source code after submitting a job but before it starts strutting its stuff, keep in mind that SLURM will roll with the latest version of your script when showtime hits. It’s all about that real-time action, not the script you tossed in initially.

Watching over your jobs and having the power to pull the plug when needed gives you the upper hand in the SLURM arena. It’s like being the director of your own job show — call the shots and keep things running smooth!

Navigating Output and Error Files: A Practical Guide

As your SLURM job progresses, understanding the nuances of output and error files becomes pivotal. These files serve as comprehensive records, chronicling both the triumphs and detours of your computational journey.

Introduction to the Files

Upon execution, your job generates two essential files: the output file, conventionally labeled as `output.txt` in SLURM directives, encapsulates the affirmative aspects — results, print statements, and successful executions.

#SBATCH — output=output.txt

Conversely, the error file, identified as `error.txt` in your script, acts as a repository for your job’s grievances, documenting any anomalies, crashes, or unforeseen deviations in the code’s course.

#SBATCH — error=error.txt

Examining the Contents

Deciphering these files entails utilizing conventional tools such as text editors or the `cat` command:

cat output.txt

This command unveils the contents of the output file, offering a chronological narrative of your job’s activities.

cat error.txt

Similarly, perusing the error file can shed light on instances where your code encountered challenges.

Leveraging for Debugging

Beyond mere documentation, these files serve as indispensable aides in debugging. In instances where your code encounters hurdles, the logs within the output file often disclose valuable insights. Simultaneously, the error file pinpoints unexpected twists that disrupted your code’s trajectory.

Practical Tips

- Tail Command Utility:To monitor your job’s real-time progress during execution, consider utilizing the `tail` command:

tail -f output.txt

This command provides a live feed of the most recent updates.

Conclusion

In the ever-evolving landscape of deep learning, where colossal language models often strain the limits of individual GPUs, the journey from single-GPU experimentation to distributed systems is a strategic leap. This transition, exemplified by the move from a Tesla T4 to the potent A100 GPU, marks a paradigm shift in GPU computing. The A100’s enhanced computational performance and expanded memory capacity address the challenges posed by intricate models like Llama 2, significantly reducing training times.

However, even with the prowess of high-end GPUs, the demand for computational might exceeds the capabilities of a single device, leading to the embrace of distributed learning environments. Cluster setups, in contrast to platforms like Google Colab, provide greater control and customization of hardware, optimizing resource utilization and overcoming bottlenecks associated with singular GPU setups.

In this pursuit of unlocking unparalleled computational power, SLURM emerges as a crucial ally. The Simple Linux Utility for Resource Management orchestrates the efficient allocation of resources in distributed environments, enabling seamless collaboration between multiple GPUs and unleashing the full potential of GPUs like the A100.

We delved into the practical aspects, starting with the challenges encountered in training on a single GPU and navigating the troubleshooting steps. The path led us to the creation of SLURM scripts, enhancing the efficiency of job scheduling, and managing resources across distributed systems. The step-by-step guide provided insights into SLURM directives, script execution, and monitoring job statuses.

Furthermore, we explored the historical evolution of SLURM, tracing its roots to the early 2000s, and how it has become a versatile and adaptable resource manager. SLURM’s hierarchical architecture ensures efficient communication in large-scale computing clusters, making it a trusted solution for high-performance computing environments.

As a final touch, we delved into practical tips for monitoring jobs, canceling if necessary, and navigating the crucial output and error files. These files, beyond documentation, serve as invaluable aids in debugging, offering insights into the intricate dance of code execution.

In conclusion, the journey from single-GPU training to distributed systems, coupled with the guidance of SLURM, is a testament to the dynamism and adaptability required in the pursuit of advancing language models. Whether overcoming memory constraints, optimizing resource utilization, or debugging complex code, this exploration encapsulates the collaborative efforts of researchers and developers in pushing the boundaries of deep learning.

Acknowledgment and Resources

All the files utilized in this demonstration, including SLURM scripts, code snippets, and configurations, are available on our GitHub repository. You can access them [here]

Additionally, for those interested in setting up and fine-tuning Ludwig models, and learning more about how to optimize SLURM for distributed systems, a comprehensive guide is provided on our [Medium Page], offering step-by-step instructions and insights into leveraging the power of Ludwig for language model development. Feel free to explore the repository for further resources and hands-on learning.

SLURM. (n.d.). SLURM Workload Manager Documentation. 2023, from https://slurm.schedmd.com/documentation.html

--

--