핀아의 저장소 ( •̀ ω •́ )✧
[ML] 혼자 공부하는 머신러닝_4장 본문
💡 학습목표
1. K-최근접 이웃 알고리즘을 사용해서 다중 분류 수행과 로지스틱 회귀 알고리즘을 사용해서 이진/다중 분류 수행 2. 시그모이드 함수와 소프트맥스 함수의 차이에 대해 학습함
목차
- K-최근접 이웃을 사용한 다중 분류
- 데이터 준비
- K-최근접 이웃 분류기의 확률 예측
- 로지스틱 회귀
- 로지스틱 회귀 (이진 분류)
- 로지스틱 회귀 (다중 분류)
- 점진적인 학습
- 확률적 경사 하강법
1️⃣ K-최근접 이웃을 사용한 다중 분류
💡 다중 분류란 타깃 데이터에 2개 이상의 클래스가 포함된 문제임
K-최근접 이웃 알고리즘을 사용해서 이웃 클래스의 비율을 확률로 표현해봄
✅ 데이터 준비
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
'''
print(pd.unique(fish['Species']))
#['Bream', 'Roach', 'Whitefish', 'Parkki', 'Perch', 'Pike', 'Smelt']
#pandas to numpy
fish_input = fish[['Weight','Length','Diagonal','Height','Width']].to_numpy()
fish_target = fish['Species'].to_numpy()
print(fish_target[:5])
# ['Bream' 'Bream' 'Bream' 'Bream' 'Bream']
사이킷런 라이브러리는 타깃 데이터가 정수 타입(1, 0)으로 치환하지 않은 문자열이여도 입력 값으로 받을 수 있음
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
train_input, test_input, train_target, test_target = train_test_split(
fish_input, fish_target, random_state=42)
#표준점수로 스케일 변환
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, train_target)
print(kn.score(train_scaled, train_target))
#0.8907563025210085
print(kn.score(test_scaled, test_target))
#0.85
print(kn.classes_)
#['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish']
- 사이킷런의 객체 속성 뒤에 가 붙을 경우(ex. kn.classes) 학습 이후 나온 결과를 출력하는 것을 의미함
- kn.classes_는 K-최근접 이웃 알고리즘 학습 이후 학습 데이터 세트로부터 추출된 클래스 정보(속성)이며 알파벳 순으로 정렬됨
- Bream은 첫 번째 클래스, Parkki는 두 번째 클래스 임을 의미함
import numpy as np
print(kn.predict(test_scaled[:5]))
#['Perch' 'Smelt' 'Pike' 'Perch' 'Perch']
proba = kn.predict_proba(test_scaled[:5])
print(np.round(proba, decimals=4))
#['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish']
'''
[[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. ]]
'''
- predict_proba 메서드를 통해 해당 결과의 확률을 출력 할 수 있음
K-최근접 이웃 알고리즘의 단점은 이웃의 수에 따라 나올수 있는 확률이 정해져있음
예로 이웃의 수를 3으로 학습 시켰을 때 나올 수 있는 확률은 [0, 1/3, 2/3, 3/3]로 정해져있음
2️⃣ 로지스틱 회귀
💡 대표적인 분류 알고리즘이자 인공신경망에 기본이 되는 알고리즘
이름은 회귀 알고리즘이지만 분류 알고리즘에 속함
- 로지스틱 회귀는 선형회귀랑 유사한 형태로 선형 함수를 학습하는 알고리즘임
- 계산되는 z값을 바로 사용하면 회귀의 결과로 도출함으로 0~1의 사이의 확률로 표현하기 위해 시그모이드 함수(로지스틱 함수)를 취함
- z값을 시그모이드 함수를 취해 나온 출력 결과가 0.5 보다 크면 양성 클래스, 작으면 음성 클래스로 분류되고 만약 값이 정확히 0.5라면 일반적으로 음성 클래스로 판단함
- 시그모이드 함수 특성상 z 값만 가지고 음성/양성 클래스를 구분 할 수 있음(z값이 0보다 작으면 음성)
- predict 메서드는 z값만 가지고 결과를 출력하고 predict_proba 메서드는 시그모이드를 취한 결과 값을 기준으로 결과를 출력함
import numpy as np
import matplotlib.pyplot as plt
z = np.arange(-5, 5, 0.1)
#지수 함수 계산을 위해 numpy의 exp 메서드를 사용
phi = 1 / (1 + np.exp(-z))
plt.plot(z, phi)
plt.xlabel('z')
plt.ylabel('phi')
plt.show()
2️⃣ 로지스틱 회귀(이진 분류)
💡 로지스틱 회귀를 사용하여 이진 분류를 수행함
numpy의 boolean indexing을 사용하여 ['True']로 되어있는 원소만 추출
#도미와 빙어 데이터 세트만 True로 변경
bream_smelt_indexes = (train_target == 'Bream') | (train_target == 'Smelt')
print(bream_smelt_indexes)
'''
[
True False True False False False ...
]
'''
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)
print(lr.classes_)
# ['Bream' 'Smelt']
# Bream(음성 클래스), Smelt(양성 클래스)
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]]
'''
#계수와 절편을 출력해 z값의 선형 함수식을 구하고 직접 양성 클래스일 확률을 구해봄
print(lr.coef_, lr.intercept_)
#[[-0.4037798 -0.57620209 -0.66280298 -1.01290277 -0.73168947]] [-2.16155132]
#z = -0.404x무게 - 0.576x길이 - 0.663x대각선 - 0.013x높이 - 0.732x두께 - 2.161
decisions = lr.decision_function(train_bream_smelt[:5]) # z값 출력
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]
로지스틱 회귀가 이진분류일 경우 양성 클래스의 확률만 계산함
✅ 로지스틱 회귀(다중 분류)
💡 로지스틱 회귀를 사용하여 7개 종류의 생선 다중 분류를 수행함
#L2 규제(릿지), C의 값이 작아질수록 규제 강도가 강해짐
#C = default(1)
#max_iter 만큼 반복하여 학습을 수행함
#max_iter = default(10)
lr = LogisticRegression(C=20, max_iter=1000)
lr.fit(train_scaled, train_target)
print(lr.score(train_scaled, train_target))
# 0.93
print(lr.score(test_scaled, test_target))
# 0.92
print(lr.classes_)
# ['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish']
print(lr.predict(test_scaled[:5]))
# ['Perch' 'Smelt' 'Pike' 'Roach' 'Perch']
#z값에 소프트맥스 함수를 취한 확률 값
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,)
- lr.coef_.shape(7, 5)의 경우 7개의 클래스와 5개의 특성과 곱해지는 계수임을 알 수 있음
- 각 클래스 마다 선형 함수가 1개씩 만들여짐, 즉 z값이 클래스마다 1개씩 만들어짐을 의미함
- OvR(One-vs-Rest) 방식을 사용하여 z값을 도출함 각 클래스는 이진 분류의 형태로 z값을 산출하며 예로 클래스1의 z값을 구하기 위해 클래스1과 나머지 클래스2~7로 나누어 이진 분류를 수행함
- z값이 산출되면 소프트맥스 함수를 취해 확률을 계산함
#z값 출력
decision = lr.decision_function(test_scaled[:5])
print(np.round(decision, decimals=2))
#5개 샘플에 대해 각 7개의 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]]
'''
- 출력된 선형 함수의 결과인 각 7개의 z를 시그모이드 취하여 합하면 1의 값이 나오지 않음
- 다중 분류일 경우 시그모이드 함수가 아닌 소프트맥스 함수를 취하여 확률을 출력함
- z값에 지후 함수를 취한 값을 각 z 값을 지수 함수 취해 나온 모든 결과를 더한 값으로 분모로 나눔
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]]
'''
3️⃣ 점진적인 학습
💡 새로운 데이터가 들어왔을 때 모델을 학습하는 다양한 방법에 대해 알아봄
- 첫번째 방법은 새로운 데이터가 추가되면 매번 다시 훈련시키는 방법임 이 방법은 시간이 지날수록 쌓이는 데이터의 양이 많아지기 때문에 지속 가능한 방법으론 적절하지 못함
- 두번째 방법은 새로운 데이터가 추가되면 이전 데이터를 버림으로써 훈련 데이터 크기를 일정하하게 유지시켜 훈련하는 방법임 이 방법은 데이터를 버릴 때 중요한 데이터가 포함될 가능성이 있음
- 세번째 방법은 앞서 훈련한 모델을 버리지 않고 새로운 데이터가 들어오면 그 데이터에 대해서만 학습을 수행함 이러한 훈련 방법을 점진적 학습(또는 온라인 학습)이라 부르며 대표적인 점진적 학습 알고리즘은 확률적 경사 하강법임
✅ 확률적 경사 하강법
훈련 세트에서 랜덤하게 하나의 샘플을 선택하여 손실 함수의 경사를 따라 최적의 모델을 찾는 알고리즘을 의미함
'Big Data > ML & DL' 카테고리의 다른 글
[ML] 혼자 공부하는 머신러닝_5장 (0) | 2023.06.01 |
---|---|
[ML] 혼자 공부하는 머신러닝_3장-2 (0) | 2023.05.23 |
[ML] 혼자 공부하는 머신러닝_3장-1 (1) | 2023.05.22 |
[ML] 혼자 공부하는 머신러닝_2장 (1) | 2023.05.17 |
[ML] 혼자 공부하는 머신러닝_1장 (0) | 2023.05.16 |
Comments