CodeBlanks Logo Icon CodeBlanks

Decoder

The decoder uses encoder output and previous outputs to generate predictions.

The decoder

The decoder produces the output sequence from left to right. It has two differences from the encoder block:

  • Masked self-attention: position i only looks at 0..i; it cannot see future words.
  • Cross-attention: the Query comes from the decoder, the Key and Value from the encoder output; in translation the target word looks at the source sentence.
x = layer_norm(x + masked_self_attn(x))
x = layer_norm(x + cross_attn(Q=x, K=enc, V=enc))
x = layer_norm(x + ffn(x))

The cross-attention weight matrix has shape (target length × source length); it need not be square.

The causal mask

Before softmax, the mask sets the scores of future positions to −∞, so their weights become exactly 0. It is built with a lower-triangular boolean matrix:

n = 4
maske = np.tril(np.ones((n, n), dtype=bool))
skor = np.where(maske, skor, -np.inf)
A = softmax(skor, eksen=-1)
print(np.round(A[1], 2))     # only the first two positions are above zero

In training the whole target sequence is given at once; thanks to the mask each position still sees only the past (teacher forcing).

Practice Masked and cross-attention

Compute the masked self-attention weights for the decoder sequence y (3 positions) and print them with np.round(..., 3). Then compute the cross-attention weights with the encoder output enc (5 positions) and print only their shape.

Starter code

model.py
import numpy as np

def softmax(z, eksen=-1):
    e = np.exp(z - z.max(axis=eksen, keepdims=True))
    return e / e.sum(axis=eksen, keepdims=True)

y = np.array([[1., 0.], [0., 1.], [1., 1.]])
enc = np.random.default_rng(0).normal(size=(5, 2))
n = len(y)

# buraya yaz

Sample solution

model.py
import numpy as np

def softmax(z, eksen=-1):
    e = np.exp(z - z.max(axis=eksen, keepdims=True))
    return e / e.sum(axis=eksen, keepdims=True)

y = np.array([[1., 0.], [0., 1.], [1., 1.]])
enc = np.random.default_rng(0).normal(size=(5, 2))
n = len(y)

maske = np.tril(np.ones((n, n), dtype=bool))
skor = np.where(maske, y @ y.T / np.sqrt(2), -np.inf)
print(np.round(softmax(skor), 3))
C = softmax(y @ enc.T / np.sqrt(2))
print(C.shape)

Expected output

[[1.    0.    0.   ]
 [0.33  0.67  0.   ]
 [0.248 0.248 0.503]]
(3, 5)

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 Where do the Key and Value come from in cross-attention?

  • From the decoder itself
  • From the positional encoding
  • They are randomly initialized
  • From the encoder output Correct answer

Explanation: The Query comes from the decoder, Key/Value from the encoder.

2 Mask the future positions.

Question
skor = np.where(maske, skor, -np.____)
Answer
skor = np.where(maske, skor, -np.inf)

Explanation: The exponential of −∞ is 0; the weight becomes exactly zero.

3 Match the attention types.

Term Meaning
encoder self-attention Looks at the whole sequence
masked self-attention Looks only at the past
cross-attention Looks at the source sentence
np.tril Lower-triangular mask

All lessons in Expert