LSTMs are a specialised type of RNN designed to reduce the vanishing gradient / short-term memory problem. They introduce a separate cell state which is like a "memory highway" that can carry information across many timesteps with minimal degradation.
Why use LSTM for longer dependencies?
In a standard RNN, the hidden state at step is:
| RNN Mechanism | Problem |
|---|---|
| Words --> vectors | Fine |
| Process one-by-one, passing hidden state | Hidden state gets overwritten each step |
| tanh squeezes values to | Repeated squashing causes vanishing gradients |
| Early inputs far from the output | The network "forgets" them |
LSTMs provide an additive path through a separate cell state . Gates control how much earlier information remains on this path.
LSTM Architecture

Main concept is that the LSTM cell has two parallel flows:
- Cell state : the long-term memory highway (modified by gates, not a full overwrite)
- Hidden state (): the short-term output passed to the next timestep and to the prediction head
Cell State
The cell state acts as a transport highway through the sequence:
C_{t-1} → [Forget Gate] → [Input Gate adds new info] → C_t
Information can be added or removed from the cell state at each step. A forget gate near one retains earlier information. A forget gate near zero can remove almost all of it. The cell state therefore supports long-term memory when the learned gates retain useful information.
Gates
LSTM uses three gates, each implemented as a small neural network with a sigmoid activation:
- Output near 0 means gate is closed (block information)
- Output near 1 means gate is open (allow information through)
Gate 1: Forget Gate
"What should we erase from the cell state?"
- Takes previous hidden state () + current input ()
- Outputs a value in per cell-state dimension
- Multiplied element-wise with () where values near are erased, while values near are kept
Gate 2: Input Gate
"What new information should we write to the cell state?"
Two parallel operations:
Combined cell state update:
- : old memory, partially forgotten
- : new information, selectively written
Gate 3: Output Gate
"What part of the cell state should we expose as the hidden state?"
- squashes the cell state to
- The output gate selectively exposes parts of it as the new hidden state
- Both and are passed to the next timestep
Final Prediction, y
This is the same as RNN.
Summary
| Gate | Controls | Key operation |
|---|---|---|
| Forget | What to erase from | |
| Input | What new info to write to | |
| Output | What portion of becomes |
Candidate state and updated state
The candidate proposes new content. The forget gate and input gate combine earlier memory with that candidate to produce the updated cell state . The output gate then controls how much of becomes the hidden state.
Code
PyTorch: two returned states
import torch
from torch import nn
ids = torch.tensor([[1, 2, 3], [3, 2, 1]], dtype=torch.long)
embedding = nn.Embedding(10, 8)
lstm = nn.LSTM(8, 16, batch_first=True)
head = nn.Linear(16, 2)
output, (h_n, c_n) = lstm(embedding(ids))
logits = head(h_n[-1])
print(tuple(output.shape)) # (2, 3, 16)
print(tuple(h_n.shape)) # (1, 2, 16)
print(tuple(c_n.shape)) # (1, 2, 16)
print(tuple(logits.shape)) # (2, 2)Read the returned values:
outputcontains the hidden state at each of the three positionsh_ncontains the final hidden state for each layer and directionc_ncontains the final cell state, which is separate from the hidden stateh_n[-1]selects the single layer's final hidden state for both sequencesheadmaps each 16-value state to two class scores
For token-level predictions, use head(output) to obtain shape (2, 3, 2). batch_first changes the input/output layout; the layer axis remains first in h_n and c_n. PyTorch LSTM reference.
TensorFlow/Keras: select sequences and states
import tensorflow as tf
ids = tf.constant([[1, 2, 3], [3, 2, 1]])
embedded = tf.keras.layers.Embedding(10, 8)(ids)
lstm = tf.keras.layers.LSTM(16, return_sequences=True, return_state=True)
output, h_n, c_n = lstm(embedded)
logits = tf.keras.layers.Dense(2)(h_n)
print(tuple(output.shape)) # (2, 3, 16)
print(tuple(h_n.shape)) # (2, 16)
print(tuple(c_n.shape)) # (2, 16)
print(tuple(logits.shape)) # (2, 2)return_sequences=True keeps every position. return_state=True returns the two final states as additional values. Keras returns the final states directly as (batch, units) for this layer.
In a Sequential model, replace SimpleRNN(16) with LSTM(16). Its default output is one final hidden state per sequence. The gate calculations are performed inside the layer.