ชั้นการพูล: การลดขนาดเชิงพื้นที่และความไม่แปรผัน
ผู้เรียนจะเพิ่ม MaxPool2d หลังชั้น卷积 คำนวณมิติผลลัพธ์ และทำความเข้าใจว่าการพูลช่วยให้ตำแหน่งคลาดเคลื่อนได้พร้อมลดการคำนวณอย่างไร
ชั้นการพูล: การลดขนาดเชิงพื้นที่และความไม่แปรผัน เป็นบทเรียน Machine Learning Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Machine Learning Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Machine Learning Academy มีบทเรียนทั้งหมด 4 บทเรียน
เหตุผลที่มีเลเยอร์พูลลิง
เลเยอร์พูลลิง จะลดมิติเชิงพื้นที่ของแผนที่คุณลักษณะ ช่วยลดการคำนวณและการใช้หน่วยความจำ พร้อมทำให้ตัวแทนข้อมูลกระชับขึ้น นอกจากนี้ยังทำให้เกิด ความไม่แปรผันเชิงพื้นที่ ในระดับหนึ่ง กล่าวคือ การเลื่อนตำแหน่งเล็กน้อยของคุณลักษณะในอินพุตจะให้เอาต์พุตหลังพูลลิงเหมือนเดิม หากไม่มีพูลลิง (หรือคอนโวลูชันที่มี stride เท่ากับ 2) มิติเชิงพื้นที่จะคงที่ตลอดทุกเลเยอร์ ทำให้เครือข่ายต้องใช้การคำนวณสูงเกินไปสำหรับภาพขนาดใหญ่
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!Max Pooling: เลือกค่าสูงสุด
MaxPool2d จะแบ่งแผนที่คุณลักษณะอินพุตออกเป็นหน้าต่างที่ไม่ซ้อนทับกัน แล้วเลือกค่าสูงสุดในแต่ละหน้าต่าง ค่าสูงสุดสอดคล้องกับการกระตุ้นที่แรงที่สุดของตัวกรองในตำแหน่งใดก็ตามภายในหน้าต่างนั้น จึงบอกได้ว่าคุณลักษณะปรากฏอยู่ที่ใดที่หนึ่งในบริเวณนั้นหรือไม่ โดยไม่ขึ้นกับตำแหน่งที่แน่นอน Max pool ขนาด 2x2 ที่มี stride เท่ากับ 2 จะลดความสูงและความกว้างลงครึ่งหนึ่ง ทำให้ขนาดเชิงพื้นที่ทั้งหมดลดลง 4 เท่า
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)=7Average Pooling เทียบกับ Max Pooling
AvgPool2d จะคำนวณค่าเฉลี่ยของค่าในแต่ละหน้าต่างพูลลิงแทนการเลือกค่าสูงสุด Average pooling จะทำให้แผนที่คุณลักษณะเรียบขึ้นและเก็บข้อมูลจากการกระตุ้นทั้งหมดไว้ ขณะที่ max pooling จะทิ้งการกระตุ้นที่อ่อนกว่า โดยทั่วไปเครือข่ายสำหรับการจำแนกประเภทจะเลือกใช้ max pooling เมื่อ การปรากฏอยู่ ของคุณลักษณะสำคัญกว่าความแรงโดยเฉลี่ย ส่วน average pooling มักใช้สำหรับ global pooling ที่ส่วนท้ายของ CNN เพื่อยุบมิติเชิงพื้นที่
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 Average Pooling
Global Average Pooling (GAP) จะยุบมิติเชิงพื้นที่ทั้งหมดให้เหลือค่าเดียวต่อช่องสัญญาณ โดยหาค่าเฉลี่ยจากทุกตำแหน่งเชิงพื้นที่ หากแผนที่คุณลักษณะมีรูปร่าง (B, C, H, W), GAP จะให้ผลลัพธ์เป็น (B, C) ซึ่งเป็นเวกเตอร์ที่มีค่า C ค่า แทนค่าการกระตุ้นเฉลี่ยของตัวกรองแต่ละตัวทั่วทั้งภาพ GAP จะเข้ามาแทนที่การแปลงเป็นเวกเตอร์แบนและชั้นเชื่อมต่อเต็มรูปแบบขนาดใหญ่ด้านบนของ CNN แบบดั้งเดิม (VGG) ช่วยลดจำนวนพารามิเตอร์ลงอย่างมากและทำหน้าที่เป็นตัวทำให้เป็นระเบียบที่มีประสิทธิภาพ โดยใช้ใน ResNet, MobileNet และ 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)การคำนวณมิติเอาต์พุต
การคำนวณมิติเชิงพื้นที่ของเอาต์พุตหลังพูลลิงทำได้ตรงไปตรงมา: output_size = floor((input_size - kernel_size) / stride) + 1 สำหรับพูลขนาด 2x2 ที่มี stride เท่ากับ 2 และอินพุตขนาด 28x28 เอาต์พุตจะมีขนาด 14x14 การคำนวณมิติผิดเป็นสาเหตุที่พบบ่อยของข้อผิดพลาดด้านรูปร่าง AdaptiveMaxPool2d และ AdaptiveAvgPool2d ช่วยแก้ปัญหานี้โดยรับขนาดเอาต์พุตที่ต้องการโดยตรง แล้วคำนวณ kernel และ stride ที่จำเป็นโดยอัตโนมัติ เหมาะอย่างยิ่งเมื่อต้องการเอาต์พุตขนาดคงที่ไม่ว่าอินพุตจะมีขนาดเท่าใด
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!พูลลิงเพื่อความไม่แปรผันต่อการเลื่อนตำแหน่ง
พูลลิงทำให้เกิด ความไม่แปรผันต่อการเลื่อนตำแหน่งในระดับท้องถิ่น กล่าวคือ หากคุณลักษณะเลื่อนไปไม่กี่พิกเซล ก็ยังมีแนวโน้มว่าจะอยู่ในหน้าต่างพูลลิงเดิมและให้ค่าการกระตุ้นสูงสุดเท่าเดิม ดังนั้นเครือข่ายจึงจำแนกแมวได้ไม่ว่ามันจะอยู่ค่อนไปทางซ้ายหรือขวาเล็กน้อยในภาพ อย่างไรก็ตาม พูลลิงมีความไม่แปรผันเพียงในระดับท้องถิ่นเท่านั้น การเลื่อนตำแหน่งเป็นระยะไกลยังคงให้เอาต์พุตที่แตกต่างกัน จึงต้องใช้การเพิ่มข้อมูล (การครอปแบบสุ่มและการพลิกภาพ) เพื่อให้เกิดความไม่แปรผันต่อการเลื่อนตำแหน่งในระดับทั่วทั้งภาพ
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!พูลลิงเทียบกับคอนโวลูชันที่มี Stride เท่ากับ 2
สถาปัตยกรรม CNN สมัยใหม่ (ResNet, EfficientNet) หันมาใช้ คอนโวลูชันที่มี stride เท่ากับ 2 แทนการใช้เลเยอร์ max pooling แยกต่างหากมากขึ้น ข้อดีคือคอนโวลูชันที่มี stride เท่ากับ 2 สามารถ เรียนรู้ได้ เครือข่ายจึงเป็นผู้กำหนดวิธีลดขนาด และอาจรักษาข้อมูลที่มีประโยชน์ไว้ได้มากกว่าการดำเนินการ max แบบตายตัว Max pooling ยังคงใช้ในสถาปัตยกรรมแบบดั้งเดิม (VGGNet, AlexNet) และมีข้อดีด้านความเรียบง่ายในการคำนวณ ทั้งสองวิธีมีเป้าหมายเดียวกัน คือ ลดความละเอียดเชิงพื้นที่ลง 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!พูลลิงไม่มีพารามิเตอร์ที่เรียนรู้ได้
คุณสมบัติสำคัญของเลเยอร์พูลลิงคือ ไม่มีพารามิเตอร์ที่เรียนรู้ได้ Max pooling เพียงเลือกค่าสูงสุด ส่วน average pooling จะคำนวณค่าเฉลี่ย ทำให้เลเยอร์พูลลิงทำงานได้รวดเร็วมากและไม่ต้องใช้หน่วยความจำสำหรับพารามิเตอร์ โดยไม่เพิ่มจำนวนพารามิเตอร์ของโมเดล การไม่มีพารามิเตอร์ยังหมายความว่าพูลลิงไม่สามารถทำให้เกิดการเรียนรู้เกินได้ นี่คือเหตุผลที่ global average pooling ตอนท้ายของเครือข่ายเป็นตัวทำให้เป็นระเบียบที่มีประสิทธิภาพ เพราะบังคับให้เครือข่ายเข้ารหัสข้อมูลไว้ในค่าเฉลี่ยของแต่ละช่องสัญญาณ ไม่ใช่ในตำแหน่งเชิงพื้นที่
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สถาปัตยกรรม CNN ทั่วไปที่มีพูลลิง
สถาปัตยกรรม CNN มาตรฐานจะสลับระหว่างบล็อกคอนโวลูชัน (Conv+BN+ReLU) และเลเยอร์พูลลิง โดยค่อย ๆ ลดมิติเชิงพื้นที่พร้อมเพิ่มจำนวนช่องสัญญาณ หลังบล็อกคอนโวลูชัน global average pooling จะยุบมิติเชิงพื้นที่ จากนั้นเลเยอร์เชิงเส้นจะแปลงผลลัพธ์เป็นคะแนนของแต่ละคลาส รูปแบบนี้ทำให้ได้เครือข่ายที่มีจำนวนพารามิเตอร์จัดการได้ง่าย มีการทำให้เป็นนัยทั่วไปที่ดี และจำแนกภาพได้อย่างมีประสิทธิภาพ
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)Fractional และ Adaptive Pooling
AdaptiveMaxPool2d(output_size) จะคำนวณขนาด kernel และ stride ที่จำเป็นโดยอัตโนมัติ เพื่อให้ได้มิติเอาต์พุตตามที่ระบุ ไม่ว่าอินพุตจะมีขนาดเท่าใดก็ตาม คุณสมบัตินี้มีประโยชน์อย่างยิ่งเมื่อต้องการเวกเตอร์ขนาดคงที่สำหรับตัวจำแนก แต่ภาพอินพุตมีขนาดแตกต่างกัน (เช่น การตรวจจับวัตถุและชุดข้อมูลที่มีความละเอียดหลากหลาย) FractionalMaxPool2d ใช้บริเวณพูลลิงแบบสุ่มเพื่อเพิ่มความสุ่ม ทำหน้าที่เป็นตัวทำให้เป็นระเบียบเพิ่มเติมในสถาปัตยกรรมบางประเภท
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)การแสดงผลลัพธ์ของพูลลิง
เพื่อทำความเข้าใจผลของพูลลิงในเชิงภาพ: แผนที่คุณลักษณะก่อน max pooling จะแสดงการตอบสนองดิบของตัวกรองที่แต่ละพิกเซล หลังพูลลิง เอาต์พุตจะมีความละเอียดเชิงพื้นที่หยาบขึ้น โดยแต่ละค่าจะแทนการตอบสนองที่แรงที่สุดในบริเวณใกล้เคียง แผนที่คุณลักษณะจะมีความเป็นนามธรรมมากขึ้นและไม่ขึ้นกับตำแหน่งมากนัก เมื่ออยู่ในส่วนลึกของเครือข่าย หลังผ่านพูลลิงหลายรอบ ขอบเขตการรับข้อมูลของเซลล์ประสาทแต่ละตัวจะครอบคลุมภาพต้นฉบับเกือบทั้งหมด ทำให้สามารถจดจำรูปแบบในภาพโดยรวมได้
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)ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจแนวคิด Machine Learning ด้วย Python จากบทเรียนนี้
สรุปบทเรียน
ในบทเรียนนี้ คุณได้เรียนรู้ว่า MaxPool2d และ AvgPool2d ลดมิติเชิงพื้นที่ด้วยการเลือกค่าสูงสุดหรือค่าเฉลี่ยในแต่ละหน้าต่างพูลลิง ทำให้ได้ตัวแทนข้อมูลที่กระชับและมีความไม่แปรผันต่อการเลื่อนตำแหน่งในระดับท้องถิ่น AdaptiveAvgPool2d สร้างเอาต์พุตขนาดคงที่ไม่ว่ามิติของอินพุตจะเป็นเท่าใด (ใช้เป็น Global Average Pooling) และ พูลลิงไม่มีพารามิเตอร์ที่เรียนรู้ได้ จึงทำงานได้รวดเร็วและไม่ทำให้เกิดการเรียนรู้เกิน บทถัดไปเราจะสร้างและฝึก CNN แบบสมบูรณ์บน CIFAR-10
เรียนรู้ Python ด้วย AI tutor — ฟรี
เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป
- คอร์ส
- 30
- บทเรียน
- 120
คำถามที่พบบ่อย
บทเรียน “ชั้นการพูล: การลดขนาดเชิงพื้นที่และความไม่แปรผัน” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ชั้นการพูล: การลดขนาดเชิงพื้นที่และความไม่แปรผัน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Machine Learning Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Machine Learning Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ชั้นการพูล: การลดขนาดเชิงพื้นที่และความไม่แปรผัน”
ผู้เรียนจะเพิ่ม MaxPool2d หลังชั้น卷积 คำนวณมิติผลลัพธ์ และทำความเข้าใจว่าการพูลช่วยให้ตำแหน่งคลาดเคลื่อนได้พร้อมลดการคำนวณอย่างไร คุณปฏิบัติ Machine Learning Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Machine Learning Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Machine Learning Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “ชั้นการพูล: การลดขนาดเชิงพื้นที่และความไม่แปรผัน” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Machine Learning Academy นี้ได้ไหม
ได้ บทเรียน Machine Learning Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การ卷积และตัวกรอง: การตรวจจับขอบและรูปแบบ
- ชั้นการพูล: การลดขนาดเชิงพื้นที่และความไม่แปรผัน
- การสร้างและฝึก CNN บน CIFAR-10
- การเพิ่มข้อมูล: การแปลงเพื่อความทนทาน