이 포스팅은 혼자 공부하는 머신러닝 + 딥러닝을 공부하고 정리한 것 입니다
만약 데이터가 한 번에 준비되는 것이 아니라 조금씩 전달되는 상황이면 어떨까? 기존의 훈련 데이터에 새로운 데이터를 추가하여 모델을 매일매일 다시 훈련하면 될까? 이렇게 하면 매일 추가되는 새로운 데이터를 활용해 모델을 훈련할 수 있다. 그러나 시간이 지날수록 데이터가 늘어난다는 단점이 있다.
또 다른 방법은 새로운 데이터를 추가할 때 이전 데이터를 버림으로써 훈련 데이터 크기를 일정하게 유지하는 것이다. 이렇게 하면 데이터셋의 크기가 너무 커지지 않을 수 있다. 하지만 데이터를 버릴 때 다른 데이터에 없는 중요한 생선 데이터가 포함되어 있다면 큰일이다. 앞으로 모델이 그 생선을 예측하지 못할 것이기 때문이다.
위에 언급한 방법은 이전에 훈련한 모델을 버리고 다시 새로운 모델을 훈련하는 방식이다. 앞서 훈련한 모델을 버리지 않고 새로운 데이터에 대해서만 조금씩 더 훈련할 수 없을까? 이렇게 할 수 있다면 훈련에 사용한 데이터를 모두 유지할 필요도 없고 앞서 학습한 생선을 까먹을 일도 없을 것이다.
이런 식의 훈련 방식을 점진적 학습 또는 온라인 학습 이라고 부른다. 대표적인 점진적 학습 알고리즘은 확률적 경사 하강법 (stochastic Gradient Descent) 이다. 물론 사이킷런에서도 확률적 경사 하강법을 위한 클래스를 제공한다.
확률적 경사 하강법
확률적 이란 말은 무작위하게 혹은 랜덤하게의 기술적 표현이다. 경사 하강법은 경사를 따라 내려가는 방법을 말한다. 산에서 내려온다고 생각해보면 집으로 돌아가려면 등산로 입구까지 내려가야 한다. 가장 빠른 길은 경사가 가장 가파른 길이다.
경사 하강법이 바로 이런 방식이다. 가장 가파른 경사를 따라 원하는 지접에 도달하는 것이 목표이다. 경사 하강법에서 가장 가파른 길을 찾아 내려오지만 조금씩 내려오는 것이 중요하다. 이렇게 내려오는 과정이 바로 경사 하강법 모델을 훈련하는 것이다.
그럼 여기서 확률적이라는 말을 통해 경사 하강법으로 내려올때 가장 가파른 길을 찾는 방법은 무엇일까? 훈련 세트를 사용해 모델을 훈련하기 때문에 경사 하강법도 당연히 훈련 세트를 사용하여 가장 가파른 길을 찾을 것이다. 그런데 전체 샘플을 사용하지 않고 딱 하나의 샘플을 훈련 세트에서 랜덤하기 골라 가장 가파른 길을 찾는다. 이처럼 훈련 세트에서 랜덤하게 하나의 샘플을 고르는 것이 바로 확률적 경사 하강법 이다.
모든 샘플을 다 사용했는데 산을 다 내려오지 못했다면 다시 처음부터 시작하는 것이다. 이렇게 만족할 만한 위치에 도달할 때까지 계속 내려간다. 확률적 경사 하강법에서 훈련 세트를 한 번 모두 사용하는 과정을 에포크(epoch) 라고 부른다.
하나 말고 무작위로 몇 개의 샘플을 사용해 경사 하강법을 수행하는 방식을 미니배치 경사 하강법(minibatch gradient descent) 라고 한다. 전체 데이터를 사용하기 때문에 가장 안정적인 방법이 될 수 있지만 전체 데이터를 사용하면 그만큼 컴퓨터 자원을 많이 사용하게 된다.
결론적으로 확률적 경사 하강법은 훈련 세트를 사용해 산 아래에 있는 최적의 장소로 조금씩 이동하는 알고리즘이라고 생각할 수 있다. 이 때문에 훈련 데이터가 모두 준비되어 있지 않고 매일매일 업데이트 되어도 학습을 계속 이어나갈 수 있다. 다시 산꼭대기에서부터 시작할 필요가 없는것이다.
그런데 어디서 내려가야 하는걸까? 다시 말해 가장 빠른 길을 찾아 내려가려고 하는 이 산은 도대체 무엇일까? 이 산이 바로 손실 함수라 부르는 것이다.
손실 함수
손실 함수(loss function)는 어떤 문제에서 머신러닝 알고리즘이 얼마나 엉터리인지를 측정하는 기준이다. 어떤 값이 최솟값인지 모르기 때문에 가능한 많이 찾아보고 만족할만한 수준이라면 산을 다 내려왔다고 인정해야 한다.
손실 함수는 미분 가능해야 한다.
로지스틱 손실함수
양성 클래스(타킷=1) 일때 손실은 -log(예측 확률)로 계산한다. 확률이 1에서 멀어질수록 손실은 아주 큰 양수가 된다. 음성 클래스(타킷=0) 일 때 손실은 -log(1-예측 확률)로 계산 한다. 이 예측 확률이 0에서 멀어질수록 손실은 아주 큰 양수가 된다.
이 함수를 로지스틱 손실 함수(logistic loss function)이라고 부른다. 또는 이진 크로스엔트로피 손실함수(binary corss-entropy loss function)라고도 부른다. 다중 분류에서 사용하는 손실 함수를 크로스엔트로피 손실함수(cross-entropy loss function)라고 부른다/
SGDClassifier
import pandas as pd from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler fish=pd.read_csv('https://bit.ly/fish_csv') fish_input=fish[['Weight','Length','Diagonal','Height','Width']].to_numpy() fish_target=fish['Species'].to_numpy() 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)
사이킷런에서 확률적 경사 하강법을 제공하는 대표적인 분류용 클래스는 SGDClassifier 이다.
from sklearn.linear_model import SGDClassifier
SGDClassifier의 객체를 만들 때 2개의 매개변수를 지정한다. loss는 손실 함수의 종류를 지정한다. 여기에서는 loss='log'로 지정하여 로지스틱 손실 함수를 지정했다. max_iter는 수행할 에포크 횟수를 지정한다.
sc=SGDClassifier(loss='log', 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))
앞서 이야기한 것처럼 확률적 경사 하강법은 점진적 학습이 가능하다. SGDClassifier 객체를 다시 만들지 않고 훈련한 모델 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))
아직 점수가 낮지만 에포크를 한 번 더 실행하니 정확도가 향상되었다. 이 모델을 여러 에포크에서 더 훈련해 볼 필요가 있을것 같다. 그런데 얼마나 더 훈련해야 할까? 무작정 많이 반복할 수는 없고 기준이 필요할 것이다.
에포크와 과대/과소적합
확률적 경사 하강법을 사용한 모델은 에포크 횟수에 따라 과소적합이나 과대적합이 될 수 있다.
에포크 횟수가 적으면 모델이 훈련 세트를 덜 학습하고 과소적합된 모델일 가능성이 크다. 반대로 많은 에포크 횟수동안에 훈련한 모델은 훈련 세트에 너무 잘 맞아 과대적합된 모델일 가능성이 높다.
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) 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))
300번의 에포크 동안 기록한 훈련 세트와 테스트 세트의 점수를 그래프로 그려 보겠습니다.
import matplotlib.pyplot as plt plt.plot(train_score) plt.plot(test_score) plt.show()
데이터가 작기 때문에 아주 잘 드러나지는 않지만, 백 번째 에포크 이후에는 훈련 세트와 테스트 세트의 점수가 조금씩 벌어지고 있다. 확실히 에포크 초기에는 과소적합되어 훈련 세트와 테스트 세트의 점수가 낮다. 이 모델의 경우 백 번째 에포크가 적절한 반복 횟수로 보인다.
그럼 SGDClassifier의 반복 횟수를 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))
SCDClassifier는 일정 에포크 동안 성능이 향상되지 않으면 더 훈련하지 않고 자동으로 멈춘다. tol매개변수에서 향상될 최솟값을 지정한다. 앞의 코드에서는 tol매개변수를 None으로 지정하여 자동으로 멈추지 않고 max_iter=100만큼 무조건 반복하도록 하였다.
마무리 하기 전에 SGDClassifier의 loss 매개변수를 보면 사실loss매개변수의 기본값은 'hinge'이다. 힌지 손질(hinge loss) 는 서포트 벡터 머신(support vector machine)이라 불리는 또 다른 머신러닝 알고리즘을 위한 손실 함수이다. 서포트 벡터 머신은 널리 사용하는 머신러닝 알고리즘 중 하나이고 SGDClassifier가 여러 종류의 손실 함수를 loss 매개변수에 지정하여 다양한 머신러닝 알고리즘을 지원한다.
간단한 예로 힌지 손실을 사용해 같은 반복 횟수 동안 모델을 훈련시켜 보겠다.
sc=SGDClassifier(loss='hinge',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))
결론
이번 시간에는 데이터를 실시간으로 학습하기 위해 확률적 경사 하강법을 사용해 점진적으로 학습하는 로지스틱 회귀 모델을 훈련했다. 확률적 경사 하강법은 손실 함수라는 산을 정의하고 가장 가파른 경사를 따라 조금씩 내려오는 알고리즘 이다. 충분히 반복하여 훈련하면 훈련 세트에서 높은 점수를 얻는 모델을 만들 수 있다.
지금까지 회귀와 분류에 널리 사용되는 다양한 알고리즘을 배웠다. 최근접 이웃, 선형 회귀, 릿지, 라쏘, 로지스틱 회귀, 확률적 경사 하강법 등을 배웠다. 이 알고리즘들은 실전에서 널리 사용되는 뛰어난 기법이지만 최고는 아니다. 이 다음장에서 신경망 알고리즘을 제외하고 머신러닝에서 가장 뛰어난 성능을 내는 알고리즘을 배울 것이다.
'Machine Learning > Basic' 카테고리의 다른 글
[5-2] 교차 검증과 그리드 서치 (0) | 2021.04.07 |
---|---|
[5-1] 결정 트리 (0) | 2021.04.07 |
[4-1] 로지스틱 회귀 (0) | 2021.04.06 |
[3-3] 특성 공학과 규제 (0) | 2021.04.06 |
[3-2] 선형 회귀 (0) | 2021.04.05 |