Add spec: runpod_oom_debug.md
Browse files- specs/runpod_oom_debug.md +349 -0
specs/runpod_oom_debug.md
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# RunPod H200 SXM SIGKILL Debug: Qwen3.6-35B-A3B Full SFT
|
| 2 |
+
|
| 3 |
+
**Date:** 2026-06-30
|
| 4 |
+
**Pod:** vocal_coral_quokka (jifd0w19x1c61j)
|
| 5 |
+
**Status:** Root cause identified. Full SFT is not viable on this pod configuration.
|
| 6 |
+
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
## Root Cause: Container Memory Limit, Not Host RAM
|
| 10 |
+
|
| 11 |
+
### The Lie `free -h` Tells You
|
| 12 |
+
|
| 13 |
+
RunPod containers see **host RAM** (2TB) in `/proc/meminfo` and `free -h`, but the
|
| 14 |
+
actual memory available to the container is enforced by **cgroup limits** that are
|
| 15 |
+
much lower. For a 1x H200 SXM pod, RunPod allocates:
|
| 16 |
+
|
| 17 |
+
| Resource | Spec per 1x H200 SXM |
|
| 18 |
+
|-----------------|----------------------|
|
| 19 |
+
| VRAM | 141 GB HBM3e |
|
| 20 |
+
| **System RAM** | **276 GB** |
|
| 21 |
+
| vCPUs | 24 |
|
| 22 |
+
|
| 23 |
+
**The container is capped at ~276 GB system RAM.** When a process exceeds this,
|
| 24 |
+
the Linux OOM killer sends SIGKILL (-9). There is no warning, no OOM error message,
|
| 25 |
+
just instant death. This matches the observed behavior exactly.
|
| 26 |
+
|
| 27 |
+
### How to Verify Inside the Pod
|
| 28 |
+
|
| 29 |
+
```bash
|
| 30 |
+
# cgroup v1 (most RunPod pods)
|
| 31 |
+
cat /sys/fs/cgroup/memory/memory.limit_in_bytes
|
| 32 |
+
|
| 33 |
+
# cgroup v2
|
| 34 |
+
cat /sys/fs/cgroup/memory.max
|
| 35 |
+
|
| 36 |
+
# What the process THINKS is available (WRONG - shows host RAM)
|
| 37 |
+
free -h
|
| 38 |
+
|
| 39 |
+
# What's actually enforced (check OOM events)
|
| 40 |
+
dmesg | grep -i "oom\|killed"
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
---
|
| 44 |
+
|
| 45 |
+
## Memory Math: Why Full SFT Cannot Fit
|
| 46 |
+
|
| 47 |
+
### Qwen3.6-35B-A3B Architecture
|
| 48 |
+
|
| 49 |
+
- **Total parameters:** 35B (35 billion)
|
| 50 |
+
- **Active per token:** 3B (8 routed + 1 shared expert of 256 total)
|
| 51 |
+
- **Layers:** 40
|
| 52 |
+
- **Experts:** 256 per MoE layer, 9 active
|
| 53 |
+
- **Hidden dim:** 2,048
|
| 54 |
+
- **Architecture:** Sparse MoE β inference is cheap, but **training touches ALL 35B params**
|
| 55 |
+
|
| 56 |
+
### The Critical MoE Training Problem
|
| 57 |
+
|
| 58 |
+
During inference, only 3B parameters are active per token. But during **training**:
|
| 59 |
+
- **Forward pass:** routes through selected experts (cheap)
|
| 60 |
+
- **Backward pass:** computes gradients for ALL activated expert parameters
|
| 61 |
+
- **Optimizer step:** maintains states for **ALL 35B parameters** regardless of activation
|
| 62 |
+
|
| 63 |
+
This means training memory scales with TOTAL params (35B), not active params (3B).
|
| 64 |
+
|
| 65 |
+
### Memory Breakdown for Full SFT (Single GPU)
|
| 66 |
+
|
| 67 |
+
| Component | Calculation | Size |
|
| 68 |
+
|----------------------------|-----------------------|----------|
|
| 69 |
+
| Model weights (bf16) | 35B x 2 bytes | **70 GB** |
|
| 70 |
+
| fp32 master weights | 35B x 4 bytes | **140 GB** |
|
| 71 |
+
| AdamW momentum (fp32) | 35B x 4 bytes | **140 GB** |
|
| 72 |
+
| AdamW variance (fp32) | 35B x 4 bytes | **140 GB** |
|
| 73 |
+
| Gradients (bf16) | 35B x 2 bytes | **70 GB** |
|
| 74 |
+
| **Total (steady state)** | | **560 GB** |
|
| 75 |
+
| Activation memory | batch/seq dependent | 10-50 GB+ |
|
| 76 |
+
| CUDA/PyTorch overhead | fragmentation, buffers| 5-15 GB |
|
| 77 |
+
| **Peak during init** | temporary copies | **600-700 GB** |
|
| 78 |
+
|
| 79 |
+
### What Each ZeRO Stage Actually Requires
|
| 80 |
+
|
| 81 |
+
**ZeRO-2, no offload (attempt 1):**
|
| 82 |
+
- GPU needs: 70 (weights) + 140 (master) + 280 (optimizer) + 70 (grads) = **560 GB**
|
| 83 |
+
- Available GPU: 141 GB
|
| 84 |
+
- Result: `torch.OutOfMemoryError` trying to allocate 129 GB. Correct behavior.
|
| 85 |
+
|
| 86 |
+
**ZeRO-2, CPU offload (attempt 2):**
|
| 87 |
+
- GPU: ~70 GB model weights + activations + grad buffers β 100-130 GB
|
| 88 |
+
- CPU: fp32 master weights (140 GB) + optimizer states (280 GB) = **420 GB**
|
| 89 |
+
- Available CPU (container): **276 GB**
|
| 90 |
+
- Deficit: **~144 GB over limit**
|
| 91 |
+
- Result: SIGKILL during init. 100% CPU for 2 min = optimizer state allocation eating RAM until OOM killer fires.
|
| 92 |
+
|
| 93 |
+
**ZeRO-3, CPU offload (attempt 3):**
|
| 94 |
+
- GPU: only active parameter slice + activations β 20-40 GB
|
| 95 |
+
- CPU: partitioned states BUT with 1 GPU, nothing to partition across
|
| 96 |
+
- CPU needs: same ~420-560 GB for optimizer + master weights
|
| 97 |
+
- **Additionally:** ZeRO-3 does NOT support MoE models. DeepSpeed raises `AssertionError: MoE not supported with Stage 3` in current versions.
|
| 98 |
+
- Even if it initialized, peak init memory creates temporary copies that spike higher
|
| 99 |
+
- Result: SIGKILL after ~4 min of init. Same container RAM ceiling.
|
| 100 |
+
|
| 101 |
+
### The ZeRO-3 + MoE Incompatibility
|
| 102 |
+
|
| 103 |
+
This is a confirmed, documented limitation:
|
| 104 |
+
- **GitHub Issue #2870** (filed Feb 2023, still open): "ZeRO stage 3 support for mixture-of-experts (MoE) layer" β explicitly unsupported
|
| 105 |
+
- **GitHub Issue #7156** (open): Even with ZeRO-2, expert optimizer states are NOT partitioned β only non-expert params get ZeRO treatment
|
| 106 |
+
- The MoE expert parameters maintain full unpartitioned optimizer states, making them the dominant memory consumer
|
| 107 |
+
|
| 108 |
+
### The ms-swift Confirmation
|
| 109 |
+
|
| 110 |
+
GitHub Issue **modelscope/ms-swift#6473** documents the identical failure:
|
| 111 |
+
- **Same model family:** Qwen3-30B-A3B (MoE)
|
| 112 |
+
- **Same setup:** DeepSpeed ZeRO-2 with CPU offload
|
| 113 |
+
- **Same result:** System RAM climbed to 1.7 TiB, OOM killed
|
| 114 |
+
- **Hardware:** 8x H200 GPUs, 1.5TB RAM, 96 CPU cores
|
| 115 |
+
- **Key insight:** Even with 8 GPUs and 1.5TB RAM, full SFT of this MoE model exhausted all system memory
|
| 116 |
+
|
| 117 |
+
If 8x H200s with 1.5TB RAM cannot do this, 1x H200 with 276 GB has zero chance.
|
| 118 |
+
|
| 119 |
+
---
|
| 120 |
+
|
| 121 |
+
## Viable Alternatives (Ranked by Practicality)
|
| 122 |
+
|
| 123 |
+
### Option 1: LoRA Fine-Tuning with Unsloth (RECOMMENDED)
|
| 124 |
+
|
| 125 |
+
**Why it works:** LoRA only trains adapter weights (~2-3% of params), so optimizer states
|
| 126 |
+
are proportional to LoRA rank, not total model params.
|
| 127 |
+
|
| 128 |
+
| Metric | Value |
|
| 129 |
+
|----------------------------|------------------------|
|
| 130 |
+
| Trainable params | ~931M of 36B (2.58%) |
|
| 131 |
+
| VRAM required (bf16 LoRA) | ~63-74 GB |
|
| 132 |
+
| System RAM required | ~40-80 GB |
|
| 133 |
+
| Fits on 1x H200 SXM? | **Yes** (141 GB VRAM) |
|
| 134 |
+
| Framework | Unsloth or ms-swift |
|
| 135 |
+
|
| 136 |
+
**Unsloth Configuration:**
|
| 137 |
+
```python
|
| 138 |
+
from unsloth import FastLanguageModel
|
| 139 |
+
|
| 140 |
+
model, tokenizer = FastLanguageModel.from_pretrained(
|
| 141 |
+
model_name="Qwen/Qwen3.6-35B-A3B",
|
| 142 |
+
max_seq_length=2048, # Safe ceiling for 1 GPU
|
| 143 |
+
dtype=torch.bfloat16,
|
| 144 |
+
load_in_4bit=False, # QLoRA NOT recommended for MoE (BnB limitation)
|
| 145 |
+
)
|
| 146 |
+
|
| 147 |
+
model = FastLanguageModel.get_peft_model(
|
| 148 |
+
model,
|
| 149 |
+
r=16, # LoRA rank
|
| 150 |
+
lora_alpha=16, # alpha == r, not r*2
|
| 151 |
+
target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
|
| 152 |
+
"gate_proj", "up_proj", "down_proj"],
|
| 153 |
+
lora_dropout=0,
|
| 154 |
+
use_gradient_checkpointing="unsloth",
|
| 155 |
+
)
|
| 156 |
+
```
|
| 157 |
+
|
| 158 |
+
**Critical Gotchas:**
|
| 159 |
+
1. Set `UNSLOTH_COMPILE_DISABLE=1` before imports (bf16/fp32 dtype mismatch in compiled MoE kernels)
|
| 160 |
+
2. Set `dataloader_num_workers=0` and `dataset_num_proc=1` (multiprocessing deadlock with large model)
|
| 161 |
+
3. Do NOT use QLoRA/4-bit β BitsandBytes does not support MoE architectures
|
| 162 |
+
4. Router layer is NOT fine-tuned by default (and shouldn't be for SFT)
|
| 163 |
+
5. Max safe sequence length on single H200: ~2048 tokens (activation memory explodes at 4096)
|
| 164 |
+
6. vLLM cannot load LoRA adapters directly β merge weights before serving
|
| 165 |
+
|
| 166 |
+
**Unsloth Split LoRA** provides additional MoE-specific optimization: reorders matmul
|
| 167 |
+
to compute `(X @ loraA) @ loraB` instead of materializing `loraA @ loraB`, saving ~35%
|
| 168 |
+
VRAM and providing ~2x speedup.
|
| 169 |
+
|
| 170 |
+
### Option 2: Multi-GPU with Expert Parallelism (Megatron-SWIFT)
|
| 171 |
+
|
| 172 |
+
**Why it works:** Distributes expert params AND their optimizer states across GPUs.
|
| 173 |
+
Each GPU only holds a subset of experts.
|
| 174 |
+
|
| 175 |
+
| Metric | Value |
|
| 176 |
+
|----------------------------|--------------------------|
|
| 177 |
+
| GPUs required | 4-8x (H100/H200) |
|
| 178 |
+
| Framework | ms-swift + Megatron |
|
| 179 |
+
| Expert parallelism size | 4 or 8 |
|
| 180 |
+
| Per-GPU VRAM | ~40 GB with EP=8 |
|
| 181 |
+
| System RAM per node | 1+ TB recommended |
|
| 182 |
+
|
| 183 |
+
**ms-swift Megatron configuration for Qwen3.5-35B-A3B:**
|
| 184 |
+
```bash
|
| 185 |
+
NPROC_PER_NODE=8 swift sft \
|
| 186 |
+
--model Qwen/Qwen3.6-35B-A3B \
|
| 187 |
+
--train_type full \
|
| 188 |
+
--expert_model_parallel_size 8 \
|
| 189 |
+
--tuner_type lora --lora_rank 8 --lora_alpha 32 \
|
| 190 |
+
--moe_permute_fusion true \
|
| 191 |
+
--moe_grouped_gemm true \
|
| 192 |
+
--moe_shared_expert_overlap true \
|
| 193 |
+
--moe_aux_loss_coeff 1e-6 \
|
| 194 |
+
--offload_model true \
|
| 195 |
+
--offload_optimizer true
|
| 196 |
+
```
|
| 197 |
+
|
| 198 |
+
**Cost:** 8x H200 SXM on RunPod = ~$28.72/hr (community) or ~$35.12/hr (secure).
|
| 199 |
+
This is expensive but may be necessary for true full-parameter training.
|
| 200 |
+
|
| 201 |
+
**Warning:** Even 8x H200 with 1.5TB RAM has been reported to OOM on full SFT
|
| 202 |
+
(ms-swift#6473). Expert parallelism with LoRA is the safer configuration.
|
| 203 |
+
|
| 204 |
+
### Option 3: Selective Layer Unfreezing
|
| 205 |
+
|
| 206 |
+
**Hybrid approach:** Freeze most experts, unfreeze shared expert + attention + embeddings.
|
| 207 |
+
|
| 208 |
+
```python
|
| 209 |
+
# Freeze all expert parameters
|
| 210 |
+
for name, param in model.named_parameters():
|
| 211 |
+
if "experts" in name and "shared" not in name:
|
| 212 |
+
param.requires_grad = False
|
| 213 |
+
|
| 214 |
+
# Keep trainable: attention, shared expert, embeddings, LM head
|
| 215 |
+
# Trainable params: ~3-5B instead of 35B
|
| 216 |
+
# Optimizer states: ~24-40 GB instead of 420 GB
|
| 217 |
+
```
|
| 218 |
+
|
| 219 |
+
This fits comfortably in 276 GB system RAM on 1x H200 and gives more capacity
|
| 220 |
+
than LoRA while avoiding the full 35B optimizer state problem.
|
| 221 |
+
|
| 222 |
+
### Option 4: Beast Server (NOT Recommended)
|
| 223 |
+
|
| 224 |
+
The 3x RTX 3090 server has:
|
| 225 |
+
- 72 GB total VRAM (3x 24 GB)
|
| 226 |
+
- ~186 GB system RAM
|
| 227 |
+
- Worse than 1x H200 in every dimension for this task
|
| 228 |
+
|
| 229 |
+
Even with ZeRO-2 across 3 GPUs, the optimizer states for 35B params would need
|
| 230 |
+
~420 GB CPU RAM. Does not fit. Would only work for LoRA, and the H200 is better
|
| 231 |
+
for LoRA anyway (more VRAM per device, faster memory bandwidth).
|
| 232 |
+
|
| 233 |
+
---
|
| 234 |
+
|
| 235 |
+
## Diagnostic Checklist for the Running Pod
|
| 236 |
+
|
| 237 |
+
If the pod is still accessible, run these to confirm the analysis:
|
| 238 |
+
|
| 239 |
+
```bash
|
| 240 |
+
# 1. Check ACTUAL container memory limit
|
| 241 |
+
cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null || \
|
| 242 |
+
cat /sys/fs/cgroup/memory.max 2>/dev/null
|
| 243 |
+
|
| 244 |
+
# 2. Compare with what free reports (will show host RAM, not limit)
|
| 245 |
+
free -h
|
| 246 |
+
|
| 247 |
+
# 3. Check OOM kill events
|
| 248 |
+
dmesg | grep -i "oom\|killed\|memory" | tail -20
|
| 249 |
+
|
| 250 |
+
# 4. Check cgroup memory usage at time of kill
|
| 251 |
+
cat /sys/fs/cgroup/memory/memory.usage_in_bytes 2>/dev/null || \
|
| 252 |
+
cat /sys/fs/cgroup/memory.current 2>/dev/null
|
| 253 |
+
|
| 254 |
+
# 5. Check if swap is available (usually no on RunPod)
|
| 255 |
+
swapon --show
|
| 256 |
+
|
| 257 |
+
# 6. Check GPU memory
|
| 258 |
+
nvidia-smi --query-gpu=memory.total,memory.used,memory.free --format=csv
|
| 259 |
+
```
|
| 260 |
+
|
| 261 |
+
---
|
| 262 |
+
|
| 263 |
+
## If You Still Want to Attempt Full SFT
|
| 264 |
+
|
| 265 |
+
The absolute minimum requirements for full-parameter SFT of Qwen3.6-35B-A3B:
|
| 266 |
+
|
| 267 |
+
1. **Multiple GPUs with Expert Parallelism** β not ZeRO-3 (unsupported for MoE)
|
| 268 |
+
2. **At minimum 8 GPUs** with expert_parallel_size=8
|
| 269 |
+
3. **System RAM:** 2+ TB to be safe (the ms-swift issue showed 1.5TB was not enough)
|
| 270 |
+
4. **Framework:** ms-swift with Megatron backend, NOT raw DeepSpeed
|
| 271 |
+
5. **Use LoRA even in multi-GPU** β full param SFT of 35B MoE may be fundamentally
|
| 272 |
+
impractical without custom expert-aware optimizer state partitioning
|
| 273 |
+
6. **sub_group_size:** Set to 1e8 or lower (default 1e9) to reduce init memory peak
|
| 274 |
+
7. **pin_memory: false** in offload config to reduce pinned memory allocation
|
| 275 |
+
8. **Disable overlap_comm** during init if possible
|
| 276 |
+
|
| 277 |
+
### DeepSpeed Config Tweaks (if retrying ZeRO-2 on multi-GPU)
|
| 278 |
+
|
| 279 |
+
```json
|
| 280 |
+
{
|
| 281 |
+
"zero_optimization": {
|
| 282 |
+
"stage": 2,
|
| 283 |
+
"offload_optimizer": {
|
| 284 |
+
"device": "cpu",
|
| 285 |
+
"pin_memory": false,
|
| 286 |
+
"buffer_count": 4,
|
| 287 |
+
"fast_init": false
|
| 288 |
+
},
|
| 289 |
+
"offload_param": {
|
| 290 |
+
"device": "cpu",
|
| 291 |
+
"pin_memory": false
|
| 292 |
+
},
|
| 293 |
+
"allgather_partitions": true,
|
| 294 |
+
"allgather_bucket_size": 5e7,
|
| 295 |
+
"overlap_comm": false,
|
| 296 |
+
"reduce_scatter": true,
|
| 297 |
+
"reduce_bucket_size": 5e7,
|
| 298 |
+
"contiguous_gradients": true,
|
| 299 |
+
"sub_group_size": 1e8
|
| 300 |
+
},
|
| 301 |
+
"bf16": { "enabled": true },
|
| 302 |
+
"train_micro_batch_size_per_gpu": 1,
|
| 303 |
+
"gradient_accumulation_steps": 4,
|
| 304 |
+
"gradient_clipping": 1.0
|
| 305 |
+
}
|
| 306 |
+
```
|
| 307 |
+
|
| 308 |
+
Key changes from default:
|
| 309 |
+
- `pin_memory: false` β pinned memory doubles the RAM footprint by keeping non-swappable copies
|
| 310 |
+
- `sub_group_size: 1e8` β processes optimizer updates in smaller tiles, reducing peak init memory
|
| 311 |
+
- `overlap_comm: false` β prevents double-buffering during init phase
|
| 312 |
+
- `fast_init: false` β sequential initialization instead of bulk allocation
|
| 313 |
+
- Reduced bucket sizes (5e7 vs default 5e8) β smaller communication buffers
|
| 314 |
+
|
| 315 |
+
---
|
| 316 |
+
|
| 317 |
+
## Decision Matrix
|
| 318 |
+
|
| 319 |
+
| Approach | Fits 1x H200? | Quality vs Full SFT | Cost/hr | Complexity |
|
| 320 |
+
|-------------------------|----------------|----------------------|----------|------------|
|
| 321 |
+
| LoRA (Unsloth) | YES | ~95% for most tasks | $3.59 | Low |
|
| 322 |
+
| Selective unfreeze | YES | ~97% | $3.59 | Medium |
|
| 323 |
+
| 8x H200 + EP + LoRA | N/A (8 GPUs) | ~98% | $28.72 | High |
|
| 324 |
+
| 8x H200 + EP + Full | MAYBE | 100% (if it works) | $28.72 | Very High |
|
| 325 |
+
| Beast server | NO | N/A | Free | N/A |
|
| 326 |
+
|
| 327 |
+
**Recommendation:** Use LoRA with Unsloth on the current 1x H200 pod. The quality
|
| 328 |
+
difference between LoRA rank-16 and full SFT is minimal for SFT tasks, and the
|
| 329 |
+
training will actually complete instead of being killed.
|
| 330 |
+
|
| 331 |
+
---
|
| 332 |
+
|
| 333 |
+
## Sources
|
| 334 |
+
|
| 335 |
+
- [ms-swift#6473: System RAM OOM with Qwen3-30B-A3B](https://github.com/modelscope/ms-swift/issues/6473)
|
| 336 |
+
- [DeepSpeed#2870: ZeRO-3 MoE unsupported](https://github.com/microsoft/DeepSpeed/issues/2870)
|
| 337 |
+
- [DeepSpeed#7156: Expert optimizer state partitioning request](https://github.com/deepspeedai/DeepSpeed/issues/7156)
|
| 338 |
+
- [DeepSpeed#2899: ZeRO-3 OOM with 30B model](https://github.com/microsoft/DeepSpeed/issues/2899)
|
| 339 |
+
- [DeepSpeed#7021: CPU offload OOM with ZeRO-3](https://github.com/deepspeedai/DeepSpeed/issues/7021)
|
| 340 |
+
- [DeepSpeed ZeRO-3 Documentation](https://deepspeed.readthedocs.io/en/latest/zero3.html)
|
| 341 |
+
- [RunPod GPU Pricing (confirms 276 GB RAM per H200)](https://www.runpod.io/pricing)
|
| 342 |
+
- [RunPod OOM Guide](https://www.runpod.io/articles/guides/avoid-oom-crashes-for-large-models)
|
| 343 |
+
- [RunPod Resource Selection](https://www.runpod.io/blog/avoid-pod-errors-runpod-resources)
|
| 344 |
+
- [Unsloth MoE Fine-tuning](https://unsloth.ai/docs/basics/faster-moe)
|
| 345 |
+
- [Qwen3.6-35B-A3B Specs](https://apxml.com/models/qwen36-35b-a3b)
|
| 346 |
+
- [Qwen3.5 MoE vs Dense Fine-tuning](https://medium.com/@ishaafsalman/qwen3-5-fine-tuning-in-2026-moe-vs-dense-b2d17de73a9e)
|
| 347 |
+
- [ms-swift Qwen3.5 Best Practices](https://swift.readthedocs.io/en/latest/BestPractices/Qwen3_5-Best-Practice.html)
|
| 348 |
+
- [Megatron Core MoE Training (arxiv:2603.07685)](https://arxiv.org/pdf/2603.07685)
|
| 349 |
+
- [DeepSeek Memory Analysis (arxiv:2502.07846)](https://arxiv.org/pdf/2502.07846)
|