RNN (Recurrent Neural Network)
An RNN is a neural network designed for sequential data (text, time series, audio). It has "memory" that allows information from the previous step to influence the current prediction.
Basic Concept
import numpy as np
# Simple RNN forward pass
def simple_rnn(x, Wxh, Whh, bias):
h = np.zeros((x.shape[0], Whh.shape[0]))
outputs = []
for t in range(x.shape[1]):
h = np.tanh(x[:, t, :].dot(Wxh) + h.dot(Whh) + bias)
outputs.append(h)
return outputs
RNN with Keras
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([
layers.SimpleRNN(64, return_sequences=True, input_shape=(10, 1)),
layers.SimpleRNN(32),
layers.Dense(1)
])
model.compile(optimizer='adam', loss='mse')
model.summary()
LSTM (Long Short-Term Memory)
LSTMs address the vanishing gradient problem with a gating mechanism.
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([
layers.LSTM(64, return_sequences=True, input_shape=(10, 1)),
layers.LSTM(32),
layers.Dense(1)
])
model.compile(optimizer='adam', loss='mse')
model.summary()
GRU (Gated Recurrent Unit)
The GRU is a simpler version of the LSTM.
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([
layers.GRU(64, return_sequences=True, input_shape=(10, 1)),
layers.GRU(32),
layers.Dense(1)
])
model.compile(optimizer='adam', loss='mse')
The Vanishing Gradient Problem
The gradient shrinks exponentially through the time steps
→ LSTMs/GRUs address this with a gating mechanism
Sequence-to-Sequence
from tensorflow import keras
from tensorflow.keras import layers
# Encoder
encoder = keras.Sequential([
layers.LSTM(128, input_shape=(None, 1))
])
# Decoder
decoder = keras.Sequential([
layers.RepeatVector(10),
layers.LSTM(128, return_sequences=True),
layers.TimeDistributed(layers.Dense(1))
])
model = keras.Sequential([encoder, decoder])
model.compile(optimizer='adam', loss='mse')
Example: Time Series Prediction
import numpy as np
from tensorflow import keras
from tensorflow.keras import layers
# Generate data
np.random.seed(42)
t = np.linspace(0, 100, 1000)
data = np.sin(t) + np.random.randn(1000) * 0.1
# Create sequences
def create_sequences(data, seq_length):
X, y = [], []
for i in range(len(data) - seq_length):
X.append(data[i:i+seq_length])
y.append(data[i+seq_length])
return np.array(X), np.array(y)
seq_length = 50
X, y = create_sequences(data, seq_length)
X = X.reshape(-1, seq_length, 1)
split = int(0.8 * len(X))
X_train, X_test = X[:split], X[split:]
y_train, y_test = y[:split], y[split:]
model = keras.Sequential([
layers.LSTM(32, input_shape=(seq_length, 1)),
layers.Dense(16, activation='relu'),
layers.Dense(1)
])
model.compile(optimizer='adam', loss='mse')
model.fit(X_train, y_train, epochs=20, batch_size=32,
validation_split=0.1, verbose=0)
loss = model.evaluate(X_test, y_test)
print(f"Test MSE: {loss:.4f}")
Bidirectional LSTM
from tensorflow import keras
from tensorflow.keras import layers
model = keras.Sequential([
layers.Bidirectional(layers.LSTM(64, return_sequences=True), input_shape=(10, 1)),
layers.Bidirectional(layers.LSTM(32)),
layers.Dense(1)
])
Comparing RNN Variants
| Type | Pros | Cons |
|---|---|---|
| SimpleRNN | Simple | Vanishing gradient |
| LSTM | Long-term memory | Slower |
| GRU | Faster than LSTM | Less powerful |
Next chapter: NLP, applying ML to natural language processing.