바뀜

3,768 바이트 추가됨 ,  2021년 12월 8일 (수) 11:59
잔글
편집 요약 없음
8번째 줄: 8번째 줄:  
= 사용예 =
 
= 사용예 =
 
각 층을 클래스로 만들고, 해당 레이어의 전방전파, 후방전파 메서드를 만들어 구현한다.
 
각 층을 클래스로 만들고, 해당 레이어의 전방전파, 후방전파 메서드를 만들어 구현한다.
 +
 +
처음 출발하는 수는 1이다.  <math>\frac{\partial L}{\partial L} =1</math>이므로.
    
== Relu ==
 
== Relu ==
42번째 줄: 44번째 줄:  
# <math>y= -x</math>의 미분 -1을 곱한다.
 
# <math>y= -x</math>의 미분 -1을 곱한다.
   −
이들의 결과는 <math>y^2\exp(-x)</math>인데, <math>y= \frac{1}{1+\exp(-x)}</math>이므로 정리하면 <math>y(1-y)</math>가 된다. 즉, 순전파의 출력 y로부터 역전파를 구하게 되었다.<syntaxhighlight lang="python">
+
이들의 결과는 <math>y^2\exp(-x)</math>인데, <math>y= \frac{1}{1+\exp(-x)}</math>이므로 정리하면 <math>y(1-y)</math>가 된다.(사실, 그냥 미분을 계산해도 뭐;;) 즉, 순전파의 출력 y로부터 역전파를 구하게 되었다.<syntaxhighlight lang="python">
 
class Sigmoid:
 
class Sigmoid:
 
     def __init__(self):
 
     def __init__(self):
58번째 줄: 60번째 줄:     
== softmax ==
 
== softmax ==
 +
원리는 다음 affine 계층에서의 역전파를 참고하자.
 +
 +
일반적으로 Cross entropy error층과 함께 사용된다. 교차 엔트로피의 수식은 <math>L=-\sum_{k}t_k \ln y_k</math>이다. 역전파가 들어오면
 +
 +
=== 크로스엔트로피의 역전파 ===
 +
 +
* +의 역전파 -1을 그대로 전달하고,
 +
* 위에 곱해 *의 역전파 두 입력을 바꾸어 각 노드에 <math>-t_k</math>가  전달되고,
 +
* 위에 곱해 log의 역전파 <math>-\frac{t_k}{y_k}</math>를 전달한다.
 +
 +
=== 소프트맥스의 역전파 ===
 +
 +
* 입력값으로 <math>-\frac{t_k}{y_k}</math>가 들어오고,
 +
* 나누어주는 값으론 <math>\frac{t_1+t_2+...}{S}</math>인데, t_k는 정답레이블이므로 다 더하면 1이 된다. 즉, 분모의 시그마로 나누는 과정의 역전파는 <math>\frac{1}{S}</math>
 +
* 각각의 분자에는 <math>-\frac{t_k}{S y_k}</math>가 들어오고 exp연산에서 <math>y_k -t_k</math>가 결과로 나오게 된다.
    +
=== 구현 ===
 +
<syntaxhighlight lang="python">
 +
class SoftmasWithLoss:
 +
    def __init__(self):
 +
        self.loss = None  # 손실함수값을 저장하기 위한 변수.
 +
        self.y = None  # 출력값을 저장하기 위한 변수.
 +
        self.t = None  # 정답레이블
 +
       
 +
    def forward(self, x):
 +
        self.t = t
 +
        self.y = softmax(x)  # 이전에 구현한 소프트맥스 함수값을 넣는다.
 +
        self.loss = cross_entrop_error(self.y, self.t)  # 오차함수로 오차값을 얻는다. 내 생각엔 backward에서 다뤄도 될 것 같은데..
 +
        return self.loss  # 포워드니까 y값이 나와야 하는 거 아닌감??
 +
    def backward(self, dout):
 +
        batch_size = self.t.shape[0]  # 데이터 갯수
 +
        dx = (self.y - self.t) / batch_size  # 역전파값을 데이터 갯수로 나눈다.(데이터를 여러 개 넣었을 경우.)
 +
        return dx
 +
</syntaxhighlight>
 
== affine ==
 
== affine ==
 
딥러닝의 중간 층들에선 행렬 계산이 이루어지는데, 이런 행렬의 곱을 affine변환이라 부른다. 때문에 중간 뉴런들에 대한 층은 어파인층이라 부르고, 이들의 역전파를 통해 어파인층 학습도 가능하다.
 
딥러닝의 중간 층들에선 행렬 계산이 이루어지는데, 이런 행렬의 곱을 affine변환이라 부른다. 때문에 중간 뉴런들에 대한 층은 어파인층이라 부르고, 이들의 역전파를 통해 어파인층 학습도 가능하다.
 +
 +
오차함수에 대하여 각 입력에 대한 편미분은 <math>\frac{\partial E}{\partial X} = \left ( \frac{\partial E}{\partial x_1} , \frac{\partial E}{\partial x_2} , \ldots    \right )</math> 형태이다. 핵심은 <math>\left ( X+\frac{\partial E}{\partial X}  \right ) W = \left ( Y + \frac {\partial E}{\partial Y}  \right )</math>에서 <math> X W = Y</math>이므로 <math>\frac{\partial E}{\partial X} W = \frac{\partial E}{\partial Y}</math>이런 관계를 갖는다는 말이다. 즉, <math>\frac{\partial E}{\partial X} = \frac{\partial E}{\partial Y} W^T</math> 로 역전파와 가중치의 전치행렬을 통해 다음층으로 보낼 보낼 영향을 구할 수 있으며, 달리 쓰면 <math>\frac{\partial E}{\partial W} = \frac{\partial E}{\partial Y} X^T</math>을 통해 이전층에서 왔던 신호의 전치행렬을 통해 가중치의 영향력을 구할 수 있다.
 +
 +
=== 구현 ===
 +
<syntaxhighlight lang="python">
 +
class Affine:
 +
    def __init__(self, W, b):  # 처음에 만들 때 가중치와 편향값을 부여한다.
 +
        self.W = W
 +
        self.b = b
 +
        self.x = None  # 앞 층으로부터 받을 x. 이 값은 저장해뒀다가 W의 미분 때 쓰인다.
 +
        self.dW = None  # 가중치의 영향력(미분값)을 담을 것.
 +
       
 +
    def forward(self, x):
 +
        self.x = x
 +
        out = np.dot(x, self.W) + self.b
 +
        return out
 +
    def backward(self, dout):
 +
        dx = np.dot(dout, self.W.T)  # x로 전파할 값을 구한다.
 +
        self.dW = np.dot(self.x.T, dout)
 +
        self.db = np.sum(dout, axis=0)  # +의 역전파는 그냥 더할 뿐이니, 전파된 값들을 다 더해 하나의 축으로 만든다.
 +
</syntaxhighlight>
 +
[[분류:딥러닝 이론]]