머신러닝&딥러닝/이론

[머신러닝&딥러닝] 논리회귀(Logistic Regression)

거북이07 2024. 1. 12. 09:00

단항 논리회귀(Logistic Regression)

✨ 참고

https://wikidocs.net/57805

 

04-01 로지스틱 회귀(Logistic Regression)

일상 속 풀고자하는 많은 문제 중에서는 두 개의 선택지 중에서 정답을 고르는 문제가 많습니다. 예를 들어 시험을 봤는데 이 시험 점수가 합격인지 불합격인지가 궁금할 수도 있고, …

wikidocs.net

 

일상 속 풀고자하는 많은 문제 중에서는 두 개의 선택지중에 정답을 고르는 문제가 많다. 예를 들어 시험을 봤는데 시험 결과가 합격인지 불합격인지, 메일을 받았을 때 해당 메일이 스팸메일인지 정상 메일인지 이렇게 둘 중 하나를 결정하는 문제를 이진 분류(Binry Classification)라고 하는데 이 이진 분류를 풀기위한 대표적인 알고리즘이 로지스틱  회귀(Logistic Regression)이다.

 

로지스틱 회귀는 알고리즘의 이름은 회귀지만 실제로는 분류(Classigication) 작업에 사용할 수 있다.

이진 분류(Binary Classification)

아래와 같은 데이터가 있다고 가정해보자. 시험 커트라인은 공개되지 않은채로 이 데이터로부터 특정 점수를 얻었을 때의 합격, 불합격 여부를 판정하는 모델을 만들어보려고한다.

 

score(x) result(y)
45 0 (불합격)
50 0 (불합격)
55 0 (불합격)
60 1 (합격)
65 1 (합격)
70 1 (합격)
x_train = torch.FloatTensor([[45], [50], [55], [60], [65], [70]])
y_train = torch.FloatTensor([[0], [0], [0], [1], [1], [1]])

 

 

시험성적이 x_train 시험결과가 y_train으로 설정해주었다.

plt.figure(figsize=(8, 5))
plt.scatter(x_train, y_train)

 

위 데이터를 scatter 그래프로 시각화했을 때 아래와 같은 그래프가 나타나게된다.

이 점들을 이어보면 알파벳의 S자 형태로 표현되는데 이라헌 x와 y의 관계를  표현하기 위해서는 S자 형태로 표현할 수 있는 함수가 필요하다.

그래서 로지스틱 회귀는  선형 회귀 때의 H(x) = Wx + b가 아닌 S자 모양의 그래프를 만들 수 있는 특정 함수 f를 추가적으로 사용하여 H(x) = f(Wx + b)를 사용한다.

 

그리고 위와 같이 S자 모양의 그래프를 그릴 수 있는 함수 f로는 시그모이드 함수(Sigmoid function) 가 있다. 예를 들어 임계값을 0.5라고 정했을 때 출력값이 0.5 이상이면


시그모이드 함수(Sigmoind function)

시그모이드 함수의 방정식은 아래와 같다.

 

시그모이드 함수는 입력값이 한없이 커지면 1에 수렴하고 입력값이 한없이 작아지면 0에 수렴한다.

시그모이드 함수의 출력값은 0과 1 사이의 값을 가지는데 이 특성을 이용하여 분류  작업을 할 수 있다. 출력값이 0.5 이상이면 1(True), 0.5dlgkaus 0(False)로 판단한다.


비용 함수(Cost function)

비용 함수(Cost function)은 모델이 예측한 값과 실제 값 간의 차이를 나타내는 함수입니다. 이 함수를 최소화하려고 모델의 매개변수를 조정하여 학습을 진행한다.  보통 이진 분류 문제에서는 교차 엔트로피 손실(Binary Cross-Entropy Loss) 또는 로지스틱 손실(Logistic)이라고 불리는 함수를 사용합니다


파이토치로 로지스틱 회귀 구현

파이토치로 로지스틱 회귀를 구현해봅시다. 우선 구현에 필요한 도구들을 임포트한다.

import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
x_train = torch.FloatTensor([[45], [50], [55], [60], [65], [70]])
y_train = torch.FloatTensor([[0], [0], [0], [1], [1], [1]])

 

x_train, y_train을 텐서로 선언해줍니다.

print(x_train.shape)
print(y_train.shape)
torch.Size([6, 1])
torch.Size([6, 1])

 

선언한 x_train, y_train에 .shape를 써주면 차원 확인이  가능합니다. 현재  x_train, y_train은 6,1 행렬인것을 확인할 수 있습니다.

 

모델 구현

model = nn.Sequential(
    nn.Linear(1, 1), # 레이어
    nn.Sigmoid()
)

print(model)

 

 

 

선형회귀 모델과는 다르게 nn.Sequental() 함수안에 nn.Linear()함수와 시그모이드 함수를 선언해주어야한다.

여기서 nn.Sequention()각 레이어를 차례로 쌓아 하나의 신경망을 만들어주는 PyTorch의 클래스입니다.

 

nn.Linear() 선형  변환 레이어로 해당 코드에서는 입력 차원과 출력 차원이 1입니다. 이 레이어는 입력에 대해 가중치를 곱하고 편향을 더한 후 그 결과를 출력하며 이 경우에는 단일 특성을 입력받아 하나의 출력을 반환하는 간단한 선형 변환을 수행합니다.

 

nn.Sigmoind()는 시그모이드 활성화 함수를 적용하는 레이어이며 시그모이드 함수는 출력을 0과 1 사이로 압축시키는 역할을 합니다. 해당 함수는 이진 분류 문제에서 확률 값을 얻기 위해 사용할 수 있습니다.

 

print(list(model.parameters()))

 

model.parameter()를 호출하여 모델의 학습 가능한 매개변수들을 확인한 결과 두 개의 매개변수가 출력되었는데요

이 매개변수들은 선형 회귀 모델의 W(가중치, weight), b(편향, bias)에 해당합니다.

 

아래 출력값을 보면 w가 0.0634, b는 0.6625가 나온 모습을 확인할 수 있습니다.

[Parameter containing:
tensor([[0.0634]], requires_grad=True), Parameter containing:
tensor([0.6625], requires_grad=True)]

비용함수 구하기

논리 회귀에서는 Binary Cross-Entropy, 즉 nn.BCELoss()함수를 사용하여 Loss를  계산합니다.

nn.BCELoss() 함수는 모델의 출력(확률)과 실제 라벨 간의 크로스 엔트로피를 계산하는데요 이 함수는 주로 시그모이드 활성화 함수와 함께 사용되며, 출력값이 0또는 1에 가까워지면서 손실을 증가시킵니다.

 

따라서 BCELoss는 모델이 정확한 예측을 하지 못할 때 큰 손실을 발생시키도록하여 모델이 학습하도록 도와줍니다.

 

간단하게 말해서 'nn.BCELoss()'는 이진 분류 문제에서 모델이 얼마나 잘 예측하고 있는지를 측정하는데 사용되며 이를 최소화 하는 방향으로 모델을 학습시킵니다.

model = nn.Sequential(
    nn.Linear(1, 1), # 레이어
    nn.Sigmoid()
)

print(model)

 

선형회귀 모델과는 다르게 nn.Sequental() 함수안에 nn.Linear()함수와 시그모이드 함수를 선언해주어야한다.

여기서 nn.Sequention() 각 레이어를 차례로 쌓아 하나의 신경망을 만들어주는 PyTorch의 클래스입니다.

 

nn.Linear() 선형  변환 레이어로 해당 코드에서는 입력 차원과 출력 차원이 1입니다. 이 레이어는 입력에 대해 가중치를 곱하고 편향을 더한 후 그 결과를 출력하며 이 경우에는 단일 특성을 입력받아 하나의 출력을 반환하는 간단한 선형 변환을 수행합니다.

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

모델 학습시키기

optim.SGD()는 확률적 경사 하강법(Stochastic Gradient Descent) 옵티마이저를 생성하는 함수입니다. 

매개변수로 model.paramerers()를 입력해주었는데 이 값은 모델의 학습 가능한 매개변수들을 가져오는 함수로, SGD 옵티마이저에게 최적화할 변수들을 전달합니다.

 

lr=0.01은 학습률(learning rate)을 의미하며 각 경사 하강법 단계에서 얼마나 많이 업데이트를 수행할지를 조절합니다.

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}')

 

epochs는 전체 데이터셋을 면 번 반복해서 학습할지 결정하는 횟수인데 epochs가 높을수록 학습을 더 많이 시킨다.

 

y_pred = model(x_train)은 모델에  입력 데이터인 x_train을 전달하여 예측값을 얻습니다.

 

loss = nn.BCELoss()(y_pred, y_train) BCELoss를 사용하여 예측 값과 실제 라벨(종속변수)간의 이진 교차 엔트로피 손실을 계산한다.

 

optimizer.zero_grad() 기울기를 0으로 초기화해주는 함수이며 기울기를 초기화해주지 않을 경우 기울기가 중첩되므로 학습시킬때 문제가 발생한다.

 

loss.backward() 역전파를 수행하여 각 파라미터에 대한 손실의 기울기(gradient)를 계산해줍니다.

 

optimizer.step() 계산된 기울기를 사용하여 파라미터를 업데이트합니다.

 

이렇게 단항 논리회귀에 대해 알아보았습니다. 


다항 논리회귀(Logistic Regression)

다항 논리회귀는 입력값이 여러개인 기법이다.

 

코드상으로 단항과 다른점은 Sigmoid() 함수대신 Softmax()함수를 쓴다는점과  비용함수를 구할 때 BCELoss()  대신 CrossEntropyLoss() 를 사용한다는  점 이다.