08_ML(Machine_Learning)

14_로지스틱 회귀

chuuvelop 2025. 4. 7. 17:51
728x90
로지스틱 회귀

 

  • 의의
    • 확률을 예측하는 데에 사용되는 통계 모델
      • 특정 결과의 확률을 계산(선형회귀의 변형, 이름은 회귀이지만 분류에 쓰인다)
    • 결과값은 이진이어야함(결과는 둘 중 하나)
      • 원하는 결과는 1, 원하지 않는 결과는 0
    • 선형 회귀와 마찬가지로 다중공선성은 거의 없어야 함
      • 독립변수는 서로 독립적이어야 함
    • 종속변수와 관련 없는 독립변수를 제거할 때 더 효율적인 경향이 있음
  • 장점
    • 많은 양의 연산 자원을 필요로 하지 않음
    • 쉽게 해석할 수 있음
  • 단점
    • 비선형 데이터에 사용하기 힘듦
    • 과적합에 취약함
    • 1과 0으로만 결과가 출력되기 때문에 범주형 결과를 예측하는 데에만 사용가능

 

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_curve, roc_auc_score
from scipy.special import expit, softmax
# 로지스틱 함수 시각화
z = np.arange(-5, 5, 0.1)
phi = 1 / (1 + np.exp(-z)) #자연상수
plt.plot(z, phi)
plt.xlabel("z")
plt.ylabel("phi")
plt.show()

 

 

 

 

01. 데이터 준비
df = pd.read_csv("./data/Fish.csv")

 

df_bs = df[df["Species"].isin(["Bream", "Smelt"])]

 

df_bs.head()

 

 

df_bs.shape
(49, 7)

 

 

df_bs = df_bs.drop("Length1", axis = 1)
df_bs.shape
(49, 6)

 

 

x = df_bs.drop("Species", axis = 1)
y = df_bs["Species"]
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.25, stratify = y, random_state = 26)

 

ss = StandardScaler()
scaled_train = ss.fit_transform(x_train)
scaled_test = ss.transform(x_test)

 

 

02_모델 훈련

 

logi = LogisticRegression()

 

logi.fit(scaled_train, y_train)

 

logi.predict(scaled_test)
array(['Bream', 'Bream', 'Bream', 'Smelt', 'Smelt', 'Bream', 'Smelt',
       'Smelt', 'Bream', 'Bream', 'Bream', 'Bream', 'Bream'], dtype=object)
 
 
y_test.values
array(['Bream', 'Bream', 'Bream', 'Smelt', 'Smelt', 'Bream', 'Smelt',
       'Smelt', 'Bream', 'Bream', 'Bream', 'Bream', 'Bream'], dtype=object)

 

# 예측확률 출력
logi.predict_proba(scaled_test)
array([[0.90804325, 0.09195675],
       [0.96957412, 0.03042588],
       [0.95348546, 0.04651454],
       [0.02292033, 0.97707967],
       [0.03225314, 0.96774686],
       [0.99525805, 0.00474195],
       [0.02734957, 0.97265043],
       [0.04104716, 0.95895284],
       [0.99862585, 0.00137415],
       [0.8282424 , 0.1717576 ],
       [0.88577569, 0.11422431],
       [0.97134194, 0.02865806],
       [0.98991588, 0.01008412]])
 
 
 
# 종속변수 값 확인
logi.classes_
array(['Bream', 'Smelt'], dtype=object)

 

 

# 로지스틱 회귀 회귀식 확인
logi.coef_, logi.intercept_
(array([[-0.62908294, -0.7688014 , -0.79029437, -0.80450965, -0.74886043]]),
 array([-2.54215838]))

 

 

x_train.columns
Index(['Weight', 'Length2', 'Length3', 'Height', 'Width'], dtype='object')
  • z = Weight * -0.63 + Length2 * -0.77 + Length3 * -0.79 + Height * -0.8 + Width * -0.75 - 2.54

 

 

03. 회귀식을 통한 확률값 계산
scaled_test[0]
array([-0.42533364, -0.1403064 , -0.08178733,  0.07758921,  0.16754518])

 

-0.43 * -0.63 + -0.14 * -0.76 + -0.08 * -0.79 + 0.08 * -0.8 + 0.17 * -0.75 -2.54
-2.291

 

 

# 로지스틱 함수에 대입
1 / (1 + np.exp(-(-2.291)))
0.09187108508473252

 

 

# z값 계산해주는 함수
decision_z = logi.decision_function(scaled_test)

 

decision_z
array([-2.28997365, -3.46156319, -3.02035933,  3.75254384,  3.40135517,
       -5.34655313,  3.57132385,  3.15112035, -6.58854707, -1.57322166,
       -2.04829957, -3.52324383, -4.58665766])
 

 

# z값을 로지스틱 함수를 통과시켜 확률값 계산
expit(decision_z)
array([0.09195675, 0.03042588, 0.04651454, 0.97707967, 0.96774686,
       0.00474195, 0.97265043, 0.95895284, 0.00137415, 0.1717576 ,
       0.11422431, 0.02865806, 0.01008412])
 
 

 

# roc 커브
pred_proba_class1 = logi.predict_proba(scaled_test)[:, 1]

fprs, tprs, thresholds = roc_curve(y_test.map(lambda x : 0 if x == "Bream" else 1), pred_proba_class1)
# fprs 1-특이도 : 음성인 데이터를 양성으로 잘못 판단한 비율
# tprs: 재현율

plt.figure()
plt.plot(fprs, tprs, label = "ROC")
plt.plot([0, 1], [0, 1], "k--", label = "Random")

plt.xticks(np.round(np.arange(0, 1, 0.1), 2))
plt.xlabel("FPR")
plt.ylabel("TPR")
plt.legend()

plt.show()

 

# pred_proba_class1: 양성 클래스 예측 확률
roc_auc_score(y_test, pred_proba_class1) # 1.0에 가까울 수록 좋음
1.0

 

 

# 정확도
logi.score(scaled_test, y_test)
1.0

 

 

 

04. 로지스틱 회귀로 다중 분류
df = df.drop("Length1", axis = 1)

 

df.head()

 

 

df.shape
(159, 6)

 

x = df.drop("Species", axis = 1)
y = df["Species"]
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.25, stratify = y, random_state = 26)

 

ss = StandardScaler()
scaled_train = ss.fit_transform(x_train)
scaled_test = ss.transform(x_test)

 

logi = LogisticRegression()
logi.fit(scaled_train, y_train)
print(logi.score(scaled_train, y_train))
print(logi.score(scaled_test, y_test))
0.8151260504201681
0.85

 

 

logi.predict(scaled_test)
array(['Perch', 'Bream', 'Pike', 'Pike', 'Perch', 'Bream', 'Smelt',
       'Perch', 'Perch', 'Smelt', 'Bream', 'Perch', 'Parkki', 'Smelt',
       'Perch', 'Bream', 'Bream', 'Perch', 'Perch', 'Pike', 'Perch',
       'Perch', 'Perch', 'Perch', 'Perch', 'Perch', 'Perch', 'Bream',
       'Bream', 'Parkki', 'Pike', 'Perch', 'Perch', 'Bream', 'Bream',
       'Parkki', 'Perch', 'Smelt', 'Perch', 'Perch'], dtype=object)

 

 

# 예측 확률값 확인
proba = logi.predict_proba(scaled_test[:2])
print(np.round(proba, decimals = 3))
[[0.003 0.078 0.577 0.007 0.256 0.063 0.016]
 [0.513 0.27  0.06  0.011 0.098 0.    0.047]]
 
#  한 행을 더하면 1 ->이 연산 과정을 softmax라함
0.003+0.078+0.577+0.007+0.256+0.063+0.016
1.0

 

 

# 종속변수 확인
logi.classes_
array(['Bream', 'Parkki', 'Perch', 'Pike', 'Roach', 'Smelt', 'Whitefish'],
      dtype=object)

 

 

# 회귀식 확인
logi.coef_
array([[ 0.05877873,  0.01562219,  0.69807034,  2.85510498, -0.31387147],
       [-0.48731066, -0.68056718, -0.63679093,  1.91203212, -0.70619899],
       [ 1.20020293,  0.2899595 , -1.40059462, -1.76853091,  1.62919262],
       [-0.06357223,  1.72959572,  1.7313491 , -1.2627758 , -0.57310946],
       [-0.8970852 , -0.59302522,  0.27676377, -0.40917778,  0.55354201],
       [-0.38181641, -0.71099101, -0.69668469, -1.32221282, -1.64701691],
       [ 0.57080283, -0.05059399,  0.02788703, -0.00443979,  1.05746219]])

 

 

# 회귀식이 7개임
logi.intercept_
array([ 0.26882243,  0.03222666,  2.27854232, -0.02070776,  0.83203303,
       -3.74712614,  0.35620947])

 

  • 현재의 로지스틱 다중분류에서 독립변수는 5개를 사용하기 때문에 coef_ 배열의 열은 5개임
  • 종속변수의 범주 수가 7개이기 때문에 coef_ 배열의 행과 intercept_ 의 행은 7개임
    • 즉, 로지스틱 다중분류는 각 클래스마다 z값을 계산함
    • 이 때 확률값은 각각의 z값을 합이 1이 되도록 압축하기 위해 소프트맥스 함수(softmax)를 사용

 

 

 

05. 회귀식을 통한 확률값 계산
decision_z = logi.decision_function(scaled_test[:5])
np.round(decision_z, decimals = 2)
array([[-2.77,  0.64,  2.64, -1.78,  1.82,  0.42, -0.97],
       [ 2.52,  1.88,  0.38, -1.34,  0.87, -4.43,  0.13],
       [-1.6 , -1.2 ,  1.66,  2.52,  1.18, -1.92, -0.63],
       [-0.28, -2.79,  2.27,  4.98,  0.33, -5.06,  0.55],
       [ 1.76, -2.31,  5.3 ,  1.41,  0.07, -9.39,  3.16]])

 

proba = softmax(decision_z, axis = 1) # axis = 1: 각 행에 대해서 소프트맥스 계산
np.round(proba, decimals = 3)
# 이것이 predict_proba임
array([[0.003, 0.078, 0.577, 0.007, 0.256, 0.063, 0.016],
       [0.513, 0.27 , 0.06 , 0.011, 0.098, 0.   , 0.047],
       [0.009, 0.014, 0.238, 0.562, 0.147, 0.007, 0.024],
       [0.005, 0.   , 0.061, 0.914, 0.009, 0.   , 0.011],
       [0.025, 0.   , 0.853, 0.017, 0.005, 0.   , 0.1  ]])

 

 

 

06. 정리
  • 로지스틱 회귀를 선형 회귀처럼 선형 방정식을 사용
  • 방정식의 값을 0 ~ 1 사이로 압축
    • 해당 값을 0 ~ 100% 사이의 확률로 이해할 수 있음
  • 이진 분류
    1. 하나의 선형 방정식을 훈련
    2. 출력값을 시그모이드 함수에 통과시켜 0 ~ 1 사이의 값을 만듦
    3. 2번의 출력값이 양성 클래스에 대한 확률
  • 다중 분류
    1. 클래스 개수만큼 방정식을 훈련
    2. 각 방정식의 출력값을 소프트맥스 함수를 통과시켜 전체 클래스에 대한 답이 항상 1이 되도록 계산
    3. 2번의 출력값이 각 클래스에 대한 확률값

 

 

07. ROC 곡선(Receiver Operation Characteristic Curve)

 

  • 수신자 판단 곡선
  • 머신러닝의 이진 분류 모델의 예측 성능을 판단하는 평가 지표
  • FPR(False Positive Rate)가 변할 때 TPR(True Positive Rate)의 변화를 나타낸 곡선
    • FPR
      • 1 - 특이도
      • FP / (FP + TN)
      • 실제 음성인 데이터 중 양성으로 잘못 예측된 비율
    • TPR
      • 재현율
      • TP / (TP + FN)
      • 실제 양성인 데이터 중 양성으로 예측된 비율
  • ROC 곡선의 가운데 직선은 ROC곡선의 최저값
    • 랜덤 수준의 이진 분류의 ROC 직선
    • ROC곡선이 직선에서 멀어질수록 성능이 뛰어난 것
  • 분류 결정 임계값을 변경하여 FPR을 0부터 1까지 변경하면서 TPR의 변화값을 구함
    • 임계값을 1로 지정하면 FPR은 0이됨
      • 100% 확실한 경우에만 양성으로 예측한다면 실제 음성인 데이터를 양성으로 잘못 예측할 확률은 0
    • 임계값을 0으로 지정하면 FPR은 1이 됨
      • 모든 데이터를 양성으로 예측한다면 실제 음성인 데이터를 양성으로 잘못 예측할 확률은 100%
 
728x90