Datasets and DataLoaders
3.3.4· AI-ML › Deep Learning Frameworks
Dataset kya hota hai?
Dataset Interface ko First Principles se Derive karna
Problem se shuru karo: Hamare paas N samples hain (images, texts, etc.). Training ke dauran humein:
- Koi bhi sample index se access karna hai (batching ke liye)
- Total count pata hona chahiye (epoch iteration ke liye)
Naive approach: Sab kuch ek list mein load karo.
data = [load_image(i) for i in range(N)] # Uses N × image_size RAM!Problem: ImageNet ke liye (1.2M images, ~150GB), yeh crash kar deta hai.
Solution: Lazy loading indexing ke zariye.
class Dataset:
def __len__(self): return N
def __getitem__(self, idx): return load_image(idx) # Load on-demandAb hum ek baar mein ek image load karte hain, RAM constant rehti hai.
Example 1: Scratch se Custom Image Dataset
Problem: Ek folder se images load karo jahan filenames mein labels encoded hain (jaise cat_001.jpg).
import os
from PIL import Image
import torch
from torch.utils.data import Dataset
class CustomImageDataset(Dataset):
def __init__(self, img_dir, transform=None):
self.img_dir = img_dir
self.transform = transform
# List all image paths
self.img_paths = [f for f in os.listdir(img_dir) if f.endswith('.jpg')]
# WHY: We store paths, not images (lazy loading)
def __len__(self):
return len(self.img_paths)
# WHY: Needed for batching and epoch calculation
def __getitem__(self, idx):
img_path = os.path.join(self.img_dir, self.img_paths[idx])
image = Image.open(img_path) # Load NOW (on-demand)
# WHY: Only load when requested, not during __init__
# Extract label from filename (e.g., "cat_001.jpg" → "cat")
label = self.img_paths[idx].split('_')[0]
if self.transform:
image = self.transform(image)
# WHY: Data augmentation (random crops, flips) happens per-access
return image, labelYeh step (lazy loading) kyun? Agar humne __init__ mein saari images load ki hoti, toh training shuru hone se pehle humein gigabytes RAM chahiye hoti. Ab hum ek baar mein ~1 image load karte hain.
DataLoader kya hota hai?
Training Requirements se DataLoader Derive karna
Training loop structure:
for epoch in range(num_epochs):
for batch in data: # Need batches, not individual samples
loss = model(batch)
loss.backward()Requirements analysis:
-
Batching: SGD/Adam ko gradient estimation ke liye batches chahiye (jaise 32 samples).
- Math: Gradient estimate
- Batches kyun? Gradient variance reduce karta hai, GPU parallelism enable karta hai.
-
Shuffling: Model ko sample order seekhne se rokta hai (sequence pe overfitting).
- Har epoch kyun? Model same data dekhta hai, lekin different order mein → better generalization.
-
Parallelism: Data loading (disk I/O, decompression) CPU-bound hota hai. GPU idle wait karta hai.
- Solution: Multiple CPU processes use karo jo next batch pre-load karte hain jab GPU current pe train kar raha ho.
Example 2: DataLoader with All Features
from torch.utils.data import DataLoader
# Assume we have the CustomImageDataset from before
dataset = CustomImageDataset(img_dir='./data/images')
dataloader = DataLoader(
dataset,
batch_size=32, # Stack 32 samples per batch
shuffle=True, # Randomize order each epoch
num_workers=4, # 4 parallel CPU processes
pin_memory=True, # Speed up CPU → GPU transfer
drop_last=True # Drop incomplete last batch
)
# WHY each parameter:
# - batch_size=32: Trade-off between gradient accuracy and speed
# (larger batch → less noise, but slower per-iteration)
# - shuffle=True: Breaks correlation between consecutive samples
# (prevents overfitting to data order)
# - num_workers=4: While GPU trains batch t, CPUs load batch t+1
# (overlap computation and I/O)
# - pin_memory=True: Allocates tensors in page-locked RAM
# (faster DMA transfer to GPU, ~10-20% speedup)
# - drop_last=True: Ensures all batches have same size
# (some models/layers require fixed batch size)DataLoader use karna:
for epoch in range(10):
for batch_idx, (images, labels) in enumerate(dataloader):
# images: Tensor of shape (32, 3, 224, 224)
# labels: Tensor of shape (32,)
images = images.to(device) # Move to GPU
labels = labels.to(device)
# Train step...
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()Yeh step (enumerate) kyun? Hume batch index (logging ke liye) aur data dono milte hain. DataLoader andar se dataset.__getitem__() ko 32 baar call karta hai, results stack karta hai, aur yield karta hai.
Deep Dive: Collation
Custom Collate Function
def collate_variable_length(batch):
# batch: list of tuples [(seq1, label1), (seq2, label2), ...]
# where seq_i has shape (length_i, feature_dim)
# Step 1: Find max length
max_len = max([seq.shape[0] for seq, _ in batch])
# WHY: Tensors must be rectangular (all rows same length)
# Step 2: Pad sequences to max_len
padded_seqs = []
labels = []
for seq, label in batch:
pad_amount = max_len - seq.shape[0]
padded = torch.nn.functional.pad(seq, (0, 0, pad_amount))
# WHY: Pad on length dimension (dim 0), not feature dimension (dim 1)
padded_seqs.append(padded)
labels.append(label)
# Step 3: Stack into batch tensors
batch_seqs = torch.stack(padded_seqs) # (batch_size, max_len, feature_dim)
batch_labels = torch.tensor(labels) # (batch_size,)
return batch_seqs, batch_labels
# Use with DataLoader
dataloader = DataLoader(dataset, batch_size=32, collate_fn=collate_variable_length)Padding kyun? GPU operations (matrix multiplication) ke liye fixed-size inputs chahiye. Padding se thodi computation waste hoti hai lekin batching enable hoti hai.
Common Patterns aur Optimizations
Diagram Explanation

Yeh diagram data pipeline flow dikhata hai:
- Dataset: On-disk data (images, CSVs) with lazy
__getitem__()access - Sampler: Indices generate karta hai (shuffled ya sequential)
- Workers: Parallel CPU processes indices ke zariye samples fetch karte hain
- Collate: Samples ko batch tensors mein stack karta hai
- GPU: Batched tensors receive karta hai training ke liye
Key insight: DataLoader ek producer-consumer system hai jahan CPU workers (producers) GPU (consumer) se aage chalte hain taaki wait time khatam ho.
Recall Ek 12-saal ke bacche ko samjhao
Socho tum ek test ki taiyaari flashcards se kar rahe ho. Tumhare paas ek bade box mein 1000 cards hain.
Dataset un cards ko organize karna hai. Har card pe ek number (index) hai, aur tum kisi bhi card ko uske number se nikal sakte ho. Lekin tum ek saath saare 1000 cards NAHI nikalte—woh toh poori desk cover kar denge!
DataLoader tumhara study buddy hai jo:
- Cards shuffle karta hai taaki tum order yaad na kar lo (woh toh cheating hai!)
- Tumhe ek baar mein 10 cards deta hai (ek batch) taaki tum unhe saath mein padh sako
- 4 dost hain (workers) jo next batch pehle se laa dete hain jab tum current padhte ho—taaki kabhi wait na karo
Magic yeh hai: Tum 1000 cards padhte ho bina ek saath desk pe rakhe, aur tumhare dost ensure karte hain ki agla batch hamesha ready ho!
Connections
- 3.3.01-Introduction-to-PyTorch: DataLoader PyTorch ke
torch.utils.datamodule ka part hai - 3.3.05-Training-Loop-Basics: DataLoader training loop ko batches feed karta hai
- 2.2.03-Mini-Batch-Gradient-Descent: Batch size parameter DataLoader se aata hai
- 3.2.04-Data-Augmentation: Transforms Dataset ke
__getitem__()mein apply hote hain - 4.1.02-Transfer-Learning: Pre-trained models specific normalization expect karte hain (Dataset mein apply)
- 3.3.08-Distributed-Training: Multi-GPU ke liye DataLoader with DistributedSampler
#flashcards/ai-ml
PyTorch Dataset ko kaun se do methods implement karni zaroori hain? :: __len__() (total samples return karta hai) aur __getitem__(idx) (index idx par sample return karta hai)
Datasets mein lazy loading kyun use karte hain, __init__ mein saara data load karne ki jagah?
__getitem__ call hota hai, RAM usage constant rakhte hue. Ek saath saara data load karna N × sample_size RAM ki demand karega, jo bade datasets ke liye (jaise ImageNet ~150GB) crash kar deta hai.Dataset size N, batch size B, aur number of batches ke beech mathematical relationship kya hai?
DataLoader ka collate function kya karta hai?
DataLoader mein num_workers > 0 kyun use karte hain?
DataLoader mein pin_memory=True kya karta hai?
Shuffling DataLoader mein kyun karein, Dataset.getitem() ke andar nahi?
DataLoader mein prefetch_factor kya hai? :: Har worker kitne batches pehle se load karta hai. prefetch_factor=2 aur num_workers=4 ke saath, system current GPU batch se 8 batches aage load karta hai, computation aur I/O ke beech overlap maximize karta hai.