본문 바로가기

AI/머신러닝(코세라)

[ML] #8 머신러닝 실사용 꿀팁들(Advice for applying machine learning)

본 글은 학교 '머신러닝' 수업을 들으며 공부한 내용을, 저의 말로 바꿔서 남에게 설명해주듯이 쓰는 글입니다.
다시 한번 복습하는 과정에서 Coursera Andrew Ng 교수님의 강의를 일부 수강하였고, 인터넷 검색 등을 통해 내용을 보충하였습니다.
너무 쉬운 개념들은 따로 정리하지 않았습니다. 따라서 해당 글에는 적히지 않은 개념들이 일부 있을 수 있습니다.


본 글은 Andrew Ng 교수님의 'machine learning' 수업 강의 노트를 일부 사용하였습니다.
본 글에 사용된 강의노트 사진들 대부분의 저작권은 'DeepLearning.AI'에 귀속되어 있음을 밝히며,
본 글은 'DeepLearning.AI'의 'Copyright rights'에 따라 수익 창출을 하지 않고,
또한 해당 정책에 따라 '개인 공부 및 정보 전달'이라는 교육적 목적으로 글을 작성함을 밝힙니다.



Deciding what to try next

우리가 h 잘 정의하고 J 잘 정의해서 gradient descent든 뭐든 잘 써가지고 모델을 도출해냈음.

그리고, 이제 test set을 돌리는데, 수용하기 어려운 큰 에러들이 발생한거임...

 

그럼 이제 어뜨캄???

 

이 짤이 갑자기 기억나서...ㅋㅅㅋ

 

하나씩 설명해보겠다.

 

1. 트레이닝 예제를 더 모은다

뭐 기본적으론 많으면 좋긴함

2. 피쳐 개수를 줄인다

오버피팅 방지할 수 있음.

3. 피쳐를 추가한다(피쳐 개수를 늘린다)

내가 원래 쓰던 피쳐만으로는 좀 불충분할 수 있음. 예를 들어 집값 예측을 하는데, 뭐 면적 말고도 주변지역 물가나 소득 수준 이런 것도 좀 반영해볼 수 있지 않겠음??

4. 피쳐를 고차 poly 형태로 써본다

근데 너무 과하게 쓰면 오버피팅 생길수있다~~

5. 람다를 줄인다

6. 람다를 늘린다

리규화로 언더피팅이나 오버피팅 문제가 생긴 걸수도 있으니까~~

 

이 외에도, gradient descent를 사용했다면 learning rate alpha를 조절하거나, #iteration을 조절할 수도 있다.

 

근데... 대체 뭐가 문젤까...

데이터 셋이 부족한 것 같아서 데이터 셋만 주구장창 또 모아왔는데 해결이 안되면

그 다음은 피쳐를 더 늘려볼까??

이러면 모델은 언제 디벨롭하고 배포할건데..

 

그래서, 무작정 시도하기보단 일단 진단해서 뭐가 문젤지 생각해보고, 그 부분을 해결하는 게 효율적임.

이제 모델을 진단하고 평가해서 뭐가 문젤까 확인하는 방법을 알아볼 거임.

 


 

Evaluating a Learning Algorithm

 

현재 training이 끝나서 test를 돌렸는데, generalize에 실패한 상황이라고 해보자.

만약 우리 model이 간단해서 그리기 쉽다면, 위와 같이 직접 그래프를 그려서 과적합인지 아닌지 판단할 수 있음.

 

근데 피쳐가 개수가 실제론 엄청 많은데?? 식도 복잡하고... 그리기에는 한계가 있음.

 

그래서, 우리는 직접 그리는 게 아닌, test set을 따로 지정해서 테스트를 돌려볼 거임.

저 퍼센트는 뭐 정해진 건 아닌데, 보통 7:3으로 쓴다는 듯.

test set은 우리의 h가 다른 data set에도 잘 맞는지, 즉  well generalized 됐는지 체크함.

저기서 뭐를 어느 set으로 할 지는, 무작위가 젤 나음. 이미 무작위로 정렬된 상태면 그냥 앞에서부터 70% 따와도 되겠고.

 

주목)

1. 내가 수집한 data set이 많고 training set이 충분하다면 굳이 test set을 적게 잡을 필요는 없음. 많으면 좋은거여~~

2. test set은 절대로!!! training에 사용되면 안됨. 가끔 실수해서 training set에 포함시켜서 학습시켜버리면, 성능이 좋아지니까 좋아라 할 수 있지만 그건 나의 실수인거임!!!!

 

 

추가로, 내 data set을 저렇게 3개로 나누는 경우도 있음.

validation에 해당하는 data들로 test 하기 전에 hyperparameter 들을 결정하는 거임.

근데 저건 정말 이상적인 case고... 대부분 논문이든 뭐든 그냥 training set 과 test set 두 개로만 나눔.

 

 

위와 같이 나눌수도 있는데, 이걸 k-fold cross-validation 기법이라 부름.

요거는 머신러닝에서 많이 씀. 머신러닝에선 training set이 보통 1000개 이하고 그러다보니 overfitting을 주의해야 하는데, 그럴 때 5개로 validation 나눠서 평균내면 그게 좀 완화가 된다곤 함. (그렇대~~~)

근데 딥러닝은 training set이 엄청 많은 경우가 많아서 저렇게는 안한다는듯??

https://nonmeyet.tistory.com/entry/KFold-Cross-Validation%EA%B5%90%EC%B0%A8%EA%B2%80%EC%A6%9D-%EC%A0%95%EC%9D%98-%EB%B0%8F-%EC%84%A4%EB%AA%85

참고

 

이 부분 이해 잘 못했지만 뭐 일단 키워드만 알아두자.

 

 

선형 회귀의 경우, 일단 내가 training을 하면서 J_train을 최소화할거 아님. 그렇게 구한 J_train이 있을거고,

이제 그 h로 test set을 돌려서 J_test를 구할 수 있을거임.

만약 J_test가 J_train보다 많이 크다면, 성능이 그리 좋지는 않다고 판단할 수 있다.

예를 들어 오버피팅됐다고 하면 J_train은 엄청 작고 J_test는 그에 비해 많이 클 것이다.

아래는 test set error 이다.

 

로지스틱 회귀의 경우도 비슷하다. 

아래는 test set error 이다.

 

그리고, misclassification error은 위와 같이 정의 가능하다.

(이거 좀 이해 안가긴 했는데 일단 pass)

(아마... 좀 이상하긴 한데, test set error이랑 별도로 misclassification error을 따로 정의한 듯 일단.)

(넘어가자)

 

위에 말한 test error가 작을 수록 이 모델은 성능이 좋구나~~ 하고 평가할 수 있는거. (과적합 안되어 있다는 평가도 되고 ㅇㅇ)

 


Model selection and training/validation/test sets

우선, 우리가 training 과정에서 구한 J_train은 웬만해선 J_test보다 작을 것이다. 당연하지? overfitting까진 아니어도 ㅇㅇ.

만약, 내가 train할 때 쓴 data set이 test set에 포함되어 있다면, 실제 성능보다 test 성능이 높게, 즉 test error가 더 낮게 측정될 수 있음.

일단, 우리가 모델의 형태를 정할 때를 가정해보자. 우리는 poly 회귀를 사용해 h를 정의할 것이다.

 

어떤 모델을 선택하는게 가장 좋을까? 여기서 가장 좋다는 건, 가장 오류가 적다는 것이다.

 

우선, 각 d에 대해서 내가 training을 해서 얻은 파라미터 벡터 쎄타가 있을 거 아님. 그 쎄타로 각각 test set에 대해 J_test를 구함. 그럼 각각의 test set이 "값"으로 나올거고, 그 값이 가장 작은 d를 택하면 될것임.

 

근데... 예를 들어 d = 5일 때 J_test가 가장 작아서 선택했다 쳐보자. 그럼 이제 h를 평가할 건데, 이 h를 평가하는 데 내가 썼던 test set을 또 쓰면...? 아까 내가 d = 5 인 걸 구하기 위해 쓴 test set을 또 쓰면, overfitting이 발생해 재사용한 test set에만 적합하고, 또 다른 other test sets에는 적합하지 않을 수도 있음. 왜냐하면 d = 5라는 걸 test set으로 구했짜나!!!

 

정리하면, 우리의 파라미터 d = 5는 이 test set에만 fit할 수 있음.

그래서, 우리는 (하이퍼)파라미터 d = 5를 정하는 데 쓴 test set말고, 진짜 이 h를 평가하기 위한 test set을 따로 둬야 할거임.

 

그러니, 저렇게 cross-validation set을 둬서, 저기서 하이퍼파라미터를 정하고, 실제 평가는 test set을 가지고 하자!!

(여기는 안나왔지만, learning rate alpha도 이 과정에서 구할 수 있다는듯)

 

정리하면, 위와 같이 3가지의 data set에 대한 J 계산이 필요하다.

마지막 test error을 계산하는게 이 h(즉, 모델)를 평가하는 단계라고 생각하면 된다.

참고로, J랑 J_train은 다름!! J는 '비용 함수'고, 내가 학습시키는 과정에서 계속 업데이트해나가는 값임. 그리고 정규화 term이 포함되어 있음.

그러나 J_train은 정규화 term이 없음. 

J_train을 구할 때 J에서 정규화 term만 빼줄 수도 있으나, 일반적으로 머신러닝 라이브러리들은 그냥 minimum J를 구하면서 얻은 파라미터 쎄타가 포함된 모델로 training set을 돌려서 그 J값(물론 정규화 안한 값)을 J_train으로 하는 식으로 동작함. 우리도 그렇게 생각하면 될듯.

 


Diagnosing bias vs. variance

 

 

우리가 overfitting vs underfitting 배우면서 다뤘던 위 예제를 생각해보자.

이 때, 우리는 d값, 즉 다항식의 차수에 따라 error가 달라질 거라는 걸 유추할 수 있다.

이 때, 아까 다뤘듯이 error은 J_train과 J_cv, J_test 이 3개가 있잖음. 따라서 각 d값에 따라 저 세 error가 어떻게 변할지 그래프로 그려보자.

 

이는 위와 같은 형태가 될 것이다.

d가 작은 경우는 왼쪽 그래프처럼 underfitting된 경우로, 따라서 J_train은 크다. 

d가 큰 경우는 오른쪽 그래프처럼 overfitting된 경우로, J_train은 작다. training set에 너무 fit해버렸기 때문이다.

 

그럼, J_cv 또는 J_test는 어떨까??

일단, J_cv랑 J_test는 얼추 비슷한 양상을 보일 가능성이 높다. 굳이 따지자면 J_test > J_cv일 확률이 높겠지만, 너무 간단한 test set을 사용했다면 반대일 수도 있고, 저 둘의 관계를 굳이 따지기보단 비슷한 양상을 보이는 것만 일단 확인하자.

 

d가 작은 경우는 J_train과 똑같이 J_cv는 작을 것이다.

그럼, d가 큰 경우는 어떨까??

이 때는 overfitting으로, training set에만 너무 fit해버린 나머지 generalization을 잘 하지 못해, 새로운 cv set에서는 error가 크게 나타날 것이다. 

 

우리는, d가 너무 작지도 크지도 않은, 딱 "just fit"한 저 사이 d값으로 hyperparameter을 설정해야 할 것이다. 위 예제의 경우는 해당 d는 2가 될 것이다. (optimal value for d is 2)

 

 

역으로, 만약 우리가 모델링을 해서 학습을 돌리고 J_train과 J_cv (또는 J_test)를 구했는데, J_cv (또는 J_test) 값이 크다면(즉, test set으로 예측을 돌렸을 때 error가 크게 나오면) 어떻게 해야할까??

 

이 경우는 오른쪽과 같이 J_train 값을 같이 비교할 수 있다.

J_train과 J_cv가 둘다 크면, high bias 문제 즉 underfitting된 상태라고 볼 수 있고, 

J_train는 작은데 J_cv만 크면, high variance 문제 즉 overfitting된 상태라고 볼 수 있다. 

따라서 현재 어떤 문제때문에 error가 크게 나오는 건지 분석해볼 수 있다.

 


Diagnosing bias vs. variance

이제, 아까 예제에 대해 d를 4로 고정해놓고 생각해보자.

d = 4일 때, 정규화를 따로 하지 않으면(람다 = 0) 오른쪽처럼 overfitting이었다.

 

그럼, 이 때 적당한 람다 값은 어떻게 결정하는 게 좋을까??

 

이 또한 아까 d를 구할 때와 동일하게, cv set을 이용해, J_cv가 최소화되게 만드는 람다 값을 선택한다.

 

구체적으로 보면,

 

1. 람다를 0부터 시작해 0.01, 0.02, 0.04, ..., 10.24 (약 10) (소수점 이하 3자리는 선택에 큰 영향 x)가 될 때까지 2를 곱한 각각의 람다값에 대하여, J를 최소로 만드는 쎄타(파라미터)를 구한다. 쉽게 말해 람다를 고정시키고 학습시켜 global minimum J값과 그 때의 쎄타를 구하는 과정이다.

 

2. 그렇게 구해진 각 람다에 대한 쎄타값에 대하여, J_cv를 계산하고, 그렇게 나온 J_cv들 중 가장 작은 J_cv를 만드는 람다 값을 선택한다. (위 경우는 람다 = 0.08이 선택됨)

-> 여기서 람다 결정 완료!!

 

3. 이제 주어진 람다와 그 때의 쎄타로 J_test를 계산한다.

-> J_test 계산됨.

 


이 때, cv set이 아까 d 계산할 때도, 람다 계산할 때도 둘다 필요한데, 지금은 d가 이미 주어졌단 가정 하에 람다를 구하는 데 cv set을 쓴거잖음?? 그러면 d랑 람다 둘다 정해야 할 때는 어떡하지?

 

=> GPT 답변) 

 


그래서, 람다 값에 따른 J_train과 J_cv를 그래프로 나타내면 위와 같음.

아까 d에 따른 그래프와 똑같이, J_train과 J_cv가 얼마나 차이나냐에 따라

지금 람다값을 내려야 할지(underfitting인지) 올려야 할지(overfitting인지) 결정할 수 있음.

 


Learning curves


***참고!!!

learning curves 라는 용어가 정의가 두 가지로 가능한 것 같음.

1. 학습을 시도한 횟수(에포크) 대비 error 또는 accuracy를 표현한 곡선

2. 학습에 쓰인 training set의 크기 대비 error 또는 accuracy를 표현한 곡선

 

1.번 정의에 따른 곡선은 아래와 같을 거임.

출처 : https://yhyun225.tistory.com/17

2.번 정의에 따른 곡선은 아래와 같을 거임(우리 강노).

1번과 2번 예시 둘다 많이 쓰이는 것 같아보임, 

https://yhyun225.tistory.com/17

https://273k.tistory.com/entry/Learning-Curves

위 두 블로그에선 학습 곡선을 1번 정의로 설명하고 있고, 위키백과나 나무위키 등도 그러함.

https://273k.tistory.com/entry/Learning-Curves

https://jellyho.com/blog/85/

그러나, 위 두 블로그에선 학습 곡선을 2번 정의로 설명하고 있음. 또한 사이런킷에서 learning_curve 함수를 쓰면, 2번 정의의 학습 곡선을 plot해주는 듯함.

 

일단 우리는 "둘 다 자주 쓰이는 정의고, 이 강의노트에선 2번 정의를 사용중이다" 라고만 이해하고 넘어가자.


 

이제, 위에서 말한 2번 정의에 따른 learning curves를 구해보자.

 

CASE 1. 일반적인 경우(적당히 fit)

h를 우측 위와 같이 2차 다항식 형태라고 하고, m이 오른쪽처럼 저렇게 늘어난다고 가정해보자. 이 경우 h는 늘어나는 data set에 대한 적당히 fit하다고 할 수 있다. 즉, 일반적으로 적절한 h를 설계해서 학습이 괜찮게 된 케이스이다.

 

이 경우, J_train은 우측과 같이 m = 1일 땐 0이었다가 로그적으로 증가한다. 

J_cv는 처음엔 generalization이 잘 안되어 있으니 매우 컸다가, 학습이 되면서 로그적으로 감소한다. (아마, J_test는 어차피 J_cv랑 얼추 비슷할 거니까, 그냥 J_cv로 대표해서 소개한 듯 하다.

 

이 때, J_cv가 얼추 accuracy나 performance(성능)에 반비례한다고 여길 수 있는데,

이에 따라 "training set 크기 m이 늘어날수록 모델의 성능이 좋아진다"고 생각할 수 있다.

(우리가 초반부에 다룬 대로다. data set을 단순히 더 많이 수집하면, 성능이 좋아질 수 있음.)

 

CASE 2. high bias의 경우(underfitting된 경우)

이 경우는 h를 data set에 비해 너무 단순한 형태를 써서 underfitting되게 되는 경우다. 

 

이 경우, J_train은 똑같이 로그적으로 증가하긴 하나, 애초에 data set들에 비해 h가 너무 단순하기 때문에 J_train 값 자체가 높게 형성되고, 어느정도 data set이 쌓이면 error 계산이 어차피 평균내는 거니까, J_train은 거의 증가하지 않는다.

 

이 때, J_cv도 case 1처럼 감소하긴 하나, 어느정도 수렴하게 되는 지점까지 빠르게 떨어져, 그 다음부터는 거의 감소하지 않게 된다. 즉, "m을 키워도 J_cv가 거의 감소하지 않는다" -> "m을 키워도 성능이 거의 좋아지지 않는다"로 해석된다.

 

따라서, "학습 알고리즘이 높은 편향 문제를 겪고 있다면(underfitting 될 것 같다면) 단순히 training set 크기를 키우는 것은 거의 도움이 되지 않는다"는 사실을 도출할 수 있다. 성능을 높이려면 데이터를 더 모으지 말고, 초반부에 설명한 다른 방법들을 시도해야 할 것이다.

 

다르게 보면, m이 늘어나도 어차피 계속 underfitting된 모델이 도출될 거라고 볼 수 있다.

CASE 3. high variance의 경우(overfitting된 경우)

이 경우는 h를 data set에 비해 너무 복잡한 형태를 쓰고, 작은 정규화 term(=람다) 값을 써서 overfitting 되게 되는 경우다.

 

이 경우, J_train이 증가하긴 하나, 애초에 복잡한 h를 썼기에 training set에 specialization이 잘 되기에 m이 늘어나도 J_train은 크게 늘어나지는 않는다. 

 

J_cv의 경우는 초기에 값이 큰 건 동일하고, m이 늘어나며 점점 error가 감소하긴 하나, 애초에 h가 너무 복잡했기에 J_cv가 감소하긴 하겠으나, h가 overfitting을 벗어나기까지 m이 충분히 커져야 하므로 J_cv는 비교적 천천히, 꾸준히감소하게 된다. 또한 J_cv 자체가 더 낮아질 가능성이 충분히 있기에, high bias 때와 달리 error가 수렴하게 될 지점 자체가 아래쪽에 잡히게 된다.

 

따라서 이렇게 "학습 알고리즘이 high variance 문제를 겪고 있으면(overfitting될 것 같다면) training set의 크기를 키우는 게 도움이 될 수 있다"라는 사실을 도출할 수 있다. just fit 때와 결론은 마찬가지이다.

 

즉, m이 충분히 커지기 전까지는 계속 overfitting된 모델이 도출되겠지만, m이 많이 커지면 overfitting을 어느정도씩 벗어날 것 아닌가? 그러면 J_train도 당연히 천천히 늘어나겠고, 따라서 학습이 조금씩 더 잘 될 것이므로 J_cv도 유의미하게 감소한다.

 

 


Deciding what to try next

초반부에 설명했던 "우리가 큰 error을 맞닥뜨렸을 때 할 수 있는 것들"에 대해, 그럼 각각이 어떤 문제를 해결해줄 수 있을까?

정리하면 아래와 같다.

1. 트레이닝 예제를 더 모은다

-> high variance를 해결함. (high bias에는 거의 도움이 안됐음.)

2. 피쳐 개수를 줄인다

-> high variance를 해결함.

3. 피쳐를 추가한다(피쳐 개수를 늘린다)

-> high bias를 해결함.

4. 피쳐를 고차 poly 형태로 써본다

->high bias를 해결함.

5. 람다를 줄인다

->high bias를 해결함.

6. 람다를 늘린다

-> high variance를 해결함.

 


 

신경망에서의 underfitting과 overfitting

이제 신경망에서는 어떨지 간단히 살펴보자.

 

우선, 일반적으로는 파라미터 수가 많을수록(=노드 개수가 많을수록) 신경망의 성능은 더 좋아진다. 

그러나, 당연하게도 학습에 쓰이는 비용(시간이나 자원 등등...)도 증가하게 된다.

 

그리고, 왼쪽과 같이 파라미터 수가 적으면 underfitting에 취약할 수 있다.

반면에 오른쪽과 같이 파라미터 수가 많으면 overfitting에 취약할 수 있다.

 

비용만 감당할 수 있다면, 여러 은닉층과 은닛층 유닛(노드)들을 통해 많은 파라미터를 가져가고,

또한 발생 가능한 overfitting 문제를 정규화 term 람다를 사용해 해결하면 좋은 성능의 신경망을 얻을 수 있을 것이다.

 

 

그러면, 은닉층을 몇 개 두는게 좋을지는 어떻게 알까?? 은닉층을 1 2 3개 몇개 두지??

일단 기본적으로는 단일 은닉층을 두는게 합리적이긴 하나, 여러 은닉층을 선택하고 싶다면?

 

우리가 차수 d나 람다 구할 때 썼던 기법대로, 각 은닉층이 1개 2개 3개 ... 일 때의 J_cv를 구하고, 가장 작은 J_cv를 만드는 은닉층 개수를 선택하는 방법을 써볼 수 있다. 그리고 그 때 구해낸 파라미터들을 실제로 사용하면 되겠다.

 

 

퀴즈 문제 하나가 괜찮아보여서 가져와봤다.

 

풀이) 지금 J_cv가 J_train에 비해 많이 큰 상황이면, 일반화에 실패한 overfitting된 상태임. 이 때는 feature 수를 늘리는 게 아닌 감소시켜야 성능 향상에 도움이 될 수 있음.

은닉층 유닛을 늘린다는 건 파라미터 개수를 늘리는 거고, 결국 feature 개수를 늘리는 거임. 이건 underfitting일 때나 도움이 되지 overfitting된 현재 상황엔 좋은 해결방안이 아님. 따라서 답은 4번.