본문 바로가기

KHUDA

KHUDA ML세션 3주차

chapter 4-1 
로지스틱 회귀 

럭키백의 확률 구하기 -> k-최근접 이웃 알고리즘 사용 

데이터 준비하기 
import pandas as pd

fish = pd.read_csv('https://bit.ly/fish_csv_data')
fish.head()

print(pd.unique(fish['Species']))

['Bream' 'Roach' 'Whitefish' 'Parkki' 'Perch' 'Pike' 'Smelt']

Species 열 빼고 리스트 나열 
fish_input = fish[['Weight','Length','Diagonal','Height','Width']].to_numpy()
print(fish_input[:5]

[[242. 25.4 30. 11.52 4.02 ] [290. 26.3 31.2 12.48 4.3056] [340. 26.5 31.1 12.3778 4.6961] [363. 29. 33.5 12.73 4.4555] [430. 29. 34. 12.444 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-최근접 이웃 분류기의 확률 예측
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

다중분류
타깃 데이터에 2개 이상의 클래스가 포함된 문제를 다중 분류라고 한다. 
print(kn.classes_)

['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish'

bream이 첫번째, parkki가 두번째 클래스가 되는 셈 

타깃데이터 예측하기
print(kn.predict(test_scaled[:5]))

['Perch' 'Smelt' 'Pike' 'Perch' 'Perch'

predict_proba() 메서드를 활용해 클래스 별 확률값을 반환

import numpy as np

proba = kn.predict_proba(test_scaled[:5])
print(np.round(proba, decimals=4)) #소숫점 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.    ]]
 
결괏값 해석 : 첫번째 열이 Bream에 대한 확률, 두번째 열이 Parkki에 대한 확률 

distances, indexes = kn.kneighbors(test_scaled[3:4])
print(train_target[indexes])

kneighbor() 메서드의 입력은 2차원 배열이어야 한다. 이를 위해 넘파이 배열의 슬라이싱 연산자를 활용

슬라이싱 연산자는 하나의 샘플만 사용해도 항상 2차원 배열이 만들어진다.

[['Roach' 'Perch' 'Perch']]

roach가 1개이고, perch가 2개이므로 세 번째 클래스의 확률은 0.3333, 다섯 번째 클래스의 대한 확률은 0.6777

 

그렇게 된다면 럭키백의 확률은 0/3,1/3,2/3,3/3 밖에 안나옴

 

로지스틱 회귀(logistic regression)
이 알고리즘은 선형 회귀와 동일하게 선형 방정식을 학습합니다.
시그모이드 함수(sigmoid function)
z가 아주 큰 음수일때 0 ,아주 큰 양수일때 1로 하는 시그모이드 함수
세타는 절대로 0~1사이의 범위를 벗어날 수 없기때문에 0~100%확률로 해석할 수 있다.  

 

로지스틱 회귀 모델 클래스 : LogisticRegression()

이진 분류

시그모이드 함수의 출력이 0.5보다 크면 양성 클래스, 0.5보다 작으면 음성 클래스, 정확히 0.5일때는 음성클래스로 파악

로지스틱 회귀로 이진 분류 수행하기 
불리언 인덱싱(boolean indexing)
True, False 값을 전달하여 분류 
ex) A~E까지 이루어진 배열에서 A와 C만 골라내려면 첫번재와 세번째 원소에만 True, 나머지는 False를 전달하면 된다.2
['A' 'C']
도미와 방어 골라내기 
 
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]))
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.classes_)

['Bream' 'Smelt']

 

계수 확인하기 
 
[[-0.4037798  -0.57620209 -0.66280298 -1.01290277 -0.73168947]] [-2.16155132]

z값 확인하기 
# train_bream_smelt의 처음 5개 샘플 
decisions = lr.decision_function(train_bream_smelt[:5]) 
print(decisions)

[-6.02927744 3.57123907 -5.26568906 -4.24321775 -6.0607117 ]

expit 메서드를 이용하여 확률로 변환 

from scipy.special import expit

print(expit(decisions))
로지스틱 회귀로 다중 분류 수행하기 

LogisticRegression 클래스는 반복 알고리즘 사용

반복 횟수 매개변수 max_iter의 기본 값 = 100 훈련하면서 증량

L2 규제 : 계수의 제곱 규제

규제 매개변수 C

0.9327731092436975

0.925

print(lr.predict(test_scaled[:5]))

['Perch' 'Smelt' 'Pike' 'Roach' 'Perch']

샘플 예측 출력
 

 

[[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개의 특성을 사용하므로 coef_배열의 열은 5개이다.

행이 7개니 intercept_도 7이다. z값을 7번 계산해야한다.

다중분류는 이진 분류와 달리 시그모이드 함수가 아닌 소프트맥스 함수를 사용한다. 

소프트맥스 함수(softmax function)
여러 개의 선형 방정식의 출력값을 0~1사이로 압축하고, 전체 합이 1이 되도록 만든다. 
정규화된 지수 함수라고도 한다

처음 샘플에 대한 z1~7의 값 구하기 
[[ -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]]
소프트 맥스 함수로 전달
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]]

계산 축을 지정하는 매개변수 axis의 값을 1로 지정하여 계산

axis매개변수를 지정하지 않으면 배열 전체에 대한 소프트 맥스를 계산한다. 

 

chapter 4-2 확률적 경사 하강법

확률적 경사 하강법(Stochastic Gradient Descent)
대표적인 점진적 학습 알고리즘 중 하나
하나의 샘플을 훈련 세트에서 랜덤하게 골라 가장 가파른 길을 찾는 것 
따라서 훈련세트에서 랜덤하게 하나의 샘플을 선택해 가파른 경사를 조금 내려가고, 또 다른 샘플을 통해 조금씩 내려가 전체 샘플을 다 사용하여 목적에 도달하는 것 
모든 샘플을 사용해도 못내려왔을 시에 처음으로 돌아가 다시 반복 
앞서 훈련한 모델을 버리지 않고 새로운 데이터에 대해서만 조금씩 학습 = 점진적 학습 
에포크(epoch)
확률적 경사 하강법에서 훈련 세트를 한 번 모두 사용하는 과정
미니배치 경사 하강법(minibatch gradient descent)
한 개가 아닌 여러개의 샘플을 사용해 경사하강법을 수행하는 방식
배치 경사 하강법(batch greaient descent)
극단적으로 한번 내려갈때 전체 샘플을 사용하는 것
전체 데이터를 활용하기 때문에 가장 안정적이다.
전체 데이터를 사용하기 때문에 컴퓨터 자원을 많이 사용하게 된다  

이런 과정 덕분에 데이터가 매일매일 업데이트 되어도 학습을 계속 해나갈 수 있다. 

손실 함수(loss fuction)
머신러닝이 얼마나 엉터리 인지 측정하는 기준으로 손실 함수의 값이 작아야 학습이 잘된 것이다
하지만 어떤 값이 최솟값인지는 알 수 없다. 
가능한 많이 찾아보고 만족할 만한 수준일때 경사를 다 내려왔다고 인정을 해야한다. 

정확도는 손실 함수로 적절하지 않는다

4개의 샘플만 있을때 정확도는 0.0, 0.25, 0.75, 1 다섯까지 뿐이다. 

정확도가 이렇게 듬성듬성하면 경사하강법을 통해 조금씩 움질일 수 없다 

따라서 손실함수는 미분 가능해야 한다 

 

로지스틱 손실 함수(logistict loss function)
0~1사이의 값을 출력하므로 연속적이다
  • 양성클래스(1)일때 => 예측 X 정답(타깃) 후 음수를 붙여 출력
  • 음성클래스(0)일때 => (1-예측) X 정답(타깃) 후 음수를 붙여 출력

양수가 되기 위해 로그함수 사용 

예측확률이 높다 1가 가까워 짐  손실이 작아진다 
예측확률이 낮다 0과 가까워 짐  손실이 커진다 
경사 하강법을 이용한 분류 모델 제작 
import pandas as pd

fish = pd.read_csv('https://bit.ly/fish_csv_data')
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)
경사하강법 클래스 호출
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))

2개의 매개변수 지정

loss 손실 함수 지정
max_iter 에포크 수 지정 

0.773109243697479

0.775

지정횟수 부족으로 정확도가 낮다.

CovergenceWarning 경고가뜨면 max_iter 매개변수의 값을 늘려주는 것이 좋다  

 

모델 이어서 훈련하기

경사하강법은 점진적 학습이 가능하다 

훈련한 모델 sc를 추가로 더 훈련 

훈련 메서드는 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.8151260504201681

0.85

 

에포크의 과대 과소 적합

에포크의 횟수에 따라 과소적합이나 과대적합이 될 수 있다. 

횟수가 적으면 덜 학습하고, 많으면 충분히 학습한다 

적은 에포크 수는 훈련세트와 테스트 세트에 잘 맞지 않은 과소적합된 모델일 가능성이 높다
많은 에포크 수는 훈련세트에는 너무 잘 맞지만 테스트 세트에는 오히려 점수가 나쁜 과대적합된 모델일 가능성이 높다.

조기 종료(early stopping)
훈련세트 에포크는 계속 증가하지만 
테스트 세트 에포크는 어느 순간 감소한다 
이때 학습을 멈춰 과대 적합을 방지한다 
import numpy as np

sc = SGDClassifier(loss='log_loss', random_state=42)

train_score = []
test_score = []

classes = np.unique(train_target)
300번의 에포크 동안 훈련 
for _ in range(0, 300):
    sc.partial_fit(train_scaled, train_target, classes=classes)

    train_score.append(sc.score(train_scaled, train_target))
    test_score.append(sc.score(test_scaled, test_target)) 
  • _ 변수는 파이썬의 특별한 변수! 그냥 버리는 값을 넣어두는 용도로 사용한다

100번째 에포크 이후 점수가 조금씩 벌어진다

 

import matplotlib.pyplot as plt

plt.plot(train_score)
plt.plot(test_score)
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.show()
에포크 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  

 

 

 

 

'KHUDA' 카테고리의 다른 글

KHUDA Data buisness 01  (0) 2024.03.13
KHUDA ML 세션 5주차  (1) 2024.02.27
KHUDA ML 세션 4주차  (1) 2024.02.20
KHUDA ML세션 2주차  (1) 2024.02.05
KHUDA ML세션 1주차  (1) 2024.01.31