Machine Learning Academy · leksjon

Pooling-lag: Romlig nedsampling og invarians

Deltakere vil legge til MaxPool2d etter konvolusjonslag, beregne utdataenes dimensjoner og forstå hvordan pooling gir posisjonstoleranse og reduserer beregningene.

Leksjon 2 av 413 trinn

Pooling-lag: Romlig nedsampling og invarians er en gratis leksjon i Machine Learning Academy på CoddyKit. Dette er leksjon 2 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Machine Learning Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Machine Learning Academy inneholder totalt 4 leksjoner.

Hvorfor poolinglag finnes

Poolinglag reduserer de romlige dimensjonene til feature maps. Dermed reduseres beregnings- og minnebruken, samtidig som representasjonene blir mer kompakte. De introduserer også en viss grad av romlig invarians – små forskyvninger av et trekk i inndataene gir samme resultat etter pooling. Uten pooling (eller konvolusjoner med stride 2) ville de romlige dimensjonene vært konstante gjennom alle lagene, noe som ville gjort nettverket uoverkommelig kostbart for store bilder.

import torch
import torch.nn as nn

# Without pooling: feature map grows in channels but not reduced
conv1 = nn.Conv2d(3, 32, 3, padding=1)   # (B, 32, H, W)
conv2 = nn.Conv2d(32, 64, 3, padding=1)  # (B, 64, H, W)

# With pooling: spatial dims are halved each time
pool = nn.MaxPool2d(kernel_size=2, stride=2)

x = torch.randn(4, 3, 32, 32)
out = pool(torch.relu(conv1(x)))
print(out.shape)   # (4, 32, 16, 16) -- halved!

Maks-pooling: Ta maksimumsverdien

MaxPool2d deler feature mapet i inndataene inn i overlappingsfrie vinduer og tar maksimumsverdien i hvert vindu. Maksimumsverdien tilsvarer den sterkeste aktiveringen av filteret på en hvilken som helst posisjon i vinduet – den fanger opp om trekket finnes noe sted i området, uavhengig av den nøyaktige posisjonen. En 2x2-maks-pooling med stride 2 halverer høyden og bredden, slik at den totale romlige størrelsen reduseres 4 ganger.

import torch
import torch.nn as nn

pool = nn.MaxPool2d(kernel_size=2, stride=2)

# Simple 4x4 feature map
x = torch.tensor([[[[ 1., 3., 2., 4.],
                     [ 5., 6., 7., 8.],
                     [ 9., 2., 1., 3.],
                     [ 4., 5., 6., 7.]]]])

out = pool(x)
print(out.shape)   # (1, 1, 2, 2)
print(out)
# Max of top-left 2x2: max(1,3,5,6)=6
# Max of top-right 2x2: max(2,4,7,8)=8
# Max of bottom-left 2x2: max(9,2,4,5)=9
# Max of bottom-right 2x2: max(1,3,6,7)=7

Gjennomsnittspooling kontra maks-pooling

AvgPool2d beregner gjennomsnittet av verdiene i hvert poolingvindu i stedet for maksimumsverdien. Gjennomsnittspooling jevner ut feature mapet og beholder informasjon fra alle aktiveringer, mens maks-pooling forkaster de svakere aktiveringene. Maks-pooling foretrekkes i klassifiseringsnettverk der tilstedeværelsen av et trekk er viktigere enn den gjennomsnittlige styrken. Gjennomsnittspooling brukes ofte til global pooling på slutten av et CNN for å slå sammen de romlige dimensjonene.

import torch
import torch.nn as nn

x = torch.tensor([[[[ 1., 3., 2., 4.],
                     [ 5., 6., 7., 8.],
                     [ 9., 2., 1., 3.],
                     [ 4., 5., 6., 7.]]]])

max_pool = nn.MaxPool2d(2, stride=2)
avg_pool = nn.AvgPool2d(2, stride=2)

print('MaxPool:', max_pool(x))
# tensor([[[[6., 8.], [9., 7.]]]])

print('AvgPool:', avg_pool(x))
# tensor([[[[3.75, 5.25], [5.00, 4.25]]]])

Global gjennomsnittspooling

Global Average Pooling (GAP) slår sammen hele den romlige dimensjonen til én enkelt verdi per kanal ved å beregne gjennomsnittet av alle romlige posisjoner. For et feature map med formen (B, C, H, W) gir GAP formen (B, C) – en vektor med C verdier som representerer gjennomsnittsaktiveringen til hvert filter over hele bildet. GAP erstatter flatten + store fullt tilkoblede lag øverst i klassiske CNN-er (VGG), reduserer antallet parametere betydelig og fungerer som en effektiv regularisator. Det brukes i ResNet, MobileNet og EfficientNet.

import torch
import torch.nn as nn

# Global Average Pooling (AdaptiveAvgPool2d to any output size)
gap = nn.AdaptiveAvgPool2d(output_size=(1, 1))

x = torch.randn(8, 512, 7, 7)   # typical final feature map
out = gap(x)
print(out.shape)   # (8, 512, 1, 1)

# Flatten to (B, C) for the classifier
flat = out.view(out.size(0), -1)
print(flat.shape)  # (8, 512)

# Then: nn.Linear(512, num_classes)

Beregning av utdatadimensjoner

Det er enkelt å beregne de romlige utdatadimensjonene etter pooling: output_size = floor((input_size - kernel_size) / stride) + 1. For en 2x2-pooling med stride 2 på en inndata på 28x28 blir resultatet 14x14. Feilberegning av dimensjoner er en vanlig kilde til formfeil. AdaptiveMaxPool2d og AdaptiveAvgPool2d løser dette ved å ta imot ønsket utdatastørrelse direkte og automatisk beregne nødvendig kernel og stride – ideelt når du ønsker en fast utdata uavhengig av inndatastørrelsen.

import torch
import torch.nn as nn

# Classic fixed-size pooling
x = torch.randn(1, 32, 28, 28)
pool = nn.MaxPool2d(kernel_size=2, stride=2)
print(pool(x).shape)   # (1, 32, 14, 14)

# Adaptive pooling: specify desired output size
adaptive_pool = nn.AdaptiveAvgPool2d((4, 4))
print(adaptive_pool(x).shape)  # (1, 32, 4, 4) -- any input

# Works even with irregular input sizes:
x2 = torch.randn(1, 32, 17, 23)
print(adaptive_pool(x2).shape)  # (1, 32, 4, 4) -- still 4x4!

Pooling for translasjonsinvarians

Pooling gir lokal translasjonsinvarians: Hvis et trekk forskyves noen få piksler, ligger det sannsynligvis fortsatt i samme poolingvindu og gir samme maksimumsaktivering. Det betyr at nettverket gjenkjenner en katt enten den er litt til venstre eller høyre i bildet. Pooling er imidlertid bare lokalt invariant – store forskyvninger gir fortsatt andre resultater. Dataforstørring (tilfeldige utsnitt og speilvendinger) er nødvendig for å oppnå global translasjonsinvarians.

import torch
import torch.nn as nn

pool = nn.MaxPool2d(2, 2)

# Feature map with an 'activated' pixel at position (1,1)
fm1 = torch.zeros(1, 1, 4, 4)
fm1[0, 0, 1, 1] = 1.0   # activation at (1,1)

# Shift by 1 pixel to (1,2)
fm2 = torch.zeros(1, 1, 4, 4)
fm2[0, 0, 1, 2] = 1.0

# Both fall in the same 2x2 window -> same pool output
print(torch.allclose(pool(fm1), pool(fm2)))  # True!

Pooling kontra konvolusjoner med stride 2

Moderne CNN-arkitekturer (ResNet, EfficientNet) bruker i økende grad konvolusjoner med stride 2 i stedet for separate maks-poolinglag. Fordelen er at konvolusjoner med stride 2 er lærbare – nettverket bestemmer selv hvordan nedsamplingen skal utføres, og kan dermed bevare mer nyttig informasjon enn den faste maks-operasjonen. Maks-pooling brukes fortsatt i klassiske arkitekturer (VGGNet, AlexNet) og fordi det er beregningsmessig enkelt. Begge metodene oppnår samme mål: å redusere den romlige oppløsningen med en faktor på 2.

import torch
import torch.nn as nn

# Option 1: Conv + explicit MaxPool
block_maxpool = nn.Sequential(
    nn.Conv2d(32, 64, 3, padding=1),
    nn.ReLU(),
    nn.MaxPool2d(2, 2)      # separate pooling
)

# Option 2: Stride-2 Conv (learnable downsampling)
block_stride = nn.Sequential(
    nn.Conv2d(32, 64, 3, padding=1, stride=2),  # stride=2
    nn.ReLU()
)

x = torch.randn(4, 32, 16, 16)
print(block_maxpool(x).shape)  # (4, 64, 8, 8)
print(block_stride(x).shape)   # (4, 64, 8, 8) -- same!

Pooling har ingen lærbare parametere

En viktig egenskap ved poolinglag er at de ikke har lærbare parametere. Maks-pooling velger ganske enkelt maksimumsverdien, mens gjennomsnittspooling beregner gjennomsnittet. Dette gjør poolinglag svært raske og uten parametere – de bidrar ikke til det totale antallet parametere. Mangelen på parametere betyr også at pooling ikke kan overtilpasse. Derfor er global gjennomsnittspooling på slutten av et nettverk en effektiv regularisator – den tvinger nettverket til å kode informasjon i gjennomsnittet for hver kanal, ikke i romlige posisjoner.

import torch.nn as nn

pool = nn.MaxPool2d(2, 2)
avg_pool = nn.AvgPool2d(2, 2)

# Count parameters
max_params = sum(p.numel() for p in pool.parameters())
avg_params = sum(p.numel() for p in avg_pool.parameters())

print('MaxPool parameters:', max_params)    # 0
print('AvgPool parameters:', avg_params)    # 0

# Compare with a Conv2d layer
conv = nn.Conv2d(32, 32, 2, stride=2)  # similar operation
conv_params = sum(p.numel() for p in conv.parameters())
print('Conv2d (stride-2) parameters:', conv_params)  # 4128

Typisk CNN-arkitektur med pooling

En standard CNN-arkitektur veksler mellom konvolusjonsblokker (Conv+BN+ReLU) og poolinglag, og reduserer gradvis de romlige dimensjonene samtidig som antallet kanaler økes. Etter konvolusjonsblokkene slår global gjennomsnittspooling sammen de romlige dimensjonene, og et lineært lag mapper til klasseskårer. Dette mønsteret gir et nettverk med et håndterbart antall parametere, god generalisering og sterk ytelse i bildeklassifisering.

import torch
import torch.nn as nn

class SimpleCNN(nn.Module):
    def __init__(self, num_classes=10):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 32, 3, padding=1), nn.ReLU(),
            nn.MaxPool2d(2),                # 32->16
            nn.Conv2d(32, 64, 3, padding=1), nn.ReLU(),
            nn.MaxPool2d(2),                # 16->8
            nn.Conv2d(64, 128, 3, padding=1), nn.ReLU(),
        )
        self.pool = nn.AdaptiveAvgPool2d((1, 1))  # GAP
        self.classifier = nn.Linear(128, num_classes)

    def forward(self, x):
        x = self.features(x)
        x = self.pool(x).view(x.size(0), -1)
        return self.classifier(x)

model = SimpleCNN()
x = torch.randn(4, 3, 32, 32)
print(model(x).shape)  # (4, 10)

Fraksjonell og adaptiv pooling

AdaptiveMaxPool2d(output_size) beregner automatisk kernel-størrelsen og stride-verdien som trengs for å produsere de angitte utdatadimensjonene, uavhengig av inndatastørrelsen. Dette er svært nyttig når du ønsker en vektor med fast størrelse for klassifikatoren, men inndatabildene har varierende størrelser (for eksempel ved objektdeteksjon eller datasett med variabel oppløsning). FractionalMaxPool2d bruker randomiserte poolingområder for å tilføre stokastisitet og fungerer som en ekstra regularisator i enkelte arkitekturer.

import torch
import torch.nn as nn

# Adaptive pooling: always produces 3x3 output
adaptive = nn.AdaptiveMaxPool2d((3, 3))

sizes = [(32, 32), (48, 64), (100, 100)]
for h, w in sizes:
    x = torch.randn(2, 64, h, w)
    out = adaptive(x)
    print(f'Input {h}x{w} -> Output {out.shape[2]}x{out.shape[3]}')
# Always 3x3 regardless of input!

# Fractional max pooling (randomised pooling regions)
frac = nn.FractionalMaxPool2d(kernel_size=2, output_size=(6, 6))
x = torch.randn(2, 32, 12, 12)
print('Fractional pool:', frac(x).shape)  # (2, 32, 6, 6)

Visualisering av effekten av pooling

For å forstå hva pooling gjør visuelt: Et feature map før maks-pooling viser filterresponsen på hver piksel. Etter pooling er utdataene grovere romlig – hver verdi representerer den sterkeste responsen i nærområdet. Feature mapet blir mer abstrakt og mindre avhengig av posisjon. Dypt inne i et nettverk, etter mange runder med pooling, dekker hvert nevron sitt reseptive felt det meste av det opprinnelige bildet, noe som muliggjør global mønstergjenkjenning.

import torch
import torch.nn as nn

# Simulate pooling effect on a feature map
pool2 = nn.MaxPool2d(2, 2)
pool4 = nn.MaxPool2d(4, 4)

x = torch.randn(1, 1, 16, 16)
print('Before pooling:', x.shape)        # (1,1,16,16)
print('After 2x2 pool:', pool2(x).shape) # (1,1,8,8)
print('After 4x4 pool:', pool4(x).shape) # (1,1,4,4)

# After 3 rounds of 2x2 pooling on 32x32 input:
pool_3x = nn.Sequential(
    nn.MaxPool2d(2), nn.MaxPool2d(2), nn.MaxPool2d(2)
)
print('After 3x 2x2 pool:', pool_3x(torch.randn(1,1,32,32)).shape)
# (1, 1, 4, 4)

Hurtigsjekk

Test forståelsen din av konsepter innen Machine Learning with Python fra denne leksjonen.

Oppsummering av leksjonen

I denne leksjonen lærte du at: MaxPool2d og AvgPool2d reduserer de romlige dimensjonene ved å ta maksimumsverdien eller gjennomsnittet i hvert poolingvindu, noe som gir kompakte representasjoner og lokal translasjonsinvarians, at AdaptiveAvgPool2d produserer en fast utdatastørrelse uavhengig av inndatadimensjonene (og brukes som global gjennomsnittspooling), og at pooling ikke har lærbare parametere, noe som gjør den rask og immun mot overtilpasning. Neste tema er å bygge og trene et komplett CNN på CIFAR-10.

Gratis å komme i gang

Lær deg Python med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
30
Leksjoner
120

Ofte stilte spørsmål

Er leksjonen «Pooling-lag: Romlig nedsampling og invarians» gratis?

Ja – hele teksten i «Pooling-lag: Romlig nedsampling og invarians» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av Machine Learning Academy-kurset, kan du oppgradere til CoddyKit PRO. Kurset i Machine Learning Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «Pooling-lag: Romlig nedsampling og invarians»?

Deltakere vil legge til MaxPool2d etter konvolusjonslag, beregne utdataenes dimensjoner og forstå hvordan pooling gir posisjonstoleranse og reduserer beregningene. Du øver på Machine Learning Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Machine Learning Academy?

Ingen tidligere erfaring er nødvendig. Machine Learning Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 2 av 4.

Hvor lang tid tar leksjonen «Pooling-lag: Romlig nedsampling og invarians»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Machine Learning Academy-leksjonen?

Ja. Alle Machine Learning Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Konvolusjon og filtre: Oppdage kanter og mønstre
  2. Pooling-lag: Romlig nedsampling og invarians
  3. Bygge og trene et CNN på CIFAR-10
  4. Dataforsterkning: Transformasjoner for robusthet
← Tilbake til Machine Learning Academy