9강 - 로지스틱 회귀 알아보기

2021. 6. 24. 19:59ICT 멘토링/혼자 공부하는 머신러닝+딥러닝

 

학습 로드맵

지난 시간에

8강 복습

럭키 백

럭키백: 도미-72%, 빙어-16%

확률에 따라 다른 상품.

확률 예측으로 회귀로 판단하는 것이 아니라 각 상품에 대한 확신(확률)이 얼마인지에 따라 분류하는 것임.

 

확률 계산하기

knn 방식을 통해 분류

데이터 준비

import pandas as pd

fish = pd.read_csv('https://bit.ly/fish_csv_data')
fish.head()	# 만들어진 데이터프레임을 테이블로 출력. 첫번째 행은 csv 1라인 헤더 의미. 인덱스는 실제 csv 파일에는 없는 것.

fish_input = fish[['Weight', 'Length', 'Diagonal', 'Height', 'Width']].to_numpy()

fit_target = fish['Species'].to_numpy()

head 메소드 결과

to_numpy()를 통해 넘파이 형식으로 변경.

데이터 변환

k-최근접 이웃의 다중 분류

from sklearn. neighbors improt KNeighborsClassifier

kn = KNeighborsClassifier(n_neighbors=3)
kn.fit(train_scaled, train_target)

print(kn.classes_)
# //['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish']

print(kn.predict(test_scaled[:5]))
# //['Perch' 'Smelt' 'Pike' 'Perch' 'Perch']

proba = kn.predict_proba(test_scaled[:5])
print(np.round(proba, decimals=4))
#	//[[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.	]]

n_neighbors = 3으로 지정해서 default 5에서 3으로 변경

species는 문자열로 되어 있는데 사이킷런은 자체적으로 정수로 바꿔줌.

 

훈련된 후에 타깃값을 확인하려면 classes_를 하면 됨.

객체 속성에 _가 끝에 붙어 있으면 학습한 값이라고 생각하면 됨.

알파벳 순서로 되어 있음.

어떤 것이 0이고 1이고 한지 label을 정해야 하는데 이걸 기본적으로 알파벳 순서로 하는 것.

 label 순서를 정하고 싶으면 수동으로 0과 1 지정해서 넣어주면 됨.

 

확률 출력하려면 predict_proba 메소드 사용.

출력된 확률은 각 행이 각 샘플을 의미하는 것이고 각 열의 요소가 클래스에 대한 확률임.

각 행의 확률의 합은 1이 되고, 높은 확률을 갖는 것이 해당 값으로 정해짐.

 

그런데 이웃한 샘플이 3개밖에 없어서 확률은 1/3, 2/2, 3/3 밖에 나오지 않음.

확률이라고 하기엔 너무 단순하지 않은가?

더 나은 확률은??

 

로지스틱 회귀

logistic regression

선형 함수 형태와 유사.

z 값을 그대로 사용하면 회귀 알고리즘이 되므로 확률로 쓰기 위해서는 z 값을 0~1 의 값으로 만들어줘야 함.

S자 형태의 함수를 시그모이드 함수 = 로지스틱 함수 라고 함.

Z값이 -∞에서 ∞의 값을 가지고 Φ 값은 0에서 1.0의 값을 가짐.

 

Z=0 == Φ = 0.5

 

위와 같이 Φ 값으로 굳이 계산하여 확률 값으로 바꾸지 않더라도 Z 값만으로도 예측 가능함.

 

메소드

predict - Zpredict_proba - Φ

 

로지스틱 회귀(이진 분류)

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]]

boolean indexing - true, false 값을 넣어서 true 값만 뽑아내는 방식.

도미와 빙어만 true로 만들고 나머지는 모두 false

스케일은 행의 길이와 동일.

 

predict는 Z 값에 따른 분류

predict_proba는 Φ 값에 따른 분류

 

알파벳 순서로 label 하여 Bream = 0, Smelt = 1 이 됨.

 

로지스틱 회귀 계수 확인

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

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

from scipy.special import expit

print(expit(decisions))
# //[0.00240145 0.97264817 0.00513928 0.01415798 0.00232731]

가중치와 절편 적용한 z 값 계산.

expit - decisions 값을 sigmoid 함수에 넣어 확률 값을 구하는 메소드

앞에서 본 predict_proba 메소드 결과 값과 동일함.

 

로지스틱 회귀(다중 분류)

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

print(lr.score(train_scaled, train_target))
print(lr.score(test_scaled, test_target))

# // 0.9327731092436975
# // 0.925

max_iter - 반복횟수 조정. default = 100

C - 규제의 강도. L2 norm 규제가 기본적으로 적용되는데 C 값↑ 규제↓, C 값↓ 규제↑.

 

score 정확도

분류 모델이라 R square가 아닌 score - 정확도 확인.

proba = lr.predict_proba(test_scaled[:5])
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]]

print(lr.coef_.shape, lr.intercept_.shape)
# //(7, 5) (7,)

5개 행-샘플, 7개 확률-클래스

 

이진분류와 동일하게 확률 출력함.

행렬의 크기만 확인하면 7개의 행과 5개의 열을 가진 행렬이 존재함.

5개는 특성과 곱해지는 계수이고 7개는 클래스 의미.

클래스마다 선형함수(Z)가 하나씩 만들어짐.

7개의 선형함수를 만들어서 각 샘플에 대해 선형함수 값이 가장 큰 것이 예측값이 됨.

Z 값을 학습할 때는 마치 이진분류와 같이 학습함.

하나만 양성, 나머지는 다 음성으로 두고 학습하는 것.

OvR - One vs Rest

 

7개의 Z가 나오는데 이를 시그모이드로 바꾸면 7개의 시그모이드의 합은 1이 됨.

다중 분류일 때는 시그모이드가 아니라 소프트맥스 함수를 사용함.

 

소프트맥스 함수

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]]

지수함수를 적용하고 sum으로 나눠주면 시그모이드 값을 구할 수 있음. 

시그모이드 값을 다 더하면 1이 나옴.

 

scipy에서 softmax를 지원.

from scipy.special import softmax

proba = softmax(decision, axis=1)
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]]

 

 

시그모이드 함수 - 이진 분류

소프트맥스 함수 - 다중 분류

 

 

참고 자료

https://www.youtube.com/watch?v=pO27UnTsYQU&list=PLVsNizTWUw7HpqmdphX9hgyWl15nobgQX&index=9