If you’ve ever wondered why a network that can perfectly classify photos of cats and dogs struggles badly at predicting the next word in a sentence, the answer usually comes down to one thing: memory. Feed-forward networks process each input in isolation, with no concept of “what came before.” Recurrent networks were built specifically to fix that gap. This article explains both architectures in depth — their structure, the math behind them, and when to use each.
Table of Contents
- What Is a Feed-Forward Neural Network?
- What Is a Recurrent Neural Network?
- The Key Structural Difference
- The Math of a Feed-Forward Pass
- The Math of a Recurrent Pass
- Backpropagation vs. Backpropagation Through Time
- Why RNNs Struggle: Vanishing Gradients Over Time
- LSTM and GRU: Improved Recurrent Architectures
- Feed-Forward vs. RNN: Side-by-Side Comparison
- Code Example: Both Architectures in PyTorch
- When to Use Each
- Advantages and Disadvantages
- Best Practices
- Summary
1. What Is a Feed-Forward Neural Network?
A feed-forward neural network (FNN) is the most basic neural network architecture: data flows in a single direction — from the input layer, through one or more hidden layers, to the output layer — with no loops or cycles. Each input is processed completely independently of any other input; the network has no memory of previous inputs it has seen.
flowchart LR
A[Input Layer] --> B[Hidden Layer 1]
B --> C[Hidden Layer 2]
C --> D[Output Layer]
This makes feed-forward networks well suited to tasks where each data point is independent — classifying a single image, predicting house prices from a fixed set of features, or detecting fraud from a single transaction’s attributes.
2. What Is a Recurrent Neural Network?
A Recurrent Neural Network (RNN) is designed for sequential data — text, speech, time series, or anything where order matters and past context influences the current prediction. Unlike a feed-forward network, an RNN has a loop: the output (or hidden state) from processing one element of a sequence is fed back in as part of the input for the next element.
flowchart LR
X1[x1] --> H1((h1))
H0[h0] --> H1
H1 --> H2((h2))
X2[x2] --> H2
H2 --> H3((h3))
X3[x3] --> H3
H1 --> Y1[y1]
H2 --> Y2[y2]
H3 --> Y3[y3]
This hidden state acts as the network’s “memory” — a running summary of everything it has seen in the sequence so far.
3. The Key Structural Difference
| Aspect | Feed-Forward Network | Recurrent Network |
|---|---|---|
| Data flow | One direction, no loops | Loops back through time via hidden state |
| Memory | None — each input independent | Maintains hidden state across sequence steps |
| Input type | Fixed-size vectors | Variable-length sequences |
| Weight sharing | Different weights per layer | Same weights reused at every time step |
4. The Math of a Feed-Forward Pass
For a feed-forward network, each layer computes:
$$ h = f(Wx + b) $$
Where $x$ is the input, $W$ and $b$ are the layer’s weights and bias, and $f$ is an activation function. The output depends only on the current input — there is no dependency on any previous input.
5. The Math of a Recurrent Pass
For an RNN, the hidden state at time step $t$ depends on both the current input and the previous hidden state:
$$ h_t = f(W_{xh} x_t + W_{hh} h_{t-1} + b_h) $$
$$ y_t = g(W_{hy} h_t + b_y) $$
Where:
- $x_t$ = input at time step $t$
- $h_{t-1}$ = hidden state from the previous time step (the network’s “memory”)
- $W_{xh}, W_{hh}, W_{hy}$ = weight matrices, shared across all time steps
- $f, g$ = activation functions (commonly tanh for the hidden state)
The crucial detail is that $W_{xh}$, $W_{hh}$, and $W_{hy}$ are the same weights reused at every single time step — this is what allows an RNN to process sequences of any length with a fixed number of parameters.
6. Backpropagation vs. Backpropagation Through Time
Feed-forward networks are trained with standard backpropagation — gradients flow backward through the layers once per training example.
RNNs are trained with Backpropagation Through Time (BPTT) — the network is conceptually “unrolled” across all time steps into an equivalent (very deep) feed-forward network, and gradients are computed by applying the chain rule across this unrolled sequence:
$$ \frac{\partial L}{\partial W_{hh}} = \sum_{t=1}^{T} \frac{\partial L_t}{\partial h_t} \cdot \frac{\partial h_t}{\partial W_{hh}} $$
Where $T$ is the sequence length. Note that this sum, and the chain of dependencies within each term, grows with sequence length — which is directly responsible for the vanishing gradient issues RNNs are known for.
7. Why RNNs Struggle: Vanishing Gradients Over Time
Because BPTT effectively creates a network as “deep” as the sequence is long, gradients for long sequences must pass through many repeated multiplications of the same recurrent weight matrix $W_{hh}$ and activation derivative — exactly the compounding effect described in the vanishing gradient problem. For a sequence of length 100, this is equivalent to backpropagating through a 100-layer feed-forward network, and early time steps’ influence on the final gradient can shrink to nearly zero.
This is the core motivation behind gated architectures like LSTM and GRU.
8. LSTM and GRU: Improved Recurrent Architectures
LSTM (Long Short-Term Memory) networks add a separate cell state and three gates (forget, input, output) that control what information is kept, added, or discarded at each time step:
$$ f_t = \sigma(W_f [h_{t-1}, x_t] + b_f) \quad \text{(forget gate)} $$ $$ i_t = \sigma(W_i [h_{t-1}, x_t] + b_i) \quad \text{(input gate)} $$ $$ c_t = f_t \odot c_{t-1} + i_t \odot \tanh(W_c [h_{t-1}, x_t] + b_c) \quad \text{(cell state update)} $$
Because this cell state update is largely additive rather than repeatedly squashed through an activation function, gradients can flow across many time steps with far less decay.
GRU (Gated Recurrent Unit) simplifies this with just two gates (update and reset), offering similar benefits with fewer parameters and often faster training.
| Architecture | Gates | Memory Mechanism | Typical Use |
|---|---|---|---|
| Vanilla RNN | None | Single hidden state | Short sequences only |
| LSTM | 3 (forget, input, output) | Separate cell state + hidden state | Long sequences, complex dependencies |
| GRU | 2 (update, reset) | Single hidden state (merged) | Similar to LSTM, fewer parameters |
9. Feed-Forward vs. RNN: Side-by-Side Comparison
| Feature | Feed-Forward Network | Recurrent Network |
|---|---|---|
| Handles sequences? | No (fixed-size input only) | Yes (variable-length sequences) |
| Has memory of past inputs? | No | Yes, via hidden state |
| Parameter count vs. sequence length | Independent of sequence length | Fixed (weights shared across time steps) |
| Training algorithm | Backpropagation | Backpropagation Through Time (BPTT) |
| Vulnerable to vanishing gradients | Yes, with depth | Yes, especially with long sequences |
| Common tasks | Image classification, tabular prediction | Text generation, time series, speech recognition |
| Modern alternative | — | Transformers (attention-based, no recurrence) |
10. Code Example: Both Architectures in PyTorch
import torch
import torch.nn as nn
# Feed-Forward Network
class FeedForwardNet(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super().__init__()
self.fc1 = nn.Linear(input_size, hidden_size)
self.fc2 = nn.Linear(hidden_size, output_size)
self.relu = nn.ReLU()
def forward(self, x):
return self.fc2(self.relu(self.fc1(x)))
# Recurrent Network
class SimpleRNN(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super().__init__()
self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)
self.fc = nn.Linear(hidden_size, output_size)
def forward(self, x):
# x shape: (batch, sequence_length, input_size)
out, hidden = self.rnn(x) # processes the whole sequence, step by step
return self.fc(out[:, -1, :]) # use the final time step's output
ff_model = FeedForwardNet(10, 32, 1)
rnn_model = SimpleRNN(input_size=10, hidden_size=32, output_size=1)
single_input = torch.randn(5, 10) # 5 independent samples
sequence_input = torch.randn(5, 20, 10) # 5 sequences, 20 time steps each
print(ff_model(single_input).shape)
print(rnn_model(sequence_input).shape)
Notice the input shapes: the feed-forward network takes independent samples, while the RNN takes a (batch, sequence_length, features) tensor — explicitly modeling the temporal dimension.
10b. Sequence Modeling Patterns: One-to-Many, Many-to-One, Many-to-Many
Unlike feed-forward networks, which always map one fixed-size input to one fixed-size output, RNNs support several different input/output patterns depending on the task:
| Pattern | Example Task | Description |
|---|---|---|
| One-to-one | Image classification (not really an RNN use case) | Single input, single output — this is what feed-forward networks handle |
| One-to-many | Image captioning | Single input (image) produces a sequence of outputs (caption words) |
| Many-to-one | Sentiment analysis | A sequence of inputs (words) produces a single output (positive/negative) |
| Many-to-many (aligned) | Part-of-speech tagging | A sequence of inputs maps to an equal-length sequence of outputs |
| Many-to-many (unaligned) | Machine translation | An input sequence maps to an output sequence of a different length (encoder-decoder) |
This flexibility is one of the RNN’s biggest structural advantages over feed-forward networks, which are fundamentally restricted to fixed-size, one-to-one mappings unless you artificially reshape the problem (e.g., using a sliding window of fixed size, which loses the ability to model arbitrarily long dependencies).
10c. Bidirectional RNNs
A standard RNN only has access to past context — when predicting the meaning of a word, it has seen everything before that word but nothing after it. For many tasks, future context matters just as much. Bidirectional RNNs (BiRNNs) address this by running two separate RNNs over the sequence — one forward, one backward — and combining their hidden states at each time step:
$$ h_t = [\overrightarrow{h_t} ; \overleftarrow{h_t}] $$
Where $\overrightarrow{h_t}$ is the hidden state from the forward-direction RNN and $\overleftarrow{h_t}$ is the hidden state from the backward-direction RNN, concatenated together. This is especially valuable for tasks like named entity recognition or part-of-speech tagging, where the correct label for a word often depends on words that come later in the sentence, not just earlier ones. Bidirectional variants of LSTM and GRU are extremely common in practice and typically outperform their unidirectional counterparts whenever the full sequence is available at prediction time (i.e., not a real-time streaming scenario).
11. When to Use Each
Use a Feed-Forward Network when:
- Each data point is independent (no meaningful order or time dependency).
- You’re working with fixed-size tabular data, images (typically paired with CNNs), or simple classification/regression tasks.
Use a Recurrent Network (or its modern successors) when:
- Data is sequential and order matters — text, audio, time series, sensor streams.
- You need the model to use context from earlier in the sequence to make predictions later.
- Note: for many NLP tasks today, Transformers have largely replaced RNNs due to better parallelization and longer-range dependency handling, but RNNs remain relevant for streaming data, smaller models, and certain time-series applications.
12. Advantages and Disadvantages
Feed-Forward Networks
Advantages: simple, fast to train, easy to parallelize, well-understood. Disadvantages: cannot handle sequential dependencies or variable-length input naturally.
Recurrent Networks
Advantages: naturally models sequential/temporal data; shares weights across time steps, keeping parameter count manageable regardless of sequence length. Disadvantages: sequential computation is hard to parallelize (slower training); prone to vanishing/exploding gradients on long sequences (mitigated by LSTM/GRU); increasingly outperformed by Transformer architectures on many NLP tasks.
13. Best Practices
- Don’t force sequential data into a feed-forward network by simply concatenating time steps — you’ll lose the ability to generalize across different sequence lengths and positions.
- For any RNN-based model, prefer LSTM or GRU over vanilla RNN cells unless your sequences are very short.
- Use gradient clipping when training recurrent architectures to guard against exploding gradients.
- For new NLP projects requiring long-range context, evaluate Transformer-based architectures before defaulting to RNNs — they often outperform RNNs and train faster due to parallelization.
- Normalize/scale time-series inputs before feeding them into an RNN, just as you would for a feed-forward network.
14. Summary
The fundamental difference between feed-forward and recurrent neural networks comes down to memory: feed-forward networks process each input independently with no sense of history, while recurrent networks maintain a hidden state that carries information from previous time steps forward, making them naturally suited to sequential data. This memory comes at a cost — RNNs are harder to train (via Backpropagation Through Time) and prone to vanishing gradients over long sequences, which is why gated variants like LSTM and GRU, and more recently Transformer architectures, have become the preferred choice for demanding sequence modeling tasks.
14b. Where Transformers Fit Into This Picture
No discussion of feed-forward versus recurrent networks is complete without addressing the architecture that has largely superseded RNNs for many sequence tasks: the Transformer. It’s worth clarifying an easy point of confusion — Transformers are not recurrent at all; they process an entire sequence in parallel using self-attention, then apply position-wise feed-forward layers to each token independently. In a sense, a Transformer combines ideas from both worlds covered in this article: it handles sequential, order-dependent data (like an RNN) but does so without any recurrence, using purely feed-forward computation plus attention (like a traditional feed-forward network, applied per position).
This design gives Transformers two major advantages over RNNs: they can be parallelized during training (since there’s no need to process time steps sequentially, one after another), and they don’t suffer from the same vanishing gradient issues over long sequences, since attention creates a direct path between any two positions regardless of their distance apart. This is why, for tasks like machine translation, text generation, and most large language models, Transformers have become the default choice, with RNNs now reserved mostly for streaming applications, smaller-scale time-series problems, or settings with strict memory constraints where the quadratic cost of full self-attention is impractical.
14c. Closing Perspective
The comparison between feed-forward and recurrent networks ultimately comes down to a single question: does your problem have meaningful order and history, or does each data point stand alone? Get that answer right, and the rest of the architectural decisions — whether to use plain RNN cells or gated LSTM/GRU variants, whether to go bidirectional, whether to reach for a Transformer instead — become much easier to reason through, because they’re all just refinements of how to handle the sequential structure you’ve already identified as present (or absent) in your data.
References
- Rumelhart, D., Hinton, G., & Williams, R. (1986). Learning representations by back-propagating errors. Nature. https://www.nature.com/articles/323533a0
- Hochreiter, S., & Schmidhuber, J. (1997). Long Short-Term Memory. https://www.bioinf.jku.at/publications/older/2604.pdf
- Cho, K., et al. (2014). Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation (GRU introduction). https://arxiv.org/abs/1406.1078
- PyTorch Documentation — nn.RNN, nn.LSTM, nn.GRU. https://pytorch.org/docs/stable/nn.html#recurrent-layers
- Vaswani, A., et al. (2017). Attention Is All You Need. https://arxiv.org/abs/1706.03762