import pandas as pd
fish = pd.read_csv('http://bit.ly/fish_csv_data')
fish_input = fish[['Weight','Length','Diagonal','Width']].to_numpy()
print(fish_input[: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 StandadScaler
ss = StandardScaler()
ss.fit(train_input)
train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)
k-최근접 이웃 분류기의 확률 예측
from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier(n_neighbors=3)
kn.fit(train_scaled, test_scaled)
print(kn.score(train_scaled, train_target))
>>> 0.8067226890756303
print(kn.score(test_scaled, test_target))
>>> 0.725
타깃 데이터를 만들 때 fish['Species']를 사용해 만들었기 때문에 훈련 세트와 테스트 세트의 타깃 데이터에는 7가지 생선이 들어가있다.
이렇게 타깃 데이터가 2개 이상의 클래스가 포함된 문제를 다중 분류라고 부른다.
이진 분류에는 양성, 음성 클래스를 1과 0으로 지정하여 타깃 데이터를 만들었지만
다중 분류에서는 문자열로 된 타깃값을 그대로 사용할 수 있다.
타깃값을 사이킷런 모델에 전달하면 알파벳 순으로 매겨진다.
print(kn.classes_)
>>> ['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish']
print(kn.predict(test_scaled[:5]))
>>> ['Perch' 'Smelt' 'Pike' 'Parkki' 'Perch']
predict_proba()는 5개 샘플에 대한 예측은 어떤 확률로 만들어졌는지 알려준다.
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. ]]
첫 번째 열이 Bream, 두 번째 열이 parkki에 대한 확률이다.
distances, indexes = kn.neighbors(test_scaled[3:4])
print(train_target[indexes])
>>> [['Parkki' 'Parkki' 'Perch']]
로지스틱 회귀
로지스티 회귀는 이름은 회귀이지만 분류 모델이다.
특성은 늘어났지만 다중 회귀를 위한 선형 방정식과 같다.
z는 어떤 값도 가능하지만 우리가 원하는 확률이 되려면 0~1 사이 값이 되어야 한다.
시그모이드 함수를 사용하면 이것이 가능하다.
로지스틱 회귀로 이진 분류 수행하기
불리언 인덱싱 방식을 사용해서 훈련 세트에서 도미와 빙어의 행만 골라내겠다.
bream_smelt_indexes = (train_target == 'Bream') or (train_target == 'smelt')
train_bream_smelt = train_scaeld[bream_smelt_indexes]
target_bream_smelt = train_target[bream_smelt_indexes]
from sklearn.limear_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']
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]])
print(lr.classes_)
>>> ['Bream' 'Smelt']
print(lr.coef_, lr.intercept_)
>>> [[-0.59865464 -0.85208293 -0.97968478 -1.0791584 ]] [-2.67789827]
LogistiRegression 클래스는 decision_function() 메서드로 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(decisions))
>>> [0.00360556 0.96253235 0.0081313 0.02370357 0.00364374]
decision_function() 메서드는 양성 클래스에 대한 z 값을 반환한다.
로지스틱 회귀로 다중 분류 수행하기
LogisticRegression 클래스는 기본적으로 반복적인 알고리즘을 사용한다.
max_iter 매개변수에서 반복 횟수를 지정하는 것이다. 기본값은 100이다.
또한 기본적으로 릿지 회귀와 같이 계수의 제곱을 규제한다. 이런 규제를 L2 규제라고 부른다.
LogisticRegression에서 규제를 제어하는 매개변수는 C이다. 기본값은 1이다.
alpha와 다르게 작을 수록 규제가 커진다.
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.8823529411764706
>>> 0.875
print(lr.predict(test_scaled[:5]))
>>> ['Perch' 'Smelt' 'Pike' 'Perch' 'Perch']
print(lr.classes_)
>>> ['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish']
proba = lr.predict_proba(test_scaled[:5])
print(np.round(proba, decimals=3))
>>> [[0.002 0.062 0.801 0. 0.128 0.003 0.003]
[0. 0.051 0.018 0. 0.003 0.928 0. ]
[0.036 0.005 0.002 0.951 0.005 0.001 0. ]
[0.111 0.114 0.52 0.003 0.219 0. 0.033]
[0.016 0.082 0.67 0.001 0.221 0.001 0.009]]
[0.016 0.082 0.67 0.001 0.221 0.001 0.009]]
# 5개 샘플에 대한 예측과 7개 생선에 대한 확률을 계산했으므로 7개의 열이 출력된다.
이진 분류에서는 시그모이드 함수를 사용해서 z를 0~1 사이의 값으로 변환한다.
다중 분류는 이와 달리 소프트맥스 함수를 사용해서 7개의 z값을 확률로 변환한다.
decision = lr.decision_function(test_scaeld[:5])
print(np.round(decision, decimals=2))
>>> [[-1.57 1.96 4.51 -5.57 2.68 -1.06 -0.95]
[-1.75 3.48 2.41 -6.15 0.63 6.37 -4.99]
[ 1.92 -0.13 -0.81 5.2 -0. -1.62 -4.56]
[ 1.42 1.45 2.97 -2.08 2.11 -6.09 0.21]
[-0.26 1.4 3.51 -3.16 2.4 -3.15 -0.75]]
from scipy.special import softmax
proba = softmax(decision, axis=1)
print(np.round(proba, decimals=3))
>>> [[0.002 0.062 0.801 0. 0.128 0.003 0.003]
[0. 0.051 0.018 0. 0.003 0.928 0. ]
[0.036 0.005 0.002 0.951 0.005 0.001 0. ]
[0.111 0.114 0.52 0.003 0.219 0. 0.033]
[0.016 0.082 0.67 0.001 0.221 0.001 0.009]]
SGDClassifier
from sklearn.linear_model import SGDClassifier
sc = SGDClassifier(loss='log_loss', max_iter=10, random_state=42)
sc.fit(train_scaled, train_target)
print(sc.score(train_scaled, train_target))
print(sc.score(test_scaled, test_target))
>>> 0.773109243697479
>>> 0.775
# partial_fit()은 fit() 메서드와 사용법은 같지만 호출할 때마다 1 에포크씩 이어서 학습 가능
sc.partial_fit(train_scaled, train_target)
print(sc.score(train_scaled, train_target))
print(sc.score(test_scaled, test_target))
>>> 0.7983193277310925
>>> 0.775
아직 점수가 낮지만 조금 향상된 것을 확인할 수 있다.
이 모델을 여러 에포크에서 더 훈련을 시켜야 한다.
하지만 얼마나 더 훈련을 시킬지 명확하지 않다.
300번의 에포크 동안 훈련을 반복하고 반복마다 훈련 세트와 테스트 세트의 점수를 계산해서 리스트에 추가한다.
import numpy as np
sc = SGDClassifier(loss='log_loss', random_state=42)
train_score = []
test_score = []
classes = np.unique(train_target)
for _ in range(0,300) :
sc.partial_fit(train_scaled, train_target, claasses=classes)
train_score.append(sc.score(train_scaled, train_target))
test_score.append(sc.score(test_scaled, test_target))
import matplotlib.pyplot as plt
plt.plot(train_score)
plt.plot(test_score)
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.show()
100 에폭쯤에서 변화가 보이니 100 에폭으로 맞추고 다시 모델을 훈련해보겠다.
sc = SGDClassifier(loss='log_loss', max_iter=100, tol=None, random_state=42)
sc.fit(train_scaled, train_target)
print(sc.score(train_scaled, train_target))
print(sc.score(test_scaled, test_target))
>>> 0.957983193277311
>>> 0.925
SGDClassifier는 일정 에포크 동안 성능이 향상되지 않으면 더 훈련하지 않고 자동으로 멈춘다.
tol 매개변수는 향상될 최솟값을 지정한다.
앞의 코드에서는 None으로 지정해서 자동으로 멈추지 않고 100 에폭을 무조건 반복하게 설정한 것이다.
사실 loss 매개변수의 기본값은 hinge이다. 이것은 서포트 벡터 머신으로 불리는 또다른 손실 함수이다.
sc = SGDClassifier(loss='hinge', tol = None, max_iter=100, random_state=42)
sc.fit(train_scaled, train_target)
print(sc.score(train_scaled, train_target))
print(sc.score(test_scaled, test_target))
>>> 0.9495798319327731
>>> 0.925
'Major Study > 25-1 Machine Learning' 카테고리의 다른 글
[Machine Learning] chapter5 - 트리 알고리즘 (0) | 2025.05.14 |
---|---|
[Machine Learning] chapter3 - 회귀 알고리즘과 모델 규제 (0) | 2025.04.11 |
[Machine Learning] chapter 2 - 데이터 다루기 (0) | 2025.04.01 |
[Machine Learning] chapter 1 - 머신러닝 (0) | 2025.04.01 |