How the ClipLoss Contrastive Loss Function Is Implemented in rwkv-clip

The ClipLoss class in loss.py implements a distributed-aware contrastive loss that computes symmetric cross-entropy between normalized image and text embeddings, supporting multi-GPU feature gathering, cached ground-truth labels, and optional local-loss computation for memory efficiency.

The deepglint/rwkv-clip repository implements a RWKV-based vision-language model that relies on the ClipLoss contrastive loss function to align image and text representations. This loss module, defined in loss.py, extends torch.nn.Module to provide a scalable, distributed-training-compatible implementation of the symmetric cross-entropy objective originally introduced in OpenAI's CLIP.

ClipLoss Architecture and Core Components

The ClipLoss class orchestrates four primary operations: gathering features across distributed workers, generating ground-truth labels, computing similarity logits, and calculating the symmetric cross-entropy loss.

Feature Gathering Across Distributed GPUs

The helper function gather_features (lines 12‑55 in loss.py) collects image and text embeddings from all GPUs or Horovod workers. It supports two modes:

  • gather_with_grad=False (default): Features are gathered without gradient tracking, which is memory efficient but restricts gradients to the local rank.
  • gather_with_grad=True: Preserves gradients across all workers, enabling full backpropagation through the global batch.

This mechanism ensures that the contrastive loss can operate on a global batch of embeddings even when training is distributed across multiple nodes.

Ground-Truth Label Management

The get_ground_truth method (lines 82‑93 in loss.py) constructs the target tensor [0, 1, …, N‑1] on the appropriate device. When cache_labels=True, these tensors are stored per-device to eliminate allocation overhead across iterations.

In distributed settings with local_loss=True, the labels are offset by num_logits * rank, ensuring each rank computes loss against its unique slice of the global batch. This allows for local contrastive loss computation, reducing memory usage by avoiding the full global similarity matrix on each worker.

Computing Contrastive Logits

The get_logits method (lines 95‑113 in loss.py) handles the computation of similarity matrices between image and text embeddings.

With local_loss=False (the default matching the original CLIP paper), the method first gathers all features to create a global similarity matrix:

logits_per_image = logit_scale * all_image_features @ all_text_features.T
logits_per_text = logits_per_image.T

When local_loss=True, each rank computes its local image embeddings against the global text pool (or vice versa), which significantly reduces memory consumption on each GPU. For single-GPU training, the method falls back to the simpler image_features @ text_features.T computation.

Forward Pass and Symmetric Cross-Entropy

The forward method (lines 115‑125 in loss.py) orchestrates the final loss calculation. It receives normalized image and text embeddings along with a learnable logit_scale parameter.

The method executes the following steps:

  1. Obtains similarity logits via get_logits
  2. Generates ground-truth labels via get_ground_truth
  3. Computes symmetric cross-entropy:
loss = (
    F.cross_entropy(logits_per_image, labels) +
    F.cross_entropy(logits_per_text, labels)
) / 2

The method returns either a scalar loss value or a dictionary {"contrastive_loss": loss} when output_dict=True, facilitating integration with training loops that expect dictionary outputs.

Configuring ClipLoss in the Training Pipeline

The ClipLoss class is instantiated in train.py (lines 47‑53) with configuration parameters mapped directly from command-line arguments:

contrastive_loss = ClipLoss(
    local_loss=args.local_loss,          # Default: False

    gather_with_grad=args.gather_with_grad,  # Default: False

    cache_labels=True,                  # Enable label caching

    rank=int(os.environ["RANK"]),       # Distributed rank

    world_size=int(os.environ["WORLD_SIZE"]), # Total processes

    use_horovod=args.horovod)           # Horovod backend flag

Typical single-node multi-GPU configurations leave local_loss=False and gather_with_grad=False, maintaining compatibility with the original CLIP paper's global contrastive loss approach. The cache_labels=True setting is recommended for all configurations to minimize CPU-GPU synchronization overhead.

Practical Code Example

The following example demonstrates direct usage of ClipLoss with dummy embeddings, mirroring the training loop implementation in train.py:

import torch
import torch.nn.functional as F
from loss import ClipLoss

# Simulate normalized embeddings (batch_size=4, embed_dim=512)

image_features = torch.randn(4, 512)
image_features = F.normalize(image_features, dim=-1)
text_features = torch.randn(4, 512)
text_features = F.normalize(text_features, dim=-1)

# Learnable temperature parameter (logit_scale)

logit_scale = torch.nn.Parameter(torch.ones([]) * 2.0)

# Initialize loss module (single-GPU configuration)

criterion = ClipLoss(
    local_loss=False,
    gather_with_grad=False,
    cache_labels=True,
    rank=0,
    world_size=1
)

# Forward pass

loss = criterion(image_features, text_features, logit_scale)
loss.backward()

print(f"Contrastive loss: {loss.item():.4f}")

This implementation exactly follows the forward pass logic found in loss.py lines 115‑125 and demonstrates gradient flow through both the embeddings and the learnable logit_scale parameter.

Summary

  • ClipLoss in loss.py implements the standard CLIP symmetric cross-entropy objective with distributed training support.
  • Feature gathering via gather_features enables multi-GPU training with optional gradient preservation (gather_with_grad).
  • Label caching (cache_labels=True) eliminates per-iteration allocation overhead by storing ground-truth tensors per device.
  • Local loss mode (local_loss=True) reduces memory usage by computing similarity against global pools rather than full global matrices.
  • Configuration occurs in train.py (lines 47‑53) via constructor arguments mapped to command-line flags.

Frequently Asked Questions

How does the local_loss parameter affect distributed training?

When local_loss=False (default), each GPU computes the full global similarity matrix using gathered features from all workers, matching the original CLIP implementation. When local_loss=True, each rank computes similarity only between its local batch and the global feature pool, significantly reducing memory consumption while maintaining contrastive learning effectiveness.

What is the purpose of gather_with_grad in the ClipLoss implementation?

The gather_with_grad flag controls whether gradients are preserved when gathering features across distributed workers. When True, the gather_features function maintains gradient flow through all workers, enabling full backpropagation through the global batch. When False (default), gradients are computed only for the local rank, which is more memory efficient but restricts gradient updates to local features.

How does ClipLoss handle label generation and caching?

The get_ground_truth method generates target labels as an ascending integer tensor [0, 1, …, N‑1]. When cache_labels=True, these tensors are stored in a dictionary keyed by device and batch size, eliminating CPU-GPU synchronization overhead across training iterations. In distributed mode with local_loss=True, labels are offset by num_logits * rank to ensure each worker targets the correct global indices.

What is the symmetric cross-entropy calculation in the forward pass?

The forward method computes contrastive loss as the average of two cross-entropy terms: F.cross_entropy(logits_per_image, labels) (image-to-text direction) and F.cross_entropy(logits_per_text, labels) (text-to-image direction). This symmetric formulation ensures that both modalities are equally optimized to predict their corresponding pairs within the batch, which is the core objective of CLIP-style contrastive learning.

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 →