attention_weights = torch.eye(10).reshape((1, 1, 10, 10))
d2l.show_heatmaps(attention_weights, xlabel='Keys', ylabel='Queries')Dive into Deep Learning · §10.1
Queries, keys, and values
attention as soft lookup · softmax weights · Nadaraya–Watson pooling · why learn the kernel
Every network so far assumes inputs of fixed, known size — 224 \times 224 images, or a sequence squeezed through a fixed-dimensional RNN state.
Databases don’t have this problem. A database is a set of (\text{key}, \text{value}) pairs; a query retrieves the matching value.
We want a differentiable layer with these properties.
Over a database \mathcal{D} = \{(\mathbf{k}_1, \mathbf{v}_1), \ldots, (\mathbf{k}_m, \mathbf{v}_m)\}:
\textrm{Attention}(\mathbf{q}, \mathcal{D}) = \sum_{i=1}^m \alpha(\mathbf{q}, \mathbf{k}_i)\, \mathbf{v}_i.
One-hot \alpha → exact lookup; uniform \alpha → average pooling. Everything in between is attention.
Attention pooling: a linear combination of values, with weights from query–key compatibility.
Pick any scoring function a(\mathbf{q}, \mathbf{k}), exponentiate, normalize:
\alpha(\mathbf{q}, \mathbf{k}_i) = \frac{\exp(a(\mathbf{q}, \mathbf{k}_i))}{\sum_j \exp(a(\mathbf{q}, \mathbf{k}_j))}.
The (queries × keys) heatmap is the standard diagnostic — here the identity, i.e. exact lookup:
Nadaraya–Watson regression is attention pooling with a hand-picked similarity kernel:
f(\mathbf{q}) = \sum_i \mathbf{v}_i \frac{\alpha(\mathbf{q}, \mathbf{k}_i)}{\sum_j \alpha(\mathbf{q}, \mathbf{k}_j)}.
Keys = training inputs, values = labels, query = where to predict. No training at all — and consistent, if the kernel narrows with more data.
Gaussian, boxcar, constant, and triangular kernels.
y = 2\sin(x) + x + \epsilon, 40 noisy points. Four lines of code: distances → kernel → normalize over keys → weighted sum of labels.
def nadaraya_watson(x_train, y_train, x_val, sigma):
dists = x_train[:, None] - x_val[None, :]
k = torch.exp(-dists**2 / (2 * sigma**2))
attention_w = k / k.sum(0) # Normalize over keys for each query
return y_train @ attention_w, attention_w
sigmas = (0.1, 0.5, 2.0)
estimates = [nadaraya_watson(x_train, y_train, x_val, s)[0] for s in sigmas]
d2l.plot(x_val, estimates + [y_val], 'x', 'y',
legend=[f'sigma = {s:g}' for s in sigmas] + ['truth'])
d2l.plt.plot(x_train, y_train, 'o', alpha=0.4);