본문 바로가기
CS/머신러닝

혼자 공부하는 머신러닝 + 딥러닝 - Ch 4-1

by jaehoonChoi 2023. 1. 12.

Ch 4-1에서는 로지스틱 회귀에 대해 공부합니다.

 

로지스틱 회귀는 선형회귀를 토대로 활성화 함수를 결합하여 이진분류를 하는데

사용됩니다. 또한 다중회귀를 토대로 다중분류도 가능합니다. 

 

[ 문제 ] 

이번장에서 해결할 문제는 다음과 같습니다. 상품으로 생선 럭키백을 만듭니다.

럭키백에는 각 생선이 등장할 확률이 제시됩니다. 상품에 들어갈 생선의 크기, 무게, 길이, 높이

등이 주어질 때, 7종류의 생선에 대한 확률을 예측하는 문제입니다. 

 

 

[ 접근 ] 

간단한 접근으로 KNN 알고리즘을 생각해볼까요? KNN 알고리즘으로 확률을 구한다면

각 생선이 나올 확률들이 우리가 정한 \(k\)값에 의존하게 됩니다. 예를 들어 \(k=4\)로 한다면, 확률은 항상

0, 1/4, 2/4, 3/4, 4/4 중 하나입니다. 생선은 7종인데 좀만 생각해봐도 너무 이상하죠.

그냥 데이터만으로 확률을 구하는 방법이 필요합니다.

 

3-2에서 학습한 다중회귀를 통해 로지스틱 회귀를 진행할 수 있습니다.

로지스틱 회귀는 크로스 엔트로피 함수를 손실함수로 하여 최적인 계수들을 구하고

활성화 함수를 통해 확률을 예측해줍니다. 

 

 

[ 데이터 분석 ] 

우선 데이터를 불러와서 어떻게 구성되어 있는지 봅시다. 파이썬의 head()와 tail()은 데이터의 

상단이나 하단을 출력해줍니다. 상단과 하단을 출력합니다. 

import numpy as np
import pandas as pd
fish=pd.read_csv("https://bit.ly/fish_csv_data")
print(fish.head()) # 데이터 상단 출력 
print(fish.tail()) # 데이터 하단 출력
Species  Weight  Length  Diagonal   Height   Width
0   Bream   242.0    25.4      30.0  11.5200  4.0200
1   Bream   290.0    26.3      31.2  12.4800  4.3056
2   Bream   340.0    26.5      31.1  12.3778  4.6961
3   Bream   363.0    29.0      33.5  12.7300  4.4555
4   Bream   430.0    29.0      34.0  12.4440  5.1340
    Species  Weight  Length  Diagonal  Height   Width
154   Smelt    12.2    12.2      13.4  2.0904  1.3936
155   Smelt    13.4    12.4      13.5  2.4300  1.2690
156   Smelt    12.2    13.0      13.8  2.2770  1.2558
157   Smelt    19.7    14.3      15.2  2.8728  2.0672
158   Smelt    19.9    15.0      16.2  2.9322  1.8792

 

bream과 smelt가 있네요. 더 다른 종은 없는지 Species 열의 unique 원소들을 출력해봅시다.

 

print(pd.unique(fish["Species"])) # 종 출력
['Bream' 'Roach' 'Whitefish' 'Parkki' 'Perch' 'Pike' 'Smelt']

 

다양한 종이 있습니다. 다중 분류가 필요하겠군요. 이제 Species 열을 빼고 데이터를 만든 후에

훈련과 데스트 데이터로 분할합시다. 

df=fish.drop(['Species'], axis=1)
fish_input=df.to_numpy()
fish_target=fish['Species'].to_numpy()

# 데이터 분리
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test=train_test_split(
    fish_input, fish_target, random_state=42
)

# 정규화
from sklearn.preprocessing import StandardScaler
ss=StandardScaler()
ss.fit(X_train)
X_train=ss.transform(X_train)
X_test=ss.transform(X_test)

 

 

 

[ 로지스틱 회귀 ] 

이제 로지스틱 회귀를 수행합니다. 규제값 C와 반복수 max_iter를 조정하며 훈련합니다.

# 다중회귀 로지스틱 
from sklearn.linear_model import LogisticRegression
lr=LogisticRegression(C=20, max_iter=1000)
# C, max_iter: 규제값과 반복횟수 # C가 클수록 규제 완화
lr.fit(X_train, y_train)
print("훈련 성능: ", lr.score(X_train, y_train))
print("테스트 성능 ", lr.score(X_test, y_test))
훈련 성능:  0.9327731092436975
테스트 성능  0.925

 

성능이 비교적 괜찮은걸 알 수 있습니다.

데이터가 더 많고 반복횟수나 규제를 조정하면 더 좋은 성능을 낼 수 있습니다.

근데 식이 어떻게 되는지 궁금합니다. coef_와 intercept_를 출력해보죠. 

 

print("계수, 상수: ", lr.coef_.shape, lr.intercept_.shape)
계수, 상수:  (7, 5) (7,)

 

계수가 7*5 행렬로 나옵니다. 원래 다중회귀를 하면 coef_는 1*n 꼴로 각 데이터의 feature의 개수만큼 나옵니다. 

그런데 그런게 7개가 있다는 말은 7종 생선에 대해 각각 다중회귀를 수행했음을 알 수 있습니다! 

이제 각 회귀의 결괏값을 토대로 활성화함수로 확룰을 예측하게 되는 것입니다. 

 

 

[ 확률 구하기 by softmax ] 

이제 확률을 구해주는 내장함수 predict_proba 함수를 이용합니다.

5개의 데이터에 대해 7종의 생선 중 어떤게 확률이 높은지 출력을 해봅시다.

# 확률 예측
prob_pred=lr.predict_proba(X_test[:5])
print("내장함수 predict_proba를 통한 확률계산")
print(np.round(prob_pred, decimals=3))
내장함수 predict_proba를 통한 확률계산
[[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]]

보다시피 1번 데이터는 3번 생선, 2번 데이터는 6번 생선... 이렇게 확률을 말해주고 있습니다.

이러한 확률은 softmax 함수를 통해 계산됩니다.

다중회귀의 결괏값 \(z_1, ... , z_7\)에 대하여, \(e^z\)의 값을 구해줍니다. 

각 확률은 $$ p_i=\frac{e^{z_i} }{S} , S=\sum e^{z_i} $$ 로 계산됩니다.

\(z\)값들을 출력하고 softmax 함수를 적용한 확률값도 같이 출력을 해봅시다.

# 다중회귀 z값 출력
decision=lr.decision_function(X_test[:5])
print("다중회귀 z값 출력")
print(np.round(decision, decimals=2))

# softmax 함수 계산으로 확률예측과 일치하는지 확인
from scipy.special import softmax
prob_softmax=softmax(decision, axis=1) # 각 행에 대한 softmax 계산 
print("softmax 함수를 통한 확률계산")
print(np.round(prob_softmax, decimals=3))
다중회귀 z값 출력
[[ -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]]
softmax 함수를 통한 확률계산
[[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]]

 

softmax 함수를 통한 계산결과가 내장함수 predict_proba 를 사용한 결과와 일치함을 알 수 있습니다.

이렇게 다중분류는 각각의 분류종마다 선형(다중)회귀를 수행하여 \(z\)값을 만들고,

이들을 지수함수 \(e^z\)로 만들어  그 값들의 비율로 확률을 정해줍니다. 

 

이번 실습의 전체 코드입니다.

import numpy as np
import pandas as pd
fish=pd.read_csv("https://bit.ly/fish_csv_data")
print(fish.head()) # 데이터 상단 출력 
print(fish.tail()) # 데이터 하단 출력
print(pd.unique(fish["Species"])) # 종 출력
df=fish.drop(['Species'], axis=1)
fish_input=df.to_numpy()
fish_target=fish['Species'].to_numpy()

# 데이터 분리
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test=train_test_split(
    fish_input, fish_target, random_state=42
)

# 정규화
from sklearn.preprocessing import StandardScaler
ss=StandardScaler()
ss.fit(X_train)
X_train=ss.transform(X_train)
X_test=ss.transform(X_test)

# 다중회귀 로지스틱 
from sklearn.linear_model import LogisticRegression
lr=LogisticRegression(C=20, max_iter=1000)
# C, max_iter: 규제값과 반복횟수 # C가 클수록 규제 완화
lr.fit(X_train, y_train)
print("훈련 성능: ", lr.score(X_train, y_train))
print("테스트 성능 ", lr.score(X_test, y_test))

# 계수와 상수 관찰
print("계수, 상수: ", lr.coef_.shape, lr.intercept_.shape)

# 확률 예측
prob_pred=lr.predict_proba(X_test[:5])
print("내장함수 predict_proba를 통한 확률계산")
print(np.round(prob_pred, decimals=3))

# 다중회귀 z값 출력
decision=lr.decision_function(X_test[:5])
print("다중회귀 z값 출력")
print(np.round(decision, decimals=2))

# softmax 함수 계산으로 확률예측과 일치하는지 확인
from scipy.special import softmax
prob_softmax=softmax(decision, axis=1) # 각 행에 대한 softmax 계산 
print("softmax 함수를 통한 확률계산")
print(np.round(prob_softmax, decimals=3))

 

댓글