Skip to content

Transformer

Simple transformer-based sequence-to-sequence model with sinusoidal positional encoding.

Overview

SimpleTransformer implements the encoder-decoder Transformer architecture from "Attention Is All You Need" (Vaswani et al., 2017), using the paper's sinusoidal positional encoding.

Architecture

┌─────────────────────────────────────────────────┐
│                    ENCODER                       │
│  ┌───────────────────────────────────────────┐  │
│  │  Token Embedding + Sinusoidal Position    │  │
│  │   src_vocab_size → d_model                │  │
│  └───────────────────────────────────────────┘  │
│                      ↓                           │
│  ┌───────────────────────────────────────────┐  │
│  │         Transformer Encoder × N           │  │
│  │   • Multi-Head Self-Attention             │  │
│  │   • Feed-Forward Network                  │  │
│  │   • LayerNorm + Residual                  │  │
│  └───────────────────────────────────────────┘  │
└─────────────────────────────────────────────────┘
              Encoder Output (Memory)
┌─────────────────────────────────────────────────┐
│                    DECODER                       │
│  ┌───────────────────────────────────────────┐  │
│  │  Token Embedding + Sinusoidal Position    │  │
│  │   tgt_vocab_size → d_model                │  │
│  └───────────────────────────────────────────┘  │
│                      ↓                           │
│  ┌───────────────────────────────────────────┐  │
│  │         Transformer Decoder × N           │  │
│  │   • Masked Multi-Head Self-Attention      │  │
│  │   • Cross-Attention (to encoder)          │  │
│  │   • Feed-Forward Network                  │  │
│  │   • LayerNorm + Residual                  │  │
│  └───────────────────────────────────────────┘  │
│                      ↓                           │
│  ┌───────────────────────────────────────────┐  │
│  │         Linear Generator                  │  │
│  │   d_model → tgt_vocab_size                │  │
│  └───────────────────────────────────────────┘  │
└─────────────────────────────────────────────────┘

Quick Start

from torchlingo.models import SimpleTransformer

model = SimpleTransformer(
    src_vocab_size=10000,
    tgt_vocab_size=10000,
    d_model=512,
    n_heads=8,
    num_encoder_layers=6,
    num_decoder_layers=6,
)

# Training
logits = model(src_batch, tgt_batch)  # [batch, tgt_len, vocab]

# Inference
memory = model.encode(src_batch)
logits = model.decode(tgt_batch, memory)

API Reference

SimpleTransformer

SimpleTransformer(src_vocab_size: int, tgt_vocab_size: int, d_model: Optional[int] = None, n_heads: Optional[int] = None, num_encoder_layers: Optional[int] = None, num_decoder_layers: Optional[int] = None, d_ff: Optional[int] = None, max_seq_length: Optional[int] = None, dropout: Optional[float] = None, pad_idx: Optional[int] = None, config: Optional[Config] = None)

Bases: Module

Transformer encoder-decoder model with sinusoidal positional encoding.

A standard transformer architecture combining an encoder and decoder, each composed of stacked multi-head self-attention and feed-forward layers. Uses the fixed sinusoidal positional encoding from Vaswani et al. (2017). Embeddings are scaled by sqrt(d_model) to prevent vanishing gradients.

Parameters:

Name Type Description Default
src_vocab_size int

Size of the source vocabulary.

required
tgt_vocab_size int

Size of the target vocabulary.

required
d_model int

Model hidden dimension. Defaults to 512.

None
n_heads int

Number of attention heads. Defaults to 8.

None
num_encoder_layers int

Number of encoder transformer blocks. Defaults to 6.

None
num_decoder_layers int

Number of decoder transformer blocks. Defaults to 6.

None
d_ff int

Feed-forward inner dimension. Defaults to 2048.

None
max_seq_length int

Maximum sequence length for the positional encoding table. Defaults to 512.

None
dropout float

Dropout rate throughout the model. Defaults to 0.1.

None
pad_idx int

Padding token index. Falls back to config.pad_idx.

None
config Config

Configuration object. Defaults to default config.

None

Attributes:

Name Type Description
d_model int

Model dimension.

max_seq_length int

Maximum sequence length.

pad_idx int

Resolved padding index.

src_tok_emb Embedding

Source token embedding layer.

tgt_tok_emb Embedding

Target token embedding layer.

pos_encoding SinusoidalPositionalEncoding

Positional encoding module.

transformer Transformer

PyTorch transformer with encoder and decoder.

generator Linear

Output projection to target vocabulary logits.

Source code in src/torchlingo/models/transformer_simple.py
def __init__(
    self,
    src_vocab_size: int,
    tgt_vocab_size: int,
    d_model: Optional[int] = None,
    n_heads: Optional[int] = None,
    num_encoder_layers: Optional[int] = None,
    num_decoder_layers: Optional[int] = None,
    d_ff: Optional[int] = None,
    max_seq_length: Optional[int] = None,
    dropout: Optional[float] = None,
    pad_idx: Optional[int] = None,
    config: Optional[Config] = None,
) -> None:
    super().__init__()
    cfg = config if config is not None else get_default_config()
    self.pad_idx = pad_idx if pad_idx is not None else cfg.pad_idx
    d_model = d_model if d_model is not None else cfg.d_model
    n_heads = n_heads if n_heads is not None else cfg.n_heads
    num_encoder_layers = (
        num_encoder_layers
        if num_encoder_layers is not None
        else cfg.num_encoder_layers
    )
    num_decoder_layers = (
        num_decoder_layers
        if num_decoder_layers is not None
        else cfg.num_decoder_layers
    )
    d_ff = d_ff if d_ff is not None else cfg.d_ff
    max_seq_length = (
        max_seq_length if max_seq_length is not None else cfg.max_seq_length
    )
    dropout = dropout if dropout is not None else cfg.dropout

    self.d_model = d_model
    self.max_seq_length = max_seq_length
    self.dropout = dropout

    self.src_tok_emb = nn.Embedding(
        src_vocab_size, d_model, padding_idx=self.pad_idx
    )
    self.tgt_tok_emb = nn.Embedding(
        tgt_vocab_size, d_model, padding_idx=self.pad_idx
    )
    self.pos_encoding = SinusoidalPositionalEncoding(
        d_model, max_seq_length, dropout=dropout
    )
    self.transformer = nn.Transformer(
        d_model=d_model,
        nhead=n_heads,
        num_encoder_layers=num_encoder_layers,
        num_decoder_layers=num_decoder_layers,
        dim_feedforward=d_ff,
        dropout=dropout,
        batch_first=True,
    )
    self.generator = nn.Linear(d_model, tgt_vocab_size)
    self._init_parameters()

forward

forward(src: Tensor, tgt: Tensor, src_key_padding_mask: Optional[Tensor] = None, tgt_key_padding_mask: Optional[Tensor] = None, tgt_mask: Optional[Tensor] = None) -> Tensor

Encode source and decode target in a single forward pass.

Automatically generates padding and causal masks if not provided. Calls encode() to process source sequences, then decode() to generate target sequence predictions.

Parameters:

Name Type Description Default
src Tensor

Source token indices of shape (batch_size, src_len).

required
tgt Tensor

Target token indices of shape (batch_size, tgt_len).

required
src_key_padding_mask Tensor

Boolean mask for source padding. If None, generated automatically from padding index. Defaults to None.

None
tgt_key_padding_mask Tensor

Boolean mask for target padding. If None, generated automatically from padding index. Defaults to None.

None
tgt_mask Tensor

Causal mask for target self-attention. If None, generated automatically. Defaults to None.

None

Returns:

Type Description
Tensor

torch.Tensor: Logits of shape (batch_size, tgt_len, tgt_vocab_size).

Source code in src/torchlingo/models/transformer_simple.py
def forward(
    self,
    src: torch.Tensor,
    tgt: torch.Tensor,
    src_key_padding_mask: Optional[torch.Tensor] = None,
    tgt_key_padding_mask: Optional[torch.Tensor] = None,
    tgt_mask: Optional[torch.Tensor] = None,
) -> torch.Tensor:
    """Encode source and decode target in a single forward pass.

    Automatically generates padding and causal masks if not provided.
    Calls encode() to process source sequences, then decode() to generate
    target sequence predictions.

    Args:
        src (torch.Tensor): Source token indices of shape (batch_size, src_len).
        tgt (torch.Tensor): Target token indices of shape (batch_size, tgt_len).
        src_key_padding_mask (torch.Tensor, optional): Boolean mask for source padding.
            If None, generated automatically from padding index. Defaults to None.
        tgt_key_padding_mask (torch.Tensor, optional): Boolean mask for target padding.
            If None, generated automatically from padding index. Defaults to None.
        tgt_mask (torch.Tensor, optional): Causal mask for target self-attention.
            If None, generated automatically. Defaults to None.

    Returns:
        torch.Tensor: Logits of shape (batch_size, tgt_len, tgt_vocab_size).
    """
    if src_key_padding_mask is None:
        src_key_padding_mask = create_key_padding_mask(src, pad_idx=self.pad_idx)
    if tgt_key_padding_mask is None:
        tgt_key_padding_mask = create_key_padding_mask(tgt, pad_idx=self.pad_idx)
    if tgt_mask is None:
        tgt_mask = create_causal_mask(tgt.size(1), tgt.device)
    memory = self.encode(src, src_key_padding_mask)
    return self.decode(
        tgt,
        memory,
        src_key_padding_mask=src_key_padding_mask,
        tgt_key_padding_mask=tgt_key_padding_mask,
        tgt_mask=tgt_mask,
    )

encode

encode(src: Tensor, src_key_padding_mask: Optional[Tensor] = None) -> Tensor

Encode source sequence using the transformer encoder.

Embeds source tokens, adds sinusoidal positional encodings, then passes the result through the transformer encoder stack.

Parameters:

Name Type Description Default
src Tensor

Source token indices of shape (batch_size, src_len).

required
src_key_padding_mask Tensor

Boolean mask where True indicates padding positions to ignore. Shape (batch_size, src_len). Defaults to None.

None

Returns:

Type Description
Tensor

torch.Tensor: Encoded source representation of shape (batch_size, src_len, d_model).

Source code in src/torchlingo/models/transformer_simple.py
def encode(
    self, src: torch.Tensor, src_key_padding_mask: Optional[torch.Tensor] = None
) -> torch.Tensor:
    """Encode source sequence using the transformer encoder.

    Embeds source tokens, adds sinusoidal positional encodings, then passes
    the result through the transformer encoder stack.

    Args:
        src (torch.Tensor): Source token indices of shape (batch_size, src_len).
        src_key_padding_mask (torch.Tensor, optional): Boolean mask where True indicates
            padding positions to ignore. Shape (batch_size, src_len). Defaults to None.

    Returns:
        torch.Tensor: Encoded source representation of shape (batch_size, src_len, d_model).
    """
    src_emb = self._embed(src, is_src=True)
    return self.transformer.encoder(
        src_emb, src_key_padding_mask=src_key_padding_mask
    )

decode

decode(tgt: Tensor, memory: Tensor, src_key_padding_mask: Optional[Tensor] = None, tgt_key_padding_mask: Optional[Tensor] = None, tgt_mask: Optional[Tensor] = None) -> Tensor

Decode target sequence using the transformer decoder and encoder output.

Embeds target tokens, adds sinusoidal positional encodings, then passes them through the transformer decoder with cross-attention to the encoder output. The decoder is typically run with a causal mask to prevent attending to future tokens.

Parameters:

Name Type Description Default
tgt Tensor

Target token indices of shape (batch_size, tgt_len).

required
memory Tensor

Encoded source from encoder output, shape (batch_size, src_len, d_model).

required
src_key_padding_mask Tensor

Mask for encoder output padding. Shape (batch_size, src_len). Defaults to None.

None
tgt_key_padding_mask Tensor

Mask for target padding. Shape (batch_size, tgt_len). Defaults to None.

None
tgt_mask Tensor

Causal mask for target self-attention. Shape (tgt_len, tgt_len). Defaults to None.

None

Returns:

Type Description
Tensor

torch.Tensor: Decoder output logits of shape (batch_size, tgt_len, tgt_vocab_size).

Source code in src/torchlingo/models/transformer_simple.py
def decode(
    self,
    tgt: torch.Tensor,
    memory: torch.Tensor,
    src_key_padding_mask: Optional[torch.Tensor] = None,
    tgt_key_padding_mask: Optional[torch.Tensor] = None,
    tgt_mask: Optional[torch.Tensor] = None,
) -> torch.Tensor:
    """Decode target sequence using the transformer decoder and encoder output.

    Embeds target tokens, adds sinusoidal positional encodings, then passes
    them through the transformer decoder with cross-attention to the encoder output.
    The decoder is typically run with a causal mask to prevent attending to future tokens.

    Args:
        tgt (torch.Tensor): Target token indices of shape (batch_size, tgt_len).
        memory (torch.Tensor): Encoded source from encoder output, shape (batch_size, src_len, d_model).
        src_key_padding_mask (torch.Tensor, optional): Mask for encoder output padding.
            Shape (batch_size, src_len). Defaults to None.
        tgt_key_padding_mask (torch.Tensor, optional): Mask for target padding.
            Shape (batch_size, tgt_len). Defaults to None.
        tgt_mask (torch.Tensor, optional): Causal mask for target self-attention.
            Shape (tgt_len, tgt_len). Defaults to None.

    Returns:
        torch.Tensor: Decoder output logits of shape (batch_size, tgt_len, tgt_vocab_size).
    """
    tgt_emb = self._embed(tgt, is_src=False)
    dec = self.transformer.decoder(
        tgt_emb,
        memory,
        tgt_mask=tgt_mask,
        memory_key_padding_mask=src_key_padding_mask,
        tgt_key_padding_mask=tgt_key_padding_mask,
    )
    return self.generator(dec)

Constructor Parameters

Parameter Type Default Description
src_vocab_size int required Source vocabulary size
tgt_vocab_size int required Target vocabulary size
d_model int 512 Model hidden dimension
n_heads int 8 Number of attention heads
num_encoder_layers int 6 Encoder transformer blocks
num_decoder_layers int 6 Decoder transformer blocks
d_ff int 2048 Feed-forward inner dimension
max_seq_length int 512 Maximum sequence length
dropout float 0.1 Dropout rate
pad_idx int 0 Padding token index
config Config None Configuration object

Examples

Basic Training

import torch
from torchlingo.models import SimpleTransformer
from torchlingo.config import Config

config = Config(d_model=256, n_heads=8)
model = SimpleTransformer(
    src_vocab_size=10000,
    tgt_vocab_size=10000,
    config=config,
)

# Dummy data
src = torch.randint(0, 10000, (32, 20))  # [batch, src_len]
tgt = torch.randint(0, 10000, (32, 25))  # [batch, tgt_len]

# Forward pass
logits = model(src, tgt[:, :-1])  # [32, 24, 10000]

# Compute loss
criterion = torch.nn.CrossEntropyLoss(ignore_index=0)
loss = criterion(
    logits.reshape(-1, logits.size(-1)),
    tgt[:, 1:].reshape(-1)
)

Inference (Greedy Decoding)

def greedy_decode(model, src, tgt_vocab, max_len=50, device="cpu"):
    model.eval()
    src = src.to(device)

    # Encode source
    memory = model.encode(src)

    # Start with SOS
    ys = torch.tensor([[tgt_vocab.sos_idx]], device=device)

    for _ in range(max_len):
        logits = model.decode(ys, memory)
        next_token = logits[:, -1, :].argmax(dim=-1, keepdim=True)
        ys = torch.cat([ys, next_token], dim=1)

        if next_token.item() == tgt_vocab.eos_idx:
            break

    return ys[0].tolist()

Custom Masking

# Manual padding mask
src_pad_mask = (src == pad_idx)  # True where padding

# Manual causal mask
tgt_len = tgt.size(1)
causal_mask = torch.triu(
    torch.ones(tgt_len, tgt_len, dtype=torch.bool),
    diagonal=1
)

logits = model(
    src, tgt,
    src_key_padding_mask=src_pad_mask,
    tgt_mask=causal_mask,
)

Key Features

Sinusoidal Positional Encoding

Fixed sine/cosine waves of geometrically increasing wavelengths are added to the token embeddings:

  • No learned parameters; valid for any position
  • Encodings for nearby positions are related by simple rotations, which helps the model attend by relative offset
  • Matches the original "Attention Is All You Need" recipe

Automatic Masking

The model automatically generates:

  1. Padding masks: Prevent attention to PAD tokens
  2. Causal masks: Prevent decoder from seeing future tokens

Embedding Scaling

Embeddings are scaled by √d_model to maintain variance:

src_emb = self.src_tok_emb(src) * math.sqrt(self.d_model)

Xavier Initialization

Weights are initialized using Xavier uniform for stable training:

for m in self.modules():
    if isinstance(m, nn.Linear):
        nn.init.xavier_uniform_(m.weight)

Model Variants

Tiny (Demo/Testing)

model = SimpleTransformer(
    src_vocab_size, tgt_vocab_size,
    d_model=64, n_heads=2,
    num_encoder_layers=1, num_decoder_layers=1,
)
# ~500K params, trains in seconds

Base

model = SimpleTransformer(
    src_vocab_size, tgt_vocab_size,
    d_model=512, n_heads=8,
    num_encoder_layers=6, num_decoder_layers=6,
)
# ~65M params, good quality

Large

model = SimpleTransformer(
    src_vocab_size, tgt_vocab_size,
    d_model=1024, n_heads=16,
    num_encoder_layers=6, num_decoder_layers=6,
    d_ff=4096,
)
# ~200M params, high quality

Computational Considerations

Aspect Complexity
Self-attention O(n² × d)
Memory O(n² + n × d)
Parameters O(L × d²)

Where n = sequence length, d = d_model, L = num layers.

Long Sequences

Attention has O(n²) complexity. For very long sequences (>1000 tokens), consider using flash attention or other efficient attention implementations.