본문 바로가기
Study/혼자 공부하는 머신러닝 + 딥러닝

[혼자 공부하는 머신러닝 + 딥러닝] 확률적 경사 하강법

by kimdapadata 2024. 10. 28.

 

만약, 훈련 데이터가 한 번에 준비되는 것이 아니고 조금씩 전달된다면 어떡해야 할까요?

데이터가 쌓일 때까지 기다려야 할까요?

또는 기존의 훈련 데이터에 새로운 데이터를 추가하여 모델을 매일매일 다시 훈련할까요?

아니면 새로운 데이터를 추가할 때 이전 데이터를 버림으로써 훈련 데이터 크기를 일정하게 유지시키며 모델을 훈련 시킬까요?

 

 

이런 경우에는 앞서 훈련한 모델을 버리지 않고 새로운 데이터에 대해서만 조금씩 더 훈련하는 방법인
점진적 학습 또는 온라인 학습을 사용하는게 좋습니다. 

그 중 대표적인 점진적 학습 알고리즘인 확률적 경사 하강법을 배워볼 것입니다.

 

 

 

확률적 경사 하강법이란?


확률적이라는 말은 '무작위하게' 혹은 '랜덤하게'의 기술적인 표현입니다.

경사는 '기울기'를 의미하고 하강법은 '내려가는 방법'을 의미합니다.

즉, 경사 하강법은 '경사를 따라 내려가는 방법'을 의미합니다.

 

 

예로 들어, 산에서 내려온다고 가정 했을 때 집으로 돌아가려면 등산로 입구까지 내려가야 됩니다.

이 때 가장 빠르게 가는 길(경사가 가파른 길)을 선택하는 것이 좋겠죠?

이처럼 가장 가파른 경사를 따라 원하는 지점에 도달하는 것이 경사 하강법의 목표입니다.

 

 

하지만 주의해야 할 점이 있습니다.

바로 경사를 내려올 때 천천히 내려오는 것입니다.

왜냐면 내려오는 길에 나뭇잎 속에 가려진 웅덩이가 있을지도 모르기 때문입니다.

이처럼 내려오는 과정은 경사 하강법 모델을 훈련하는 것입니다.

 

 

아까 위에서 얘기했던 '확률적'이라는 말을 다시 한번 더 얘기하겠습니다.

경사 하강법도 마찬가지로 훈련 세트를 사용해 모델을 훈련하여 가장 가파른 길을 찾을 것입니다.

단, 딱 하나의 샘플을 훈련 세트에서 랜덤하게 골라 가장 가파른 길을 찾습니다.

좀 더 자세하게 얘기하면 훈련 세트에서 랜덤한 하나의 샘플을 선택하여 경사를 조금 내려갑니다.

그리고 랜덤하게 또 다른 샘플을 선택하여 경사를 내려갑니다.
이런 식으로 전체 샘플을 다 사용할 때까지 진행하는 것이 바로 확률적 경사 하강법입니다.

 

 

만약 전체 샘플을 다 사용했지만 내려오지 못했다면 어떻게 될까요?

훈련 세트에 다시 모든 샘플을 채워 넣어 반복합니다.

이렇게 훈련 세트를 한 번 모두 사용하는 과정을 에포크(Epoch)라고 부릅니다.

 

 

그리고 랜덤하게 1개씩 꺼내는 방법 외에
무작위로 몇 개의 샘플을 선택해서 경사를 따라 내려가는 미니배치 경사 하강법,
모든 샘플을 꺼내 사용하는 배치 경사 하강법도 존재합니다.

 

 

그럼 가장 빠른 길을 찾아 내려가려고 하는 이 산은 도대체 무엇일까요?

바로 손실 함수라는 산입니다.

 

 

 

손실 함수(Loss function)란?


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

높을수록 매우 엉터리가 되니 손실 함수의 값이 작을수록 좋습니다.

하지만 이 값의 최솟값은 알지 못합니다.

그렇기에 가능한 많이 찾아보고 만족할만한 수준을 찾아야 합니다.

 

 

분류에서 손실은 "정답을 못맞힌다"입니다.

예시로 4개의 예측 중에 2개의 예측만 맞았다면 정확도는 1/2 = 0.5입니다.

만약, 정확도를 손실 함수로 사용한다면 괜찮을까요?

이렇게 된다면 정확도는 0, 0.25, 0.5, 0.75, 1 다섯 가지뿐입니다.

이렇게 되면 간격이 커서 조금씩 움직일 수 없는 단점이 있습니다.

그렇기에 손실 함수는 연속적이여야 합니다.

그러면 연속적인 손실 함수의 예를 알아보겠습니다.

 

 

 

로지스틱 손실 함수(Logistic loss function)


예측은 0 또는 1이지만 확률은 0~1 사이의 어떤 값도 될 수 있습니다. 즉 연속적입니다.

그러면 확률로 어떻게 손실 함수를 만드는지 확인해보겠습니다.

 

 

샘플 4개의 예측 확률을 각각 0.9(1), 0.3(1), 0.2(0), 0.8(0)이라고 가정해 보겠습니다.

첫 번째 샘플의 예측은 0.9에 양성 클래스의 타깃인 1과 곱한 다음 음수로 바꿀 수 있습니다.

0.9 * 1 -> -0.9

예측이 1에 가까울수록 예측과 타깃의 곱의 음수는 점점 작아지겠죠?

 

 

두 번째 샘플의 예측은 0.3에 양성 클래스 타깃인 1과 곱한 다음 음수로 바꿔줍니다.

0.9 * 1 -> -0.9

0.3 * 1 -> -0.3

 

이 값은 첫 번째 샘플보다 높은 손실이 됩니다.

 

 

세 번째 샘플의 예측을 보겠습니다. 이 샘플의 타깃은 음성 클래스가 0입니다.
이 값을 예측 확률인 0.2와 곱하면 0이 되니, 타깃을 양성 클래스로 바꾸어 1로 만든 후 1에서 다시 뺍니다.

그리고 음수로 바꾸어 주면 됩니다.

0.9 * 1 -> -0.9

0.3 * 1 -> -0.3

 

(1 - 0.2) -> 0.8 * 1 -> - 0.8

 

세 번째 샘플은 타깃값(0)을 맞췄기 때문에 손실이 낮아야 합니다. 

 

네 번째 샘플도 마저 보겠습니다.

0.9 * 1 -> -0.9

0.3 * 1 -> -0.3

 

(1 - 0.2) -> 0.8 * 1 -> - 0.8

 

(1 - 0.8) -> 0.2 * 1 -> -0.2

 

 

예측 확률을 사용해 이런 방식으로 계산하면 연속적인 손실 함수를 얻을 수 있습니다.

여기에서 예측 확률에 로그 함수를 적용하면 더 좋습니다.

예측 확률의 범위는 0~1 사이인데 로그 함수는 이 사이에서 음수가 되어 최종 손실 값은 양수가 됩니다.(이해하기 좀 더 편해짐)

그리고 로그 함수는 0에 가까울수록 아주 큰 음수가 되기 때문에 손실을 아주 크게 만들어 모델에 큰 영향을 미칠 수 있습니다.

 

즉, 정리하면 다음과 같습니다.

양성 클래스일 때 -log(예측 확률)

음성 클래스일 때 -log(1 - 예측확률)

 

이 손실 함수를 로지스틱 손실 함수(Logistic loss function)이라고 부릅니다.

또는 이진 크로스엔트로피 손실 함수(Binary cross-entropy loss function)이라고도 부릅니다.

 

손실 함수까지 설명이 끝났으니 이제 확률적 경사 하강법을 사용한 분류 모델을 만들어 보겠습니다.

 

 

 

SGDClassifier


데이터는 로지스틱 회귀처럼 fish_data입니다.

 

 

 

Species 열을 제외한 나머지 5개는 입력 데이터로 사용하고 Species 열은 타깃 데이터로 사용합니다.

 

 

 

그 다음 사이킷런의 train_test_split() 함수를 사용해 이 데이터를 훈련 세트와 테스트 세트로 나눕니다.

 

 

 

이제 훈련 세트와 테스트 세트의 특성을 표준화 전처리 합니다.

 

 

 

이제 사이킷런에서 확률적 경사 하강법을 제공하는 대표적인 분류용 클래스인 SGDClassifier를 사용해보겠습니다.

SGDClassifier의 객체를 만들 때 2개의 매개변수를 지정하는데, 바로 loss와 max_iter입니다.

loss는 손실 함수의 종류를 지정하는데 여기에서는 loss = 'log_loss'로 지정하여 로지스틱 손실 함수를 지정하겠습니다.

그리고 max_iter는 수행할 에포크 횟수를 지정하는데 여기에서는 10으로 지정하여 전체 훈련 세트를 10회 반복하겠습니다.

 

출력된 훈련 세트와 테스트 세트 정확도가 낮은 걸 알 수 있습니다. 

 

 

 

앞서 이야기한 것처럼 확률적 경사 하강법은 점진적 학습이 가능합니다.

여기서 다시 한번 더 훈련된 모델 sc를 추가로 더 훈련시켜보겠습니다.

partial_fit() 메서드는 모델을 이어서 훈련할 때 쓰이는 메서드다.

 

점수가 낮지만 정확도가 향상된 모습을 볼 수 있습니다.

그렇다면 매우 많은 에포크에서 훈련을 시키면 좋은걸까요?

밑에서 알아보겠습니다.

 

 

에포크와 과대/과소적합


에포크 횟수가 적으면 모델이 훈련 세트를 덜 학습하고

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

 

바꾸어 말하면

적은 에포크 횟수 동안에 훈련한 모델은 훈련 세트와 테스트 세트에 잘 맞지 않는 과소적합된 모델일 가능성이 높습니다.

반대로 많은 에포크 횟수 동안에 훈련한 모델은 테스트 세트에서 오히려 점수가 나쁜 과대적합한 모델일 가능성이 높습니다.

 

훈련 세트 점수는 에포크가 진행될수록 꾸준히 증가하지만 테스트 세트 점수는 어느 순간 감소합니다.

이 곳이 바로 과대적합이 시작되는 곳인데, 시작하기 전에 훈련을 멈추는 것을 조기 종료(Early Stopping)이라고 합니다.

 

 

한번 이와 비슷한 그래프를 그려보겠습니다.

우선 partial_fit() 메서드만 사용하고, 전체 클래스의 레이블을 np.unique()로 구해줍니다.

그 후 에포크마다 훈련 세트와 테스트 세트에 대한 점수를 저장할 수 있게끔 만들어 줍니다.

 

 

 

300번의 에포크 동안 기록된 훈련 세트와 테스트 세트의 점수를 matplotlib을 사용해서 그래프를 그려 보겠습니다.

 

잘 보이지는 않지만 100번째 에포크 이전에는 훈련 세트와 테스트 세트의 점수가 낮고,
100번째 에포크 이후에는 훈련 세트와 테스트 세트의 점수가 조금씩 벌어지고 있습니다.

이 그래프를 보았을 때 적절한 반복 횟수는 100번째 에포크로 보입니다.

 

 

 

그럼 SGDClassifier의 반복 횟수를 100에 맞추고 모델을 다시 훈련해 보겠습니다.

 

SGDClassifier는 일정 에포크 동안 성능이 향상되지 않으면 더 훈련하지 않고 자동으로 멈춥니다.

tol = None으로 지정하여 자동으로 멈추지 않고 max_iter = 100만큼 무조건 반복하도록 합니다.

 

이로써 최종 점수는 비교적 높은 점수를 얻었습니다.

 

요즘엔 대량의 데이터를 이용해 문제를 해결해야 하는 일이 흔한데 이런 문제를 해결 할 때 쓰이는 방법이
바로 확률적 경사 하강법입니다.

 

 

출처 : 혼자 공부하는 머신러닝 + 딥러닝

https://product.kyobobook.co.kr/detail/S000001810330

 

다음 장에서는 트리 알고리즘에 대해서 알아보겠습니다.