2021. 6. 24. 19:59ㆍICT 멘토링/혼자 공부하는 머신러닝+딥러닝

지난 시간에

럭키 백

확률에 따라 다른 상품.
확률 예측으로 회귀로 판단하는 것이 아니라 각 상품에 대한 확신(확률)이 얼마인지에 따라 분류하는 것임.
확률 계산하기

데이터 준비
import pandas as pd
fish = pd.read_csv('https://bit.ly/fish_csv_data')
fish.head() # 만들어진 데이터프레임을 테이블로 출력. 첫번째 행은 csv 1라인 헤더 의미. 인덱스는 실제 csv 파일에는 없는 것.
fish_input = fish[['Weight', 'Length', 'Diagonal', 'Height', 'Width']].to_numpy()
fit_target = fish['Species'].to_numpy()

to_numpy()를 통해 넘파이 형식으로 변경.
데이터 변환
k-최근접 이웃의 다중 분류
from sklearn. neighbors improt KNeighborsClassifier
kn = KNeighborsClassifier(n_neighbors=3)
kn.fit(train_scaled, train_target)
print(kn.classes_)
# //['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish']
print(kn.predict(test_scaled[:5]))
# //['Perch' 'Smelt' 'Pike' 'Perch' 'Perch']
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. ]]
n_neighbors = 3으로 지정해서 default 5에서 3으로 변경
species는 문자열로 되어 있는데 사이킷런은 자체적으로 정수로 바꿔줌.
훈련된 후에 타깃값을 확인하려면 classes_를 하면 됨.
객체 속성에 _가 끝에 붙어 있으면 학습한 값이라고 생각하면 됨.
알파벳 순서로 되어 있음.
어떤 것이 0이고 1이고 한지 label을 정해야 하는데 이걸 기본적으로 알파벳 순서로 하는 것.
label 순서를 정하고 싶으면 수동으로 0과 1 지정해서 넣어주면 됨.
확률 출력하려면 predict_proba 메소드 사용.

출력된 확률은 각 행이 각 샘플을 의미하는 것이고 각 열의 요소가 클래스에 대한 확률임.
각 행의 확률의 합은 1이 되고, 높은 확률을 갖는 것이 해당 값으로 정해짐.
그런데 이웃한 샘플이 3개밖에 없어서 확률은 1/3, 2/2, 3/3 밖에 나오지 않음.
확률이라고 하기엔 너무 단순하지 않은가?
더 나은 확률은??
로지스틱 회귀

선형 함수 형태와 유사.
z 값을 그대로 사용하면 회귀 알고리즘이 되므로 확률로 쓰기 위해서는 z 값을 0~1 의 값으로 만들어줘야 함.
S자 형태의 함수를 시그모이드 함수 = 로지스틱 함수 라고 함.
Z값이 -∞에서 ∞의 값을 가지고 Φ 값은 0에서 1.0의 값을 가짐.
Z=0 == Φ = 0.5
위와 같이 Φ 값으로 굳이 계산하여 확률 값으로 바꾸지 않더라도 Z 값만으로도 예측 가능함.
메소드
predict - Zpredict_proba - Φ
로지스틱 회귀(이진 분류)
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)
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]]
boolean indexing - true, false 값을 넣어서 true 값만 뽑아내는 방식.
도미와 빙어만 true로 만들고 나머지는 모두 false
스케일은 행의 길이와 동일.
predict는 Z 값에 따른 분류
predict_proba는 Φ 값에 따른 분류
알파벳 순서로 label 하여 Bream = 0, Smelt = 1 이 됨.
로지스틱 회귀 계수 확인
print(lr.coef_, lr.intercept_)
# //[[-0.4037798 -0.57620209 -0.66280298 -1.01290277 -0.73168947]] [-2.16155132]
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]

expit - decisions 값을 sigmoid 함수에 넣어 확률 값을 구하는 메소드
앞에서 본 predict_proba 메소드 결과 값과 동일함.
로지스틱 회귀(다중 분류)
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
max_iter - 반복횟수 조정. default = 100
C - 규제의 강도. L2 norm 규제가 기본적으로 적용되는데 C 값↑ 규제↓, C 값↓ 규제↑.
score 정확도
분류 모델이라 R square가 아닌 score - 정확도 확인.
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,)
5개 행-샘플, 7개 확률-클래스
이진분류와 동일하게 확률 출력함.
행렬의 크기만 확인하면 7개의 행과 5개의 열을 가진 행렬이 존재함.
5개는 특성과 곱해지는 계수이고 7개는 클래스 의미.
클래스마다 선형함수(Z)가 하나씩 만들어짐.
7개의 선형함수를 만들어서 각 샘플에 대해 선형함수 값이 가장 큰 것이 예측값이 됨.
Z 값을 학습할 때는 마치 이진분류와 같이 학습함.
하나만 양성, 나머지는 다 음성으로 두고 학습하는 것.
OvR - One vs Rest
7개의 Z가 나오는데 이를 시그모이드로 바꾸면 7개의 시그모이드의 합은 1이 됨.
다중 분류일 때는 시그모이드가 아니라 소프트맥스 함수를 사용함.
소프트맥스 함수
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]]

지수함수를 적용하고 sum으로 나눠주면 시그모이드 값을 구할 수 있음.
시그모이드 값을 다 더하면 1이 나옴.
scipy에서 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]]
시그모이드 함수 - 이진 분류
소프트맥스 함수 - 다중 분류
참고 자료
https://www.youtube.com/watch?v=pO27UnTsYQU&list=PLVsNizTWUw7HpqmdphX9hgyWl15nobgQX&index=9
'ICT 멘토링 > 혼자 공부하는 머신러닝+딥러닝' 카테고리의 다른 글
11강 - 로지스틱 회귀로 와인 분류하기 & 결정 트리 (0) | 2021.06.24 |
---|---|
10강 - 확률적 경사 하강법 알아보기 (0) | 2021.06.24 |
8강 - 특성 공학과 규제 알아보기 (0) | 2021.06.17 |
7강 - 사이킷런으로 선형 회귀 모델 만들어 보기 (0) | 2021.06.17 |
6강 - 회귀 문제를 이해하고 k-최근접 이웃 알고리즘으로 풀어 보기 (0) | 2021.06.17 |