5.3. 역전파
덧셈과 곱셈 연산 각각에 대한 역전파를 살펴본 후, 실전 문제에 적용해보자.
5.3.1. 덧셈 노드의 역전파
z = x + y 라는 식을 대상으로 역전파를 했을 때, x, y 각각에 대한 미분값은 1이 된다. 이를 계산 그래프로 나타냈을 때는 다음과 같다.
즉, 덧셈 노드의 역전파는 상류에서 전해진 미분값을 그대로 하류로 보낸다.
가령 '10 + 5 = 15'라는 계산이 있고, 상류에서 1.3이라는 값이 흘러나올 때, 계산 그래프는 다음과 같다.
5.3.2. 곱셈 노드의 역전파
z = xy 라는 식을 대상으로 역전파를 했을 때, x, y 각각에 대한 미분값은 y, x가 된다. 이를 계산 그래프로 나타냈을 때는 다음과 같다.
즉, 곱셈 노드 역전파는 상류에서 전해진 값에 순전파 때의 입력 신호들을 서로 바꿔서 곱해준 후 하류로 곱해준다.
가령 '10 x 5 = 50'이라는 계산이 있고, 상류에서 1.3 값이 흘러올 때 계산 그래프는 다음과 같다.
5.3.3. 사과 쇼핑의 예
앞 장에서 다뤘던 문제들로 역전파를 다시 살펴보자.
문제 1: 1개에 100원인 사과를 2개 샀을 때의 지불금액은? 단, 소비세가 10% 부과된다.
사과 가격의 미분은 2.2, 사과 개수의 미분은 110, 소비세의 미분은 200이다. 따라서 소비세와 사과 가격이 같은 양만큼 오르면 최종 금액에는 소비세는 200의 크기로, 사과 가격은 2.2의 크기로 영향을 준다고 해석할 수 있다.
문제 2: 1개에 100인 사과를 2개, 1개에 150원인 귤을 3개 샀을 때의 지불금액은? 단, 소비세가 10% 부과된다.
5.4. 단순한 계층 구현하기
이번 절은 앞 절에서 다룬 '사과 쇼핑' 문제를 파이썬으로 구현해본다. 신경망을 구성하는 '계층' 각각은 하나의 클래스로 구현한다. 참고로 곱셈 노드는 MulLayer, 덧셈 노드는 AddLayer이라는 클래스로 각각 나타낸다.
5.4.1. 곱셈 계층
모든 계층은 순전파를 처리하는 forward()와 역전파를 처리하는 backward()라는 공통의 메서드(인터페이스)를 갖도록 구현한다.
Class MulLayer:
def __init__(self):
self.x = None
self.y = None
def forward(self, x, y):
self.x = x
self.y = y
out = x * y
return out
def backward(self, dout):
dx = dout * self.y
dy = dout * self.x # x 와 y를 바꾼다.
return dx, dy
__init__()에서는 인스턴스 변수인 x와 y를 초기화해준다. 위 문제1에서 사과 2개 살 때의 순전파는 다음과 같이 구현된다.
apple = 100
apple_num = 2
tax = 1.1
# 계층들
mul_apple_layer = MulLayer()
mul_tax_layer = MulLayer()
# 순전파
apple_price = mul_apple_layer.forward(apple, apple_num)
price = mul_tax_layer.forward(apple_price, tax)
print(price) #220
역전파는 다음과 같이 구할 수 있다. 참고로 backward() 호출 순서는 forward() 때와는 반대다.
# 역전파
dprice = 1
dapple_price, dtax = mul_tax_layer.backward(dprice)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)
print(dapple, dapple_num, dtax) # 2.2, 110, 200
5.4.2. 덧셈 계층
덧셈 계층의 코드는 다음과 같이 구현할 수 있다.
class AddLayer:
def __init__(self):
pass
def forward(self, x, y):
out = x + y
retrn out
def backward(self, dout):
dx = dout * 1
dy = dout * 1
return dx, dy
덧셈 계층은 초기화가 필요없어서 __init__()에서 그냥 pass라는 명령어를 입력해준다.
위 문제 2에서 사과 2개, 귤 3개 살 때의 계산그래프를 파이썬으로 구현하면 다음과 같다.
apple = 100
apple_num = 2
orange = 150
orange_num = 3
tax = 1.1
# 계층들
mul_apple_layer = MulLayer()
mul_orange_layer = MulLayer()
add_apple_orange_layer = AddLayer()
mul_tax_layer = MulLayer()
#순전파
apple_price = mul_apple_layer.forward(apple, apple_num) #(1)
orange_price = mul_orange_layer.forward(orange, orange_num) #(2)
all_price = add_apple_orange_layer.forward(apple_price, orange_price) #(3)
price = mul_tax_layer.forward(all_price, tax) #(4)
#역전파
dprice = 1
dall_price, dtax = mul_tax_layer.backward(dprice) #(4)
dapple_price, dorange_price = add_apple_orange_layer.backward(dall_price) #(3)
dorange, dorange_num = mul_orange_layer.backward(dorange_price) #(2)
dapple, dapple_num = mul_apple_layer.backward(dapple_price) #(1)
print(price) # 715
print(dapple_num, dapple, dorange, dorange_num, dtax) #110, 2.2, 3.3, 165. 650
퀴즈
문제1. 다음 중 덧셈 노드와 곱셈 노드의 역전파의 차이점에 대해 옳은 것은?
1. 덧셈 노드와 곱셈 노드는 모두 상류에서 전달된 미분값을 그대로 하류로 보낸다.
2. 덧셈 노드는 입력값을 서로 바꿔 곱한 후 미분값을 전달하지만, 곱셈 노드는 그대로 전달한다.
3. 덧셈 노드와 곱셈 노드는 모두 입력값을 서로 바꿔 곱한 후 전달한다.
4. 덧셈 노드는 상류에서 전달된 미분값을 그대로 하류로 보내지만, 곱셈 노드는 입력값을 서로 바꿔 곱한 후 전달한다.
문제2. 빈칸을 채운 것으로 옳은 것은?
backward() 호출 순서는 forward() 때와는 ____이(하)며, backward()가 받는 인수는 '____'이다.
1. 반대, 순전파의 출력에 대한 미분값
2. 동일, 순전파의 출력에 대한 미분값
3. 반대, 순전파의 출력값
4. 동일, 순전파의 출력값
'Miscellaneous' 카테고리의 다른 글
[2025-1] 임준수 - 밑바닥부터 시작하는 딥러닝 리뷰, (CH 5.1,5.6) 계산 그래프, Affine/Softmax 계층 구현 (0) | 2025.03.26 |
---|---|
[2025-1] 박경태 - 밑바닥부터 시작하는 딥러닝 리뷰, (CH 5.5) 활성화 함수 계층 구현하기 (0) | 2025.03.26 |
[2025-1] 박경태 - 밑바닥부터 시작하는 딥러닝 리뷰, (CH 5.2) 연쇄 법칙 (0) | 2025.03.26 |
[2025-1] 박제우 - TabNet : Attentive Interpretable Tabular Learning (0) | 2025.03.15 |
[2025-1] 임준수 - 밑바닥부터 시작하는 딥러닝 리뷰, (CH 4.2) 손실함수 (0) | 2025.03.12 |