2 minute read

로지스틱 회귀

회귀이지만 분류 모델에서 사용한다. 왜 그런지에 대해서 검색해봤더니 위키 백과에서 그 이유를 어느정도 추측할 수 있었다.

로지스틱 회귀분석에서는 종속 변수가 이항분포를 따르는 모형을 사용하기 때문에 타겟 데이터를 범주형 데이터로 사용해야 하므로 로지스틱 회귀이지만 분류의 역할을 하게 되는것이라고 생각된다.

그렇다면 왜 로지스틱 분류가 아닌 회귀인가?

로지스틱 회귀는 선형 회귀처럼 독립 변수의 선형 결합으로 종속 변수를 설명하기 때문이다.

그러면 로지스틱 함수는 어떻게 정의되었는지 살펴보자

logistic(sigmoid) function

베르누이 시행에서 1이 나올 확률을 p 0이 나올 확률과 1-p라고 했을 때 이것의 비율을 \(odds=\frac{p}{1-p}\) 라고 하고 이것에 로그를 취하면 logit함수가 된다. \(z=logit(odds)=log(\frac{p}{1-p})\) image-20220504000128498

이것의 역함수를 취해주면 로지스틱(시그모이드)함수가 된다 \(p=log(\frac{z}{1-z}) \\ e^p=\frac{z}{1-z} \\ p=\frac{e^z}{1+e^z} \\ p=\frac{1}{1-e^{-z}} \\ \phi=\frac{1}{1+e^{-z}}\)

KakaoTalk_20220502_212813819

로지스틱 회귀

Fish Market 데이터를 사용해서 Species 제외한 다른 특성을 독립변수로 Species 중에서 Bream,Smelt를 타겟으로 하는 로지스틱 회귀 분석을 해 보자

y=df['Species']
X=df.drop(['Species'],axis=1)

from sklearn.model_selection import train_test_split
train_input, test_input, train_target, test_target = train_test_split(X, y, random_state=42)

데이터를 준비해 주고

from sklearn.preprocessing import StandardScaler

ss = StandardScaler()
ss.fit(train_input)
train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)

bream_smelt_indexes = (train_target == 'Bream') | (train_target == 'Smelt')
train_bream_smelt = train_scaled[bream_smelt_indexes]
target_bream_smelt = train_target[bream_smelt_indexes]

StandardScaler를 통해서 표준화를 한번 해 준 다음 Species중 Bream,Smelt 만 따로 뽑아준다.

from sklearn.linear_model import LogisticRegression

lr = LogisticRegression()
lr.fit(train_bream_smelt, target_bream_smelt)
print(lr.predict_proba(train_bream_smelt[:5]))

LogisticRegression으로 훈련하고 테스트 5개 샘플의 predict확률을 보면 음성 클래스와 양성 클래스의 확률을 출력해준다.

image-20220504073233557

decisions = lr.decision_function(train_bream_smelt[:5])
print(decisions)

image-20220504072816825

LogisticRegression은 또한 decision값(z)값을 확인할 수있는데 이 값을 로지스틱 함수에 넣어 주면 확률값을 구할 수 있게 될 것이다.

from scipy.special import expit

print(expit(decisions))

SciPy 라이브러리에서 시그모이드함수를 가져와서 값을 넣어주면 양성클래스의 확률과 동일한 값을 출력한다.

image-20220504073450977

로지스틱 회귀를 사용한 다중 분류

LogisticRegression이 로지스틱 함수를 사용한 회귀를 한다는 것을 확인했으니 이제 Species 를 전부 사용해서 다중 분류를 해 보자

lr = LogisticRegression(C=20, max_iter=1000)
lr.fit(train_scaled, train_target)

로지스틱 회귀도 릿지 회귀처럼 L2규제를 사용하는데 위의 하이퍼파리미터 C는 릿지 회귀에서의 람다의 역수이다. 그러므로 값이 작을수록 규제가 강화된다.

proba = lr.predict_proba(test_scaled[:5])
print(np.round(proba, decimals=3))

5개 샘플에 대한 예측을 출력해보면

image-20220504082403038

print(lr.classes_)

image-20220504082256845

몇 퍼센트의 확률로 해당 class를 예측했는지 알 수 있다.

print(lr.coef_.shape, lr.intercept_.shape)

coef와 intercept를 확인해보면 class가 7개이고 특성이 6개이기 때문에 아래와 같은 출력이 표시된다.

image-20220504083050842

소프트맥스 함수

이진 분류가 아니기 때문에 softmax함수를 사용한다.

e값을 구할 때는 시그모이드가 p: 해당 확률 1-p:해당 확률이 아닌 것 으로 계산하였는데

다중 분류에서도 각각에 클래스에 해당 클래스의 확률p와 전체에서 해당 클래스를 뺀 확률 1-p를 사용하여 각각에 클래스에 대하여 시그모이드 함수값을 구한 다음 소프트맥스를 통해서 그 확률들의 합이 1이 되도록 해준다.

\[e_{sum}=e^{z1}+e^{z2}+e^{z3}+e^{z4}+e^{z5}+e^{z6}+e^{z7} \\ s_1=\frac{e^{z1}}{e_{sum}},\ s_2=\frac{e^{z2}}{e_{sum}} \ ,..., \ s_7=\frac{e^{z7}}{e_{sum}}\]

각각의 z값을 구하면 아래와 같이 나오고

decision = lr.decision_function(test_scaled[:5])
print(np.round(decision, decimals=2))

image-20220504084416728

from scipy.special import softmax

proba = softmax(decision, axis=1)
print(np.round(proba, decimals=3))

SciPy 라이브러리에서 소프트맥스 함수를 가져와서 확인해 보면

image-20220504084515429

이것을 위의 LogisticRegression의 pridict와 비교했을 때 같은 결과가 나오는 것을 확인할 수 있다.

참고

wikipedia,2022년 4월 13일 (수) 01:18 수정,2022년 5월 3일 접속, 로지스틱 함수 위키 링크

박해선,혼자 공부하는 머신러닝, 한빛미디어, 2021,183~196p