본문 바로가기
내가 하는 데이터분석/내가 하는 머신러닝

[로지스틱 회귀, Logistic Regression] with Python

by Pymoon 2023. 2. 3.

선형 회귀모델을 분류로 사용해서 샘플이 특정 클래스에 속할 확률을 추정하는 데 사용하는 기법인 로지스틱 회귀(Logistic Regression)에 대해서 다뤄보자.

 

 

 

이 전의 머신러닝에서는 다중 회귀(Multiple Regression)에 대해서 다뤄보며 L1규제, L2규제, Elastic net에 대한 내용도 다뤄보았다.

 

다중 회귀(Multiple Regression Model) with Python

여러 개의 독립변수와 한 개의 종속변수로 이루어진 회귀 분석인 다중 회귀(Multiple Regression) 분석에 대해서 정리해보고자 한다. 이 전글에서 머신러닝 중 다항 회귀(Polynomial Regression)에 대해서 정

py-moon.tistory.com

 

 

 

로지스틱 회귀분석은 종속변수의 수치를 직접 모델링하는 회귀모델이 아닌 특정 범주의 속하는 확률을 모델링하는 기법이기 때문에 분류모델에 속한다.

 

따라서, 변수의 형태로 종속변수는 범주형이어야 한다.

 

 

 

기본적인 이진 분류(Binary Classification)을 지원하는 로지스틱 회귀를 모델링하는 과정에서 원리가 되는 수식인 시그모이드(Sigmoid) 함수를 활용한다.

 

하지만, 이 글의 후반부에서 이진 분류가 아닌 다중 분류(Multiclass Classification)를 지원하도록 소프트맥스(Softmax) 함수를 활용한 회귀도 실습하고자 한다.

 

 

 


로지스틱 회귀 모델은 캐글에서 지원하는 bodyPerformance데이터를 활용해서 진행한다.

 

 

 

 

1
2
3
4
5
6
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings(action='ignore')
 
body = pd.read_csv('data/bodyPerformance.csv')
cs

 

> 모델 구현에 필요한 라이브러리와 데이터를 불러와준다.

 

 

 

body.head()

> body.head()를 통해서 데이터를 확인해 본다.

 

> 데이터를 분석에 활용할 수 있도록 object형 변수를 numeric형으로 변환한다.

 

 

 

 

1
2
body['gender'= np.where(body['gender'== 'M'01)
body['class_1'= np.where(body['class'== 'A'01)
cs

 

> gerder변수와 class변수를 0과 1로 표현되도록 인코딩해준다.

 

 

 

 

> 분석에 활용할 수 있도록 모두 수치형 변수로 세팅되었다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
from sklearn.model_selection import train_test_split
 
feature_columns = list(body.columns.difference(['class''class_1']))
= body[feature_columns]
= body['class_1']
 
train_x, test_x, train_y, test_y = train_test_split(x, y, stratify = y, train_size = 0.7, random_state = 1)
print(train_x.shape, test_x.shape, train_y.shape, test_y.shape)
 
 
(937511) (401811) (9375,) (4018,)
cs

 

> 독립변수로는 class변수와 class_1 변수를 제외한 모든 변수를 사용하고, 종속변수로 class변수와 class_1변수를 활용한다.

 

> 그리곤 학습을 위해 train_test_split() 함수로 train, test를 각각 7 : 3의 비율로 데이터를 분할해 준다.

 

 

 

 

1
2
3
4
5
6
7
from sklearn.linear_model import LogisticRegression
 
lr = LogisticRegression()
lr.fit(train_x, train_y)
 
proba = pd.DataFrame(lr.predict_proba(train_x))
cs = pd.DataFrame(lr.decision_function(train_x))
cs

 

> 모델 객체를 생성하고 분할된 데이트를 학습에 사용한다.

 

> 학습을 통해 구축된 모델이 예측한 값을 proba에, 예측한 값을 시그모이드 함수에 적용했을 때 0을 기준으로 계산한 거리값을 cs에 할당해 주고 둘 다 데이터프레임으로 변환해 준다.

 

 

 

 

1
2
3
4
5
df = pd.concat([proba, cs], axis = 1)
df.columns = ['Not A''A''decision_function']
 
df.sort_values(['decision_function'], inplace = True)
df.reset_index(inplace = True, drop = True)
cs

 

> 위에서 생성한 두 데이터프레임을 df라는 이름의 데이터프레임으로 병합한 후, 칼럼 명을 지정한다.

 

> df를 다루기 용이하도록 cs값을 기준으로 내림차순으로 정리하고 index까지 초기화해준다.

 

 

 

df.head()

> 이렇게 해서 생성된 df.head()를 확인해 본다.

 

> Not A 변수의 데이터 값은 A가 아닌 범주에 속할 확률을 나타낸다.

 

> A 변수의 데이터 값은 A 범주에 속할 확률을 나타낸다.

 

> decision_function 변수의 데이터 값은 0을 기준으로 해당 데이터가 음수인지 양수인지, 얼마나 떨어져 있는지를 나타낸다.

 

 

 

구축된 모델의 값을 나타내는 시그모인드 함수 그래프

> decision_function 값을 좀 더 직관적으로 이해하기 위해 시각화를 통해 표현한다.

 

> x축의 0을 기준으로 해당 데이터의 위치를 나타낸 것이라고 이해하면 될 것 같다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score
 
pred = lr.predict(test_x)
 
test_cm = confusion_matrix(test_y, pred)
test_acc = accuracy_score(test_y, pred)
test_prc = precision_score(test_y, pred)
test_rcll = recall_score(test_y, pred)
test_f1 = f1_score(test_y, pred)
 
print(test_cm)
print('\n')
print('정확도 : {:.2f}%'.format(test_acc*100))
print('정밀도 : {:.2f}%'.format(test_prc*100))
print('재현율 : {:.2f}%'.format(test_rcll*100))
print('F1-score : {:.2f}%'.format(test_f1*100))
 
 
[[ 603  401]
 [ 243 2771]]
 
정확도 : 83.97%
정밀도 : 87.36%
재현율 : 91.94%
F1-score : 89.59%
cs

 

> 생성된 모델의 성능을 평가하기 위해 분류모델에서 주로 사용하는 성능 평가지표를 활용해 본다.

 

> 혼돈행렬을 기반으로 정확도, 정밀도, 재현율, F1_score까지 나타낸다.

 

> 해당 모델은 약 83.9%의 정확도를 가지는 모델이라고 해석할 수 있다.

 

 

 

plot_roc_curve

> Roc_Auc_score는 머신러닝에서 이진 분류의 예측 성능 특정에서 중요하게 사용되는 평가 지표이다. 

 

> 이 지표는 혼동행렬을 기반으로 한 지표로써, FPR(False Positive Rate)이 변할 때 TPR(True Positive Rate)가 어떻게 변하는지를 나타내는 곡선이다.

 

> 따라서, 이 지표에서 그래프가 직선에 가까워질수록 성능이 떨어지고, 곡선 형태를 띨수록 성능이 좋다.

 

> 수치로 표현하자면, 곡선 아래 면적이 1에 가까울수록 성능이 좋다는 이야기이다.

 

 

> 이 개념을 위 그래프에 적용해 보면, 곡선을 잘 유지하고 있고, Roc_Auc_score가 약 0.91로 좋은 성능을 가지는 모델이라고 해석할 수 있다.

 

 


 

이번에는 이진 분류가 아닌 2개 이상의 클래스 분류를 지원하는 다중 분류(Multiclass Classification)에 대해서 실습해 보자.

 

도입부에서 언급했듯이, 다중 분류를 지원하는 로지스틱 회귀모델에서의 분류는 소프트맥스 함수를 적용하여 갹 클래스의 확률을 추정한다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings(action='ignore')
 
body = pd.read_csv('data/bodyPerformance.csv')
 
body['gender'= np.where(body['gender'== 'M'01)
mapping = {'A':0'B':1'C':2'D':3}
body['class_2'= body['class'].map(mapping)
cs

 

> 앞서 진행한 실습과 동일한 데이터를 사용하고, 이번에는 class 변수에서 A, B, C, D를 0, 1, 2, 3으로 변환해 준다.

 

> 이는 다중 분류 실습을 위한 데이터 변환이다.

 

 

 

body.head()

> 아까는 이진 분류였기 때문에 0과 1로만 표현했었지만, 이번에는 class 변수를 0, 1, 2, 3으로 변환한 class_2를 활용한다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
from sklearn.model_selection import train_test_split
 
feature_columns = list(body.columns.difference(['class''class_2']))
= body[feature_columns]
= body['class_2']
 
train_x, test_x, train_y, test_y = train_test_split(x, y, stratify = y, train_size = 0.7, random_state = 1)
print(train_x.shape, test_x.shape, train_y.shape, test_y.shape)
 
 
(937511) (401811) (9375,) (4018,)
cs

 

> 모델 학습을 위해 데이터셋을 분할해 준다. 이진 분류와 동일한 구조이다.

 

 

 

 

1
2
3
4
from sklearn.linear_model import LogisticRegression
 
lr = LogisticRegression(multi_class = 'multinomial', solver = 'lbfgs', C = 10)
lr.fit(train_x, train_y)
cs

 

> 모델 객체를 불러와주고 다중 분류를 위해 하이퍼파라미터를 조정해 준 후에 학습을 진행한다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from sklearn.metrics import confusion_matrix, accuracy_score
 
pred = lr.predict(test_x)
 
test_cm = confusion_matrix(test_y, pred)
test_acc = accuracy_score(test_y, pred)
 
print(test_cm)
print('\n')
print('정확도 : {:.2f}%'.format(test_acc*100))
 
 
[[707 261  36   0]
 [269 403 300  32]
 [ 92 207 525 181]
 [ 13  63 157 772]]
 
정확도 : 59.91%
cs

 

> 혼돈행렬과 함께 성능검증을 위해 평가지표를 확인해 본다.

 

> 다중 분류를 지원하는 해당 로지스틱 회귀모델은 60%도 되지 않는 정확도를 가지고 있다.

 

 

 

> lr.predict()lr.predict_proba()를 통해서 아래와 같은 인사이트를 얻을 수 있다.

 

1
2
3
4
5
6
test_x의 마지막 행의 데이터가 속할 클래스는? : [0]
 
test_x의 마지막 행의 데이터가 class 0에 속할 확률 : 약 62.64%
test_x의 마지막 행의 데이터가 class 1에 속할 확률 : 약 31.19%
test_x의 마지막 행의 데이터가 class 2에 속할 확률 : 약 6.02%
test_x의 마지막 행의 데이터가 class 3에 속할 확률 : 약 0.15%
cs

 

> test_x의 마지막 행의 데이터가 속할 클래스를 찍어보니 0번 클래스가 나왔다.

 

> 이에 각 클래스마다 어떠한 확률을 가지는지도 확인할 수 있었다.

 

 

 


 

OUTTRO.

 

 

데이터를 가지고 로지스틱 회귀를 활용해서 이진 분류와 다중 분류를 진행해 봤다.

 

이진 분류에서 성능을 검증했을 땐 결과를 통해서 좋은 모델이라는 것을 확인할 수 있었다.

 

하지만, 다중 분류에서는 그렇지 못한 결과로 좋지 못한 모델임을 알 수 있었다.

 

이에 우리는 로지스틱 회귀가 다중 분류에 적합하지 못할 수 있다는 생각과 추가적인 데이터 핸들링이 필요했다는 생각을 해볼 수 있다.

 

 

 

작년에 처음 데이터분석을 접할 때 로지스틱 회귀라는 이름 때문에 회귀모델이라고 생각했던 적이 있다. 

 

그것은 모델에 대한 이해도가 부족했다고 말할 수 있고, 지금은 좀 더 깊은 공부를 할 수 있음에 감사할 뿐이다.