AI/혼자공부하는머신러닝딥러닝

[ML] 04-1 로지스틱 회귀

inthyes 2024. 1. 21. 19:03

럭키백의 확률

럭키백에 생선이 7개 들어있고, 럭키백에 들어간 생선의 크기, 무게 등이 주어졌을 때 7개의 생선에 대한 확률을 출력해아한다.
 

데이터 준비

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

 
 
판다스의 unique()함수를 사용하여 Species 열에서의 고유한 값들을 출력할 수 있다.

print(pd.unique(fish['Species']))

 
species를 제외한 나머지 5개의 열을 입력 데이터로 사용한다.

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

 
species는 타깃 데이터로 사용한다.

fish_target = fish['Species'].to_numpy()
print(fish_target[:5])

 
훈련 세트, 테스트 세트로 train_test_split 클래스를 활용하여 분리한다.

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)

 
사이킷런의 StandardScaler 클래스를 사용해 훈련 세트와 테스트 세트를 표준화 전처리한다.

from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_input)
train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)

 

 

k-최근접 이웃 분류기의 확률 예측

최근접 이웃 개수인 k를 3으로 지정하여 모델을 훈련하고 점수를 확인한다.

from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier(n_neighbors = 3)
kn.fit(train_scaled, train_target)
print(kn.score(train_scaled, train_target))
print(kn.score(test_scaled, test_target))

 
classes_ 속성에는 타깃값이 알파벳순으로 정렬되어 저장된다.
그렇기 때문에 unique()로 출력되는 순서와 다르다.

print(kn.classes_)

 
테스트 세트에 있는 처음 5개의 샘플의 타깃값을 예측하면 아래와 같다.

print(kn.predict(test_scaled[:5]))

 
 
predict_proba() 메서드를 사용해 클래스별 확률값 반환을 확인할 수 있다.
round() 함수는 기본적으로 소수점 첫째 자리에서 반올림하며 decimals 매개변수를 사용하여 소수점 자리를 설정할 수 있다.

import numpy as np
proda = kn.predict_proba(test_scaled[:5])
print(np.round(proda, decimals= 4))

 
이 샘플의 이웃은 'Roach' 1개, 'Perch' 2개로 'Perch'일 확률이 0.6667로 클래스 확률을 제대로 예측했음을 확인할 수 있다.

distances, indexes = kn.kneighbors(test_scaled[3:4])
print(train_target[indexes])

 
 
하지만 3개의 최근접 이웃을 사용하기 때문에 확률은 0/3, 1/3, 2/3, 3/3이 전부라는 한계를 갖는다.

 

로지스틱 회귀

로지스틱 회귀는 분류 모델이다.

위 식에서 a, b, c, d, e는 가중치 혹은 계수를 뜻한다.
 
시그모이드(로지스틱)함수는 아래와 같이 코드를 구현하고 그려질 수 있다.
지수 함수 계산은 np.exp()함수를 통해 구현 가능하다.
 
아래와 같이 구현함으로서 시그모이드 함수의 출력이 0에서 1까지 변화하는 것을 알 수 있다.

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()

 
 

로지스틱 회귀로 이진 분류 수행하기

불리언 인덱싱을 이해하기 위해 예시 코드를 수행한다.

char_arr = np.array(['A', 'B', 'C', 'D', 'E'])
print(char_arr[[True, False, True, False, False]])

 
도미(Bream)와 빙어(Smelt)의 행만 골라내고, 이에 맞는 훈련 세트와 테스트 세트를 구현한다.

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]

 
훈련한 모델을 사용해 train_bream_smelt에 있는 5개의 샘플을 예측한다.

from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
lr.fit(train_bream_smelt, target_bream_smelt)
print(lr.predict(train_bream_smelt[:5]))

 
predict_proba() 메서드를 사용하여 예측 확률을 확인한다.
첫번째 열이 음성 클래스(0)에 대한 확률, 두번째 열이 양성 클래스(1)에 대한 확률이다.

print(lr.predict_proba(train_bream_smelt[:5]))

 
Bream, Smelt중 무엇이 양성 클래스인지는 classes_속성을 통해 확인이 가능하다.

 
로지스틱 회귀가 학습한 계수를 확인한다.

print(lr.coef_, lr.intercept_)

 
이 로지스틱 회귀 모델이 아래와 같은 방정식을 학습하였다는 것을 알 수 있다.

 
decision_function() 메서드를 통해 z값 출력이 가능하다.

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

 
scipy 라이브러리에 존재하는 explit() 메서드를 통해 시그모이드 함수를 거친 확률값을 출력할 수 있다.

from scipy.special import expit
print(expit(decisions))

 
출력값을 통해 decision_function() 메서드는 양성 클래스에 대한 z값을 반환함을 확인할 수 있다.
 

로지스틱 회귀로 다중 분류 수행하기

LogisticRegression 클래스는 기본적으로 릿지 회귀와 같이 계수의 제곱을 규제한다.(L2)

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

 
테스트  세트의 처음 5개 샘플에 대한 예측을 출력한다.

print(lr.predict(test_scaled[:5]))

 

 
이에 따른 예측 확률을 출력하면 아래와 같다.

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

첫 번째 샘플을 보면 세 번째 열의 확률(84%)이 가장 높다.
 
클래스 정보를 확인하기 위해 classes_속성을 출력한다.

print(lr.classes_)

 
다중 분류일 경우 선형 방정식의 형태를 확인하기 위해 coef_, intercept_를 출력한다.

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

coef_ 배열의 행 7개, 열 5개이며 intercept_ 7개가 존재함을 확인할 수 있다.
이는 이진 분류에서 보았던 z를 7개 계산했다는 의미이다.
다중 분류는 클래스마다 z값을 하나씩 계산한다.
이진 분류는 시그모이드 함수를 사용하지만 다중 분류는 소프트맥스 함수를 사용하여 7개의 z값을 확률로 변환한다.
 
소프트맥수 함수를 사용해 확률로 변환하는 코드를 수행하고자 한다.
 
우선 테스트 세트의 처음 5개 샘플에 대한 z1~z7의 값을 구해야한다.

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

 
사이파이의 소프트맥스 함수를 사용한다.
softmax()의 axis 매개변수는 소프트맥스를 계산할 축을 지정할 수 있다. 
 
아래 결과값이 proba 배열과 동일함을 알 수 있다.

from scipy.special import softmax
proba = softmax(decision, axis = 1)
print(np.round(proba, decimals = 3))

 

'AI > 혼자공부하는머신러닝딥러닝' 카테고리의 다른 글

[ML] 05-1 결정 트리  (1) 2024.01.22
[ML] 04-2 확률적 경사하강법  (1) 2024.01.21
[ML] 03-3 특성 공학과 규제  (0) 2024.01.20
[ML] 03-2 선형 회귀  (0) 2024.01.20
[ML] 03-1 k-최근접 이웃 회귀  (0) 2024.01.20