CNN을 이루는 네 가지 연산
CNN은 Convolution(합성곱)으로 특징을 뽑고, Padding으로 출력 크기를 보정하고, Pooling으로 크기를 줄이고, 마지막에 Softmax로 분류 확률을 만든다. 하나씩 숫자로 직접 계산해본다.
Convolution: 필터가 이미지를 훑으며 특징을 뽑는다
합성곱(Convolution)은 작은 필터(Kernel)를 입력 위에서 일정 간격(Stride)만큼 움직이며, 겹치는 영역끼리 곱해서 더하는 연산이다. 4×4 입력에 3×3 필터를 stride 1로 적용하면 2×2 출력이 나온다.
import numpy as np
x = np.array([
[1, 2, 3, 0],
[0, 1, 2, 3],
[3, 0, 1, 2],
[2, 3, 0, 1],
])
kernel = np.array([
[2, 0, 1],
[0, 1, 2],
[1, 0, 2],
])
out = np.zeros((2, 2))
for i in range(2):
for j in range(2):
patch = x[i:i + 3, j:j + 3] # 필터 크기만큼 잘라낸 영역
out[i, j] = np.sum(patch * kernel) # 원소별 곱의 합
print(out)
[[15. 16.]
[ 6. 15.]]
출력 크기는 (입력 크기 − 필터 크기) / Stride + 1 이다. 여기서는 (4 − 3) / 1 + 1 = 2이므로 2×2 출력이 나왔다. 이 출력을 Feature Map이라 부르며, 필터가 “어떤 패턴”을 뽑아내는지에 따라 값이 달라진다.
Padding: 출력이 계속 작아지는 것을 막는다
합성곱을 반복하면 출력 크기가 계속 줄어든다. 입력 가장자리에 값(대개 0)을 채워 이 축소를 보정하는 것이 Padding이다. 4×4 입력 가장자리에 0을 한 겹 두르면(Zero-padding 1) 6×6이 되고, 같은 3×3 필터·stride 1을 적용해도 출력이 (6 − 3) / 1 + 1 = 4, 즉 원래 입력과 같은 4×4 크기로 유지된다.
x_padded = np.pad(x, pad_width=1, mode="constant", constant_values=0)
print(x_padded.shape) # (6, 6)
out_size = (x_padded.shape[0] - kernel.shape[0]) // 1 + 1
print(out_size) # 4
(6, 6)
4
Pooling: 대표값만 남겨 크기를 줄인다
Pooling은 일정 영역에서 대표값 하나만 남겨 이미지(Feature Map)를 축소한다. 가장 많이 쓰이는 것이 영역 안 최댓값을 남기는 Max Pooling이고, 학습되는 파라미터가 없다는 특징이 있다.
x2 = np.array([
[1, 3, 2, 4],
[5, 6, 1, 2],
[3, 2, 8, 7],
[1, 0, 4, 5],
])
pooled = np.zeros((2, 2))
for i in range(2):
for j in range(2):
block = x2[i * 2:i * 2 + 2, j * 2:j * 2 + 2] # 2x2 영역
pooled[i, j] = block.max()
print(pooled)
[[6. 4.]
[3. 8.]]
4×4 이미지가 2×2로 줄었지만, 각 영역에서 가장 두드러진 값(최댓값)은 그대로 유지된다. 이렇게 Convolution과 Pooling을 여러 번 반복하며 이미지를 작고 의미 있는 특징으로 압축해간다.
Flatten과 Softmax: 마지막엔 분류 확률로
여러 번 Convolution·Pooling을 거친 특징 맵은 Flatten Layer에서 1차원으로 펼쳐진 뒤, 완전연결층(Dense Layer)을 지나 클래스 개수만큼의 값(로짓, logit)을 출력한다. 이 로짓을 “각 클래스일 확률”로 바꿔주는 것이 Softmax다.
logits = np.array([2.0, 1.0, 0.1]) # 클래스 3개에 대한 점수
exp = np.exp(logits - logits.max()) # 오버플로 방지를 위해 최댓값을 뺀다
probs = exp / exp.sum()
print(np.round(probs, 3))
print(round(probs.sum(), 3))
[0.659 0.242 0.099]
1.0
가장 높은 로짓(2.0)을 가진 클래스가 가장 높은 확률(65.9%)로 변환됐고, 세 확률의 합은 정확히 1이 된다. 이 확률에서 가장 큰 값을 가진 클래스를 최종 예측으로 선택한다.
정리
CNN은 Convolution으로 필터가 훑으며 특징을 뽑고, Padding으로 출력 크기를 보정하고, Pooling으로 크기를 줄이며 중요한 특징만 남기고, 마지막에 Softmax로 분류 확률을 만드는 흐름으로 동작한다. 이 중 학습되는 파라미터를 가진 것은 Convolution 필터뿐이고, Pooling은 파라미터가 없다는 점도 기억해둘 만하다.
참고 자료
Convolution arithmetic — vdumoulin (GitHub) — Stride·Padding에 따른 합성곱 출력 크기 시각화 (2026-08-07 확인)
torch.nn.Conv2d — PyTorch 공식 문서 — Convolution 레이어의 공식 API와 출력 크기 계산식 (2026-08-07 확인)
댓글 0
댓글을 불러오는 중…