이진 분류 모델
주어진 트레이닝 데이터를 사용하여 특징 변수와 목표 변수(두 가지 범주) 사이의 관계를 학습하고, 이를 바탕으로 트레이닝 데이터에 포함되지 않은 새로운 데이터를 사전에 정의된 두 가지 범주 중 하나로 분류하는 모델을 구축하는 과정
1. 데이터 전처리
# Donwload dataset from kaggle
!kaggle datasets download -d uciml/iris
# unzip zip file
!unzip iris.zip
# 트레이닝 데이터 불러오기
import pandas as pd
df = pd.read_csv("Iris.csv", sep = ",", header = 0)[["PetalLengthCm", "Species"]]
# Iris_data 데이터프레임에서 Species 열의 값이 setosa와 versicolor의 두 가지 범주에 속하는 행을 필터링
filtered_data = df[df['Species'].isin(['Iris-setosa', 'Iris-versicolor'])]
filtered_df = filtered_data
# 목표 변수를 이산형 레이블로 매핑
filtered_df.loc[:, 'Species'] = filtered_df['Species'].map({'Iris-setosa': 0, 'Iris-versicolor': 1})
# 특징 변수(features)와 목표 변수(target)을 추출
x = filtered_df[['PetalLengthCm']].values # 2차원 배열로 변환
t = filtered_df['Species'].values.astype(int) # 1차원 배열로 변환
# 데이터 분할
from sklearn.model_selection import train_test_split
x_train, x_test, t_train, t_test = train_test_split(x, t, test_size=0.2, random_state=42)
# 데이터 표준화
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
x_train = scaler.fit_transform(x_train)
x_test = scaler.transform(x_test)
# 데이터를 Tensor로 변환
x_train = torch.tensor(x_train, dtype=torch.float32)
x_test = torch.tensor(x_test, dtype=torch.float32)
t_train = torch.tensor(t_train, dtype=torch.float32).unsqueeze(1)
t_test = torch.tensor(t_test, dtype=torch.float32).unsqueeze(1)
- 데이터 분할
- 학습용 데이터 : 데이터 모델을 학습시키는 데 사용되는 데이터, 가중치와 바이어스를 최적화하기 위해 사용
- 테스트 데이터 : 최종 모델의 성능을 평가하는 데 사용되는 데이터, 모델의 실제 성능을 확인하기 위해 사용
- random_state = 42
- random_state는 일종의 "비밀번호"라고 생각하자. 이 비밀번호를 알고 있으면, 같은 과정을 반복할 때마다 항상 같은 결과를 얻을 수 있음
- 데이터를 섞는 과정이 무작위다보니 매번 섞을 때마다 결과가 다를 수 있는데, random_state=42라는 비밀번호(시드)를 입력하면, 데이터가 항상 같은 순서로 섞
- 이렇게 하면 나중에 같은 데이터를 똑같이 섞어야 할 때 같은 순서를 얻을 수 있어서 실험을 반복하거나, 다른 사람과 결과를 공유할 때 유용함
- 즉 random_state=42라는 데이터를 섞는 방법을 미리 정해둠으로써 실험 결과를 다시 확인하거나 다른 사람과 공유할 때 일관성을 유지할 수 있음
- t_train과 t_test를 unsqueeze 함수를 사용해서 2차원으로 변환하는 이유
- 배치 처리를 위해서는 목표 변수의 형태가 [N, 1]이 되어야 함
- 특징 변수는 [데이터 수, 특징 수]이기 때문에 이미 2차원이며, 일관된 데이터 형태를 위해 목표 변수 또한 2차원 Tensor로 변환
- 손실함수 또한 2차원 Tensor의 형태를 기대하므로 호환성을 위해 2차원 Tensor로 변환
2. 배치 처리를 위한 Dataset 클래스와 DataLoader 클래스
# Dataset 클래스
from torch.utils.data import Dataset, DataLoader
class IrisDataset(Dataset): # CustomDataset 클래스
def __init__(self, features, labels):
self.features = features
self.labels = labels
def __len__(self):
return len(self.features)
def __getitem__(self, idx):
return self.features[idx], self.labels[idx]
# 인스턴스 생성
train_dataset = IrisDataset(x_train, t_train) # 트레이닝 데이터를 위한 CustomDataset 인스턴스를 생성
test_dataset = IrisDataset(x_test, t_test) # 테스트 데이터를 위한 CustomDataset 인스턴스를 생성
# DataLoader 생성
batch_size = 4 # 배치 크기를 4로 설정
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
- Dataset 클래스 : 데이터셋을 정의하는 기본 클래스
- CustomDataset 클래스 : Dataset 클래스를 상속받아 사용자 정의 데이터셋(IrisDataset)을 정의
- init 메서드 : 데이터를 초기화
- self.features : 데이터셋의 특징 변수를 저장
- self.labels : 데이터셋의 목표 변수를 저장
- len 메서드 : 데이터의 크기를 반환
- len(self.features) : 데이터셋의 특징 변수의 개수를 반환
- getitem 메서드 : 특정 인덱스의 데이터 샘플을 반환
- self.feature[idx] : 주어진 인덱스에 대해서 특징 변수를 반환
- self.labels[idx] : 주어진 인덱스에 대해서 목표 변수를 반환
- DataLoader 클래스 : Dataset의 인스턴스를 감싸서 배치 단위(batch_size)로 데이터를 로드하고, 데이터셋을 섞는(shuffle) 등의 작업을 수행
- 모델 훈련 시에는 데이터의 순서에 따른 편향을 줄이기 위해 데이터를 섞음
- 모델 성능 평가 시에는 데이터 순서를 유지하는 것이 일반적이므로 데이터를 섞지 않음
3. 이진 분류 모델의 학습
import torch.nn as nn
class BinaryClassificationModel(nn.Module):
def __init__(self):
super(BinaryClassificationModel, self).__init__()
self.layer_1 = nn.Linear(1, 1) # 입력 차원과 출력 차원을 1로 설정
self.sigmoid = nn.Sigmoid() # 선형 계층의 출력을 이진 분류 확률로 변환
def forward(self, x):
z = self.layer_1(x) # 특징 변수 x를 받아 self.linear 속성을 통해 중간 출력 z를 계산
y = self.sigmoid(z) # z를 시그모이드 활성화 함수에 통과시켜 최종 출력인 이진 분류 확률 y를 계산
return y
model = BinaryClassificationModel()
로지스틱 회귀 알고리즘
트레이닝 데이터의 특성과 분포를 바탕으로 데이터를 잘 구분할 수 있는 최적의 결정 경계를 찾아 시그모이드 함수를 통해 이 경계를 기준으로 데이터를 이진 분류하는 알고리즘
- 최적의 결정 경계를 찾기 위해 우선 선형 결정 경계를 찾아야 함 → nn.Linear(1, 1)
- 트레이닝 데이터의 특징 변수 x값이 회귀의 입력으로 들어가서 z = Wx + b 값으로 계산됨
- 시그모이드 함수는 비선형 함수로, 입력 값을 0과 1 사이의 값으로 변환하는 함수
- z값이 이진 분류에 입력으로 들어가서 y = Sigmoid(z) 값으로 계산됨 → nn.Sigmoid()
- 출력된 y = Sigmoid(z) 값이 0.5 이상이면 논리적인 결과값을 1로 정의하고
- 출력된 y = Sigmoid(z) 값이 0.5 미만이면 논리적인 결과값을 0으로 정의
- 시그모이드 함수의 결과는 0과 1 사이의 값으로 계산되므로 그 결과를 확률로 해석할 수 있음
- z값이 이진 분류에 입력으로 들어가서 y = Sigmoid(z) 값으로 계산됨 → nn.Sigmoid()
3-1. 손실함수와 옵티마이저 정의
import torch.optim as optim
loss_function = nn.BCELoss() # 손실 함수 정의(이진 교차 엔트로피)
optimizer = optim.SGD(model.parameters(), lr=0.01) # 옵티마이저 정의(학습률 설정)
이진 교차 엔트로피(Binary Cross Entropy, BCE)
이진 분류 문제에서 모델의 예측 변수와 목표 변수 간의 차이를 측정하기 위해 사용되는 손실 함수
이진 분류 모델에서 최종 출력 값인 y는 시그모이드 함수의 계산 값이므로 0과 1 사이의 연속적인 값을 가진다. 따라서 해당 값을 0 또는 1로 분류하기 위해 선형 회귀에서 사용하는 손실 함수(MSE)와는 다른 손실 함수인 이진 교차 엔트로피(BCE)를 사용한다.
3-2. 모델 반복 학습
num_epochs = 500 # 에폭수 설정(500)
loss_list = [] # 손실값 저장할 리스트 생성
for epoch in range(num_epochs):
model.train() # 학습 모델 설정
epoch_loss = 0 # 에폭 손실 초기화
for batch_features, batch_labels in train_loader: # 배치단위
optimizer.zero_grad()
outputs = model(batch_features) # 예측값 생성
loss = loss_function(outputs, batch_labels) # 예측값과 실제값 간의 손실 계산
loss.backward() # 현재 손실에 대한 경사 계산
optimizer.step() # 파라미터 업데이트
epoch_loss += loss.item() # 에폭 손실 입력
loss_list.append(epoch_loss / len(train_loader)) # 손실값 저장
if (epoch+1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item()}')
4. 이진 분류 모델의 테스트
model.eval()
with torch.no_grad(): # 기울기(경사) 계산 없이 실행
predictions = model(x_test)
predicted_labels = (predictions > 0.5).float()
# 예측 결과와 실제 라벨을 출력
predicted_labels = predicted_labels.numpy()
actual_labels = t_test.numpy()
print("Predictions:", predicted_labels.flatten())
print("Actual Labels:", actual_labels.flatten())
>>> Predictions: [1. 1. 1. 0. 0. 0. 0. 1. 0. 0. 0. 0. 1. 0. 1. 0. 1. 1. 0. 0.]
>>> Actual Labels: [1. 1. 1. 0. 0. 0. 0. 1. 0. 0. 0. 0. 1. 0. 1. 0. 1. 1. 0. 0.]
# 예측 결과 시각화
plt.figure()
plt.scatter(range(len(actual_labels)), actual_labels, color='blue', label='Actual Labels')
plt.scatter(range(len(predicted_labels)), predicted_labels, color='red', marker='x', label='Predicted Labels')
plt.xlabel('Sample Index')
plt.ylabel('Label')
plt.legend()
plt.title('Actual vs Predicted Labels')
plt.show()
'NAVER Boostcamp AI Tech > 주간학습정리' 카테고리의 다른 글
Day 4. 선형 회귀(Linear Regression) (0) | 2024.08.25 |
---|---|
Day 3. Tensor 연산 및 심화 (0) | 2024.08.25 |
Day 2. Tensor 생성과 조작 (0) | 2024.08.25 |
Day 1. PyTorch 기초 (0) | 2024.08.25 |