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: int | None = None, n_heads: int | None = None, num_encoder_layers: int | None = None, num_decoder_layers: int | None = None, d_ff: int | None = None, max_seq_length: int | None = None, dropout: float | None = None, pad_idx: int | None = None, config: Config | None = 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
forward
¶
forward(src: Tensor, tgt: Tensor, src_key_padding_mask: Tensor | None = None, tgt_key_padding_mask: Tensor | None = None, tgt_mask: Tensor | None = None, return_attention: bool = False) -> Tensor | tuple[Tensor, 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
|
return_attention
|
bool
|
Also return the decoder's
cross-attention weights, showing which source positions each
target position attended to. Off by default because capturing
them costs a faster attention kernel. Matches the same argument
on :class: |
False
|
Returns:
| Type | Description |
|---|---|
Tensor | tuple[Tensor, Tensor]
|
torch.Tensor: Logits of shape (batch_size, tgt_len, tgt_vocab_size). |
Tensor | tuple[Tensor, Tensor]
|
If |
Tensor | tuple[Tensor, Tensor]
|
instead, where weights has shape (batch_size, tgt_len, src_len) and |
Tensor | tuple[Tensor, Tensor]
|
comes from the last decoder layer, heads averaged. Use |
Tensor | tuple[Tensor, Tensor]
|
func: |
Example
The same call shape as the LSTM, so tooling works on both.
Call eval() first: in training mode, attention dropout
randomly zeroes weights and rescales the rest, so the rows will not
sum to 1 and the map you plot is not the one inference uses.
import torch model = SimpleTransformer(src_vocab_size=20, tgt_vocab_size=20, ... d_model=16, n_heads=2, ... num_encoder_layers=1, ... num_decoder_layers=1) _ = model.eval() src, tgt = torch.tensor([[2, 5, 9, 3]]), torch.tensor([[2, 7, 8]]) with torch.no_grad(): ... logits, weights = model(src, tgt, return_attention=True) weights.shape torch.Size([1, 3, 4])
Each row is a distribution over source positions, so it sums to 1:
bool(torch.allclose(weights.sum(-1), torch.ones(1, 3), atol=1e-5)) True
Source code in src/torchlingo/models/transformer_simple.py
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | |
encode
¶
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
decode
¶
decode(tgt: Tensor, memory: Tensor, src_key_padding_mask: Tensor | None = None, tgt_key_padding_mask: Tensor | None = None, tgt_mask: Tensor | None = 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
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:
- Padding masks: Prevent attention to PAD tokens
- Causal masks: Prevent decoder from seeing future tokens
Embedding Scaling¶
Embeddings are scaled by √d_model to maintain variance:
Xavier Initialization¶
Weights are initialized using Xavier uniform for stable training:
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.