앞의 데이터와는 달리 만약 훈련 데이터들이 준비되어 있지 않고,

시간에 걸쳐 조금씩 추가가된다면 어떻게 학습을 시켜야할까?

 

기존에 훈련한 모델을 버리지 않고 새로운 데이터에 대해서만 조금씩 훈련시킬 수는 없을까?

점진적 학습을 통해 이를 해결할 수 있다.

대표적인 점진적 학습법은 확률적 경사 하강법이라고 한다. (stochastic Gradient Descent)

 

확률적 경사 하강법에서 확률적이다 라는 말은 '무작위하게' 혹은 '랜덤하게'의 기술적인 표현이다.

경사 = 기울기

하강법 = 내려가는 방법

 

경사하강법은 기본적으로 가장 빠르게 내려가는 방법을 고수한다. (산에서 내려오는것을 생각)

하지만 만약 발걸음이 너무 크다면 경사를 따라 내려가는게 아니라 오히려 올라갈 수 도있다.

 경사하강법은 훈련 세트를 사용해 훈련할 때, 전체 샘플을 사용하지 않고, 딱 하나의 샘플을 훈련 세트에서 랜덤하게 골라 가장 가파른 길을 찾는다. 이처럼 훈련 세트에서 랜덤하게 하나의 샘플을 고르는것이 바로 률적 경사 하강법이라고 한다.

 < 훈련세트에서 랜덤하게 하나의 샘플을 택해 조금 내려간 후 다음 샘플 선택해서 내려가고, 다음,..다음...이런방식 >

 이렇게 모든 샘플을 사용할때 까지 반복한다.

 

하지만 만약 그래도 산을 다 내려오지 못한다면? 

다시 처음부터! 훈련세트에 모든 샘플을 다시 채워넣은 후 다시 랜덤하게 하나의 샘플을 선택해 이어서 경사를 내려간다.

이렇게 만족할 때 까지 계속 내려가면 된다.

확률적 경사 하강법에서 훈련 세트를 한 번 모두 사용하는 과정을 에포크라고 부른다.

일반적으로 경사하강법은 수십,수 백번의 에포크를 수행한다고 한다..

 

경사하강법은 무작위 샘플을 선택해서 산에서 내려가는건데 너무 무책임한거 아니야??라고 생각할 수 있다.

원래는 걱정하는것과 달리 이 알고리즘은 매우 정상적으로 작동하지만, 그래도 걱정이 된다면

1개씩 말고 몇 개씩 샘플을 선택해서 따라 내려갈 수 있다. 이를

미니배치 경사 하강법이라고 부른다. 이 방법또한 실전에서 많이 쓰인다.

 

그 밖에도 배치 경사 하강법이라는 방법도 있는데, 이는 한 번 경사를 따라 이동하는데에 모든 샘플을 사용한다.

이 방법이 가장 안전,정확하지만 그만큼 전체데이터를 사용하기 때문에 시간,자원이 많이 소모된다.

 

이 확률적 경사 하강법을 통해 매일매일 데이터가 업데이트 되어도 학습을 계속 이어나갈 수 있을것이다!

 

그런데 문제가 있다. 어디서 내려가야하는걸까? + 산은 도대체 무엇인걸까?? 이 산은 손실함수라고 부른다.

 

손실 함수 ( loss function )

손실함수란 어떤 문제에서 머신러닝 알고리즘이 얼마나 엉터리인지를 측정하는 기준이다.

그렇다면 손실함수의 값이 작을수록 좋다. 다행히도 우리가 다루는 많은 문제에 필요한 손실함수는 이미 정의가 되어있다.

 

이제 더 자세히 알아보도록 하자.

분류에서 손실은 확실하다. 못맞추는것 !

 

그럼 이를 어떻게 표현할 수 있지 않을까?

만약 4번의 예측 중에 두 번만 맞췄다면 정확도는 1/2 ( 0.5 ) 이다.

정확도를 손실함수로 사용한다면? 음수를 취해 -1이 가장 낮고, -0이 가장 높다. 이렇게 정확도를 손실함수로 사용할 수 있다.

하지만 정확도에는 치명적 단점이 있는데, 앞의 4개의 샘플만 있다면 가능한 정확도는 0, 0.25, 0.5 , 0.75, 1 다섯가지 뿐이다. 이는 경사하강법에서 연속적이지 않고 듬성듬성하게 내려오면 부적절할 수 있다.

 

그렇다면 손실함수를 연속적으로 만들려면 앞의 로지스틱과 연관지어보면된다.

 

로지스틱 손실 함수

우선 예시로 샘플 4개의 예측 확률을 각각 0.9, 0.3, 0.2, 0.8 이라고 가정해보자.

그리고 첫번째 두번째의 타겟은 1,   세 번째, 네 번째의 타겟은 0 이다. (불리안)

첫 번째 샘플의 예측은 0.9이므로 양성 클래스의 타깃인 1과 곱한다음 음수로 바꿀 수 있다. 이 경우 예측이 1에 가까울 수록 좋은 모델이라고 할 수 있다. 반대로 예측이 1에 가까울수록 음수는 점점 작아진다. 이 값을 손실함수로 사용해보자.

 

두 번째 샘플의 예측은 0.3이다. 거리가 멀다. 첫 번째 샘플처럼  - (예측 x 타겟)을 해보자. -0.3이다. 첫 번째보다 높은 손실.

 

세 번째 샘플에서 타겟 0을 곱하면 무조건 0이되기 때문에 문제가 생긴다.

★★★따라서 타겟을 양성클래스와 같이 1로 바꿔준다. 대신에 예측값도 양성클래스에 대한 예측으로 바꿔준다!!! ★★★

그렇다면 1-0.2 = - (0.8 x 1) = -0.8이다. 꽤 낮은 손실이다.

 

네 번째 샘플또한 세번째처럼 양성처럼 바꿔준다. -0.2이다. 높은 손실이다.

1,3번째 샘플 = 낮은 손실, 2,4번째 샘플  = 높은 손실

 

 

예측 확률을 사용해 이런 방식으로 계산한다면 연속적인 손실 함수를 얻을 수 있을 것 같다. 여기에서 예측 확률에 로그함수를 적용한다면 더욱 보기 좋을 수 있다.

로그함수의 특성 ( 0에 가까울 수록 큰 음수가 되어 손실을 크게 만들어줆.)

위와 같은  손실 함수를 로지스틱 손실 함수 또는 <이진or다중>크로스엔트로피 손실 함수라고 부른다.

 

이제 손실함수까지 알아봤으니, 경사 하강법을 이용한 분류 모델을 보자.

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)

 

SGD Classifier

★SGD Classifier클래스는 사이킷런에서 확률적 경사 하강법을 제공하는 대표적인 분류용 클래스이다. !!!!★

from sklern.linear_model import SGDClassifier

SGDClassifier는 두개의 매개변수가 필요하다. loss= 와 max_iter= 이다.

loss=는 손실함수의 종류, max_iter = 은 에포크 횟수

여기서는 loss='log', max_iter = 10으로 해보자.

sc = SGDClassifier(loss='log', max_iter=10 , random_state=42)
sc.fit(train_scaled,train_target)

sc.score(train_scaled, train_target)
sc.score(test_scaled, test_target)

0.773109243697479
0.775

0.7로 정확도가 그리 높지 않은것을 볼 수 있다.

아마도 에포크가 부족한 것일 수도 있다.

 

SGDClassifier의 장점?특성에 따라 다시 만들 필요없이 추가 훈련을 해보자.

모델 추가 훈련할 때에는 partial_fit() 메소드를 사용하면 된다.

 

이 메소드는 fit()과 같지만 호출할 때마다 에포크를 1씩 이어서 훈련할 수 있다는 특징이 있다.

partial_fit() 메소드를 호출하고 다시 훈련세트와 테스트세트의 점수를 확인해보자.

 

sc.partial_fit(train_scaled, train_target)
sc.score(train_scaled, train_target)
sc.score(test_scaled, test_target)

0.8151260504201681
0.85

오 더 올라감. 그런데 얼마나 올려야할까????? 기준이 필요하다.

 

에포크의 과대/과소 적합

확률적 경사 하강법을 사용한 모델은 에포크 횟수에 따라 과소적합/과대적합이 될 수 있다.

에포크 횟수가 적으면 모델이 훈련 세트를 덜 학습한다. 마치 산을 다 내려오지 못하고 훈련이 끝난 상황.

에포크 횟수가 많으면 훈련 세트를 완전히 학습할 것이다.

바꿔 말하면 적은 에포크 횟수 동안 훈련한 모델은 훈련/테스트 세트에 잘 맞지 않는 과소적합된 모델일 것이고,  많은 에포크 횟수 동안 훈련한 모델은 훈련세트에 잘맞아 테스트세트에는 점수가 나쁜 과대적합된 모델일 가능성이 높다.

이를 그림으로 표현하면

 

이 그래프는 에포크가 진행됨에 따라 모델의 정확도를 나타낸 것이다. 훈련 세트 점수는 에포크가 진행될수록 꾸준히 증가하지만 테스트 세트 점수는 어느 순간 감소하기 시작한다. 이 지점이 바로 모델이 과대적합되기 시작하는 지점이다.

과대적합이 시작하기전에 훈련을 멈추는것을 조기종료라고 한다.

 

우리가 가진 데이터으로 위와 같은 그래프를 만들어보자.

이제 fit()이 아닌 partial_fit()만 사용할건데, 이를 위해서는 훈련세트에 있는 전체 클래스의 레이블을 partial_fit()메소드에 전달해줘야 한다. 이를 위해 np.unique()함수로 train_target 에 있는 7개 생선 목록을 만들고, 에포크마다 훈련세트와 테스트 세트의 점수를 보기 위해 2개의 리스트를 준비해보자.

 

import numpy as np
sc = SGDClassifier(loss='log', random_state= 42)
train_score = []
test_score = []
classes = np.unique(train_target)		# unique() = 오름차순으로 고유값 정렬시킴(중복제거)

300번의 에포크를 해보자.

for _ in range(0,300):
	sc.partial_fit(train_scaled, train_target, classes=classes)
    train_score.append(sc.score(train_scaled,train_target))
    train_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에 맞춰볼까?

sc = SGDClassifier(loss='log', 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 매개변수에서 향상될 최솟값을 지정한다. 앞의 코드에서는 tol 매개변수를 None으로 지정하여 자동으로 멈추지 않고 max_iter = 100만큼 무조건 반복하도록 하였다.

 

+ Recent posts