[Machine Learning] chapter 4. 다양한 분류 알고리즘
1. 로지스틱 회귀
데이터 준비
import pandas as pd
fish = pd.read_csv('http://bit.ly/fish_csv_data')
fish.head()
위 데이터프레임에서 Species 열을 타깃으로 만들고 나머지 5개 열을 입력 데이터로 사용하겠다.
print(pd.unique(fish['Species']))
>>> ['Bream' 'Roach' 'Whitefish' 'Parkki' 'Perch' 'Pike' 'Smelt']
fish_input = fish[['Weight','Length','Diagonal','Width']].to_numpy()
print(fish_target[:5])
>>> [[242. 25.4 30. 4.02 ]
[290. 26.3 31.2 4.3056]
[340. 26.5 31.1 4.6961]
[363. 29. 33.5 4.4555]
[430. 29. 34. 5.134 ]]
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-최근접 이웃 분류기의 확률 예측
KNeighborsClassifier 클래스 객체를 만들고 훈련시킨다.
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)
>>> 0.8067226890756303
>>> 0.725
지금 우리의 타깃 데이터에는 7개의 생선 종류가 들어가있다.
이렇게 타깃 데이터에 2개 이상의 클래스가 포함된 문제를 다중 분류라고 부른다.
앞에서 이진 분류를 할 때는 클래스를 1과 0으로 지정해서 타깃 데이터를 만들었다.
다중 분류에서도 숫자로 바꾸어 입력해도 되지만 사이킷런에는 문자열로 된 타깃값을 그대로 사용해도 된다.
주의할 점은 사이킷런 모델로 전달하면 순서가 기존과 다르게 알파벳 순으로 매겨진다.
print(kn.classes_)
>>> ['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish']
predict() 메서드로 처음 5개 샘플의 타깃값을 예측해보겠다.
print(kn.predict(test_scaled[:5]))
>>> ['Perch' 'Smelt' 'Pike' 'Parkki' 'Perch']
이제 predict_proba() 메서드로 클래스별 확률값을 반환하겠다.
import numpy as np
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.6667 0.3333 0. 0. 0. 0. ]
[0. 0. 1. 0. 0. 0. 0. ]]
round() 함수는 기본적으로 소수점 첫째 자리에서 반올림을 하는데 decimals 매개변수로 소수점 아래 자릿수를 지정할 수 있다.
위 출력 결과는 앞서 보았던 classes_ 속성과 같다.
즉, 첫 번째 열이 'Bream'에 대한 확률이고 두 번째 열이 'Parkki'에 대한 확률이다.
이 모델이 계산한 확률이 가장 가까운 이웃의 비율이 맞는지 확인해보자.
distances, indexes = kn.neighbors(test_scaled[3:4])
print(train_scaled[indexes])
>>> [['Parkki' 'Parkki' 'Perch']]
로지스틱 회귀
로지스틱 회귀는 이름은 회귀이지만 분류 모델이다.
이 알고리즘은 선형회귀와 동일한 선형 방정식을 학습한다.
시그모이드 함수를 사용하면 z가 아주 큰 음수일 때는 0이 되고 아주 큰 양수일 때는 1이 되도록 바뀌고 0일 때는 0.5가 된다.
그럼 z는 어떤 값이 되더라도 0~1 사이의 범위를 벗어날 수 없기 때문에 확률로 계산할 수 있다.
로지스틱 회귀로 이진 분류 수행하기
char_arr = np.array(['A','B','C','D','E'])
print(char_arr[[True, False, True, False, False]])
>>> ['A','C']
위 배열에서 'A'와 'C'만 골라내려면 첫 번째, 세 번째 원소만 True이고 나머지 원소는 False인 배열을 전달하면 된다.
이와 같은 방식을 사용해서 훈련 세트에서 Bream와 Smelt 행을 골라내겠다.
bream_smelt_indexes = (train_target == 'Bream') or (train_target == 'Smelt')
train_bream_smelt = train_scaled[bream_smelt_indexes]
target_bream_smelt = train_target[bream_smelt_indexes]
bream_smelt_indexes 배열은 도미와 빙어일 경우 True이고 그 외는 모두 False 값이 들어가 있을 것이다.
따라서 이 배열을 이용해서 train_scaled와 train_target 배열에 불리언 인덱싱을 적용하면 도미와 빙어 데이터만 골라낼 수 있다.
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
lr.fit(train_bream_smelt, target_bream_smelt)
print(lr.score(train_bream_smelt[:5]))
>>> ['Bream' 'Smelt' 'Bream' 'Bream' 'Bream']
두 번째 샘플을 제외하고 모두 도미로 예측했다.
print(lr.predict_proba(train_bream_smelt[:5])
>>> array([[0.99639444, 0.00360556],
[0.03746765, 0.96253235],
[0.9918687 , 0.0081313 ],
[0.97629643, 0.02370357],
[0.99635626, 0.00364374]])
첫 번째 열이 음성 클래스 (0)에 대한 확률이고 두 번재 열이 양성 클래스 (1)에 대한 확률이다.
bream과 smelt 중에 어떤 것이 양성 클래스인지 확인해보겠다.
print(lr.classes_)
>>> ['Bream', 'Smelt']
빙어가 양성 클래스인 것을 알 수 있다.
predict_proba() 메서드가 반환한 두 번째 샘플 값을 보면 양성 클래스인 빙어의 확률이 높으므로 빙어로 예측한다.
나머지는 모두 도미로 예측한다.
print(lr.coef_, lr.intercept_)
>>> [[-0.59865464 -0.85208293 -0.97968478 -1.0791584 ]] [-2.67789827]
이 로지스틱 회귀 모델이 학습한 방정식은 위와 같다.
그럼 LogisticRegression 모델로 z 값을 계산해보자.
# z값 계산
decisions = lr.decision_function(train_bream_smelt[:5])
print(decisions)
>>> [-5.6216656 3.24608969 -4.80387025 -3.71814047 -5.61109532]
# 확률 계산
from scipy.special import expit
print(expit(dicisions)) # sigmoid
>>> [0.00360556 0.96253235 0.0081313 0.02370357 0.00364374]
decision_function() 메서드는 양성 클래스에 대한 z 값을 반환한다.