RoBERTa › Comparing BERT and RoBERTa Training

Michael BrenndoerferUpdated July 18, 20253 min read
Language AI HandbookAll chapters
Part 24 of 67BERT and Variants
Chapter 173RoBERTa › Comparing BERT and RoBERTa Training
Reading section 9 of 17 · Comparing BERT and RoBERTa Training · 3 minSection 9 of 17 · 3 min

Book contents

Section 9 of 17 · 3 min read

RoBERTa › Comparing BERT and RoBERTa TrainingOne section · Sections in this chapter9 / 17 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

The contrast between BERT and RoBERTa training becomes especially clear when you look at the training loops side by side. BERT's loop must handle two separate objectives with two different kinds of labels, while RoBERTa's loop handles only one. The simpler training objective means fewer hyperparameters to tune, fewer places for bugs to hide, and a cleaner gradient signal flowing through the model.

Let's put together a minimal training loop that highlights the differences:

In[22]:
Code
def train_step_bert(model, batch, optimizer, nsp_weight=0.5):
    """
    BERT training step with MLM + NSP.

    Requires: input_ids, attention_mask, token_type_ids,
              mlm_labels, nsp_labels
    """
    outputs = model(
        input_ids=batch["input_ids"],
        attention_mask=batch["attention_mask"],
        token_type_ids=batch["token_type_ids"],  # Segment embeddings for NSP
    )

    # MLM loss
    mlm_logits = outputs.mlm_logits
    mlm_loss = F.cross_entropy(
        mlm_logits.view(-1, mlm_logits.size(-1)),
        batch["mlm_labels"].view(-1),
        ignore_index=-100,
    )

    # NSP loss
    nsp_logits = outputs.nsp_logits
    nsp_loss = F.cross_entropy(nsp_logits, batch["nsp_labels"])

    # Combined loss
    loss = mlm_loss + nsp_weight * nsp_loss

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    return {
        "loss": loss.item(),
        "mlm_loss": mlm_loss.item(),
        "nsp_loss": nsp_loss.item(),
    }


def train_step_roberta(model, batch, optimizer):
    """
    RoBERTa training step with MLM only.

    Requires: input_ids, attention_mask, labels
    No token_type_ids needed (no NSP)
    """
    outputs = model(
        input_ids=batch["input_ids"],
        attention_mask=batch["attention_mask"],
        # No token_type_ids - RoBERTa doesn't use them
    )

    # MLM loss only
    logits = outputs.logits
    loss = F.cross_entropy(
        logits.view(-1, logits.size(-1)),
        batch["labels"].view(-1),
        ignore_index=-100,
    )

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    return {"loss": loss.item()}

The RoBERTa training step is cleaner. No NSP labels to prepare, no segment embeddings to track, no secondary loss to balance. This simplicity makes training easier to debug and scale.

The absence of token_type_ids is worth emphasizing. BERT uses token type IDs to distinguish sentence A tokens from sentence B tokens, embedding a 0 for all tokens in the first sentence and a 1 for all tokens in the second. These segment embeddings were necessary for NSP to work: the model needed to know which tokens belonged to which sentence to make its "is next?" prediction. Once you drop NSP, there is no longer a need to distinguish sentences. Every token in the sequence is treated uniformly. Removing this distinction simplifies the embedding layer (no segment embedding table needed) and eliminates a source of potential artifact: the model can no longer inadvertently learn to use segment boundaries as shortcuts for other tasks.

When you fine-tune RoBERTa on tasks that involve two text inputs, such as natural language inference or question-answer pairs, you concatenate the two texts with a separator token between them. The model has no explicit marker telling it which text is which; it infers the separation from the </s> token's position. This works well in practice because the </s> token's learned representation implicitly signals a boundary, and the model has seen this pattern throughout pretraining in the DOC-SENTENCES format whenever it encountered the end of a document.