Data Analysis/Machine Learning

Python_ML_Supervised_Logistic Regression

mansoorrr 2023. 12. 18. 10:46

로지스틱회귀(Logistic Regression)

  • 선형 모델을 분류하는데 사용해 샘플이 특정 클래스에 속할 확률을 추정 가능
  • 이름은 회귀지만 종속변수가 범주형인 경우 사용
  • 종속변수가 특정 범수에 속하는 확률을 모델링함
  • 시그모이드(로지스틱)함수
    • 로지스틱 회귀는 선형회귀처럼 $y=\beta_0 + \beta_1X$로 표현해야 하는데 y가 범주형 변수이기 때문에 저렇게 표현하면 예측이 맞지 않는다.
    • 따라서 모든 값에 0 또는 1사이의 값을 제공하는 함수를 사용해야 함
    • X가 아주 큰 음수일때 0이 되고 아주 큰 양수일때 1이 되도록 바꿔줌
  • 승산비(Odds)
    • 실패(1-p)에 비해 성공(p)할 확률
    • $p/(1-p)$
    • $P(Y=1|X)$ 가 1에 가까워 질수록 Odds가 무한대로 발산하는 한계가 있음 
    • 이를 극복하기 위해 log를 씌움
      • log(Odds)는 범위가 $-\infty < log(Odds) < \infty$로 변경됨
      • X가 음수일때는 y가 0에서 0.5 이하인 값을 갖고, X가 양수일때는 Y가 0.5 이상에서 1이하인 값을 반환
      • 즉, 성공확률 0.5를 기준으로 X값이 나뉘게 됨
      • 이를 활용해 독립변수에 로짓반응을 적용하여 모델링 하는 것이 로지스틱 회귀
  • 훈련 및 비용함수
    • 로지스틱 회귀 모델의 학습목적은 양성 샘플(y=1)에 대해서는 높은 확률을 추정하고, 음성 샘플(y=0)에 대해서는 낮은 확률을 추정하는 모델의 파라미터를 찾는것
    • 이를 위해 최대우도추정법(MLE: Maximum Likelihood Function)을 사용
  • sklearn.linear_model.LogisticRegression(parameters)
Parameter
penalty - default: l2
- 패널티에 적용할 norm결정
- l2, l1, elasticnet 적용 가능
dual - default: False
- Dual formulation을 실행할지 여부를 나타냄
- n_sample이 n_feature보다 클 경우 False를 추천
tol - default: 1e-4
- 중지기준에 대한 허용 오차
C - default: 1.0
- 정규화 강도의 역수
fit_intercept - default: True
- bias or 절편을 추가해야 하는지 여부
class_weigth - default: None
- 클래스와 관련된 가중치
- 지정하지 않으면 모든 클래스의 가중치는 1
random_state - default: None
solver - default: lbfgs
- 최적화 문제를 푸는 해를 구할 때 사용하는 알고리즘 설정
- 데이터 세트의 크기가 작으면 liblinear
- 다중클래스에서는 newton-cg, sag, saga, libfgs사용
multi_class - default: auto
- 클래스 타입 설정
- auto: 자동, ovr: 2진분류, multinomial: 다중분류
verbose - default: 0
- 과정 확인
l1_ratio - default: None
- penalty가 elasticnet일 경우만 사용
Property
classes_ - 분류기에서 라벨링된 클래스
coef_ - feature에 할당된 가중치
intercept_ - 선형모델의 절편
n_iter - 중지 기준에 도달하기 전까지 실제 반복 횟수
Method
fit(X, y) - 학습
- X: train data( 2차원)
- y: train target data
get_params([deep]) - 모델의 파라미터 가져옴
- 딕셔너리 형태로 반환
predict(X) - 모델을 사용해 예측
- X: test data(2차원)
score(X, y) - 모델의 결정계수(모델성능)
- X: test data(2차원)
- y: test target data
predict_proba(X) - X: test data(2차원)
- 샘플데이터가 클래스에 속할 확률
predict_log_proba(X) - X: test data(2차원)
- 샘플데이터가 클래스에 속할 로그확률
decision_function - X에 대해 예측한 y가 양수인지 음수인지
- 예측한 y가 Hyperplane(0)과 얼마나 떨어져 있는지

 

 

예시 1) Kaggle bodyPerformance 데이터를 활용한 로지스틱 회귀(2진 분류) 구현

  • 13393개데이터이며 12개의 컬럼을 가지고 있다.
  • 종속변수인 class는 범주형 변수이다.
  • 독립변수들중 gender를 제외한 나머지는 수치형 변수이다.
  • gender, class의 분포는 사진과 같다.

 

  • gender를 남자=0, 여자=1로 변경한다.
  • 2진분류를 수행하기 위해 class가 A에 속하면 1로, 나머지는 0으로 변경한다.
body["gender"] = np.where(body["gender"]=="M", 0, 1) #gender변경
body["class_1"] = np.where(body["class"]=="A", 1, 0) #class변경

obj_cols = ["gender", "class_1"]
for col in obj_cols:
  print(f"\n{col}")
  print(body[col].value_counts())

변경 완료된 후 분포

 

  • 데이터를 독립변수와 종속변수로 분리한다.
  • 학습용과 테스트용으로 7:3 분할을 실시한다.
from sklearn.model_selection import train_test_split

#데이터 분할
remove_cols = ["class", "class_1"]
x_cols = body.columns.difference(remove_cols)

X = body[x_cols]
y = body["class_1"]


x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=.3, random_state=1)
print(f"x_train shape: {x_train.shape}")
print(f"y_train shape: {y_train.shape}")
print(f"x_test shape: {x_test.shape}")
print(f"y_test shape: {y_test.shape}")

# x_train shape: (9375, 11)
# y_train shape: (9375,)
# x_test shape: (4018, 11)
# y_test shape: (4018,)

 

  • x_train, y_train을 통해 학습한 결과를 확인해 본다.
  • 분류한 결과와 왜 그렇게 분류했는지를 알아본다.
  • predict를 통해 분류결과를 확인하고
  • predict_proba를 통해 분류결과의 이유를 확인한다. 2차원 array로 반환된다.
  • decision_fuction을 통해 0과 떨어진 결과를 확인한다.
  • 확인 결과
    • org_y와 x_train_pred를 보면 맞게 분류한것도 있고 틀리게 분류한것도 있다.
    • x_train_proba0과 x_train_proba1은 predict_proba의 결과이다. 0은 0으로 분류할 확률, 1은 1로 분류할 확률을 나타낸다.
    • 0으로 분류했을때의 확률이 1로 분류했을때의 확률보다 높은 경우 0으로 분류한다.
    • 반대로 1로 분류했을때의 확률이 높으면 1로 분류하는 것을 알 수 있다.
    • 0으로 분류된 경우 decision_function은 음수값을 가지며 확률이 높을수록 값도 크다.
x_train_pred = logr.predict(x_train) # x_train으로 예측한 결과 반환
x_train_proba = logr.predict_proba(x_train) # x_train으로 예측한 결과에 대한 확률 반환
cs = logr.decision_function(x_train) # 0을 기준으로 양수인지 음수인지 확인

compare_df = pd.DataFrame({"org_y": y_train})

compare_df["x_train_pred"] = x_train_pred
compare_df[["x_train_proba0", "x_train_proba1"]] = x_train_proba
compare_df["decision_function"] = cs
compare_df.head()

 

  • decision_function에 따른 확률값을 매칭시킨다
  • decision_function의 크기가 작아지면 0으로 분류할 확률이 커지고, 커지면 1로 분류할 확률이 커지는 것을 볼 수 있다
  • x=0, y=0.5를 기준으로 1과 0으로 나뉜다
import matplotlib.pyplot as plt
import seaborn as sns
import warnings

warnings.filterwarnings("ignore")

plt.axhline(y=0.5, linewidth=1, linestyle="--", color="black")
plt.axvline(x=0, linewidth=1, linestyle=":", color="black")
plt.plot(compare_df["decision_function"], compare_df["x_train_proba0"], "r.", linewidth=0.3, label="class0 proba")
plt.plot(compare_df["decision_function"], compare_df["x_train_proba1"], "b*", linewidth=0.3, label="class1 proba")
plt.legend(loc="upper left")
plt.show();

 

  • 혼동행렬과 정확도 등 평가 지표들을 확인한다.
  • 잘못 예측한 갯수는 620개 정도 된다.
  • 정확도는 84.5%로 나타나고 나머지 지표들은 준수한 수준이다.
  • auc값을 확인한 결과 0.91로 나타난다.
#---------- 정확도 파악
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score

pred = logr.predict(x_test) #예측

cfm = confusion_matrix(y_test, pred)
acc = accuracy_score(y_test, pred)
prc = precision_score(y_test, pred)
rc = recall_score(y_test, pred)
f1 = f1_score(y_test, pred)

print(f"\n[confusion matrix]\n {cfm}")
print(f"\n[accuracy]: {acc}")
print(f"[precision]: {prc}")
print(f"[recall]: {rc}")
print(f"[f1 score]: {f1}")

#---------- ROC curve 확인
from sklearn.metrics import RocCurveDisplay
RocCurveDisplay.from_estimator(logr, x_test, y_test)

 

 

예시2) Kaggle bodyPerformance 데이터를 활용한 소프트맥스 회귀(다중분류) 구현

  • 소프트맥스 함수
    • 클래스에 대한 점수를 계산하고 그 점수에 소프트맥스 함수를 적용해 각 클래스의 확률을 추정
    • LogisticRegression의 multi_class를 multinomial로 바꾸면 소프트맥스 회귀 사용 가능
    • solver도 소프트맥스 회귀를 적용가능하도록 알고리즘 설정해야 함
  • 혼동행렬도 4개 클래스로 분리되서 나타남
  • 정확도는 60%가 나타남
  • 예측결과와 예측확률을 확인해본 결과 확률이 가장 높은 클래스로 분류함을 확인 가능
#---------- 전처리
class_mapping = {
    "A": 0,
    "B": 1,
    "C": 2,
    "D": 3
}
body["class_2"] = body["class"].map(class_mapping)

#---------- 데이터 분할
y = body["class_2"]
x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=.3, random_state=1)
print(x_train.shape)
print(x_test.shape)
print(y_train.shape)
print(y_test.shape)

#---------- 데이터 정의
sm_logr = LogisticRegression(multi_class="multinomial", solver="lbfgs", C=10) # 다중분류 지정


#--------- 데이터 평가
sm_pred = sm_logr.predict(x_test)
sm_cfm = confusion_matrix(y_test, sm_pred)
sm_acc = accuracy_score(y_test, sm_pred)

print(sm_cfm)
print(sm_acc)

#--------- 분류 결과에 대한 확인
sm_pred[0] # 3번
sm_proba = sm_logr.predict_proba(x_test)
sm_proba[0, :] #array([0.00510206, 0.07849222, 0.22014509, 0.69626064])
#3번째 레이블의 확률이 가장 높은것을 확인할 수 있다.