"An Empirical Evaluation of Generic Convolutional and Recurrent Networks for Sequence Modeling." The paper that asked a heretical question — do we even need recurrence? — and answered it with a stack of plain causal, dilated 1D convolutions that match or beat LSTMs and GRUs across a battery of sequence tasks.
You are training a model to predict the next character in a stream of text 1,000 characters long. Every standard answer in 2017 says the same thing: use a recurrent net. An LSTM reads character 1, updates a hidden state, reads character 2, updates again, and so on, all the way to character 1,000. To compute the loss at the very end, the network must walk through one thousand sequential steps. Then, to learn, the gradient must crawl all the way back through those same thousand steps.
Two problems fall out of that single design decision, and you pay both as a tax on every training run.
Tax one: you cannot parallelize. Hidden state ht depends on ht-1, which depends on ht-2. The recurrence is a chain — you literally cannot compute step 500 until step 499 is finished. A modern GPU has thousands of arithmetic units sitting idle while the LSTM dribbles through the sequence one timestep at a time. Training is bottlenecked not by the math but by the ordering of the math.
Tax two: gradients must survive a long, fragile path. The signal connecting the loss at step 1,000 to the input at step 1 passes through 999 multiplicative hidden-state transitions. Even with the LSTM's gates designed precisely to protect this path, in practice the effective memory is far shorter than the sequence. Long-range dependencies — the kind where a token now depends on something hundreds of steps ago — are exactly what recurrence is supposed to capture, and exactly what it does worst.
Top: an RNN must process timesteps left to right — each cell waits for the previous one. Bottom: a convolution applies the same small filter to every position at once. Click "Animate" to watch the RNN crawl while the TCN computes a whole layer in one shot.
Bai, Kolter, and Koltun asked the question almost nobody was asking in 2017: what if recurrence was never actually necessary for sequence modeling? Convolutional networks had quietly been doing sequence work — WaveNet (van den Oord et al., 2016) generated raw audio with stacked dilated convolutions; the gated convolutional language model of Dauphin et al. (2017) matched LSTM language models. The pieces existed. Nobody had run the controlled, head-to-head bake-off.
So the authors distilled those pieces into a single, deliberately generic architecture they named the Temporal Convolutional Network (TCN), and ran it against canonical LSTM/GRU baselines on a suite of standard tasks — the adding problem, sequential and permuted MNIST, copy memory, polyphonic music, character- and word-level language modeling. Same task, same data, same hyperparameter search budget. A fair fight.
The headline finding was uncomfortable for the prevailing orthodoxy: across a "diverse set of sequence modeling tasks", the simple TCN convincingly outperformed canonical recurrent networks such as LSTMs and GRUs, while also exhibiting longer effective memory. The conclusion the authors drew was blunt — the common association of sequence modeling with recurrent networks should be reconsidered, and convolutional architectures deserve to be the default starting point.
| Property | RNN / LSTM / GRU | TCN |
|---|---|---|
| Computation per layer | Sequential — O(T) steps, one timestep at a time | Parallel — every position computed at once |
| Longest gradient path | O(T) through the time axis | O(number of layers) = O(log T) through depth |
| Receptive field | In principle infinite, in practice short | Explicit, controllable: grows as 2L |
| Memory at train time | Stores activations across all timesteps in the unrolled chain | Stores activations per layer; filters are shared |
| Variable length | Native (just keep stepping) | Native (a 1D conv slides over any length) |
Let us build the TCN exactly the way the paper does — one ingredient at a time — and prove to ourselves that each piece earns its place.
The first ingredient is the easiest to state and the most important to get exactly right: a causal convolution. The output at time t may depend only on inputs at times t, t-1, t-2, ..., and never on t+1, t+2, .... No peeking at the future.
Why insist on this? Because the TCN is meant to be a drop-in replacement for an autoregressive RNN — at training time we hand it the whole sequence and ask it to predict the next element at every position simultaneously. If the convolution at position t were allowed to look even one step ahead, the model would "see" the answer it is supposed to predict. The loss would plummet, the model would learn nothing, and at inference time — where the future genuinely does not exist yet — it would fall apart. Causality is what makes the parallel training signal honest.
An ordinary 1D convolution with kernel size k centered on position t reads inputs from t-(k-1)/2 to t+(k-1)/2 — half of them in the future. To make it causal we simply shift the window so it ends at t. With kernel size k, the causal output is:
Here f = (f0, f1, ..., fk-1) is the learned filter and x is the input sequence. Every term xt-i has index t-i ≤ t. The future is structurally unreachable. Implementations realize this by left-padding the input with (k-1) zeros and then running a standard convolution with no padding on the right — the alignment falls out automatically and the output length equals the input length.
Take a kernel of size k = 3 with weights f = (f0, f1, f2) = (0.5, 0.3, 0.2), and an input sequence x = (2, 1, 4, 3, 5). Causality says yt = f0 xt + f1 xt-1 + f2 xt-2, where any x with a negative index is treated as the zero we padded on the left. Compute each position:
Notice three things. First, the output is the same length as the input (5 values), thanks to the two left-pad zeros. Second, y0 only depends on x0 and the padding — it genuinely cannot see anything else, which is correct, since nothing precedes it. Third, and crucially, no yt ever touches an x with a larger index. That is causality, mechanically enforced.
A size-3 causal filter slides over a 7-element input. The lit cells are the (at most 3) inputs feeding the current output; they are always at or to the left of t. Drag the slider to move the output position t and watch the window stay anchored to the past.
python import torch import torch.nn as nn class CausalConv1d(nn.Module): """1D convolution that never reads the future.""" def __init__(self, c_in, c_out, k, dilation=1): super().__init__() self.pad = (k - 1) * dilation # left pad only self.conv = nn.Conv1d(c_in, c_out, k, dilation=dilation) def forward(self, x): # x: [B, C, T] x = nn.functional.pad(x, (self.pad, 0)) # pad LEFT by (k-1)*d return self.conv(x) # [B, C_out, T] — same length
A plain causal convolution has a problem: its memory is tiny. A size-3 causal filter at layer one sees 3 steps back. Stack two such layers and you reach 5 steps. To remember 1,000 steps with kernel size 3 you would need about 500 stacked layers — an absurdly deep, slow, hard-to-train network. The receptive field grows only linearly with depth. That is far too slow.
The fix is the second ingredient: dilated convolution. A dilation factor d means the filter skips d-1 inputs between each tap. Instead of reading the three contiguous inputs xt, xt-1, xt-2, a size-3 filter with dilation d reads xt, xt-d, xt-2d. The filter still has only k weights, still costs the same to compute — but it now spans a window of (k-1)·d + 1 inputs.
This is the paper's exact definition (its Equation for the dilated causal convolution), with f the size-k filter and d the dilation factor. When d = 1, it reduces to the ordinary causal convolution from Chapter 1. The genius is in how d is scheduled across the stack.
The TCN sets the dilation to grow exponentially with depth: layer 0 uses d = 1, layer 1 uses d = 2, layer 2 uses d = 4, and in general layer ℓ uses d = 2ℓ. Each layer doubles the reach of the layer below it. Stacking just L layers gives a window that spans roughly 2L timesteps — exponential coverage from linear depth. Ten layers reach back about a thousand steps; twenty layers reach back a million.
Take a size-3 filter (k = 3) and walk it down a 3-layer stack with dilations 1, 2, 4, asking each layer "which input indices does my output at position t = 8 ultimately depend on?"
Layer 0 (d = 1): output 8 reads inputs 8, 7, 6. Three contiguous steps. The closest octave.
Layer 1 (d = 2): output 8 reads the layer-0 outputs at 8, 6, 4 — and each of those, in turn, looked back 2 more steps. So through two layers, position 8 already reaches down to input 2.
Layer 2 (d = 4): output 8 reads the layer-1 outputs at 8, 4, 0 — reaching input 0. Through just three layers of a 3-tap filter, position 8 sees the entire history back to the start of the sequence.
Contrast with three non-dilated layers: they would only have reached 3 + 2 + 2 = 7 steps. Dilation turned a window of 7 into a window that covers the whole sequence, at identical compute and parameter cost.
A size-3 causal filter with an adjustable dilation. At d = 1 the taps are contiguous; at d = 2, 4, 8 they fan out, each tap jumping d steps back — same three weights, exponentially wider window. Drag to change the dilation.
python # Dilated causal conv: same filter, wider reach import numpy as np def dilated_causal_conv(x, f, d): k = len(f) T = len(x) y = np.zeros(T) for t in range(T): acc = 0.0 for i in range(k): j = t - i * d # tap i sits i*d steps back if j >= 0: # left-pad = treat j<0 as zero acc += f[i] * x[j] y[t] = acc return y
The receptive field is the single most important number to control in a TCN: how far back can the output at time t actually see? If your task needs 200 steps of context and your receptive field is 100, the model is structurally blind to half the relevant past — no amount of training will fix it. So let us derive the exact formula the paper relies on.
Consider one dilated causal layer with kernel size k and dilation d. Its output at t depends on inputs spanning from t back to t - (k-1)·d. So one such layer adds (k-1)·d to the reach. Now stack layers with dilations d0, d1, ..., dL-1. The receptive field of the top output is one (the position itself) plus the sum of each layer's added reach:
With the exponential schedule dℓ = bℓ (commonly base b = 2), the sum is a geometric series:
Read that off the page: with kernel size k = 2, RF = 2L, dead simple. With k = 3, RF = 2L+1 - 1. The receptive field is exponential in the number of layers and only linear in the kernel size. Depth is the cheap, powerful knob; kernel size is the fine adjustment.
Suppose a task needs to remember at least T = 1000 steps. Use kernel size k = 3, dilations 1, 2, 4, ..., 2L-1. We need 1 + 2·(2L - 1) ≥ 1000, i.e. 2L ≥ 500.5, i.e. L ≥ log2(500.5) ≈ 8.97. So L = 9 layers gives RF = 1 + 2·(512 - 1) = 1023, comfortably over 1000. Nine layers — not five hundred — to see a thousand steps back. That is the whole magic trick, quantified.
If 9 layers is not enough headroom (the paper warns the receptive field should be set with margin above the longest dependency you expect), you have three levers, all visible in the formula: increase the number of layers L (exponential payoff), increase the kernel size k (linear payoff), or increase the dilation base b. The paper's reference implementation stacks residual blocks and bumps the dilation by 2 each block, and recommends choosing the depth so the receptive field generously covers the task's longest dependency.
The receptive field RF = 1 + (k-1)(2L-1) plotted against the number of layers L, for the selected kernel size. The curve is exponential — a handful of layers already covers thousands of steps. Drag to change the kernel size k.
python def receptive_field(k, n_layers, base=2): """Total reach of a dilated TCN stack.""" rf = 1 for ell in range(n_layers): d = base ** ell # 1, 2, 4, 8, ... rf += (k - 1) * d # each layer adds (k-1)*d return rf # Need 1000 steps of memory? for L in range(1, 12): rf = receptive_field(3, L) print(f"L={L:2d} RF={rf}") # L= 9 RF=1023 -> first depth that clears 1000
We now know how to build an arbitrarily long receptive field cheaply: stack dilated causal convolutions and double the dilation each layer. But there is a catch. To cover a long sequence you need many layers, and very deep plain convolutional stacks are notoriously hard to train — gradients degrade, and stacking more layers can actually hurt. The third ingredient solves this directly: wrap pairs of dilated convolutions in a residual block.
A residual block computes a transformation F of its input and then adds the input back:
This is the He et al. (2016) residual idea, adapted to sequences. The point is the shortcut path: x flows around F unchanged. So the block only has to learn the residual — the difference between what it received and what it should output. If the ideal thing a layer can do is "pass the input through unchanged," it can do so trivially by driving F toward zero, instead of having to learn the identity from scratch through a nonlinear stack. Deep TCNs become trainable.
The paper's block F is not a single convolution — it is a small two-layer sequence applied within the block, each layer consisting of a dilated causal convolution followed by weight normalization, a ReLU nonlinearity, and dropout. Concretely, the data flow inside one block at dilation d is:
Two design details earn their own sentences. Weight normalization (Salimans & Kingma, 2016) reparameterizes each filter's weight vector into a direction and a magnitude, which stabilizes and speeds up training of these deep stacks — the paper uses it instead of, say, batch norm. Spatial dropout zeros out whole channels (entire feature maps) rather than individual entries, which is the right regularizer for convolutions where neighboring entries are highly correlated.
The residual add o = x + F(x) requires x and F(x) to have the same number of channels. But the very first block often changes the channel count — say the input has 1 channel (a univariate series) and the hidden width is 64. Then x has 1 channel and F(x) has 64, and you cannot add them. The fix is an optional 1×1 convolution on the shortcut path that projects x from its channel count up (or down) to match F(x). It is the cheapest possible learned linear map per timestep — one weight per (in-channel, out-channel) pair, no mixing across time.
Step through the TCN residual block. The shortcut (warm) carries x around the two-conv transform F; the output adds them. Click "Next Step" to advance through the block; the optional 1×1 conv on the shortcut appears when the channel counts differ.
python import torch.nn as nn from torch.nn.utils import weight_norm class TemporalBlock(nn.Module): def __init__(self, c_in, c_out, k, dilation, dropout=0.2): super().__init__() pad = (k - 1) * dilation self.conv1 = weight_norm(nn.Conv1d(c_in, c_out, k, padding=pad, dilation=dilation)) self.conv2 = weight_norm(nn.Conv1d(c_out, c_out, k, padding=pad, dilation=dilation)) self.pad = pad self.relu = nn.ReLU() self.drop = nn.Dropout(dropout) # 1x1 conv to match channels on the shortcut, only if they differ self.downsample = nn.Conv1d(c_in, c_out, 1) if c_in != c_out else None def _causal(self, conv, x): out = conv(x) return out[:, :, :-self.pad] # chomp the right pad -> causal def forward(self, x): # x: [B, C_in, T] y = self.drop(self.relu(self._causal(self.conv1, x))) y = self.drop(self.relu(self._causal(self.conv2, y))) # F(x) res = x if self.downsample is None else self.downsample(x) return self.relu(y + res) # o = ReLU(F(x) + x)
We have all three ingredients. Assembling them into the complete network the paper evaluates is mechanical, and the data flow is worth tracing end to end with real tensor shapes so there is no mystery left.
A TCN is a sequence of residual blocks (Chapter 4), one per "level," with the dilation doubling at each level: block 0 uses d = 1, block 1 uses d = 2, block 2 uses d = 4, and so on. The number of channels is typically constant across blocks (the paper uses a fixed hidden width); only the first block changes channels, from the input feature count to the hidden width, so only it needs the 1×1 shortcut projection.
Take a univariate input sequence of length T = 1000, batch B = 32, hidden channels C = 64, kernel k = 3, with 8 blocks (dilations 1, 2, 4, ..., 128). Tensors are in PyTorch's channels-first convention [B, C, T]:
Two structural facts deserve emphasis. The length T is preserved through every layer — left-padding plus causal trimming keeps output length equal to input length, so the TCN emits a prediction at every timestep, exactly like an unrolled RNN. And all timesteps at a given layer are computed in one parallel convolution; there is no left-to-right loop anywhere in the forward pass.
For sequence-to-sequence tasks (next-step prediction, language modeling, polyphonic music), keep the full [B, C, T] output and attach a per-timestep linear head: the model predicts the next element at every position at once. For sequence classification (e.g. predicting one label for a whole sequence, like sequential MNIST), take the output at the last timestep — which, thanks to the receptive field covering the whole input, summarizes the entire sequence — and feed it to a classifier head.
The whole network, bottom to top: each residual block doubles its dilation, the receptive field of the top output (highlighted) expands as you add blocks. Drag to set the number of blocks and watch the receptive-field cone widen toward the start of the sequence.
python import torch.nn as nn class TCN(nn.Module): def __init__(self, c_in, channels, k=3, dropout=0.2): super().__init__() layers = [] for i, c_out in enumerate(channels): d = 2 ** i # dilation doubles per block cin = c_in if i == 0 else channels[i - 1] layers += [TemporalBlock(cin, c_out, k, d, dropout)] self.net = nn.Sequential(*layers) def forward(self, x): # x: [B, c_in, T] return self.net(x) # [B, channels[-1], T] # 8 blocks of width 64 -> dilations 1..128, RF = 511 model = TCN(c_in=1, channels=[64] * 8, k=3)
It is one thing to assert that a TCN should rival an RNN; it is another to understand the structural reasons it often wins. The paper frames the comparison along a few crisp axes, and each one favors convolution.
In an RNN, the forward pass is a strict left-to-right loop. A TCN layer is one convolution: every output position is computed simultaneously. On parallel hardware this is the difference between filling every GPU core and feeding them one timestep at a time. The paper highlights this as a primary practical advantage — TCNs can process a long input sequence as a whole rather than sequentially.
An RNN's memory is implicit and fixed by its architecture — you cannot easily say "I want exactly 500 steps of context." A TCN's receptive field is a transparent function of depth, kernel size, and dilation (Chapter 3). You dial it to the task. Need more memory? Add blocks or widen the kernel, no architectural redesign.
The RNN's gradient flows backward through time, the same multiplicative path that vanishes or explodes — the very pathology LSTMs were invented to mitigate and still only partly tame. The TCN's gradient flows backward through depth, along a path orthogonal to the time axis, and the residual shortcuts give it a clean highway. Empirically the paper finds TCNs are less prone to the exploding/vanishing gradient problems that plague RNNs.
An LSTM/GRU at train time keeps the hidden and cell states for every timestep in the unrolled chain — with multiplicative gates, that is a lot of stored activation. A TCN shares its filters across all positions within a layer, so for long sequences it tends to use less memory for training. The catch, which the paper is honest about, is at inference: an RNN keeps a single fixed-size state and updates it in O(1) per new token, whereas a naive TCN reprocesses the raw prefix.
The most striking result is not that TCNs are merely competitive but that they exhibit longer effective memory than recurrent architectures of the same capacity. On stress tests explicitly designed to require very long context — the copy-memory task, and the adding problem with long sequences — the TCN retains and uses information from far in the past more reliably than LSTMs and GRUs. The thing recurrence was built for, convolution does better.
Left: an RNN's gradient must traverse every timestep — a path that grows with T. Right: a TCN's gradient hops up O(log T) layers, with residual shortcuts. Drag the slider to grow the sequence length and watch the RNN path lengthen while the TCN path barely moves.
| Axis | RNN / LSTM / GRU | TCN |
|---|---|---|
| Parallel forward pass | No — strict sequential loop | Yes — one convolution per layer |
| Receptive field | Implicit, fixed by architecture | Explicit: RF = 1 + (k-1)(2L-1) |
| Gradient path | Through time, O(T), fragile | Through depth, O(L), residual highway |
| Train-time memory | State per timestep in the unroll | Lower for long sequences (shared filters) |
| Inference per step | O(1) state update — its advantage | Naive: reprocess prefix (slower) |
| Effective memory | Shorter in practice | Longer (measured by the paper) |
The contribution of this paper is empirical, so the experiments are the whole point. The authors assembled a battery of canonical sequence-modeling benchmarks — chosen deliberately because they had been used historically to evaluate RNNs — and ran a generic TCN against carefully tuned LSTM, GRU, and vanilla RNN baselines under matched conditions.
| Task | What it tests | Type |
|---|---|---|
| The adding problem | Sum two marked numbers in a long noise sequence — pure long-range memory + arithmetic | Regression |
| Sequential MNIST | Classify a digit fed one pixel at a time (784-step sequence) | Classification |
| Permuted MNIST (P-MNIST) | Same, but pixels shuffled by a fixed permutation — destroys locality, demands long memory | Classification |
| Copy memory | Reproduce a sequence seen many steps earlier — direct test of memory length | Sequence |
| JSB Chorales / Nottingham | Polyphonic music modeling — predict the next set of notes | Generative |
| PennTreebank, WikiText-103, LAMBADA, text8 | Word- and character-level language modeling | Generative |
Across this diverse suite, the generic TCN convincingly outperformed the canonical recurrent baselines on a substantial majority of the tasks. It was not a narrow win on a cherry-picked benchmark — the breadth is the message. The same simple architecture, with the same family of hyperparameters, was competitive-to-dominant on arithmetic memory, image-as-sequence classification, music, and language alike.
On the two tasks engineered specifically to probe memory — the adding problem at long lengths and copy-memory — the gap was especially telling. RNNs are supposed to own these. The TCN solved them more reliably, converging where LSTMs stalled, which is the concrete evidence behind the "longer effective memory" claim from Chapter 6.
The paper's analysis attributes the wins to exactly the three ingredients we built: the residual connections and dilation together allow a stable, very large receptive field; the parallel convolution makes training efficient; and the gradient behaves better than backprop-through-time. The authors also note the importance of choosing the receptive field large enough for the task — under-sizing it caps performance, which is precisely the receptive-field arithmetic of Chapter 3 showing up in the results.
The paper does not overclaim. It flags that the naive TCN's inference can require more memory and reprocessing than an RNN's compact recurrent state, and that for some specific settings recurrent or attention-based models may still be preferable. The recommendation is measured: treat convolutional architectures as a strong, simple default for sequence modeling, and stop assuming recurrence is the obvious choice.
A qualitative scoreboard: for each task, relative performance of a tuned LSTM versus the generic TCN (higher bar = better; schematic, illustrating the paper's reported direction of results, not exact published numbers). Click a task to highlight it.
This is the payoff chapter. Below is a fully interactive TCN you can configure. Set the kernel size, the number of dilated residual blocks, and the input sequence length, and the explorer renders three linked views in real time: the connectivity cone (which inputs the final output actually depends on), the live receptive field readout, and a per-layer dilation schedule. Use it to build the intuition that no static diagram can: how a handful of layers reaches across a vast sequence.
Drive the controls and watch the cone of dependencies widen toward the start of the sequence. Notice the moment the receptive field exceeds the sequence length — that is when the final output can see all the way back to the first input, the condition you want for any task with sequence-spanning dependencies.
Each row is a layer (bottom = input, top = output). Lit cells are positions the final output depends on; the warm cone shows the receptive field expanding as the dilated taps reach back. The readout reports RF and whether the whole sequence is covered.
A few experiments worth running in the explorer. (1) Fix T = 16 and crank L from 1 to 5 at k = 2: watch the receptive field 2, 4, 8, 16, 32 double each step — the exponential of Chapter 3 made visible. (2) Bump k from 2 to 4 at a fixed L: the cone widens, but linearly, not exponentially — confirming kernel size is the fine adjustment, depth the powerful one. (3) Stretch T beyond the receptive field and see the cone fail to reach the leftmost inputs — the model is structurally blind to them, exactly the failure Chapter 3 warned about. The point is to feel the arithmetic in your fingertips, not just read it.
The TCN is a beautifully simple idea, and like every idea it has edges. Knowing them is what separates using a tool from understanding it.
Inference cost and memory. The TCN's great strength — see the whole sequence in parallel — becomes a liability when generating one token at a time. A recurrent net carries a fixed-size state and updates it in O(1) per step. A naive TCN must, for each new token, re-run its convolutions over the entire growing prefix and keep the whole receptive field in memory. Production autoregressive TCNs cache intermediate activations to avoid recomputation, but the bookkeeping is real, and the paper is explicit that evaluation can demand more memory than an RNN.
Fixed receptive field. The receptive field is set at design time. A task whose dependency length exceeds it is simply out of reach — the model cannot adapt the way an RNN's (in-principle) unbounded recurrence might. You must size the network for the longest dependency you expect, with margin.
Transfer across sequence lengths. A model tuned for one receptive field may not transfer cleanly to domains needing very different memory, since the architecture's reach is baked in rather than learned.
The TCN sits in a clear arc. Upstream, WaveNet pioneered stacked dilated causal convolutions for raw audio generation — the TCN distilled that mechanism into a general-purpose sequence model and benchmarked it rigorously. Downstream, the Transformer (Vaswani et al., 2017) took the same impulse — abandon recurrence, gain parallelism — but replaced the convolution's fixed local window with global self-attention, trading the TCN's O(T) per-layer cost and fixed receptive field for attention's O(T2) cost and content-based, unbounded reach. The TCN and the Transformer are siblings: both killed recurrence; they differ in how they let positions talk to each other (a fixed dilated stencil vs. a learned all-pairs lookup).
| TCN cheat-sheet | Value / Formula |
|---|---|
| Core op | Dilated causal conv: F(x)t = ∑i=0k-1 fi xt - i·d |
| Causality | Left-pad by (k-1)·d, no right padding — output never reads x>t |
| Dilation schedule | dℓ = 2ℓ (doubles per block): 1, 2, 4, 8, ... |
| Receptive field | RF = 1 + (k-1)(2L - 1) — exponential in depth L |
| Residual block | o = ReLU(x + F(x)); F = two (DilatedConv → WeightNorm → ReLU → Dropout) |
| Channel match | 1×1 conv on the shortcut when Cin ≠ Cout |
| Length | Preserved at every layer — a prediction at every timestep |
| Parallelism | Forward = one conv per layer; gradient path O(L), not O(T) |
| Headline result | Beats canonical LSTM/GRU across a diverse suite; longer effective memory |
| Main caveat | Autoregressive inference reprocesses the prefix; fixed receptive field |