KERNEL: ONLINE
3-DAY STREAK|350 XP (LVL 2)
HOME/BLOG/Natural Language Processing
Natural Language Processing10 min read|Dataset: IMDb Movie Reviews|Stack: PyTorch, TorchText, NLTK

PyTorch NLP Sentiment Analysis on IMDb: From Word Embeddings to Bidirectional LSTM

Natural language processing requires sequential feature representation. Master text tokenization, vocabulary mapping, nn.Embedding matrix lookups, and Bidirectional LSTM recurrent neural networks in PyTorch.

imdb pytorch nlp tutorialpytorch sentiment analysisnn embedding layer pytorchbidirectional lstm pytorchpack padded sequence pytorchnlp text classification deep learning
INTERACTIVE AI LAB READY
7-STEP VALIDATION

PRACTICE THIS COMPLETE 7-STEP PIPELINE IN YOUR BROWSER

Write your code in the retro IDE, audit your tensor shapes, and receive sub-second AI diagnostics powered by Qwen 2.5 Coder.

⚡ LAUNCH 7-STEP INTERACTIVE WORKSPACE (1-CLICK)

PyTorch NLP Sentiment Analysis on IMDb: From Word Embeddings to Bidirectional LSTM

Text classification and sentiment analysis form the backbone of modern Natural Language Processing (NLP). Before fine-tuning giant Transformer models like BERT or LLaMA, mastering Recurrent Neural Networks (RNNs) and Long Short-Term Memory (LSTM) networks is essential for understanding how neural networks model temporal sequence dependencies.

In this comprehensive tutorial, we build an end-to-end sentiment classifier on the IMDb Large Movie Review Dataset (50,000 reviews) from raw text tokenization to custom word embeddings and a Bidirectional LSTM in PyTorch.


#1. Sequence Processing Architecture

CODE
Raw Text: "This film was breathtaking..."
   │
[1. Regex Tokenization & Lowercase] ──► ['this', 'film', 'was', 'breathtaking']
   │
[2. Vocabulary Index Lookup]       ──► [42, 319, 14, 8942]
   │
[3. nn.Embedding Lookup Table]     ──► Tensor of Shape [Batch, Seq_Len, Embedding_Dim]
   │
[4. Bidirectional LSTM Stack]      ──► Forward & Backward Hidden Context Vectors
   │
[5. Classification Head]           ──► Raw Scalar Logit ➔ Probability

#2. Step-by-Step PyTorch NLP Pipeline

Step 1 & 2: Tokenization, Vocabulary Construction & Padding

python
import re
from collections import Counter
import torch
from torch.utils.data import Dataset, DataLoader

# Sample ingestion
raw_reviews = [
    ("This movie was absolutely incredible and stunning", 1),
    ("Terrible waste of time. Poor acting and dreadful plot.", 0),
    ("A masterpiece of modern cinematic storytelling.", 1),
    ("Boring, predictable, and completely uninspired.", 0),
]

def tokenize(text: str):
    text = re.sub(r'<[^>]+>', ' ', text.lower())  # Remove HTML tags
    return re.findall(r'\b\w+\b', text)

# Build Vocabulary
word_counts = Counter()
for text, _ in raw_reviews:
    word_counts.update(tokenize(text))

# Assign token indices (<PAD>=0, <UNK>=1)
vocab = {"<PAD>": 0, "<UNK>": 1}
for word, count in word_counts.items():
    if count >= 1:
        vocab[word] = len(vocab)

VOCAB_SIZE = len(vocab)
MAX_LEN = 32

def encode_text(text: str, max_len: int = MAX_LEN):
    tokens = tokenize(text)
    indices = [vocab.get(t, vocab["<UNK>"]) for t in tokens]
    if len(indices) < max_len:
        indices += [vocab["<PAD>"]] * (max_len - len(indices))
    else:
        indices = indices[:max_len]
    return indices

Step 3: Custom PyTorch Sequence Dataset

python
class IMDbDataset(Dataset):
    def __init__(self, data):
        self.data = data

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        text, label = self.data[idx]
        seq = encode_text(text)
        return torch.tensor(seq, dtype=torch.long), torch.tensor(label, dtype=torch.float32)

dataset = IMDbDataset(raw_reviews)
loader = DataLoader(dataset, batch_size=2, shuffle=True)

Step 4: Structuring the Bidirectional LSTM with nn.Embedding

python
import torch.nn as nn

class SentimentBiLSTM(nn.Module):
    def __init__(self, vocab_size: int, embed_dim: int = 64, hidden_dim: int = 64):
        super(SentimentBiLSTM, self).__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
        self.lstm = nn.LSTM(
            embed_dim,
            hidden_dim,
            num_layers=2,
            batch_first=True,
            bidirectional=True,
            dropout=0.25
        )
        # Bidirectional LSTM concatenates forward and backward hidden states (hidden_dim * 2)
        self.fc = nn.Sequential(
            nn.Linear(hidden_dim * 2, 32),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(32, 1)
        )

    def forward(self, text_seq: torch.Tensor) -> torch.Tensor:
        # text_seq: [Batch, Seq_Len]
        embedded = self.embedding(text_seq)  # [Batch, Seq_Len, Embed_Dim]
        
        # LSTM returns: output, (hidden, cell)
        lstm_out, (hidden, cell) = self.lstm(embedded)
        
        # Concatenate final forward and backward hidden states
        # hidden shape: [num_layers * 2, Batch, Hidden_Dim]
        forward_hidden = hidden[-2, :, :]
        backward_hidden = hidden[-1, :, :]
        cat_hidden = torch.cat((forward_hidden, backward_hidden), dim=1)  # [Batch, Hidden_Dim * 2]
        
        logits = self.fc(cat_hidden)
        return logits

model = SentimentBiLSTM(vocab_size=VOCAB_SIZE, embed_dim=64, hidden_dim=64)

Step 5 & 6: Training Loop with Gradient Clipping

Recurrent neural networks are notoriously susceptible to exploding gradients across long sequences. Always use clip_grad_norm_:

python
criterion = nn.BCEWithLogitsLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4)

for epoch in range(1, 11):
    model.train()
    running_loss = 0.0
    for bx, by in loader:
        optimizer.zero_grad()
        logits = model(bx).squeeze(1)
        loss = criterion(logits, by)
        loss.backward()
        
        # Prevent exploding recurrent gradients
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
        optimizer.step()
        running_loss += loss.item() * bx.size(0)

Step 7: Interactive Live Inference

python
model.eval()

def predict_sentiment(review_text: str) -> dict:
    seq = torch.tensor([encode_text(review_text)], dtype=torch.long)
    with torch.no_grad():
        logit = model(seq)
        prob = torch.sigmoid(logit).item()
        
    return {
        "review": review_text,
        "sentiment": "POSITIVE" if prob >= 0.50 else "NEGATIVE",
        "confidence": prob if prob >= 0.50 else (1.0 - prob)
    }

print(predict_sentiment("An incredible cinematic triumph with brilliant direction!"))

#Launch the Interactive NLP Lab

Write and validate your PyTorch NLP pipeline live in our browser sandbox with instant AI feedback.

PRACTICAL MASTERY

READY TO AUDIT YOUR PYTORCH CODE LIVE?

Experience active deep learning with real-time feedback loops. No installation required—run directly in your browser.