검수요청.png검수요청.png

"역전파"의 두 판 사이의 차이

위키원
이동: 둘러보기, 검색
잔글 (계산 그래프)
 
(사용자 4명의 중간 판 65개는 보이지 않습니다)
1번째 줄: 1번째 줄:
'''역전파'''(Backpropagation)는 먼저 계산 결과와 정답의 오차를 구해 이 오차에 관여하는 값들의 가증치를 수정하여 오차가 작아지는 방향으로 일정 횟수를 반복해 수정하는 방법이다. '''오차역전파''' 또는 '''오류역전파'''라고도 불린다.<ref>eccjang, 〈[http://www.incodom.kr/%EA%B8%B0%EA%B3%84%ED%95%99%EC%8A%B5/%EC%98%A4%EC%B0%A8%EC%97%AD%EC%A0%84%ED%8C%8C 오차역전파]〉, 《인코덤》, 2019-01-07</ref>
+
'''역전파'''(Backpropagation)는 먼저 계산 결과와 정답의 오차를 구해 이 오차에 관여하는 값들의 가증치를 수정하여 오차가 작아지는 방향으로 일정 횟수를 반복해 수정하는 방법이다. '''오차역전파''' 또는 '''오류역전파'''라고도 불린다.<ref>eccjang, 〈[http://www.incodom.kr/%EA%B8%B0%EA%B3%84%ED%95%99%EC%8A%B5/%EC%98%A4%EC%B0%A8%EC%97%AD%EC%A0%84%ED%8C%8C 오차역전파]〉, 《인코덤》, 2019-01-07</ref> 반대말은 [[순전파]]이다.
  
 
==개요==
 
==개요==
역전파 알고리즘은 사슬 규칙을 이용하는 기울기 기반 최적화 알고리즘에 따라 인공신경망(ANN)을 효율적으로 훈련시키는 데 사용되는 방법이다. 이 역전파의 주요 특징은 학습 중인 작업을 수행할 수 있을 때까지 네트워크를 개선하기 위해 가중치 업데이트를 계산하는 반복적이고 재귀적이며 효율적인 방법이다. 역전파는 네트워크 설계 시 활성화 함수의 파생물을 알아야 한다. 자동 미분은 파생물을 훈련 알고리즘에 자동 및 분석적으로 제공할 수 있는 기술이다.<ref>〈[https://en.wikipedia.org/wiki/Backpropagation Backpropagation]〉, 《위키피디아》</ref>
+
[[역전파 알고리즘]]은 사슬 규칙을 이용하는 기울기 기반 최적화 알고리즘에 따라 [[인공신경망]](ANN)을 효율적으로 훈련하는 데 사용되는 방법이다. 이 역전파의 주요 특징은 학습 중인 작업을 수행할 수 있을 때까지 네트워크를 개선하기 위해 가중치 업데이트를 계산하는 반복적이고 재귀적이며 효율적인 방법이다. 역전파는 네트워크 설계 시 활성화 함수의 파생물을 알아야 한다. 자동 미분은 파생물을 훈련 알고리즘에 자동 및 분석적으로 제공할 수 있는 기술이다.<ref>〈[https://en.wikipedia.org/wiki/Backpropagation Backpropagation]〉, 《위키피디아》</ref>
 +
 
 +
==배경==
 +
역전파는 [[XOR]] 문제를 해결하기 위해 1986년이 되어서야 등장했다. 엄밀히 따지면 1974년도에 처음 해결책이 제시되었는데, 그 출처는 당시 하버드 대학원생이었던 폴 웨어보스(Paul Werbos)의 박사 논문이다. [[다층 퍼셉트론]](MLP) 구조를 통해 어떤 결과가 예측되었을 때, 그 예측이 틀린다면 퍼셉트론의 Weight와 Bias 값을 조정해야 한다. 이것을 우리는 학습이라 말하며, 이 과정을 반복할수록 최적의 Weight와 Bias 값을 찾을 수 있다. 기존의 문제는 이 다층 퍼셉트론을 이루는 수많은 퍼셉트론 각각의 Weight와 Bias 값을 구조가 어렵다는 이유로 수정할 방법이 없었다. 이 문제에 대해 폴 웨어보스가 역전파 알고리즘을 제시했다. 이 폴 웨어보스의 혁신적인 논문은 처음부터 주목받지 못했다. 그만큼 분위기가 침체되어 있었기 때문이다. 그러다 1986년 [[제프리 힌턴]](Geoffrey Hinton) 교수가 XOR 문제의 해결책을 제시하는데, 그 방법은 [[폴 웨어보스]] 박사의 역전파 [[알고리즘]]과 동일했다. 서로 다른 두 사람이 동일한 방법을 생각해낸 것이다. 그래서 엄밀히 따지면 제프리힌턴 교수가 해결책을 재발견했다고 말할 수 있다.<ref>수박, 〈[https://hobbang143.blog.me/221470048265 (머신러닝 - 08) 인공 신경망의 위기, 역전파(Backpropagation)]〉, 《네이버 블로그》, 2019-02-20</ref>
  
 
==구동 방식==
 
==구동 방식==
 
#임의의 초기 가중치(<math>W</math>)를 준 뒤 결과 (<math>Y_{out}</math>)를 계산한다.
 
#임의의 초기 가중치(<math>W</math>)를 준 뒤 결과 (<math>Y_{out}</math>)를 계산한다.
 
#계산 결과와 우리가 원하는 값 사이의 오차를 구한다.
 
#계산 결과와 우리가 원하는 값 사이의 오차를 구한다.
#경사 하강법을 이용해 바로 앞 가중치를 오차가 작아지는 방향으로 업데이트한다.
+
#경사 하강법을 이용해 바로 앞 가중치를 오차가 작아지는 방향으로 [[업데이트]]한다.
#위 과정을 더이상 오차가 줄어들지 않을 때까지 반복한다.
+
#위 과정을 더는 오차가 줄어들지 않을 때까지 반복한다.
  
여기서 '오차가 작아지는 방향으로 업데이트한다'는 의미는 미분 값이 0에 가까워지는 방향으로 나아간다는 말이다. 즉, '기울기가 0이 되는 방향'으로 나아가야 하는데, 이 말은 가중치에서 기울기를 뺐을 때 가중치의 변화가 전혀 없는 상태를 말한다. 따라서 오차역전파를 다른 방식으로 표현하자면 가중치에서 기울기를 빼도 값의 변화가 없을 때까지 계속해서 가중치 수정 작업을 반복하는 것이다. 이를 수식으로 표현하면 <math>W(t+1)=Wt-{\partial_{\text{오 차 }} \over \partial W}</math> 즉, 새 가중치는 현 가중치에서 '가중치에 대한 기울기'를 뺀 값이다.<ref>〈[https://thebook.io/006958/part03/ch08/01-02/ 모두의 딥러닝]〉, 《더북》</ref>
+
: 여기서 '오차가 작아지는 방향으로 업데이트한다'는 의미는 미분 값이 0에 가까워지는 방향으로 나아간다는 말이다. 즉, '기울기가 0이 되는 방향'으로 나아가야 하는데, 이 말은 가중치에서 기울기를 뺐을 때 가중치의 변화가 전혀 없는 상태를 말한다. 따라서 오차역전파를 다른 방식으로 표현하자면 가중치에서 기울기를 빼도 값의 변화가 없을 때까지 계속해서 가중치 수정 작업을 반복하는 것이다. 이를 수식으로 표현하면 <math>W(t+1)=Wt-{\partial_{\text{오 차 }} \over \partial W}</math> 즉, 새 가중치는 현 가중치에서 '가중치에 대한 기울기'를 뺀 값이다.<ref>〈[https://thebook.io/006958/part03/ch08/01-02/ 모두의 딥러닝]〉, 《더북》</ref>
  
==계산 그래프==
+
==특징==
계산 그래프(Computational Graph)란 계산 과정을 그래프로 나타낸 것이다. 그래프는 자료구조의 일종으로 여러 개의 노드(node)와 그 노드들을 잇는 선, 엣지(edge)로 표현된다. 계산 그래프를 이용한 문제 풀이는 다음 흐름으로 진행된다.
+
;[[계산 그래프]]
 +
계산 그래프(Computational Graph)란 계산 과정을 그래프로 나타낸 것이다. 역전파는 계산 그래프로 문제를 푼다는 특징이 있다. 그래프는 자료구조의 일종으로 여러 개의[[노드]]와 그 노드들을 잇는 선, 엣지(edge)로 표현된다. 계산 그래프를 이용한 문제 풀이는 다음 흐름으로 진행된다.
  
#계산 그래프를 구성한다.
+
* 계산 그래프를 구성한다.
#그래프에서 계산을 왼족에서 오른쪽으로 진행한다.
+
* 그래프에서 계산을 왼쪽에서 오른쪽으로 진행한다.<br>
  
여기서 왼족에서 오른쪽으로 진행하는 단계를 순전파(forward propagation)라고 한다. 순전파는 계산 그래프의 출발점부터 종작첨으로의 전파이다. 또한 반대 방향(오른쪽에서 왼쪽)의 전파도 가능한데, 이를 역전파라고 한다. 역전파는 이후의 미분을 계산할 때 중요한 역할을 한다. 계산 그래프의 특징은 '국소적 계산'을 전파함으로써 최종 결과를 얻는다는 점에 있다. 국소적이란 '자신과 직접 관계된 작은 범위'라는 뜻이다. 국소적 계산은 결국 전체에서 어떤 일이 벌어지든 상관없이 자신과 관계된 정보만으로 결과를 출력할 수 있다는 것이다. 국소적 계산은 단순하지만, 그 결과를 전달함으로써 전체를 구성하는 복잡한 계산을 해낼 수 있다. 계산 그래프의 이점 중 하나는 '국소적 계산'이다.  전체가 아무리 복잡해도 각 노드에서는 단순한 계산에 집중하여 문제를 단순화 할 수 있다. 또 다른 이점으로는, 계산 그래프는 중간 계산 결과를 모두 보관할 수 있다. 마지막으로 실제 계산 그래프를 사용하는 가장 큰 이유는 역전파를 통해 '미분'을 효율적으로 계산할 수 있다는 것이다.
+
여기서 왼쪽에서 오른쪽으로 진행하는 단계를 [[순전파]](forward propagation)라고 한다. 순전파는 계산 그래프의 출발점부터 종착첨으로의 전파이다. 또한 반대 방향(오른쪽에서 왼쪽)의 전파도 가능한데, 이를 역전파라고 한다. 역전파는 이후의 미분을 계산할 때 중요한 역할을 한다.  
  
==연쇄법칙==
+
;특징
역전파는 '국소적인 비문'을 순방향과는 반대인 오른쪽에서 왼족으로 전달한다. 또한, 이 국소적 미분을 전달하는 원리는 연쇄법칙(Chain rule)에 따른 것이다. 연쇄법칙은 계산 그래프 상의 역전파와 같다. 역전파의 계산 절차는 전달되는 신호에 노드의 국소적 미분을 곱한 후 다음 노드로 전달하는 것이다. 여기서 말하는 국소적 미분은 순전파 때의 미분을 구한다는 것이다. 연쇄법칙을 설명하려면 합성 함수 이야기부터 시작해야 한다. 합성 함수란 여러 함수로 구성된 함수이다. 예를 들어 <math>y=(x+a)^2</math>라는 식은 다음 두개의 식으로 구성된다.
+
계산 그래프의 특징은 '국소적 계산'을 전파함으로써 최종 결과를 얻는다는 점에 있다. 국소적이란 '자신과 직접 관계된 작은 범위'라는 뜻이다. 국소적 계산은 결국 전체에서 어떤 일이 벌어지든 상관없이 자신과 관계된 정보만으로 결과를 출력할 수 있다는 것이다. 국소적 계산은 단순하지만, 그 결과를 전달함으로써 전체를 구성하는 복잡한 계산을 해낼 수 있다.
 +
;이점
 +
계산 그래프의 이점 중 하나는 '국소적 계산'이다.  전체가 아무리 복잡해도 각 노드에서는 단순한 계산에 집중하여 문제를 단순화 할 수 있다. 또 다른 이점으로는, 계산 그래프는 중간 계산 결과를 모두 보관할 수 있다. 마지막으로 실제 계산 그래프를 사용하는 가장 큰 이유는 역전파를 통해 '미분'을 효율적으로 계산할 수 있다는 것이다.
 +
 
 +
==원리==
 +
역전파는 계산 결과를 순방향과는 반대인 오른쪽에서 왼쪽으로 전달한다. 또한, 이 계산 결과를 전달하는 원리는 연쇄법칙에 따른 것이다. 연쇄법칙은 계산 그래프상의 역전파와 같다. 역전파의 계산 절차는 전달되는 신호에 노드의 국소적 미분을 곱한 후 다음 노드로 전달하는 것이다. 여기서 말하는 국소적 미분은 순전파 때의 미분을 구한다는 것이다. 연쇄법칙을 설명하려면 합성 함수 이야기부터 시작해야 한다. 합성 함수란 여러 함수로 구성된 함수이다. 예를 들어 <math>y=(x+a)^2</math>라는 식은 다음 두 개의 식으로 구성된다.
  
 
  <math>y=u^2</math>
 
  <math>y=u^2</math>
 
  <math>u=x+a</math>
 
  <math>u=x+a</math>
  
연쇄법칙은 합성 함수의 미분에 대한 성질이며, 다음과 같이 정의된다. "합성 함수의 미분은 합성 함수를 구성하는 각 함수의 미분의 곱으로 나타낼 수 있다." 이것이 연쇄법칙의 원리이다. 예를 들어 위에 식에서 <math>x</math>에 대한 <math>y</math>의 미분은, <math>u</math>에 대한 <math>y</math>의 미분과 <math>x</math>에 대한 <math>u</math>의 미분의 곱으로 나타낼 수 있다.  
+
;연쇄법칙
 +
연쇄법칙은 합성 함수의 미분에 대한 성질이며, 다음과 같이 정의된다. "합성 함수의 미분은 합성 함수를 구성하는 각 함수의 미분 곱으로 나타낼 수 있다." 이것이 연쇄법칙의 원리이다. 예를 들어 위에 식에서 <math>x</math>에 대한 <math>y</math>의 미분은, <math>u</math>에 대한 <math>y</math>의 미분과 <math>x</math>에 대한 <math>u</math>의 미분의 곱으로 나타낼 수 있다.  
 
  <math>{dy \over dx}={dy \over du}\cdot{du \over dx}</math>
 
  <math>{dy \over dx}={dy \over du}\cdot{du \over dx}</math>
위의 식에서 <math>{dy \over du} = 2u</math>이고, <math>{du \over dx}=1</math>로, 최종적으로 <math>{dy \over dx}=2u=2(x+a)</math>가 된다.
+
위의 식에서 <math>{dy \over du} = 2u</math>이고, <math>{du \over dx}=1</math>로, 최종적으로 <math>{dy \over dx}=2u=2(x+a)</math>가 된다.
 +
 
 +
==노드==
 +
*'''덧셈 노드''' : 덧셈 노드의 역전파는 입력값을 그대로 흘려보낸다.
 +
*'''덧셈 계층''' : 덧셈 계층을 구현하는 방법이다.<ref name="연산">KIMINZU, 〈[https://blog.naver.com/poppy9401/221629339066 5.오차역전파법-역전파(덧셈, 곱셈)]〉, 《네이버 블로그》, 2019-08-26</ref>
 +
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
 +
 
 +
*'''곱셈 노드''' : 곱셈 노드의 역전파는 상류의 값에 순전파 때의 입력 신호들을 '서로 바꾼 값"을 곱해서 하류로 보낸다. 순전파 때 <math>x</math>가 곱해졌다면 역전파에서는 <math>y</math>를, 반대로 순전파 때 <math>y</math>가 곱해졌다면 역전파 때 <math>x</math>를 곱해서 전달한다. (한 노드에 <math>x, y</math> Input이 들어가서 <math>z</math>가 나온다고 하면, <math>x</math>쪽으로는 <math>y</math>가 곱해진 값이 전달되고, <math>y</math>쪽으로는 <math>x</math>가 곱해진 값이 전달된다.)
 +
*'''곱셈 계층''' : 곱셈 계층을 구현하는 방법이다.<ref name="연산"></ref>
 +
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  //역전파 : 서로 바꾼 값에 상류의 역전파 미분 값을 곱하여 흘린다.
 +
 +
    return dx, dy
 +
 
 +
==문제점 및 해결 기법==
 +
오류역전파 [[알고리즘]]의 문제점으로는 [[시그모이드 함수]]의 문제점이 있다. 시그모이드 함수는 가중치 조정을 위해 경사감소법을 사용하며 계단형식 함수를 미분 가능하도록 곡선화한다. 이로 인해 시그모이드 함수를 사용함으로써 히든 레이어(hidden layer)가 깊어질수록 가중치가 0으로 수렴하여 정확성이 감소한다. 이러한 문제를 해결하기 위해 [[Relu]]를 사용한다. x값이 0 이하이면 0을 출력, 0 이상이면 비례 함수를 적용하고 max(0, x) 함수를 사용한다.
 +
 
 +
==역전파 계층 구현==
 +
===신경망 프로세스===
 +
신경망에는 적응 가능한 가중치와 편향이 있고, 이 가중치와 편향을 훈련 [[데이터]]에 적응하도록 조정하는 과정을 '학습'이라 한다. 신경망 학습은 다음과 같이 4단계로 수행한다.
 +
 
 +
;1단계 "미니배치"
 +
훈련 데이터 중 일부를 무작위로 가져온다. 이렇게 선별한 데이터를 미니배치라 하며, 그 미니배치의 손실 함수값을 줄이는 것이 목표이다.
 +
;2단계 "기울기 산출"
 +
미니배치의 손실 함숫값을 줄이기 위해 각 가중치 매개변수의 기울기를 구한다. 기울기는 손실 함수의 값을 가장 적게 하는 방향을 제시한다.
 +
;3단계 "매개변수 갱신"
 +
가중치 매개변수를 기울기 방향으로 아주 조금 갱신한다.
 +
;4단계 "반복"
 +
1~3단계를 반복한다.
 +
 
 +
===신경망 구현하기===
 +
class TwoLayerNet:
 +
    def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
 +
        #가중치 초기화
 +
        self.params = { }
 +
        self.params[‘W1’] = weight_init_std * np.random.randn(input_size, hidden_size)
 +
        self.params[‘b1’] = np.zeros(hidden_size)
 +
        self.params[‘W2’] = weight_init_std * np.random.randn(hidden_size, output_size)
 +
        self.params[‘b2’] = np.zeros(output_size)
 +
 +
#계층 생성
 +
        self.layers = OrderedDict() # 순서가 보장되어야 한다.
 +
        self.layers[‘Affine1’] = Affine(self.params[‘W1’], self.params[‘b1’])
 +
        self.layers[‘Relu1’] = Relu()
 +
        self.layers[;Affine2’] = Affine(self.params[‘W2’], self.params[‘b2’])
 +
        self.lastLayer = SoftmaxWithLoss()
 +
 +
  def predict(self, x):
 +
        for layer in self.layers.values():
 +
            x = layer.forward(x)
 +
        return x
 +
 +
  def loss(self, x, t):
 +
        y = self.predict(x)
 +
        return self.lastLayer.forward(y, t)
 +
 +
  def accuracy(self, x, t):
 +
        y = self.predict(x)
 +
        y = np.argmax(y, axis=1)
 +
        if t.ndim != 1 :
 +
            t = np.argmax(t, axis=1)
 +
 +
        accuracy = np.sum(y == t) / float(x.shape[0])
 +
        return accuracy
 +
 +
  def numerical_gradient(self, x, t):
 +
        loss_W = lambda W: self.loss(x, t)
 +
 +
        grads = { }
 +
        grads[‘W1’] = numerical_gradient(loss_W, self.params[‘W1’])
 +
        grads[‘b1’] = numerical_gradient(loss_W, self.params[‘b1’])
 +
        grads[‘W2’] = numerical_gradient(loss_W, self.params[‘W2’])
 +
        grads[‘b2’] = numerical_gradient(loss_W, self.params[‘b2’])
 +
        return grads
 +
 +
  def gradient(self, x, t):
 +
        #순전파
 +
        self.loss(x, t)
 +
 +
        #역전파
 +
        dout = 1
 +
        dout = self.lastLayer.backward(dout)
 +
 +
        layers = list(self.layers.values())
 +
        layers.reverse()
 +
        for layer in layers:
 +
            dout = layer.backward(dout)
 +
 +
        #결과 저장
 +
        grads = { }
 +
        grads[‘W1’] = self.layers[‘Affine1’].dW
 +
        grads[‘b1’] = self.layers[‘Affine1’].db
 +
        grads[‘W2’] = self.layers[‘Affine2’].dW
 +
        grads[‘b2’] = self.layers[‘Affine2’].db
 +
        return grads
 +
 
 +
===기울기 검증하기===
 +
수치 미분은 느리다. 그리고 오차역전파법을 제대로 구현해두면 수치 미분은 더 필요 없다. 수치 미분은 오차역전파법을 정확히 구현했는지 확인하기 위해 필요하기도 하다. 수치미분의 이점은 구현하기 쉬워 버그가 숨어 있기 어렵지만, 오차역전파법은 구현하기 복잡해서 종종 실수하곤 한다. 그래서 수치 미분의 결과와 오차역전파법의 결과를 비교하여 오차역전파법을 제대로 구했는지 검증하곤 한다. 두 방식으로 구한 기울기가 일치함(엄밀히 말하면 거의 같음)을 확인하는 작업을 기울기 확인(gradient check)이라고 한다. 기울기 확인은 다음과 같이 구현한다.
 +
x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
 +
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
 +
 +
x_batch = x_train[:3]
 +
t_batch = t_train[:3]
 +
 +
grad_numerical = network.numerical_gradient(x_batch, t_batch)
 +
grad_backprop = network.gradient(x_batch, t_batch)
 +
 +
for key in grad_numerical.keys():
 +
    diff = np.average( np.abs(grad_backprop[key] - grad_numerical[key]) )
  
==종류==
+
수치 미분과 오차역전파법의 결과 오차가 0이 되는 일은 드물다. 이는 컴퓨터가 할 수 있는 계산의 정밀도가 유한하기 때문이다. 이 정밀도의 한계 때문에 오차는 대부분 0이 되지는 않지만, 올바르게 구현했다면 0에 아주 가까운 값이 된다. 만약 그 값이 크면 오차역전파법을 잘못 구현했다고 의심해봐야 한다.
*'''덧셈 노드''' : 덧셈 노드의 역전파는 입력 값을 그대로 흘려보낸다.
 
*'''곱셈 노드''' : 곱셈 노드의 역전파는 상류의 값에 순전파 때의 입력 신호들을 '서로 바꾼 값"을 곱해서 하류로 보낸다. 순전파때 <math>x</math>가 곱해졌다면 역전파에서는 <math>y</math>를, 반대로 순전파때 <math>y</math>가 곱해졌다면 역전파때 <math>x</math>를 곱해서 전달한다. (한 노드에 <math>x, y</math> Input이 들어가서 <math>z</math>가 나온다고 하면, <math>x</math>쪽으로는 <math>y</math>가 곱해진 값이 전달되고, <math>y</math>쪽으로는 <math>x</math>가 곱해진 값이 전달된다.)
 
  
 
{{각주}}
 
{{각주}}
40번째 줄: 182번째 줄:
 
* 〈[https://en.wikipedia.org/wiki/Backpropagation Backpropagation]〉, 《위키피디아》
 
* 〈[https://en.wikipedia.org/wiki/Backpropagation Backpropagation]〉, 《위키피디아》
 
* 〈[https://thebook.io/006958/part03/ch08/01-02/ 모두의 딥러닝]〉, 《더북》
 
* 〈[https://thebook.io/006958/part03/ch08/01-02/ 모두의 딥러닝]〉, 《더북》
 +
* 돼지왕왕돼지, 〈[https://aroundck.tistory.com/5325 (머신러닝) #5 오차역전파법 (Back propagation)]〉, 《티스토리》, 2018-07-07
 +
* 수박, 〈[https://hobbang143.blog.me/221470048265 (머신러닝 - 08) 인공 신경망의 위기, 역전파(Backpropagation)]〉, 《네이버 블로그》, 2019-02-20
 +
* KIMINZU, 〈[https://blog.naver.com/poppy9401/221629339066 5.오차역전파법-역전파(덧셈, 곱셈)]〉, 《네이버 블로그》, 2019-08-26
  
 
==같이 보기==
 
==같이 보기==
 +
* [[인공신경망]]
 +
* [[XOR]]
 +
* [[순전파]]
  
{{알고리즘|토막글}}
+
{{인공지능 기술|검토 필요}}

2022년 10월 17일 (월) 13:34 기준 최신판

역전파(Backpropagation)는 먼저 계산 결과와 정답의 오차를 구해 이 오차에 관여하는 값들의 가증치를 수정하여 오차가 작아지는 방향으로 일정 횟수를 반복해 수정하는 방법이다. 오차역전파 또는 오류역전파라고도 불린다.[1] 반대말은 순전파이다.

개요[편집]

역전파 알고리즘은 사슬 규칙을 이용하는 기울기 기반 최적화 알고리즘에 따라 인공신경망(ANN)을 효율적으로 훈련하는 데 사용되는 방법이다. 이 역전파의 주요 특징은 학습 중인 작업을 수행할 수 있을 때까지 네트워크를 개선하기 위해 가중치 업데이트를 계산하는 반복적이고 재귀적이며 효율적인 방법이다. 역전파는 네트워크 설계 시 활성화 함수의 파생물을 알아야 한다. 자동 미분은 파생물을 훈련 알고리즘에 자동 및 분석적으로 제공할 수 있는 기술이다.[2]

배경[편집]

역전파는 XOR 문제를 해결하기 위해 1986년이 되어서야 등장했다. 엄밀히 따지면 1974년도에 처음 해결책이 제시되었는데, 그 출처는 당시 하버드 대학원생이었던 폴 웨어보스(Paul Werbos)의 박사 논문이다. 다층 퍼셉트론(MLP) 구조를 통해 어떤 결과가 예측되었을 때, 그 예측이 틀린다면 퍼셉트론의 Weight와 Bias 값을 조정해야 한다. 이것을 우리는 학습이라 말하며, 이 과정을 반복할수록 최적의 Weight와 Bias 값을 찾을 수 있다. 기존의 문제는 이 다층 퍼셉트론을 이루는 수많은 퍼셉트론 각각의 Weight와 Bias 값을 구조가 어렵다는 이유로 수정할 방법이 없었다. 이 문제에 대해 폴 웨어보스가 역전파 알고리즘을 제시했다. 이 폴 웨어보스의 혁신적인 논문은 처음부터 주목받지 못했다. 그만큼 분위기가 침체되어 있었기 때문이다. 그러다 1986년 제프리 힌턴(Geoffrey Hinton) 교수가 XOR 문제의 해결책을 제시하는데, 그 방법은 폴 웨어보스 박사의 역전파 알고리즘과 동일했다. 서로 다른 두 사람이 동일한 방법을 생각해낸 것이다. 그래서 엄밀히 따지면 제프리힌턴 교수가 해결책을 재발견했다고 말할 수 있다.[3]

구동 방식[편집]

  1. 임의의 초기 가중치()를 준 뒤 결과 ()를 계산한다.
  2. 계산 결과와 우리가 원하는 값 사이의 오차를 구한다.
  3. 경사 하강법을 이용해 바로 앞 가중치를 오차가 작아지는 방향으로 업데이트한다.
  4. 위 과정을 더는 오차가 줄어들지 않을 때까지 반복한다.
여기서 '오차가 작아지는 방향으로 업데이트한다'는 의미는 미분 값이 0에 가까워지는 방향으로 나아간다는 말이다. 즉, '기울기가 0이 되는 방향'으로 나아가야 하는데, 이 말은 가중치에서 기울기를 뺐을 때 가중치의 변화가 전혀 없는 상태를 말한다. 따라서 오차역전파를 다른 방식으로 표현하자면 가중치에서 기울기를 빼도 값의 변화가 없을 때까지 계속해서 가중치 수정 작업을 반복하는 것이다. 이를 수식으로 표현하면 즉, 새 가중치는 현 가중치에서 '가중치에 대한 기울기'를 뺀 값이다.[4]

특징[편집]

계산 그래프

계산 그래프(Computational Graph)란 계산 과정을 그래프로 나타낸 것이다. 역전파는 계산 그래프로 문제를 푼다는 특징이 있다. 그래프는 자료구조의 일종으로 여러 개의노드와 그 노드들을 잇는 선, 엣지(edge)로 표현된다. 계산 그래프를 이용한 문제 풀이는 다음 흐름으로 진행된다.

  • 계산 그래프를 구성한다.
  • 그래프에서 계산을 왼쪽에서 오른쪽으로 진행한다.

여기서 왼쪽에서 오른쪽으로 진행하는 단계를 순전파(forward propagation)라고 한다. 순전파는 계산 그래프의 출발점부터 종착첨으로의 전파이다. 또한 반대 방향(오른쪽에서 왼쪽)의 전파도 가능한데, 이를 역전파라고 한다. 역전파는 이후의 미분을 계산할 때 중요한 역할을 한다.

특징

계산 그래프의 특징은 '국소적 계산'을 전파함으로써 최종 결과를 얻는다는 점에 있다. 국소적이란 '자신과 직접 관계된 작은 범위'라는 뜻이다. 국소적 계산은 결국 전체에서 어떤 일이 벌어지든 상관없이 자신과 관계된 정보만으로 결과를 출력할 수 있다는 것이다. 국소적 계산은 단순하지만, 그 결과를 전달함으로써 전체를 구성하는 복잡한 계산을 해낼 수 있다.

이점

계산 그래프의 이점 중 하나는 '국소적 계산'이다. 전체가 아무리 복잡해도 각 노드에서는 단순한 계산에 집중하여 문제를 단순화 할 수 있다. 또 다른 이점으로는, 계산 그래프는 중간 계산 결과를 모두 보관할 수 있다. 마지막으로 실제 계산 그래프를 사용하는 가장 큰 이유는 역전파를 통해 '미분'을 효율적으로 계산할 수 있다는 것이다.

원리[편집]

역전파는 계산 결과를 순방향과는 반대인 오른쪽에서 왼쪽으로 전달한다. 또한, 이 계산 결과를 전달하는 원리는 연쇄법칙에 따른 것이다. 연쇄법칙은 계산 그래프상의 역전파와 같다. 역전파의 계산 절차는 전달되는 신호에 노드의 국소적 미분을 곱한 후 다음 노드로 전달하는 것이다. 여기서 말하는 국소적 미분은 순전파 때의 미분을 구한다는 것이다. 연쇄법칙을 설명하려면 합성 함수 이야기부터 시작해야 한다. 합성 함수란 여러 함수로 구성된 함수이다. 예를 들어 라는 식은 다음 두 개의 식으로 구성된다.



연쇄법칙

연쇄법칙은 합성 함수의 미분에 대한 성질이며, 다음과 같이 정의된다. "합성 함수의 미분은 합성 함수를 구성하는 각 함수의 미분 곱으로 나타낼 수 있다." 이것이 연쇄법칙의 원리이다. 예를 들어 위에 식에서 에 대한 의 미분은, 에 대한 의 미분과 에 대한 의 미분의 곱으로 나타낼 수 있다.


위의 식에서 이고, 로, 최종적으로 가 된다.

노드[편집]

  • 덧셈 노드 : 덧셈 노드의 역전파는 입력값을 그대로 흘려보낸다.
  • 덧셈 계층 : 덧셈 계층을 구현하는 방법이다.[5]
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
  • 곱셈 노드 : 곱셈 노드의 역전파는 상류의 값에 순전파 때의 입력 신호들을 '서로 바꾼 값"을 곱해서 하류로 보낸다. 순전파 때 가 곱해졌다면 역전파에서는 를, 반대로 순전파 때 가 곱해졌다면 역전파 때 를 곱해서 전달한다. (한 노드에 Input이 들어가서 가 나온다고 하면, 쪽으로는 가 곱해진 값이 전달되고, 쪽으로는 가 곱해진 값이 전달된다.)
  • 곱셈 계층 : 곱셈 계층을 구현하는 방법이다.[5]
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   //역전파 : 서로 바꾼 값에 상류의 역전파 미분 값을 곱하여 흘린다.

    return dx, dy

문제점 및 해결 기법[편집]

오류역전파 알고리즘의 문제점으로는 시그모이드 함수의 문제점이 있다. 시그모이드 함수는 가중치 조정을 위해 경사감소법을 사용하며 계단형식 함수를 미분 가능하도록 곡선화한다. 이로 인해 시그모이드 함수를 사용함으로써 히든 레이어(hidden layer)가 깊어질수록 가중치가 0으로 수렴하여 정확성이 감소한다. 이러한 문제를 해결하기 위해 Relu를 사용한다. x값이 0 이하이면 0을 출력, 0 이상이면 비례 함수를 적용하고 max(0, x) 함수를 사용한다.

역전파 계층 구현[편집]

신경망 프로세스[편집]

신경망에는 적응 가능한 가중치와 편향이 있고, 이 가중치와 편향을 훈련 데이터에 적응하도록 조정하는 과정을 '학습'이라 한다. 신경망 학습은 다음과 같이 4단계로 수행한다.

1단계 "미니배치"

훈련 데이터 중 일부를 무작위로 가져온다. 이렇게 선별한 데이터를 미니배치라 하며, 그 미니배치의 손실 함수값을 줄이는 것이 목표이다.

2단계 "기울기 산출"

미니배치의 손실 함숫값을 줄이기 위해 각 가중치 매개변수의 기울기를 구한다. 기울기는 손실 함수의 값을 가장 적게 하는 방향을 제시한다.

3단계 "매개변수 갱신"

가중치 매개변수를 기울기 방향으로 아주 조금 갱신한다.

4단계 "반복"

1~3단계를 반복한다.

신경망 구현하기[편집]

class TwoLayerNet:
   def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
       #가중치 초기화
       self.params = { }
       self.params[‘W1’] = weight_init_std * np.random.randn(input_size, hidden_size)
       self.params[‘b1’] = np.zeros(hidden_size)
       self.params[‘W2’] = weight_init_std * np.random.randn(hidden_size, output_size)
       self.params[‘b2’] = np.zeros(output_size)

#계층 생성
       self.layers = OrderedDict() # 순서가 보장되어야 한다.
       self.layers[‘Affine1’] = Affine(self.params[‘W1’], self.params[‘b1’])
       self.layers[‘Relu1’] = Relu()
       self.layers[;Affine2’] = Affine(self.params[‘W2’], self.params[‘b2’])
       self.lastLayer = SoftmaxWithLoss()

  def predict(self, x):
       for layer in self.layers.values():
           x = layer.forward(x)
       return x

  def loss(self, x, t):
       y = self.predict(x)
       return self.lastLayer.forward(y, t)

  def accuracy(self, x, t):
       y = self.predict(x)
       y = np.argmax(y, axis=1)
       if t.ndim != 1 :
           t = np.argmax(t, axis=1)

       accuracy = np.sum(y == t) / float(x.shape[0])
       return accuracy

  def numerical_gradient(self, x, t):
       loss_W = lambda W: self.loss(x, t)

       grads = { }
       grads[‘W1’] = numerical_gradient(loss_W, self.params[‘W1’])
       grads[‘b1’] = numerical_gradient(loss_W, self.params[‘b1’])
       grads[‘W2’] = numerical_gradient(loss_W, self.params[‘W2’])
       grads[‘b2’] = numerical_gradient(loss_W, self.params[‘b2’])
       return grads

  def gradient(self, x, t):
       #순전파
       self.loss(x, t)

       #역전파
       dout = 1
       dout = self.lastLayer.backward(dout)

       layers = list(self.layers.values())
       layers.reverse()
       for layer in layers:
           dout = layer.backward(dout)

       #결과 저장
       grads = { }
       grads[‘W1’] = self.layers[‘Affine1’].dW
       grads[‘b1’] = self.layers[‘Affine1’].db
       grads[‘W2’] = self.layers[‘Affine2’].dW
       grads[‘b2’] = self.layers[‘Affine2’].db
       return grads

기울기 검증하기[편집]

수치 미분은 느리다. 그리고 오차역전파법을 제대로 구현해두면 수치 미분은 더 필요 없다. 수치 미분은 오차역전파법을 정확히 구현했는지 확인하기 위해 필요하기도 하다. 수치미분의 이점은 구현하기 쉬워 버그가 숨어 있기 어렵지만, 오차역전파법은 구현하기 복잡해서 종종 실수하곤 한다. 그래서 수치 미분의 결과와 오차역전파법의 결과를 비교하여 오차역전파법을 제대로 구했는지 검증하곤 한다. 두 방식으로 구한 기울기가 일치함(엄밀히 말하면 거의 같음)을 확인하는 작업을 기울기 확인(gradient check)이라고 한다. 기울기 확인은 다음과 같이 구현한다.

x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

x_batch = x_train[:3]
t_batch = t_train[:3]

grad_numerical = network.numerical_gradient(x_batch, t_batch)
grad_backprop = network.gradient(x_batch, t_batch)

for key in grad_numerical.keys():
   diff = np.average( np.abs(grad_backprop[key] - grad_numerical[key]) )

수치 미분과 오차역전파법의 결과 오차가 0이 되는 일은 드물다. 이는 컴퓨터가 할 수 있는 계산의 정밀도가 유한하기 때문이다. 이 정밀도의 한계 때문에 오차는 대부분 0이 되지는 않지만, 올바르게 구현했다면 0에 아주 가까운 값이 된다. 만약 그 값이 크면 오차역전파법을 잘못 구현했다고 의심해봐야 한다.

각주[편집]

  1. eccjang, 〈오차역전파〉, 《인코덤》, 2019-01-07
  2. Backpropagation〉, 《위키피디아》
  3. 수박, 〈(머신러닝 - 08) 인공 신경망의 위기, 역전파(Backpropagation)〉, 《네이버 블로그》, 2019-02-20
  4. 모두의 딥러닝〉, 《더북》
  5. 5.0 5.1 KIMINZU, 〈5.오차역전파법-역전파(덧셈, 곱셈)〉, 《네이버 블로그》, 2019-08-26

참고자료[편집]

같이 보기[편집]


  검수요청.png검수요청.png 이 역전파 문서는 인공지능 기술에 관한 글로서 검토가 필요합니다. 위키 문서는 누구든지 자유롭게 편집할 수 있습니다. [편집]을 눌러 문서 내용을 검토·수정해 주세요.