5.5.1 ReLU 계층

 

먼저 활성화 함수로 사용되는 ReLU의 수식이다.

ReLU의 수식
ReLU의 미분

순전파 때의 입력인 x가 0보다 크면 역전파는 상류의 값을 그대로 하류로 흘린다, 반면 x가 0보다 작은 경우엔 신호를 보내지 않는다. 

 

Computational Graph로는 다음과 같다.

이제 코드로 옮겨보자

class ReLU:
    def __init__(self):
        self.mask = None
        
    def forward(self, x):
        self.mask = (x<=0)
        out =x.copy()
        out[self.mask] = 0
        
        return out
    
    def backward(self, dout):
        dout[self.mask] = 0
        dx = dout
        
        return dx

여기서 mask 의 정체에 대해서 알기 어려웠지만 예시를 통한다면 쉽게 파악할 수 있다.

>>>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]]
 
>>>type(mask)
numpy.ndarray

>>>mask.dtype
dtype('bool')

기본적으로 x를 numpy array로 선언하기 때문에 mask 역시 이에 따라서 생성된다. 확인 결과 mask는 bool type의 ndarray임을 알 수가 있다.

>>relu = ReLU()
>>relu.forward(x)
array([[1., 0.],
       [0., 3.]])

0보다 작은 경우엔 mask가 True가 되어, out의 값을 0으로 만들어버렸다. 

>>>out[mask]
array([-0.5, -2. ])

>>>out[mask]=0
>>>out
array([[1., 0.],
       [0., 3.]])

다음과 같음을 볼 수 있다.

 

 

5.5.2 Sigmoid 계층

 

시그모이드 함수

이를 computational graph로 나타내면 다음과 같다.

이제 역전파의 흐름대로 한 단계 씩 알아보자. 

 

1단계

'/' 노드를 즉 y = 1/x를 미분하면 다음과 같은 결과를 얻을 수 있다. 

해석 하자면, 역전파 때는 순전파의 출력을 제곱한 후 음수값으로 하류로 전달한다.

 

2단계

'+' 노드는 단순히 값을 하류로 보낸다.

 

3단계

'exp'노드는 y=exp(x)를 수행하고, 미분과 원함수가 동일하다.

4단계

'x' 노드는 순전파 때의 값을 '서로 바꿔' 곱한다.

 

 

이제 전체의 결과를 보자

 

Sigmoid 계층의 Computational Graph (간소화)

 

식을 한 번 정리해보자 

굉장히 간단해졌다! Sigmoid 계층의 역전파는 순전파의 출력 (y) 만으로 계산할 수 있다.

 

마지막으로 간단하게 코드로 구현해보자

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-self.out)*self.out
        
        return dx

이 구현에서는 순전파의 출력을 인스턴스 변수 out에 저장했다가, 역전파 계산 때 그 값을 사용한다.

+ Recent posts