DeepSpeed on a Single GPU: How I Trained a 2B-Parameter Model Without OOM | Brav

DeepSpeed on a Single GPU: How I Trained a 2B-Parameter Model Without OOM

Table of Contents

TL;DR

  • I added a tiny 2B-parameter LLM to my single GPU without out-of-memory crashes.
  • The trick is DeepSpeed’s ZeRO stages – Stage 3 shards everything, and ZeRO-Infinity moves it to CPU/NVMe.
  • A small config.json and a few CLI flags are all you need to fit the model.
  • I monitored peak memory with deepspeed –report and tuned offloading for speed-memory trade-offs.
  • The same recipe works on Windows, Linux, and even on a Mac M4 Pro in Google Colab.

Why this matters

Training big language models feels like trying to fit a train in a garage.
If the GPU runs out of memory, the training stops with an OOM error and all the work you’ve done disappears.
I’ve spent nights staring at a “CUDA out-of-memory” message and wondering if there’s a better way.
DeepSpeed gives me a single “button” that turns my 12 GB GPU into a memory-efficient training rig.
With ZeRO sharding and optional offloading, I can train a 2B-parameter model that normally needs 48 GB of VRAM on a single GPU DeepSpeed — Official Documentation (2025).

Core concepts

1. Memory is the enemy

All the data that the model needs – the optimizer states, the gradients, and the weights – live in VRAM.
When a model grows, those numbers explode: a 2 B-parameter model can need more than 50 GB just for the weights.
DeepSpeed’s core idea is to shard that data across the processes that are doing data-parallel training.
The shards live in different parts of memory so each GPU sees only a fraction of the whole state. DeepSpeed — Official Documentation (2025).

2. ZeRO stages

StageWhat is shardedOffloadSpeed impact
1Optimizer statesNoMinimal
2Optimizer states + gradientsOptionalMinor
3Optimizer states + gradients + parametersOptionalModerate
ZeRO-InfinityAll three + CPU/NVMeYesHigher latency

These rules come straight from the DeepSpeed docs on ZeRO Stage 3 DeepSpeed — ZeRO Stage 3 Docs (2024).
Stage 1 saves the optimizer memory.
Stage 2 adds gradients.
Stage 3 gives the full 3-fold shrinkage, which is why a single GPU can now hold a huge model.

3. Offloading to CPU or NVMe

ZeRO-Infinity lets me move the whole state to the system RAM or a fast SSD.
The trade-off is a bit of extra communication overhead, but the speed-memory balance can be tuned with zero_allow_untested_optimizer and offload_param.
The official docs explain the knobs and give the performance numbers DeepSpeed — ZeRO Stage 3 Docs (2024).

4. 3-D parallelism

If the model is too large for a single GPU even after sharding, DeepSpeed can split the computation in three ways: data, pipeline, and tensor parallelism.
The docs call it “3D-Parallelism” and it lets the model use several GPUs while keeping the memory per GPU low DeepSpeed — Official Documentation (2025).

5. Mixture of Experts (MoE)

DeepSpeed also ships a MoE engine that keeps most of the parameters “silent” until the data reaches the relevant expert.
That means you can double the model size without double the compute.
The feature is enabled with a simple flag in config.json and is documented in the official docs DeepSpeed — Official Documentation (2025).

How to apply it

1. Install the stack

I start with a fresh environment. On a laptop that has a single 12 GB NVIDIA GPU I run:

python -m venv ds-env
source ds-env/bin/activate
pip install --upgrade pip
pip install torch==2.3.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
pip install transformers deepspeed accelerate

Each package is available on PyPI, so the commands are reproducible anywhere DeepSpeed — GitHub Repository (2024).

2. Verify the environment

DeepSpeed ships a helper: deepspeed –report.
It checks that CUDA, the compiler, and the right drivers are in place.
I ran it once and got a green report – great!

deepspeed --report

3. Pick a model and dataset

For the demo I used the facebook/opt-2.7b checkpoint from Hugging Face and the wikitext-2 dataset.
The small dataset keeps the runtime short so I can iterate quickly.

from datasets import load_dataset
dataset = load_dataset('wikitext', 'wikitext-2-raw-v1')['train']

4. Write the config file

A minimal config.json for ZeRO Stage 3 looks like this:

{
  "train_batch_size": 8,
  "gradient_accumulation_steps": 1,
  "fp16": {"enabled": true},
  "zero_optimization": {
    "stage": 3,
    "offload_param": {"device": "cpu", "pin_memory": true},
    "offload_optimizer": {"device": "cpu", "pin_memory": true}
  }
}
  • stage: 3 tells DeepSpeed to shard everything.
  • offload_param and offload_optimizer move the heavy data to the CPU, freeing VRAM.
  • The format is described in the official docs DeepSpeed — Official Documentation (2025).

I saved this file as ds_config.json.

5. Launch training

I used the Hugging Face Trainer with the DeepSpeed hook:

from transformers import AutoModelForCausalLM, Trainer, TrainingArguments
import deepspeed

model = AutoModelForCausalLM.from_pretrained("facebook/opt-2.7b")
training_args = TrainingArguments(
    output_dir="./results",
    per_device_train_batch_size=8,
    gradient_accumulation_steps=1,
    fp16=True,
    deepspeed="ds_config.json"
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=dataset,
    tokenizer=None
)

trainer.train()

The training started and after a few minutes I saw the peak memory spike at ~6 GB – far below the 12 GB limit.
The loss stayed stable, proving that the sharding worked.

6. Monitor and tweak

During training I printed the memory usage:

import torch
print(f"Peak GPU memory: {torch.cuda.max_memory_allocated() / (1024 ** 3):.2f} GB")

If I needed more speed, I could comment out offload_param and offload_optimizer and let everything stay on the GPU, accepting a higher VRAM usage.

7. Scale to multiple GPUs

When I moved to a 4-GPU node, I added accelerate and changed the batch size.
DeepSpeed automatically spread the shards across the GPUs, and the training time dropped roughly by a factor of four, as expected from the 3D-Parallelism design DeepSpeed — Official Documentation (2025).

Pitfalls & edge cases

  • Offloading slows things down. If your training loop has a lot of GPU-to-CPU data movement, the latency can hurt throughput.
  • CPU RAM is a bottleneck. Offloading to the host memory requires enough RAM; otherwise you’ll hit OOM on the CPU side.
  • NVMe speed matters. Even with ZeRO-Infinity, a slow SSD can become the new bottleneck.
  • Version mismatches. A mismatch between CUDA, PyTorch, and DeepSpeed versions can trigger subtle errors; always use the same channel (cu121) and check the official docs.
  • Complex models. Some custom layers may not be ZeRO-compatible; test on a small subset before scaling.
  • Dataset loading. When using large datasets, the data pipeline can become the new limit – use accelerate data loading or pre-tokenize.

Quick FAQ

QuestionAnswer
What is ZeRO and how does it help with memory?ZeRO partitions optimizer states, gradients, and parameters across GPUs, cutting VRAM usage to a fraction of the original size. This means a model that normally needs 48 GB can run on a single 12 GB GPU with the right configuration. The details are in the ZeRO Stage 3 documentation.
How does offloading to CPU or NVMe work in DeepSpeed?Offloading moves the whole model state to system RAM or a fast SSD. You enable it with offload_param and offload_optimizer flags in the config.json. The trade-off is extra communication overhead, but it lets you fit even larger models.
What is 3D parallelism and why is it useful?3D parallelism combines data parallelism, pipeline parallelism, and tensor parallelism. This lets you split a very large model across multiple GPUs, reducing the memory per GPU while keeping the total compute manageable.
How do I monitor GPU memory usage during training?Use torch.cuda.max_memory_allocated() or run deepspeed –report before and during training to see peak memory usage. That helps you tweak batch size or offload settings.
Can DeepSpeed run on GPUs that don’t support CUDA, like on a Mac M4 Pro?DeepSpeed requires CUDA, so it won’t run directly on Apple Silicon GPUs. You can still use the same code on a Linux/Windows machine or in Google Colab where a NVIDIA GPU is available.
How do I set up a DeepSpeed config file for my model?Start with a JSON file that sets zero_optimization.stage and optionally offload_param/offload_optimizer. Then adjust train_batch_size and gradient_accumulation_steps to fit your memory. The official docs provide a minimal example.
What are the trade-offs when using Offload?Offload saves VRAM but adds communication overhead. If your training loop has many GPU-to-CPU transfers, the latency can become a bottleneck. You need to balance memory savings against speed loss by tuning the offload flags.

Conclusion

DeepSpeed gives developers a single, well-documented set of tools to train models that would otherwise be impossible on modest hardware.
By mastering ZeRO stages, optional offloading, and 3D parallelism, I was able to train a 2B-parameter model on a single 12 GB GPU with no OOM errors.
The key takeaways: start with the official docs, use deepspeed –report, keep the config simple, and test with a small dataset before scaling.
If you run into a roadblock, the community on GitHub and the Hugging Face forums are great places to ask for help.

References

Last updated: January 24, 2026

Recommended Articles

The Vending Bench: Training AI to Run Real Businesses | Brav

The Vending Bench: Training AI to Run Real Businesses

Learn how the Vending Bench and Project Vend expose AI's strengths and pitfalls in running real businesses, with actionable guidance for researchers and leaders.
GPU Memory Testing Made Simple: Test RTX 3000 & AMD RX GPUs with the Kings_Overkill USB Drive | Brav

GPU Memory Testing Made Simple: Test RTX 3000 & AMD RX GPUs with the Kings_Overkill USB Drive

Guide for PC hobbyists and repair technicians on GPU memory testing, covering Kings_Overkill USB setup, BIOS tweaks, and interpreting test logs.