TIL | Logistic Regression |
로지스틱 회귀란?

[ AI / ML ] 로지스틱 회귀

👩🏻‍💻 K-MOOC 실습으로 배우는 머신러닝 강의

📙 해당 포스트는 K-MOOC 강의 내용과 추가로 다른 자료들을 찾아 내용을 작성하였으며, 이론 및 개념에 대해 공부하고 예제 실습도 진행한 후 내용을 정리하였다.


Logistic Regression

로지스틱 회귀는 이름에 회귀가 들어가지만 분류 Classification 작업에 사용할 수 있다.

이진 분류 Binary Classification

컴퓨터공학과 학생들이 중간고사로 알고리즘 시험을 봤다고 가정을 해보자. 이때 Pass와 Fail로 성적이 나뉘는데, 그 커트라인은 공개되지 않았다. 이때, 로지스틱 회귀를 이용해서 넘겨받은 학생들의 데이터로부터 특정 점수를 얻었을 때 Pass와 Fail을 판정하는 모델을 만들 수 있다.

해당 내용을 선형으로 나타내면 분류 작업이 잘 작동하지 않는다. 로지스틱 회귀는 S자 모양의 그래프를 만들어서 분류 예측 작업을 진행한다. S자 모양의 그래프로 만들어 줄 수 있는 함수 중 하나인 시그모이드에 대해 알아보자.


Sigmoid 함수

선형 회귀와 마찬가지로 로지스틱 회귀 역시 최적의 W와 b를 찾는 것이 목표이다.

H ( x ) = s i g m o i d ( W x + b ) = 1 1 + e ( W x + b ) = σ ( W x + b )

시그모이드 함수는 입력값이 한없이 커지면 1에 수렴하고, 입력값이 한없이 작아지면 0에 수렴한다. 시그모이드 함수의 출력값은 0과 1 사이의 값을 가지는데 이 특성을 이용하여 분류 작업에 사용할 수 있다. 출력값을 확률이라고 생각을 하면 해당 레이블에 속할 확률이 50%가 넘으면 해당 레이블로 판단하고, 해당 레이블에 속할 확률이 50%보다 낮으면 아니라고 판단하는 것으로 볼 수 있다. 이와 마찬가지로 multi-class classification도 로지스틱 회귀를 이용해 수행할 수 있다.

비용 함수 Cost Function

이제 아래의 가설에서 최적의 W와 b를 찾을 수 있는 cost function을 정의해야 한다.


[ 로지스틱 회귀 가설 ]

H ( x ) = s i g m o i d ( W x + b )

🤔 선형 회귀와 로지스틱 회귀의 cost function?

➡️ 선형 회귀에서 사용했던 cost function 평균 제곱 오차 MSE : Mean Squared Error를 로지스틱 회귀의 cost function으로 사용하면 안될까?

다음은 선형 회귀에서 사용했던 MSE의 수식이다.


[ Mean Squared Error ]

c o s t ( W , b ) = 1 n i = 1 n [ y ( i ) H ( x ( i ) ) ] 2

위의 cost function 수식에 로지스틱 회귀의 가설을 대입하고 미분하면 심한 비볼록 (non-convex) 형태의 그래프가 나온다.

위와 같은 그래프는 경사 하강법을 사용했을 때 Local minimum을 Global minimum 값이라고 착각해서 최적의 가중치 W가 아닌 다른 값을 택하면 모델의 성능이 낮게 나올 수 있다. cost가 최소가 되는 가중치 W를 찾은 것이 아니기 때문에 cost function의 목적에 맞지 않다.

시그모이드 함수는 0과 1 사이의 값을 출력한다. 실제값이 1일 때 예측값이 0에 가까워지면 오차가 커져야 하고, 실제값이 0일 때 예측값이 1에 가까워지면 오차가 커져야 한다. 이를 충족시키는 함수가 바로 로그 함수이다. 이를 이용해 cost function을 지정해줄 수 있다.

이를 식으로 나타내면

if  y = 1 cost ( H ( x ) , y ) = log ( H ( x ) ) if  y = 0 cost ( H ( x ) , y ) = log ( 1 H ( x ) )

로 나타낼 수 있다. 그럼 이 두 수식을 하나의 수식으로 통합해보자.


[ 로지스틱 회귀 Cost Function ]

cost ( H ( x ) , y ) = [ y l o g H ( x ) + ( 1 y ) l o g ( 1 H ( x ) ) ]

위의 cost function에 대해 경사 하강법을 수행하면서 최적의 가중치 W를 찾는다. 이를 수식으로 나타내면 다음과 같다.


[ 최적의 W 구하기 ]

W := W α W c o s t ( W )

💻 실습 예제 코드

PyTorch로 로지스트 회귀 구현하기

[ 필요한 도구 import ]
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

torch.manual_seed(1)
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)
y_train = torch.FloatTensor(y_data)


[ W와 b 값 초기화 ]

W와 b를 전부 0으로 초기화해주자.

W = torch.zeros((2, 1), requires_grad=True)  # 크기는 2 x 1
b = torch.zeros(1, requires_grad=True)

W와 b를 전부 0으로 초기화해준 상태에서 예측하면 예측값은 전부 0.5가 나온다.

# hypothesis = 1 / (1 + torch.exp(-(x_train.matmul(W) + b)))
hypothesis = torch.sigmoid(x_train.matmul(W) + b)
print(hypothesis)  # 예측값인 H(x) 출력
tensor([[0.5000],
        [0.5000],
        [0.5000],
        [0.5000],
        [0.5000],
        [0.5000]], grad_fn=<MulBackward>)


[ 모델 훈련 ]

이제 모델을 훈련해보자.

# 모델 초기화
W = torch.zeros((2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
# optimizer 설정
optimizer = optim.SGD([W, b], lr=1)

nb_epochs = 1000
for epoch in range(nb_epochs + 1):

    # Cost 계산
    hypothesis = torch.sigmoid(x_train.matmul(W) + b)
    # cost = -(y_train * torch.log(hypothesis) +
    #          (1 - y_train) * torch.log(1 - hypothesis)).mean()
    cost = F.binary_cross_entropy(hypothesis, y_train)


    # cost로 H(x) 개선
    optimizer.zero_grad()
    cost.backward()
    optimizer.step()

    # 100번마다 로그 출력
    if epoch % 100 == 0:
        print('Epoch {:4d}/{} Cost: {:.6f}'.format(
            epoch, nb_epochs, cost.item()
        ))
Epoch    0/1000 Cost: 0.693147
... 중략 ...
Epoch 1000/1000 Cost: 0.019852


위의 코드에서는 직접 벡터 계산을 한 후에 torch의 Sigmoid 함수를 적용해줬지만 아래 코드처럼 nn.Module을 이용해서 한 번에 연결해서 계산할 수도 있다. 난 이 방법이 더 간단하고 나중에 layer을 쌓기도 편해서 nn.Module을 많이 쓴다. 그리고 뒤에 인공 신경망을 더 배우면서 나오겠지만, 시그모이드 함수는 인공 신경망의 은닉층에서는 거의 사용되지 않는다.

model = nn.Sequential(
   nn.Linear(2, 1), # input_dim = 2, output_dim = 1
   nn.Sigmoid() # 출력은 시그모이드 함수를 거친다
)

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

nb_epochs = 1000
for epoch in range(nb_epochs + 1):

    # H(x) 계산
    hypothesis = model(x_train)

    # cost 계산
    cost = F.binary_cross_entropy(hypothesis, y_train)

    # cost로 H(x) 개선
    optimizer.zero_grad()
    cost.backward()
    optimizer.step()

    # 20번마다 로그 출력
    if epoch % 10 == 0:
        prediction = hypothesis >= torch.FloatTensor([0.5]) # 예측값이 0.5를 넘으면 True로 간주
        correct_prediction = prediction.float() == y_train # 실제값과 일치하는 경우만 True로 간주
        accuracy = correct_prediction.sum().item() / len(correct_prediction) # 정확도를 계산
        print('Epoch {:4d}/{} Cost: {:.6f} Accuracy {:2.2f}%'.format( # 각 에포크마다 정확도를 출력
            epoch, nb_epochs, cost.item(), accuracy * 100,
        ))


[ 예측 ]

학습 끝! 학습 후 최적의 W와 b를 가지고 예측값을 구해보자.

hypothesis = torch.sigmoid(x_train.matmul(W) + b)
print(hypothesis)
tensor([[2.7648e-04],
        [3.1608e-02],
        [3.8977e-02],
        [9.5622e-01],
        [9.9823e-01],
        [9.9969e-01]], grad_fn=<SigmoidBackward>)


마지막으로, test data에 대해서 예측까지!!

prediction = hypothesis >= torch.FloatTensor([0.5])
print(prediction)

다음 포스트에서 만나요 🙌



참고

K-MOOC 실습으로 배우는 머신러닝

Wikidocs

7 HIDDEN LAYERS

 

Related Posts



💙 You need to log in to GitHub to write comments. 💙
If you can't see comments, please refresh page(F5).