PyTorch와 이진 분류

J. Hwang·2024년 8월 11일
0

이진 분류 (Binary classification) 는 두 가지 범주 사이의 관계를 학습하여 데이터를 입력받았을 때 두 범주 중 하나로 분류하는 것이다. 이메일 스팸 분류 (스팸 / 스팸 아님), 병 진단 (양성 / 음성) 등이 이진 분류의 예시이다.

Discrete labeling

이진 분류에서는 목표 변수를 이산형 라벨로 매핑하는 것이 중요하다. 예를 들어 병 진단 자료라면, positive / negative 로 표현된 데이터를 negative → 0, positive → 1로 변환해주는 것이다.

# df = data to use [pandas DataFrame]

df.loc[:, 'Diagnosis'] = df['Diagnosis'].map({'Negative': 0, 'Positive': 1})

그리고 데이터를 특징 변수와 목표 변수로 나누자.

x = df[['Germ']].values
y = df[['Diagnosis']].values.astype(int)    

데이터 분할

전체 데이터를 training set, test set으로 나누어야 한다.

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)

위의 코드로 전체 데이터를 training set : test set = 8 : 2의 비율로 나눌 수 있다.
데이터 분할은 엄밀하게 따지면 보다 복잡한데, 더 자세한 내용은 cross validation을 참고하자.

☆ 검증 데이터 (validation set) 도 있는데, 모델의 학습 과정 중 성능을 평가하는 데 사용되는 데이터로서, 모델의 1 에폭마다 overfitting 여부를 확인하기 위해 사용한다.


데이터 표준화

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
x_train = scaler.fit_transform(x_train)
x_test = scaler.transform(x_test)

y의 경우 0, 1로만 표현되는 데이터이므로 표준화(스케일링)가 필요없다.

이제 데이터를 tensor로 만들자.

x_train = torch.tensor(x_train, dtype=torch.float32)
x_test = torch.tensor(x_test, dtype=torch.float32)
y_train = torch.tensor(y_train, dtype=torch.float32).unsqueeze(1)
y_test = torch.tensor(y_test, dtype=torch.float32).unsqueeze(1)

이진 분류 모델 구축

이진 분류에는 Logistic regression이 사용된다. (자세한 것은 여기의 logistic regression 부분 참조) logistic regression은 sigmoid function (y=11+exp(x)y = \frac{1}{1+exp(-x)})를 사용하여 분류 경계를 설정하고 입력된 값을 0과 1 사이의 값으로 변환하여, 0에 가까운 값을 A group, 1에 가까운 값을 B group으로 분류한다.

다만 최적의 분류 경계를 설정하기 위해서 우선 선형 경계 찾기(→ linear regression)를 먼저 수행하게 된다. 이를 이용해 이진 분류 모델을 PyTorch로 구현한 코드는 아래와 같다.

import torch.nn as nn

class BinaryClassificationModel(nn.Module):    # 부모 class의 기능을 상속받은 자식 class
    def __init__(self):
        super(BinaryClassificationModel, self).__init__()      # 부모 class인 nn.Module의 생성자를 호출하여 상속받은 모든 초기화 작업을 수행
        self.layer_1 = nn.Linear(1, 1)  # 입력 차원과 출력 차원이 1
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        z = self.layer_1(x)
        y = self.sigmoid(z)
        return y
        
model = BinaryClassificationModel()

Loss function

이진 분류 모델에서 sigmoid function을 사용하면 최종 출력값이 0~1 사이의 값이 된다. 따라서 선형 회귀 때와는 다르게 이진 교차 엔트로피 (Binary Cross Entropy; BCE) 를 손실 함수로 사용한다.

E(w,b)E(w, b) = i=1n- \sum_{i = 1}^{n} {ytrue,iy_{true, i} loglog ypredicted,iy_{predicted, i} + (1ytrue,i)(1 - y_{true, i}) loglog (1ypredicted,i)(1 - y_{predicted, i})}

BCE가 왜 이런 형태인지는 조건부 확률, maximum likelihood estimation 을 참고.

PyTorch에서 BCE를 구현한 코드는 아래와 같이 사용할 수 있다.

import torch.optim as optim

loss_function = nn.BCELoss()

Optimization

본격적으로 학습을 하기 전에, Dataset과 DataLoader로 정리한 데이터를 이용하도록 하자.

from torch.utils.data import Dataset, DataLoader

class MyDataset(Dataset):      
    def __init__(self, x_tensor, y_tensor):     
        self.x = x_tensor
        self.y = y_tensor

    def __len__(self):     
        return len(self.x)

    def __getitem__(self, idx):     
        return self.x[idx], self.y[idx]
        
train_dataset = MyDataset(x_train, y_train)
test_dataset = MyDataset(x_test, y_test)

batch_size = 32  
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

이제 본격적으로 학습 데이터로 최적화를 시켜 학습을 진행한다.

# optimizer로 stochastic gradient descent를 사용해 학습하자.
optimizer = optim.SGD(model.parameters(), lr=0.01)

num_epochs = 100
loss_list = []
for epoch in range(num_epochs):
    model.train()
    epoch_loss = 0
    for x, y in train_loader:
        optimizer.zero_grad()
        outputs = model(x)
        loss = loss_function(outputs, y)
        loss.backward()
        optimizer.step()
        epoch_loss += loss.item()

    loss_list.append(epoch_loss / len(train_loader))

모델 테스트

model.eval()
with torch.no_grad():
    prediction = model(x_test)
    predicted_labels = (prediction > 0.5).float()    # 0.5보다 크면 1로 분류, 0.5보다 작거나 같으면 0으로 분류
profile
Let it code

0개의 댓글