Skip to main content
Summary

이 장에서 배울 것

이 장의 목표는 신경망을 막연한 상자가 아니라 계산 가능한 구조로 이해하는 것이다. 딥러닝의 출발점은 매우 단순하다.

입력값이 들어온다. 각 입력값에 가중치를 곱한다. 편향을 더한다. 그 결과를 활성화 함수에 통과시킨다. 이 계산을 여러 개 쌓으면 신경망이 된다.

이 장에서는 다음을 배운다.

  • 인공 뉴런을 입력값, 가중치, 편향, 가중합, 활성화 함수로 나누어 이해한다.

  • 퍼셉트론이 어떻게 0 또는 1을 예측하는지 작은 숫자로 계산한다.

  • 단층 퍼셉트론이 만드는 결정 경계가 왜 직선인지 이해한다.

  • XOR 문제가 왜 단층 퍼셉트론으로 풀리지 않는지 살펴본다.

  • 은닉층과 비선형 활성화 함수가 왜 필요한지 직관적으로 이해한다.

  • NumPy로 작은 다층 퍼셉트론의 순전파를 직접 구현한다.

  • PyTorch의 nn.Module로 같은 구조를 만들고 결과를 비교한다.

여기서는 아직 학습, 즉 가중치를 자동으로 고치는 과정까지 깊게 들어가지 않는다. 이번 장에서는 먼저 "신경망이 입력을 받아 출력을 만드는 과정"인 순전파를 정확히 이해한다.

퍼셉트론과 신경망

왜 이 주제가 중요한가

딥러닝 모델은 복잡해 보인다. 이미지 모델, 언어 모델, Transformer 모델은 수많은 층과 파라미터를 가진다. 하지만 가장 안쪽으로 들어가면 같은 계산이 반복된다.

입력 → 가중치 곱셈 → 편향 더하기 → 활성화 함수 → 출력

CNN, RNN, Transformer도 이 흐름을 벗어나지 않는다. 구조가 달라지고 데이터의 모양이 달라질 뿐이다. 그래서 퍼셉트론과 신경망의 기본 계산을 이해하면 이후 장의 내용을 훨씬 편하게 받아들일 수 있다.

초보자가 딥러닝에서 자주 막히는 지점은 "모델이 뭔가 알아서 한다"는 느낌이다. 하지만 모델은 알아서 생각하지 않는다. 숫자를 계산한다. 예측이 틀리면 손실이 커지고, 다음 장에서 배울 역전파를 통해 가중치가 조금씩 바뀐다. 이 장은 그중 예측이 만들어지는 앞부분을 다룬다.

핵심 개념 설명

인공 뉴런

인공 뉴런은 생물학적 뉴런에서 영감을 받은 계산 단위다. 하지만 실제 뇌를 그대로 복사한 것은 아니다. 딥러닝에서 뉴런은 다음 계산을 하는 작은 함수에 가깝다.

z = x1w1 + x2w2 + ... + xnwn + b
y = f(z)

각 기호의 뜻은 다음과 같다.

기호이름뜻
x입력값모델에 들어오는 숫자
w가중치각 입력을 얼마나 중요하게 볼지 정하는 값
b편향전체 기준점을 이동시키는 값
z가중합입력과 가중치를 곱해 더한 값
f활성화 함수가중합을 최종 출력으로 바꾸는 함수
y출력뉴런의 계산 결과

작은 예를 보자. 어떤 뉴런이 두 입력을 받는다고 하자.

x1 = 2, x2 = 3
w1 = 0.5, w2 = -1.0
b = 1

가중합은 다음과 같다.

z = 2 × 0.5 + 3 × (-1.0) + 1
  = 1 - 3 + 1
  = -1

활성화 함수가 "0보다 크면 1, 아니면 0"을 출력하는 계단 함수라면 결과는 0이다.

f(z) = 1 if z > 0 else 0
f(-1) = 0

여기서 가중치 w1을 2.0으로 바꾸면 어떻게 될까?

z = 2 × 2.0 + 3 × (-1.0) + 1
  = 4 - 3 + 1
  = 2
f(2) = 1

입력은 그대로인데 가중치 하나가 바뀌자 출력이 0에서 1로 바뀌었다. 이처럼 가중치는 입력의 영향력을 조절한다. 편향은 기준선을 움직인다. 같은 입력과 가중치라도 편향이 커지면 1이 나오기 쉬워지고, 편향이 작아지면 0이 나오기 쉬워진다.

퍼셉트론

퍼셉트론은 가장 단순한 인공 뉴런 모델이다. 여러 입력을 받아 가중합을 계산한 뒤, 계단 함수로 0 또는 1을 출력한다.

예를 들어 공부 시간과 수면 시간을 보고 시험 합격 여부를 예측한다고 하자.

x1 = 공부 시간
x2 = 수면 시간
출력 = 합격이면 1, 불합격이면 0

퍼셉트론은 다음과 같은 식으로 판단한다.

z = x1w1 + x2w2 + b
if z > 0: 합격
else: 불합격

이 식은 2차원 평면에서 직선을 만든다.

x1w1 + x2w2 + b = 0

이 직선을 기준으로 한쪽은 1, 다른 한쪽은 0으로 나뉜다. 이를 결정 경계라고 한다. 결정 경계는 모델이 클래스를 나누는 기준선이다.

z 값위치예측
z > 0직선의 한쪽1
z = 0결정 경계 위경계
z < 0직선의 반대쪽0

즉 단층 퍼셉트론은 데이터를 직선 하나로 나누는 모델이다. 입력 변수가 3개라면 평면으로 나누고, 입력 변수가 더 많으면 초평면으로 나눈다. 초평면은 고차원 공간에서의 직선 또는 평면이라고 생각하면 된다.

AND 문제를 퍼셉트론으로 풀기

논리 연산 AND는 두 입력이 모두 1일 때만 1을 출력한다.

x1x2AND
000
010
100
111

다음 가중치와 편향을 사용해 보자.

w1 = 1
w2 = 1
b = -1.5

계산하면 다음과 같다.

x1x2z = x1w1 + x2w2 + b출력
00-1.50
01-0.50
10-0.50
110.51

잘 작동한다. OR 문제도 퍼셉트론으로 풀 수 있다.

w1 = 1
w2 = 1
b = -0.5
x1x2z출력
00-0.50
010.51
100.51
111.51

AND와 OR은 한 직선으로 0과 1을 나눌 수 있다. 이런 문제를 선형 분리 가능하다고 한다.

XOR 문제와 단층 퍼셉트론의 한계

XOR은 두 입력이 서로 다를 때 1을 출력한다.

x1x2XOR
000
011
101
110

점을 평면에 찍어 보면 1인 점은 대각선 방향으로 떨어져 있다.

(0,1) → 1        (1,1) → 0

(0,0) → 0        (1,0) → 1

이 네 점을 직선 하나로 0과 1로 나눌 수 있을까? 불가능하다. 어떤 직선을 그어도 한쪽에 있어야 할 점이 섞인다.

이것이 단층 퍼셉트론의 한계다. 단층 퍼셉트론은 직선 하나로 나눌 수 있는 문제에는 강하지만, XOR처럼 비선형 구조를 가진 문제에는 약하다. 비선형이란 직선 하나로 설명되지 않는 관계를 뜻한다.

이 한계를 넘기 위해 필요한 것이 은닉층과 비선형 활성화 함수다.

다층 퍼셉트론과 은닉층

다층 퍼셉트론, 즉 MLP는 퍼셉트론을 여러 층으로 쌓은 신경망이다. 입력층과 출력층 사이에 은닉층이 들어간다.

입력층 → 은닉층 → 출력층

은닉층은 입력을 새로운 표현으로 바꾸는 역할을 한다. 예를 들어 원래 입력이 (x1, x2)라면 은닉층은 이를 (h1, h2, h3, ...) 같은 새로운 특징으로 변환한다. 이 변환 덕분에 출력층은 더 쉽게 분류할 수 있다.

XOR 문제를 직관적으로 생각해 보자. 원래 좌표에서는 0과 1이 대각선으로 섞여 있어 직선 하나로 나눌 수 없다. 하지만 은닉층이 입력을 다른 공간으로 옮겨 놓으면, 출력층은 그 공간에서 더 단순한 기준으로 나눌 수 있다.

TensorFlow Playground 같은 시각화 도구를 보면 은닉층의 뉴런들이 각자 다른 경계를 만든다. 어떤 뉴런은 왼쪽과 오른쪽을 나누고, 어떤 뉴런은 위와 아래를 나눈다. 여러 뉴런의 결과가 합쳐지면 네모, 곡선, 섬 모양 같은 복잡한 영역도 표현할 수 있다.

중요한 점은 은닉층만 많다고 항상 좋은 것은 아니라는 것이다. 은닉층이 있어도 모든 층이 단순한 선형 계산만 한다면 전체 모델도 결국 하나의 선형 모델과 같다. 그래서 비선형 활성화 함수가 필요하다.

활성화 함수가 필요한 이유

활성화 함수는 가중합 z를 다음 층으로 넘길 값으로 바꾼다. 대표적인 활성화 함수는 다음과 같다.

함수식 또는 동작특징
계단 함수z > 0이면 1, 아니면 0퍼셉트론의 기본 형태
시그모이드0과 1 사이 값 출력확률처럼 해석하기 쉬움
tanh-1과 1 사이 값 출력중심이 0에 가까움
ReLUmax(0, z)딥러닝에서 널리 사용

ReLU는 음수는 0으로 만들고 양수는 그대로 통과시킨다.

ReLU(-2) = 0
ReLU(0.5) = 0.5
ReLU(3) = 3

언뜻 보면 너무 단순해 보이지만, 이 비선형 꺾임이 신경망의 표현력을 크게 높인다. 각 층이 선형 변환을 하고 ReLU 같은 비선형 함수를 거치면, 신경망은 여러 개의 조각난 선형 영역을 조합해 복잡한 패턴을 표현할 수 있다.

쉬운 비유 또는 사례

신경망을 여러 명의 심사위원이 있는 평가 과정으로 생각해 보자.

지원자의 합격 여부를 판단한다고 하자. 입력은 다음과 같다.

x1 = 시험 점수
x2 = 면접 점수
x3 = 프로젝트 경험

한 명의 심사위원은 각 항목에 중요도를 준다.

시험 점수는 0.6만큼 중요하다.
면접 점수는 0.3만큼 중요하다.
프로젝트 경험은 0.8만큼 중요하다.
기본 기준은 -1.2다.

이 중요도가 가중치이고, 기본 기준이 편향이다. 심사위원은 점수를 합산한 뒤 일정 기준을 넘으면 합격이라고 판단한다. 이것이 하나의 뉴런이다.

그런데 실제 판단은 더 복잡하다. 어떤 심사위원은 "기초 실력"을 보고, 다른 심사위원은 "협업 가능성"을 보고, 또 다른 심사위원은 "성장 가능성"을 본다. 이 중간 판단들이 은닉층이다. 마지막 심사위원은 이 중간 판단들을 종합해 최종 결정을 내린다.

단층 퍼셉트론은 한 명의 심사위원이 바로 합격 여부를 정하는 구조다. 다층 퍼셉트론은 여러 중간 심사위원이 각자 특징을 뽑고, 마지막에 종합하는 구조다.

직접 해보는 실습

이번 실습에서는 학습은 하지 않는다. 정해진 가중치로 입력이 출력으로 바뀌는 순전파만 구현한다.

실습 1: NumPy로 뉴런 하나 계산하기

import numpy as np

x = np.array([2.0, 3.0])      # 입력
w = np.array([0.5, -1.0])     # 가중치
b = 1.0                      # 편향

z = np.dot(x, w) + b

def step(v):
    return 1 if v > 0 else 0

y = step(z)

print("z:", z)
print("y:", y)

예상 결과는 다음과 같다.

z: -1.0
y: 0

여기서 w나 b를 바꾸어 출력이 어떻게 달라지는지 확인해 보자. 특히 w = np.array([2.0, -1.0])로 바꾸면 출력이 1로 바뀐다.

실습 2: NumPy로 작은 MLP 순전파 구현하기

이번에는 입력 2개, 은닉 뉴런 3개, 출력 뉴런 1개를 가진 작은 MLP를 만든다.

구조는 다음과 같다.

입력 x: shape (2,)
은닉층 W1: shape (2, 3)
은닉층 b1: shape (3,)
은닉 출력 h: shape (3,)
출력층 W2: shape (3, 1)
출력층 b2: shape (1,)
최종 출력 y: shape (1,)

코드는 다음과 같다.

import numpy as np

# 입력 1개: 특성 2개
x = np.array([1.0, 0.5])

# 입력층 → 은닉층
W1 = np.array([
    [0.2, -0.4, 0.7],
    [0.5,  0.3, -0.2]
])
b1 = np.array([0.1, 0.0, -0.1])

# 은닉층 → 출력층
W2 = np.array([
    [0.6],
    [-0.1],
    [0.8]
])
b2 = np.array([0.05])

def relu(z):
    return np.maximum(0, z)

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

z1 = x @ W1 + b1
h = relu(z1)
z2 = h @ W2 + b2
y = sigmoid(z2)

print("z1:", z1)
print("h:", h)
print("z2:", z2)
print("y:", y)

여기서 @는 행렬 곱셈이다. x @ W1은 입력 벡터와 가중치 행렬을 곱한다는 뜻이다.

직접 계산의 흐름은 다음과 같다.

x → z1 = xW1 + b1 → h = ReLU(z1) → z2 = hW2 + b2 → y = sigmoid(z2)

출력 y는 0과 1 사이의 값이다. 이진 분류에서는 보통 y >= 0.5이면 1, 아니면 0으로 판단한다.

실습 3: 배치 입력으로 바꾸기

실제 딥러닝에서는 입력 하나씩 계산하지 않고 여러 샘플을 묶어서 계산한다. 이 묶음을 배치라고 한다.

입력 샘플이 4개이고 각 샘플의 특성이 2개라면 입력의 shape은 (4, 2)다.

X = np.array([
    [1.0, 0.5],
    [0.2, 0.1],
    [0.0, 1.0],
    [1.5, 0.3]
])

z1 = X @ W1 + b1
h = relu(z1)
z2 = h @ W2 + b2
y = sigmoid(z2)

print("X shape:", X.shape)
print("z1 shape:", z1.shape)
print("h shape:", h.shape)
print("z2 shape:", z2.shape)
print("y shape:", y.shape)
print(y)

shape은 다음처럼 변한다.

단계계산shape
입력X(4, 2)
은닉 가중치W1(2, 3)
은닉 가중합X @ W1 + b1(4, 3)
은닉 출력h(4, 3)
출력 가중치W2(3, 1)
최종 출력y(4, 1)

배치 차원은 샘플 개수다. 위 예제에서는 4다. 가중치 행렬은 모든 샘플에 공통으로 적용된다. 즉 같은 모델이 네 개의 입력을 한 번에 처리한다.

실습 4: PyTorch의 nn.Module로 같은 구조 만들기

이제 PyTorch로 같은 구조를 만든다. 가중치를 NumPy 예제와 똑같이 넣으면 결과도 거의 같아야 한다.

import torch
import torch.nn as nn

class SmallMLP(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(2, 3)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(3, 1)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        z1 = self.fc1(x)
        h = self.relu(z1)
        z2 = self.fc2(h)
        y = self.sigmoid(z2)
        return y

model = SmallMLP()

with torch.no_grad():
    model.fc1.weight.copy_(torch.tensor([
        [0.2, 0.5],
        [-0.4, 0.3],
        [0.7, -0.2]
    ]))
    model.fc1.bias.copy_(torch.tensor([0.1, 0.0, -0.1]))

    model.fc2.weight.copy_(torch.tensor([[0.6, -0.1, 0.8]]))
    model.fc2.bias.copy_(torch.tensor([0.05]))

X_torch = torch.tensor([
    [1.0, 0.5],
    [0.2, 0.1],
    [0.0, 1.0],
    [1.5, 0.3]
])

output = model(X_torch)
print(output)

주의할 점이 있다. NumPy 예제의 W1 shape은 (2, 3)이었다. 하지만 PyTorch의 nn.Linear(2, 3)에서 weight shape은 (3, 2)다. PyTorch는 내부적으로 다음처럼 계산한다.

output = input @ weight.T + bias

그래서 PyTorch에 직접 가중치를 넣을 때는 NumPy에서 사용한 행렬을 전치한 모양으로 넣어야 한다.

구현입력 shape가중치 shape계산
NumPy(batch, 2)(2, 3)X @ W1 + b1
PyTorch nn.Linear(2, 3)(batch, 2)(3, 2)X @ W.T + b

이 차이는 초보자가 자주 헷갈리는 부분이다. shape을 출력해 확인하는 습관을 들이면 많은 오류를 줄일 수 있다.

흔한 실수와 오류 분석

1. 가중치와 입력의 shape을 맞추지 못한다

가장 흔한 오류는 행렬 곱셈의 차원이 맞지 않는 것이다.

ValueError: matmul: Input operand has a mismatch in its core dimension

행렬 곱셈에서는 앞 행렬의 열 수와 뒤 행렬의 행 수가 같아야 한다.

(batch, input_dim) @ (input_dim, hidden_dim) = (batch, hidden_dim)

예를 들어 입력이 (4, 2)라면 첫 번째 가중치 행렬은 (2, 3)처럼 시작 차원이 2여야 한다.

2. 배치 차원을 특성 차원과 혼동한다

(4, 2)에서 4는 샘플 개수이고, 2는 특성 개수다. 모델이 보는 입력 특성은 2개다. 따라서 nn.Linear(2, 3)이 맞다. nn.Linear(4, 3)이 아니다.

배치 크기는 학습 중 바뀔 수 있다. 하지만 특성 개수는 모델 구조와 직접 연결되므로 쉽게 바뀌지 않는다.

3. 활성화 함수를 빼먹는다

은닉층 사이에 활성화 함수를 넣지 않으면 여러 선형층을 쌓아도 결국 하나의 선형 변환과 비슷해진다.

선형 → 선형 → 선형

이 구조는 겉으로는 층이 많아 보이지만 비선형 패턴을 표현하는 힘이 약하다. 일반적인 MLP에서는 은닉층 뒤에 ReLU 같은 활성화 함수를 넣는다.

선형 → ReLU → 선형

단, 출력층의 활성화 함수는 문제 유형에 따라 다르다. 이진 분류에서는 sigmoid를 사용할 수 있고, 다중 분류에서는 softmax와 cross entropy를 함께 고려한다. 이 내용은 다음 장과 PyTorch 학습 루프 장에서 더 자세히 다룬다.

4. PyTorch의 Linear 가중치 방향을 착각한다

NumPy에서 직접 X @ W를 쓸 때는 보통 W를 (입력 차원, 출력 차원)으로 둔다. PyTorch의 nn.Linear는 가중치를 (출력 차원, 입력 차원)으로 저장한다.

이 차이를 모르고 직접 가중치를 복사하면 결과가 달라지거나 shape 오류가 난다.

5. 출력값을 곧바로 정답이라고 생각한다

sigmoid 출력이 0.73이라면 이것은 보통 "1일 가능성이 높다"는 점수로 해석한다. 최종 클래스는 기준값을 정해 판단한다.

0.73 >= 0.5 → 클래스 1
0.31 < 0.5 → 클래스 0

하지만 이 기준값 0.5가 항상 최선은 아니다. 의료 진단처럼 놓치면 안 되는 문제가 있다면 기준값을 낮출 수도 있다. 이 부분은 평가 지표를 다룰 때 다시 연결된다.

요약

신경망은 복잡한 이름과 구조를 갖고 있지만 기본 계산은 단순하다.

가중합 = 입력 × 가중치 + 편향
출력 = 활성화 함수(가중합)

퍼셉트론은 이 계산을 하는 가장 단순한 모델이다. 단층 퍼셉트론은 직선 형태의 결정 경계를 만든다. 그래서 AND, OR처럼 선형 분리 가능한 문제는 풀 수 있지만 XOR처럼 직선 하나로 나눌 수 없는 문제는 풀기 어렵다.

다층 퍼셉트론은 은닉층을 추가해 입력을 새로운 표현으로 바꾼다. 여기에 ReLU 같은 비선형 활성화 함수가 들어가면 신경망은 더 복잡한 패턴을 표현할 수 있다.

실습에서는 NumPy로 순전파를 직접 구현하고, PyTorch의 nn.Module로 같은 구조를 만들었다. 이 과정에서 shape을 확인하는 것이 중요하다. 딥러닝 코드의 많은 오류는 수학 자체보다 텐서 모양을 잘못 이해해서 생긴다.

퀴즈

  1. 인공 뉴런에서 가중치와 편향은 각각 어떤 역할을 하는가?

  2. 다음 값에서 가중합 z와 계단 함수 출력을 계산해 보자.

x1 = 1, x2 = 2
w1 = 0.5, w2 = -0.2
b = 0.1
계단 함수: z > 0이면 1, 아니면 0
  1. 단층 퍼셉트론의 결정 경계는 왜 직선인가?

  2. XOR 문제가 단층 퍼셉트론으로 풀리지 않는 이유를 한 문장으로 설명해 보자.

  3. 은닉층이 하는 역할은 무엇인가?

  4. 활성화 함수를 사용하지 않고 선형층만 여러 개 쌓으면 어떤 문제가 생기는가?

  5. 입력 배치의 shape이 (32, 10)이고 은닉 뉴런이 5개라면 NumPy에서 첫 번째 가중치 행렬의 shape은 무엇이어야 하는가?

  6. PyTorch에서 nn.Linear(10, 5)의 weight shape은 무엇인가?

정답 예시는 다음과 같다.

  1. 가중치는 각 입력의 영향력을 조절하고, 편향은 판단 기준을 이동시킨다.

  2. z = 1×0.5 + 2×(-0.2) + 0.1 = 0.2, 출력은 1이다.

  3. x1w1 + x2w2 + b = 0이 2차원에서 직선이기 때문이다.

  4. 0과 1인 점들이 대각선으로 섞여 있어 직선 하나로 분리할 수 없기 때문이다.

  5. 입력을 새로운 특징 공간으로 변환해 더 복잡한 패턴을 표현하게 한다.

  6. 전체 모델이 결국 하나의 선형 변환처럼 되어 비선형 패턴을 표현하기 어렵다.

  7. (10, 5)

  8. (5, 10)

체크리스트

다음 항목에 답할 수 있으면 이 장의 핵심을 이해한 것이다.

  • 뉴런의 계산식을 z = xw + b, y = f(z)로 설명할 수 있다.

  • 작은 숫자 예제로 가중합과 출력을 직접 계산할 수 있다.

  • 퍼셉트론이 만드는 결정 경계가 무엇인지 설명할 수 있다.

  • AND, OR과 XOR의 차이를 선형 분리 관점에서 설명할 수 있다.

  • 은닉층이 입력 표현을 바꾼다는 말을 이해한다.

  • 활성화 함수가 없으면 깊은 신경망의 표현력이 제한된다는 점을 설명할 수 있다.

  • NumPy로 MLP의 순전파를 구현할 수 있다.

  • PyTorch nn.Linear의 입력 차원, 출력 차원, weight shape을 구분할 수 있다.

  • 배치 차원과 특성 차원을 구분할 수 있다.

다음 장 연결

이번 장에서는 신경망이 예측을 만드는 과정을 배웠다. 입력이 들어오면 가중치와 편향을 거쳐 출력이 나온다. 이것이 순전파다.

하지만 아직 중요한 질문이 남아 있다.

예측이 틀렸을 때 가중치는 어떻게 고쳐지는가?

이 질문에 답하려면 먼저 "얼마나 틀렸는지"를 숫자로 표현해야 한다. 그 숫자가 손실 함수다. 그리고 손실을 줄이는 방향으로 가중치를 조정하는 핵심 절차가 역전파다.

다음 장에서는 손실 함수와 역전파를 다룬다. 신경망이 단순히 계산하는 구조에서 벗어나, 데이터를 통해 스스로 개선되는 학습 과정으로 들어간다.

책 목록

AI 교육 3권 : 딥러닝 핵심: 입력에서 업데이트까지, 신경망을 제대로 이해하는 법

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기