Deep Learning Models: A collection of various deep learning architectures, models, & their implementations (PyTorch or TensorFlow) in Jupyter Notebooks.

  • Author: Chizoba Obasi
  • Github Repo: https://github.com/chizkidd/deep-learning-models
In [3]:
!pip install watermark
Requirement already satisfied: watermark in /usr/local/lib/python3.12/dist-packages (2.6.0)
Requirement already satisfied: ipython>=6.0 in /usr/local/lib/python3.12/dist-packages (from watermark) (7.34.0)
Requirement already satisfied: importlib-metadata>=1.4 in /usr/local/lib/python3.12/dist-packages (from watermark) (8.7.1)
Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (from watermark) (75.2.0)
Requirement already satisfied: zipp>=3.20 in /usr/local/lib/python3.12/dist-packages (from importlib-metadata>=1.4->watermark) (3.23.0)
Requirement already satisfied: jedi>=0.16 in /usr/local/lib/python3.12/dist-packages (from ipython>=6.0->watermark) (0.19.2)
Requirement already satisfied: decorator in /usr/local/lib/python3.12/dist-packages (from ipython>=6.0->watermark) (4.4.2)
Requirement already satisfied: pickleshare in /usr/local/lib/python3.12/dist-packages (from ipython>=6.0->watermark) (0.7.5)
Requirement already satisfied: traitlets>=4.2 in /usr/local/lib/python3.12/dist-packages (from ipython>=6.0->watermark) (5.7.1)
Requirement already satisfied: prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0 in /usr/local/lib/python3.12/dist-packages (from ipython>=6.0->watermark) (3.0.52)
Requirement already satisfied: pygments in /usr/local/lib/python3.12/dist-packages (from ipython>=6.0->watermark) (2.19.2)
Requirement already satisfied: backcall in /usr/local/lib/python3.12/dist-packages (from ipython>=6.0->watermark) (0.2.0)
Requirement already satisfied: matplotlib-inline in /usr/local/lib/python3.12/dist-packages (from ipython>=6.0->watermark) (0.2.1)
Requirement already satisfied: pexpect>4.3 in /usr/local/lib/python3.12/dist-packages (from ipython>=6.0->watermark) (4.9.0)
Requirement already satisfied: parso<0.9.0,>=0.8.4 in /usr/local/lib/python3.12/dist-packages (from jedi>=0.16->ipython>=6.0->watermark) (0.8.5)
Requirement already satisfied: ptyprocess>=0.5 in /usr/local/lib/python3.12/dist-packages (from pexpect>4.3->ipython>=6.0->watermark) (0.7.0)
Requirement already satisfied: wcwidth in /usr/local/lib/python3.12/dist-packages (from prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0->ipython>=6.0->watermark) (0.6.0)
In [4]:
!pip install torchinfo datasets
Requirement already satisfied: torchinfo in /usr/local/lib/python3.12/dist-packages (1.8.0)
Requirement already satisfied: datasets in /usr/local/lib/python3.12/dist-packages (4.0.0)
Requirement already satisfied: filelock in /usr/local/lib/python3.12/dist-packages (from datasets) (3.20.3)
Requirement already satisfied: numpy>=1.17 in /usr/local/lib/python3.12/dist-packages (from datasets) (2.0.2)
Requirement already satisfied: pyarrow>=15.0.0 in /usr/local/lib/python3.12/dist-packages (from datasets) (18.1.0)
Requirement already satisfied: dill<0.3.9,>=0.3.0 in /usr/local/lib/python3.12/dist-packages (from datasets) (0.3.8)
Requirement already satisfied: pandas in /usr/local/lib/python3.12/dist-packages (from datasets) (2.3.3)
Requirement already satisfied: requests>=2.32.2 in /usr/local/lib/python3.12/dist-packages (from datasets) (2.32.4)
Requirement already satisfied: tqdm>=4.66.3 in /usr/local/lib/python3.12/dist-packages (from datasets) (4.67.1)
Requirement already satisfied: xxhash in /usr/local/lib/python3.12/dist-packages (from datasets) (3.6.0)
Requirement already satisfied: multiprocess<0.70.17 in /usr/local/lib/python3.12/dist-packages (from datasets) (0.70.16)
Requirement already satisfied: fsspec<=2025.3.0,>=2023.1.0 in /usr/local/lib/python3.12/dist-packages (from fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (2025.3.0)
Requirement already satisfied: huggingface-hub>=0.24.0 in /usr/local/lib/python3.12/dist-packages (from datasets) (1.4.1)
Requirement already satisfied: packaging in /usr/local/lib/python3.12/dist-packages (from datasets) (25.0)
Requirement already satisfied: pyyaml>=5.1 in /usr/local/lib/python3.12/dist-packages (from datasets) (6.0.3)
Requirement already satisfied: aiohttp!=4.0.0a0,!=4.0.0a1 in /usr/local/lib/python3.12/dist-packages (from fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (3.13.3)
Requirement already satisfied: hf-xet<2.0.0,>=1.2.0 in /usr/local/lib/python3.12/dist-packages (from huggingface-hub>=0.24.0->datasets) (1.2.0)
Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.12/dist-packages (from huggingface-hub>=0.24.0->datasets) (0.28.1)
Requirement already satisfied: shellingham in /usr/local/lib/python3.12/dist-packages (from huggingface-hub>=0.24.0->datasets) (1.5.4)
Requirement already satisfied: typer-slim in /usr/local/lib/python3.12/dist-packages (from huggingface-hub>=0.24.0->datasets) (0.21.1)
Requirement already satisfied: typing-extensions>=4.1.0 in /usr/local/lib/python3.12/dist-packages (from huggingface-hub>=0.24.0->datasets) (4.15.0)
Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.12/dist-packages (from requests>=2.32.2->datasets) (3.4.4)
Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.12/dist-packages (from requests>=2.32.2->datasets) (3.11)
Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.12/dist-packages (from requests>=2.32.2->datasets) (2.5.0)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.12/dist-packages (from requests>=2.32.2->datasets) (2026.1.4)
Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.12/dist-packages (from pandas->datasets) (2.9.0.post0)
Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas->datasets) (2025.2)
Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas->datasets) (2025.3)
Requirement already satisfied: aiohappyeyeballs>=2.5.0 in /usr/local/lib/python3.12/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (2.6.1)
Requirement already satisfied: aiosignal>=1.4.0 in /usr/local/lib/python3.12/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (1.4.0)
Requirement already satisfied: attrs>=17.3.0 in /usr/local/lib/python3.12/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (25.4.0)
Requirement already satisfied: frozenlist>=1.1.1 in /usr/local/lib/python3.12/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (1.8.0)
Requirement already satisfied: multidict<7.0,>=4.5 in /usr/local/lib/python3.12/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (6.7.0)
Requirement already satisfied: propcache>=0.2.0 in /usr/local/lib/python3.12/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (0.4.1)
Requirement already satisfied: yarl<2.0,>=1.17.0 in /usr/local/lib/python3.12/dist-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (1.22.0)
Requirement already satisfied: anyio in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->huggingface-hub>=0.24.0->datasets) (4.12.1)
Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.12/dist-packages (from httpx<1,>=0.23.0->huggingface-hub>=0.24.0->datasets) (1.0.9)
Requirement already satisfied: h11>=0.16 in /usr/local/lib/python3.12/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->huggingface-hub>=0.24.0->datasets) (0.16.0)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.8.2->pandas->datasets) (1.17.0)
Requirement already satisfied: click>=8.0.0 in /usr/local/lib/python3.12/dist-packages (from typer-slim->huggingface-hub>=0.24.0->datasets) (8.3.1)
In [5]:
%load_ext watermark
# %reload_ext watermark
%watermark -a 'Chizoba Obasi' -v -p torch
Author: Chizoba Obasi

Python implementation: CPython
Python version       : 3.12.12
IPython version      : 7.34.0

torch: 2.9.0+cu126

Single-Layer Recurrent Neural Network (RNN) Classifier on AG News¶

The network in this notebook is an implementation of a simple RNN [1] on AG News [5, 6].

Network Architecture:

The Elman RNN (or Simple RNN) replaced the "static" assumption of standard Feed-forward networks by introducing a Hidden State ($h_t$) [4]. This creates a feedback loop where the network's output at one time step becomes an input for the next, allowing the model to process sequences of variable length.

RNN architecture

Source


Key Concepts of the RNN Architecture:¶

  1. Hidden State Recurrence ($h_t$): Unlike NiN or ResNet which only pass information forward, the RNN maintains a "hidden state" that acts as the network's internal memory. At each time step $t$, the current input $x_t$ is combined with the previous hidden state $h_{t-1}$ to produce a new $h_t$. This allows the model to capture temporal dependencies in text.

  2. Shared Weights Across Time: In a CNN, weights are shared across spatial locations (pixels). In an RNN, the same weights ($W_{ih}$ and $W_{hh}$) are reused at every single time step. This drastically reduces the parameter count compared to a massive Flatten layer and allows the model to generalize across different sentence lengths.

  3. Many-to-One Mapping: For classification tasks like IMDB (Sentiment) or AG News (Topic), we process the entire sequence but only use the final hidden state for the prediction. This final state acts as a "neural summary" of the entire sentence, similar to how Global Average Pooling summarizes a feature map.

Improvements of RNN over Deep CNN:¶

  1. Temporal Dependency Modeling: Standard CNNs (like VGG or ResNet) assume that inputs are independent of each other. While a CNN is excellent at extracting spatial features (like a nose in a face), it cannot inherently understand that "not" at the beginning of a sentence changes the meaning of "good" at the end. RNNs introduce Recurrence, allowing the network to build a "contextual summary" as it reads through a sequence.

  2. Variable-Length Processing: Traditional deep CNNs require a fixed-size input (e.g., $224\times224$ pixels). If an image is larger or smaller, it must be resized. RNNs are architecturally flexible; because the same weights are applied at every step, the model can process a 5-word tweet or a 500-word review using the exact same number of parameters.

  3. Parameter Efficiency for Sequences: To capture long-range dependencies in a CNN, one would need a massive receptive field (very deep layers or huge kernels). An RNN achieves this "depth" through time. By reusing the same $W_{hh}$ (Hidden-to-Hidden) matrix, the model achieves a form of "infinite depth" in the temporal dimension without the explosive memory cost of the 4096-unit fully connected layers found in AlexNet or VGG.

Quick Comparison Table: CNN vs. RNN Architecture¶

Feature CNN (NiN / ResNet / VGG) Recurrent Neural Network (RNN)
Primary Data Type Spatial (Images, Grids, Fixed-size) Sequential (Text, Audio, Time-series)
Input Relationship Assumes inputs are independent Assumes inputs are temporally dependent
Information Flow Feed-forward (Layer $L \to L+1$) Recurrent (Step $T-1 \to T$)
Weight Sharing Across Space (Kernel sliding) Across Time (Step-wise recurrence)
Memory Mechanism No internal memory (Static) Hidden State ($h_t$) (Internal memory)
Input Constraints Requires fixed-size (Resizing/Padding) Handles variable-length sequences
Pattern Recognition Extracts local features (Edges, Shapes) Extracts Contextual context (Grammar, Logic)
Downsampling Max Pooling / Global Average Pooling Final Hidden State (Sequence summary)



REFERENCES:

  1. Elman, Jeffrey L. "Finding Structure in Time." Cognitive Science, 1990.
  2. PyTorch. "Recurrent Layers: nn.RNN." PyTorch Documentation, 2026.
  3. Zhang, Aston, et al. "Recurrent Neural Networks (Chapter 9.1)." Dive into Deep Learning. Cambridge University Press, 2023.
  4. Raschka, Sebastian. "L15: Introduction to recurrent neural networks." Introduction to Deep Learning, 2021. [slides]
  5. Zhang, Xiang, Junbo Zhao, and Yann LeCun. "Character-level Convolutional Networks for Text Classification." Advances in Neural Information Processing Systems (NeurIPS), 2015.
  6. Gulli, Antonio. "AG's Corpus of News Articles." Academic dataset curated from the 4th largest news search engine, 2004.
  7. Lhoest, Quentin, et al. "Datasets: A Community Library for Natural Language Processing." Hugging Face Datasets, 2021.
  8. Google. (2026). "Gemini 3 Flash" [Large language model]. Google AI.
In [19]:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from sklearn.model_selection import train_test_split
from torch.utils.data import DataLoader
from torch.nn.utils.rnn import pad_sequence
from datasets import load_dataset, DatasetDict
from collections import Counter


# from torch.cuda.amp import GradScaler, autocast
from torch.amp import GradScaler, autocast
from torchinfo import summary

import matplotlib.image as mpimg
import matplotlib.pyplot as plt
%matplotlib inline

import pandas as pd
import numpy as np
import math
import time
import os
import random
import re

if torch.cuda.is_available():
    torch.backends.cudnn.deterministic = True
In [7]:
def set_all_seeds(seed):
    os.environ["PL_GLOBAL_SEED"] = str(seed)
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)

0. Model Hyperparameters¶


In [8]:
##########################
### SETTINGS
##########################

# Hyperparameters
RANDOM_SEED = 123
BATCH_SIZE = 128 
LEARNING_RATE = 0.001
NUM_EPOCHS = 20
NUM_CLASSES = 4

VOCABULARY_SIZE = 20000

EMBEDDING_DIM = 128
HIDDEN_DIM = 256
OUTPUT_DIM = 1

# Set device
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(DEVICE)
cuda

1. Data Preparation & Loading¶


In [9]:
# 1. Load Dataset (IMDB)
raw_datasets = load_dataset("ag_news")


# 2. Split 10% of 'train' to create a 'validation' set (seed for reproducibility)
train_val_split = raw_datasets["train"].train_test_split(test_size=0.1, seed=RANDOM_SEED)

ds_splits = DatasetDict({
    'train': train_val_split['train'],
    'validation': train_val_split['test'], # The 10% we just carved out
    'test': raw_datasets['test']
})

print(ds_splits)

# 2. Build Vocabulary (From Scratch Logic)
tokenizer = lambda text: re.findall(r'\w+', text.lower())
word_counts = Counter()

# Important: We only iterate over ds_splits['train']
for text in ds_splits['train']['text']:
    word_counts.update(tokenizer(text))

# Filter rare words and create mapping: : <PAD>=0, <UNK>=1, then top 20k words
vocab = {word: i + 2 for i, (word, count) in enumerate(word_counts.most_common(VOCABULARY_SIZE))}
vocab['<PAD>'] = 0
vocab['<UNK>'] = 1

print(f"Vocab size: {len(vocab)}")

# 3. Custom Dataset Class 
class TextDataset(torch.utils.data.Dataset):
    def __init__(self, dataset_split):
        self.data = dataset_split

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

    def __getitem__(self, idx):
        # Return raw text and label. Let collate_fn handle the rest.
        item = self.data[idx]
        return item['text'], item['label']

# 4. Robust Text Pipeline
def text_pipeline(text, max_len=128):
    tokens = tokenizer(text)
    # Use .get(t, 1) because 1 is your <UNK> index
    return [vocab.get(t, 1) for t in tokens][:max_len]

# 5. Robust Collate Batch
def collate_batch(batch):
    label_list, text_list, lengths = [], [], []
    
    for _text, _label in batch:
        # 1. Labels: Use LONG for AG News (Multi-class)
        label_list.append(_label) 
        
        # 2. Numericalize and Truncate
        processed_text = torch.tensor(text_pipeline(_text), dtype=torch.int64)
        text_list.append(processed_text)
        lengths.append(processed_text.size(0))
    
    # 3. Padding logic
    # We use <PAD> index 0. We use vocab.get just in case names change.
    pad_idx = vocab.get('<PAD>', 0)
    
    # This dynamically pads to the longest sequence in THIS batch (more efficient)
    text_list_padded = pad_sequence(text_list, batch_first=True, padding_value=pad_idx)
    label_tensor = torch.tensor(label_list, dtype=torch.long)
    length_tensor = torch.tensor(lengths, dtype=torch.int64)
    
    return text_list_padded.to(DEVICE), label_tensor.to(DEVICE), length_tensor.to(DEVICE)

# 6. Initialize DataLoaders
train_ds = TextDataset(ds_splits['train'])
val_ds   = TextDataset(ds_splits['validation'])
test_ds  = TextDataset(ds_splits['test'])

train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True, collate_fn=collate_batch)
val_loader   = DataLoader(val_ds, batch_size=BATCH_SIZE, shuffle=False, collate_fn=collate_batch)
test_loader  = DataLoader(test_ds, batch_size=BATCH_SIZE, shuffle=False, collate_fn=collate_batch)
Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
DatasetDict({
    train: Dataset({
        features: ['text', 'label'],
        num_rows: 108000
    })
    validation: Dataset({
        features: ['text', 'label'],
        num_rows: 12000
    })
    test: Dataset({
        features: ['text', 'label'],
        num_rows: 7600
    })
})
Vocab size: 20002

How do we figure out a good max_len? see code below


def get_robust_maxlen(dataset, tokenizer, p=95, absolute_max=512):
    # 1. Get the statistical length
    lengths = [len(tokenizer(ex[0])) for ex in dataset]
    p_length = np.percentile(lengths, p)
    
    # 2. Find the next power of 2
    # math.log2 gives the exponent, ceil rounds up, 2** raises it back
    # We use max(16, ...) to ensure we don't get a tiny length like 4 or 8
    robust_len = 2 ** math.ceil(math.log2(max(16, p_length)))
    
    # 3. Cap at absolute_max
    final_len = min(robust_len, absolute_max)
    
    print(f"{p}th Percentile: {p_length:.2f}")
    print(f"Nearest Power of 2: {robust_len}")
    print(f"Final Robust MAX_LEN: {final_len}")
    
    return final_len

# Usage
__MAX_LEN = get_robust_maxlen(train_ds, tokenizer, p=95)

In [10]:
_lengths = [len(tokenizer(text)) for text in ds_splits['train']['text'][:5000]]
import pandas as pd
print(pd.Series(_lengths).describe(percentiles=[0.5, 0.75, 0.9, 0.95, 0.99]))
count    5000.00000
mean       39.50820
std        11.20376
min        14.00000
50%        39.00000
75%        44.00000
90%        51.00000
95%        57.00000
99%        79.00000
max       133.00000
dtype: float64

2. Model Creation¶


In [11]:
class RNNFromScratch(nn.Module):
    def __init__(self, vocab_size, embed_dim, hidden_size, output_size):
        super().__init__()
        self.hidden_size = hidden_size
        
        self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
        self.w_ih = nn.Linear(embed_dim, hidden_size)
        self.w_hh = nn.Linear(hidden_size, hidden_size)
        self.fc = nn.Linear(hidden_size, output_size)
        
    def forward(self, x, lengths):
        # Transform indices to vectors: [Batch, Seq_Len, Embed_Dim]
        embedded = self.embedding(x)
        
        # embedded shape: [Batch, Seq_Len, embed_Size]
        batch_size, seq_len, _ = embedded.size()
        
        # Initialize hidden state with zeros
        h_t = torch.zeros(batch_size, self.hidden_size).to(x.device)
        
        # Manual loop over time steps (The "Recurrent" part)
        all_h = []
        for t in range(seq_len):
            x_t = embedded[:, t, :] # Get current time step
            h_t = torch.tanh(self.w_ih(x_t) + self.w_hh(h_t))
            all_h.append(h_t)

        all_h = torch.stack(all_h)
        last_indices = (lengths - 1).long().to(x.device)
        h_final = all_h[last_indices, torch.arange(batch_size)]
            
        # Only take the final hidden state for classification
        out = self.fc(h_final)
        return out
In [12]:
VOCAB_SIZE = len(vocab)
In [13]:
# --- Configuration Switch ---
DATASET_NAME = "ag_news" 

if DATASET_NAME == "IMDB":
    IS_BINARY = True
    NUM_CLASSES = 1
    # criterion = nn.BCEWithLogitsLoss()
else:
    IS_BINARY = False
    NUM_CLASSES = 4 # AG News has 4 classes
    # criterion = nn.CrossEntropyLoss()
In [14]:
set_all_seeds(RANDOM_SEED)
model = RNNFromScratch(
    vocab_size=VOCAB_SIZE, 
    embed_dim=EMBEDDING_DIM, 
    hidden_size=HIDDEN_DIM, 
    output_size=NUM_CLASSES
).to(DEVICE)

summary(model, 
        input_data=(
            torch.zeros((BATCH_SIZE, EMBEDDING_DIM), dtype=torch.long).to(DEVICE),
            torch.ones((BATCH_SIZE,), dtype=torch.long).to(DEVICE) * EMBEDDING_DIM
        ),
        col_names=["input_size", "output_size", "num_params", "mult_adds"],
        device=DEVICE)
Out[14]:
============================================================================================================================================
Layer (type:depth-idx)                   Input Shape               Output Shape              Param #                   Mult-Adds
============================================================================================================================================
RNNFromScratch                           [128, 128]                [128, 4]                  --                        --
├─Embedding: 1-1                         [128, 128]                [128, 128, 128]           2,560,256                 327,712,768
├─Linear: 1-2                            [128, 128]                [128, 256]                33,024                    4,227,072
├─Linear: 1-3                            [128, 256]                [128, 256]                65,792                    8,421,376
├─Linear: 1-4                            [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-5                            [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-6                            [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-7                            [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-8                            [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-9                            [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-10                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-11                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-12                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-13                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-14                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-15                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-16                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-17                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-18                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-19                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-20                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-21                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-22                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-23                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-24                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-25                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-26                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-27                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-28                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-29                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-30                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-31                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-32                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-33                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-34                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-35                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-36                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-37                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-38                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-39                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-40                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-41                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-42                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-43                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-44                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-45                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-46                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-47                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-48                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-49                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-50                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-51                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-52                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-53                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-54                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-55                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-56                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-57                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-58                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-59                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-60                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-61                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-62                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-63                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-64                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-65                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-66                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-67                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-68                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-69                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-70                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-71                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-72                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-73                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-74                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-75                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-76                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-77                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-78                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-79                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-80                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-81                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-82                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-83                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-84                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-85                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-86                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-87                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-88                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-89                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-90                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-91                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-92                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-93                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-94                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-95                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-96                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-97                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-98                           [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-99                           [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-100                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-101                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-102                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-103                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-104                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-105                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-106                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-107                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-108                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-109                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-110                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-111                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-112                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-113                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-114                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-115                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-116                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-117                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-118                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-119                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-120                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-121                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-122                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-123                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-124                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-125                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-126                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-127                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-128                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-129                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-130                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-131                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-132                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-133                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-134                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-135                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-136                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-137                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-138                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-139                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-140                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-141                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-142                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-143                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-144                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-145                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-146                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-147                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-148                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-149                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-150                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-151                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-152                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-153                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-154                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-155                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-156                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-157                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-158                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-159                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-160                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-161                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-162                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-163                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-164                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-165                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-166                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-167                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-168                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-169                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-170                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-171                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-172                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-173                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-174                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-175                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-176                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-177                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-178                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-179                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-180                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-181                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-182                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-183                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-184                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-185                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-186                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-187                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-188                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-189                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-190                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-191                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-192                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-193                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-194                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-195                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-196                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-197                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-198                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-199                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-200                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-201                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-202                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-203                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-204                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-205                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-206                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-207                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-208                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-209                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-210                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-211                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-212                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-213                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-214                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-215                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-216                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-217                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-218                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-219                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-220                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-221                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-222                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-223                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-224                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-225                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-226                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-227                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-228                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-229                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-230                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-231                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-232                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-233                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-234                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-235                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-236                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-237                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-238                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-239                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-240                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-241                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-242                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-243                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-244                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-245                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-246                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-247                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-248                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-249                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-250                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-251                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-252                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-253                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-254                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-255                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-256                          [128, 128]                [128, 256]                (recursive)               4,227,072
├─Linear: 1-257                          [128, 256]                [128, 256]                (recursive)               8,421,376
├─Linear: 1-258                          [128, 256]                [128, 4]                  1,028                     131,584
============================================================================================================================================
Total params: 2,660,100
Trainable params: 2,660,100
Non-trainable params: 0
Total mult-adds (Units.GIGABYTES): 1.95
============================================================================================================================================
Input size (MB): 0.13
Forward/backward pass size (MB): 83.89
Params size (MB): 10.64
Estimated Total Size (MB): 94.66
============================================================================================================================================

3. Model Training, Testing & Evaluation¶


In [15]:
def calculate_accuracy(loader, model, device, is_binary=True):
    model.eval()
    correct, total = 0, 0
    
    with torch.no_grad():
        for inputs, labels, lengths in loader:
            inputs, labels, lengths = inputs.to(device), labels.to(device), lengths.to(device)
            logits = model(inputs, lengths)
            
            if is_binary:
                predicted = (torch.sigmoid(logits.view(-1)) > 0.5).long()
            else:
                _, predicted = torch.max(logits.data, 1)
                
            total += labels.size(0)
            correct += (predicted == labels.long()).sum().item()
            
    return 100 * correct / total
In [16]:
IS_BINARY, NUM_CLASSES
Out[16]:
(False, 4)
In [17]:
# 1. Update Optimizer and Scheduler
# Binary uses BCEWithLogitsLoss; Multi-class uses CrossEntropy
criterion = nn.BCEWithLogitsLoss() if IS_BINARY else nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)

# Robust Scheduler: Cosine Annealing 
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=NUM_EPOCHS)
In [20]:
start_time = time.time()
history = {'train_loss': [], 'val_loss': [], 'train_acc': [], 'val_acc': [], 'lrs': []}

print(f"Starting training on {DEVICE}...")

for epoch in range(NUM_EPOCHS):
    model.train()
    running_loss = 0.0
    
    # 1. Training Phase
    for i, (inputs, labels, lengths) in enumerate(train_loader):
        inputs, labels, lengths = inputs.to(DEVICE), labels.to(DEVICE), lengths.to(DEVICE)

        optimizer.zero_grad()
        
        # Forward pass (passing lengths for masking logic)
        logits = model(inputs, lengths)
        
        if IS_BINARY:
            loss = criterion(logits.squeeze(), labels)
        else:
            loss = criterion(logits, labels.long())
            
        loss.backward()
        
        # --- ROBUST RNN FIX: Gradient Clipping ---
        # Prevents gradients from "exploding" during the backprop through time (BPTT)
        nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
        
        optimizer.step()
        running_loss += loss.item() * inputs.size(0)

    # 2. Metrics Calculation
    # Note: Using the updated calculate_accuracy that handles (inputs, lengths)
    train_loss = running_loss / len(train_ds)
    train_acc = calculate_accuracy(train_loader, model, DEVICE, is_binary=IS_BINARY)

    # 3. Validation Phase
    model.eval()
    val_loss = 0.0
    with torch.no_grad():
        for inputs, labels, lengths in val_loader:
            inputs, labels, lengths = inputs.to(DEVICE), labels.to(DEVICE), lengths.to(DEVICE)
            
            logits = model(inputs, lengths)
            
            if IS_BINARY:
                loss = criterion(logits.squeeze(), labels)
            else:
                loss = criterion(logits, labels.long())
                
            val_loss += loss.item() * inputs.size(0)

    val_loss /= len(val_ds)
    val_acc = calculate_accuracy(val_loader, model, DEVICE, is_binary=IS_BINARY)

    scheduler.step()
    current_lr = optimizer.param_groups[0]['lr']

    # 4. Logging
    history['train_loss'].append(train_loss)
    history['val_loss'].append(val_loss)
    history['train_acc'].append(train_acc)
    history['val_acc'].append(val_acc)
    history['lrs'].append(current_lr)

    print(f'Epoch {epoch + 1}/{NUM_EPOCHS} | '
          f'Loss (train/val): {train_loss:.4f} / {val_loss:.4f} | '
          f'Acc (train/val): {train_acc:.2f}% / {val_acc:.2f}% | '
          f'LR: {current_lr:.6f}')

print('\nTotal Training Time: %.2f min' % ((time.time() - start_time)/60))
Starting training on cuda...
Epoch 1/20 | Loss (train/val): 0.8025 / 0.6077 | Acc (train/val): 79.80% / 78.70% | LR: 0.000994
Epoch 2/20 | Loss (train/val): 0.4915 / 0.4435 | Acc (train/val): 87.89% / 86.38% | LR: 0.000976
Epoch 3/20 | Loss (train/val): 0.4110 / 0.4690 | Acc (train/val): 87.49% / 85.42% | LR: 0.000946
Epoch 4/20 | Loss (train/val): 0.3578 / 0.4514 | Acc (train/val): 88.86% / 85.62% | LR: 0.000905
Epoch 5/20 | Loss (train/val): 0.3148 / 0.4054 | Acc (train/val): 91.89% / 88.34% | LR: 0.000854
Epoch 6/20 | Loss (train/val): 0.2819 / 0.3945 | Acc (train/val): 92.18% / 88.00% | LR: 0.000794
Epoch 7/20 | Loss (train/val): 0.2484 / 0.3732 | Acc (train/val): 93.86% / 89.27% | LR: 0.000727
Epoch 8/20 | Loss (train/val): 0.2140 / 0.3960 | Acc (train/val): 94.43% / 88.96% | LR: 0.000655
Epoch 9/20 | Loss (train/val): 0.1873 / 0.4021 | Acc (train/val): 95.12% / 88.85% | LR: 0.000578
Epoch 10/20 | Loss (train/val): 0.1596 / 0.4227 | Acc (train/val): 95.93% / 89.27% | LR: 0.000500
Epoch 11/20 | Loss (train/val): 0.1297 / 0.4392 | Acc (train/val): 96.23% / 89.31% | LR: 0.000422
Epoch 12/20 | Loss (train/val): 0.1048 / 0.4454 | Acc (train/val): 97.53% / 89.22% | LR: 0.000345
Epoch 13/20 | Loss (train/val): 0.0824 / 0.4643 | Acc (train/val): 98.27% / 89.10% | LR: 0.000273
Epoch 14/20 | Loss (train/val): 0.0639 / 0.5131 | Acc (train/val): 98.52% / 89.12% | LR: 0.000206
Epoch 15/20 | Loss (train/val): 0.0501 / 0.5272 | Acc (train/val): 98.83% / 89.38% | LR: 0.000146
Epoch 16/20 | Loss (train/val): 0.0397 / 0.5431 | Acc (train/val): 99.12% / 89.24% | LR: 0.000095
Epoch 17/20 | Loss (train/val): 0.0326 / 0.5570 | Acc (train/val): 99.26% / 89.12% | LR: 0.000054
Epoch 18/20 | Loss (train/val): 0.0280 / 0.5784 | Acc (train/val): 99.30% / 89.25% | LR: 0.000024
Epoch 19/20 | Loss (train/val): 0.0251 / 0.5823 | Acc (train/val): 99.33% / 89.18% | LR: 0.000006
Epoch 20/20 | Loss (train/val): 0.0236 / 0.5805 | Acc (train/val): 99.34% / 89.27% | LR: 0.000000

Total Training Time: 23.59 min
In [21]:
# --- Final Evaluation ---
test_accuracy = calculate_accuracy(test_loader, model, DEVICE, is_binary=IS_BINARY)
print(f'\nFinal Test Accuracy: {test_accuracy:.2f}%')
Final Test Accuracy: 89.03%
In [22]:
def plot_history(history):
    """
    Plots the training and validation loss, accuracy, and learning rate
    from a PyTorch history dictionary.

    Args:
        history (dict): Dictionary containing training metrics.
                        Expected keys: 'train_loss', 'val_loss', 'train_acc', 'val_acc', 'lrs'.
    """
    epochs = range(len(history['train_loss']))

    plt.figure(figsize=(18, 5))

    # Plot 1: Loss
    plt.subplot(1, 3, 1)
    plt.plot(epochs, history['train_loss'], label='Training Loss')
    plt.plot(epochs, history['val_loss'], label='Validation Loss')
    plt.title('Loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()
    # plt.grid(True)

    # Plot 2: Accuracy
    plt.subplot(1, 3, 2)
    plt.plot(epochs, history['train_acc'], label='Training Accuracy')
    plt.plot(epochs, history['val_acc'], label='Validation Accuracy')
    plt.title('Accuracy')
    plt.xlabel('Epochs')
    plt.ylabel('Accuracy')
    plt.legend()
    # plt.grid(True)

    # Plot 3: Learning Rate
    plt.subplot(1, 3, 3)
    plt.plot(epochs, history['lrs'], label='Learning Rate', color='orange')
    plt.title('Learning Rate')
    plt.xlabel('Epochs')
    plt.ylabel('Learning Rate')
    plt.legend()
    # plt.grid(True)

    plt.tight_layout()
    plt.show()
    # plt.savefig()

plot_history(history)
No description has been provided for this image

4. Model Inference and Sentiment Prediction¶


In [26]:
# 1. Define the Human Labels
ag_news_labels = {0: "World", 1: "Sports", 2: "Business", 3: "Sci/Tech"}

# 2. Re-use your Training Tokenizer
# This ensures "Don't" becomes ["don", "t"] consistently in both phases
tokenizer = lambda text: re.findall(r'\w+', text.lower())


# Adapted the below code from:
# https://github.com/bentrevett/pytorch-sentiment-analysis/blob/experimental/2%20-%20Upgraded%20Sentiment%20Analysis.ipynb
def predict_topic(model, sentence):
    model.eval()
    
    # Step A: Tokenize exactly like training
    tokens = tokenizer(sentence)
    
    # Step B: Numericalize using the <UNK> index (1)
    indexed = [vocab.get(t, 1) for t in tokens]
    
    # Step C: Prepare Tensors [Batch, Seq_Len]
    tensor = torch.LongTensor(indexed).unsqueeze(0).to(DEVICE)
    length = torch.LongTensor([len(indexed)]).to(DEVICE)
    
    with torch.no_grad():
        # Step D: Forward Pass
        logits = model(tensor, length)
        
        # Step E: Get the highest score
        # Using softmax to see the confidence %
        probs = torch.softmax(logits, dim=1)
        prediction_idx = torch.argmax(probs, dim=1).item()
        confidence = probs[0][prediction_idx].item()
        
    return ag_news_labels[prediction_idx], confidence

# --- Test ----------------------------------------
headline = "The Olympic games were postponed due to global health concerns."
topic, score = predict_topic(model, headline)
print(f"Result: {topic} ({score:.2%})")
Result: Sports (92.18%)
In [27]:
# Create a list of test cases
ag_news_tests = [
    "The Federal Reserve is expected to keep interest rates steady as inflation cools.", # Business
    "The star striker scored a hat-trick in the final minutes of the world cup qualifier.", # Sports
    "Astronomers have discovered a new Earth-like planet orbiting a distant star system.", # Sci/Tech
    "Protests erupted in the capital city following the controversial election results.",    # World
    "New software update fixes critical security vulnerabilities in mobile operating systems." # Sci/Tech
]

print(f"{'Headline':<60} | {'Predicted Topic':<12} | {'Confidence'}")
print("-" * 85)

for text in ag_news_tests:
    topic, conf = predict_topic(model, text)
    print(f"{text[:58]:<60} | {topic:<12} | {conf:.2%}")
Headline                                                     | Predicted Topic | Confidence
-------------------------------------------------------------------------------------
The Federal Reserve is expected to keep interest rates ste   | Business     | 100.00%
The star striker scored a hat-trick in the final minutes o   | Sports       | 99.98%
Astronomers have discovered a new Earth-like planet orbiti   | Sci/Tech     | 99.87%
Protests erupted in the capital city following the controv   | World        | 98.93%
New software update fixes critical security vulnerabilitie   | Sci/Tech     | 99.96%
In [25]:
%watermark -iv
datasets   : 4.0.0
matplotlib : 3.10.0
numpy      : 2.0.2
pandas     : 2.3.3
re         : 2.2.1
sklearn    : 1.6.1
torch      : 2.9.0+cu126
torchinfo  : 1.8.0
torchvision: 0.24.0+cu126