클래스의 확률의 출력이 필요할 때 K-nn은 주변 이웃을 찾아주니까 이웃의 클래스의 비율을 확률이라 출력하면 된다.
import pandas as pd
fish=pd.read_csv('https://bit.ly/fish_csv_data')
fish.head()
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
판다스의 unique() 함수를 이용해서 Species 열의 고유한 값을 추출
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)
사이킷런의 KNeighborsClassifier 클래스 객체를 만들고 훈련 세트로 모델을 훈련하고 훈련 세트와 테스트 세트의 점수 확인, 이웃의 개수를 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))
0.8907563025210085
0.85
훈련 세트와 테스트 세트의 타깃 데이터에도 7개의 생선 종류가 들어가 있다.
이렇게 타깃 데이터에 2개 이상의 클래스가 포함된 문제를 "다중 분류(multiclass classification)"라고 부른다
이진 분류에서는 양성 클래스와 음성 클래스를 각각 1과 0으로 지정하여 타깃 데이터를 만든다.
다중 분류에서도 타깃값을 숫자로 바꾸어 입력할 수 있지만 사이킷런에서 문자열로 된 타깃값을 그대로 사용 가능
이때 타깃값을 그대로 사이킷런 모델에 전달하면 순서가 자동으로 알파벳 순으로 저장된다.
kn.classes_
array(['Bream', 'Parkki', 'Perch', 'Pike', 'Roach', 'Smelt', 'Whitefish'], dtype=object)
'Bream'이 첫 번째 클래스, 'Parkki'가 두 번째 클래스
predict() 메서드는 타깃값으로 예측을 출력한다. 테스트 세트의 처음 5개의 샘플의 타깃값 예측
print(kn.predict(test_scaled[:5]))
['Perch' 'Smelt' 'Pike' 'Perch' 'Perch']
사이킷런의 분류 모델은 predict_proba() 메서드로 클래스별 확률값을 반환한다.
테스트 세트의 처음 5개의 샘플에 대한 확률 출력하고 round() 함수는 기본적으로 소수점 첫째 자리에서 반올림하는데, decimals 매개변수로 유지할 소수점 아래 자릿수를 지정
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. 0.6667 0. 0.3333 0. 0. ]
[0. 0. 0.6667 0. 0.3333 0. 0. ]]
predictproba() 메서드의 출력 순서는 classes 속성과 같다.
첫 번째 열이 'Bream'에 대한 확률
distances, indexes=kn.kneighbors(test_scaled[3:4])
print(train_target[indexes])
[['Roach' 'Perch' 'Perch']]
이 샘플의 이웃은 'Roach'가 1개 'Perch'가 2개이다.
'Roach'의 확률은 1/3, 'Perch'의 확률은 2/3이다. 앞에서 출력한 네 번째 샘플의 클래스 확률과 같다.
로지스틱 회귀는 이름은 회귀이지만 분류 모델이다.
이 알고리즘은 선형 회귀와 동일하게 선형 방정식을 학습한다.
ex)
x (Weight) + x (Length) + x (Diagonal) + x (Height) + x (Width) +
여기서 a,b,c,d,e는 가중치 or 계수
z는 어떤 값도 가능하다. 그러나 확률이 되려면 0~1 사이 값이어야 한다.
z가 아주 큰 음수일 때 0이 되고, z가 아주 큰 양수일 때 1이 되도록 바꾸는 방법: 시그모이드 함수(로지스틱 함수)
시그모이드 그래프
z가 무한하게 큰 음수일 경우 0에 가까워지고, z가 무한하게 큰 양수가 될 때는 1에 가까워진다.
z가 0이 될 때는 0.5가 된다.
시그모이드 함수의 출력이 0.5보다 크면 양성 클래스, 0.5보다 작으면 음성 클래스로 판단.
불리언 인덱싱을 통해 훈련 세트에서 '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]
로지스틱 회귀 모델 훈련
from sklearn.linear_model import LogisticRegression
lr=LogisticRegression()
lr.fit(train_bream_smelt, target_bream_smelt)
처음 5개 샘플 예측, 예측확률 출력
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.coef_, lr.intercept_)
[[-0.4037798 -0.57620209 -0.66280298 -1.01290277 -0.73168947]] [-2.16155132]
x (Weight) + x (Length) + x (Diagonal) + x (Height) + x (Width) +
LogistiRegression 모델의 decision_function() 메서드로 z값 계산
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]
predict_proba() 메서드 출력의 두 번째 열과 동일.
즉 decision_function() 메서드는 양성 클래스에 대한 z 값을 반환
LogisticRegression 클래스는 기본적으로 반복적인 알고리즘 사용.
max_iter 매개변수에서 반복 횟수를 지정할 수 있다. 기본값은 100
LogisticRegression은 릿지 회귀와 같이 계수의 제곱을 규제한다.
LogisticRegression 에서 규제를 제어하는 매개변수는 C이다. C는 alpha와 반대로 작을수록 규제가 커긴다. C의 기본값은1
Logistic 클래스로 다중 분류 모델을 훈련
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
print(lr.predict(test_scaled[:5]))
['Perch' 'Smelt' 'Pike' 'Roach' 'Perch']
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,)
다중 분류는 클래스마다 z값을 하나씩 계산한다. 가장 높은 z값을 출력하는 클래스가 예측 클래스가 된다.
이진 분류에서는 시그모이드 함수를 이용했다.
다중 분류는 소프트 맥스 함수를 사용해 7개의 z값을 확률로 변환
소프트맥스 함수
여러 개의 선형 방정식의 출력값을 0~1 사이로 압축하고 전체 합이 1이 되도록 만든다.
이를 위해 지수 함수를 사용하기 때문에 정규화된 지수 함수라고도 부른다
decision_function() 메서드로 z1~z7까지의 값을 구한 다음 소프트맥스 함수를 사용해 확률로 바꾼다
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]]
사이파이는 소프트맥스 함수를 제공한다. scipy.special 아래에 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]]
앞에서 구한 proba 배열과 일치한다