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 MechanismProblem
Words --> vectorsFine
Process one-by-one, passing hidden stateHidden state gets overwritten each step
tanh squeezes values to Repeated squashing causes vanishing gradients
Early inputs far from the outputThe 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?"

  1. Takes previous hidden state () + current input ()
  2. Outputs a value in per cell-state dimension
  3. 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?"

  1. squashes the cell state to
  2. The output gate selectively exposes parts of it as the new hidden state
  3. Both and are passed to the next timestep

Final Prediction, y

This is the same as RNN.

Summary

GateControlsKey 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:

  • output contains the hidden state at each of the three positions
  • h_n contains the final hidden state for each layer and direction
  • c_n contains the final cell state, which is separate from the hidden state
  • h_n[-1] selects the single layer's final hidden state for both sequences
  • head maps 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.