들어가며
딥러닝을 공부하면서 "신경망이 뇌를 모방했다"는 말을 많이 들었는데, 처음엔 그게 코드로 어떻게 구현되는지 전혀 감이 오지 않았다. 웹 개발로 비유하면 신경망은 여러 함수가 파이프라인처럼 연결된 구조다. 입력이 들어오면 각 함수를 거쳐 최종 출력이 나오는 것이다. 기초부터 차근차근 정리해둔다.
퍼셉트론 (Perceptron)
신경망의 가장 기본 단위다. 1958년 Frank Rosenblatt이 제안했다. 뇌의 뉴런을 수학적으로 모델링한 것이다.
생물학적 뉴런 vs 인공 뉴런
생물학적 뉴런:
수상돌기(입력) → 세포체(처리) → 축삭(출력)
신호가 임계값을 넘으면 다음 뉴런으로 신호 전달
인공 뉴런(퍼셉트론):
입력값(x) → 가중합(Σ) → 활성화 함수(f) → 출력(y)
퍼셉트론 수식
y = f(w₁x₁ + w₂x₂ + ... + wₙxₙ + b)
= f(W·x + b)
x : 입력값
w : 가중치 (weight) - 각 입력의 중요도
b : 편향 (bias) - 활성화 임계값 조정
f : 활성화 함수
y : 출력값
import numpy as np
def perceptron(x, w, b, activation):
z = np.dot(w, x) + b # 가중합
y = activation(z) # 활성화 함수 적용
return y
x = np.array([0.5, 0.8])
w = np.array([0.4, 0.6])
b = -0.3
def step(z):
return 1 if z >= 0 else 0
output = perceptron(x, w, b, step)
print(f"입력: {x}")
print(f"가중합: {np.dot(w, x) + b:.4f}") # 0.4*0.5 + 0.6*0.8 - 0.3 = 0.38
print(f"출력: {output}") # 1 (0.38 >= 0 이므로)
AND, OR 게이트 구현
# AND 게이트
def AND(x1, x2):
w = np.array([0.5, 0.5])
b = -0.7
z = w[0]*x1 + w[1]*x2 + b
return 1 if z >= 0 else 0
print("AND 게이트:")
print(f"0 AND 0 = {AND(0, 0)}") # 0
print(f"1 AND 1 = {AND(1, 1)}") # 1
# OR 게이트
def OR(x1, x2):
w = np.array([0.5, 0.5])
b = -0.2
z = w[0]*x1 + w[1]*x2 + b
return 1 if z >= 0 else 0
print("OR 게이트:")
print(f"0 OR 0 = {OR(0, 0)}") # 0
print(f"0 OR 1 = {OR(0, 1)}") # 1
단층 퍼셉트론의 한계: XOR 문제
단층 퍼셉트론은 선형으로 분리되지 않는 문제를 풀 수 없다.
# XOR은 단층 퍼셉트론으로 구현 불가
# 0 XOR 0 = 0 / 0 XOR 1 = 1 / 1 XOR 0 = 1 / 1 XOR 1 = 0
# → 선형으로 분리 불가능
# 다층 퍼셉트론으로는 가능
def XOR(x1, x2):
s1 = OR(x1, x2)
nand_w = np.array([-0.5, -0.5])
s3 = 1 if nand_w[0]*x1 + nand_w[1]*x2 + 0.7 >= 0 else 0
return AND(s1, s3)
print("XOR 게이트 (다층 퍼셉트론):")
print(f"0 XOR 0 = {XOR(0, 0)}") # 0
print(f"0 XOR 1 = {XOR(0, 1)}") # 1
print(f"1 XOR 0 = {XOR(1, 0)}") # 1
print(f"1 XOR 1 = {XOR(1, 1)}") # 0
이게 다층 신경망(Multi-Layer Perceptron, MLP)의 출발점이다. 레이어를 여러 개 쌓으면 선형으로 분리되지 않는 복잡한 문제도 풀 수 있다.
다층 신경망 (Multi-Layer Perceptron)
입력층(Input Layer) → 데이터가 들어오는 곳
은닉층(Hidden Layer) → 특징을 추출하고 변환하는 곳
출력층(Output Layer) → 최종 결과를 출력하는 곳
np.random.seed(42)
class SimpleMLP:
def __init__(self, input_size, hidden_size, output_size):
# He 초기화
self.W1 = np.random.randn(input_size, hidden_size) * np.sqrt(2/input_size)
self.b1 = np.zeros(hidden_size)
self.W2 = np.random.randn(hidden_size, output_size) * np.sqrt(2/hidden_size)
self.b2 = np.zeros(output_size)
def forward(self, x):
self.z1 = x @ self.W1 + self.b1
self.h1 = np.maximum(0, self.z1) # ReLU
self.z2 = self.h1 @ self.W2 + self.b2
exp_x = np.exp(self.z2 - self.z2.max(axis=-1, keepdims=True))
self.output = exp_x / exp_x.sum(axis=-1, keepdims=True) # Softmax
return self.output
# 4개 입력 → 8개 은닉 → 3개 출력 (3클래스 분류)
model = SimpleMLP(input_size=4, hidden_size=8, output_size=3)
X = np.random.randn(5, 4)
output = model.forward(X)
print(f"입력 shape: {X.shape}") # (5, 4)
print(f"출력 shape: {output.shape}") # (5, 3)
print(f"확률 합: {output.sum(axis=1)}") # 모두 1.0
활성화 함수 (Activation Function)
활성화 함수가 없으면 레이어를 아무리 많이 쌓아도 결국 하나의 선형 변환과 같다.
활성화 함수가 비선형성을 추가해서 복잡한 패턴을 학습할 수 있게 된다.
활성화 함수 없는 2레이어:
y = W2 @ (W1 @ x + b1) + b2
= (W2@W1) @ x + (W2@b1 + b2)
= W' @ x + b' → 결국 1레이어와 동일
1. Step Function (계단 함수)
def step_function(x):
return np.where(x >= 0, 1, 0)
x = np.array([-2, -1, 0, 1, 2])
print(step_function(x)) # [0, 0, 1, 1, 1]
# 문제점: 미분값이 0이라 역전파 불가능 → 현재 사용 안 함
2. Sigmoid
f(x) = 1 / (1 + e⁻ˣ)
출력 범위: (0, 1)
def sigmoid(x):
return 1 / (1 + np.exp(-x))
print(f"sigmoid(-6) = {sigmoid(-6):.4f}") # ≈ 0.0025
print(f"sigmoid(0) = {sigmoid(0):.4f}") # = 0.5
print(f"sigmoid(6) = {sigmoid(6):.4f}") # ≈ 0.9975
장점: 출력이 0~1 → 확률로 해석 가능
단점:
1. 기울기 소실: 입력이 크거나 작으면 기울기 ≈ 0
2. 출력이 0 중심이 아님 → 학습 속도 저하
3. exp 연산 비용이 큼
사용처: 이진 분류 출력층, LSTM 게이트
3. Tanh
f(x) = (eˣ - e⁻ˣ) / (eˣ + e⁻ˣ)
출력 범위: (-1, 1)
print(f"tanh(-6) = {np.tanh(-6):.4f}") # ≈ -1.0
print(f"tanh(0) = {np.tanh(0):.4f}") # = 0.0
print(f"tanh(6) = {np.tanh(6):.4f}") # ≈ 1.0
# Sigmoid 대비: 출력이 0 중심 → 학습 빠름
# 사용처: RNN, LSTM 내부 활성화
4. ReLU (Rectified Linear Unit)
현재 가장 많이 사용되는 활성화 함수다.
f(x) = max(0, x)
출력 범위: [0, ∞)
def relu(x):
return np.maximum(0, x)
x = np.array([-3, -1, 0, 1, 3])
print(relu(x)) # [0, 0, 0, 1, 3]
장점:
1. 계산 단순 → 학습 빠름
2. 양수 구간에서 기울기 = 1 → 기울기 소실 없음
3. 깊은 신경망에서도 잘 동작
단점 (Dying ReLU):
- 입력이 음수이면 항상 출력 0, 기울기 0
- 특정 뉴런이 한 번 음수 영역에 빠지면 영원히 0만 출력
사용처: 은닉층 기본 선택
5. Leaky ReLU
ReLU의 Dying ReLU 문제를 해결한다.
def leaky_relu(x, alpha=0.01):
return np.where(x > 0, x, alpha * x)
x = np.array([-3, -1, 0, 1, 3])
print(leaky_relu(x)) # [-0.03, -0.01, 0, 1, 3]
6. GELU (Gaussian Error Linear Unit)
Transformer, BERT, GPT 등 최신 모델에서 사용된다.
def gelu(x):
return 0.5 * x * (1 + np.tanh(np.sqrt(2/np.pi) * (x + 0.044715 * x**3)))
x = np.array([-3, -1, 0, 1, 3])
print(gelu(x).round(4)) # [-0.0036, -0.1588, 0, 0.8413, 2.9964]
출력층 활성화 함수
# 이진 분류 (0 또는 1)
output_binary = sigmoid(x) # 출력: 0~1 확률
# 다중 분류 (클래스 중 하나)
output_multiclass = softmax(x) # 출력: 클래스별 확률 (합=1)
# 회귀 (연속값 예측)
output_regression = x # 활성화 함수 없음 (선형)
print("""
문제 유형 출력층 활성화 손실 함수
이진 분류 → Sigmoid Binary Cross-Entropy
다중 분류 → Softmax Categorical Cross-Entropy
회귀 → 없음 (선형) MSE
""")
활성화 함수 비교
| 활성화 함수 | 출력 범위 | 기울기 소실 | 계산 비용 | 사용처 |
|---|---|---|---|---|
| Step | {0, 1} | 심각 | 낮음 | 사용 안 함 |
| Sigmoid | (0, 1) | 있음 | 보통 | 이진 분류 출력, LSTM |
| Tanh | (-1, 1) | 있음 | 보통 | RNN, LSTM |
| ReLU | [0, ∞) | 없음 | 낮음 | 은닉층 기본 |
| Leaky ReLU | (-∞, ∞) | 없음 | 낮음 | ReLU 대안 |
| GELU | (-∞, ∞) | 없음 | 높음 | Transformer, GPT |
| Softmax | (0, 1) | - | 보통 | 다중 분류 출력 |
실무에서 활성화 함수 선택 방법
은닉층 기본값 → ReLU
ReLU가 잘 안 될 때 → Leaky ReLU 또는 ELU
Transformer 계열 → GELU
이진 분류 출력 → Sigmoid
다중 분류 출력 → Softmax
회귀 출력 → 없음 (선형)
처음 모델을 만들 때는 은닉층에 ReLU, 출력층은 문제 유형에 맞는 함수를 선택하면 대부분 잘 동작한다.
정리
| 개념 | 설명 | 핵심 포인트 |
|---|---|---|
| 퍼셉트론 | 신경망의 기본 단위 | 가중합 + 활성화 함수 |
| 다층 신경망 | 여러 레이어를 쌓은 구조 | XOR 등 비선형 문제 해결 |
| 활성화 함수 | 비선형성 추가 | 없으면 레이어 쌓는 의미 없음 |
| ReLU | 현재 가장 많이 쓰임 | 은닉층 기본 선택 |
| Sigmoid | 0~1 출력 | 이진 분류 출력층 |
| Softmax | 확률 분포 출력 | 다중 분류 출력층 |
| Dying ReLU | ReLU의 단점 | Leaky ReLU로 해결 |
'딥러닝 > 딥러닝 기초 개념' 카테고리의 다른 글
| 과적합과 정규화 (Overfitting, Dropout, BatchNorm) (0) | 2026.05.15 |
|---|---|
| 손실 함수 (Loss Function) (0) | 2026.05.15 |
| 경사하강법과 최적화 (Gradient Descent, Adam, SGD) (0) | 2026.05.15 |
| 순전파와 역전파 (Feedforward & Backpropagation) (0) | 2026.05.15 |