수달이네 기술 블로그

6. 다층 퍼셉트론 본문

AI공부/딥러닝

6. 다층 퍼셉트론

슬픈 수달이 2026. 2. 1. 17:13

다층 퍼셉트론(Multi-Layer Perceptron MLP)

입력층, 하나 이상의 은닉층, 출력층으로 구성된 인공 신경망 구조

  • 각 층은 여러 뉴런으로 이루어져 앞 층의 출력은 다음 층의 입력으로 전달된다. 이후 가중치가 부여된다.
  • 비선형 활성화 함수(ReLU, sigmoid 등)을 사용해 복잡한 패턴, 비선형 문제를 학습할 수 있다.
  • 역전파 알고리즘으로 오차를 최소화하는 방향으로 가중치를 반복적으로 조정한다.

  • 직선을 여러개 붙여서 곡선을 만들수도 있고,

  • 공간을 분리하는 것으로 생각해 나눠줄 수도 있다.

  • 입력층: 4개의 데이터가 준비되어있을때.
  • 은닉층: and, or연산을 여러겹 쌓아 결과적으로
  • 출력층: XOR과 같은 결과를 만들어낸다.

오차 역전파(Backpropagation)

인공신경망이 학습할 때 출력값과 실제값의 차이인 오차를 출력층에서 입력층 방향으로 역으로 전달하여 가중치를 수정하는 알고리즘

  1. 순전파(Forward propagation)으로 예측값을 계산
  2. 손실함수(Loss function)으로 오차를 계산
  3. Chain Rule을 적용해 각 층의 가중치가 오차에 얼마나 영향을 주었는지 게산
  4. 이렇게 계산된 기울기를 기반으로 경사하강법(Grandient Descent)을 이용해 가중치를 업데이트하여 정확도 향상
model  = nn.Sequential(
    nn.Linear(2, 2),
    nn.Sigmoid(),
    nn.Linear(2, 1),
    nn.Sigmoid()
)
  • 위 다층 퍼셉트론 그림을 코드로 구현한 형태
    • 2개를 받아서 2개를 만들고, 다시 2개를 받아 1개로 만들어줌.
  • 위 그림에서 보면, 직접 x1이 n1을 통해 y가 나올수도 있고 꼬아서 x1이 n2를 거쳐 y로 갈수도 있는 구조이다.
    • 추가 연산가능

XOR연산 다층 퍼셉트론으로 확인

# 논리 회귀(단층 퍼셉트론)로 XOR 문제 풀기

X = torch.FloatTensor([[0, 0], [0, 1], [1, 0], [1, 1]])
y = torch.FloatTensor([[0], [1], [1], [0]])

model = nn.Sequential(
    nn.Linear(2, 2),
    nn.Sigmoid(),
    nn.Linear(2, 1),
    nn.Sigmoid()
)

optimizer = optim.SGD(model.parameters(), lr=1)

epochs = 1000

for epoch in range(epochs + 1):
    y_pred = model(X)
    loss = nn.BCELoss()(y_pred, y)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    if epoch % 100 == 0:
        y_bool = (y_pred >= 0.5).float()
        accuracy = (y == y_bool).float().sum() / len(y) * 100
        print(f'Epoch {epoch:4d}/{epochs} Loss: {loss:.6f} Accuracy: {accuracy:.2f}%')

# Epoch    0/1000 Loss: 0.694332 Accuracy: 50.00%
# Epoch  100/1000 Loss: 0.693078 Accuracy: 50.00%
# Epoch  200/1000 Loss: 0.692530 Accuracy: 50.00%
# Epoch  300/1000 Loss: 0.690087 Accuracy: 75.00%
# Epoch  400/1000 Loss: 0.669867 Accuracy: 75.00%
# Epoch  500/1000 Loss: 0.582578 Accuracy: 75.00%
# Epoch  600/1000 Loss: 0.456542 Accuracy: 75.00%
# Epoch  700/1000 Loss: 0.247471 Accuracy: 100.00%
# Epoch  800/1000 Loss: 0.127959 Accuracy: 100.00%
# Epoch  900/1000 Loss: 0.080590 Accuracy: 100.00%
# Epoch 1000/1000 Loss: 0.057608 Accuracy: 100.00%
  • Accuracy가 100%에 도달하는 것으로 보아 XOR연산을 수행 가능함을 확인했다.

비선형 활성화 함수

시그모이드 연산이 들어간 이유

  • 해당 신경망을 선형으로만 표현할 경우

  • 위 공식에 따르면 선형 + 선형 > 선형이 되므로 불가하다. 여기서 시그모이드를 추가해주면,

  • 시그모이드 적용한 것은 분류기에 넣은 것이 아닌 직선을 비선형화 시켜준 활성화 함수이므로 두번째 층에 적용
  • 해당 결과를 다시 시그모이드에 넣어 연산을 하면 0.55라는 결과가 나온다.
    • 만약 0.5 이상이 1이라는 분류면 해당 값은 1이다.

비선형 활성화 함수란?

선형 변환만으로 표현할 수 없는 비선형 패턴을 학습할 수 있게 도와주는 함수

  • 입력 값을 비선형 적으로 변환해, 신경망의 계층마다 다른 특징을 학습하게 한다.
  • 이로 복잡한 함수, 데이터 분포를 모델링한다.
  • 출력값을 제한하거나 왜곡해 학습을 안정화시키고, 신경망이 강력한 표현력을 가지도록 한다.
  • ReLU, Sigmoid, Tanh등이 있다.

시그모이드

입력값을 0~1로 스케일링 하는데 사용된다.

  • 입력값 x가 큰 양수일수록 1에, 작은 음수일수록 0에 가까워진다.
  • 입력값을 확률처럼 해석할 수 있도록 변환하는 특징으로 이진 분류 문제에서 자주 사용되지만,
  • 출력의 미분값이 작은 구간에서 그래디언트(기울기) 소실 문제가 발생할 수 있어 최근엔 ReLU같은 다른 활성화 함수들이 널리 사용된다.
  • 시그모이드는 모델의 특정 부분이나, 확률적 해석이 필요할 때 사용한다.
import numpy as np
import matplotlib.pyplot as plt

x = np.arange(-5.0, 5.0, 0.1)
y = 1 / (1 + np.exp(-x))                    # sigmoid
dy = y * (1 - y)                             # derivative

plt.plot(x, y,  label='Sigmoid', color='blue')
plt.plot(x, dy, label='Derivative', color='orange', linestyle='--')

plt.plot([0, 0], [1.0, 0.0], ':', color='gray')
plt.title('Sigmoid & Derivative (orange)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.ylim(-0.05, 1.05)
plt.show()

하이퍼볼릭 탄젠트(tanh)

입력값을 -1~1 사이의 값으로 변환하는 함수

  • 입력값이 큰 양수일수록 1에 가까워지고, 작은 음수일수록 -1에 가까워진다.
  • 출력 범위가 대칭적이어서 Sigmoid함수와 달리 출력이 0을 기준으로 분포되어 데이터가 양수, 음수로 분리되는 문제에서 적합하다.
  • 입력값이 매우 크거나 작으면 기울기가 거의 0에 가까워지는 기울기 소실 문제가 존재한다.
import numpy as np
import matplotlib.pyplot as plt

x = np.arange(-5.0, 5.0, 0.1)
y = np.tanh(x)

# tanh의 미분: 1 - tanh(x)^2
dy = 1 - y**2

plt.plot(x, y,  label='tanh(x)', color='blue')
plt.plot(x, dy, label="tanh'(x)", color='orange', linestyle='--')

plt.plot([0, 0], [1.0, -1.0], ':', color='gray')     # y축 보조선
plt.axhline(y=0, color='gray', linestyle='-', linewidth=0.8)
plt.axhline(y=1,  color='gray', linestyle=':', linewidth=0.6)
plt.axhline(y=-1, color='gray', linestyle=':', linewidth=0.6)

plt.title('Tanh Function & Its Derivative')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.grid(True, alpha=0.3)
plt.ylim(-1.1, 1.1)
plt.show()

ReLU(Rectifiend Linear Unit)

신경망에서 가장 널리 사용되는 비선형 활성화 함수

  • 입력값이 0보다 크면 그대로, 음수이면 0으로 출력하는 함수
  • 출력값이 양수인 경우 기울기가 1로 일정하게 유지되어 기울기 소실 문제를 완화한다.
  • 학습이 빠르고 효율적이다.
  • 깊은 신경망에서 성능이 뛰어나다.
  • 입력값이 0이하라면 기울기가 0이 되어 학습되지 않는 죽은 ReLU문제가 존재한다. 이때문에 Leaky ReLU등이 개발되었다.

import numpy as np
import matplotlib.pyplot as plt

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

def relu_derivative(x):
    return np.where(x > 0, 1.0, 0.0)   # 또는 (x > 0).astype(float)

x = np.arange(-5.0, 5.0, 0.1)
y     = relu(x)
dy    = relu_derivative(x)

plt.plot(x, y,  label='ReLU(x)', color='blue')
plt.plot(x, dy, label="ReLU'(x)", color='orange', linestyle='--')

plt.plot([0, 0], [5.0, 0.0], ':', color='gray')     # y축 보조선
plt.axhline(y=0, color='gray', linestyle='-', linewidth=0.8)

plt.title('ReLU Function & Its Derivative')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

Reaky ReLU

  • ReLU와 비슷하지만, 음수가 0이 되는것이 아닌 아주 작은 음수로 변환된다.
import numpy as np
import matplotlib.pyplot as plt

def leaky_relu(x, alpha=0.01):
    return np.maximum(alpha * x, x)

def leaky_relu_derivative(x, alpha=0.01):
    return np.where(x > 0, 1.0, alpha)

x = np.arange(-5.0, 5.0, 0.1)
y  = leaky_relu(x)
dy = leaky_relu_derivative(x)

plt.plot(x, y,  label='Leaky ReLU(x)', color='blue')
plt.plot(x, dy, label="Leaky ReLU'(x)", color='orange', linestyle='--')

plt.plot([0, 0], [5.0, -0.5], ':', color='gray')     # y축 보조선
plt.axhline(y=0, color='gray', linestyle='-', linewidth=0.8)

plt.title('Leaky ReLU Function & Its Derivative (α = 0.01)')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.grid(True, alpha=0.3)
plt.ylim(-0.6, 5.1)
plt.show()