1. Introduction
이전 포스트에서 최대 마진 목적 함수를 통해 손실 함수를 계산하는 방법을 살펴보았습니다.
오답 점수가 정답 점수보다 더 큰 경우와 정답 점수가 오답 점수보다 더 큼에도
그 차이가 1(margin)보다 작은 경우에 손실을 계산합니다.
minimize J=max(1+sc−s,0)
손실을 계산해야 하는 경우는 J가 양수일 때를 뜻하는데 이때 J를 비용이라고 하고
이 비용이 양수일 때 모델의 파라미터를 훈련(업데이트)해야 합니다.
이번 포스팅에서는 모델의 여러 파라마터를 어떻게 훈련할 수 있는지를 다뤄보겠습니다.
2. Gradient
파라미터를 업데이트할 때 일반적으로 경사 하강법(gradient descent)을 사용합니다.
이때 필요한 것이 기울기(gradient) 정보입니다.
θ(t+1)=θ(t)−α▽θ(t)J
위 식은 파라미터를 업데이트하는 수식입니다.
θ(t+1)은 다음 시점의 파라미터, θ(t)는 현재 시점의 파라미터, α는 학습률,
▽θ(t)J는 현재 파라미터에 대한 손실의 변화율, 즉 기울기입니다.
기울기는 현재 파라미터 θ(t)가 변할 때 이에 따라 손실 함수 J는 얼마나 민감하게 변하는지를 의미합니다.
참고로 기울기가 양수라면 −α▽θ(t)J는 음수가 되므로
업데이트된 파라미터(θ(t)−α▽θ(t)J)는 현재의 파라미터(θ(t))보다 더 작은 값을 갖습니다.
이는 현재 파리미터가 손실을 증가시키는 방향에 있으므로 파라미터를 더 감소시키겠다는 것을 의미합니다.
기울기가 음수라면 −α▽θ(t)J는 양수가 되므로
업데이트된 파라미터(θ(t)−α▽θ(t)J)는 현재의 파라미터(θ(t))보다 더 큰 값을 갖습니다.
이는 현재 파라미터가 손실을 줄이고 있으므로 조금 더 같은 방향으로 나아가자는 것을 의미합니다.
기울기는 파라미터가 변함에 따라 손실은 얼마나 민감하게 변하는지를 의미한다.
3. Backpropagation
여기서는 미분이 주된 계산이므로 상수항인 편향은 따로 표현하지 않겠습니다.
역전파(backpropagation)은 chain rule을 이용하여
feed-forward computation에 사용된 어떤 파라미터에 대해서도
손실의 기울기를 계산할 수 있도록 해주는 기법입니다.
간단히 말하면 역전파는 그래디언트를 계산하는 것입니다.
아래의 피규어를 살펴봅시다.

먼저 이 피규어에 사용된 기호를 살펴봅시다.
- xi: 신경망의 입력
- s: 출력
- k: 레이어 번호
- j: 뉴런 번호
z(k)=W(k−1)a(k−1)a(k)=f(z(k))
가중치 행렬(W)과 이전 레이어의 출력(a)을 이용해 다음 레이어의 입력(z)을 만듭니다.
레이어의 입력(z)은 활성 함수(f)를 거쳐 다음 레이어의 출력(a)을 만듭니다.
zj(k)→Neuron→aj(k)
k번 레이어의 j번 뉴런은 스칼라 입력 zj(k)를 받고 활성 출력 aj(k)를 만듭니다.
xj=zj(1)=aj(1)
1번 레이어는 입력층이므로 xj와 zj(1), aj(1)은 같습니다.
W(k)는 k번째 레이어의 출력을 k+1번째 레이어의 입력으로 보내는 전달 행렬(transfer matrix)입니다.
가중치(파라미터)는 W로, 은닉층의 마지막 가중치는 U로 표현할 수 있습니다.
피규어에서는 2번 레이어의 가중치가 마지막 가중치이므로 W(2)=U입니다.
δj(k)=f′(zj(k))⋅i∑δi(k+1)Wij(k)
zj(k)에 대해 계산된 오차는 δj(k)로 표기합니다.
뉴런 출력의 미분(f′(zj(k)))과 이 뉴런과 연결된 모든 뉴런의 오차(δi(k+1))와 가중치(Wij(k))의 곱을 통해
새로운 역전파 오차를 구할 수 있습니다.
이에 대해서는 아래의 총 오차 일반화 부분에서 더 자세히 다루겠습니다.
a. 역전파 시작
파라미터와 가중치는 같은 말이므로 설명의 편의를 위해 지금부터 가중치라고 표현하도록 하겠습니다.
또한 손실과 비용도 같은 말이므로 비용이라고 표현하도록 하겠습니다.
앞서 역전파란 가중치에 대한 비용의 기울기를 계산하는 것이라고 했습니다.
역전파 과정을 자세히 살펴보기 위해 다음과 같은 가정을 하겠습니다.
비용 J=1+sc−s가 양수일 때 가중치 W14(1)을 업데이트

W14란 다음 레이어의 뉴런 번호가 1, 현재 레이어의 뉴런 번호는 4를 뜻합니다.
따라서 W14(1)은 1번 레이어의 4번 뉴런에서 2번 레이어의 1번 뉴런으로 향하는 가중치(그림의 빨간 선)을 의미합니다.
a1(3)부터 역전파를 진행하고 이 스텝에서의 오차(δ(3))는 1로 설정합니다.
중요한 사실은 W14(1)은 오직 z1(2)와 그에 따른 a1(2)에만 영향을 준다는 것입니다.
다시 말해 역전파는 해당 값에 영향을 주는 경로에서만 그래디언트를 전파합니다.
a1(2)는 다음 단계에서 W(2)와 곱해져 점수 s를 만드는 데 사용됩니다.
역전파는 가중치에 대해 손실의 기울기를 계산하는 것이다.
b. 기울기 계산
역전파가 무엇인지 다시 한번 떠올려 봅시다.
가중치에 대해 비용의 기울기를 계산하는 것 = 비용 J를 가중치로 미분하는 것
그렇다면 1번 레이어에 대해선 1번 레이어의 가중치로 미분해야 하므로 ∂Wij(1)∂J을 구해야 합니다.
이 값을 구하기 위해 체인룰(chain rule)을 사용합니다.
체인룰이란 합성 함수의 미분을 계산하는 규칙으로써 ∂Wij(1)∂J을 다음과 같이 표현할 수 있습니다.
∂Wij(1)∂J=∂s∂J∂Wij(1)∂s
먼저 ∂s∂J를 계산하겠습니다.
앞서 비용을 다음과 같이 정의했습니다.
minimize J=max(1+sc−s,0)
여기서 비용 J는 1+sc−s가 0보다 클 때만 계산되므로 J=1+sc−s입니다.
따라서
∂J=∂(1+sc−s)
이를 대입하면 다음과 같습니다.
∂s∂J=∂s∂(1+sc−s)
s로 미분하면 1+sc는 상수항이 되고 s의 계수인 −1만 남으므로 다음과 같습니다.
∂s∂J=∂s∂(1+sc−s)=−1
따라서
∂Wij(1)∂J=∂s∂J∂Wij(1)∂s=−1⋅∂Wij(1)∂s
이제 ∂Wij(1)∂s을 계산해야 합니다.
신경망의 출력인 s가 어떻게 계산되는지 생각해봅시다.
신경망 내의 다른 뉴런과 마찬가지로 가중치(W)와 활성값(a)을 곱하고
편향(b)을 더한 후 활성 함수(f)가 적용된 값일 것입니다.
s는 마지막 가중치와 활성값을 입력받으므로
s의 가중치 벡터는 W(2), 활성값 벡터는 a(2)로 표현할 수 있습니다.
따라서
∂Wij(1)∂s=∂Wij(1)∂(W(2)a(2))
지금 설명하고 있는 역전파는 특정 가중치 W14(1)에 대한 기울기 계산의 예를 통해 일반화를 유도함을 잊지 맙시다.
∂Wij(1)∂(W(2)a(2))를 구체적으로 살펴봅시다.
앞서 i는 다음 은닉층의 뉴런 번호, j는 현재 은닉층의 뉴런 번호를 의미한다고 언급했습니다.
W14(1)에서 출력으로 어떻게 도달하는지 아래 그림을 통해 살펴봅시다.

a4(1)→W14(1)→z1(2)→a1(2)→W1(2)→z1(3)→a1(3)→s
이 순서를 보면 i가 1로 고정되어 있음을 확인할 수 있습니다.
뉴런이 표적하는 다음 레이어의 뉴런이 모두 1번 뉴런이기 때문입니다.
i를 1로 바꾸어 식을 전개할 수 있으나 일반화를 위해 i로 고정하여 표현하도록 하겠습니다.
따라서
∂Wij(1)∂(W(2)a(2))=∂Wij(1)∂(Wi(2)ai(2))
중간 정리를 하면
∂Wij(1)∂J=∂s∂J∂Wij(1)∂s=−1⋅∂Wij(1)∂s=−1⋅∂Wij(1)∂(Wi(2)ai(2))
이제 좀 더 구체화된 ∂Wij(1)∂(Wi(2)ai(2))을 살펴봅시다.
Wi(2)ai(2)를 Wij(1)로 미분해야 합니다.
여기서 2번 레이어의 가중치 벡터 Wi(2)는 1번 레이어의 가중치 벡터 Wij(1)와
직접적인 연관이 없으므로 상수로 취급합니다.
하지만 ai(2)=f(zi(2))=f(∑jWij(1)aj(1))이므로 ai(2)는 Wij(1)와 직접적인 연관이 있습니다.
따라서 Wi(2)만 상수로 빼내면
∂Wij(1)∂(Wi(2)ai(2))=Wi(2)∂Wij(1)∂ai(2)
입니다.
다시 체인룰을 이용하여 다음과 같이 표현해봅시다.
Wi(2)∂Wij(1)∂ai(2)=Wi(2)∂zi(2)∂ai(2)∂Wij(1)∂zi(2)
ai(2)=f(zi(2))이므로
Wi(2)∂zi(2)∂ai(2)∂Wij(1)∂zi(2)=Wi(2)∂zi(2)∂f(zi(2))∂Wij(1)∂zi(2)
∂zi(2)∂f(zi(2))항은 f(zi(2))를 zi(2)로 미분하므로 f′(zi(2))입니다.
따라서
Wi(2)∂zi(2)∂f(zi(2))∂Wij(1)∂zi(2)=Wi(2)f′(zi(2))∂Wij(1)∂zi(2)
∂Wij(1)∂zi(2)의 ∂zi(2)를 살펴봅시다.
zi(2)=∑jWij(1)aj(1)이므로
Wi(2)f′(zi(2))∂Wij(1)∂zi(2)=Wi(2)f′(zi(2))∂Wij(1)∂j∑Wij(1)aj(1)
여기서
∂Wij(1)∂j∑Wij(1)aj(1)=aj(1)
따라서
Wi(2)f′(zi(2))∂Wij(1)∂j∑Wij(1)aj(1)=Wi(2)f′(zi(2))aj(1)
Wi(2)f′(zi(2))는 zi(2)에서의 역전파 오차이며 δi(2)로 표현됩니다.
앞서 오차는 δj(k)=f′(zj(k))⋅∑iδi(k+1)Wij(k)를 통해 구할 수 있음을 살펴봤습니다.
하지만 이 예제에서는 하나의 뉴런 출력을 전제하고 있으므로 ∑을 사용하지 않고
이전 스텝의 오차(δ(k+1)) 또한 1이므로 아래와 같이 표현할 수 있습니다.
Wi(2)f′(zi(2))aj(1)=δi(2)aj(1)
정리하면
∂Wij(1)∂J=∂s∂J∂Wij(1)∂s=−1⋅Wi(2)∂zi(2)∂ai(2)∂Wij(1)∂zi(2)=−1⋅Wi(2)f′(zi(2))aj(1)=−1⋅δi(2)aj(1)
W14(1)에 대한 손실의 기울기는 i=1,j=4이므로 아래와 같습니다.
−1⋅δ1(2)a4(1)=−1⋅W1(2)f′(z1(2))a4(1)
체인룰을 이용하여 기울기를 계산할 수 있다.
c. 편향 업데이트
편향 항들은 수학적으로 뉴런의 입력값 z1(2)에 기여하는 다른 가중치들처럼 작동하지만
편향이 곱해지는 입력은 항상 1이라는 점만 다릅니다.
다시 말해 뉴런의 경우 출력인 a(k)가 다음 뉴런의 입력이 되지만 편향의 경우 이 값이 1입니다.
따라서 k번 레이어의 i번 뉴런에 대한 편향의 그래디언트는 단순히 δi(2)입니다.
예를 들어 앞에서 살펴본 W14(1)대신 b1(1)을 업데이트하려 했다면
그 그래디언트는 단순히 f′(z1(2))⋅W1(2), 즉 δ1(2)이 되었을 겁니다.
편향의 다른 가중치와 동일하게 작용하나 입력은 항상 1이다.
d. 총 오차 일반화
δ(k)를 δ(k−1)로 업데이트하는 방법인 δ의 계층 간 전파를 살펴보고 이를 일반화해보겠습니다.
아래 그림을 살펴보겠습니다.

이전에 δi(k)가 zi(k), 즉 k번 레이어의 i번 뉴런에서 역방향으로 전파되는 오차(error signal)라고 정의했습니다.
이 오차는 δi(k)⋅Wij(k−1)로 계산되어 이전 계층의 뉴런 aj(k−1)로 전달됩니다.
하지만 aj(k−1)는 아래처럼 다수의 뉴런들에 연결되어 있을 수 있습니다.

이 경우, 연결된 모든 뉴런으로 부터 오차를 받아야 합니다.
따라서 위 피규어를 예로 들 때 aj(k−1)에 전달되는 총 오차는 다음과 같습니다.
δi(k)⋅Wij(k−1)+δm(k)⋅Wmj(k−1)
총 오차를 일반화하면
i∑δi(k)Wij(k−1)
이 총 오차를 j번 뉴런의 로컬 그래디언트 f′(zj(k−1))와 곱하면
zj(k−1)로 들어가는 최종 오차, 즉 δj(k−1)를 얻을 수 있습니다.
따라서 최종 식은 다음과 같습니다.
δj(k−1)=f′(zj(k−1))⋅i∑δi(k)Wij(k−1)
현재 뉴런의 총 오차 = 현재 뉴런의 그래디언트 ⋅ ∑ (현재 뉴런과 연결된 오차 ⋅ 현재 뉴런과 연결된 가중치)
4. Vectorization
지금까지 모델의 특정 파라미터 Wij(k)에 대한 그래디언트를 element-wise로 계산하는 방법을 다루었습니다.
이제 이 방식을 일반화하여 가중치 행렬과 편향 벡터 전체를 한꺼번에 업데이트할 수 있도록 해봅시다.
이 모델은 앞서 논의한 개별 계산을 단순히 확장한 것이며 오차 전파(error propagation)를 행렬-벡터 수준에서 이해하는 데 도움을 줍니다.
a. 가중치 행렬에 대한 그래디언트
특정 파라미터 Wij(k)에 대한 그래디언트는 다음과 같다고 했었습니다.
∂Wij(k)∂J=δi(k+1)⋅aj(k)
여기서 W(k)는 a(k)를 받아 z(k+1)를 생성하는 전달 행렬(transfer matrix)입니다.
따라서 전체 행렬 W(k)에 대한 그래디언트는 다음과 같이 정리할 수 있습니다.
▽W(k)=⎣⎢⎢⎢⎡δ1(k+1)a1(k)δ2(k+1)a1(k)⋮δ1(k+1)a2(k)δ2(k+1)a2(k)⋮⋯⋯⋱⎦⎥⎥⎥⎤=δ(k+1)⋅(a(k))T
예를 들어 a1(k)에 전달되는 오차는 a1(k)와 연결된 모든 오차 δ1(k+1),…,δn(k+1) 이므로 ⎣⎢⎢⎢⎡δ1(k+1)a1(k)δ2(k+1)a1(k)⋮⎦⎥⎥⎥⎤로 표현됩니다.
가중치 행렬 그래디언트는 이전 스텝의 오차와 뉴런 출력의 곱들로 이루어진다.
b. δ의 역전파
오차 δ(k+1)가 k+1번 레이어에서 계산되었을 때 이것을 k번 레이어로 역전파시키는 방법은 다음과 같습니다.
δ(k)=f′(z(k))∘((W(k))Tδ(k+1))
여기서 ∘는 원소별 곱(element-wise product)입니다.
다시 말해 f′(z(k))와 (W(k))Tδ(k+1))을 원소별로 곱하는 것입니다.
이 식은 다음같은 일반적인 구조를 전제합니다.
- 순전파 시 z(k) 먼저 계산
- 그 위에 비선형 함수 f가 적용되어 a(k)가 만들어 짐
- a(k)는 가중치 행렬 W(k)를 거쳐 z(k+1)로 전환됨
c. 계산 효율
역전파 계산 방법에는 element-wise 업데이트와 vector-wise 업데이트가 있습니다.
element-wise 업데이트는 각 가중치 하나하나에 대해 직접 미분 값(그래디언트)을 계산하는 방식이며
앞서 미분 값 계산을 위해 수식을 유도한 ∂Wij(k)∂J=δi(k+1)⋅aj(k)같은 계산을 의미합니다.
하지만 컴퓨팅 환경에서는 벡터화된 구현이 훨씬 더 빠릅니다.
vector-wise 업데이트는 모든 가중치에 대한 그래디언트를 한 번에 행렬 연산으로 계산하며
▽W(k)=δ(k+1)⋅(a(k))T같은 계산을 의미합니다.
또한 역전파 과정에서 중복 계산을 줄이는 것도 중요하므로 W(k)를 업데이트할 때
계산해둔 δ(k+1)를 저장해두어 이후 δ(k) 계산에 재사용할 수 있습니다.
따라서 실제로는 벡터화된 구현을 사용하는 것이 좋습니다.
5. 정리
이번 포스팅에서는 역전파를 살펴봤습니다.
내용을 간략히 정리하면
- 가중치를 업데이트하기 위해선 가중치에 대한 기울기가 필요하다.
- 역전파란 가중치에 대해 손실의 기울기를 계산하는 것이다.
- 체인룰을 이용하여 기울기를 구하는 과정을 쉽게 이해할 수 있다.
- 하지만 실제로는 효율을 위해 원소별로 계산하지 않고 벡터화하여 구현한다.
- 뉴런 출력의 전체 행렬과 손실의 전체 행렬을 행렬곱하여 기울기 전체 행렬을 구할 수 있다.
- 원래의 가중치 전체 행렬에서 기울기 전체 행렬을 뺄셈하여 가중치를 업데이트한다.
다음 포스팅은 신경망을 실제로 사용할 때 일반적으로 사용되는 팁을 살펴보겠습니다.