LeetLLM
My PlanLearnGlossaryTracksPracticeBlog
LeetLLM

Your go-to resource for mastering AI & LLM systems.

Product

  • Learn
  • Glossary
  • Tracks
  • Practice
  • Blog
  • RSS

Legal

  • Terms of Service
  • Privacy Policy

© 2026 LeetLLM. All rights reserved.

All Topics
Your Progress
0%

0 of 196 articles completed

🛠️Computing Foundations0/9
Git, Shell, Linux for AIDocker for Reproducible AIPython for AI EngineeringNumPy and Tensor ShapesCUDA for ML TrainingMPS & Metal for ML on MacData Structures for AISQL and Data ModelingAlgorithms for ML Engineers
📊Math & Statistics0/9
Gradients and BackpropVectors, Matrices & TensorsLinear Algebra for MLAdam, Momentum, SchedulersProbability for Machine LearningBayesian Inference from EvidenceStatistics and UncertaintyDistributions and SamplingHypothesis Tests, Intervals, and pass@k
📚Preparation & Prerequisites0/13
Neural Networks from ScratchCNNs from ScratchTraining & BackpropagationSoftmax, Cross-Entropy & OptimizationRNNs, LSTMs, GRUs, and Sequence ModelingAutoencoders and VAEsThe Transformer Architecture End-to-EndLanguage Modeling & Next TokensFrom GPT to Modern LLMsPrompt Engineering FundamentalsCalling LLM APIs in ProductionFirst AI App End-to-EndThe LLM Lifecycle
🧮ML Algorithms & Evaluation0/15
Linear Regression from ScratchLogistic Regression and MetricsDecision Trees, Forests, and BoostingReinforcement Learning BasicsValidation and LeakageStatistical Learning and GeneralizationConformal Prediction and UncertaintyClustering and PCAProbabilistic Generative ModelsCore Retrieval AlgorithmsDecoding AlgorithmsExperiment Design and A/B TestingCausal Inference and CounterfactualsPyTorch Training LoopsDataset Pipelines and Data Quality
📦Production ML Systems0/6
Feature Engineering for Production MLBatch and Streaming Feature PipelinesGradient Boosted Trees in ProductionRanking and Recommendation SystemsForecasting and Anomaly DetectionMonitoring Predictive Models
🧪Core LLM Foundations0/8
The Bitter Lesson & ComputeBPE, WordPiece, and SentencePieceStatic to Contextual EmbeddingsPerplexity & Model EvaluationFile Ingestion for AIChunking StrategiesLLM Benchmarks & LimitationsInstruction Tuning & Chat Templates
🧰Applied LLM Engineering0/25
Dimensionality Reduction for EmbeddingsCoT, ToT & Self-Consistency PromptingFunction Calling & Tool UseMCP & Tool Protocol StandardsContext EngineeringPrompt Injection DefenseResponsible AI GovernancePrivacy-Preserving Machine LearningData Labeling and Human FeedbackEvaluating AI AgentsProduction RAG PipelinesHybrid Search: Dense + SparseReranking and Cross-Encoders for RAGRAG Evaluation for Reliable AnswersLLM-as-a-Judge EvaluationBias & Fairness in LLMsHallucination Detection & MitigationLLM Observability & MonitoringExperiment Tracking with MLflow and W&BPrompt Optimization with DSPyModel Versioning & DeploymentSemantic Caching & Cost OptimizationLLM Cost Engineering & Token EconomicsModel Gateways, Routing, and FallbacksDesign an Automated Support Agent
🎓Portfolio Capstones0/9
Capstone: Delivery ETA PredictionCapstone: Product RankingCapstone: Demand ForecastingCapstone: Image Damage ClassifierCapstone: Production ML PipelineCapstone: Document QACapstone: Eval DashboardCapstone: Fine-Tuned ClassifierCapstone: Reproducible ML Study
🧠Transformer Deep Dives0/8
Sentence Embeddings & Contrastive LossEmbedding Similarity & QuantizationScaled Dot-Product AttentionVision Transformers and Image EncodersPositional Encoding: RoPE & ALiBiLayer Normalization: Pre-LN vs Post-LNMechanistic InterpretabilityDecoding Strategies: Greedy to Nucleus
🧬Advanced Training & Adaptation0/17
Scaling Laws & Compute-Optimal TrainingPre-training Data at ScaleBuild GPT from Scratch LabJAX for PyTorch ResearchersContinued Pretraining for Domain ShiftSynthetic Data PipelinesSupervised Fine-Tuning PipelineMixed Precision TrainingDistributed Training: FSDP & ZeROLoRA & Parameter-Efficient TuningTraining Run OperationsReward Modeling from Preference DataRLHF & DPO AlignmentConstitutional AI & Red TeamingRLVR & Verifiable RewardsKnowledge Distillation for LLMsModel Merging and Weight Interpolation
🤖Advanced Agents & Retrieval0/16
Vector DB Internals: HNSW & IVFAdvanced RAG: HyDE & Self-RAGGraphRAG & Knowledge GraphsRAG Security & Access ControlStructured Output GenerationReAct & Plan-and-ExecuteGuardrails & Safety FiltersCode Generation & SandboxingComputer-Use / GUI / Browser AgentsHuman-in-the-Loop Agent ArchitectureAI Coding Workflow with AgentsAgent Memory & PersistenceAgent Failure & RecoveryRecursive Language Models (RLM)Multi-Agent OrchestrationCapstone: Production Agent
⚡Inference & Production Scale0/31
Inference: TTFT, TPS & KV CacheMulti-Query & Grouped-Query AttentionKV Cache & PagedAttentionPrefix Caching and Prompt CachingFlashAttention & Memory EfficiencyContinuous Batching & SchedulingScaling LLM InferenceModel Parallelism for LLM InferenceAccelerator Architecture Field GuideCorrect GPU Profiling and BenchmarkingGPU Kernel Performance Engineering LabGPU Parallel Primitives LabGPU Compilers and Kernel LanguagesGEMM From Naive CUDA to Tensor CoresLow-Precision GPU ProgrammingGPU Runtime Execution LabAI-Generated Kernel EngineeringModel Quantization: GPTQ, AWQ & GGUFLocal LLM DeploymentSLM Specialization & Edge DeploymentSpeculative DecodingLong Context Window ManagementMixture of Experts ArchitectureMamba & State Space ModelsReasoning & Test-Time ComputeAdvanced MLOps & DevOps for AIGPU Serving & AutoscalingGPU Failure OperationsDistributed Inference Data PlaneServing Benchmark EngineeringA/B Testing for LLMs
🏗️System Design Capstones0/9
Content Moderation SystemCode Completion SystemMulti-Tenant LLM PlatformLLM-Powered Search EngineVision-Language Models & CLIPMultimodal LLM ArchitectureDiffusion Models: Images & TextReal-Time Voice AI AgentReasoning Agent System Design
🎤AI Lab Interviewing0/4
AI Lab Coding Interview: Python SystemsAI Lab System Design InterviewAI Lab Behavioral InterviewAI Lab Technical Presentation
🔬Project Deep Dives0/17
Deep Dive - vLLMDeep Dive - SkyRLDeep Dive - FlashAttentionDeep Dive - FlashInferDeep Dive - DeepGEMMDeep Dive - NCCLDeep Dive - MegatronDeep Dive - DeepSpeedDeep Dive - RayDeep Dive - MLflowDeep Dive - PyTorchDeep Dive - TransformersDeep Dive - SGLangDeep Dive - slimeDeep Dive - DeepEPDeep Dive - TinkerDeep Dive - Light-PEFT
Back to Topics
LearnAdvanced Training & AdaptationTraining Run Operations
⚡HardFine-Tuning & Training

Training Run Operations

Recover training state after interruption, budget checkpoint time, preserve effective batch and scheduler progress, and choose an adaptation recipe that fits the data and memory.

25 min read
Learning path
Step 113 of 196 in the full curriculum
LoRA & Parameter-Efficient TuningReward Modeling from Preference Data

Personalize this lesson

Adapt explanations and teaching visuals to your background and preferred voice.

It's 3:15 AM. Your training cluster dies. You check the storage bucket: yesterday's weights are intact. You load them into your script, hit resume, and watch yesterday's training samples replay from the start while the learning rate restarts its warmup from zero. The model weights survived, but the training run died.

We'll use a concrete run named policy-sft-v4 to walk through every recovery boundary: four data-parallel replicas, two sequences per microbatch, eight microbatches per accumulation window, and a committed checkpoint at update 1840. The same operational rules govern full-weight pretraining and parameter-efficient fine-tuning (LoRA): which state survived, which data sample comes next, and did your replacement hardware alter the optimization trajectory?

Three-tier asynchronous checkpointing and preemption contract. Sharded state is copied from GPU VRAM to pinned host RAM in 3 seconds, unblocking GPU compute. A background thread writes shards to storage and updates an atomic manifest. A 120-second preemption warning can finish the current accumulation window and commit step 1841; an abrupt crash restores step 1840 from the atomic pointer.
Work completed in memory isn't necessarily recoverable. This run saves at optimizer-update boundaries; its recovery point advances only after the corresponding state reaches storage.

Weights are only one part of a continuation

Training loops introduced the optimizer and scheduler. A restart must restore their internal state, not merely reconstruct the model architecture. You need to distinguish three operations before calling a checkpoint loader:

OperationState you needMeaning of the next update
Continue trainingModel weights, optimizer moments, scheduler counters, progress counters, data cursor, RNG states, scaler stateContinue from a recorded training boundary
Initialize a new runCompatible weights or adapters and their base modelStart fresh optimization from those parameters
Export for inferenceWeights or adapters, tokenizer, configuration, and serving assetsNo optimizer update is implied

For AdamW, optimizer state includes the first and second gradient moments (mmm and vvv) alongside coordinate step counters [1]Reference 1Decoupled Weight Decay Regularization.https://arxiv.org/abs/1711.05101. For mixed precision, you must include the gradient scaler when the recipe uses one. Save per-rank random number generator (RNG) states and the data loader resumable cursor. Preserve the model, tokenizer, chat template, dataset slice, and training arguments needed to interpret them. Evaluation history helps select a checkpoint, but it isn't a substitute for optimization state.

Adapter checkpoints also require the correct frozen base weights and adapter configuration. A merged inference export isn't a resumable adapter checkpoint. Tooling frameworks distinguish intermediate training checkpoints from final exports; torchtune documents separate resume requirements for full-weight and LoRA recipes [2]Reference 2Checkpointing in torchtune.https://meta-pytorch.org/torchtune/stable/deep_dives/checkpointer.html.

Continuation isn't a guarantee of bitwise replay. A different cluster topology can change all-reduce reduction trees, random streams, and data partition assignments. Restore the intended state, document any topology changes, and compare loss trajectories against an acceptable tolerance. Exact replay demands strict control over CUDA kernels, non-deterministic operations, and sample batching.

What lost optimizer moments break

What happens when you restore weights but let AdamW start with zeroed moments? AdamW normalizes gradient steps by running second moments: θt+1=θt−ηt(m^tv^t+ϵ+λθt)\theta_{t+1} = \theta_t - \eta_t \left( \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon} + \lambda \theta_t \right)θt+1​=θt​−ηt​(v^t​​+ϵm^t​​+λθt​) When mmm and vvv reset to zero at step 1840, the bias-corrected variance v^1\hat{v}_1v^1​ collapses to (1−β2)g12/(1−β2)=g12(1 - \beta_2) g_1^2 / (1 - \beta_2) = g_1^2(1−β2​)g12​/(1−β2​)=g12​. In coordinates where gradients are tiny, dividing by g12+ϵ\sqrt{g_1^2} + \epsilong12​​+ϵ creates massive, erratic step sizes. In steep directions where past variance previously damped updates, the optimizer stumbles. The loss curve shows an artificial jump that wastes hundreds of steps re-accumulating historical momentum.

This scalar Adam example minimizes (w−y)2/2(w-y)^2/2(w−y)2/2 over a repeating target sequence. It tracks first and second moments, bias-correction steps, a step-based learning rate, and a consumed data cursor. JSON round-tripping models a serialized restart:

adam_resume.py
1import json 2import math 3 4TARGETS = [1.0, -2.0, 3.0, 0.5] 5 6def fresh(w=0.0): 7 return dict(w=w, m=0.0, v=0.0, step=0, cursor=0) 8 9def advance(state, updates): 10 if type(updates) is not int or updates < 0: 11 raise ValueError("updates must be a nonnegative integer") 12 state = dict(state) 13 for _ in range(updates): 14 g = state["w"] - TARGETS[state["cursor"] % len(TARGETS)] 15 state["step"] += 1 16 t = state["step"] 17 state["m"] = 0.9 * state["m"] + 0.1 * g 18 state["v"] = 0.999 * state["v"] + 0.001 * g * g 19 m_hat = state["m"] / (1 - 0.9 ** t) 20 v_hat = state["v"] / (1 - 0.999 ** t) 21 lr = 0.05 / (1 + 0.1 * (t - 1)) 22 state["w"] -= lr * m_hat / (math.sqrt(v_hat) + 1e-8) 23 state["cursor"] += 1 24 return state 25 26saved = advance(fresh(), 7) 27restored = json.loads(json.dumps(saved)) 28resumed = advance(restored, 5) 29baseline = advance(fresh(), 12) 30assert resumed == baseline 31 32# Isolate missing moments: keep the weights, step and data cursor. 33lost_moments = dict(restored, m=0.0, v=0.0) 34wrong = advance(lost_moments, 5) 35assert abs(wrong["w"] - baseline["w"]) > 1e-4 36assert advance(restored, 0) == restored 37print(f"continuous={baseline['w']:.6f}; resumed={resumed['w']:.6f}") 38print(f"reset moments={wrong['w']:.6f}; next cursor={resumed['cursor']}")
Continuation versus fresh moments
1continuous=0.125436; resumed=0.125436 2reset moments=0.126672; next cursor=12

Even keeping the step counter doesn't reconstruct the lost moments. In a real cluster restore drill, compare the next sample IDs, current learning rate, optimizer moments, and next gradient update against an uninterrupted reference run. A smooth-looking loss curve doesn't prove mathematical continuity.

Fault tolerance, cluster MTBF, and asynchronous checkpointing

Training large models requires reckoning with cluster reliability physics. An individual GPU server might average a Mean Time Between Failures (MTBF) of roughly 26,000 hours (about 3 years). On a small 32-GPU pool, an unrecoverable failure strikes once every 800 hours (about a month).

At scale, the failure arithmetic changes drastically. In the Meta Llama 3 405B training run, 16,384 H100 GPUs trained concurrently [3]Reference 3The Llama 3 Herd of Models.https://arxiv.org/abs/2407.21783. Cluster MTBF scales inversely with device count: MTBFcluster=MTBFsingleNGPUs=26000 hours16384≈1.59 hours\text{MTBF}_{\mathrm{cluster}} = \frac{\text{MTBF}_{\mathrm{single}}}{N_{\mathrm{GPUs}}} = \frac{26000\text{ hours}}{16384} \approx 1.59\text{ hours}MTBFcluster​=NGPUs​MTBFsingle​​=1638426000 hours​≈1.59 hours When hardware faults strike every 90 to 120 minutes, synchronous blocking checkpointing becomes an operational bottleneck. If each checkpoint takes 12 minutes to serialize and upload, saving every hour consumes 20% of your cluster budget in pure idle time, with another 10% lost replaying uncommitted steps.

Modern distributed frameworks solve this dilemma through a three-tier asynchronous checkpointing pipeline:

  1. Tier 1 (GPU HBM): Live model parameters and optimizer states mutate during forward and backward passes.
  2. Tier 2 (Host Pinned CPU Memory): At the checkpoint step, each rank copies its sharded state tensors across PCIe into host pinned memory using non-blocking CUDA streams. On PCIe Gen5 links, copying 100 GB of sharded weights and optimizer state takes roughly 3 to 5 seconds. Once the device-to-host (D2H) copy finishes, the GPU unblocks and immediately begins the next microbatch.
  3. Tier 3 (Distributed Remote Storage): An asynchronous background thread pool streams host memory buffers over the storage fabric (Lustre, Ceph, GPFS, or object store buckets) over the next several minutes without taking GPU compute cycles.

Atomic manifest publishing and data cursor alignment

Never write sharded files directly into the active checkpoint directory. An unexpected node crash mid-write leaves truncated files that crash subsequent restore attempts.

Each rank writes its local shard into an isolated staging directory (such as checkpoints/step_1840.tmp/). Ranks flush and fsync their files. When all ranks confirm completion, Rank 0 publishes an authoritative manifest file or atomically updates a directory symlink (checkpoints/latest -> step_1840). If preemption or a host kernel panic hits during storage writes, the recovery job ignores the incomplete .tmp directory and rolls back cleanly to the last committed manifest pointer.

A clean weight restore still fails if the data cursor drifts. PyTorch data loaders with multi-worker prefetching buffer dozens of batches ahead in host memory. Recording the main thread iteration number doesn't tell you how many samples were actually consumed by the model before preemption. The checkpoint manifest must store the deterministic data loader state: the random epoch seed, the active shard file index, and the exact byte offset or sample ID processed at the completed optimizer boundary.

Preemption is a strict deadline, not a save command

Suppose update 1840 is durably committed to storage, and a preemption warning signal (such as a cloud spot termination notice or a cluster scheduler SIGTERM) arrives after microbatch 5 of the next 8-microbatch accumulation window.

Your training policy saves only at completed optimizer boundaries. There are two valid operational outcomes:

  1. All nodes remain healthy and there's enough time to complete the remaining 3 microbatches, perform the optimizer step, stage the checkpoint to host RAM, and update the manifest for step 1841.
  2. Time runs out or a rank dies abruptly. The orchestrator terminates, discards partial in-flight state, and reboots from durable step 1840, rewinding the data cursor to sample 117,760.

Never pair weights from step 1840 with a data cursor that advanced past microbatch 5. That mistake silently drops the gradient contributions of those five microbatches without ever applying them to the model weights. Saving mid-window requires persisting unscaled accumulated gradients, the microbatch accumulation counter, and exact intermediate RNG states. Unless your framework explicitly supports and tests mid-window recovery, stick to completed update boundaries.

Signal handlers should only set an internal shutdown flag, not initiate distributed network I/O. The main training loop coordinates the save at the next clean accumulation boundary. When an uncoordinated hardware failure kills a rank, surviving ranks can't complete collective all-reduce operations; periodic durable checkpoints remain mandatory even with preemption warning systems.

Budget the entire remaining path

A 90-second checkpoint doesn't fit into a 90-second warning if you also need to finish active microbatches and publish manifests. The preemption budget equation is: Twarning≥Twindow+Tsave+TmarginT_{\mathrm{warning}} \ge T_{\mathrm{window}} + T_{\mathrm{save}} + T_{\mathrm{margin}}Twarning​≥Twindow​+Tsave​+Tmargin​ Here TsaveT_{\mathrm{save}}Tsave​ includes time through durable publication, and TmarginT_{\mathrm{margin}}Tmargin​ absorbs network and disk jitter:

preemption_budget.py
1import math 2 3def can_finish(grace, window, save, margin): 4 times = (grace, window, save, margin) 5 if any(isinstance(t, bool) or not isinstance(t, (int, float)) 6 or not math.isfinite(t) or t < 0 for t in times): 7 raise ValueError("times must be finite nonnegative seconds") 8 return window + save + margin <= grace 9 10assert can_finish(120, 20, 90, 10) 11assert not can_finish(119, 20, 90, 10) 12assert not can_finish(90, 20, 90, 10) 13print("120s warning: feasible at the estimate; 119s: insufficient") 14for productive_seconds in (300, 1200): 15 fraction = 90 / (productive_seconds + 90) 16 print(f"{productive_seconds}s compute + 90s save: {fraction:.1%} save overhead")
Warning-period budget
1120s warning: feasible at the estimate; 119s: insufficient 2300s compute + 90s save: 23.1% save overhead 31200s compute + 90s save: 7.0% save overhead

A blocking save every five minutes of compute spends 23.1% of cluster wall-clock time writing files. Increasing the interval to twenty minutes drops save overhead to 7.0%, but exposes up to twenty minutes of uncheckpointed work to hardware failure. Choose your checkpoint interval based on measured save latency, cluster MTBF, and acceptable replay cost.

Sharded saves need a compatible restore path

In distributed training with Fully Sharded Data Parallel (FSDP) or DeepSpeed ZeRO-3, no individual GPU holds the complete set of parameters or optimizer moments [4]Reference 4ZeRO: Memory Optimizations Toward Training Trillion Parameter Models.https://arxiv.org/abs/1910.02054. This raises two operational questions: did the save complete reliably, and can your restore loader interpret the sharded layout?

The naive approach gathers all shards to Rank 0 to write a single monolithic state dictionary. For a 70B parameter model, the weights take 140 GB in BF16, and the AdamW moments take 280 GB in FP32. Gathering 420 GB of state onto Rank 0 causes an instant Out-Of-Memory (OOM) crash or stalls the cluster for twenty minutes over host PCIe busses.

PyTorch Distributed Checkpoint (DCP) avoids this bottleneck by saving sharded state directly from each GPU [5]Reference 5PyTorch Distributed Checkpoint (DCP) Recipehttps://docs.pytorch.org/tutorials/recipes/distributed_checkpoint_recipe.html. Each rank writes its local tensor slice alongside global coordinate metadata: the fully qualified parameter name, global tensor dimensions, data type, and the coordinate bounding box (such as [0:4096, 0:2048]).

PyTorch Distributed Checkpoint (DCP) resharding across topologies. A tensor with 16 elements is saved across 4 source ranks as slices [0:4], [4:8], [8:12], and [12:16]. Resuming on 8 destination ranks uses global coordinate offsets from the metadata manifest to stream slices [0:2] through [14:16] in parallel, avoiding a single-node memory bottleneck.
The logical tensor stays the same while ownership changes. Model and optimizer mappings must both survive; moving the weight slices alone doesn't restore Adam.

Resharding across topologies without single-node bottlenecks

When a cluster shrinks or grows (for instance, resuming a 4-node run on 8 nodes after replacement hardware arrives, or dropping from 8 nodes to 7 after a node failure), your loader must reshard parameters across different world sizes (N≠MN \ne MN=M).

Under DCP, resharding happens without central gathering:

  1. The destination model initializes its partitioned DTensor structures according to the new MMM-rank topology.
  2. Each destination rank consults the shared .metadata index file.
  3. Each rank calculates which source shard files intersect its assigned tensor slice.
  4. All MMM ranks read their required byte ranges directly and concurrently from shared storage.

DCP doesn't guarantee backward compatibility across arbitrary PyTorch major versions [6]Reference 6Distributed Checkpointhttps://docs.pytorch.org/docs/stable/distributed.checkpoint.html. Exercise a 4-to-8 rank restore drill on your target software stack before launching multi-week training. DeepSpeed Universal Checkpointing provides similar cross-topology conversion for ZeRO runs, requiring an explicit conversion step rather than reading arbitrary rank files [7]Reference 7Universal Checkpointing with DeepSpeed: A Practical Guide.https://www.deepspeed.ai/tutorials/universal-checkpointing/.

This code demonstrates the coordinate-indexing principle by slicing a 16-element vector into four source shards and reconstructing eight destination shards:

reshard_offsets.py
1def reshard(shards, size, destinations): 2 if type(size) is not int or size <= 0: 3 raise ValueError("positive tensor size required") 4 if type(destinations) is not int or destinations <= 0 or size % destinations: 5 raise ValueError("this toy requires equal nonempty destination shards") 6 tensor = [None] * size 7 for start, values in shards: 8 if type(start) is not int or start < 0 or start + len(values) > size: 9 raise ValueError("invalid range") 10 for offset, value in enumerate(values, start): 11 if tensor[offset] is not None: 12 raise ValueError("overlapping ranges") 13 if value is None: 14 raise ValueError("None is reserved for a missing element") 15 tensor[offset] = value 16 if any(value is None for value in tensor): 17 raise ValueError("missing range") 18 width = size // destinations 19 return [(i, tensor[i:i + width]) for i in range(0, size, width)] 20 21saved = [(i, list(range(i, i + 4))) for i in range(0, 16, 4)] 22loaded = reshard(saved, 16, 8) 23assert [v for _, values in loaded for v in values] == list(range(16)) 24assert reshard(loaded, 16, 4) == saved 25for broken in (saved[:-1], saved + [saved[0]]): 26 try: 27 reshard(broken, 16, 8) 28 except ValueError as error: 29 print(error) 30 else: 31 raise AssertionError("incomplete or overlapping state was accepted") 32print(loaded)
Logical ranges survive a new layout
1missing range 2overlapping ranges 3[(0, [0, 1]), (2, [2, 3]), (4, [4, 5]), (6, [6, 7]), (8, [8, 9]), (10, [10, 11]), (12, [12, 13]), (14, [14, 15])]

Keep the update size separate from the GPU count

For complete accumulation windows with equal microbatch sizes: Bglobal=Bmicro×K×DreplicaB_{\mathrm{global}} = B_{\mathrm{micro}} \times K \times D_{\mathrm{replica}}Bglobal​=Bmicro​×K×Dreplica​ Here BmicroB_{\mathrm{micro}}Bmicro​ is the sequence count per device, KKK is the gradient accumulation steps per update, and DreplicaD_{\mathrm{replica}}Dreplica​ is the count of independent data-parallel replicas.

Don't confuse total physical GPUs with data-parallel degree. In 3D parallelism, tensor-parallel (TP\mathrm{TP}TP) and pipeline-parallel (PP\mathrm{PP}PP) workers collaborate on a single model instance: Dreplica=NGPUsTP×PP×CPD_{\mathrm{replica}} = \frac{N_{\mathrm{GPUs}}}{\mathrm{TP} \times \mathrm{PP} \times \mathrm{CP}}Dreplica​=TP×PP×CPNGPUs​​ Eight GPUs configured with TP=2\mathrm{TP}=2TP=2 and PP=1\mathrm{PP}=1PP=1 form D=4D=4D=4 replicas, not eight. Halving accumulation steps while keeping D=4D=4D=4 cuts the global batch in half.

Global batch size decomposition and 3D parallelism mapping. Eight GPUs configured as tensor parallel 2 and data parallel 4 yield 4 independent replicas. Each replica accumulates 8 microbatches of 2 sequences, producing 64 sequences per optimizer update. Halving accumulation steps while doubling data-parallel replicas preserves the 64-sequence global batch, whereas doubling replicas alone scales the batch to 128.
Eight microbatches form one accumulation window. More replicas can shorten that window without changing the nominal global batch.
Microbatch BμB_{\mu}Bμ​Accumulation KKKData-Parallel DDDGlobal Sequences per Update
28464
288128
24864

Sequences versus supervised tokens and loss reduction

Sequence counts don't equal token throughput. In instruction tuning and conversational SFT, sequence packing, system prompt masking, and variable response lengths mean that two batches with identical sequence counts carry different numbers of loss-bearing tokens: Tokens per update=∑i=1BglobalLengthunmasked(i)\text{Tokens per update} = \sum_{i=1}^{B_{\mathrm{global}}} \text{Length}_{\mathrm{unmasked}}(i)Tokens per update=∑i=1Bglobal​​Lengthunmasked​(i) If replica 0 processes 4,800 supervised tokens while replica 1 processes 9,600, averaging their per-device mean loss values equally computes 12(Lˉ0+Lˉ1)\frac{1}{2}(\bar{\mathcal{L}}_0 + \bar{\mathcal{L}}_1)21​(Lˉ0​+Lˉ1​). That naive average assigns twice as much weight to tokens on replica 0 as tokens on replica 1, distorting the true gradient.

Distributed loss computation must use global token-weighted reduction: Lglobal=∑r∑jLr,j∑rNtokens,r\mathcal{L}_{\mathrm{global}} = \frac{\sum_{r} \sum_{j} \mathcal{L}_{r, j}}{\sum_{r} N_{\mathrm{tokens}, r}}Lglobal​=∑r​Ntokens,r​∑r​∑j​Lr,j​​ Frameworks handle this by executing an all-reduce sum over unmasked token counts before dividing the accumulated loss. Don't divide by accumulation steps twice when the framework already normalizes by global tokens.

A 4-replica job with microbatch 2 and accumulation 8 restarts on eight GPUs configured as tensor parallel 2 and data parallel 4. Should you halve accumulation to preserve the batch?

Answer

No. Data parallelism is still four, so the batch remains 2 × 8 × 4 = 64. Halving accumulation would reduce it to 32. Count independent replicas, not devices.

Preserve the scheduler before tuning a new batch

When continuing a run at the same effective batch size, restore the scheduler phase directly. Don't restart warmup just because the process restarted.

If you intentionally double the global batch size, you're changing the optimization problem. At a fixed token budget, doubling sequences per update halves the total number of updates. A learning rate schedule defined in steps will now traverse through warmup and decay twice as fast relative to data volume. Always log whether schedules are indexed by optimizer updates, examples, or total consumed tokens.

Why AdamW rejects linear learning rate scaling

The popular linear scaling rule (η∝B\eta \propto Bη∝B) originated in large-batch stochastic gradient descent (SGD) experiments for computer vision. Goyal et al. scaled ResNet-50 minibatch size up to 8,192 on 256 GPUs by multiplying the learning rate by kkk when multiplying batch size by kkk, pairing the rule with gradual warmup [8]Reference 8Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour.https://arxiv.org/abs/1706.02677. In SGD, a kkk-times larger batch reduces gradient noise variance by 1/k1/k1/k. Taking a step kkk times larger in the same direction approximates taking kkk consecutive small-batch steps when curvature is gentle.

That logic doesn't hold for AdamW SFT:

  1. Adaptive coordinate normalization: AdamW divides gradients by running second moment roots (vt+ϵ\sqrt{v_t} + \epsilonvt​​+ϵ). Increasing batch size shrinks gradient variance, but the magnitude of normalized updates remains bounded by coordinate geometry.
  2. Square root scaling: Empirical and theoretical studies demonstrate that for adaptive optimizers, learning rate scaling typically follows a square root rule (η∝B\eta \propto \sqrt{B}η∝B​) or an exponent between 0.5 and 0.7. Doubling the batch and doubling AdamW's learning rate frequently triggers divergence or sharp quality degradation.
  3. Critical batch size limits: McCandlish et al. formalized the critical batch size B∗B^*B∗, governed by the gradient noise scale [9]Reference 9An Empirical Model of Large-Batch Training.https://arxiv.org/abs/1812.06162: Bnoise=Tr(Σ)∣G∣2\mathcal{B}_{\mathrm{noise}} = \frac{\text{Tr}(\Sigma)}{|G|^2}Bnoise​=∣G∣2Tr(Σ)​ When batch size is below B∗B^*B∗, increasing batch size yields roughly linear speedup in wall-clock time. As batch size approaches and exceeds B∗B^*B∗, gradient noise is already saturated; further batch increases yield diminishing returns where more compute no longer reduces the total optimization steps needed.

Hardware telemetry and run diagnostics: silent killers at scale

A loss spike doesn't reveal its cause on its own. On large GPU clusters, hardware degradation and silent data anomalies cause subtle failures that look like modeling divergence.

SymptomRoot cause investigationAction
Same weights, different first LRScheduler and optimizer parameter-group restore; warmup resetVerify step counter and scheduler state dictionary
Old sample IDs reappearConsumed cursor, sampler epoch seed, prefetch buffer stateRewind cursor to exact completed update boundary
Straggler on one rankThermal throttling (clocks dropping to 1050 MHz) or PCIe degradationInspect nvidia-smi -q -d PERFORMANCE for throttling reasons
Spiking all-reduce latencyInfiniBand link flapping or degraded SerDes transceiver bit errorsCheck IB switch port error counters (SymbolErrors, LinkDowned)
Loss NaN on a clean batchZero supervised tokens or nonfinite gradient under FP16Check loss scale factor, token count masks, and inputs
Sudden loss divergenceSilent Data Corruption (SDC) from ALU/tensor core micro-defectsRun canary GEMM tests across ranks; trigger automated rollback

In bulk-synchronous parallel execution (All-Reduce or Reduce-Scatter), every GPU in the replica group waits at the barrier until the slowest GPU finishes. A single GPU throttling its clocks from 1980 MHz to 1050 MHz due to fan failure or VRM overheating drags down the entire 16,384-GPU cluster. Continuous telemetry must track per-rank backward pass latency, NCCL barrier wait times, and GPU core clock distributions.

InfiniBand link flapping presents an even trickier failure mode. A dirty optical connector or loose fiber transceiver produces intermittent physical bit errors. The link renegotiates or drops from 400 Gbps (NDR) to 50 Gbps without throwing an unhandled socket exception. The job stays alive, but all-reduce latency jumps tenfold. Monitor physical fabric counters to evict degraded nodes automatically.

Silent Data Corruption (SDC) occurs when hardware defects or cosmic rays cause bit flips in compute ALUs without triggering ECC memory alerts [3]Reference 3The Llama 3 Herd of Models.https://arxiv.org/abs/2407.21783. An undetected bit flip during matrix multiplication can produce an erroneous activation or poisoned gradient. Canary batches (running an identical small matrix multiplication periodically across all ranks and verifying checksums) catch corrupt silicon before poisoned weights propagate to checkpoints.

Automated loss-spike rollback tripwires

When a loss spike hits at 3:00 AM, waiting for a human on-call engineer burns thousands of idle GPU hours. Modern training harnesses implement automated tripwire circuits:

  1. Anomaly tripwire: If step loss exceeds the rolling median by three standard deviations (Lt>median(Lt−50:t)+3σ\mathcal{L}_t > \text{median}(\mathcal{L}_{t-50:t}) + 3\sigmaLt​>median(Lt−50:t​)+3σ), or if unclipped gradient norm exceeds a threshold (∣∣g∣∣2>gmax||g||_2 > g_{\mathrm{max}}∣∣g∣∣2​>gmax​), pause the run immediately.
  2. Automated rewind: The orchestrator rewinds model and optimizer state to two checkpoints prior (t−2t - 2t−2), well before the destabilizing batch entered the accumulation window.
  3. Data quarantine: The offending data partition is logged, quarantined, and skipped in the data loader.
  4. Seed perturbation: The data loader perturbs its shuffle seed, and training resumes automatically. If a second spike hits immediately, the orchestrator triggers an alert and halts the job safely.

Choose the objective, trainable weights, and precision separately

Training recipes aren't a single dropdown choice. SFT and continued pretraining define what the model learns. LoRA defines which parameters update. QLoRA adds 4-bit base-model quantization to adapter tuning. Distillation defines learning from teacher logits [10]Reference 10Distilling the Knowledge in a Neural Network.https://arxiv.org/abs/1503.02531.

DecisionExamplesWhat it changes
Learning signalLabeled responses; domain text; teacher soft targetsOptimization objective and data distribution
Trainable parametersFull model weights; LoRA adaptersParameter memory, optimizer footprint, and capacity
Frozen-base precisionBF16; FP8; 4-bit NormalFloat (NF4)Base model memory, CUDA kernels, numerical behavior

Domain-adaptive pretraining can improve specialized comprehension, but unfamiliar vocabulary alone doesn't prove it's necessary [11]Reference 11Don't Stop Pretraining: Adapt Language Models to Domains and Tasks.https://aclanthology.org/2020.acl-main.740/. Small labeled datasets make LoRA SFT an attractive starting point, though adapters aren't guaranteed to beat full fine-tuning [12]Reference 12LoRA: Low-Rank Adaptation of Large Language Models.https://arxiv.org/abs/2106.09685.

Adaptation recipe composition and memory reality check. A recipe composes three orthogonal choices: learning signal (Response SFT), trainable parameters (LoRA adapters), and base precision (4-bit NF4). A 70B 4-bit base model requires at least 32.6 GiB for weights alone, proving that an all-resident training plan on a 24 GiB GPU is physically impossible before accounting for activations or adapters.
Pick a learning signal and parameterization, then check the actual memory budget. Quantization reduces the base-weight term; it doesn't eliminate it.

Reject impossible memory plans before launching

The original QLoRA paper demonstrated fine-tuning a 65B model on a single 48GB GPU [13]Reference 13QLoRA: Efficient Finetuning of Quantized LLMshttps://arxiv.org/abs/2305.14314. That empirical result doesn't mean a 70B model fits into a consumer 24 GiB GPU.

At an ideal four bits per parameter, a 70B model requires: Memorybase=70×109×4 bits8 bits/byte=35×109 bytes≈32.6 GiB\text{Memory}_{\mathrm{base}} = \frac{70 \times 10^9 \times 4\text{ bits}}{8\text{ bits/byte}} = 35 \times 10^9\text{ bytes} \approx 32.6\text{ GiB}Memorybase​=8 bits/byte70×109×4 bits​=35×109 bytes≈32.6 GiB Base weights alone exceed 24 GiB by 8.6 GiB before allocating a single byte for LoRA adapters, activations, gradients, optimizer moments, or KV caches.

weight_memory.py
1def weight_gib(parameters, bits): 2 if type(parameters) is not int or parameters <= 0: 3 raise ValueError("positive integer parameter count required") 4 if type(bits) is not int or bits <= 0: 5 raise ValueError("positive integer bit width required") 6 return ((parameters * bits + 7) // 8) / (1024 ** 3) 7 8large = weight_gib(70_000_000_000, 4) 9small = weight_gib(8_000_000_000, 16) 10assert large > 24 11assert small < 24 # Only the base fits this lower-bound check. 12assert weight_gib(1, 4) == 1 / (1024 ** 3) 13print(f"70B at 4 bits: at least {large:.1f} GiB; exceeds 24 GiB") 14print(f"8B at 16 bits: at least {small:.1f} GiB; training needs more")
Base-weight memory lower bounds
170B at 4 bits: at least 32.6 GiB; exceeds 24 GiB 28B at 16 bits: at least 14.9 GiB; training needs more

For 70B models on 24 GiB hardware, you need multi-GPU sharding or CPU offloading. Passing this arithmetic check doesn't guarantee your job fits; failing it guarantees an OOM crash.

Rehearse the restart before the long run

Don't wait for your first midnight preemption to discover that your checkpoint loader crashes. Run a restart drill on small test jobs:

  • Local state continuity: Compare five uninterrupted steps against five steps resumed from an intermediate checkpoint. Verify that weights, Adam moments, learning rates, and data sample IDs match within floating-point tolerance.
  • Distributed topology restoration: Save a checkpoint on four GPUs, then load it onto eight GPUs (or six GPUs). Ensure your DCP or ZeRO loader resshards state cleanly without Rank-0 memory exhaustion.
  • Preemption deadline drill: Trigger a simulated termination signal (kill -15). Verify that in-flight accumulation windows finish within the safety margin, the atomic manifest pointer updates, and partial .tmp files are ignored if killed prematurely.
  • Hardware telemetry and tripwire check: Verify that stragglers and simulated loss anomalies trigger automated rollback and quarantine routines rather than hanging the cluster.

Distinguish between your latest resumable checkpoint and your best evaluation checkpoint. Step 1840 might be your operational recovery point, while step 1600 holds your highest validation score. Store both with explicit metadata tags.

Diagram showing Checkpoint at step 1840, Restore model and optimizer state, Align data cursor, LR and batch, and Execute next intended update.
Checkpoint at step 1840, Restore model and optimizer state, Align data cursor, LR and batch, and Execute next intended update.
Complete the lesson

Mastery Check

Answer every question, then check your score. Score 75% or higher to mark this lesson complete.

1.A restart restores weights, the data cursor and step counter, but initializes Adam's moments to zero. What has changed?

Correct answer: The next parameter update can change.

Adam's update depends on accumulated first and second moments. Preserving the weights and step number does not reconstruct those moments.

2.A four-rank checkpoint is labelled DCP in an application manifest. What establishes that it can resume on eight ranks?

Correct answer: A supported loader and a successful target-topology restore test.

The label does not create tensor metadata or model/optimizer compatibility. Exercise the actual framework, state format and destination layout.

3.A run has 120 seconds of warning, 20 seconds of remaining computation, a 90-second durable save and a 15-second margin. Does its estimated recovery path fit?

Correct answer: No; the full path needs 125 seconds.

The budget is 20 + 90 + 15 = 125 seconds. A warning shorter than that cannot accommodate this planned path at the stated estimates.

4.Eight GPUs use tensor parallelism 2 and data parallelism 4. With microbatch 2 and accumulation 8, how many sequences contribute to a full update?

Correct answer: 64, because there are four data-parallel replicas.

The independent batch multiplier is the data-parallel degree: 2 × 8 × 4 = 64. Tensor-parallel workers cooperate on a replica.

5.A continuation doubles data-parallel replicas and halves accumulation, preserving nominal batch size. What is the starting scheduler policy?

Correct answer: Restore the saved scheduler phase and verify the update.

Process restart is not a new optimization schedule. Preserving batch size does not require a new LR scaling rule, though topology changes still need validation.

6.An FP16 scaler skips an optimizer step because gradients are nonfinite. The scheduler is defined per successful optimizer update. What should happen?

Correct answer: The successful-update count and scheduler remain unchanged.

No parameter update occurred. Track attempted windows and consumed data separately, and use the framework's supported overflow handling.

7.A 70B-parameter model is stored at an ideal four bits per weight. What follows for an all-resident 24 GiB training plan?

Correct answer: It cannot fit; base weights alone require about 32.6 GiB.

70 billion × 4/8 bytes is 35 billion bytes, approximately 32.6 GiB. Training requires additional memory beyond that lower bound.

8.Which recipe correctly separates learning signal, trainable parameters and frozen-base precision?

Correct answer: Response SFT, LoRA adapters, four-bit frozen base.

SFT supplies the objective and data, LoRA selects trainable adapters, and base quantization supplies the QLoRA storage choice. These choices compose.

9.An asynchronous checkpoint has finished copying training tensors into CPU staging buffers, but its storage write is pending. What can you conclude?

Correct answer: The staged copy is stable; durable recovery is not yet established.

Staging separates the snapshot from live mutations. The storage operation must finish before the checkpoint is published as recoverable.

10.A boundary-only checkpoint saves weights after update 1840 but records a cursor advanced through five microbatches of unfinished update 1841. What is the recovery risk?

Correct answer: Those five microbatches can be skipped without their gradients being applied.

The weights and data cursor describe different boundaries. Restore the cursor matching the saved update, or save a complete supported mid-window state.

10 questions remaining.

Next Step
Continue to Reward Modeling from Preference Data

Turn ranked answers into a training signal, then apply the same checkpoint, batch and restart checks to that run.

PreviousLoRA & Parameter-Efficient Tuning
Share this article
XFacebookLinkedInBlueskyRedditHacker NewsEmail
References

Decoupled Weight Decay Regularization.

Loshchilov, I., Hutter, F. · 2019 · ICLR 2019

https://arxiv.org/abs/1711.05101

Checkpointing in torchtune.

PyTorch Contributors · 2026

https://meta-pytorch.org/torchtune/stable/deep_dives/checkpointer.html

The Llama 3 Herd of Models.

Dubey, A., et al. · 2024 · arXiv preprint

https://arxiv.org/abs/2407.21783

ZeRO: Memory Optimizations Toward Training Trillion Parameter Models.

Rajbhandari, S., et al. · 2020 · SC 2020

https://arxiv.org/abs/1910.02054

PyTorch Distributed Checkpoint (DCP) Recipe

PyTorch Contributors · 2026

https://docs.pytorch.org/tutorials/recipes/distributed_checkpoint_recipe.html

Distributed Checkpoint

PyTorch Contributors · 2026

https://docs.pytorch.org/docs/stable/distributed.checkpoint.html

Universal Checkpointing with DeepSpeed: A Practical Guide.

DeepSpeed Team · 2026

https://www.deepspeed.ai/tutorials/universal-checkpointing/

Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour.

Goyal, P., Dollár, P., Girshick, R., Noordhuis, P., Wesolowski, L., Kyrola, A., Tulloch, A., Jia, Y., and He, K. · 2017

https://arxiv.org/abs/1706.02677

An Empirical Model of Large-Batch Training.

McCandlish, S., Kaplan, J., Amodei, D., and the OpenAI Dota Team · 2018

https://arxiv.org/abs/1812.06162

Distilling the Knowledge in a Neural Network.

Hinton, G., Vinyals, O., & Dean, J. · 2015

https://arxiv.org/abs/1503.02531

Don't Stop Pretraining: Adapt Language Models to Domains and Tasks.

Gururangan, S., Marasovic, A., Swayamdipta, S., et al. · 2020 · ACL 2020

https://aclanthology.org/2020.acl-main.740/

LoRA: Low-Rank Adaptation of Large Language Models.

Hu, E. J., et al. · 2021 · ICLR

https://arxiv.org/abs/2106.09685

QLoRA: Efficient Finetuning of Quantized LLMs

Dettmers, T., et al. · 2023 · NeurIPS

https://arxiv.org/abs/2305.14314

Discussion

Questions and insights from fellow learners.

Discussion loads when you reach this section.