1. 단항 논리회귀(Logistic Regrssion)실습

  • 분류를 할 때 주로 사용하며, 선형 회귀 공식으로부터 나왔기 때문에 논리회귀라는 이름이 붙여짐
  • 직선 하나(선형 회귀)를 사용해서 분류를 예측한다면 제대로 에측하지 못할 경우가 많음
  • Sogmoid 함수(Logistic 함수)를 사용하여 정확도를 높일 수 있음

 

2. 시그모이드(Sigmoid)함수

  • 예측값을 0에서 1사이의 값이 되도록 만듦
  • 0에서 1사이의 연속된 값을 출력으로 하기 때문에 보통 0.5를 기준으로 구분

실습 진행

1. 필요 모듈 임포트

import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt

# seed값 고정
torch.manual_seed(2024)
x_train = torch.FloatTensor([[0], [1], [3], [5], [8], [11], [15], [20]])
y_train = torch.FloatTensor([[0], [0], [0], [0], [0], [1], [1], [1]])

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

# 결과값 => 
# torch.Size([8, 1])
# torch.Size([8, 1])
plt.figure(figsize=(8, 5))
plt.scatter(x_train, y_train)

 

▷ 공식 사진

 

model = nn.Sequential(
    nn.Linear(1, 1),
    nn.Sigmoid()
)

print(model)

# 결과값 => 
# Sequential(
#   (0): Linear(in_features=1, out_features=1, bias=True)
#   (1): Sigmoid()
# )

# --------------------------------------------

print(list(model.parameters()))

# 결과값 => 
# [Parameter containing:
# tensor([[0.0634]], requires_grad=True), Parameter containing:
# tensor([0.6625], requires_grad=True)]

3. 비용 함수

  • 논리회귀에서는 nn.BCELoss() 함수를 사용하여 Loss를 계산함
  • Binary Cross Entropy

▷공식 사진


비용 함수 실습 진행

y_pred = model(x_train)
y_pred

# 결과값 => 
# tensor([[0.6598],
#         [0.6739],
#         [0.7012],
#         [0.7270],
#         [0.7631],
#         [0.7958],
#         [0.8340],
#         [0.8734]], grad_fn=<SigmoidBackward0>)

# -----------------------------------------

loss = nn.BCELoss()(y_pred, y_train)
loss

# 결과값 => tensor(0.8364, grad_fn=<BinaryCrossEntropyBackward0>)
optimizer = optim.SGD(model.parameters(), lr=0.01)

epochs = 1000

for epoch in range(epochs + 1):
    y_pred = model(x_train)
    loss = nn.BCELoss()(y_pred, y_train)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    if epoch % 100 == 0 :
        print(f'Epoch: {epoch}/{epochs} Loss: {loss: .6f}')
        
# 결과값 => 
# Epoch: 0/1000 Loss:  0.836359
# Epoch: 100/1000 Loss:  0.725281
# Epoch: 200/1000 Loss:  0.645149
# Epoch: 300/1000 Loss:  0.579139
# Epoch: 400/1000 Loss:  0.524739
# Epoch: 500/1000 Loss:  0.479715
# Epoch: 600/1000 Loss:  0.442194
# Epoch: 700/1000 Loss:  0.410667
# Epoch: 800/1000 Loss:  0.383936
# Epoch: 900/1000 Loss:  0.361067
# Epoch: 1000/1000 Loss:  0.341329
print(list(model.parameters()))

# 결과값 => 
# [Parameter containing:
# tensor([[0.2019]], requires_grad=True), Parameter containing:
# tensor([-1.4572], requires_grad=True)]

# ------------------------------------------------

x_test = torch.FloatTensor([[12]])
y_pred = model(x_test)
print(y_pred)

# 결과값 => tensor([[0.7243]], grad_fn=<SigmoidBackward0>)

# ------------------------------------------------

# 임계치 설정하기
# 0.5보다 크거나 같으면 1
# 0.5보다 작으면 0
y_bool = (y_pred >= 0.5).float()
print(y_bool)

# 결과값 => tensor([[1.]])

다항 논리회귀 실습

x_train = [[1, 2, 1, 1],
           [2, 1, 3, 2],
           [3, 1, 3, 4],
           [4, 1, 5, 5],
           [1, 7, 5, 5],
           [1, 2, 5, 6],
           [1, 6, 6, 6],
           [1, 7, 7, 7]]
y_train = [0, 0, 0, 1, 1, 1, 2, 2]

x_train = torch.FloatTensor(x_train)
y_train = torch.LongTensor(y_train)
print(x_train.shape)
print(y_train.shape)

# 결과값 => 
# torch.Size([8, 4])
# torch.Size([8])
model = nn.Sequential(
    nn.Linear(4, 3) 
    # 3을 적는 이유 리니어 회귀에서 몇개의 0, 1, 2 이렇게 나온 클래스 만큼의 갯수를 적는거다.
)

print(model)

# 결과값 => 
# Sequential(
#   (0): Linear(in_features=4, out_features=3, bias=True)
# )
# 예측
y_pred = model(x_train)
print(y_pred)

# 학습이 안된 내용이 나옴

# 결과값 => 

# tensor([[-0.3467,  0.0954, -0.5403],
#         [-0.3109, -0.0908, -1.3992],
#         [-0.1401,  0.1226, -1.3379],
#         [-0.4850,  0.0565, -2.1343],
#         [-4.1847,  1.6323, -0.7154],
#         [-2.4318,  1.2809, -0.8234],
#         [-4.2877,  1.7462, -0.8999],
#         [-5.1520,  2.1004, -0.9593]], grad_fn=<AddmmBackward0>)
# 다항 논리회귀에서는 BCELoss() 대신 CrossEntropyLoss()를 사용
# 소프트맥스 함수가 포함되어 있음
loss = nn.CrossEntropyLoss()(y_pred, y_train)
print(loss)

# 결과값 => tensor(1.2130, grad_fn=<NllLossBackward0>)
optimizer = optim.SGD(model.parameters(), lr=0.1)

epochs = 1000

for epoch in range(epochs + 1):
    y_pred = model(x_train)
    loss = nn.CrossEntropyLoss()(y_pred, y_train)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    if epoch % 100 == 0 :
        print(f'Epoch: {epoch}/{epochs} Loss: {loss: .6f}')
        
# 결과값 => 
# Epoch: 0/1000 Loss:  1.212971
# Epoch: 100/1000 Loss:  0.629261
# Epoch: 200/1000 Loss:  0.556415
# Epoch: 300/1000 Loss:  0.505015
# Epoch: 400/1000 Loss:  0.462015
# Epoch: 500/1000 Loss:  0.423341
# Epoch: 600/1000 Loss:  0.386988
# Epoch: 700/1000 Loss:  0.351581
# Epoch: 800/1000 Loss:  0.316010
# Epoch: 900/1000 Loss:  0.279698
# Epoch: 1000/1000 Loss:  0.247014
x_test = torch.FloatTensor([[1, 7, 8, 7]])
y_pred = model(x_test)
print(y_pred)

# 결과값 => tensor([[-10.2333,   0.3633,   5.1844]], grad_fn=<AddmmBackward0>)

# --------------------------------------------------------

# 예측값과 확률 구하기
y_prob = nn.Softmax(1)(y_pred) # 1은 차원을 뜻함.
y_prob

# 결과값 => tensor([[1.9985e-07, 7.9936e-03, 9.9201e-01]], grad_fn=<SoftmaxBackward0>)

# --------------------------------------------------------

print(f'0일 확률: {y_prob[0][0]:.2f}')
print(f'1일 확률: {y_prob[0][1]:.2f}')
print(f'2일 확률: {y_prob[0][2]:.2f}')

# 결과값 => 
# 0일 확률: 0.00
# 1일 확률: 0.01
# 2일 확률: 0.99

# --------------------------------------------------------

torch.argmax(y_prob, axis=1)

# 결과값 => tensor([2])