로지스틱 회귀

Jaeseok Han·2024년 4월 18일

머신러닝&딥러닝

목록 보기
8/22
post-thumbnail

로지스틱 회귀

분류 모델로, 선형 회귀와 동일하게 선형 방정식을 학습한다.
[참고] 선형 방정식

z=a×(Weight)+b×(Length)+c×(Diagonal)+d×(Height)+e×(Width)+fz = a \times (Weight) + b \times (Length) + c \times (Diagonal) + d \times (Height) + e \times (Width) + f
  • a,b,c,d,ea,b,c,d,e 가중치 또는 계수

1. 시그모이드 함수(로지스틱 함수)

확률은 0~1 (0~100%)이므로 zz가 큰 음수 일 경우 0, 큰 양수일 경우 1(100%)이 되도록 바꿔야하며 이런작업을 시그모이드 함수로 해결할 수 있다.
[참고] 시그모이드 함수

import pandas as pd
fish = pd.read_csv('https://bit.ly/fish_csv_data')
fish.head()

# 생선 종류 출력
print(pd.unique(fish['Species']))
# ['Bream' 'Roach' 'Whitefish' 'Parkki' 'Perch' 'Pike' 'Smelt']
# 타깃으로 지정

## 새로운 데이터프레임 생성
fish_input = fish[['Weight', 'Length', 'Diagonal', 'Height', 'Width']].to_numpy()
fish_target = fish['Species'].to_numpy()

from sklearn.model_selection import train_test_split
# 훈련 세트, 테스트 세트
train_input, test_input, train_target, test_target = train_test_split(fish_input, fish_target, 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)

#k-최근접 이웃 분류기의 확률 예측
from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier(n_neighbors=3)
kn.fit(train_scaled, train_target)
print(kn.score(train_scaled, train_target))
# 출력 0.8907563025210085
print(kn.score(test_scaled, test_target))
# 출력 0.85

#정렬된 타깃값 (사이킷런 모델이 알파벳 순서 자동 정렬)
print(kn.classes_)
# 출력 ['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish']

# 테스트 세트 5개 샘플 예측
print(kn.predict(test_scaled[:5]))
# 출력 ['Perch' 'Smelt' 'Pike' 'Perch' 'Perch']


# 클래스별 확률값 확인
import numpy as np
proba = kn.predict_proba(test_scaled[:5]) #클래스별 확률값 반환
print(np.round(proba, decimals=4)) # 5번째 자리에서 반올림

#[[0.     0.     1.     0.     0.     0.     0.    ]
# [0.     0.     0.     0.     0.     1.     0.    ]
# [0.     0.     0.     1.     0.     0.     0.    ]
# [0.     0.     0.6667 0.     0.3333 0.     0.    ]
# [0.     0.     0.6667 0.     0.3333 0.     0.    ]]

# 근접한 이웃 클래스
distances, indexed = kn.kneighbors(test_scaled[3:4])
print(train_target[indexed])
# 출력 [['Roach' 'Perch' 'Perch']]

# 시그모이드 함수
import numpy as np
import matplotlib.pyplot as plt
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()

2. 로지스틱 회귀로 이진 분류

LogisticRegression

사이킷런의 로지스틱 회귀 모델

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]

from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
lr.fit(train_bream_smelt, target_bream_smelt)

print(lr.predict(train_bream_smelt[:5]))
#출력 ['Bream' 'Smelt' 'Bream' 'Bream' 'Bream']

# 샘플 예측 확률
print(lr.predict_proba(train_bream_smelt[:5]))
# [[0.99759855 0.00240145]
# [0.02735183 0.97264817]
# [0.99486072 0.00513928]
# [0.98584202 0.01415798]
# [0.99767269 0.00232731]]

첫번째 열은 음성 클래스(0)에 대한 확률, 두번째 열은 양성 클래스(1)에 대한 확률

print(lr.classes_)
# ['Bream' 'Smelt']

기본으로 알파벳로 정렬되어있어 음성 클래스는 Bream, 양성 클래스는 Smelt이다.

print(lr.coef_, lr.intercept_)
# [[-0.4037798  -0.57620209 -0.66280298 -1.01290277 -0.73168947]] [-2.16155132]

이 로지스틱 회귀 모델이 학습한 방정식
z=0.404×(Weight)0.576×(length)0.663×(Diagonal)1.013×(Height)0.732×(Width)2.162z = -0.404 \times (Weight) - 0.576 \times (length) -0.663 \times(Diagonal) - 1.013 \times (Height) - 0.732\times(Width) - 2.162

# z값 계산
decisions = lr.decision_function(train_bream_smelt[:5])
print(decisions)
# [-6.02927744  3.57123907 -5.26568906 -4.24321775 -6.0607117 ]

여기서 나온 z 값은 시그모이드 함수에 통과시키면 확률을 얻을 수 있다.

# 시그모이드 함수 사용
from scipy.special import expit
print(expit(decisions))
# [0.00240145 0.97264817 0.00513928 0.01415798 0.00232731]

양성 클래스에 대한 확률을 반환한다.

3. 로지스틱 회귀로 다중 분류

다중 분류는 소프트맥스 함수를 사용하여 타깃개수 만큼의 z 값 확률로 변환한다.

소프트맥스

z1z7z_1 \sim z_7 : 7개의 z값

e_sum=ez1+ez2+ez3+ez4+ez5+ez6+ez7e\_sum = e^{{z_1}} + e^{{z_2}} + e^{{z_3}} + e^{{z_4}} + e^{{z_5}} + e^{{z_6}} + e^{{z_7}}

ez1ez2e^{{z_1}} \sim e^{{z_2}} 값을 e_sum으로 나눠준다.

s1=ez1esum,s2=ez1esum,,s7=ez7esums1 = \frac {e^{{z_1}}}{e_sum},s2 = \frac {e^{{z_1}}}{e_sum}, \cdots,s7 = \frac {e^{{z_7}}}{e_sum}
decision = lr.decision_function(test_scaled[:5])
print(np.round(decision, decimals=2))
#출력
#[[ -6.5    1.03   5.16  -2.73   3.34   0.33  -0.63]
# [-10.86   1.93   4.77  -2.4    2.98   7.84  -4.26]
# [ -4.34  -6.23   3.17   6.49   2.36   2.42  -3.87]
# [ -0.68   0.45   2.65  -1.19   3.26  -5.75   1.26]
# [ -6.4   -1.99   5.82  -0.11   3.5   -0.11  -0.71]]

from scipy.special import softmax
proba = softmax(decision, axis=1) #axis : 계산할 축을 지정
print(np.round(proba, decimals=3))
#출력
#[[0.    0.014 0.841 0.    0.136 0.007 0.003]
# [0.    0.003 0.044 0.    0.007 0.946 0.   ]
# [0.    0.    0.034 0.935 0.015 0.016 0.   ]
# [0.011 0.034 0.306 0.007 0.567 0.    0.076]
# [0.    0.    0.904 0.002 0.089 0.002 0.001]]

0개의 댓글