1. 검증(validation data)셋을 만드는 목적
- 열심히 학습데이터를 만들고(데이터 + y값/정답/라벨)
- 모델(학생)을 학습(train)데이터로 훈련을 시켰는데
- 얘가 실제 수능에서 잘할지 못할지 알수 없어
- 왜냐하면 현실세계는 아직 (y값/정답/라벨)을 만들지 못한 불확실한 데이터 이기에 그걸로 시험해서는 결과가 잘 나오는지 안 나오는지 채점할 수가 없어
- 또한 모의고사는 여러번 시험쳐도 피해가 없지만, 수능(실제 현실세계)은 한번 떨어지면 손해가 막심하거든
- 그래서 모의고사를 치르도록 하는 거야 그러기 위하여는
- 학습(훈련)한 내용 외에 한번도 보지 못한 문제를 풀어보게 해서 객관적으로 학생(모델)의 실력을 측정하려는 거지
- 방법은 모아둔 문제(데이터)와 정답(y값/라벨)을 쪼개서 하나는 훈련(학습)에만 사용하고 다른 하나는 모의평가 용지로 사용하여 모델의 역량을 측정할 수 있지
- 그거 없이 풀었던 문제(학습 데이터)만 계속 푸는 걸로 모델을 사전평가해버리면, 아예 문제도 안보고 순서대로 답만 외워버리지 않겠니? 그리고 실제 수능가면 빵점 맞고
2. 검증(validation data)셋이 필요한 이유
- 평가(test)은 정답(y값, label)이 없다는 전제야
- 그 말인 즉은, 문제를 풀어도 총점은 알 수 있지만
- 어떤 과목에서 어떤 문제를 얼마나 틀렸는지 알 수 없고
- 오답노트를 만들 수가 없어
- 다시 말해서, 모델(학생)이 어떤 문제를 어려워하는지 알수 없어서, 학습이 아닌 시험(평가, test data)에서 잘하기 위해서 어떻게 학습계획을 짜야하는지 알 수가 없는거야
- 그래서 검증셋은,
- 시험(test)문제와 최대한 유사하면서
- 학생이 학습 기간 중에는 한번도 보지 못한 문제들로 이루어져야 해
- 정답이 없는 평가를 대비해, 정답이 있는 가상의 평가문제를 만든게 검증데이터야
- 만약에 검증셋에 있던 데이터들이 학습셋에 포함되어서 모델이 검증셋을 엄청 잘풀게 된지만(이미 학습했기에) 실제 현실 문제에서는 낮은 성능을 갖게 되는 문제를 데이터 누수(Data Leakage)가 발생했다고 해. 엄격하게 분리되어야 할 시험(평가)문제가 훈련데이터 묶음으로 누수되었다고 하는 거야.
3. 검증셋 만들어도 평가셋 성능 안 오르던데?
- 검증셋은 말그대로 모의고사야. 모의고사는 사전 준비하는 것일 뿐 실제 수능시험에서 잘 본다는 것과는 전혀 상관없어
- 특히 모의고사(검증셋)을 봐서 86점을 맞았다고 치자
- 내가 틀리는 14점에 연관된 문제를 찾아내서, 그 개념을 익히고, 연습문제를 풀어봐서 내가 취약한 부분을 보완하지 않으면 모의고사 100만번 보아도 너는 영원히 86점이야.
- 그래서 검증셋은 만들어서 모델이 추론해본다고 해도 그거 자체만으로는 아무 쓸모 없는 거야. 그걸 보고 나서 회고를 잘 해야지.
4. 검증셋 만들어서 그럼 뭘 할 수 있는거야?
1) 모델의 과적합(학습이 아닌 문제 암기) 방지
- 기존 고정된 학습(train) 데이터가 모델 성능향상에 얼마나 효과적인지 알 수 있어
- 같은 훈련셋만 가지고 모델에 학습시키면 일정한 횟수를 넘어가면 정확도가 100점에 가까와져. 사실 그건 같은 문제를 풀었기 때문이지 모델이 실력이 좋아서가 아니거든.
- 그래서 한번도 안 풀어보면서도 정답이 있는 검증셋을 만든 거야.
- 훈련데이터셋을 만들어놓고, 모델이 100번 학습하게 해. 그리고 매번 학습할 때마다 검증셋(모의고사) 시험을 보게 해서 그 결과를 확인해봐. 어느 순간까지는 검증셋 점수가 계속 오르다가 어느 시점부터는 더 이상 오르지 않고 오히려 떨어지는 순간이 되. 이걸 모델이 과적합(Overfitting)되었다고 해. 이 때 모델의 훈련을 멈춰야 해. 이걸 Early Stopping이라고 해.
- 훈련을 반복했는데 점수가 떨어지니까 이상하지? 모델은 사람이 아니라 확률에 의해 스스로를 발전시키는데, 처음에는 학습데이터의 패턴을 학습하며 진정한 학습을 진행했다면, 나중가면 꾀가 생겨서 이미 풀어본 문제의 답만 제출하는 방식으로 잔꾀만 부리니 실제 시험에서 처음 본 문제를 보게 되면 실력이 없으니까 오히려 점수가 떨어지게 되는 거지. 그래서 적절한 학습데이터 반복횟수에서 중단(Early Stopping)을 하는 거야.
- 가지고 있는 훈련데이터를 1번 반복하는 것을 인공지능에서 EPOCH(에픽,이팍,에폭)이라고 해. 검증셋을 돌려보면 학생에게 훈련셋을 몇번까지 훈련시키는 게 한번도 안 본(unseen) 문제를 잘 푸는데 최적의 훈련횟수(EPOCH)인지를 알 수 있게 되지
- 이렇게 모델(학생)이 공부한 문제 뿐만 아닌 안 본(unseen) 문제도 잘 풀 수 있게 만드는 것을 모델을 강건하게(robust) 만든다, 혹은 모델의 일반화 성능(generalization performance)을 향상시킨다고 해
- 경진대회 같은 곳에서는, 참가자들이 주어진 데이터에만 정답암기 후 찍기 형식으로 모델을 학습시키고 실제 현실 데이터에서는 부실한 모델을 훈련시켜서 우승하는 것을 방지하기 위해 평가데이터를 보여주지 않기도 해.
2) 모델의 취약점 개선 방안 도출
- 또 한가지 중요한 장점은, 학생(모델)이 어떤 부분에 취약한지를 알게 되는 거야.
- 검증셋을 풀었던 결과를 총점이 아닌 개별 문항을 보면서 분석하게 되면 이 아이(모델)가 어떤 부분이 약한지 알 수 있겠지? 분수, 함수, 그래프, 동사, 명사, reading 등
- 그 취약점 목록을 가지고 오답노트도 만들고 다음 번 학습 때 맞은 거는 안 보고 틀린 문제의 패턴들만 주구장창 학습시키는 거야
- 그럼 해당 개념을 숙지하고 학습하면 다음번에는 그 패턴들은 다 맞을 수 있겠지?
- 이렇게 검증셋은 검증셋을 풀어본것만 가지고는 학생의 실력향상과 무관하고, 그 결과를 꼼꼼히 분석해서 오답노트를 만들어 재학습을 완벽히 하였을 때만 의미가 있어.
3) 검증셋 뿐 아니라 훈련셋에 대해서도 시험을 봐
- 훈련셋은 모델이 이미 학습한 문제이기는 해서, 그걸 많은 횟수(EPOCH)를 공부하면 자연히 그 문제를 풀게되면(추론, inference) 점수가 잘 나오는 건 불을 보듯 뻔하지. 그렇다면 훈련셋을 공부(훈련)만 하지 다시 추론(문제풀기)할 필요가 있을까?
- 응 있어. 훈련셋을 다 풀 수 있나가 궁금해서 보는게 아니라, 훈련셋 공부를 완성했는지 여부를 알 수 있어. 1번 보고 추론해서는 점수가 낮지만 13번 봐서는 만점에 가까운 점수가 나왔고 14번 이후에는 계속 만점이라면, 더 이상 공부할 필요가 없어지는 거지.
- 모델이 수차례 학습할 동안 매차례 학습 후에 모델보고 풀어보게 하면, 얘가 이미 봤던 문제긴 하지만, 그 문제라도 제대로 풀 수 있는지 아니면 아직 학습이 덜 되었는지 알 수 있기는 하잖아?
- 이렇게 학습이 덜 된 상태를 과소적합(Underfitting)되었다고 해. 즉 주어진 학습데이터를 아직 덜 공부했다 이 말이야. 이 때는 아직 주어진 문제들도 다 공부하지 못해서, 검증셋 평가점수를 비교해 가면서, 얼마나 훈련셋을 모델에게 더 훈련시켜야 할지를 결정하게 돼.
- 이렇게 Overfitting과 Underfitting의 중간에 모델이 훈련셋을 학습하는 최적의 횟수를 결정하는 것이 모델 학습 계획 수립에 있어서 굉장히 중요해
5. 검증셋 만들 때의 장점
- train만 가지고 열심히 무작위로 공부해도 실력은 오르겠지만
- 미리 검증(val(validation dataset))을 한번 해가면서 학습을 하면 데이터에 기반한 정확한 분석을 통해 체계적으로 학습하여 효과적으로 실력을 올릴 수 있어
- 데이터에서 취조하면 다 나온다는 말이 있데. 데이터를 요리잡고 조리잡고 돌리고 고문하면 문제를 풀 수 있는 통찰력이 나온다는 말이래.
- 그런데 이 모델이 잘 성장(훈련)되었는지 아닌지 알려면 이 모델을 평가를 해야 하잖아? 그런데 test셋은 실제 평가라 정답이나 시험 분석데이터를 받을 수가 없으니, 궁여지책으로 가지고 있던 예를 들면 100문제 중에 20문제를 소중히 따로 꿍쳐놓고 보지 않고 있다가 미리 풀어보고, 정답(사전)과 비교해 보고 모델을 개선시킬 실마리를 찾을 수 있지.
6. 검증셋 만들 때 고려할 점은?
- 모델을 만드는 최종 목표가 평가셋(test set)에서 잘 하는 모델을 만드는 것이지?
- 하지만, 평가셋은 라벨이 없어서 모델 평가 및 개선작업을 못하니까 검증셋을 만드는 거거든.
- 그래서 검증셋을 만들 때 가장 중요한 점은, 먼저 평가셋을 살펴보고 시험셋과 동일한 데이터 속성을 가지도록 검증셋을 만들어야 해
- 가능하면 시험셋을 평가할 때 사용되는 평가방법도, 검증셋 시험할 때 동일하게 사용하면 좋아
- 단 검증셋과 평가셋이 다른 점은 평가셋은 정답이 없다고 한다면, 검증셋은 내가 기대하는 정답값을 갖고 있는 거야.
- 그래서 검증셋과 평가셋을 동일하게 만들었다면, 이제 이 검증셋과 시험셋을 잘 풀 수 있는 모델을 훈련시키기 위해서 훈련 데이터셋을 역시 검증셋과 시험셋 문제와 동일하게 만들어주는 거야. 그리고 검증셋과 평가셋에서 모델이 만들어주었으면 하는 수준의 결과물, 즉 정답(혹은 y값, 라벨)을 훈련셋에 함께 만들어주어서 이 모델이 검증셋과 평가셋 문제를 보았을 때 동일한 수준의 결과물을 만들어주도록 유도하는 거야.
- 다시 정리하면, 1) 최종목적지인 내가 풀어야 할 문제인 평가셋을 먼저 살펴보고, 2) 그걸 잘 할 수 있을지 측정하기 위해 검증셋과 정답을 준비하고, 3) 그 문제를 모델이 풀 수 있도록 훈련시키기 위해 훈련셋과 정답을 준비하는 거야.
7. 검증셋 없이 평가셋으로 바로 테스트해도 되잖아?
- 경진대회 같은 특수한 상황만 보면 그렇게 생각할 수 있어
- 경진대회 같은 일반적이지 않은 상황에서는 평가(test)셋에 대한 레이블이 있고, 그 레이블을 참가자 입장에서 볼 수는 없지만 리더보드에 제출하면 점수를 알 수 있지
- 하지만 몇 가지 아쉬운 점이 있어
- 에러분석 못함
- 리더보드 제출을 하면 총점을 알 수 있지만, 세부내역에 대한 채점결과는 알지 못해. 예를 들어 내가 어느 부분에서 틀렸는지, 틀린 부분의 패턴은 어떠한지, 틀린 부분만 집중적으로 재교육하려면 어떻게 모델링을 설계해야 할지
- 최적의 하이퍼파라미터를 추출할 수 없어.
- 하이퍼파라미터는 epoch, batch size, learning rate 등 실험을 어떻게 할 것인지 정할 때 쓰는 변수들이야. 어떤 값을 써야 최적의 모델이 탄생할 것인지에 영향을 주지.
- 1부터 100까지라고 하면, 변수가 3개면 100 X 100 X 100번 1,000,000 번의 실험이 필요해.
- 평가셋 점수를 아무리 리더보드에 제출해서 알 수 있다고 하고 하루에 12번씩 (Upstage AI Lab 기준) 할 수 있다고 해도 충분하지가 않다. 이걸로만 하려면 정말 주먹구구식으로 할 수 밖에 없고, 체계적이고 과학적인 최적의 하이퍼파라미터 값을 추출할 수가 없는 거지.
- 훈련셋에 대한 성능측정은 정확하지 않아. 이미 본 데이터를 가지고 추론하는 거니까.
- 그래서 결국 모델이 제대로 잘하고 있나를 보려고 하면 데이터 누수(Data Leakage: 모델이 보지 말아야 할 데이터가 모델에게 노출되어 검증의 유효성이 훼손된 경우)된 훈련셋 제외하고, 확인(제출)횟수가 제한되어있는 평가셋 제외하고는 검증셋만 쓸 수 있는 거지.
8. 어떻게 나누는거지? 손으로 하는 거야?
-
사이킷런(Scikit Learn, sklearn)이라는 라이브러리(library, 미리 만들어 놓은 파이썬 함수 묶음)를 사용하면 저절로 알아서 나눠줘.
-
hold-out(따로 떼어두기) 방식은 1회만 validation 검증셋을 따로 떼어놓는 건데 그러면 특정 데이터의 패턴에 대해서만 검증이 되지? 그래서 cross validation(교차검증)은 여러번 validation 검증셋을 떼어놓아서 모든 훈련데이터의 부분에 대해서 다 검증을 하는 거야. 데이터 양이 많으면 그냥 hold-out 방식으로 하기도 하고 데이터가 적거나 편향을 줄이고 싶을 때는 cross-validation을 쓴데. 그래서 여러 시나리오에 대해서 모델을 훈련시키니 훈련시간은 더 오래걸리는 건 감안해야겠지?
hold-out 방식 (따로 떼어두기) (검증셋을 한번만 분류)
- train_test_split 라이브러리
from sklearn.model_selection import train_test_split as tts
X_train, X_test, y_train, y_test = tts
(*arrays, test_size=None, train_size=None,
random_state=None, shuffle=True, stratify=None)
- shuffle은 섞는 다는 건데 default는 True지만 False로 하면 섞지 않는다는 거야
- random_state은 섞을 때 섞는 방식을 결정하는 기계에 어떤 방식으로 섞을지를 결정하는 숫자를 넣는거야 (예:42). 똑같은 숫자를 넣으면 똑같은 결과가 나오겠지? 예: Shuffle=True, random_state=42.
- 그런데 Shuffle=False로 하면 아예 섞지를 않으니 random_state값은 의미가 없으니 쓸 필요가 없어.
- stratify=y로 하면 종류별로 공평하게 나누는 거야. 예를 들어 고양이와 개 사진이 각각 50장씩 합해서 100장이 있는데, 이걸 8:2로 나누는데 고양이 50+ 개30을 80으로 나누고, 나머지 20장을 개 사진으로만 남기면, 고양이에 대해서는 모델이 잘하는지 못하는지 성능평가가 안 되겠지? 그래서 stratify를 적용하면 개 25장, 고양이 25장을 훈련셋으로, 나머지 50장을 검증셋으로 비율을 균등하게 나누는 거야.
cross-validation 방식 (교차검증) (여러번 나누어서 모든 데이터에 대해서 검증셋으로 적용하기)
- KFold 라이브러리
from sklear.model_selection import KFold
KFold(n_splits=2, random_state=None, shuffle=False)
- 한 데이터를 K개로 나누는 거야. 예를 들어 100개의 데이터를 5(여기서는 K)개로 나눈다면 20, 20, 20, 20, 20개로 나누는 거야. 그리고 다섯개가 번갈아가면서 한 번씩 검증셋 역할을 하는 거고, 나머지 4개의 20개씩 데이터로 훈련(학습)을 하는 거지.