월레스와 그로밋: 코딩의 날

심층 신경망(Deep Neural Network, DNN)과 Iris 데이터셋 학습 본문

Python/Deep Learning

심층 신경망(Deep Neural Network, DNN)과 Iris 데이터셋 학습

구운 감자 2025. 2. 7. 21:37
iris_data_check.py
from sklearn.datasets import load_iris
import numpy as np

# Iris 데이터 로드
iris = load_iris()
data, target = iris.data, iris.target
feature_names = iris.feature_names
class_names = iris.target_names

# 데이터 확인
print("특성:", feature_names)
print("클래스:", class_names)
print("데이터 샘플:", data[0])  # 꽃받침 길이/넓이, 꽃잎 길이/넓이
print("타겟 샘플:", target[0])

# 각 클래스별 데이터 개수 확인
unique, counts = np.unique(target, return_counts=True)  # unique : 클래스 번호 [0,1,2] / counts : 클래스별 데이터 개수 [50,50,50]
class_distribution = dict(zip(class_names, counts))
# zip -> [('setosa', 50), ('versicolor', 50), ('virginica', 50)]
# dict -> {'setosa': 50, 'versicolor': 50, 'virginica': 50}
print("\n각 클래스별 데이터 개수:")
for class_name, count in class_distribution.items():    # class_distribution.items() -> [('setosa', 50), ('versicolor', 50), ('virginica', 50)]
    print(f"{class_name}: {count}개")

output

특성: ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
클래스: ['setosa' 'versicolor' 'virginica']
데이터 샘플: [5.1 3.5 1.4 0.2]
타겟 샘플: 0

각 클래스별 데이터 개수:
setosa: 50개
versicolor: 50개
virginica: 50개

iris_classification.py
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import numpy as np

# 데이터셋 로드 및 전처리
iris = load_iris()
X = iris.data
y = iris.target

# 데이터 정규화(특성 간 범위 차이가 클 때 중요)
scaler = StandardScaler()   # 평균이 0, 표준편차가 1, 모든 특성이 같은 크기를 갖게 함
X = scaler.fit_transform(X) # fit() : 데이터를 학습시키는 메서드, transform() : 실제로 학습시킨 것을 적용하는 메서드

# 학습/테스트 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)   # 학습용(80%) 테스트용(20%) / 시드 고정(실행할 때마다 동일한 결과를 얻기 위해)

# PyTorch Tensor로 변환
X_train = torch.FloatTensor(X_train)
X_test = torch.FloatTensor(X_test)
y_train = torch.LongTensor(y_train)  # CrossEntropyLoss에 필요
y_test = torch.LongTensor(y_test)

# 간단한 모델 정의
# 너무 많은 뉴런을 사용하면 과적합(overfitting) 위험 증가
model = nn.Sequential(
    nn.Linear(4, 8),  # 입력층 -> 중간층1 / 데이터 특성 수 : 4 / 일반적으로 입력 크기보다 큰 값(보통 2배 또는 3배 정도)으로 설정해 시작
    nn.Sigmoid(),     # 활성화 함수 / 출력값을 0~1 사이로 변환 / 비선형성 추가
    nn.Linear(8, 4),  # 중간층1 -> 중간층2 / 네트워크를 단순화
    nn.Sigmoid(),     # 활성화 함수 / 더 복잡한 패턴을 학습
    nn.Linear(4, 3),  # 중간층2 -> 출력층 (Iris 데이터셋의 클래스 수 : 3)
    nn.Softmax(dim=1) # 출력층 활성화 함수 / 클래스 확률로 변환(각 클래스에 대한 신뢰도) / 소프트맥스 연산을 수행할 축(axis) 지정(dim=1 -> 행단위)
)

# 손실 함수와 옵티마이저 정의
criterion = nn.CrossEntropyLoss()  # CrossEntropyLoss는 내부적으로 Softmax 포함
# 옵티마이저 : 손실 값을 최소화하기 위해 모델의 가중치(Weights)와 편향(Bias)을 업데이트
optimizer = optim.Adam(model.parameters(), lr=0.01) # model.parameters() : 모델의 모든 가중치와 편향 값(Parameter)을 가져옴 / lr=0.01 : 학습률(매개변수를 얼마나 크게 업데이트할지 결정)

# 학습
for epoch in range(500):
    # Forward pass
    outputs = model(X_train)    # 예측값 계산
    loss = criterion(outputs, y_train)  # 예측값과 실제값 비교해 손실 계산(오차 계산)

    # Backward pass 및 최적화
    optimizer.zero_grad()   # 이전 기울기 값을 초기화
    loss.backward() # 손실 함수의 미분값(기울기) 계산
    optimizer.step()    # 모델의 가중치 업데이트(계산된 기울기 기반)

    # 로그 출력(손실값)
    if (epoch + 1) % 50 == 0:
        print(f'Epoch [{epoch + 1}/500], Loss: {loss.item():.4f}')

# 평가
with torch.no_grad():   # 그래디언트 계산 비활성화 (더 빠른 계산, 메모리 절약)
    y_pred = model(X_test).argmax(dim=1)    # 확률이 가장 높은 클래스 선택
    accuracy = (y_pred == y_test).float().mean().item() # 예측값과 실제값 비교 / .float().mean().item() : 정확도를 백분율로 계산
    print(f'Accuracy on test set: {accuracy:.4f}')

# 랜덤으로 하나의 샘플 선택 및 예측
random_idx = np.random.randint(0, len(X_test))  # 랜덤 인덱스 생성
random_sample = X_test[random_idx]  # 랜덤 샘플 추출

# 모델을 사용하여 예측
# 예측할 때는 학습 상태의 영향을 받지 않도록 해야 정확한 결과를 얻을 수 있음
model.eval()  # 평가 모드로 전환 / 배치 정규화와 드롭아웃 비활성화
with torch.no_grad():
    # 모델 입력 : 배치(여러 개의 샘플 동시 처리) 형태
    predicted_class = model(random_sample.unsqueeze(0)).argmax(dim=1).item()  # 예측 클래스 / .unsqueeze() : 1인 차원 생성 함수(배치 형태로변환) / model() -> 각 클래스에 대한 확률
    true_class = y_test[random_idx].item()  # 실제 클래스

# 예측 결과 출력
print("\n----- 랜덤 샘플 예측 -----")
print(f"실제 클래스: {iris.target_names[true_class]}")
print(f"예측 클래스: {iris.target_names[predicted_class]}")

output

Epoch [50/500], Loss: 0.9224
Epoch [100/500], Loss: 0.8171
Epoch [150/500], Loss: 0.6769
Epoch [200/500], Loss: 0.6123
Epoch [250/500], Loss: 0.5950
Epoch [300/500], Loss: 0.5866
Epoch [350/500], Loss: 0.5813
Epoch [400/500], Loss: 0.5772
Epoch [450/500], Loss: 0.5736
Epoch [500/500], Loss: 0.5707
Accuracy on test set: 1.0000

----- 랜덤 샘플 예측 -----
실제 클래스: versicolor
예측 클래스: versicolor

'Python > Deep Learning' 카테고리의 다른 글

퍼셉트론(Perceptron _ AND, OR, NAND, XOR)  (0) 2025.02.07
Flower Classification  (0) 2025.02.06