포스트

02. Machine Learning_Neural Network Basics (Perceptron, Loss, Gradient Descent, Backprop)

머신러닝및실습(2) 5~7주차 정리: 퍼셉트론과 XOR, 활성화 함수(step, sigmoid, tanh, ReLU, softmax), 순전파, 손실 함수(MSE, CEE)와 미니 배치, 수치 미분의 두 가지 문제, 편미분과 기울기, 경사하강법과 학습률, 오차역전파와 계산 그래프, MNIST 3층 신경망과 Keras 결과

02. Machine Learning_Neural Network Basics (Perceptron, Loss, Gradient Descent, Backprop)

학습 목표

  • 퍼셉트론의 정의와 단층 퍼셉트론의 한계(XOR), 다층 퍼셉트론
  • 활성화 함수가 필요한 이유와 함수별 특징, softmax의 overflow 대책
  • 신경망 학습의 의미, 손실 함수(MSE, CEE)와 미니 배치 CEE 코드
  • 수치 미분의 두 가지 문제와 해결책, 편미분과 기울기, 경사하강법과 학습률
  • 오차역전파: 계산 그래프, 국소적 계산, 연쇄법칙 (사과, 귤 예제를 계층 코드로 계산)
  • 범용 게이트: NAND 하나로 NOT, AND, OR, XOR, XNOR 만들기
  • MNIST를 직접 만든 3층 신경망과 Keras로 학습한 결과

별도 표시가 없는 코드는 numpy로 다시 실행한 결과. MNIST 학습 결과는 실습 노트북에서 실행한 출력


1. 퍼셉트론

  • 퍼셉트론: 뉴런이 입력 신호를 받아 일정 기준을 넘으면 신호를 내보내는 원리를 본뜬 인공 뉴런. 인공 신경망의 기초
  • 단층 퍼셉트론: 신호의 총합이 0을 넘으면 1, 아니면 0. 식으로 쓰면 \(y = 1 \text{ if } w_1x_1 + w_2x_2 + b > 0 \text{ else } 0\)
  • 가중치가 클수록 그 입력이 중요하다는 뜻이고, 편향은 얼마나 쉽게 활성화되는지를 조절함
  • AND, OR, NAND는 같은 구조에서 가중치와 편향만 바꿔 구현함
  • XOR은 직선 하나로 나눌 수 없어서(선형 분리 불가) 단층으로는 불가능 → 층을 쌓은 다층 퍼셉트론으로 해결
  • NAND만으로(또는 NOR만으로) 모든 논리 게이트를 만들 수 있음 (범용 게이트)
1
2
3
4
AND  = lambda a, b: perceptron(a, b, 0.5, 0.5, -0.7)
NAND = lambda a, b: perceptron(a, b, -0.5, -0.5, 0.7)
OR   = lambda a, b: perceptron(a, b, 0.5, 0.5, -0.2)
XOR  = lambda a, b: AND(NAND(a, b), OR(a, b))   # 2층
1
2
3
4
(0, 0) AND 0 NAND 1 OR 0 XOR 0
(1, 0) AND 0 NAND 1 OR 1 XOR 1
(0, 1) AND 0 NAND 1 OR 1 XOR 1
(1, 1) AND 1 NAND 0 OR 1 XOR 0

범용 게이트: NAND 하나로 전부 만들기

범용 게이트(Universal Gate)는 그것 하나만으로 모든 논리 게이트를 만들 수 있는 게이트. NAND(NOT + AND)와 NOR(NOT + OR)가 있고, 실제 회로에서는 NAND가 가장 많이 쓰임. 퍼셉트론 NAND 하나만 정의하고 나머지를 조합해 확인했음.

1
2
3
4
5
6
NAND = lambda a, b: int(-0.5 * a - 0.5 * b + 0.7 > 0)   # 퍼셉트론 하나
NOT  = lambda a: NAND(a, a)
AND  = lambda a, b: NOT(NAND(a, b))
OR   = lambda a, b: NAND(NOT(a), NOT(b))                 # 드모르간 법칙
XOR  = lambda a, b: AND(NAND(a, b), OR(a, b))
XNOR = lambda a, b: NOT(XOR(a, b))
1
2
3
4
5
a b | NOT(a) AND OR XOR XNOR  (모두 NAND만으로 구성)
0 0 | 1       0    0   0    1
0 1 | 1       0    1   1    0
1 0 | 0       0    1   1    0
1 1 | 0       1    1   0    1

퍼셉트론 하나(NAND)를 층으로 쌓기만 하면 XOR, XNOR까지 표현됨 → 다층 퍼셉트론은 이론상 어떤 논리 회로도 표현할 수 있음 (컴퓨터도 결국 NAND의 조합).


2. 활성화 함수

필요한 이유: 선형 함수만 쌓으면 아무리 깊어도 하나의 선형 함수와 같음. 예를 들어 h(x) = 2x를 세 번 쌓아도 8x 한 층과 같음. 활성화 함수로 비선형성을 넣어야 층을 쌓는 의미가 생김.

a Leaky ReLU는 모양이 보이도록 기울기 0.1로 그렸음 (보통 0.01)

함수정의특징
계단(step)0 이하면 0, 크면 1x = 0에서 불연속(미분 불가), 나머지 기울기가 모두 0이라 학습 불가
logistic sigmoid출력 0 ~ 1확률 출력(이진 분류)에 사용. 미분 최대값이 0.25라 깊으면 기울기 소실
tanh출력 -1 ~ 10 중심이라 sigmoid보다 학습이 안정적
ReLU0 이하면 0, 크면 x기울기가 0 또는 1이라 계산이 빠르고 기울기 소실을 줄임. 음수 구간은 기울기 0
Leaky ReLU0 이하면 0.01x, 크면 x음수 구간에도 작은 기울기를 남김
softmax출력 총합이 1다중 클래스 분류의 출력층
1
2
3
4
5
6
step   : [0 0 0 1 1]
sigmoid: [0.119203 0.377541 0.5      0.622459 0.880797]
tanh   : [-0.964028 -0.462117  0.        0.462117  0.964028]
ReLU   : [0.  0.  0.  0.5 2. ]
Leaky  : [-0.02  -0.005  0.     0.5    2.   ]
sigmoid'(0) = 0.25 / 최대값이 0.25라 층이 깊으면 기울기가 작아짐

softmax의 overflow

지수 함수를 쓰기 때문에 입력이 크면 inf / inf = nan이 됨. 각 원소에서 최대값을 빼도 결과가 같으므로 최대값을 빼고 계산함.

1
2
3
def softmax(a):
    e = np.exp(a - np.max(a))   # [1000, 1000, 1002] - 1002 = [-2, -2, 0]
    return e / np.sum(e)
1
2
naive : [nan nan nan]
stable: [0.106507 0.106507 0.786986] sum = 1.0

3. 신경망과 순전파

  • 구조: 입력층 → 은닉층(여러 개 가능) → 출력층
  • 순전파: 입력이 층을 따라 순서대로 전달되며 최종 출력을 만드는 과정 (행렬곱 + 편향 + 활성화의 반복)
  • 역전파: 순전파에서 생긴 오차를 줄이도록 출력층에서 입력층 방향으로 미분을 전달하며 가중치를 갱신하는 과정
1
2
3
z1 = sigmoid(x @ W1 + b1)    # (2,) @ (2, 3) -> (3,)
z2 = sigmoid(z1 @ W2 + b2)   # (3,) @ (3, 2) -> (2,)
y  = sigmoid(z2 @ W3 + b3)   # (2,) @ (2, 2) -> (2,)
1
z1: [0.574443 0.668188 0.75026 ] z2: [0.626249 0.771011] y: [0.578551 0.667362]

실습 노트북에서 계산한 값(z1 = [0.5744 0.6682 0.7503], y = [0.5786 0.6674])과 같음. 미리 학습된 가중치(784 → 50 → 100 → 10)로 MNIST 테스트 10,000장을 추론했을 때 정확도는 93.52%.


4. 손실 함수와 미니 배치

  • 신경망 학습: 데이터를 보고 손실 함수 값이 가장 작아지는 가중치와 편향을 자동으로 찾는 것. 손실 함수의 기울기(미분)를 이용해 매개변수를 조금씩 갱신함
  • 훈련 데이터와 시험 데이터를 나누는 이유: 처음 보는 데이터에 대한 범용 성능을 확인하기 위해. 모델이 복잡해질수록 훈련 오차는 계속 줄지만 시험 오차는 줄다가 과적합 구간부터 다시 커짐
  • 손실 함수: 예측값과 실제값의 차이(손실)를 수치로 나타낸 함수. 작을수록 학습이 잘 된 것
  • 회귀는 MSE(오차 제곱의 평균), 분류는 CEE(교차 엔트로피, 자연로그). one-hot 정답이면 CEE는 정답 클래스 확률에만 의존하며, 식으로 쓰면 \(E = -\log y_{k}\) (k는 정답 클래스)
1
2
3
4
5
6
def cross_entropy_error(y, t):          # 미니 배치 대응 (출제 예고)
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
    batch_size = y.shape[0]
    return -np.sum(t * np.log(y + 1e-7)) / batch_size
1
2
3
4
MSE  정답 확률 0.6: 0.0975 / 0.1: 0.5975
CEE  정답 확률 0.6: 0.5108 / 0.1: 2.3026 / -log(0.6) = 0.5108
batch CEE (2개 평균): 1.4067
label 버전: 1.4067
  • 정답 확률이 0.6에서 0.1로 떨어지면 CEE가 0.51에서 2.30으로 크게 늘어남
  • 1e-7(delta)은 log(0) = -inf를 막기 위한 값. 정답이 레이블 숫자(one-hot이 아님)라면 y[np.arange(batch_size), t]로 정답 확률만 골라 계산함
  • *는 원소별 곱, np.dot은 행렬곱. one-hot과의 원소별 곱 + 합은 결과적으로 정답 확률만 남김
  • 미니 배치 학습: 전체(배치) 대신 무작위로 뽑은 일부(미니 배치)로 기울기를 구해 갱신하는 것. 이를 반복하는 방법이 미니 배치 확률적 경사하강법(SGD)

5. 수치 미분, 편미분, 기울기

수치 미분의 두 가지 문제 (서술형)

문제원인해결
반올림 오차h = 10e-50처럼 너무 작으면 x + h가 x와 같아져 결과가 0h = 1e-4 정도 사용
차분 오차전진차분은 x와 x+h 사이의 기울기라 진짜 접선 기울기와 차이가 생김중심차분 사용

식으로 쓰면 중심차분은 \(f'(x) \approx \frac{f(x+h) - f(x-h)}{2h}\)

1
2
3
def numerical_diff(f, x):
    h = 1e-4
    return (f(x + h) - f(x - h)) / (2 * h)   # 분자 전체를 괄호로 묶어야 함
1
2
3
4
5
np.float32(1e-50) = 0.0
x + h == x ? True -> 전진차분 결과: 0.0
전진차분 h=1e-4: 0.20000099999917254
중심차분 h=1e-4: 0.1999999999990898 / 해석해 0.02x+0.1 = 0.2
괄호 틀린 식 (f(x+h) - f(x-h) / (2*h)): -3749.1499804999003

f(x) = 0.01x² + 0.1x의 x = 5에서 진짜 미분값은 0.2. 중심차분이 전진차분보다 정확하고, 괄호를 빠뜨리면 완전히 다른 값이 나옴.

편미분과 기울기

  • 편미분: 변수가 여러 개인 함수에서 목표 변수 하나만 남기고 나머지를 상수로 보고 미분하는 것
  • 기울기(gradient): 모든 변수의 편미분을 벡터로 묶은 것. numerical_gradient는 변수마다 중심차분을 구함
1
2
수치 기울기 (3, 4): [ 516.       1936.000001]
해석해: d/dx0 = 12x0^3 + 4x0x1^2 = 516 , d/dx1 = 4x0^2x1 + 28x1^3 = 1936

6. 경사하강법과 학습률

  • 기울기가 양수면 왼쪽(감소 방향), 음수면 오른쪽으로 이동 → 식으로 쓰면 \(w \leftarrow w - \eta \frac{\partial L}{\partial w}\)
  • 목적은 손실 함수가 최소가 되는 매개변수를 찾는 것
  • 학습률: 한 번 갱신할 때 기울기 방향으로 얼마나 이동할지 정하는 값 (하이퍼파라미터)
1
2
3
lr=10: [-9.374161e+13  2.051099e+15]
lr=0.001: [-0.585389  0.354076]
lr=1e-08: [-2.999484  3.998065]
학습률결과
10 (너무 큼)값이 10의 15제곱 수준으로 발산
0.001 (적절)100번 만에 최소점 (0, 0) 쪽으로 이동
1e-8 (너무 작음)시작점 (-3, 4)에서 거의 움직이지 않음

7. 오차역전파

  • 계산 그래프: 계산 과정을 노드와 엣지로 표현한 그래프
  • 국소적 계산: 각 노드는 자기와 직접 연결된 입력, 출력만 보고 계산 → 전체가 복잡해도 문제가 단순해짐
  • 역전파는 출력 쪽(오른쪽)에서 입력 쪽(왼쪽)으로 국소적 미분을 연쇄법칙으로 곱해 전달하는 과정
  • 덧셈 노드는 미분을 그대로 전달, 곱셈 노드는 입력을 서로 바꿔 곱해 전달

계산 그래프 예제: 사과와 귤 (강의 문제)

문제 1: 1개에 100원인 사과를 2개 샀고 소비세 10%가 붙는다. 지불 금액은? 문제 2: 사과 2개(100원), 귤 3개(150원)를 샀고 소비세 10%가 붙는다. 지불 금액은?

flowchart LR
    A["사과 100"] --> M1(("×"))
    AN["사과 개수 2"] --> M1
    O["귤 150"] --> M2(("×"))
    ON["귤 개수 3"] --> M2
    M1 -->|200| P(("+"))
    M2 -->|450| P
    P -->|650| M3(("×"))
    T["소비세 1.1"] --> M3
    M3 -->|715| R["지불 금액"]

곱셈 노드와 덧셈 노드를 계층(클래스)으로 만들어 순전파 후 역전파를 해 봤음.

1
2
3
4
5
6
class MulLayer:
    def forward(self, x, y): self.x, self.y = x, y; return x * y
    def backward(self, d): return d * self.y, d * self.x       # 입력을 서로 바꿔 곱함
class AddLayer:
    def forward(self, x, y): return x + y
    def backward(self, d): return d, d                          # 그대로 흘려보냄
1
2
3
순전파: 사과 200 + 귤 450 = 650 → 지불금액 715.0
역전파: d사과가격 2.2 d사과개수 110.0 d귤가격 3.3 d귤개수 165.0 d소비세 650
문제1: 지불금액 220.0 / dz/da = 2.2 dz/db = 110.0 dz/dc = 200
미분값의미
지불 금액 / 사과 가격2.2사과 값이 1원 오르면 지불 금액은 2.2원 오름 (개수 2 × 세금 1.1)
지불 금액 / 사과 개수110사과를 하나 더 사면 110원 더 냄
지불 금액 / 귤 가격3.3귤 개수 3 × 세금 1.1
지불 금액 / 소비세650세율이 1(100%)만큼 오르면 세전 금액 650원만큼 오름

신경망의 역전파도 이와 똑같이, 출력(손실)에서 시작해 각 노드의 국소적 미분을 곱해 가며 모든 가중치의 기울기를 한 번의 역방향 계산으로 구함. 수치 미분은 가중치마다 함수를 두 번씩 계산해야 해서 훨씬 느림.

  • Sigmoid 계층의 역전파: 식으로 쓰면 \(\frac{\partial y}{\partial x} = y(1 - y)\) 이라서 순전파 출력만 저장하면 됨
  • Softmax-with-Loss(Softmax + CEE)의 역전파는 깔끔하게 y − t
1
2
해석 y(1-y): [0.244458] / 수치: [0.244458]
softmax+CEE 역전파 (y - t): [ 0.018211  0.245192 -0.263403]

MNIST 3층 신경망 (실습 노트북 결과)

Affine → Sigmoid → Affine → Sigmoid → Affine → SoftmaxWithLoss 구조(784-50-100-10)를 직접 구현하고 오차역전파로 학습. 배치 50, 학습률 0.1, 50,000번 반복 = 60,000 / 50 = 1,200번이 1 epoch이라 약 42 epoch.

1
2
3
4
5
6
7
Epoch : 1 => train 정확도 : 9.75 % / test 정확도 : 9.74 % / loss : 2.2736
Epoch : 4 => train 정확도 : 29.79 % / test 정확도 : 29.85 % / loss : 1.9107
Epoch : 7 => train 정확도 : 84.57 % / test 정확도 : 84.9 % / loss : 0.5045
Epoch : 10 => train 정확도 : 91.58 % / test 정확도 : 91.5 % / loss : 0.193
Epoch : 20 => train 정확도 : 96.29 % / test 정확도 : 95.81 % / loss : 0.0949
Epoch : 30 => train 정확도 : 97.74 % / test 정확도 : 96.57 % / loss : 0.0244
Epoch : 42 => train 정확도 : 98.47 % / test 정확도 : 96.79 % / loss : 0.0114

b epoch마다 마지막 미니 배치의 loss (배치 하나의 값이라 들쭉날쭉함)

c train과 test 정확도

  • 처음 3 epoch은 10% 근처(찍는 수준)에 머무름. 가중치를 0.01 × 표준정규분포로 작게 초기화했고 sigmoid라서 기울기가 작기 때문
  • 후반에는 train 98.5%, test 96.8%로 약 1.7%p 차이가 생김 → 가벼운 과적합의 시작

Keras로 같은 문제 (실습 노트북 결과)

Flatten(28×28) → Dense(128, sigmoid) → Dense(10, softmax), optimizer adam, 10 epoch. 파라미터는 784×128 + 128 = 100,480과 128×10 + 10 = 1,290을 더해 101,770개.

1
2
3
4
5
Epoch 1/10 - accuracy: 0.8969 - loss: 0.3984
Epoch 5/10 - accuracy: 0.9753 - loss: 0.0874
Epoch 10/10 - accuracy: 0.9904 - loss: 0.0364
loss값 :  0.07481884211301804
모델 정확도 :  97.74 %

Adam 덕분에 1 epoch만에 89.7%에 도달하고, 테스트 정확도도 직접 만든 SGD 모델(96.79%)보다 높음.


정리 노트에서 정정한 부분 정리

위치원래정정이유
수치 미분 코드(f(x+h) - f(x) / h)(f(x + h) - f(x)) / h괄호 위치. 실행하면 전혀 다른 값
중심차분 코드(f(x+h) - f(x - h) / (2*h))(f(x + h) - f(x - h)) / (2 * h)괄호를 빠뜨리면 -3749.15가 나옴 (실행 결과)
반올림 오차 설명계산하면 0으로 출력x + h가 x와 같아져 분자가 010e-50은 1e-49이고, 5.0 + 1e-49 == 5.0이 True
편미분 예제x0 편미분에서 partial_diff(function_2, 3) 호출function_1 호출x0 함수는 function_1. 바르게 하면 516, x1은 1936
경사하강법손실 함수가 최대일 때 가중치를 찾음손실 함수가 최소가 되는 가중치오타
오차역전파왼쪽에서 오른쪽으로 전달오른쪽(출력)에서 왼쪽(입력)으로 전달역전파의 방향
신경망 구조입력층, 은닉층, 입력층입력층, 은닉층, 출력층오타
미니 배치 CEEnp.log(y)np.log(y + 1e-7)y에 0이 있으면 -inf가 됨
강의 자료 계산 그래프 (문제 1)최종 결과 z(= 200)z = 100 × 2 × 1.1 = 220200은 세금 적용 전 금액(a × b). 실행 결과도 220
ReLU미분값이 항상 0과 1이라 빠름x > 0이면 1, x < 0이면 0 (x = 0은 보통 0으로 정함)정의를 명확하게

최종 핵심 정리

  1. 퍼셉트론은 가중합이 기준을 넘으면 1을 내는 인공 뉴런이고, 단층은 선형이라 XOR이 불가능해 다층으로 해결함
  2. 선형 함수를 쌓아도 선형이므로 활성화 함수로 비선형성을 넣어야 하며, 계단 함수는 미분 불가와 기울기 0 때문에 쓰지 않음
  3. ReLU는 0 이하면 0, 크면 x이고, softmax는 출력 합이 1이며 overflow를 막으려고 최대값을 빼고 계산함
  4. 신경망 학습은 손실 함수를 최소화하는 매개변수를 기울기로 찾는 과정이고, 회귀는 MSE, 분류는 CEE를 씀
  5. 미니 배치 CEE는 배치 크기로 나눈 평균이며 log(0)을 막기 위해 아주 작은 delta를 더함
  6. 수치 미분은 반올림 오차(h를 1e-4로)와 차분 오차(중심차분으로)를 해결해야 함
  7. 학습률이 너무 크면 발산, 너무 작으면 거의 움직이지 않고, 적절하면 최소점으로 수렴함
  8. 오차역전파는 계산 그래프의 국소적 미분을 출력에서 입력 방향으로 연쇄법칙으로 전달하며, Softmax-with-Loss의 역전파는 y − t

End.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.