본문으로 건너뛰기

신경망: 미분 가능한 함수 근사기

📍 우리가 있는 위치: 3부 · 처음부터 배우는 학습, 10장. 「학습이란 무엇인가」에서는 직선 경계가 데이터에 맞아떨어질 때까지 숫자를 조정했지만, 어떤 패턴은 직선으로 결코 맞출 수 없습니다. 이 장에서는 그런 패턴을 해결할 수 있는 네트워크를 만들고, 이를 작동시키는 수학을 유도합니다.

앞선 두 장이 이 장의 토대를 마련했습니다. 학습이란 무엇인가는 학습을 추론의 거울상으로 규정했습니다. 규칙을 앞으로 발화시키는 대신, 학습자는 예시에서 출발하여 예측이 들어맞을 때까지 숫자를 조금씩 조정합니다. 이어서 경사 하강법은 그 조정 과정을 정밀하게 다듬었습니다. 그 장에 등장한 두 일꾼, 즉 점들에 맞춘 직선과 교수와 학생을 구분하는 로지스틱 분류기는 모두 동일한 형태의 결정 경계, 즉 하나의 직선 경계를 그렸습니다. 그러나 그 형태는 동시에 한계이기도 합니다. 선형 모델이 표현할 수 있는 것은 오직 직선뿐이며, 어떤 직선으로도 자를 수 없는 패턴이 존재합니다. 이 장은 그런 패턴 중 가장 작은 것을 마주하고, 선형 모델이 이를 풀 수 없음을 증명한 다음, 이를 풀어낼 수 있는 것, 즉 역전파로 훈련되는 은닉층 하나짜리 네트워크를 만들고 완전히 유도합니다.

쉽게 말하면

탁자 위에 흩어진 빨간 구슬과 파란 구슬을 곧은 막대기 하나를 놓아서 나누어야 한다고 상상해 보십시오. 빨간 구슬은 한쪽에, 파란 구슬은 다른 쪽에 모두 놓이도록 말입니다. 어떤 배치에서는 이것이 쉽습니다. 하지만 어떤 배치에서는 불가능합니다. 빨간 구슬이 대각선으로 마주 보는 두 모서리에 있고 파란 구슬이 나머지 두 모서리에 있다면, 어떤 곧은 막대기로도 이들을 나눌 수 없습니다. 이제 막대기를 놓기 전에 식탁보를 먼저 구겨서 들어 올리고 접을 수 있다고 상상해 보십시오. 그러면 같은 구슬들이 갑자기 일렬로 늘어서고, 막대기 하나로 그들을 나눌 수 있게 됩니다. 신경망의 은닉층이 바로 이 구기는 작업입니다. 은닉층은 곧은 절단이 통할 때까지 공간을 구부리며, 놀랍게도 오직 예시만으로 정확히 어떻게 접어야 하는지를 학습합니다.

이 장에서 다루는 내용

  • 그림이 아니라 증명: 배타적 논리합(XOR) 패턴이 선형적으로 분리 불가능함을 보이고, 선형 기준 모델이 이 패턴에서 4개 중 2개만 맞히는 결과를 보입니다.
  • 뉴런과 시그모이드: 비선형 함수를 통과하는 아핀 사전 활성화, 시그모이드의 도함수를 처음부터 유도하는 과정, 그리고 비선형성이 왜 선택 사항이 아닌지에 대한 논증.
  • 2-4-1 네트워크의 순전파: 모든 기호를 동반 코드 neural.py의 각 줄에 대응시킵니다.
  • 손실과 경사 하강법: 제곱 오차, 그 그래디언트, 그리고 학습률을 포함한 갱신 규칙.
  • 연쇄 법칙으로 유도한 역전파: 각 그래디언트를 그것을 계산하는 정확한 코드 줄에 대응시키고, 숫자를 한 단계씩 직접 계산해 봅니다.
  • 성과와 정직한 한계: 은닉층이 XOR을 정복하는 이유, 보편 근사 정리의 정밀한 서술, 미분 가능성이 신경망-기호 AI로 이어지는 다리인 이유, 그리고 이 장치가 풀지 못하는 두 가지 문제.

선형 모델이 XOR에서 실패하는 이유: 그림이 아니라 증명

선형 분류기는 하나의 가중합을 구한 뒤 문턱값을 적용해 입력에 점수를 매깁니다. 각 입력을 벡터 xRn\mathbf{x} \in \mathbb{R}^n으로 적으면, 이는 "x\mathbf{x}nn개의 실수 (x1,,xn)(x_1, \ldots, x_n)로 이루어진 순서 있는 목록이다"라고 읽습니다. 정수 nn입력 차원(input dimension), 즉 하나의 예시를 기술하는 데 쓰이는 측정된 특징(feature)의 개수입니다(이전 장의 교수-대-학생 분류기에서는 n=2n = 2였고, 나가는 차수(out-degree)와 들어오는 차수(in-degree)가 그 특징이었습니다). 입력과 짝을 이루는 것은 특징마다 하나씩인 가중치 벡터(weight vector) w=(w1,,wn)\mathbf{w} = (w_1, \ldots, w_n)와, 편향(bias)이라 불리는 단일한 수 bb입니다. 그러면 분류기의 예측은 다음과 같습니다.

y^=1 ⁣[wx+b>0],\hat y = \mathbf{1}\!\left[\, \mathbf{w}^\top \mathbf{x} + b > 0 \,\right],

풀어야 할 표기가 두 가지 있습니다. 식 wx\mathbf{w}^\top\mathbf{x}는 가중치 벡터와 입력 벡터의 내적(dot product), 즉 가중합 i=1nwixi=w1x1++wnxn\sum_{i=1}^{n} w_i x_i = w_1 x_1 + \cdots + w_n x_n입니다. 위 첨자 \top("전치"라고 읽습니다)는 가중치 열을 옆으로 눕혀 x\mathbf{x}의 각 성분과 하나씩 곱한 뒤 그 곱들을 더하게 만드는 표기상의 장치입니다. 지시함수 1[]\mathbf{1}[\cdot]는 대괄호 안의 조건이 성립하면 11을, 그렇지 않으면 00을 반환합니다. 따라서 이 뉴런은 특징들의 가중합에 편향을 더한 값이 0보다 클 때, 오직 그때만 11을 출력합니다.

기하학적으로 방정식 wx+b=0\mathbf{w}^\top\mathbf{x} + b = 0은 그 점수가 정확히 0이 되는 경계, 즉 두 클래스를 가르는 분할면을 나타냅니다. nn차원 공간에서 이런 평평한 경계를 초평면(hyperplane)이라 부릅니다. 특징이 두 개뿐이어서 n=2n = 2이고 각 예시가 보통의 평면 위의 점 (x1,x2)(x_1, x_2)일 때, 초평면은 단순히 하나의 직선입니다. n=3n = 3이면 그것은 평평한 평면이 되고, nn이 더 크면 여전히 초평면이라 부르는 더 높은 차원의 대응물이 됩니다. 분류기는 이 경계의 한쪽에 있는 모든 것에 11을, 다른 쪽에 있는 모든 것에 00을 부여합니다. 이런 단일한 경계 하나로 완벽하게 나눌 수 있는 데이터셋을 선형적으로 분리 가능(linearly separable)하다고 부릅니다.

XOR(배타적 논리합(exclusive-or)의 줄임말로, "입력 중 정확히 하나가 참일 때 참"이라는 뜻입니다) 함수는 그렇지 않은 대표적인 패턴입니다. 동반 코드 neural.py는 이를 레이블이 붙은 네 점으로 적어 둡니다.

XOR_X = [[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]]
XOR_Y = [0.0, 1.0, 1.0, 0.0]

목표값 표로 읽으면 (0,0)0(0,0)\mapsto 0, (1,1)0(1,1)\mapsto 0, (0,1)1(0,1)\mapsto 1, (1,0)1(1,0)\mapsto 1입니다. 클래스 00에 속한 두 점은 단위 정사각형의 마주 보는 모서리에 있고, 클래스 11에 속한 두 점도 마찬가지입니다. 어떤 직선도 이들을 갈라놓지 못한다는 사실은 그림을 보고 납득하는 문제가 아니라, 짧은 귀류법(proof by contradiction)으로 증명되는 사실입니다.

w=(w1,w2)\mathbf{w} = (w_1, w_2)인 분리 초평면 (w,b)(\mathbf{w}, b)가 존재하여 네 점 모두를 올바르게 분류한다고 가정해 봅시다. 양성(positive) 두 점은 wx+b>0\mathbf{w}^\top\mathbf{x} + b > 0을 만족해야 합니다.

(0,1): w2+b>0,(1,0): w1+b>0.(0,1):\ w_2 + b > 0, \qquad (1,0):\ w_1 + b > 0 .

이 두 개의 엄격한 부등식을 더하면 다음을 얻습니다.

w1+w2+2b>0.(A)w_1 + w_2 + 2b > 0. \tag{A}

음성(negative) 두 점은 wx+b0\mathbf{w}^\top\mathbf{x} + b \le 0을 만족해야 합니다.

(0,0): b0,(1,1): w1+w2+b0.(0,0):\ b \le 0, \qquad (1,1):\ w_1 + w_2 + b \le 0 .

이제 이 두 사실을 이용해 합 w1+w2+2bw_1 + w_2 + 2b를 다시 묶어 봅시다.

w1+w2+2b=(w1+w2+b)0+b00.(B)w_1 + w_2 + 2b = \underbrace{(w_1 + w_2 + b)}_{\le\, 0} + \underbrace{b}_{\le\, 0} \le 0. \tag{B}

명제 (A)와 (B)는 곧바로 서로 모순됩니다. 같은 양이 엄격히 양수이면서 동시에 0 이하일 수는 없습니다. 따라서 선형 분리기는 존재하지 않습니다. 이 네 점에 대해 어떤 단일 직선이든 할 수 있는 최선은 네 개 중 세 개를 맞히는 것입니다. 네 개 모두를 맞히는 것이 바로 방금 배제한 바로 그 상황이기 때문입니다.

이전 장에서 쓴 바로 그 로지스틱 분류기를 이 데이터에 풀어놓으면, 최선의 직선보다도 더 나쁜 성적을 냅니다. 모듈은 XOR에 대해 fit_logistic을 실행하여 다음을 출력합니다.

linear classifier on XOR: [1, 1, 1, 1] target [0, 1, 1, 0] -> correct: 2 / 4

네 개 중 두 개입니다. 구부러질 수 없는 선형 모델 위의 경사 하강법은 결국 모든 것을 11이라고 부르는 데 안착하며, 이는 동전 던지기보다 나을 것이 없습니다. 증명은 우리에게 상한이 네 개 중 세 개라고 알려 주었지만, 이 시드로 실행한 결과는 두 개라는 바닥에 떨어졌습니다. 어느 쪽이든 선형 계열은 잘못된 함수족이며, 필요한 해법은 더 나은 직선이 아니라 더 풍부한 함수입니다.

뉴런과 시그모이드

네트워크의 원자는 뉴런(neuron)입니다. 뉴런은 입력들의 가중합에 편향을 더한 아핀 사전 활성화(affine pre-activation)를 계산합니다.

z=wx+b=kwkxk+b,z = \mathbf{w}^\top \mathbf{x} + b = \sum_k w_k x_k + b,

그런 다음 그 스칼라를 비선형 활성화 함수(activation function)에 통과시켜 활성화(activation) a=σ(z)a = \sigma(z)를 만들어 냅니다. neural.py 전체에서 쓰이는 활성화 함수는 로지스틱 시그모이드(logistic sigmoid)입니다.

σ(z)=11+ez,σ:R(0,1),\sigma(z) = \frac{1}{1 + e^{-z}}, \qquad \sigma : \mathbb{R} \to (0, 1),

이는 어떤 실수든 열린 구간 (0,1)(0,1)로 짓눌러 넣는 S자 모양의 곡선입니다. 큰 음수 입력은 00 근처로, 큰 양수 입력은 11 근처로 대응되며, σ(0)=12\sigma(0) = \tfrac12입니다. 코드로는 다음과 같습니다.

def sigmoid(z: float) -> float:
if z < -60:
return 0.0
if z > 60:
return 1.0
return 1.0 / (1.0 + math.exp(-z))

앞의 두 조기 반환(early return)은 zz가 극단적일 때 math.exp가 오버플로(overflow)되지 않도록 막아 줄 뿐입니다. 실제 수학은 마지막 줄, 1.0 / (1.0 + math.exp(-z))이며, 이것이 정확히 σ(z)\sigma(z)를 계산합니다.

시그모이드가 훈련에 그토록 편리한 이유는 그 도함수가 시그모이드 자기 자신을 이용한 깔끔한 닫힌 형태(closed form)로 표현되기 때문입니다. σ(z)=(1+ez)1\sigma(z) = (1 + e^{-z})^{-1}로 적고 연쇄 법칙으로 미분해 봅시다.

σ(z)=(1+ez)2ddz ⁣(1+ez)=(1+ez)2(ez)=ez(1+ez)2.\sigma'(z) = -(1 + e^{-z})^{-2}\cdot \frac{d}{dz}\!\left(1 + e^{-z}\right) = -(1 + e^{-z})^{-2}\cdot(-e^{-z}) = \frac{e^{-z}}{(1 + e^{-z})^2}.

이 분수를 두 인수로 나누어 각각을 알아봅시다.

σ(z)=11+ezez1+ez=σ(z)ez1+ez.\sigma'(z) = \frac{1}{1 + e^{-z}}\cdot\frac{e^{-z}}{1 + e^{-z}} = \sigma(z)\cdot\frac{e^{-z}}{1 + e^{-z}}.

두 번째 인수는 ez1+ez=(1+ez)11+ez=111+ez=1σ(z)\dfrac{e^{-z}}{1 + e^{-z}} = \dfrac{(1 + e^{-z}) - 1}{1 + e^{-z}} = 1 - \dfrac{1}{1 + e^{-z}} = 1 - \sigma(z)이므로 다음처럼 단순화됩니다. 따라서

σ(z)=σ(z)(1σ(z)).\boxed{\,\sigma'(z) = \sigma(z)\,\bigl(1 - \sigma(z)\bigr).\,}

이 항등식 때문에 코드는 역방향 계산(backward pass) 동안 지수 함수를 결코 계산하지 않습니다. 활성화 값 a=σ(z)a = \sigma(z)를 이미 갖고 있다면, 국소 기울기는 그저 a(1a)a(1-a)일 뿐입니다. 수치적으로 이 기울기는 z=0z = 0에서 최고점을 찍는데, 이때 σ(0)=0.5\sigma(0) = 0.5이고 σ(0)=0.5×0.5=0.25\sigma'(0) = 0.5 \times 0.5 = 0.25입니다. 그리고 zz가 원점에서 멀어질수록 기울기는 0을 향해 감소합니다. 예를 들어 z=2z = 2에서는 σ(2)0.881\sigma(2)\approx 0.881이고 σ(2)0.881×0.1190.105\sigma'(2)\approx 0.881 \times 0.119 \approx 0.105입니다.

비선형성이 대체 왜 중요할까요? 비선형성이 없다면 깊이(depth)는 착각에 불과하기 때문입니다. 아핀 사상(affine map)은 xWx+b\mathbf{x}\mapsto W\mathbf{x} + \mathbf{b} 형태의 함수입니다. 이런 사상 둘을 합성해도 결과는 여전히 아핀입니다.

W2(W1x+b1)+b2=(W2W1)x+(W2b1+b2),W_2\bigl(W_1\mathbf{x} + \mathbf{b}_1\bigr) + \mathbf{b}_2 = (W_2 W_1)\,\mathbf{x} + (W_2\mathbf{b}_1 + \mathbf{b}_2),

이는 W=W2W1W = W_2 W_1이고 b=W2b1+b2\mathbf{b} = W_2\mathbf{b}_1 + \mathbf{b}_2Wx+bW\mathbf{x} + \mathbf{b}일 뿐입니다. 사이에 아무것도 두지 않고 선형 층을 백 개 쌓아도 결국 단 하나의 선형 층으로 무너져 버리므로, 단일 직선이 그릴 수 없는 것은 그것 역시 결코 그릴 수 없습니다. 층 사이에 비선형적인 짓누름을 끼워 넣으면 이러한 붕괴가 깨집니다. 그러면 합성된 함수는 이제 굽을 수 있으며, 그 곡률(curvature)이야말로 XOR과 같은 까다로운 패턴이 요구하는 바로 그것입니다 [1].

2-4-1 네트워크: 순전파

neural.py의 네트워크는 2-4-1 모양을 갖습니다. 입력 2개(XOR 점의 두 좌표), 뉴런 4개짜리 은닉층 하나, 출력 뉴런 1개입니다. 이 네트워크의 파라미터는 네 개의 블록에 담겨 있으며, 네 개의 은닉 유닛이 서로 다르게 출발하도록 작은 균등 난수 값으로 초기화됩니다.

W1 = [[rng.uniform(-1, 1) for _ in range(din)] for _ in range(hidden)]
b1 = [rng.uniform(-1, 1) for _ in range(hidden)]
W2 = [rng.uniform(-1, 1) for _ in range(hidden)]
b2 = rng.uniform(-1, 1)

이름들을 수식과 대응시켜 읽어 봅시다. W1[j][k]로 저장되는 Wjk(1)W^{(1)}_{jk}는 입력 kk에서 은닉 유닛 jj로 가는 가중치이며, 따라서 W(1)W^{(1)}4×24\times 2 행렬입니다. bj(1)b^{(1)}_j는 은닉 유닛 jj의 편향 b1[j]입니다. Wj(2)W^{(2)}_jW2[j]로, 은닉 유닛 jj에서 단일 출력으로 가는 가중치입니다. 그리고 b(2)b^{(2)}는 출력 편향 b2입니다. 이는 전부 합쳐 8+4+4+1=178 + 4 + 4 + 1 = 17개의 훈련 가능한 수입니다. 이들을 통틀어 가장 평범한 형태의 순전파 신경망인 다층 퍼셉트론(multilayer perceptron, MLP)이라 부릅니다.

순전파(forward pass)는 하나의 입력에 대해 왼쪽에서 오른쪽으로 네트워크의 출력을 계산합니다. 각 은닉 유닛 jj에 대해

zj(1)=kWjk(1)xk+bj(1),hj=σ ⁣(zj(1)),z^{(1)}_j = \sum_{k} W^{(1)}_{jk}\, x_k + b^{(1)}_j, \qquad h_j = \sigma\!\bigl(z^{(1)}_j\bigr),

이며, 이를 벡터 형태로 쓰면 h=σ ⁣(W(1)x+b(1))\mathbf{h} = \sigma\!\bigl(W^{(1)}\mathbf{x} + \mathbf{b}^{(1)}\bigr), 즉 시그모이드를 성분별로 적용한 것입니다. 출력 뉴런은 은닉 활성화들을 읽어 들입니다.

z(2)=jWj(2)hj+b(2),o=σ ⁣(z(2)).z^{(2)} = \sum_j W^{(2)}_j\, h_j + b^{(2)}, \qquad o = \sigma\!\bigl(z^{(2)}\bigr).

모든 기호는 코드에 그대로 나타납니다.

def forward(x):
h_pre = [sum(W1[j][k] * x[k] for k in range(din)) + b1[j] for j in range(hidden)]
h = [sigmoid(z) for z in h_pre]
o = sigmoid(sum(W2[j] * h[j] for j in range(hidden)) + b2)
return h, o

목록 h_pre[j]는 사전 활성화(pre-activation) zj(1)z^{(1)}_j이고, h[j]는 활성화 hj=σ(zj(1))h_j = \sigma(z^{(1)}_j), 즉 은닉 축 jj를 따라 그 점이 갖는 새로운 좌표입니다. 그리고 o는 출력 활성화 o=σ(z(2))o = \sigma(z^{(2)})로, (0,1)(0,1) 안의 한 수입니다. 이산적인 예측은 절반에서 문턱값을 적용해 읽어 냅니다. 동반 코드의 predict_mlpo가 0.5 이상이면 11을, 그렇지 않으면 00을 반환합니다.

손실과 경사 하강법

훈련에는 출력이 얼마나 틀렸는지를 재는 단일한 수가 필요합니다. 그래야 "덜 틀리다"에 방향이 생기기 때문입니다. 출력 oo와 목표값 yy를 갖는 하나의 예시에 대해 우리는 제곱 오차 손실(squared-error loss)을 사용합니다.

L=12(oy)2.L = \tfrac12\,(o - y)^2 .

계수 12\tfrac12는 편의를 위한 것입니다. 12(oy)2\tfrac12(o-y)^2oo에 대해 미분하면 남는 상수 없이 L/o=(oy)\partial L/\partial o = (o - y)를 얻습니다. 코드는 절반 없이 동일한 양, (o - target)**2을 측정하는데, 이는 모든 그래디언트를 상수 22배만큼 늘릴 뿐이며, 이는 학습률을 두 배로 늘리는 것과 구별되지 않으므로 본질적으로 아무것도 바꾸지 않습니다. 훈련이 끝날 때 보고되는 수치는 네 예시에 걸친 (oy)2(o-y)^2의 평균인 평균제곱오차(mean squared error, MSE)입니다.

손실을 줄이기 위해 우리는 경사 하강법(gradient descent)을 사용합니다. 열일곱 개의 파라미터를 모두 모아 하나의 벡터 θ\theta로 만듭니다. 그래디언트(기울기) θL\nabla_\theta L은 편도함수 L/θi\partial L/\partial\theta_i들의 벡터이며, LL이 가장 가파르게 증가하는 방향을 가리킵니다. 내리막으로 가기 위해서는 그 반대 방향으로 한 걸음을 내딛습니다.

θθηθL,\theta \leftarrow \theta - \eta\,\nabla_\theta L ,

여기서 학습률(learning rate) η>0\eta > 0이 걸음의 크기를 정합니다. neural.py에서 η\eta는 인자 lr=0.5입니다. 걸음이 너무 작으면 느릿느릿 기어가고, 너무 크면 골짜기를 지나쳐 발산할 수 있습니다. 갱신의 요령은 모두 그 빼기 부호 하나에 담겨 있습니다. 그래디언트를 빼는 것은 충분히 작은 걸음에 대해 각 파라미터를 손실을 가장 빠르게 줄이는 방향으로 움직입니다.

연쇄 법칙으로 유도하는 역전파

이제 남은 질문은 합성 함수들로 이루어진 네트워크에 대해 θL\nabla_\theta L을 어떻게 계산하느냐입니다. 그 답은 역전파(backpropagation)입니다. 미적분학의 연쇄 법칙을 층별로, 출력에서 입력 쪽으로 거슬러 올라가며 적용하고, 그 과정에서 중간 결과를 재사용하는 것입니다 [2]. neural.py에서 역방향 계산 전체와 갱신은 놀라울 만큼 짧습니다.

for x, target in zip(X, y):
h, o = forward(x)
# output layer gradient (MSE through sigmoid)
do = (o - target) * o * (1 - o)
# hidden layer gradients
dh = [do * W2[j] * h[j] * (1 - h[j]) for j in range(hidden)]
# updates
for j in range(hidden):
W2[j] -= lr * do * h[j]
b2 -= lr * do
for j in range(hidden):
for k in range(din):
W1[j][k] -= lr * dh[j] * x[k]
b1[j] -= lr * dh[j]

각 수량을 차례로 유도해 봅시다.

출력 오차. 편리한 중간 항은 출력 사전 활성화 z(2)z^{(2)}에 대한 손실의 도함수로, 출력에서의 오차 신호(error signal) 또는 델타 δ(2)\delta^{(2)}라고 부릅니다. 연쇄 법칙은 출력 활성화를 관통해 이어집니다.

δ(2)=Lz(2)=Looz(2)=(oy)σ ⁣(z(2))=(oy)o(1o),\delta^{(2)} = \frac{\partial L}{\partial z^{(2)}} = \frac{\partial L}{\partial o}\cdot\frac{\partial o}{\partial z^{(2)}} = (o - y)\cdot\sigma'\!\bigl(z^{(2)}\bigr) = (o - y)\,o\,(1 - o),

여기서는 손실로부터 얻은 L/o=(oy)\partial L/\partial o = (o-y)와, o=σ(z(2))o = \sigma(z^{(2)})이므로 시그모이드 항등식으로부터 얻은 σ(z(2))=o(1o)\sigma'(z^{(2)}) = o(1-o)를 사용했습니다. 이 줄이 δ(2)\delta^{(2)}를 계산합니다.

do = (o - target) * o * (1 - o)

출력층의 가중치와 편향. z(2)=jWj(2)hj+b(2)z^{(2)} = \sum_j W^{(2)}_j h_j + b^{(2)}이므로, 이 사전 활성화는 각 출력 가중치와 편향에 선형으로 의존하며, z(2)/Wj(2)=hj\partial z^{(2)}/\partial W^{(2)}_j = h_j이고 z(2)/b(2)=1\partial z^{(2)}/\partial b^{(2)} = 1입니다. 여기에 δ(2)\delta^{(2)}를 곱하면

LWj(2)=δ(2)hj,Lb(2)=δ(2).\frac{\partial L}{\partial W^{(2)}_j} = \delta^{(2)}\, h_j , \qquad \frac{\partial L}{\partial b^{(2)}} = \delta^{(2)} .

이는 정확히 갱신 항 W2[j] -= lr * do * h[j]b2 -= lr * do입니다. 즉 각 출력 가중치는 ηδ(2)hj\eta\,\delta^{(2)} h_j의 반대 방향으로, 편향은 ηδ(2)\eta\,\delta^{(2)}의 반대 방향으로 한 걸음 옮깁니다.

은닉층으로의 역전파. 출력 사전 활성화는 각 은닉 활성화 hjh_j에도 의존하며, z(2)/hj=Wj(2)\partial z^{(2)}/\partial h_j = W^{(2)}_j이므로, 은닉 활성화에 대한 손실의 민감도는

Lhj=δ(2)Wj(2).\frac{\partial L}{\partial h_j} = \delta^{(2)}\,W^{(2)}_j .

입니다. 이를 은닉 오차, 즉 은닉 유닛 jj에서의 델타로 바꾸려면, 그 유닛 자신의 시그모이드를 한 단계 더 거슬러 밀어 넣어야 합니다. 이 시그모이드의 기울기는 σ(zj(1))=hj(1hj)\sigma'(z^{(1)}_j) = h_j(1-h_j)입니다.

δj(1)=Lzj(1)=Lhjhjzj(1)=δ(2)Wj(2)σ ⁣(zj(1))=δ(2)Wj(2)hj(1hj).\delta^{(1)}_j = \frac{\partial L}{\partial z^{(1)}_j} = \frac{\partial L}{\partial h_j}\cdot\frac{\partial h_j}{\partial z^{(1)}_j} = \delta^{(2)}\,W^{(2)}_j\,\sigma'\!\bigl(z^{(1)}_j\bigr) = \delta^{(2)}\,W^{(2)}_j\,h_j\,(1 - h_j).

이는 정확히 리스트 컴프리헨션(list comprehension) dh[j] = do * W2[j] * h[j] * (1 - h[j])입니다. 세 개의 국소 기울기, 즉 출력 오차 do, 유닛 jj가 출력에 얼마나 강하게 기여하는지를 나타내는 W2[j], 그리고 유닛 jj의 시그모이드가 얼마나 민감하게 반응하는지를 나타내는 h[j] * (1 - h[j])를 곱한 것입니다.

은닉층의 가중치와 편향. 마지막으로, zj(1)=kWjk(1)xk+bj(1)z^{(1)}_j = \sum_k W^{(1)}_{jk} x_k + b^{(1)}_j는 자신의 가중치와 편향에 선형으로 의존하므로, zj(1)/Wjk(1)=xk\partial z^{(1)}_j/\partial W^{(1)}_{jk} = x_k이고 zj(1)/bj(1)=1\partial z^{(1)}_j/\partial b^{(1)}_j = 1이며, 이에 따라

LWjk(1)=δj(1)xk,Lbj(1)=δj(1),\frac{\partial L}{\partial W^{(1)}_{jk}} = \delta^{(1)}_j\, x_k , \qquad \frac{\partial L}{\partial b^{(1)}_j} = \delta^{(1)}_j ,

를 얻습니다. 코드는 이를 W1[j][k] -= lr * dh[j] * x[k]b1[j] -= lr * dh[j]로 적용합니다.

이 방법 전체는 층별로 적용된 연쇄 법칙 이상도 이하도 아닙니다. 이를 효율적으로 만드는 단 하나의 사실은 δ(2)\delta^{(2)}의 재사용입니다. 이는 한 번만 계산된 뒤 모든 출력 가중치 그래디언트에 곱해지고 모든 은닉 델타로 이어져 들어가며, 열일곱 개의 파라미터마다 다시 계산되지 않습니다. 순전파 한 번과 역전파 한 번이 모든 그래디언트를 만들어 내며, 그 비용은 가중치 개수의 제곱이 아니라 가중치 개수에 비례합니다.

한 단계를 손으로 직접 계산해 보기. 입력 x=(0,1)\mathbf{x} = (0,1), 목표값 y=1y = 1에 대해 네트워크가 현재 o=0.7o = 0.7을 출력하고, 출력에 기여하는 어느 한 은닉 활성화가 hj=0.6h_j = 0.6이라고 해 봅시다. 그러면

δ(2)=(oy)o(1o)=(0.71)(0.7)(0.3)=(0.3)(0.21)=0.063.\delta^{(2)} = (o - y)\,o\,(1-o) = (0.7 - 1)(0.7)(0.3) = (-0.3)(0.21) = -0.063 .

이 됩니다. 그 출력 가중치에 대한 갱신량은 ηδ(2)hj=(0.5)(0.063)(0.6)=+0.0189-\eta\,\delta^{(2)}h_j = -(0.5)(-0.063)(0.6) = +0.0189입니다. 부호가 정확히 맞습니다. 출력이 너무 낮았고(목표값 1에 비해 0.7), 오차 신호는 음수로 나왔으며, 음수를 빼는 것은 가중치를 올려서 다음 출력을 더 높은 쪽으로 밀어냅니다. 열일곱 개의 파라미터 각각이 이와 동일한 논리로 조금씩 움직이며, 한 번의 조정으로는 손실이 거의 움직이지 않습니다. 이 반복문은 네 예시 전부에 대해 epochs=8000번의 패스(pass)를 실행하며, 그 수만 번의 자잘한 보정을 거치는 동안 접힘이 서서히 제자리를 잡아갑니다.

은닉층이 XOR을 정복하는 이유

기하학적으로 각 은닉 유닛은 입력 정사각형 안에 자기만의 직선 경계를 긋고, 시그모이드를 통해 부드럽게, 한 점이 어느 쪽에 떨어지는지를 알려줍니다. 은닉 유닛이 네 개이므로, 모든 입력 점은 그런 부드러운 답 네 개로 다시 쓰이며, 이는 4차원 은닉 공간(hidden space) 속의 새로운 위치가 됩니다. 학습된 가중치들은 이 경계들을 배치하여, 그렇게 재구성된 공간에서 클래스 11에 속한 두 점이 어떤 평면의 한쪽에, 클래스 00에 속한 두 점이 다른 쪽에 떨어지도록 만듭니다. 그런 다음 출력 뉴런은 원래의 정사각형에서 선형 분류기가 할 수 없었던 바로 그 일, 즉 하나의 평평한 분리 경계를 긋는 일을 해냅니다. 네트워크는 위의 증명이 금지하는, 입력 그림 속에서 더 영리한 직선을 찾아낸 것이 아닙니다. 네트워크는 직선 하나로 충분한 새로운 그림을 학습한 것입니다.

선형 모델을 무너뜨렸던 바로 그 네 점에 훈련된 2-4-1 네트워크를 돌려 보면, 모듈은 다음을 출력합니다.

2-4-1 MLP on XOR: [0, 1, 1, 0] target [0, 1, 1, 0] -> correct: 4 / 4 (loss 0.0003)

선형 모델의 네 개 중 두 개에 맞서, 네 개 중 네 개, 평균제곱오차는 약 0.00030.0003으로 사실상 0입니다. 어떤 직선도 자를 수 없었던 바로 그 패턴을, 네 개의 시그모이드 뉴런으로 이루어진 단 하나의 은닉층이 처음부터 학습해 냈습니다.

은닉층이 어째서 XOR을 학습하는지를 보여주는 세 패널짜리 다이어그램입니다. 왼쪽 패널은 입력 공간으로, 네 개의 XOR 점이 찍힌 단위 정사각형입니다. 좌표 (0, 0)과 (1, 1)에 있는 점은 마주 보는 모서리에 있는 파란색 클래스-0 점이고, (0, 1)과 (1, 0)에 있는 점은 나머지 두 모서리에 있는 호박색 클래스-1 점이며, 빨간색 점선 직선이 가로질러 훑고 지나가고, 어떤 하나의 직선으로도 두 파란 점을 같은 쪽에 둘 수 없어서 선형 분류기가 네 개 중 두 개밖에 맞히지 못한다는 설명이 캡션으로 달려 있습니다. 가운데 패널은 2-4-1 다층 퍼셉트론입니다. 두 좌표에 대한 두 개의 입력 노드가 수직으로 쌓인 네 개의 은닉 뉴런에 입력을 공급하며, 각 은닉 뉴런은 가중합을 계산한 다음 시그모이드를 적용하고, 네 개의 은닉 뉴런이 하나의 출력 뉴런에 입력을 공급하는데, 순전파(forward pass)라는 이름표가 붙은 실선 남색 화살표가 활성화를 왼쪽에서 오른쪽으로 나르고, 역전파(backpropagation)라는 이름표가 붙은 곡선 초록색 화살표가 그래디언트를 오른쪽에서 왼쪽으로 나르며, 출력에서의 연쇄 법칙 오차 신호 do와 은닉층에서의 dh가 함께 주석으로 달려 있습니다. 오른쪽 패널은 은닉 공간 시점으로, 은닉층이 공간을 구부린 뒤의 그 동일한 네 점이 이제는 하나의 평평한 평면이 파란색 클래스-0 점들과 호박색 클래스-1 점들을 깔끔하게 갈라놓도록 배치되어 있으며, 손실 0.0003에서 네 개 중 네 개가 맞았다고 적힌 초록색 상자가 있습니다. 맨 아래 띠는 가중합, 그다음 시그모이드, 그다음 손실이라는 파이프라인에 이름표를 달고, 모든 단계가 미분 가능하다고 적어 둡니다. XOR은 곧은 직선은 이겨내지만 구부러진 공간은 이겨내지 못합니다. 시그모이드 뉴런으로 이루어진 단 하나의 은닉층이 하나의 평면이 그것들을 갈라놓을 때까지 네 점의 모양을 다시 짜고, 역전파가 그 구부림을 학습하며, 훈련된 네트워크는 손실 0.0003에서 네 개 중 네 개를 맞힙니다. 저자가 AI의 도움을 받아 직접 제작한 원본 도해.

보편 근사

XOR은 운 좋은 특수 사례가 아닙니다. 보편 근사 정리(universal approximation theorem)는 이 일반적 사실을 정밀하게 서술합니다. 시그모이드 유닛으로 이루어진 은닉층 하나를 가진 순전파 네트워크는, 은닉층이 충분히 많은 유닛을 가지기만 한다면 Rn\mathbb{R}^n의 컴팩트한(닫혀 있고 유계인) 부분집합 위에서 어떤 연속 함수든 원하는 만큼의 정확도로 근사할 수 있습니다. 형식적으로 말하면, 컴팩트 집합 KK 위의 임의의 연속 함수 ff와 임의의 허용 오차 ε>0\varepsilon > 0에 대해, 어떤 너비 mm과 파라미터가 존재하여 네트워크 ggsupxKg(x)f(x)<ε\sup_{\mathbf{x}\in K}\lvert g(\mathbf{x}) - f(\mathbf{x})\rvert < \varepsilon을 만족합니다(Cybenko, 1989 [3]). 말로 풀면, supxK\sup_{\mathbf{x}\in K}는 영역 KK의 모든 점 x\mathbf{x}에 걸쳐 취한 상한(supremum, 순서 관계 장에서 만난 최소 상계)이고, g(x)f(x)\lvert g(\mathbf{x}) - f(\mathbf{x})\rvert는 그 점에서 네트워크와 목표 사이의 격차의 절댓값 크기입니다. 따라서 이 부등식은 KK 안 어디에서든 나타나는 최악의 격차가 여러분이 지정하는 어떤 오차 허용치 ε\varepsilon보다도 작게 만들어질 수 있다는 뜻입니다. 이것이 이 사업 전체를 떠받치는 이론적 허가증입니다. 네트워크는 직선이나 어떤 고정된 형태 계열에 국한되지 않고, 데이터에 맞추는 보편 함수 근사기(universal function approximator)입니다.

이 정리를 신중하게 서술할 가치가 있는 이유는 그것이 보장하지 않는 것 때문입니다. 이것은 존재성 결과(existence result)이며 전적으로 비구성적(non-constructive)입니다. 즉 적절한 가중치가 존재한다는 것은 보장하지만, 그것을 찾는 방법은 전혀 알려 주지 않습니다. 목표 함수가 몇 개의 은닉 유닛을 필요로 하는지에 대해서도 아무 말이 없으며, 어떤 함수에 대해서는 그 개수가 천문학적으로 클 수 있습니다. 그리고 결정적으로, 훈련에 대해서는 침묵합니다. 경사 하강법이나 그 밖의 어떤 알고리즘이든 이 정리가 약속하는 가중치를 실제로 찾아낼 것이라고는 결코 주장하지 않습니다. 보편성은 네트워크가 표현할 수 있는 것에 관한 진술이지, 우리가 믿을 수 있게 학습할 수 있는 것에 관한 진술이 아닙니다.

미분 가능성: 이 모든 것을 떠받치는 단어

이 파이프라인의 모든 단계가 공통으로 지닌 것이 무엇인지 주목해 봅시다. 아핀 사전 활성화, 시그모이드, 제곱 오차 손실은 각각 미분 가능(differentiable)합니다. 즉 이들은 어디서나 잘 정의된 기울기를 가집니다. 바로 이 하나의 속성이 연쇄 법칙으로 하여금 책임을 거꾸로 배분할 수 있게 하고, 경사 하강법이 파라미터를 개선할 수 있게 합니다. 어느 한 단계에서든 미분 가능성을 제거해 보십시오. 이를테면 시그모이드를 딱딱한 0/10/1 문턱값으로 바꾸면, 그래디언트는 거의 모든 곳에서 0이고 도약이 일어나는 지점에서는 정의되지 않으므로, 역전파는 붙잡을 것이 아무것도 없어 학습이 멈춰 버립니다. 미분 가능성은 문체상의 선택이 아니라 메커니즘 그 자체입니다.

바로 이것이 이 시리즈의 나머지 부분 전체가 돌아가는 정확한 경첩입니다. XOR에서 네트워크를 훈련시킨 바로 그 경사 하강법은 미분 가능한 손실로 표현할 수 있는 무엇이든 네트워크를 훈련시킬 수 있습니다. 4권은 논리적 제약 그 자체를 미분 가능하게 만듭니다. "모든 교수는 연구자다"와 같은 딱딱한 규칙은 그래디언트를 실어 나르는 매끄러운 진리값으로 완화되며, 이는 규칙이 성립하면 0이고 위반될수록 커지는 벌점(penalty)입니다. 그 벌점이 미분 가능하기 때문에, 심볼릭 요구사항은 바로 이 역전파에 의해 네트워크 안으로 훈련되어 들어갈 수 있으며, 같은 합 안에서 데이터 손실과 나란히 자리 잡습니다. 이 장에서 유도한, 구부리고 내려가는 엔진은 문자 그대로 뉴로-심볼릭 프로그램이 그 위에서 돌아갈 장치입니다. 여러분이 신경 쓰는 것을 미분 가능하게 만든 다음, 그래디언트가 그것을 찾아내도록 하는 것입니다.

아직 풀리지 않은 부분

위의 모든 내용 안에는 두 가지 정직한 한계가 자리 잡고 있으며, 어느 것도 사소한 디테일이 아닙니다.

첫 번째는 비볼록성(non-convexity)입니다. 경사 하강법은 국소적인 내리막 방향을 따르므로, LL국소 최소점(local minimum), 즉 θL=0\nabla_\theta L = 0이고 어떤 작은 걸음으로도 손실이 개선되지 않는 지점으로 수렴하지만, 이것이 반드시 전역 최소점(global minimum)인 것은 아닙니다. 제곱 오차를 갖는 선형 모델에서는 손실 표면이 하나의 그릇 모양(볼록)이므로 국소 최소점이 곧 전역 최소점이며, 앞서의 선형 및 로지스틱 적합은 최선의 답을 찾아낸다는 것이 보장되어 있었습니다. 네트워크의 손실 표면은 그릇 모양이 아닙니다. 그것은 수많은 국소 최소점, 안장점(saddle point), 평평한 고원(plateau)으로 어지럽혀진 고차원 지형이며, 경사 하강법이 어디에 안착하는지는 무작위 초기화와 학습률에 달려 있습니다. 우리의 XOR 실행은 0.00030.0003 근처의 손실에 도달했지만, 다른 시드에서는 더 나쁜 골짜기에 멈춰 설 수도 있습니다. 우리는 하나의 좋은 해를 찾을 수는 있지만, 일반적으로 그것이 가장 좋은 해임을 증명할 수는 없습니다.

두 번째는 불투명성(opacity)입니다. 훈련된 네트워크에게 (0,1)(0,1)에 대해 11을 출력하는지 물어보면, 유일하게 충실한 답은 열일곱 개의 실수 목록, W(1),b(1),W(2),b(2)W^{(1)}, \mathbf{b}^{(1)}, W^{(2)}, b^{(2)}뿐입니다. 그 어디에도 "입력 중 정확히 하나가 켜져 있을 때 11을 출력하라"는 문장은 없습니다. 규칙은 실재하지만, 그것은 어디에도 명시되지 않은 채 가중치들 전체에 걸쳐 뭉개져 있으며, 그래서 옳은 네트워크조차 감사 가능한(auditable) 이유를 전혀 제시하지 못합니다. 이를 2부의 혼 규칙(Horn rule)과 대조해 보십시오. researcher ← professor는 자신의 근거를 겉으로 드러내며, 여러분은 그 줄을 손가락으로 짚을 수 있습니다. 이것이 해석 가능성 간극(interpretability gap)이며, 5권은 이를 정면으로 다룹니다. 여기에는 충실성(faithfulness)이라는 날카로운 우려도 포함되는데, 이는 우리가 네트워크에서 뽑아낸 사람이 읽을 수 있는 설명이 네트워크가 실제로 계산하는 것과 일치하지 않을 수도 있다는 우려입니다.

왜 중요한가

이 장은 "신경망은 학습한다"라는 슬로건을 메커니즘으로 바꾸어 놓았습니다. 하나의 직선으로는 XOR을 자를 수 없으며, 우리는 이를 그저 스케치한 것이 아니라 증명했습니다. 시그모이드 뉴런으로 이루어진 은닉층은 그것을 해낼 수 있는데, 아핀 사상들 사이의 비선형성이 합성 함수를 굽게 만들기 때문입니다. 연쇄 법칙은 층별로 적용되고 자신의 오차 신호를 재사용하면서 한 번의 역방향 계산으로 모든 그래디언트를 계산하며, 경사 하강법은 네 개 중 네 개의 점이 거의 0에 가까운 손실로 분류될 때까지 파라미터를 내리막으로 걷게 합니다. 여기 나온 모든 방정식은 neural.py의 특정한 줄에 대응하므로, 이 이론은 장식이 아니라 바로 그 프로그램입니다. 무엇보다도, 이 모든 것을 작동시키는 속성인 미분 가능성은 이후의 권들이 논리와 학습을 융합하기 위해 이용하는 바로 그 속성입니다. 이 작은 네트워크를 완전히 익히고 나면, 앞으로 나올 최전선의 장들은 하나의 주제에 대한 변주로 읽힐 것입니다.

핵심 용어

  • 아핀 사전 활성화(Affine pre-activation) — 뉴런이 활성화 함수를 적용하기 전에 형성하는, 가중합에 편향을 더한 값 z=wx+bz = \mathbf{w}^\top\mathbf{x} + b입니다.
  • 활성화(Activation) — 비선형성을 거친 뒤 뉴런의 출력 a=σ(z)a = \sigma(z)입니다. 함수 σ\sigma 자체를 가리키는 이름이기도 합니다.
  • 시그모이드와 그 도함수(Sigmoid and its derivative) — R\mathbb{R}(0,1)(0,1)로 대응시키는 로지스틱 짓누름 σ(z)=1/(1+ez)\sigma(z) = 1/(1+e^{-z})이며, 닫힌 형태의 기울기 σ(z)=σ(z)(1σ(z))\sigma'(z) = \sigma(z)(1-\sigma(z))를 갖습니다.
  • 순전파(Forward pass) — 각 층을 왼쪽에서 오른쪽으로 계산하여 입력으로부터 네트워크의 출력을 구하는 것입니다.
  • 손실(Loss) — 출력이 얼마나 틀렸는지를 재는 스칼라입니다. 여기서는 제곱 오차 L=12(oy)2L = \tfrac12(o-y)^2이며, 데이터셋 전체에 걸쳐 평균제곱오차로 평균됩니다.
  • 그래디언트(Gradient) — 파라미터에 대한 손실의 편도함수들로 이루어진 벡터 θL\nabla_\theta L이며, 가장 가파르게 증가하는 방향을 가리킵니다.
  • 학습률(Learning rate) — 갱신 θθηθL\theta \leftarrow \theta - \eta\,\nabla_\theta L에서의 걸음 크기 η\eta이며, 코드에서는 lr=0.5입니다.
  • 역전파(Backpropagation) — 연쇄 법칙을 층별로 출력에서 입력 쪽으로 적용하여, 한 번의 역방향 계산으로 모든 그래디언트를 계산하는 방법입니다.
  • 델타 / 오차 신호(Delta / error signal) — 사전 활성화에 대한 손실의 도함수로, 출력에서는 δ(2)=(oy)o(1o)\delta^{(2)} = (o-y)\,o(1-o)이고 은닉층에서는 δj(1)=δ(2)Wj(2)hj(1hj)\delta^{(1)}_j = \delta^{(2)}W^{(2)}_j h_j(1-h_j)입니다. 역전파를 효율적으로 만드는, 재사용되는 수량입니다.
  • 보편 근사(Universal approximation) — 충분히 많은 시그모이드 유닛을 가진 은닉층 하나가 컴팩트 집합 위의 어떤 연속 함수든 임의의 정확도로 근사할 수 있다는 정리입니다. 훈련의 보장이 아니라 존재성 결과입니다.
  • 비볼록성(Non-convexity) — 네트워크의 손실 표면이 많은 국소 최소점을 갖는다는 속성으로, 이 때문에 경사 하강법은 반드시 전역 최적이 아닌 국소 최적을 찾게 됩니다.

이 다음으로 이어지는 것

은닉층은 우리가 대충 넘어가서는 안 될 무언가를 해냈습니다. 그것은 두 개의 원시 좌표를 네 개의 학습된 수치로 바꾸어 놓았고, 그 안에서 과제가 깔끔하게 갈라지는 새로운 좌표계를 만들어 냈습니다. 그 학습된 공간은 부수 효과가 아닙니다. 그것 자체로 연구할 가치가 있는 표현(representation)입니다. 다음 장인 임베딩: 기하학으로서의 의미가 정확히 그 일을 합니다. 그 장은 진행 예시의 대상들, 즉 학계 세계의 사람들과 논문들과 주제들을 가져와, 거리와 방향이 의미를 인코딩하는 벡터 공간 속의 점들로 배치합니다. 이 장이 하나의 패턴을 분리 가능하게 만들기 위해 공간을 구부렸다면, 다음 장은 의미 그 자체가 기하학 속에 산다는 것이 무엇을 뜻하는지를 묻습니다.