본문 바로가기
머신러닝,딥러닝/ML 회귀분석(이상엽 교수님)

로지스틱 회귀 모형: 함수 개념 및 코딩 실습

by 코듀킹 2023. 5. 29.
목차
1. 로지스틱 회귀(Logistic Regression) 모형
2. 실습: 로지스틱 회귀 모형 학습

 

지금까지 지도학습 알고리즘 중에 선형 회귀 모형에 대해서 알아보았다. 학습 데이터와 문제 데이터 준비 단계, 피처 엔지니어링(표준화, Min-max정규화) 단계, 알고리즘 선택(선형회귀, Lasso, Ridge, ElasticNet) 단계, 학습 단계에서 비용함수(MSE)로 파라미터 최적값 찾는 과정, 성능 평가 단계(\(R^2\)), 과적합 문제를 해결하는 규제화(L1, L2 penalty term) 방법까지 알아보았다.(아래 더 보기 참고)

 

 

오늘은 지도학습 알고리즘 중에 회귀 문제가 아닌, 분류 문제를 만났을 때, 선택할 수 있는 로지스틱 회귀 모형에 대해서 알아보겠다. 그리고 그 다음시간에는 다항 분류 문제 학습을 위해 문자 데이터를 숫자 데이터로 바꾸고, 따로 컬럼을 추가하는 방법인 더미코딩에 대해서도 알아보겠다.

 

 

1. 로지스틱 회귀(Logistic Regression) 모형

로지스틱 회귀 모형은 분류문제를 풀기 위해 사용하는 지도학습 모델이다. 범주형 변수같은 경우엔 값의 수가 2개인 경우가 있고(ex. 남녀), 3개 이상인 경우(ex. 서울, 부산, 광주 등)가 있다. 취하는 값의 수가 2개 일 때 사용하는 모델을 이항 로지스틱 회귀 모델이라고 하고, 3개 이상일 때 사용하는 모델을 다항 로지스틱 회귀 모델이라고 한다. 2개일 때는 로지스틱 함수를, 3개일 때는 소프트맥스 함수를 사용한다.(이 글에서는 로지스틱 함수만 설명하겠다.)

 

분류 문제 또한, 회귀 문제와 같이 지도학습 문제이기 때문에 데이터에 흰트 정보(=feature)와 정답 정보(=target)가 포함되어 있다. 그렇기 때문에 이 둘의 관계를 파악하기 위한 수학적 모형이 존재하는데, 로지스틱 회귀 모형에서는 학습단계에서 교차 엔트로피 비용함수를 이용하여 둘의 관계를 가장 잘 설명하는 파라미터의 최적값을 찾게 된다. 

 

교차 엔트로피 비용함수를 이해하기 위해서 우선 로지스틱 함수에 대해서 알아보자. 로지스틱 함수는 아래와 같은 형태를 띄고 있다. 이항 분류 문제에서는 종속변수 y의 값이 0과 1밖에 존재하지 않기 때문에 y가 1일 때의 경우와 y가 0일 때의 경우의 확률을 나타내는 함수이다.(P는 확률을 의미한다.)

 

$$P(y=1|X) = \frac{1}{1+e^{-z}} $$

$$P(y=0|X) = 1- \frac{1}{1+e^{-z}} $$

 

 

두 그래프가 정확하게 반비례 관계인 점을 알 수 있다. 두 그래프를 더하면, 모든 z지점에 대해 y의 값이 정확하게 1이 된다. 이 함수가 바로, 로지스틱 함수이다.(빨간색 그래프) 정확하게 이야기 하면, 로지스틱 함수는 표준 로지스틱 분포의 누적분포 함수(CDF)로, 종속변수의 값이 범주형일 때, 독립변수와의 관계를 확률로 나타낼 수 있는 함수이다. 이는 시그모이드 함수와도 똑같이 생겼다.

 

그렇다면, z는 무엇일까? z는 파라미터 값과 독립변수의 값을 합한 변수이다. 

 

$$ z = b_0 + b_1X_1 + b_2X_2 + ... + b_kX_k $$

 

여기에서는 이해를 돕기 위해서, 독립변수가 하나이고, \(b_0\)는 0이라고 가정하겠다. 그러면, \(z = b_1X_1\)이 된다. 이를 다시 로지스틱 함수에 대입해서 그래프를 그려 보면, \(b_1\)이 높아질수록, 그래프의 기울기가 높아지고, \(b_1\)값이 낮을수록 그래프의 기울기 완만해진다.

 

 

이제 실제 예시를 들어서 더 자세하게 설명해보겠다. 나이(독립변수 = X)에 따른 코로나 감염여부(종속변수=y)를 나타내는 아래와 같은 데이터가 있다고 해보자. 감염이 되었다면 1, 감염이 되지 않았다면 0으로 데이터가 설정되어 있다. 종속 변수 y가 0과 1로만 이루어져 있기 때문에 선형회귀 모형 같은 선형 그래프가 그려질 수 없다는 걸 바로 알 수 있다.

 

나이(X1) 감염 여부(y)
20 0
30 0
40 1
50 1

왼쪽: X와 y의 관계 / 오른쪽: X만 표준화 시켰을 때의 X와 y의 관계

 

이러한 분류 문제같은 경우엔 로지스틱 회귀 모형을 통해 그래프를 그릴 수 있다. 

 

$$P(y=1|X) = \frac{1}{1+e^{-b_1X_1}} $$

 

표에서 독립변수인 나이(X)를 표준화 시킨 값과 y의 점과 \(b_1\)값을 임의로 14로 설정한 로지스틱 함수를 함께 그래프에 나타내면 다음과 같이 그려진다.

 

 

위 그래프를 해석하면, 나이가 10일 경우엔 감염됐을\((y=1|X)\) 확률이 0%, 20일 경우엔 약 2%, 35일 경우엔 50%, 40일 경우엔 약 98%, 50일 경우엔 100%라는 뜻이다. 반대로, 나이가 10인데 감염되지 않았을\((y=0|X)\) 확률은 100%, 20일 경우엔 약 98%, 35일 경우엔 50%... 이런 식이다. 이처럼 로지스틱 함수는 독립변수와 종속변수의 관계를 확률로 나타낼 수 있다.

 

그러면, 이제 교차 엔트로피 비용함수를 공개하겠다.

 

$$ E = -\left[\sum_{i=1}^{N}y_i\ln p(y_i = 1|X) + (1 - y_i)\ln p(y_i = 0|X)\right] $$

 

짜잔! 매우 복잡해보이는데, 우리는 이미 위에서 로지스틱 함수를 배운 상태이다. 그러므로, 아래 식이 의미하는 바가 어떤 것이 지 알 수 있다. 전체 관측된 나이 X1에 대해서 감염이 됐을 확률을 의미한다. 그리고 이 확률을 로지스틱 함수로 나타낼 수 있었다.

 

$$p(y_i=1|X)$$

 

근데, 여기에 지수 함수의 로그인 \(ln\)을 씌우고, 계산하게 되면 아래와 같은 식이 완성된다. 

 

$$\ln p(y_i=1|X) = \ln (\frac{1}{1+e^{-b_1X_1}}) = -\ln(1+e^{-b_1X_1})$$

 

그리고 교차 엔트로피의 뒷 부분 식을 다르게 표현하면, 다음과 같이 변환된다.

 

$$ \ln p(y_i = 0|X)  = \ln (1-\frac{1}{1+e^{-b_1X_1}}) = -\ln(1+e^{b_1X_1})$$

 

이제 \(y_i, (1 - y_i)\) 이 부분을 보자. 이 부분이 의미는 바는 실제 종속변수의 값과 1에서 실제 종속변수의 값을 뺀 값을 의미한다. 한마디로 종속변수가 1이면(감염된 사람), \(y_i\)는 1이 되고, \((1 - y_i)\)는 0이 되므로, 교차 엔트로피 함수의 앞부분인 \(-\ln(1+e^{-b_1X_1})\) 이 부분만 계산하면 되고, 종속변수가 0이면(감이 안된 사람), \(y_i\)는 0이 되고, \((1 - y_i)\)는 1이 되므로, 교차 엔트로피 함수의 뒷부분인 \(-\ln(1+e^{b_1X_1})\)  이 부분만 계산하면 된다는 뜻이다.

 

그러면 여기에 테이블에 해당되는 값들을 전부 대입해서 계산을 해보겠다. 그러면 최종적으로 결국 아래와 같은 식이 되는데, 우리는 여기서 이 교차엔트로피의 값이 최솟값이 되는 지점인 \(b_1\)의 최적값을 찾으면 끝난다.

 

$$ \ln\left(1+e^{10b_1}\right)+\ln\left(1+e^{20b_1}\right)+\ln\left(1+e^{-30b_1}\right)+\ln\left(1+e^{-40b_1}\right) $$

 

위 식을 그래프로 나타면 어떻게 될까? 놀랍게도, MSE처럼 밑으로 볼록한 형태를 띤 그래프가 그려진다. 이 비용함수의 최솟값은 최대우도 추정 방법(MLE)을 통해서 찾아낼 수 있다.(이 부분은 설명이 길어져서 생략하겠다.)

 

로지스틱 회귀 모형과 데이터를 적용시켜 교차 엔트로피 함수와 파라미터(b1)의 관계를 나타낸 그래프
y가 1인 경우와, y가 0인경우를 따로따로 그래프로 나타냈을 땐, 위와 같은 그래프가 그려진다.

 

2. 실습: 로지로틱 회귀 모형 학습

이론적인 부분은 어느정도 설명했으니, 이제 로지스틱 회귀 모형을 사용해서 직접 파이썬으로 학습을 시켜보자. 위에서 사용한 예시 그대로 데이터를 만들어서 실습을 진행해 보겠다. 먼저 pandas의 DataFrame() 함수로 데이터를 생성해 준다.

 

import pandas as pd

data = {'나이':[20,30,40,50],
        '감염여부':['감염X','감염X','감염O','감염O']}

df = pd.DataFrame(data)

 

feature와 target으로 분리시키기 위해 '나이'를 X변수로, '감염여부'를 y로 설정해 주었다. 

X = df['나이']
y = df['감염여부']

 

종속변수가 문자 데이터로 저장이 되어있으므로, 학습을 위해서 이를 0과 1로 변경해주어야 한다. 여기서는 종속변수가 취하는 값이 2개만 있기 때문에 LabelEncoder() 함수를 통해서 문자데이터를 0과 1로 변경해 주겠다.

 

from sklearn import preprocessing
le = preprocessing.LabelEncoder() # 범주형 변수값을 숫자로 변환하는데 사용
input_classes = ['감염X','감염O']
le.fit(input_classes)
y = 1 - le.transform(y)#알파벳 순서에 따라 숫자로 변환하므로 1-를 했다.

 

그러면 이렇게 성공적으로 종속변수가 숫자데이터로 변경이 되었다. 이제 이 데이터를 train_test_split() 클래스를 통해 학습을 위한 데이터와 평가를 위한 데이터로 나누어 주겠다. 

 

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0))

 

위 작업을 수행하고 나면, train 데이터와 test 데이터가 3대 1 비율로 나뉜다. 현재 train 데이터는 3개, test 데이터는 1개가 되었다. 이제 로지스틱 회귀 모델을 불러오겠다. 

 

from sklearn.linear_model import LogisticRegression

model = LogisticRegression(penalty='none')

 

penalty 부분은 규제화 방법을 어떤 걸 사용할지 정하는 파라미터이다. 선형회귀는 LinearRgression() 클래스 생성자 함수가 이러한 파라미터를 가지고 있지 않았다. L1을 하려면 Lasso라는 별도의 클래스를 이용해야 하는 식이었다. 반면, LogisticRegression() 클래스는 여러 개의 파라미터를 가지고 있는데, penalty를 기본적으로 설정해주어야 한다. penalty 종류는 'l1', 'l2', 'elasticnet', 'none'이 있고, 여기서는 아무런 규제화를 적용하지 않은 로지스틱 회귀 모형을 사용했다.

 

import numpy as np
X_train = np.array(X_train)
X_train = X_train.reshape(-1,1)
X_test = np.array(X_test)
X_test = X_test.reshape(-1,1)

 

독립변수 컬럼이 1개뿐이라면, 2차원 배열 형태로 만들어주어야 학습을 시킬 수 있다. X_train 데이터와 X_test 데이터를 2차원 배열 형태로 바꾸어 주었다.

 

model.fit(X_train, y_train)

 

fit() 함수를 통해 모델 학습이 완료됐다. 파라미터 값이 몇이 되었지도 확인해 보겠다.

 

model.coef_ #array([[1.2991931]])
model.intercept_ #array([-51.45252625])

 

\(b_0\)는 -51.45252625 \(b_1\)는 1.2991931 값이 나왔다. 이를 그래프로 나타내면 아래와 같은 그래프가 나온다. 점들은 실제로 학습시킨 값들이고, 학습시켜서 나온 로지스틱 회귀 함수가 검정색 선이다. 이제 마지막으로 성능평가를 해보자.

 

 

model.score(X_test, y_test)

 

성능 평가 결과 1이 나왔다. 정확도 100%라는 뜻이다. 회귀 문제에서도 성능평가 단계에서 .score()함수를 사용하였는데, LinearRgression() 클래스를 사용한 회귀 모형에서의 .score()는 \(R^2\)을 의미하지만, LinearRgression() 클래스를 사용한 로지스틱 회귀 모형에서의 .score는 분류문제에서 성능평가 지표로 사용하는 accuracy(정확도)를 의미한다. 정확도는 아래와 같은 방법으로도 똑같이 계산해 볼 수 있다.  \(R^2\)처럼 정확도 또한 몇이 넘으면 성능이 좋다는 절대적인 기준은 없다.

 

from sklearn.metrics import accuracy_score
y_predictions = lr_baselpredict(X_test)
accuracy_score(y_test, y_predictions)

 

여기까지 로지스틱 회귀 모형을 통해 학습 및 성능평가까지 해보았다. 여기서 LinearRgression() 클래스에 대해 추가적인 설명을 하면, 하이퍼 파라미터 값들을 처음부터 사용자가 설정해 줄 수 있는데, 하이퍼 파라미터에는 패널티강도를 나타내는 C\((=\frac{1}{\lambda})\)와 어떤 규제화 방식을 사용하지에 대한 penalty의 종류, 비용함수의 값을 최소화하는 파람미터 값을 찾을 때 사용하는 방법인 solver의 종류, 그리고 경사하강법에서 업데이트를 나타냈던 에타라는 하이퍼 파라미터처럼 업데이트를 최대로 얼마까지 할지 지정하는 값이 max_iter까지 설정해줄 수 있다. 

 

model = LogisticRegression(C=0.1, penalty='l1', solver='saga', max_iter=1000)

 

  • C\((=\frac{1}{\lambda})\): 페널티 강도
  • penalty: 규제화 방식. l1, l2, elesticnet, none으로 설정 가능하다.
  • solver: 비용함수의 값을 최소화하는 파라미터 값을 찾을 때 사용하는 방법. 일반적으로 saga를 많이 사용함.
  • max_iter: 업데이트를 최대로 얼마까지 할지 지정하는 값. solver 같은 경우엔, 경사하강법과 비슷하게 순차적으로 업데이트를 해서 파라미터의 최적값을 찾는다.

이렇게 설정할 수 있는 하이퍼 파라미터가 많다 보니, 일일이 전부 다 하나씩 설정해 보면서 학습시키기엔 한계가 있다. 그래서 이를 자동화시켜 주는 하이퍼파라미터 튜닝방법으로 그리드 서치라는 방법이 있다. 하이퍼 파리미터에 대한 부분과 분류문제에서의 성능평가 방법들(정확도, 재현율, 정밀도, F1)에 대해서는 다음 시간에 알아보겠다.

댓글