Part IV: Deep Learning for Sensor Time Series
Chapter 15: Transformers for Sensor Data

Attention over time and channels

"You asked me to summarize the last ten minutes of vibration. I looked at all of it at once and decided which forty milliseconds actually mattered."

An Attentive AI Agent

Prerequisites

This section assumes you are comfortable with a multichannel sensor stream as a matrix of time by channel, the framing from Chapter 3, and with the neural-network mechanics (linear projections, softmax, minibatch training) refreshed in Appendix B and first applied to sensor windows in Chapter 13. It helps to have met the recurrent and convolutional models of Chapter 14, because attention is the third answer to the same question those models addressed: how does one sample's meaning depend on the others? No prior exposure to transformers is required. Positional encoding, patchification, and efficient long-context variants are deferred to later sections of this chapter.

The Big Picture

A recurrent network passes information through time one step at a time, and a convolution mixes only what falls inside its fixed window. Attention throws out both constraints. It lets every point in a sensor window look directly at every other point and decide, from the content of the signal itself, which ones are relevant. The mechanism is a weighted average whose weights are computed on the fly, so a spike at one moment can pull in evidence from a moment far away in a single step, with no decay and no fixed receptive field. For sensor data there are two axes to attend over, and they answer different questions. Attention over time asks which moments in the window explain each other. Attention over channels asks which sensors explain each other. This section builds the core attention operation once, then shows how pointing it along each axis buys you long-range temporal reasoning and learned cross-sensor coupling from the same few lines of algebra.

Scaled dot-product attention: a content-addressed average

Attention takes a set of tokens, here a set of vectors extracted from the sensor stream, and rebuilds each one as a weighted blend of all the others. Every token is projected three ways by learned matrices into a query \(q\), a key \(k\), and a value \(v\). The query says what this token is looking for; the key says what each token offers; the value is the content it contributes if selected. Stacking the tokens of one window into matrices \(Q\), \(K\), and \(V\), the whole operation is:

$$\mathrm{Attention}(Q,K,V) = \mathrm{softmax}\!\left(\frac{QK^{\top}}{\sqrt{d_k}}\right)V$$

Read it inside out. The product \(QK^{\top}\) scores every query against every key by dot product, producing an \(n \times n\) grid of compatibilities for a window of \(n\) tokens. Dividing by \(\sqrt{d_k}\), the square root of the key dimension, keeps those scores from growing with dimension and saturating the softmax into a near one-hot spike; this is the scaled in the name and it matters for stable gradients. The softmax turns each row into a probability distribution, the attention weights, and multiplying by \(V\) forms, for each token, a convex combination of all values weighted by relevance. The result is content addressing: a token retrieves information not by position but by what it resembles. In practice we compute several such maps in parallel with different projections, multi-head attention, so one head can track a slow trend while another locks onto transients, and their outputs are concatenated and mixed.

Key Insight

Every token reaches every other token in exactly one operation, so the path length between two samples is constant regardless of how far apart they sit. Contrast this with the recurrent cell of Chapter 14, where the path grows linearly with the gap and the gradient decays along it, and with the convolution, whose reach is capped by its receptive field. Attention pays for this reach with an \(n \times n\) score matrix: cost and memory grow with the square of the window length. That quadratic bill is the defining tradeoff of the architecture and the reason Section 15.4 is devoted entirely to taming it for long sensor sequences.

Attention over time: which moments explain each other

Point the mechanism along the time axis and each token is one time step (or, after Section 15.2, one short patch of time). The attention weights then form a map from each moment to every other moment, saying which instants the model consults to represent the present one. This is a strict generalization of what the temporal models of the previous chapter did. Where a causal convolution averages a fixed neighborhood with fixed weights, temporal attention averages the whole window with weights that depend on the signal, so a heartbeat can attend to the previous beat to measure an interval, and a gait classifier can line up the current stride against the one before it even if the cadence drifted. Because the raw operation is permutation invariant, it has no built-in sense of order; the model only learns that step \(t{-}1\) precedes step \(t\) once you add the position information of Section 15.3. For streaming and forecasting you also apply a causal mask that sets the scores for future positions to \(-\infty\) before the softmax, so a token can attend only to the past, preserving the real-time constraint that disqualified bidirectional recurrence in Chapter 14.

The listing below runs one head of self-attention over a short multichannel window and reads off the temporal attention map, the row of weights that says where the model looks.

import torch, torch.nn.functional as F

torch.manual_seed(0)
T, C, d = 50, 6, 32                       # 50 time steps, 6 sensor channels, model width d
x = torch.randn(T, C)                     # one window: time by channel

Wq, Wk, Wv = (torch.randn(C, d) for _ in range(3))
Q, K, V = x @ Wq, x @ Wk, x @ Wv          # project each time step to query/key/value

scores = (Q @ K.transpose(0, 1)) / d**0.5 # T x T compatibility between every pair of steps
weights = F.softmax(scores, dim=-1)       # each row is a distribution over source steps
context = weights @ V                      # T x d : each step rebuilt from relevant steps

print("attention-map shape:", tuple(weights.shape))          # (50, 50): time attends to time
print("step 25 looks most at step:", int(weights[25].argmax()))
Listing 15.1.1. One head of temporal self-attention on a 50-step window. The weights matrix is the time-by-time attention map; row t shows which moments the model blends to represent step t. Swapping the transpose to operate on the channel axis instead is the one-line change discussed next.

As Listing 15.1.1 shows, the attention map is a first-class, inspectable object: it tells you, per output moment, which input moments drove the representation, which is the raw material for the interpretability methods of Chapter 67. Treat those maps as evidence, not proof; a confident weight is a correlation the model found useful, not a guaranteed cause.

Attention over channels: which sensors explain each other

Now transpose the question. Treat each sensor channel as a token, with its value across the window (or a learned embedding of it) as the vector to be projected, and let attention run over the channel axis. The weights now form a sensor-by-sensor map: for a nine-axis inertial unit plus a barometer and a magnetometer, the model learns which channels inform which, so the three accelerometer axes can pool into a motion estimate while the magnetometer is down-weighted during an electrical disturbance. This is a learned, input-dependent alternative to the hand-built cross-channel features of Chapter 8, and its great practical virtue is that it does not care about channel order or, within limits, channel count: a model that attends over channels can accept a sensor dropping out by simply masking its key, rather than crashing on a missing input. Whether a given architecture should mix channels at all, or keep them strictly separate, is a genuine design fork with real accuracy and robustness consequences; that decision is the whole subject of Section 15.5, so here we only establish that channel attention is the tool that makes mixing learnable.

In Practice: cross-axis attention on an automotive suspension rig

A vehicle-dynamics team logged four wheel-speed sensors, a six-axis IMU, and steering angle at 200 Hz to detect a subtle damper fault that showed up only under cornering. A per-channel model missed it because the signature was not in any single sensor but in the relationship between them: a healthy damper keeps vertical IMU acceleration in a particular phase against wheel speed through a turn, and the fault shifted that phase. A transformer with attention over both axes solved it. The temporal heads aligned the signal across the several seconds of a corner, and a channel head learned to co-weight vertical acceleration with the two wheel-speed sensors on the loaded side, exactly the coupling a human engineer would have hand-coded. The attention map became a debugging aid: technicians could see the model consulting the correct sensor pair, which built trust before the system reached the condition-monitoring pipeline of Chapter 37. The team split train and test by vehicle so no single car's quirks could leak across the boundary and flatter the reported detection rate.

The Right Tool

The from-scratch version means writing the three projections, the scaled scores, an optional mask, the softmax, the value blend, and then the head-splitting, concatenation, and output projection of the multi-head wrapper, plus a numerically safe masked softmax that is easy to get wrong. That is roughly fifty lines. A framework gives you the whole multi-head block, fused and hardware-accelerated, in one:

import torch, torch.nn as nn

mha = nn.MultiheadAttention(embed_dim=64, num_heads=8, batch_first=True)
x = torch.randn(16, 50, 64)                       # batch, time steps, model width
out, attn = mha(x, x, x, need_weights=True)       # self-attention over the time axis
# feed x.transpose(1, 2) instead to attend over the channel axis
Listing 15.1.2. One constructor replaces about fifty lines of attention plumbing, including the masked softmax and multi-head bookkeeping, and returns the attention weights for inspection. Choosing which axis to attend over is the single transpose noted in the comment.

Putting the two axes together

Real sensor transformers rarely attend over one axis alone. The dominant patterns are three. Flatten time and channel into one long sequence of tokens and let full attention couple everything, which is the most expressive and the most expensive, since the score matrix grows with the product of the two axis lengths. Factorize the two axes into alternating blocks, a temporal-attention layer followed by a channel-attention layer, so cost stays linear in each axis while information still crosses both over depth; this axis-factorized design is the workhorse for multichannel sensing and reappears in the spatial-temporal graph models of Chapter 54. Restrict to one axis on purpose, the channel-independent choice, when the channels are better kept separate. The right pattern depends on how strongly your sensors are physically coupled and on your compute budget, and the trilemma of accuracy, latency, and memory it creates is what the rest of this chapter equips you to navigate.

Research Frontier

The axis-factorized idea is at the heart of current time-series transformers. Crossformer (Zhang and Yan, 2023) makes cross-channel attention explicit with a two-stage attention over time segments and across variables, and iTransformer (Liu et al., 2024) inverts the usual layout by treating each whole channel as a token so that attention runs primarily over variables rather than over time, reporting strong multivariate forecasting accuracy from that single design change. The open question these lines share is when explicit channel attention beats a strictly channel-independent model such as PatchTST; the honest current answer is that it depends on how coupled the sensors are, and settling it on your own data is precisely the comparison Section 15.5 and Lab 15 ask you to run.

Exercise

Take a multichannel window (real or synthetic, at least six channels and 200 steps). (1) Implement single-head self-attention and produce both the time-by-time map and, by transposing the input, the channel-by-channel map; visualize each as a heatmap. (2) Inject a known cross-channel dependency, for example set channel 4 to a delayed copy of channel 1, and confirm the channel attention map discovers the pair. (3) Add a causal mask to the temporal version and verify every row's weight on future positions is exactly zero. (4) Time the flattened variant against the axis-factorized variant as you double the window length, and plot how each scales.

Self-Check

1. In the attention formula, state precisely what the query, key, and value of a token represent, and explain why the scores are divided by \(\sqrt{d_k}\) rather than used raw.

2. Attention is permutation invariant. What does that imply the model cannot know about a time series on its own, and which later section supplies the missing ingredient?

3. Give one sensing task where attention over channels is clearly the more useful axis than attention over time, and justify it in terms of where the discriminative information lives.

What's Next

In Section 15.2, we confront the quadratic cost head-on by changing what a token is: instead of one token per sample, we chop the stream into short patches and embed each as a single token, cutting the sequence length by the patch size and giving each token a richer local view before attention ever runs.