연쇄법칙
순전파는 왼쪽 -> 오른쪽으로 전달
역전파는 국소적인 미분을 반대로 오른쪽에서 왼쪽으로 전달함
계산 그래프의 역전파
신호 E에 노드의 국소적 미분(∂y/ ∂x)을 곱한 후 다음 노드로 전달
만약 y = f(x) = x^2 이라면 ∂y/ ∂x = 2x 값을 E에 곱하여서 앞 노드로 전달한다.
덧셈노드 역전파는 입력신호는 다음 노드로 그대로 전달함
곱셈노드 역전파는 입력신호들을 서로 바꾼값을 곱해서 보낸다
더하기는 1.1를 그대로 보내주고 곱하기의 역전파는 1.1*150(반대편 순전파의 값) =165를 반대편 역전파에 대입해준다.
곱셈계층에 대한 코드
class MulLayer:
def __init__(self): #x,y를 초기화 , 순전파 시의 입력값 유지
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
return dx, dy
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)
#역전파
dprice = 1
dapple_price, dtax = mul_tax_layer.backward(dprice)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)
print(dapple, dapple_num, dtax)
220.00000000000003
2.2 110.00000000000001 200
backward 호출순서는 forward와 반대이다. backward가 받는 인수는 순전파 출력의 미분값이다.
덧셈계층에 대한 노드
class AddLayer:
def __init__(self):
pass
def forward(self, x, y):
out = x + y
return out
def backward(self, dout):
dx = dout * 1
dy = dout * 1
return dx, dy
덧셈 계층에서는 초기화가 불필요함으로 바로 pass를 부여한다. 덧셈계층은 그대로 더해서 순전파를 이루고, 역전파는 미분값을 그대로 보낸다
위의 ppt 그래프를 코드로 구현하면 다음과 같다.
apple = 100
apple_num = 2
orange = 150
orange_num = 3
tax = 1.1
#계층
mul_apple_layer = MulLayer()
mul_orange_layer = MulLayer()
mul_tax_layer = MulLayer()
add_apple_orange_layer = AddLayer()
#순전파
apple_price = mul_apple_layer.forward(apple, apple_num)
orange_price = mul_orange_layer.forward(orange, orange_num)
all_price = add_apple_orange_layer.forward(apple_price,orange_price)
price = mul_tax_layer.forward(all_price, tax)
#역전파
dprice = 1
dall_price, dtax = mul_tax_layer.backward(dprice)
dapple_price, dorange_price = add_apple_orange_layer.backward(dall_price)
dorange, dorange_num = mul_orange_layer.backward(dorange_price)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)
print(price) #710
print(dapple_num, dapple, dorange, dorange_num, dtax) #110 2.2 3.3 165.0 650
활성화 함수 계층 구현
ReLU
ReLU 계층의 수식은 다음과 같다
다음식을 미분하면, 양수에서는 1, 0이하에서는 0이 나온다.
class Relu:
def __init__(self):
self.mask = None
def forward(self, x):
self.mask = (x<=0) #순전파 입력이 x원소값이 0 이하면 True, 그외는 False
out = x.copy()
out[self.mask] = 0
return out
def backward(self, dout):
dout[self.mask] = 0
dx = dout
return dx
import numpy as np
x = np.array([[1.0, -0.5], [-2.0, 3.0]])
print(x)
#[[ 1. -0.5]
# [-2. 3. ]]
mask = (x<= 0)
print(mask)
#[[False True]
# [ True False]]
순전파 때의 입력이 0 이하면 역전파의 값은 0 이하가 되어야함. mask의 원소가 True 인곳은 상류에서 전파된 미분값을 0으로 설정함.
Sigmoid
'/' 노드는 y = 1/x 계산을 수행한다.
1단계: y= 1/x를 미분하면 다음과 같이 나온다. 이 값을 ∂L/ ∂y
2단계 : 그대로 하류로 보내기
3단계: exp의 미분값은 exp이다. 여기서 exp(-x)를 곱해 보낸다
4단계: 곱셈 노드는 순전파의 값을 서로 바꿔 곱함. 여기서-1을 곱한다.
sigmoid의 계산그래프를 간단하게 표현하면 다음과 같다.
class Sigmoid:
def __init__(self):
self.out = None
def forward(self, x):
out = 1/(1+np.exp(-x))
self.out = out
return out
def backward(self, dout):
dx = dout * (1.0 - self.out) * self.out
return dx
순전파의 출력을 out에 보관했다가 역전파 계산 시 사용한다.
'Deep Learning > from scratch I' 카테고리의 다른 글
[밑바닥부터 시작하는 딥러닝 - 6장 학습 관련 기술들 I] (0) | 2024.02.08 |
---|---|
[밑바닥부터 시작하는 딥러닝 - 5장 오차역전파법 II] (1) | 2024.02.08 |
밑바닥부터 시작하는 딥러닝 - 4장 신경망 학습 II] (0) | 2024.02.07 |
밑바닥부터 시작하는 딥러닝 - 4장 신경망 학습 I] (1) | 2024.02.07 |
[밑바닥부터 시작하는 딥러닝 - 3장 신경망] (1) | 2024.02.05 |