How to Implement Gradient Accumulation to Simulate Larger Batch Sizes in RWKV-CLIP

Gradient accumulation in RWKV-CLIP is implemented through the --gradient-acc argument in train.py, which accumulates gradients over multiple forward-backward passes before executing an optimizer step, effectively simulating a larger batch size without increasing GPU memory consumption.

Training large-scale vision-language models like RWKV-CLIP often hits hardware memory limits before reaching optimal batch sizes for statistical convergence. The repository provides native support to implement gradient accumulation to simulate larger batch sizes, allowing you to process multiple micro-batches and aggregate their gradients before updating weights. This approach maintains the statistical benefits of large-batch training while keeping per-GPU memory requirements constant.

Configuring Gradient Accumulation via Command-Line Arguments

The entry point for gradient accumulation control is defined in the argument parser at line 1412 of train.py:

parser.add_argument("--gradient-acc", type=int, default=1)

When you set --gradient-acc to a value greater than 1, the training loop delays the optimizer step until the specified number of backward passes have accumulated. To enable accumulation, launch your training script with the desired accumulation factor:

python train.py \
    --batch-size 256 \
    --gradient-acc 4 \
    --output /path/to/checkpoint \
    [other arguments]

With this configuration, you achieve an effective batch size of 1024 (256 × 4) while only storing 256 samples in GPU memory at any given time.

Understanding the Training Loop Implementation

The accumulation logic resides in the main training loop of train.py, where the code distinguishes between BF16 and mixed-precision training paths. In both implementations, global_step.step increments every iteration (line 3223), but the optimizer updates only occur when the step count is divisible by the accumulation factor.

BF16 Precision Path

For bfloat16 training, the backward pass and gradient accumulation logic appears at lines 3004–3008:

if args.precision == "bf16":
    loss.backward()
    if global_step.step % args.gradient_acc == 0:
        torch.nn.utils.clip_grad_norm_(RWKV_CLIP_model.parameters(), 1)
        opt.step()
        opt.zero_grad()

Key behavior: The loss.backward() call executes on every iteration, accumulating gradients in the parameter tensors. The opt.step() and opt.zero_grad() operations are gated by the modulo condition, ensuring gradients sum over gradient_acc iterations before the weight update.

Mixed-Precision Path with GradScaler

When using automatic mixed precision (AMP) with FP16 or BF16, the repository utilizes torch.cuda.amp.GradScaler as shown at lines 3011–3016:

auto_scaler.scale(loss).backward()
if global_step.step % args.gradient_acc == 0:
    auto_scaler.unscale_(opt)
    torch.nn.utils.clip_grad_norm_(RWKV_CLIP_model.parameters(), 1)
    auto_scaler.step(opt)
    auto_scaler.update()
    opt.zero_grad()

Key behavior: The GradScaler scales the loss before backpropagation to prevent underflow. Gradients accumulate across multiple scale(...).backward() calls, and the scaler updates its statistics only after the accumulated step completes. This maintains numerical stability while achieving the effective batch size simulation.

Calculating Effective Batch Size

To determine your actual batch size during training, use the formula:

Effective Batch Size = Per-GPU Batch Size × Gradient Accumulation Steps × Number of GPUs

For example, with --batch-size 128, --gradient-acc 8, and 4 GPUs, you simulate a global batch of 4,096 samples (128 × 8 × 4) while processing only 128 samples per GPU per forward pass.

Optional: Normalizing Loss for Consistent Gradients

By default, RWKV-CLIP sums raw losses across accumulation steps, which produces gradients gradient_acc times larger than a single large batch. To maintain gradient magnitude equivalent to a true large batch, normalize the loss before backpropagation by modifying the loss computation in train.py (around line 3022):


# Add this line before the backward() call in both BF16 and mixed-precision branches

loss = loss / args.gradient_acc

This division ensures that gradient clipping thresholds and learning rate schedules behave consistently regardless of your accumulation factor.

Summary

  • Built-in support: RWKV-CLIP provides gradient accumulation via the --gradient-acc argument in train.py (line 1412), eliminating the need for external wrapper libraries.
  • Memory efficiency: You can simulate batch sizes 4×, 8×, or larger than your GPU memory allows by accumulating gradients over multiple steps.
  • Implementation details: The training loop checks global_step.step % args.gradient_acc == 0 to determine when to execute opt.step() and opt.zero_grad(), while loss.backward() runs every iteration.
  • Precision handling: Both BF16 and mixed-precision paths support accumulation, with GradScaler properly managing scale updates only on optimizer steps.
  • Optional scaling: Divide the loss by args.gradient_acc before backward() if you require gradient magnitudes consistent with non-accumulated training.

Frequently Asked Questions

What is the default value of --gradient-acc in RWKV-CLIP?

The default value is 1, which disables gradient accumulation and performs an optimizer step after every backward pass. You must explicitly set this to a value greater than 1 to implement gradient accumulation to simulate larger batch sizes.

Does gradient accumulation affect the learning rate schedule?

Yes, the learning rate scheduler (lr_scheduler.step()) updates every iteration in RWKV-CLIP's training loop, meaning the schedule advances based on the number of forward passes, not the number of optimizer steps. This ensures the learning rate decays appropriately relative to the data exposure, though you may need to adjust total training steps when significantly increasing accumulation factors.

How do I verify that gradients are accumulating correctly?

Monitor the global step counter and loss values in your training logs. With --gradient-acc 4, you should observe four forward-backward passes between each checkpoint save or logging interval, and the loss values should reflect the micro-batch losses rather than averaged across the accumulation window unless you implement the optional loss scaling.

Should I normalize the loss when using gradient accumulation?

Normalization is optional but recommended when using aggressive gradient clipping or when comparing experiments with different accumulation factors. Without normalization (the default), gradients scale linearly with the accumulation factor, which may require adjusting your clipping threshold in torch.nn.utils.clip_grad_norm_(). With normalization, you maintain consistent gradient magnitudes across different effective batch sizes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →