[PyTorch] Logistic Regression 실습 : 모두를 위한 딥러닝 시즌2

2024. 9. 5. 15:14Artificial Intelligence/모두를 위한 딥러닝 (PyTorch)

Logistic Regression 이론 요약

Logistic Regression 구현 코드

라이브러리 import & 시드값 고정

# Library import
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

# To ensure experiment reproducibility
torch.manual_seed(1)

 
데이터 정의

# Data definition
x_data = [[1, 2], [2, 3], [3, 1], [4, 3], [5, 3], [6, 2]]
y_data = [[0], [0], [0], [1], [1], [1]]

x_train = torch.FloatTensor(x_data)  # (6, 2)
y_train = torch.FloatTensor(y_data)  # (6,)

print(x_train.shape)
print(y_train.shape)

 
지수 함수로 Hypothesis 계산

# torch.exp function resembles the exponential function
print('e^1 equals: ', torch.exp(torch.FloatTensor([1])))

W = torch.zeros((2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
hypothesis = 1 / (1 + torch.exp(-(x_train.matmul(W) + b)))

print(hypothesis)
print(hypothesis.shape)

 
시그모이드 함수로 Hypothesis 계산

print('1 / (1+e^{-1}) equals: ', torch.sigmoid(torch.FloatTensor([1])))

hypothesis = torch.sigmoid(x_train.matmul(W) + b)

print(hypothesis)
print(hypothesis.shape)

 
Cost 계산 :
1. 하나의 Element에 대해 직접 계산
2. 전체 Batch에 대해 직접 계산
3. BCE 함수로 계산

print(hypothesis)
print(y_train)

## Computing the Cost function
# 1. Calculate Cost for one element
-(y_train[0] * torch.log(hypothesis[0]) +
  (1 - y_train[0]) * torch.log(1 - hypothesis[0]))

# 2. Calculate Cost for the entire batch
losses = -(y_train * torch.log(hypothesis) +
           (1 - y_train) * torch.log(1 - hypothesis))
print(losses)

cost = losses.mean()
print(cost)

# 3. Calculate Cost with BCE function
F.binary_cross_entropy(hypothesis, y_train)

 
Sigmoid 함수와 BCE 함수를 활용한 학습

# Initialize model
W = torch.zeros((2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)

# Optimizer definition
optimizer = optim.SGD([W, b], lr=1)

nb_epochs = 1000
for epoch in range(nb_epochs + 1):
    
    # Calculate Cost
    hypothesis = torch.sigmoid(x_train.matmul(W) + b)  # or .mm or @
    cost = F.binary_cross_entropy(hypothesis, y_train)
    
    # Gradient Descent
    optimizer.zero_grad()
    cost.backward()
    optimizer.step()
    
    # Log every 100 iterations
    if epoch % 100 == 0:
        print('Epoch {:4d}/{} Cost: {:.6f}'.format(
            epoch, nb_epochs, cost.item()
        ))

 
모델 평가: 예측값과 실제값 비교 & Accuracy 계산

import numpy as np

# Evaluation of the model
hypothesis = torch.sigmoid(x_train.matmul(W) + b)

# # TypeError: set_printoptions() got an unexpected keyword argument 'sci_mode'
# torch.set_printoptions(precision=4, sci_mode=False)
# print(hypothesis[:5])

hypothesis_np = hypothesis[:5].detach().numpy()
np.set_printoptions(precision=4, suppress=True)  # suppress=True는 지수 표기법 비활성화
print(hypothesis_np)
# Compare Predicted value and Real value
prediction = hypothesis >= torch.FloatTensor([0.5])
print(prediction[:5])  # Predicted value
print(y_train[:5])  # Real value

# Accuracy
correct_prediction = prediction.float() == y_train
print(correct_prediction[:5])

 
BinaryClassifier(Rogistic Regression) 클래스를 활용한 전체 코드

class BinaryClassifier(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(2, 1)
        self.sigmoid = nn.Sigmoid()
        
    def forward(self, x):
        return self.sigmoid(self.linear(x))
    
model = BinaryClassifier()

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

nb_epochs = 100
for epoch in range(nb_epochs + 1):
    
    # Calculate H(x)
    hypothesis = model(x_train)
    
    # Calculate Cost
    cost = F.binary_cross_entropy(hypothesis, y_train)
    
    # Gradient Descent
    optimizer.zero_grad()
    cost.backward()
    optimizer.step()
    
    # Log every 100 iterations
    if epoch % 10 == 0:
        prediction = hypothesis >= torch.FloatTensor([0.5])
        correct_prediction = prediction.float() == y_train
        accuracy = correct_prediction.sum().item() / len(correct_prediction)
        print('Epoch {:4d}/{} Cost: {:.6f} Accuracy {:2.2f}%'.format(
            epoch, nb_epochs, cost.item(), accuracy * 100
        ))

실행 결과 분석

우선 첫 번째 학습 결과는 1000번의 iteration을 거쳐, Cost가 약 0.02로 감소했다. 두 번째 학습 결과는 100번의 iteration을 거쳐 약 0.129로 감소했으며, 예측 정확도는 점점 증가하다가 100%를 달성했다.

(좌) Sigmoid, BCE 함수를 활용한 학습 결과 / (우) BinaryClassifier를 활용한 학습 결과

느낀 점

  • 매번 다른 랜덤 값을 생성하지 않도록 시드값을 고정함으로써, 재현이 가능하게 함을 알게 되었다.
  • 이전 강의에서는 가설과 비용 계산을 중심으로 다루었다면, 이번 강의에서는 모델에 대한 성능 평가를 추가로 배울 수 있어서 좋았다. 실제값과 예측값을 출력해서 직접 비교해 보니, 정확도를 계산하는 이유가 더 와닿았다.
  • 강의에 나온 코드를 분명히 똑같이 작성했는데, 내 코드의 텐서 출력 형태가 지수 표기법을 따르고 있었다. 그래서 set_printoptions로 지수 표기법을 비활성화하는 방법을 사용했다. torch의 sci_mode 옵션에서 에러가 발생하여, 텐서를 numpy로 변환한 후 'suppress=True' 옵션으로 지수 표기법을 비활성화했다. 파이토치 공식 문서는 torch의 sci_mode를 사용할 수 있다는데, 왜 에러가 발생했는지 너무 궁금하다.

 
*참고 자료
모두를 위한 딥러닝 시즌2 - [PyTorch] Lab-05 Logistic Regression
https://youtu.be/HgPWRqtg254?feature=shared