LLM 엔지니어링2장 · 신경망과 딥러닝의 기본 원리7 / 23← 이전목차다음 →MetaContents

2장. 신경망과 딥러닝의 기본 원리

순전파

결재 서류가 올라가는 길을 다시 떠올려 보세요. 서류는 담당, 팀장, 부장을 차례로 거치며 의견이 붙고, 맨 위에서 결정이 나옵니다. 입력이 층을 차례로 지나 예측이 되는 계산을 순전파라고 합니다. 앞으로(순) 전해진다(전파)는 뜻입니다. 영어로는 forward propagation(앞으로 퍼뜨리기)이라고 하고, 줄여서 forward pass(앞으로 한 번 지나가기)라고도 부릅니다. PyTorch 에서 모델의 계산을 적는 메서드 이름이 forward 인 것도 여기서 왔습니다.

입력, 은닉층 1, 은닉층 2, 출력으로 이어지며 층마다 가중합과 ReLU 를 거치는 흐름도

상자마다 하는 일은 같습니다. 앞 층의 값을 받아 가중합을 내고 ReLU 를 지나 다음 층으로 넘깁니다. 마지막 출력 상자만 ReLU 없이 가중합을 그대로 예측 주문량으로 냅니다.

층 하나의 계산

층 하나의 계산은 행렬 곱 한 번입니다. 뉴런이 여럿이어도 한꺼번에 계산합니다.

단계식모양(앞 절의 7 → 32 → 16 → 1 신경망, 주문 한 건)
입력x(1, 7)
은닉층 1ReLU(x × W1 + b1)(1, 7) × (7, 32) → (1, 32)
은닉층 2ReLU(h1 × W2 + b2)(1, 32) × (32, 16) → (1, 16)
출력h2 × W3 + b3(1, 16) × (16, 1) → (1, 1)

모양의 규칙은 하나입니다. 앞 행렬의 열 수와 뒤 행렬의 행 수가 같아야 곱할 수 있고, 결과는 (앞의 행 수, 뒤의 열 수)가 됩니다.

h1, h2 는 무엇인가

h 는 은닉층(hidden layer)의 첫 글자입니다. h1 은 은닉층 1이 내놓은 값, h2 는 은닉층 2가 내놓은 값입니다. 둘 다 가중합이 ReLU 를 지난 뒤의 값이고, 바로 다음 층의 입력이 됩니다. 결재 라인으로 치면 h1 은 담당자들이 올린 의견 메모, h2 는 팀장들이 그 메모를 모아 다시 정리한 의견입니다. 부장은 h2 만 보고 결정합니다.

이름뜻결재 라인으로 치면모양(주문 한 건)
x입력. 주문 한 건의 특성올라온 서류(1, 7)
W1, b1은닉층 1의 가중치와 편향담당자들의 판단 기준(7, 32), (32,)
h1은닉층 1의 출력 = ReLU(x × W1 + b1)담당자 32명의 의견(1, 32)
h2은닉층 2의 출력 = ReLU(h1 × W2 + b2)팀장 16명이 정리한 의견(1, 16)
y_hat출력층의 값 = 예측부장의 결정(1, 1)

h1, h2 는 데이터에도 정답에도 없는 값입니다. 입력을 넣으면 계산 도중에만 생겼다가 다음 층으로 넘어갑니다. 바깥에서 보이지 않는 층이라 "은닉"이라고 부르는 것과 같은 까닭입니다. y_hat 의 hat 은 영어로 "모자"입니다. 수학에서 정답 y 위에 모자(^)를 씌워 "y 의 예측값"을 나타내는 데서 왔습니다(ŷ, "와이 햇"이라고 읽습니다).

NumPy 로 순전파

위 표의 신경망은 뉴런이 수십 개라 숫자를 눈으로 따라갈 수 없습니다. 그래서 같은 계산을 아주 작은 신경망으로 해 봅니다. 입력은 "인공 뉴런" 절의 점수 셋(거리, 비, 저녁), 은닉층은 뉴런 2개, 출력층은 뉴런 1개입니다. 결재 라인으로 치면 담당자 두 명(담당 A, B)과 부장 한 명입니다. 은닉층이 하나뿐이라 코드에는 h1 만 있고 h2 는 없습니다. 은닉층의 가중합은 z1, ReLU 를 지난 값은 h1 으로 나눠 적어, 활성화 함수 앞뒤의 값을 함께 볼 수 있게 했습니다.

입력 셋이 은닉층의 담당 A, B 를 거쳐 지연 위험 점수 2.3 이 되는 작은 신경망 그림

그림은 주문 1(먼 곳, 비 오는 날)이 왼쪽에서 오른쪽으로 지나가는 모습입니다. 주황 선이 굵을수록 가중치가 큽니다. 담당 A 는 비 점수에 큰 가중치(2.0)를 둔 "날씨 담당"이라 1.4 를 올리고, 담당 B 는 저녁 점수에 큰 가중치를 둔 "저녁 담당"이라 낮 주문에는 문턱을 넘지 못해 0 을 올립니다. 부장(출력층)은 두 의견에 1.5 와 1.0 을 곱해 더하고 편향 0.2 를 더해 2.3 을 냅니다.

가중치는 설명을 위해 사람이 정한 값입니다. 실제 신경망은 이 값들을 학습으로 찾고, 담당 A·B 처럼 사람이 붙일 이름이 생기지 않는 경우가 많습니다.

Python
import numpy as np

# 주문 두 건. 한 줄이 주문 하나, 칸은 [거리 점수, 비 점수, 저녁 점수]
X = np.array([[0.8, 1.0, 0.0],    # 주문 1: 먼 곳, 비 오는 날
              [0.2, 0.0, 1.0]])   # 주문 2: 가까운 곳, 저녁 주문

# 은닉층: 뉴런 2개. W1 의 한 열이 뉴런 하나의 가중치다
#           담당A  담당B
W1 = np.array([[0.5, 0.2],        # 거리 점수에 곱하는 수
               [2.0, 0.0],        # 비 점수에 곱하는 수
               [0.0, 2.0]])       # 저녁 점수에 곱하는 수
b1 = np.array([-1.0, -0.5])       # 뉴런마다의 편향

# 출력층: 뉴런 1개. 담당 A, B 의 의견에 곱하는 수
W2 = np.array([[1.5],
               [1.0]])
b2 = np.array([0.2])

# 1단계: 은닉층의 가중합. 주문마다 담당 A, B 의 점수가 나온다
z1 = X @ W1 + b1
print("은닉층 가중합\n", z1.round(2))

# 2단계: ReLU. 음수(문턱을 못 넘은 의견)는 0 으로 지운다
h1 = np.maximum(0, z1)
print("은닉층 출력\n", h1.round(2))

# 3단계: 출력층. 두 담당의 의견을 모아 지연 위험 점수 하나를 낸다
y_hat = h1 @ W2 + b2
print("지연 위험 점수\n", y_hat.round(2))
print("모양:", X.shape, "→", h1.shape, "→", y_hat.shape)

실행하면 이렇게 나옵니다.

단계주문 1(먼 곳, 비)주문 2(가까운 곳, 저녁)
은닉층 가중합 [담당 A, 담당 B][1.4, -0.34][-0.9, 1.54]
ReLU 를 지난 값[1.4, 0][0, 1.54]
지연 위험 점수(출력)2.31.74

모양은 (2, 3) → (2, 2) → (2, 1) 입니다. 주문 2건 × 특성 3개가 주문 2건 × 담당 2명의 의견이 되고, 마지막에 주문 2건 × 점수 1개가 됩니다.

지연 위험 점수는 무엇인가

출력층이 낸 2.3 과 1.74 를 이 예에서는 "지연 위험 점수"라고 불렀습니다. 미세먼지 지수를 떠올리면 쉽습니다. 지수가 클수록 공기가 나쁘다는 뜻이지만, 지수 숫자 자체가 "몇 %"를 말하지는 않습니다. 지연 위험 점수도 "클수록 늦을 걱정이 크다"는 것만 알려 주는 숫자입니다. 여기서 부장(출력층)이 서류를 보고 매긴 걱정의 크기가 이 점수입니다.

물음답
어떻게 읽나클수록 늦을 걱정이 크다. 주문 1(2.3)이 주문 2(1.74)보다 더 걱정스럽다
확률인가아니다. 0 과 1 사이로 묶여 있지 않아 2.3 이 230% 를 뜻하지 않는다
확률로 바꾸려면"활성화 함수" 절의 시그모이드를 씌운다. 2.3 은 0.909, 1.74 는 0.851 이 된다
지금 믿어도 되나아니다. 가중치를 설명을 위해 사람이 정했으므로, 실제 지연과 맞는지는 아직 모른다

"지연 위험 점수"라는 이름도 설명을 위해 붙인 것입니다. 신경망 스스로는 숫자 하나를 낼 뿐이고, 그 숫자에 뜻을 주는 것은 정답과 견주는 학습입니다. 정답이 "늦음(1), 정시(0)"인 데이터로 학습하면 이 점수는 늦을수록 커지도록 맞춰집니다.

이 코드가 뜻하는 것

  • 행렬 곱 한 줄이 뉴런 여럿의 계산입니다. X @ W1 + b1 이 그 한 줄입니다. "인공 뉴런" 절에서는 뉴런 하나를 np.dot(x, w) + b 로 계산했습니다. 여기서는 W1 의 열 하나가 뉴런 하나의 가중치이므로, 행렬 곱 한 번에 담당 A 와 B 의 가중합이 함께 나옵니다. 주문 1의 담당 A 는 0.8×0.5 + 1.0×2.0 + 0.0×0.0 - 1.0 = 1.4 입니다.
  • ReLU 가 "누가 의견을 낼지"를 가릅니다. 주문 1에서는 담당 A 만, 주문 2에서는 담당 B 만 0 이 아닌 값을 넘깁니다. 같은 신경망이라도 주문에 따라 일하는 뉴런이 달라지는 것입니다. 활성화 함수 없이 직선만 이어 붙였다면 이런 나눔이 생기지 않습니다.
  • 층마다 하는 일은 똑같습니다. 앞 층의 값을 받아 가중합 → 활성화 함수를 거쳐 다음 층에 넘깁니다. 출력층만 활성화 함수 없이 가중합을 그대로 냅니다(h1 @ W2 + b2). 위 표의 7 → 32 → 16 → 1 신경망도 이 계산을 층 수만큼 되풀이할 뿐입니다. 은닉층이 둘이면 h1 을 받아 h2 를 만들고, h2 를 받아 y_hat 을 냅니다.
  • 주문 여러 건을 한 번에 넣습니다. X 의 한 줄이 주문 하나라서, 주문이 1000건이어도 코드는 그대로이고 결과만 1000줄이 됩니다. 이렇게 묶어 넣는 것이 배치입니다.
  • 아직 "맞는" 답은 아닙니다. 2.3 과 1.74 는 정해 둔 가중치로 계산한 점수일 뿐입니다. 이 점수가 정답과 얼마나 다른지 재는 것이 다음 절의 손실 함수이고, 가중치를 고쳐 점수를 정답에 가깝게 만드는 것이 학습입니다.

주의할 점

  • 뉴런이 많은 신경망으로 늘려도 식은 같습니다. 입력 모양이 (건수, 7)이 되고 결과는 (건수, 1)이 됩니다. 학습 전의 가중치는 사람이 정하지 않고 작은 난수로 채웁니다.
  • 모양이 맞지 않으면 오류가 납니다. 신경망 코드의 오류는 대부분 모양 오류이니, 모르겠으면 .shape 부터 찍어 봅니다.
← 이전 절2장 · 층을 쌓은 신경망다음 절 →2장 · 손실 함수