Multi-Stream Attention Residuals
A single residual source gives depth attention only one view of the past; MSAR expands each source into context scales, retrieves independently per stream, and learns how to mix them for the next layer.
Read the paper ↗What it is
- Context-scale source views Each earlier layer output is kept as one original view and expanded into additional views that include different amounts of nearby, earlier-token context.
- Separate depth retrieval Each view independently chooses and combines earlier layer outputs, so different contextual views can retrieve different parts of the model's history.
- Adaptive stream fusion The retrieved views are combined with learned, input-dependent scalar weights to make the single input expected by the next layer.
Why it matters
A single residual source view can limit the diversity available to depth attention. MSAR adds contextual views with causal depthwise convolutions, then lets the layer retrieve and weigh each view separately—avoiding the need for a separate dense source projection for every stream.
Before we get into the moving parts, here is the idea in plain language: MSAR gives each earlier layer output several views, each with a different amount of nearby token context. A target layer searches back through those views separately, then learns how much to use each one. That matters because depth attention needs useful diversity between its streams, but making every view with a separate dense projection is an expensive way to get it.
- Diversity: If every stream sees exactly the same residual source, having multiple streams may not buy you much. The canonical example is one source vector at a particular token: one view can preserve it unchanged, while another can mix in a short trail of earlier token states. Generalizing that idea across sources gives each stream a different contextual scale.
- Causal context: In an autoregressive model, earlier token positions contain context available at the current position. MSAR gathers a bounded amount of that history with causal, channel-wise convolutions. Think of each view as a notebook page with a different-sized window onto nearby notes; unlike a literal notebook, the combination is learned separately in each channel.
- Selective retrieval: Contextual views are not useful in the same way for every target layer. Each view gets its own depth-attention query, so the target can retrieve different earlier layers through different views. Then a small gate decides how much each retrieved view contributes to the layer input.
So what exactly does “different context” mean here? One stream is the untouched source, and the others mix it with preceding token positions over different window sizes. The convolutions are causal and depthwise: each channel combines its own recent values, rather than projecting the whole width into a new representation. In the described main configuration, the original source is joined by three convolutional views with kernel sizes 4, 8, and 12. The point is not that these are universally optimal windows; it is that the streams have distinct local receptive fields.
Once those views exist, each one does its own lookup across earlier layers. In familiar terms, each stream has its own query and its own normalized distribution over the historical sources. It retrieves a weighted combination of those sources, just as attention does a lookup over keys and values; here the expanded source is both key and value. The formula says that each stream’s scores are normalized over depth, then used to combine that stream’s views of earlier outputs. What does the target layer do with several retrieved vectors? It predicts a scalar weight for each stream from their combined contents and sums the weighted vectors. These are two distinct decisions: where to look in depth, then which contextual view to trust.
The design trades dense projections for convolutional expansion, while still carrying out retrieval separately for every stream and adding a fusion step. I like the separation of jobs: source views add local context, attention chooses depth, and the gate chooses scale. But extra views are not free just because their convolutions are lightweight. So the remaining question is whether the useful diversity from those views justifies their added computation and the work of retrieving and fusing them.
Read the deep dive
Why People Wanted It
Depth attention makes earlier layer outputs retrievable instead of forcing every layer to inherit only their running sum. But one representation of each source may not offer enough distinct views for multiple attention heads: making those views with separate dense projections would add parameters and computation. The real question is how to give depth retrieval several useful perspectives on each source without paying for a stack of expensive projections.
The Core Move
MSAR makes several context-scale views of every residual source, retrieves across depth separately in each view, then adaptively mixes the retrieved results into one layer input. If is source , its stream-specific causal operator, and the retrieval for target layer in stream , then
The causal operators provide context-diverse source views; the two sets of weights answer different questions: which depths matter within a view, and which views matter for this target layer?
Why Causal Convolutions Make Contextual Views
Now the version for readers who want more of the machinery: for stream zero, , preserving the source as-is; for each additional stream, is a layer-specific causal depthwise 1D convolution. At token position , its channel-wise form is
with positions before the sequence start zero-padded. In words, each channel combines the source at the current token with a learned, bounded history, and no term reaches into a future token. Different kernel sizes give views with different local receptive fields; the identity stream keeps the unfiltered source. The described main setting uses the identity plus convolutions with kernel sizes 4, 8, and 12. This is contextual expansion, not independent dense projection: each convolution is depthwise and causal.
Why Each Stream Retrieves Across Depth Separately
Here is the part that gets technical: after expansion, each stream has its own learned pseudo-query for each target layer. Within stream , source keys and values are the same representation, , and the normalized weights are
In words, each stream scores the available earlier sources against its own query, normalizes those scores over depth, and forms a weighted retrieval. The kernel is the RMS-normalized exponential kernel used by the attention-residual formulation. Normalization is separate per stream, so each stream produces its own depth mixture rather than competing for weight in one shared softmax.
Why Fusion Uses Input-Dependent Scalar Weights
Now the deeper design choice: concatenating all retrieved vectors and applying a full output projection would require an matrix. Instead, a lightweight projection from the flattened retrieved streams produces one scalar gate per stream:
In words, the target layer's retrieved content determines a sigmoid weight for each stream, and the weighted vectors are summed to recover one layer-width input. The gate is scalar per stream, not a learned channel-by-channel output projection. This keeps fusion lightweight while letting each target layer adapt how much it uses each contextual scale.
Minimal Causal Code
This loop makes the three operations visible: causal source expansion, per-stream retrieval over earlier layers, and scalar stream fusion. It omits the paper's particular kernel and uses ordinary dot-product softmax as a readable stand-in.
import torch
import torch.nn.functional as F
# sources: [depth, batch, time, width]
# queries: [target_depth, streams, width]
# conv: one causal depthwise Conv1d per non-identity stream
# fusion: maps flattened [streams * width] to [streams]
expanded = []
for source in sources:
views = [source] # stream 0 is the identity
for conv in convs:
x = source.transpose(1, 2) # [batch, width, time]
left_pad = conv.kernel_size[0] - 1
x = F.pad(x, (left_pad, 0))
views.append(conv(x).transpose(1, 2))
expanded.append(torch.stack(views, dim=0)) # [streams, batch, time, width]
layer_inputs = []
for l in range(len(sources)):
retrieved = []
for r in range(len(queries[l])):
if l == 0:
retrieved.append(torch.zeros_like(sources[0]))
continue
keys = torch.stack([expanded[i][r] for i in range(l)], dim=0)
scores = torch.einsum("btd,ibtd->ibt", queries[l, r], keys)
weights = scores.softmax(dim=0) # normalize over earlier source layers
retrieved.append((weights.unsqueeze(-1) * keys).sum(dim=0))
z = torch.stack(retrieved, dim=0) # [streams, batch, time, width]
gates = torch.sigmoid(fusion(z.permute(1, 2, 0, 3).flatten(-2)))
h = (z * gates.permute(2, 0, 1).unsqueeze(-1)).sum(dim=0)
layer_inputs.append(h)
What You Gain, and What You Lose
You gain depth retrieval with several context scales: the identity view preserves the source, while causal convolutional views add bounded preceding-token context. Separate queries and normalizations let each stream retrieve a different mixture of earlier layers, and adaptive scalar gates decide how those mixtures contribute to the layer input.
You do not get those views for free. Expansion adds convolutional work; retrieval still maintains separate stream results; and fusion requires a projection and a read of those retrieved vectors. The design trades the parameter and compute cost of separate dense source projections for lightweight causal depthwise convolutions and scalar stream fusion. It also gives the layer an extra choice to learn: which contextual views to trust, rather than only which depth sources to retrieve.
MSAR separates two choices: which earlier layers to retrieve in each contextual view, and how much each view should contribute.