3.3.6 · HinglishDeep Learning Frameworks

GPU acceleration and device management

2,764 words13 min readRead in English

3.3.6 · AI-ML › Deep Learning Frameworks

Overview

GPU acceleration tensor computations ko CPU se specialized parallel processors (GPUs) par move karta hai jo ek saath hazaron operations perform kar sakte hain. Device management ek practice hai jisme explicitly control kiya jaata hai ki tensors aur models kahan rahenge (CPU vs GPU) aur devices ke beech data kaise move hoga.

Figure — GPU acceleration and device management

Core Concepts


GPUs kyun Jeetते Hain: Physics

Derivation: Speedup Factor

Matrix multiplication compute karne ka time jahan , :

jahan clock frequency (Hz) hai, cores (~16) hai.

GPU ke liye jisme cores (~10,000) hain:

Speedup:

Typical values daalo: GHz, , GHz, :

Lekin: Yeh assume karta hai ki . Agar tum har operation mein data transfer karo, toh speedup collapse ho jaata hai.


PyTorch Device Management: First Principles Se

Tensors Ko Apni Location Kaise Pata Hoti Hai

Har PyTorch tensor ka ek .device property hota hai. Under the hood, yeh memory mein ek pointer hai ya toh:

  • CPU DRAM (CPU cores se accessible)
  • GPU VRAM (GPU cores se accessible, CPU se isolated)

Movement Tax


Training Loop: Device-Aware Pattern

Standard Recipe

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
 
# 1. Move model to device (once)
model = MyModel().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())
 
# 2. In training loop, move batches to device
for epoch in range(num_epochs):
    for batch_idx, (data, target) in enumerate(train_loader):
        # Move batch to GPU
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)  # Computed on GPU
        loss = criterion(output, target)  # Loss on GPU
        loss.backward()  # Gradients computed on GPU
        optimizer.step()  # Parameter update on GPU
        
        # Only move to CPU for logging
        if batch_idx % 100 == 0:
            print(f'Loss: {loss.item():.4f}')  # .item() copies scalar to CPU

Har step kyun?

  1. Model par .to(device) saare parameters/buffers ko GPU memory mein move karta hai (one-time cost)
  2. Batches ko GPU par move karna: unavoidable hai (data loader disk se padhta hai → CPU RAM → GPU VRAM)
  3. Logging ke liye .item(): scalars bahut chote hote hain (~4 bytes), transfer negligible hai
  4. Baaki sab GPU par rehta hai: koi unnecessary round-trips nahi

Multi-GPU: Data Parallelism Derivation

Problem

Ek GPU ki limited memory hoti hai (~24 GB). Bade models ya batches fit nahi hote.

Solution: Batch Split Karo


Common Mistakes


Mixed Precision Training: 2× Speedup Trick

FP16 kyun?

Modern GPUs (Tensor Cores) FP16 matrix multiplies FP32 se 2× FLOPS par kar sakte hain.

Problem yeh hai: FP16 range hai. Gradients ko aksar FP32 precision chahiye hoti hai (tiny values).

Solution hai: Automatic Mixed Precision (AMP)


Memory Management: Hidden Enemy

GPU Memory Lifecycle

Allocate → Use → Free (maybe)

PyTorch ek caching allocator use karta hai:

  • Pehla allocation: cudaMalloc (slow, 1ms)
  • Baad mein: cached blocks reuse karta hai (fast, 1µs)
  • .empty_cache() memory CUDA ko return karta hai lekin OS ko free nahi karta

Bade Batches Ke Liye Gradient Accumulation

Jab batch size 512 fit nahi hoti, gradient accumulation use karo:

accumulation_steps = 4  # Effective batch size = 128× 4 = 512
 
for i, (data, target) in enumerate(train_loader):
    data, target = data.to(device), target.to(device)
    
    output = model(data)
    loss = criterion(output, target) / accumulation_steps  # Scale loss
    loss.backward()  # Accumulate gradients
    if (i + 1) % accumulation_steps == 0:
        optimizer.step()
        optimizer.zero_grad()

Derivation:

  • Normal:
  • Accumulated:

Loss divide kyun karo? .backward() gradients sum karta hai. /accumulation_steps ke bina, final gradients 4× zyada bade ho jaate.


Recall 12 Saal Ke Bacche Ko Explain Karo

Socho tumhare paas math homework hai: 1000 multiplication problems. CPU (tumhara brain): Tum unhe ek-ek karke solve karte ho. Tum smart aur fast ho, lekin time lagta hai kyunki tum ek time mein ek kaam karte ho.

GPU (1000 bachchon ki class): Har baccha EK problem solve karta hai. Yeh geniuses nahi hain, lekin milke woh utne hi time mein finish karte hain jitna ek bacche ko ek problem solve karne mein lagta hai!

Neural networks isi homework jaisi hain—lakho simple multiplications. GPUs ke paas hazaron "bacche" (cores) hain jo parallel mein kaam karte hain.

Catch yeh hai: Homework ko tumhare desk (CPU memory) se classroom (GPU memory) mein le jaane mein time lagta hai. Isliye hum saare problems ek saath move karte hain (batches), ek-ek karke nahi.

Mixed precision: Kuch problems simpler math se solve ho sakti hain (FP16 = kam decimal places use karna). Yeh faster hai lekin kabhi kabhi galti karta hai, isliye hum answer ki ek "master copy" high precision mein rakhte hain (FP32).


Connections

  • 3.3.05-Computational-graphsand-autograd: GPU computational graph ke through forward aur backward passes dono accelerate karta hai
  • 3.2.03-Backpropagation: Gradient computation embarrassingly parallel hai, GPUs ke liye perfect
  • 3.4.02-Batch-normalization: Multi-GPU mein all-reduce chahiye (sync mean/variance)
  • 4.1.01-CNN-architectures: Convolutions GPU bottleneck hain—kernels optimize karna critical hai
  • 3.3.07-Distributed-training-strategies: Multi-node device management ko machines ke across extend karta hai

#flashcards/ai-ml

Deep learning ke liye GPUs CPUs se better kyun perform karte hain? :: GPUs ke paas hazaron cores hain jo parallel arithmetic (low clock speed lekin massive parallelism) ke liye optimized hain, jabki CPUs ke paas ~16 complex cores hote hain sequential tasks ke liye. Neural network operations (matrix multiplies) embarrassingly parallel hain—har element independently compute hota hai—jo GPU architecture ke saath perfectly match karta hai.

GPU memory transfer bottleneck kya hai?
PCIe bandwidth (~16 GB/s) GPU memory bandwidth (~900 GB/s) se 50× slow hai. Frequent CPU↔GPU transfers speedup khatam kar dete hain. Solution: data ek baar move karo, computations GPU par rakho, sirf results transfer karo.
torch.cuda.synchronize() kya karta hai aur yeh kyun zaroori hai?
GPU operations asynchronous hoti hain—Python GPU compute karte waqt continue karta rehta hai. synchronize() tab tak block karta hai jab tak saare GPU kernels finish nahi ho jaate. Accurate timing ke liye aur jab CPU ko GPU results read karne hon tab zaroori hai. Iske bina, timing compute time nahi balki queue time measure karti hai.

Data parallelism ka all-reduce step explain karo :: Har GPU apne batch partition par independently gradients compute karta hai. All-reduce gradients synchronize karta hai: har GPU ko saare GPUs ke average gradient milte hain. Formula: . Ring all-reduce (NCCL library) ke through efficiently implement hota hai, bandwidth cost jahan parameter count hai, GPUs hain.

Mixed precision training loss scaling kyun use karta hai?
FP16 range hai with min . Deep networks mein gradients ho sakte hain (FP16 mein zero par underflow). Backward pass se pehle loss ko se scale karne par gradients FP16 range mein aa jaate hain. Backward ke baad, optimizer step se pehle gradients unscale karo. Chhote gradients overflow ke bina preserve hote hain.
Gradient accumulation kya hai aur kab use karna chahiye?
Limited GPU memory par large batch sizes simulate karna. Mini-batches par forward/backward compute karo, gradients accumulate karo (optimizer step mat lo), phir N accumulations ke baad step lo. Effective batch size = mini-batch × N. Critical: loss ko N se divide karo warna optimizer N× bade gradients dekhega. Tab use karo jab batch size B memory mein fit nahi hoti lekin B/N hoti hai.
GPU tensors ko list mein append karne se OOM kyun hota hai?
Har tensor reference GPU memory allocated rakhta hai. 1000 tensors ki list = VRAM mein 1000 separate allocations. PyTorch ka garbage collector Python object delete hone tak free nahi karta. Solution: scalars ke liye .item() (CPU par Python float ki tarah copy hota hai), ya bade tensors ke liye .detach().cpu(), GPU reference tod deta hai.

memory_allocated aur memory_reserved mein kya difference hai? :: memory_allocated(): currently live tensor memory. memory_reserved(): memory jo PyTorch CUDA se hold karta hai (cache bhi shaamil hai). Gap = cached free blocks. del tensor ke baad, memory_allocated girta hai lekin reserved high rehta hai (cache). .empty_cache() reserved memory CUDA ko return karta hai lekin OS ko nahi (CUDA driver ab bhi hold karta hai).

Concept Map

is

motivates

uses

speed up

few complex cores

programmed via

compiles ops into

has

needs

places tensors on

requires

bottlenecked by

reduces

maximized by

Deep Learning

Embarrassingly Parallel

GPU Acceleration

Thousands of Cores

Matrix Multiplication

CPU

Sequential Compute

CUDA Platform

GPU Kernels

Tensor Cores

Device Management

cpu or cuda

Host-to-Device Transfer

PCIe ~16 GBs

Real Speedup

Keep Data on GPU