학습 자료 글/딥러닝과 llm

[밑바닥부터 시작하는 딥러닝 1] 08. ReLU, Sigmoid, Affine 계층은 무엇을 기억하고 되돌려주는가

cedis 2026. 5. 26. 00:36

밑바닥부터 시작하는 딥러닝 1 - 계층 구현과 기울기 확인

계산 그래프의 덧셈과 곱셈 규칙을 이해했다면, 이제 신경망의 실제 계층을 같은 관점으로 볼 수 있다.

각 계층은 순전파 때 필요한 값을 저장하고, 역전파 때 상류에서 온 미분값을 자기 규칙에 맞게 변환해 하류로 보낸다.

이번 글에서 잡을 것

  • ReLU는 입력이 0 이하였던 위치를 mask로 기억한다.
  • Sigmoid는 순전파 출력값을 기억한다.
  • Affine은 행렬 곱과 편향 더하기를 담당한다.
  • Softmax-with-Loss는 출력층과 손실 계산을 함께 묶는다.
  • 기울기 확인은 수치 미분과 역전파 결과를 비교해 구현 오류를 찾는 방법이다.

계층을 보는 기준

forward
값 계산
저장
x/out/mask
backward
미분 전달

계층 구현을 읽을 때는 코드 줄을 모두 외우기보다, 이 계층이 순전파 때 무엇을 저장하고 역전파 때 어떤 규칙으로 미분값을 보내는지 보면 된다.

ReLU와 Sigmoid

계층 순전파 저장하는 값 역전파
ReLU 0 이하는 0, 양수는 그대로 0 이하였던 위치(mask) 막혔던 위치의 미분을 0으로 차단
Sigmoid S자 함수로 변환 출력값 out out을 이용해 미분값 계산

ReLU의 역전파 기준

순전파 때 0 이하라서 신호가 막힌 위치는 역전파 때도 기울기를 흘리지 않는다.

Affine 계층

Affine 계층은 신경망에서 자주 보던 `XW+B` 계산을 담당한다. 입력 X와 가중치 W의 행렬 곱을 만들고 편향 B를 더한다. 역전파에서는 X, W, B 각각에 대한 기울기를 구해야 한다.

# 개념적으로 보면 Affine 계층의 순전파는 이 한 줄이다.
out = np.dot(x, W) + b

x의 기울기

상류 미분이 입력 쪽으로 얼마나 돌아가는지

W의 기울기

가중치를 어떻게 바꿔야 손실이 줄어드는지

b의 기울기

편향을 어떻게 바꿔야 하는지

기울기 확인은 왜 필요한가

수치 미분은 느리지만 구현이 단순해서 버그가 적다. 오차역전파법은 빠르지만 구현이 복잡하다. 그래서 두 방식으로 구한 기울기가 거의 같은지 비교해 역전파 구현이 맞는지 확인한다.

방법 장점 단점 쓰임
수치 미분 단순하고 검증하기 쉬움 느림 정답에 가까운 비교 기준
오차역전파 빠름 구현 실수 가능 실제 학습에 사용

ReLU mask를 배열로 보면

ReLU는 순전파 때 0 이하였던 위치를 기억한다. 역전파 때 그 위치로 들어오는 미분은 0으로 막는다.

x = np.array([[1.0, -0.5],
              [-2.0, 3.0]])

mask = (x <= 0)
# [[False, True],
#  [ True, False]]

dout = np.ones_like(x)
dout[mask] = 0
print(dout)

예상 출력

[[1. 0.]
 [0. 1.]]
입력 x mask 역전파 결과
1.0 False 통과
-0.5 True 0으로 차단
-2.0 True 0으로 차단
3.0 False 통과

Affine 계층의 shape 추적

Affine 계층은 행렬 곱이므로 역전파에서도 shape가 맞아야 한다. 입력이 `X(N,D)`, 가중치가 `W(D,H)`, 출력 쪽 미분이 `dout(N,H)`라면 각 기울기의 shape는 다음처럼 정리된다.

shape 의미
X (N, D) 입력 배치
W (D, H) 가중치
dout (N, H) 상류에서 온 미분
dW (D, H) 가중치 기울기
db (H,) 편향 기울기
dx (N, D) 입력 쪽으로 되돌릴 미분

Affine backward를 코드로 풀어보기

Affine 계층의 역전파는 세 가지를 구한다. 입력 쪽으로 돌려보낼 `dx`, 가중치를 고칠 때 쓸 `dW`, 편향을 고칠 때 쓸 `db`다. 모두 행렬 곱의 shape 규칙에서 나온다.

# 순전파: out = X @ W + b
# X:    (N, D)
# W:    (D, H)
# dout: (N, H)

dx = np.dot(dout, W.T)       # (N,H) @ (H,D) -> (N,D)
dW = np.dot(X.T, dout)       # (D,N) @ (N,H) -> (D,H)
db = np.sum(dout, axis=0)    # (N,H) -> (H,)
구하는 값 계산 왜 이 shape인가
dx dout · W.T 다시 입력 X의 shape인 (N,D)로 돌아가야 함
dW X.T · dout 가중치 W의 shape인 (D,H)와 같아야 함
db batch 방향 합 편향은 출력 뉴런마다 하나씩 있으므로 (H,)

읽는 기준

Affine backward는 새로운 공식 암기가 아니라, 순전파의 `XW+B`에서 X, W, b 각각이 손실에 미친 영향을 shape가 맞게 되돌리는 과정이다.

작은 숫자로 한 번 확인하기

숫자를 작게 놓고 보면 `db`가 왜 batch 방향 합인지 바로 보인다. 편향 b는 모든 데이터에 더해지므로, 역전파 때도 각 데이터에서 온 미분을 출력 뉴런별로 더한다.

dout 출력 뉴런 0 출력 뉴런 1
데이터 1 1 2
데이터 2 3 4
db 1+3=4 2+4=6

스스로 점검

  1. ReLU가 mask를 저장하는 이유를 설명할 수 있는가?
  2. Affine 계층의 순전파 식을 말할 수 있는가?
  3. 수치 미분이 느린데도 기울기 확인에 쓰이는 이유를 설명할 수 있는가?

이번 글에서 기억할 것

  • ReLU는 입력이 0 이하였던 위치를 mask로 기억한다.
  • Sigmoid는 순전파 출력값을 기억한다.
  • Affine은 행렬 곱과 편향 더하기를 담당한다.
  • Softmax-with-Loss는 출력층과 손실 계산을 함께 묶는다.
  • 기울기 확인은 수치 미분과 역전파 결과를 비교해 구현 오류를 찾는 방법이다.

다음 글로 이어지는 질문

다음 글에서는 매개변수를 갱신하는 여러 방법, 특히 SGD와 Momentum, AdaGrad, Adam의 차이를 본다.

한 줄 정리: 계층 구현은 forward에서 값을 계산하고 필요한 것을 저장한 뒤, backward에서 그 저장값으로 미분을 되돌려주는 구조다.