পাঠ ১৮ · ৪০-এর মধ্যে · মডিউল ৩

Pooling — Max ও Avg

Pooling layers — spatial downsampling
৬ মিনিট পড়া মাঝারি · Intermediate PyTorch hands-on

এই পাঠে যা শিখবেন

  • Max pooling ও Average pooling — গাণিতিক সংজ্ঞা
  • Pooling-এর তিন উদ্দেশ্য — downsample, translation invariance, receptive field
  • Global Average Pooling — modern CNN-এর head
  • Stride convolution বনাম pooling — কখন কোনটা
  • PyTorch MaxPool2d, AvgPool2d, AdaptiveAvgPool2d

১ · Pooling — কেন দরকার

Convolution-এর পর feature map-এর spatial dimension সাধারণত একই থাকে (same padding-এ)। কিন্তু গভীর network-এ দরকার:

  • Compute কমানো: $224 \times 224$ feature map পরিষ্কারভাবে process করা ব্যয়বহুল।
  • Receptive field বাড়ানো: ছোট spatial size-এ একই kernel — input-এর বড় region cover।
  • Translation invariance: ছোট shift-এ একই output।
  • Overfitting কমানো: spatial detail কমে — generalize ভাল।

Pooling একটি window (সাধারণত $2 \times 2$) input-এর উপর slide করে — প্রতিটি window-এ একটি single value output।

২ · Max Pooling

Max pooling

$2 \times 2$ window-এ ৪টি value-এর সর্বোচ্চটি output: $$O[i, j] = \max_{(a, b) \in \text{window}} I[i \cdot s + a, \; j \cdot s + b]$$ Default stride = window size (overlap নেই)।

উদাহরণ:

$$I = \begin{bmatrix} 1 & 3 & 2 & 4 \\ 5 & 6 & 7 & 8 \\ 3 & 2 & 1 & 0 \\ 1 & 2 & 3 & 4 \end{bmatrix} \xrightarrow{\text{MaxPool 2×2}} \begin{bmatrix} 6 & 8 \\ 3 & 4 \end{bmatrix}$$

প্রতি $2 \times 2$ block-এ সর্বোচ্চ — output $4 \times 4 \to 2 \times 2$।

ভাবুন একটি সংবাদপত্রের প্রতিটি অনুচ্ছেদে শুধু সবচেয়ে গুরুত্বপূর্ণ একটি বাক্য রাখলেন। অনুচ্ছেদের আকার কমে গেল, কিন্তু "highlight" থেকে গেল। এটাই max pooling — feature map-এর প্রতিটি ছোট region-এ "সবচেয়ে শক্তিশালী activation" রেখে বাকিটা ফেলে দেওয়া।

৩ · Average Pooling

$$O[i, j] = \frac{1}{k^2} \sum_{(a, b) \in \text{window}} I[i \cdot s + a, \; j \cdot s + b]$$

একই উদাহরণে:

$$\xrightarrow{\text{AvgPool 2×2}} \begin{bmatrix} (1+3+5+6)/4 & (2+4+7+8)/4 \\ (3+2+1+2)/4 & (1+0+3+4)/4 \end{bmatrix} = \begin{bmatrix} 3.75 & 5.25 \\ 2.0 & 2.0 \end{bmatrix}$$

৪ · Max বনাম Avg — কখন কোনটা

  • Max: "presence" detection — কোনো feature আছে কিনা। Sharp, discriminative।
  • Avg: "smooth summary" — overall texture। Stable, less noisy।
  • Empirical: early-mid layers-এ Max প্রায়ই ভাল (sharp feature), final layer-এ Avg (global summary)।

৫ · Global Average Pooling — modern CNN-এর head

Traditional CNN: Conv → Pool → ... → Flatten → FC → Output. Flatten-এর পর FC-তে millions of parameter।

Network-in-Network (Lin et al., ২০১৪) idea — Flatten + FC সরিয়ে Global Average PoolingGlobal Average Pooling (GAP)প্রতিটি feature map-এর সম্পূর্ণ spatial mean — একটি single number per channel। Output: $C$-D vector। Modern CNN-এ FC-র জায়গায়।:

GAP

Input: $C \times H \times W$. Output: $C$-dimensional vector ($C$টি channel, প্রতিটি channel-এর spatial mean)।
$$\text{GAP}(I)[c] = \frac{1}{H \cdot W} \sum_{i, j} I[c, i, j]$$

  • Parameter zero — overfitting risk minimal।
  • Spatial structure সম্পূর্ণ collapse — শুধু channel-wise summary।
  • ResNet, DenseNet, EfficientNet — সবার head।
Max Pooling 2×2 (stride 2) 4×4 → 2×2 — প্রতি window-এর max Input 4×4 1 3 2 4 5 6 7 8 3 2 1 0 1 2 3 4 max per window Output 2×2 6 8 3 4 প্রতি 2×2 region-এর highest activation rখা — spatial detail কমে, "presence" থেকে যায়। No parameter — fixed operation।
Max Pooling 2×2: প্রতিটি color-coded window-এর সর্বোচ্চ value output-এ। Spatial size 4×4 → 2×2।

৬ · PyTorch — তিনটি pooling

Python · PyTorch
import torch
import torch.nn as nn

# একটি ছোট 4x4 feature map
x = torch.tensor([
    [1, 3, 2, 4],
    [5, 6, 7, 8],
    [3, 2, 1, 0],
    [1, 2, 3, 4],
], dtype=torch.float32).view(1, 1, 4, 4)

# Max pooling
maxpool = nn.MaxPool2d(kernel_size=2, stride=2)
print("Max:\n", maxpool(x).squeeze())

# Average pooling
avgpool = nn.AvgPool2d(kernel_size=2, stride=2)
print("\nAvg:\n", avgpool(x).squeeze())

# Global Average Pooling — output 1x1 per channel
gap = nn.AdaptiveAvgPool2d(1)
print("\nGAP:", gap(x).squeeze())

    
Max — sharp। Avg — smooth। GAP — পুরো spatial mean (single number per channel)।

৭ · Stride convolution — pooling-এর alternative

Modern CNN-এ অনেক ক্ষেত্রে separate pool layer-এর জায়গায় conv-এ stride 2:

  • Pool: fixed (max বা avg), parameter-less।
  • Stride conv: learnable downsample — kernel কীভাবে summarize শেখে।
  • ResNet, DenseNet — stride-2 conv।
  • VGG — pool layer।
Python · PyTorch
import torch.nn as nn

# Pool-based downsample
pool_block = nn.Sequential(
    nn.Conv2d(64, 128, 3, padding=1),
    nn.ReLU(),
    nn.MaxPool2d(2),
)

# Stride-conv-based downsample
stride_block = nn.Sequential(
    nn.Conv2d(64, 128, 3, stride=2, padding=1),
    nn.ReLU(),
)
# একই output spatial size — different parameter behavior

    

৮ · Pooling-এর problem

  • Information loss: spatial detail হারায় — segmentation/detection-এ অসুবিধা।
  • Aliasing: stride-2 pooling-এ shift-equivariance ভেঙে যায় (Zhang ২০১৯, "Making CNNs Shift-Invariant Again")।
  • Backprop: max pool-এ gradient শুধু max position-এ flow — অন্য neuron train পায় না।
Capsule Network (Hinton ২০১৭) — pooling-এর সবচেয়ে কঠোর critique। তবু আজকের production CNN-এ pooling প্রায়ই থাকে — perfection-এর চেয়ে practical importance বেশি।

৯ · Modern usage summary

  • Stem-এ: ৭×৭ conv stride 2 + ৩×৩ MaxPool stride 2 (ResNet)।
  • Mid layers: stride-2 conv (ResNet, DenseNet) বা MaxPool (VGG)।
  • Head: Global Average Pool → linear classifier।
  • Detection/Segmentation: pool কমিয়ে atrous/dilated conv (DeepLab)।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ "Pooling = translation invariance" — common claim। কিন্তু Zhang (২০১৯) দেখান CNN actually shift-invariant না। কী ঘটে, এবং কী fix?

Aliasing — signal processing-এর ক্লাসিক সমস্যা। stride-2 sampling Nyquist criterion ভাঙে।

সমস্যাটি:

  • Stride-2 pool — input-এর প্রতি ২য় position।
  • Input ১ pixel shift করলে — pool-এর "even" position-গুলো "odd" হয়ে যায়।
  • Output সম্পূর্ণ ভিন্ন — invariance fail।

Empirical verification:

  • Zhang showed — image ১ pixel shift করলে CNN-এর top-1 prediction probability ৪০% case-এ বদলায়।
  • Same image, slightly different translation → different class।
  • Adversarial-এর কাছাকাছি behavior।

Fix — anti-aliased pooling:

  • Stride-2 max pool-এর আগে blur (low-pass filter) — Nyquist preserve।
  • Triangle filter বা Gaussian — typical।
  • Performance + invariance দু'টোই improve।

BlurPool implementation:

class BlurPool(nn.Module):
    def __init__(self, channels, stride=2):
        super().__init__()
        kernel = torch.tensor([1., 2., 1.])
        kernel = kernel[:, None] * kernel[None, :]
        kernel = kernel / kernel.sum()
        self.register_buffer('kernel',
            kernel[None, None].repeat(channels, 1, 1, 1))
        self.stride = stride
    def forward(self, x):
        return F.conv2d(x, self.kernel,
                        stride=self.stride,
                        padding=1,
                        groups=x.shape[1])

Adoption:

  • Antialiased ResNet — accuracy + robustness improve।
  • Detection/Segmentation — bigger gain।
  • Computational cost — minimal।

Other invariance approaches:

  • Data augmentation: random crops train invariance imposed।
  • Group equivariant CNN: rotation/scale invariance built-in।
  • Transformer: patches different invariance properties।

Theoretical foundation:

  • Sampling theorem — band-limited signal proper sampling।
  • CNN-এ filtered বনাম unfiltered downsampling।
  • Classical signal processing wisdom — DL-এ rediscovered।

Modern best practice:

  • Stride-2 conv better than stride-2 pool (learnable filter)।
  • Average pool — implicit smoothing।
  • BlurPool — explicit anti-aliasing।
  • Strong augmentation — data-driven invariance।

মূল উপলব্ধি: "Pooling = translation invariance" — partially true। Aliasing-এর জন্য actual invariance imperfect। BlurPool, stride-conv, augmentation — combined approach। Robustness-এ critical, especially safety-critical applications।

প্র ০২ "Global Average Pooling vs Flatten + FC" — Network-in-Network paper-এর big idea। GAP-এর benefits কী, কোথায় limitation?

GAP — Lin et al. (২০১৪) "Network in Network"। CNN architecture-এ পরিবর্তন।

Traditional CNN head:

  • Conv → ... → Flatten → FC(4096) → FC(4096) → FC(1000)।
  • VGG-16 — শেষের ৩টি FC layer-এ ১২০M parameter (পুরো network-এর ৯০%)।
  • Overfitting risk বিশাল।

GAP head:

  • Conv → ... → GAP → FC(num_classes)।
  • Parameter — শুধু final FC ($C \times \text{classes}$)।
  • Spatial structure সম্পূর্ণ collapse।

Benefits:

  • Massive parameter reduction: 90%+ less।
  • Less overfitting: small data-এ critical।
  • Input size flexibility: different resolution accept।
  • Interpretability: CAM (Class Activation Map) সহজ।

CAM-এর secret sauce:

  • Last conv feature map (before GAP) — semantic spatial info।
  • FC weight × feature map = heatmap of class evidence।
  • "Where is the model looking?" — visualization।

Limitation — spatial info loss:

  • Object position information loss।
  • Detection/segmentation-এ direct GAP কাজে লাগে না।
  • Fine-grained classification — sometimes underperform।

Modern variants:

  • Generalized Mean Pooling (GeM): $\left(\frac{1}{N}\sum x^p\right)^{1/p}$ — learnable $p$।
  • Attention pool: learnable spatial weights।
  • Concat pool: GAP + GMP concatenated।

Adoption history:

  • NIN (২০১৪) introduce।
  • GoogLeNet (২০১৪) — adopted।
  • ResNet (২০১৫) — standard।
  • Modern CNN — universal।

When NOT to use GAP:

  • Object detection — spatial info needed (ROI pool)।
  • Semantic segmentation — pixel-level prediction।
  • Small input — Flatten + FC sometimes better।
  • Fine-grained — bilinear pool variants।

মূল উপলব্ধি: GAP — DL architecture design-এর elegant innovation। Parameter কমে + interpretability + flexibility। Classification-এ universal। কিন্তু spatial task-এ alternative। "Less is more" — DL-এ powerful principle। Modern CNN — GAP without question।

প্র ০৩ Max pooling-এ backprop কীভাবে কাজ করে? শুধু max position-এ gradient — এটা সমস্যা?

Max pool — non-differentiable। কিন্তু DL-এ "subgradient" বা "selector" interpretation works।

Forward:

  • Window-এ max value select।
  • সেই position-এর index store (cache)।

Backward:

  • Output gradient — শুধু max position-এ pass।
  • অন্য position — gradient 0।
  • "Hard selector" — discrete decision।

Implementation:

# PyTorch internal — pseudo
class MaxPoolBackward:
    def forward(self, x):
        self.indices = x.argmax_per_window()
        return x.max_per_window()

    def backward(self, grad_output):
        grad_input = zeros_like(x)
        grad_input.scatter_(self.indices, grad_output)
        return grad_input

Sparse gradient সমস্যা:

  • 2x2 max pool — 75% neuron-এ gradient 0।
  • "Dead" neurons — never update।
  • Non-max neurons learning slow।

Avg pool comparison:

  • Avg — সব 4 neuron-এ equal gradient (1/4)।
  • Smoother backprop।
  • সব neuron train পায়।

Soft alternatives:

  • LogSumExp: $\log \sum e^x$ — smooth max approximation।
  • Soft pool: weighted average — weight = $e^x / \sum e^x$।
  • Stochastic pool: probability proportional to value।

Why max still works:

  • Different windows — different position max।
  • Aggregate — effective gradient flow।
  • Empirical performance — strong।
  • Sparse gradient — implicit regularization।

Implementation detail:

  • Tied gradient between consecutive samples (cudnn cached indices)।
  • Variable window for irregular input।
  • Adaptive max pool — different output sizes।

Theoretical view:

  • Subgradient — non-differentiable function-এর জন্য।
  • Almost everywhere differentiable — measure-zero ties।
  • Gradient method-এ acceptable।

Practical tips:

  • Network depth-এ avg-এর চেয়ে max popular।
  • Dead neuron occasional — large enough capacity।
  • BN + ReLU — overall flow maintain।
  • Average for smoothing, max for detection।

Recent insights:

  • Vision Transformer — pooling avoid (different architecture)।
  • Modern CNN — stride conv replaces max pool।
  • Pure pool architecture rare আজ।

মূল উপলব্ধি: Max pool-এর gradient sparse — surprising kintu effective। Non-differentiable point-এ subgradient ব্যবহার। Soft alternatives exist কিন্তু practical performance gap small। Modern trend — stride conv বা pure attention। Classical max pool — historical importance + simplicity।

প্র ০৪ Bangladesh-এর একটি রোগী X-ray classifier বানাচ্ছেন (TB detection)। Pooling architecture কীভাবে design করবেন? কোথায় aggressive, কোথায় conservative?

Medical imaging — high stakes। Pooling decision careful।

Domain considerations:

  • X-ray: 1024×1024 typical resolution।
  • TB lesion — small spatial features (cm scale)।
  • Class imbalance: TB rare disease (5-10%)।
  • Critical: false negative প্রাণহানি।

Architecture choice:

  • Backbone: ResNet50 / DenseNet121 (medical-pretrained)।
  • CheXNet (Stanford) — DenseNet121 — chest X-ray gold standard।
  • Modern: Vision Transformer fine-tune।

Pooling strategy:

(১) Early — aggressive:

  • Stem: 7×7 conv stride 2 + 3×3 max pool stride 2।
  • 1024×1024 → 256×256 quickly।
  • Compute reduction priority।
  • Low-level features stable।

(২) Mid — conservative:

  • Stride-2 conv between blocks।
  • Max pool avoid (lesion ছোট)।
  • Atrous/dilated conv consider।

(৩) Late — minimal:

  • Final stage — 14×14 spatial keep।
  • No additional pooling।
  • Receptive field already large।

(৪) Head:

  • Global Average Pooling।
  • FC → binary (TB vs no TB)।
  • Dropout 0.3-0.5 (small data)।

Modified for TB-specific:

class TBClassifier(nn.Module):
    def __init__(self, backbone='densenet121'):
        super().__init__()
        self.backbone = timm.create_model(
            backbone,
            pretrained=True,
            features_only=True,
        )
        # Multi-scale feature pyramid
        self.fpn = FeaturePyramidNetwork()
        self.gap = nn.AdaptiveAvgPool2d(1)
        self.head = nn.Sequential(
            nn.Dropout(0.4),
            nn.Linear(256, 2),
        )

Multi-scale strategy:

  • FPN (Feature Pyramid Network) — multi-scale lesions।
  • Different resolution combine।
  • Small + large lesions detect।

Augmentation (Bangladesh climate):

  • Brightness — exposure variation।
  • Contrast — image quality differ।
  • Random crops — orientation।
  • Mixup — class imbalance address।

Validation strategy:

  • Stratified k-fold।
  • Site-wise hold-out (different hospital)।
  • Sensitivity-specificity trade-off।
  • FROC analysis — lesion localization।

Bangla deployment context:

  • Rural clinic — low-resource hardware।
  • Mobile app — quantized model।
  • Offline inference — no cloud।
  • Local language UI — actionable results।

Edge case:

  • X-ray quality variation।
  • Equipment difference (Siemens vs GE)।
  • Chest size variation।
  • Pediatric vs adult।

Regulatory:

  • BMRC approval।
  • Doctor in loop — never autonomous।
  • Confidence calibration critical।
  • Uncertainty quantification।

Resource-constrained tips:

  • Knowledge distillation: ResNet50 → MobileNetV3।
  • Quantization: INT8।
  • ONNX runtime mobile।
  • Edge device benchmark।

মূল উপলব্ধি: Medical imaging-এ pooling design — domain-specific। Aggressive early (compute), conservative mid (lesion), minimal late (receptive field already enough)। GAP standard head + dropout। Multi-scale FPN small lesion-এ। Bangladesh deployment — quantization + offline + Bangla UI। Doctor + AI partnership — patient safety।

অনুশীলন

  1. Output shape: Input $(1, 64, 56, 56)$, MaxPool2d(kernel=2, stride=2) — output shape কত?

    $(1, 64, 28, 28)$. Channel একই থাকে, spatial dim অর্ধেক।

  2. Hand-calc: Input $\begin{bmatrix} 2 & 4 & 1 & 0 \\ 6 & 5 & 3 & 2 \\ 1 & 2 & 7 & 8 \\ 4 & 3 & 9 & 6 \end{bmatrix}$ — Max ও Avg pool 2×2।

    Max: $\begin{bmatrix} 6 & 3 \\ 4 & 9 \end{bmatrix}$।

    Avg: $\begin{bmatrix} 4.25 & 1.5 \\ 2.5 & 7.5 \end{bmatrix}$।

  3. Architecture: $32 \times 32$ CIFAR-10 input থেকে শুরু — ৪টি conv block, প্রতিটির পর MaxPool 2x2, এবং শেষে GAP + FC।
    class CifarCNN(nn.Module):
        def __init__(self):
            super().__init__()
            def block(c_in, c_out):
                return nn.Sequential(
                    nn.Conv2d(c_in, c_out, 3, padding=1),
                    nn.BatchNorm2d(c_out),
                    nn.ReLU(inplace=True),
                    nn.MaxPool2d(2),
                )
            self.features = nn.Sequential(
                block(3, 32),     # 32 -> 16
                block(32, 64),    # 16 -> 8
                block(64, 128),   # 8 -> 4
                block(128, 256),  # 4 -> 2
            )
            self.gap = nn.AdaptiveAvgPool2d(1)
            self.fc  = nn.Linear(256, 10)
        def forward(self, x):
            x = self.features(x)
            x = self.gap(x).flatten(1)
            return self.fc(x)

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

কোড রানার কাজ না করলে? ব্রাউজারে কাজ না করলে Google Colab ব্যবহার করুন — Google-এর ফ্রি অনলাইন Python পরিবেশ, শুধু Gmail অ্যাকাউন্ট লাগে।
পূর্ববর্তী পাঠ
পাঠ ১৭ · Convolution