Convolution và pooling

Một bộ dò nét dùng lại trên cả ảnh

Chương 5 · Bài 5.1

2

Nét phấn dịch chỗ

MLP trải cả ảnh thành một vector.

Khi nét đổi vị trí, MLP có thể phải học lại nhiều trọng số.

Ta muốn đọc cửa sổ nhỏ.

Cùng kernel, nhiều vị trí.

3

Mục tiêu

  • Tính convolution bằng tay và so với PyTorch.
  • Tính kích thước đầu ra theo kernel, padding, stride, dilation.
  • Phân biệt convolution và pooling.
  • Tính receptive field của nhiều lớp.
01

Đọc một cửa sổ

Đọc một cửa sổ5

Bốn phép nhân, một kết quả

Nhân từng ô của cửa sổ với kernel rồi cộng: ô đầu ra đầu tiên bằng 3.120013210ảnh X · 3×31002kernel · 2×23821đầu ra · 2×21·1 + 2·0 + 0·0 + 1·2 = 3
Nhân từng ô của cửa sổ với kernel rồi cộng: ô đầu ra đầu tiên bằng 3.
Đọc một cửa sổ6

Không lật kernel

Convolution toán học lật kernel 180∘180^\circ.

Conv2d làm cross-correlation: nhân thẳng.

Kernel học được: lật hay không như nhau.

Kernel đề cho sẵn: tính không lật.

Đọc một cửa sổ7

Hỏi nhanh

Hàng ảnh [0, 0, 5, 5] có một cạnh chuyển từ tối sang sáng. Kernel [-1, 1], stride 1, không padding. Conv2d của PyTorch cho ra kết quả nào?

Đọc một cửa sổ8

Hỏi nhanh

Ảnh xám 64×6464\times 64, kernel 3×33\times 3, không padding, stride 1, đầu ra 62×6262\times 62. Nếu mỗi vị trí đầu ra có một kernel riêng thay vì dùng chung, lớp này cần bao nhiêu trọng số, bỏ qua bias?

Phỏng theoCMU 10-601 · Exam 3 Practice · Q1.4(a)

Đọc một cửa sổ9

Ô đầu của feature map

Ô trên trái là 1×1+2×0+0×0+1×2=31\times1+2\times0+0\times0+1\times2=3.

Ô bên phải là 2×1+0×0+1×0+3×2=82\times1+0\times0+1\times0+3\times2=8.

Đọc một cửa sổ10

Code đọc từng cửa sổ

Chỉ đọc cửa sổ nằm trọn trong ảnh, không lật kernel, như Conv2d.

def correlate_valid(image, kernel):
    height, width = image.shape
    kh, kw = kernel.shape
    result = np.zeros((height - kh + 1, width - kw + 1))
    for row in range(result.shape[0]):
        for col in range(result.shape[1]):
            window = image[row:row + kh, col:col + kw]
            result[row, col] = np.sum(window * kernel)
    return result
Đọc một cửa sổ11

Kết quả để đối chiếu

feature_map = correlate_valid(image, kernel)
print(feature_map)
Kết quả[[3. 8.]
 [2. 1.]]

Ô 8 lớn nhất: cửa sổ ấy có số lớn trên đường chéo, đúng chỗ kernel có trọng số dương.

Đọc một cửa sổ12

Một kernel quét cả ảnh

Kernel nét dọc trượt qua ảnh tháp; mỗi vị trí cho một ô của bảng đầu ra.
Đọc một cửa sổ13

Kernel dò nét trên ảnh thật

Kernel nét dọc làm sáng cạnh đứng của tháp; kernel ngang làm sáng mái và lan can.ảnh xámkernel nét dọckernel nét ngang−101−202−101−1−2−1000121cùng 9 trọng số trượt qua mọi vị trí
Kernel nét dọc làm sáng cạnh đứng của tháp; kernel ngang làm sáng mái và lan can.
Đọc một cửa sổ14

Hỏi nhanh

Ta muốn một kernel 2×22\times 2 cho số dương với cửa sổ nét chéo [1001]\begin{bmatrix}1&0\\0&1\end{bmatrix}, nhưng cho 0 với mọi vùng nền đều màu [cccc]\begin{bmatrix}c&c\\c&c\end{bmatrix}. Chọn kernel nào?

02

Đọc thưa, đọc sát biên

Đọc thưa, đọc sát biên16

Ba cách đổi cửa sổ

Padding thêm vành quanh ảnh.

Stride quyết định mỗi lần dịch bao nhiêu ô.

Dilation giãn vị trí kernel.

Kernel 3, dilation 2 phủ 5 ô.

Đọc thưa, đọc sát biên17

Kích thước một chiều

Hout=⌊H+2p−d(k−1)−1s⌋+1H_{\mathrm{out}}=\left\lfloor\frac{H+2p-d(k-1)-1}{s}\right\rfloor+1

pp là padding, ss là stride, còn dd là dilation.

Giữ kích thước khi s=1s=1: 2p=d(k−1)2p=d(k-1).

Đọc thưa, đọc sát biên18

Ví dụ kích thước

H=7, k=3, p=1, s=2, d=1H=7,\ k=3,\ p=1,\ s=2,\ d=1 Hout=⌊7+2−(3−1)−12⌋+1=⌊6/2⌋+1=4.H_{\mathrm{out}}=\left\lfloor\frac{7+2-(3-1)-1}{2}\right\rfloor+1=\lfloor6/2\rfloor+1=4.
Đọc thưa, đọc sát biên19

Padding, stride, dilation trên một chiều

Padding thêm vành 0, stride dịch nhiều ô, dilation đọc cách ô.p = 1, s = 20123456vànhvành4 ôd = 20123456phủ 5 ô, 3 trọng số3 ô
Padding thêm vành 0, stride dịch nhiều ô, dilation đọc cách ô.
Đọc thưa, đọc sát biên20

Hỏi nhanh

Ảnh dài 8 ô, kernel 3, padding 0, stride 2, dilation 1. Đầu ra dài bao nhiêu?

Đọc thưa, đọc sát biên21

Hỏi nhanh

Conv có kernel 3, dilation 2, stride 1. Padding đối xứng bằng bao nhiêu thì giữ nguyên chiều dài đầu vào?

Đọc thưa, đọc sát biên22

Nhiều kênh

Input có shape (N,Cin,H,W)(N,C_{in},H,W).

Một kernel tạo một kênh đầu ra và đọc toàn bộ CinC_{in} kênh đầu vào.

RGB, 8 kênh đầu ra, kernel 3×33\times3:

8(3⋅9+1)=2248(3\cdot9+1)=224 tham số.

Số tham số không phụ thuộc H,WH,W của ảnh.

Đọc thưa, đọc sát biên23

Đếm bằng thư viện

rgb_layer = nn.Conv2d(3, 8, 3, padding=1)
print(sum(parameter.numel() for parameter in rgb_layer.parameters()))
print(tuple(rgb_layer(torch.zeros(2, 3, 7, 7)).shape))
Kết quả224
(2, 8, 7, 7)

Padding 1 giữ 7×77\times7; hai ảnh, tám kênh.

Đọc thưa, đọc sát biên24

Một kernel đọc mọi kênh vào

Mỗi kernel đọc cả ba kênh RGB; tám kernel cho tám kênh ra, 224 tham số.(3, 7, 7)ảnh RGB(3, 3, 3)một kernel+ b(7, 7)một kênh ra× 8(8, 7, 7)tám kênh ra8 · (3·9 + 1) = 224 tham số
Mỗi kernel đọc cả ba kênh RGB; tám kernel cho tám kênh ra, 224 tham số.
Đọc thưa, đọc sát biên25

Hỏi nhanh

nn.Conv2d(16, 32, kernel_size=(3, 5)) có weight.shape là gì?

Đọc thưa, đọc sát biên26

Hỏi nhanh

Lớp nn.Conv2d(3, 8, 3, padding=1) chạy trên ảnh 7×77\times 7, rồi chạy trên ảnh 70×7070\times 70. Số tham số và số phép nhân thay đổi thế nào?

03

Tổng hợp và vùng nhìn

Tổng hợp và vùng nhìn28

Pooling trên bảng kết quả

[3821]\begin{bmatrix}3&8\\2&1\end{bmatrix}

Max pooling trả về 8. Average pooling trả về 3.5.

Không có trọng số học. Global pooling: cả map còn một số.

Tổng hợp và vùng nhìn29

Ba lần max pool trên ảnh thật

Ba lần max pool 2×2: từ 128×128 xuống 16×16, nét còn nhưng chi tiết mất dần.128×12864×6432×3216×16mỗi lần max pool 2×2 giữ một số trong bốn: nét còn, vị trí chính xác mất dần
Ba lần max pool 2×2: từ 128×128 xuống 16×16, nét còn nhưng chi tiết mất dần.
Tổng hợp và vùng nhìn30

Pooling dịch vị trí

Đỉnh dịch một ô qua ranh giới cửa sổ thì đầu ra max pool đổi từ (4, 0) thành (0, 4).ban đầu040040dịch một ô004004đỉnh qua ranh giới cửa sổ: kết quả đổi chỗ, pooling không bất biến dịch tuyệt đối
Đỉnh dịch một ô qua ranh giới cửa sổ thì đầu ra max pool đổi từ (4, 0) thành (0, 4).
Tổng hợp và vùng nhìn31

Dịch đỉnh qua ranh giới cửa sổ

left = torch.tensor([[[[0., 4., 0., 0.]]]])
right = torch.tensor([[[[0., 0., 4., 0.]]]])
pool = nn.MaxPool2d((1, 2))
print(pool(left).flatten().tolist())
print(pool(right).flatten().tolist())
Kết quả[4.0, 0.0]
[0.0, 4.0]

Đỉnh 4 dịch sang cửa sổ thứ hai: pooling không bất biến dịch tuyệt đối.

Tổng hợp và vùng nhìn32

Hỏi nhanh

Max pooling trên một cửa sổ 2×22\times 2 chỉ trả về số 8. Từ đó có khôi phục duy nhất được bốn ô ban đầu không?

Tổng hợp và vùng nhìn33

Gradient qua max pooling

window = y.detach().clone().requires_grad_(True)
nn.MaxPool2d(2)(window).sum().backward()
print(window.grad[0, 0].tolist())
Kết quả[[0.0, 1.0], [0.0, 0.0]]

Max: chỉ ô thắng nhận gradient. Average: chia đều 1/41/4.

Tổng hợp và vùng nhìn34

Hỏi nhanh

MaxPool1d(kernel_size=2, stride=1) trên [1, 9, 3] cho ra [9, 9]. Lấy tổng đầu ra rồi gọi backward(). Gradient về đầu vào là gì?

Tổng hợp và vùng nhìn35

Hai lớp nhìn 5 ô

Hai lớp kernel 3: mỗi ô ở lớp 2 nhìn được 5 ô đầu vào.lớp 2lớp 1đầu vàor = 1 + 2 + 2 = 5
Hai lớp kernel 3: mỗi ô ở lớp 2 nhìn được 5 ô đầu vào.
Tổng hợp và vùng nhìn36

Theo dõi hai số

rr: số ô input trong vùng nhìn; jj: khoảng cách trên input giữa hai vị trí output kề nhau. Bắt đầu r=j=1r=j=1.

rmới=r+(k−1)d j,jmới=s jr_{\mathrm{mới}}=r+(k-1)d\,j,\qquad j_{\mathrm{mới}}=s\,j

Conv 3, stride 1: r=3r=3, j=1j=1. Pool 2, stride 2: r=4r=4, j=2j=2. Conv 3, stride 1: r=8r=8, j=2j=2; cả ba lớp dùng dilation 1.

Tổng hợp và vùng nhìn37

Xếp chồng 3×33\times3

Hai lớp 3×33\times3 nhìn 5×55\times5.

18 trọng số thay vì 25 mỗi cặp kênh, thêm một activation.

Lớp nông: map lớn, cạnh, nét.

Lớp sâu: map nhỏ, thô, tổ hợp lớn.

Tổng hợp và vùng nhìn38

Hỏi nhanh

Một conv kernel 3 stride 2, tiếp theo là một conv kernel 3 stride 1, cả hai có dilation 1. Một ô ở lớp thứ hai nhìn bao nhiêu ô đầu vào theo mỗi chiều?

04

Giả định

Giả định40

Dịch thì theo, xoay thì không

Conv stride 1: ảnh dịch, map dịch theo (đồng biến).

Bất biến chỉ có sau phép gộp vị trí.

Không tự bất biến với xoay, tỉ lệ, ánh sáng.

Đề cần tọa độ: đừng gộp hết vị trí.

Giả định41

Ảnh dịch, feature map dịch theo

Dịch ảnh 20 px thì feature map dịch theo đúng 20 px.ảnhdịch phải 20 pxđầu vàofeature map nét dọccạnh mạnh nhấtdịch đúng 20 px
Dịch ảnh 20 px thì feature map dịch theo đúng 20 px.
Giả định42

Hỏi nhanh

Một bộ dò nét đã học tốt trên những ảnh chỉ có nét dựng đứng, ở đủ mọi vị trí. Ảnh test bị xoay 90∘90^\circ nên nét thành nằm ngang. Nhiều khả năng chuyện gì xảy ra?

Giả định43

Khi đi thi

  • Batch giả, in shape từng khối.
  • (H, W, C) → permute, kiểm một pixel.
  • Cần tọa độ, mask: giữ chiều không gian.
05

Luyện đề

Luyện đề45

Luyện đề 1

Một đội thay một conv 7×77\times 7, 64 kênh vào, 64 kênh ra, bằng ba conv 3×33\times 3 từ 64 sang 64 nối tiếp, stride 1, có ReLU ở giữa. So với lớp cũ, bỏ qua bias, điều nào đúng?

Phỏng theoNOAI 2026 Singapore · Vòng sơ loại · Câu 215

Luyện đề46

Luyện đề 2

Một conv 1D có một bộ lọc, và zjz_j lớn nghĩa là có mẫu cần tìm ở vị trí jj. Mạng A cho ra σ(max⁡jzj)\sigma(\max_j z_j), mạng B cho ra σ(min⁡jzj)\sigma(\min_j z_j). Đầu ra cao nghĩa là “mẫu có ở mọi vị trí” ở mạng nào, và nghĩa là “mẫu có ở ít nhất một vị trí” ở mạng nào?

Phỏng theoMIT 6.036 · Final, Spring 2022 · Q8

Luyện đề47

Luyện đề 3

Mỗi mẫu là một tensor (64, 64, 2): vector lực (Fx,Fy)(F_x, F_y) đo trên lưới 64×6464\times 64. Phải dự đoán tọa độ nguồn (xs,ys)(x_s, y_s) cùng 100 giá trị cường độ. Một đội dùng các conv stride 1, rồi global average pooling, rồi Linear ra 102 số, và lỗi tọa độ rất cao. Thay đổi nào nhắm đúng nguyên nhân?

Phỏng theoUSA-NA-AIO 2026 · Vòng 2 · Ngày 1 · Problem 2

Luyện đề48

Chốt ý

Cửa sổ nhỏ và trọng số chung giúp dò cùng một tín hiệu ở nhiều vị trí.

Pooling lấy biểu diễn gọn hơn nhưng làm mất một phần chi tiết vị trí.

Luyện đề49

Sau giờ học

Làm 10 câu kiểm tra cuối bài trên trang bài học.

Chạy notebook; đổi stride và padding, đoán trước shape đầu ra rồi kiểm lại.