Feedforward Neural Network (FNN) 실습 1
개념정리
Feedforward Neural Network(FNN)는 인공신경망의 가장 기본적인 형태로, 입력층(Input layer), 은닉층(Hidden layer), 출력층(Output layer)으로 구성되며, 정보가 한 방향으로만 흐르는 구조를 갖는다. FNN은 시간이나 공간적 순서가 없는 데이터, 예를 들어 고객 정보, 테이블형 데이터, 벡터화된 정적 특성 등을 처리하는 데 적합하다.
입력층에서는 각 데이터 샘플의 특징(feature)들이 입력되며, 이 입력은 은닉층을 거쳐 출력층으로 전달된다. 각 층은 선형 변환(Linear Transformation: y=Wx+by = Wx + b)과 활성화 함수(Activation Function)를 거쳐 다음 층으로 전달된다. FNN에서는 주로 ReLU(Rectified Linear Unit) 함수가 은닉층의 활성화 함수로 사용된다. ReLU 함수는 입력값이 0 이하일 경우 0을 출력하고, 양수일 경우 그대로 출력하여 비선형성을 부여하고 기울기 소실 문제를 완화해준다.
출력층에서는 문제의 유형에 따라 적절한 활성화 함수가 선택된다. 예를 들어 이진 분류 문제에서는 Sigmoid 함수를 사용해 출력값을 0과 1 사이의 확률로 변환하며, 다중 클래스 분류 문제에서는 Softmax 함수를 사용할 수 있다. 회귀 문제라면 활성화 함수를 생략하거나 Linear로 설정한다.
FNN 학습 과정에서는 손실 함수(Loss Function)를 통해 모델의 예측값과 실제 정답 간의 오차를 계산하고, 이를 최소화하기 위해 옵티마이저(Optimizer)가 사용된다. 이진 분류 문제에서는 Binary Cross Entropy가, 회귀 문제에서는 Mean Squared Error나 Mean Absolute Error가 주로 사용된다. 옵티마이저로는 일반적으로 Adam이 가장 널리 쓰이며, 학습 속도가 빠르고 안정적인 수렴이 가능하다.
과적합 방지를 위해 Dropout과 같은 정규화 기법을 사용할 수 있다. Dropout은 학습 중 무작위로 일부 뉴런을 비활성화시켜 네트워크가 특정 경로에 과하게 의존하지 않도록 하고, 일반화 성능을 높이는 데 도움을 준다.
FNN은 구조가 단순하고 구현이 쉬우며, 다양한 분류 및 회귀 문제에 적용할 수 있다. 특히 시간이나 순서에 민감하지 않고, 각 입력 특징이 독립적일 때 높은 성능을 발휘한다.
활용예시
고객 데이터를 이용해 이탈 여부를 예측하고자 한다. 어떤 모델을 사용할 수 있으며, 구조와 이유를 설명
활용
고객 이탈 예측 문제는 고객 개개인에 대한 다양한 특성(예: 연령, 가입 기간, 월 사용량, 서비스 이용 횟수 등)을 기반으로 이탈 여부(이탈: 1, 유지: 0)를 예측하는 이진 분류 문제다. 입력 데이터는 시간이나 순서, 공간 구조와 같은 패턴이 없는 테이블 형태의 정적 특성 벡터로 주어지며, 이와 같은 경우에는 순차 정보를 다루는 순환 신경망(RNN)이나 이미지와 같은 공간 구조를 활용하는 합성곱 신경망(CNN)보다는 Feedforward Neural Network(FNN) 를 사용하는 것이 적합하다.
FNN은 입력층, 하나 이상의 은닉층, 출력층으로 구성되며, 정보가 한 방향으로만 흐른다. 각 층은 선형 변환을 수행한 후 활성화 함수를 통해 비선형성을 추가하여 다음 층으로 전달한다. 이러한 구조는 순서나 위치 정보가 없는 특성 벡터들을 효과적으로 처리할 수 있다.
모델 구조는 예를 들어 다음과 같이 구성할 수 있다. 입력층은 고객의 다양한 특성들을 벡터 형태로 입력받으며, 첫 번째 은닉층에서는 입력 벡터에 대해 선형 연산을 수행하고 ReLU(Rectified Linear Unit) 활성화 함수를 적용한다. ReLU 함수는 입력이 0 이하일 경우 0, 양수일 경우 그대로 출력하는 함수로, 비선형성을 부여하면서 기울기 소실 문제를 완화하는 효과가 있다. 이어서 하나 이상의 은닉층을 추가할 수 있으며, 출력층은 단일 노드로 구성되고 Sigmoid 함수를 사용하여 최종 출력값을 0~1 사이의 확률로 변환한다. 이를 통해 모델은 이탈할 확률을 출력할 수 있게 된다.
손실 함수는 예측 확률과 실제 정답(0 또는 1) 간의 오차를 측정하는 Binary Cross Entropy를 사용한다. 이 손실 함수는 이진 분류 문제에서 널리 사용되며, 다음과 같은 수식으로 정의된다:
여기서 yy는 실제값, y^\hat{y}는 모델의 예측 확률이다.
모델 학습에는 일반적으로 Adam 옵티마이저를 사용하며, 이는 학습 속도가 빠르고 안정적으로 수렴하는 특성을 지니고 있어 많은 문제에서 기본값으로 활용된다. 또한 과적합을 방지하기 위해 은닉층 사이에 Dropout을 삽입할 수 있다. Dropout은 학습 중 무작위로 일부 뉴런을 꺼서 특정 뉴런이나 경로에 과도하게 의존하지 않도록 하며, 결과적으로 모델의 일반화 성능을 향상시킨다.
따라서 이탈 여부 예측 문제에서는 FNN을 기반으로 한 간단하면서도 효과적인 분류 모델을 구축할 수 있으며, 적절한 활성화 함수, 손실 함수, 최적화 기법, 정규화 방법을 함께 고려함으로써 실무에 적용 가능한 신뢰도 높은 모델을 만들 수 있다.
- StandardScaler를 사용해 수치형 입력을 정규화.
- 출력층은 Sigmoid, 손실 함수는 BCELoss.
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 1. 데이터 생성 (예시: 20개의 고객 특성, 이진 레이블)
X, y = make_classification(n_samples=1000, n_features=20,
n_informative=10, n_classes=2, random_state=42)
# 2. 전처리 (정규화)
scaler = StandardScaler()
X = scaler.fit_transform(X)
# 3. 학습/검증 데이터 분할
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
# 4. 텐서 변환
X_train_tensor = torch.tensor(X_train, dtype=torch.float32)
y_train_tensor = torch.tensor(y_train.reshape(-1, 1), dtype=torch.float32)
X_val_tensor = torch.tensor(X_val, dtype=torch.float32)
y_val_tensor = torch.tensor(y_val.reshape(-1, 1), dtype=torch.float32)
# 5. 모델 정의 (MLP 형태)
class MLP(nn.Module):
def __init__(self, input_dim):
super().__init__()
self.model = nn.Sequential(
nn.Linear(input_dim, 64),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(64, 32),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(32, 1),
nn.Sigmoid() # 이진 분류
)
def forward(self, x):
return self.model(x)
# 6. 모델, 손실 함수, 옵티마이저 설정
model = MLP(input_dim=20)
criterion = nn.BCELoss() # Binary Cross Entropy Loss
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 7. 학습 루프
epochs = 20
for epoch in range(epochs):
model.train()
y_pred = model(X_train_tensor)
loss = criterion(y_pred, y_train_tensor)
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 검증
model.eval()
with torch.no_grad():
val_pred = model(X_val_tensor)
val_loss = criterion(val_pred, y_val_tensor)
val_acc = ((val_pred > 0.5) == y_val_tensor).float().mean()
print(f"[Epoch {epoch+1}] Train Loss: {loss.item():.4f}, Val Loss: {val_loss.item():.4f}, Val Acc: {val_acc.item():.4f}")