본문 바로가기
스터디/혼공학습단 10기 - 자바 & 머신러닝

[혼공학습단] 3주차 - 최적의 에포크값, 내 맘대로 찾아보기

by 찌노오 2023. 7. 23.

 

 

 

 

선택미션

주차 진도 선택 미션
3주차 Chapter 04 Ch.04(04-2) 과대적합/과소적합
손코딩 코랩 화면 캡처하기

 

에포크와 과대/과소적합

잠시 책에 나온 내용을 떠올려보면 에포크 횟수가 적으면 모델이 훈련세트를 덜 학습하고 반대로 에포크 횟수가 많으면 훈련세트를 많이 학습하게 된다. 

 

다시 경사하강법에서는 에포크의 횟수에 따라 강도가 정해지는데, 무조건 많이 한다고 좋은 것이 아니다.

왜냐하면 과대적합(overfitting)이 일어날 수 있기 때문이다. 그러나 우리는 어떤 값에서 과대적합이 시작하는지 모르기 때문에 이를 실습해보기로 한다.

 

그 전에 필요한 코드는 미리 입력해둔다.

(여기서 random_state에 42를 입력하는 이유는 머신러닝 학습 결과를 책과 동일하게 맞추기 위함이다.)

import pandas as pd

fish  = pd.read_csv('http://bit.ly/fish_csv_data')

#Speices 열을 제외한 나머지 5개는 입력 데이터로 사용
fish_input = fish[['Weight', 'Length', 'Diagonal', 'Height', 'Width']].to_numpy()
fish_target = fish['Species'].to_numpy()

#train_test_split()함수를 사용하여 훈련세트와 데이터세트 나누기
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)

#SGDClassifier
from sklearn.linear_model import SGDClassifier
sc = SGDClassifier(loss = 'log', max_iter = 10, random_state = 42)

 

 

그래프(matplotlib)로 접근해보기

아래 손코딩은 300번의 에포크 동안 훈련을 반복해 훈련세트와 테스트 세트의 점수를 그래프로 그려본 코드이다.

 

 y축이 정확도(점수), x축이 에포크(횟수)를 나타내고,

파란선이 훈련세트 그래프, 주황선이 테스트 세트 그래프를 나타낸다.

 

직관적으로 보면 알 수 있듯이 100번 쯔음이 훈련세트와 테스트세트의 점수 차이가 적다.

이는 해당 에포크 수가 과소/과대적합되지 않는 적절한 값이라고 볼 수 있다.

 

 

데이터프레임(pandas)으로 접근해보기

 

이렇게 끝내면 재미없어서 좀 더 나아가보기로 했다.

그래프는 직관적이지만, 에포크 값을 선택할 때 실수할 가능성도 있기 때문에 새로운 방법으로 접근해보기로 했다.

 

epoch 횟수를 나타내는 값과 train_score와 test_score의 차이를 절대값으로 바꿔준 값을 추가로 리스트로 만들었다.

이를 다시 데이터프레임으로 바꿔주고 5개 행만 출력해보았다.

 

숫자(값)으로 나오니까 심신이 안정되는 느낌이다.

 

물론 이렇게 해서는 아무것도 할 수 가 없으니,

나만의 기준으로 여러가지 데이터셋을 출력해본다.

 

다음 300번의 에포크 동안 쌓인 데이터를 3가지 기준으로 출력해보았다.

1. 훈련세트의 내림차순 5개 행

2. 테스트세트 내림차순 5개 행

3. 갭이 가장 작은 에포크값 5개 행

 

아래는 그 코드와 값이다.

 

에포값이 멀리 떨어진 각자만의 값을 가지고 온다.

 

마지막의 '갭'만 보면 에포크횟수는 50번 미만이 더 최적인가 하는 생각이 든다.

아니면 테스트세트를 보면 적당히 갭은 인정하고 60~70번이 적당한가? 하는 생각도 든다.

그럼에도 test_score는 아무리 올라도 0.925를 넘지 않기에 70번에서 ~ 200번 사이쯤이 낫겠다라는 추론도 가능할 것 같다.

 

오히려 더 미궁에 빠진 기분이다.

아무튼 '숫자로 나열해서 보는 게 더 나을 거야'하는 생각이 바뀌었다. 

 

 

조기종료(early stopping)로 접근해보기

 

마지막 방법은 책에서 제시한 조기종료(early stopping)방법으로 접근해보는 것이다.

chat GPT의 도움을 받아 최적의 에포크를 찾는 코드를 구현했다.

 

에포크를 반복할 때마다 테스트세트의 점수의 향상이 있는지를 판단하고,

미리 설정한 기준 횟수를 초과하면 에포크를 종료하고 최소 에포크 값을 반환하는 코드이다.

 

아래는 허용기준치를 100으로 잡았다.

그러니까 에포크 100번 동안 성능향상이 없다면, 마지막 성능향상 에포크를 반환한다.

import numpy as np
sc = SGDClassifier(loss='log', random_state=42)
train_score = []
test_score = []
classes = np.unique(train_target)

#에포크 값
best_val_score = 0.0
best_epoch = 0
tolerance = 100  # 성능 향상이 없는 에포크 횟수를 기준으로 설정

for epoch in range(300):
    sc.partial_fit(train_scaled, train_target, classes=classes)
    train_score = sc.score(train_scaled, train_target)
    val_score = sc.score(test_scaled, test_target)

    if val_score > best_val_score:
        best_val_score = val_score
        best_epoch = epoch
    elif epoch - best_epoch > tolerance:
        print(f"조기종료 에포크 {epoch}")
        break

print(f"최적의 에포크: {best_epoch}, 최적의 유효점수: {best_val_score:.4f}")

 

코드를 실행해보면,

167번째에서 에포크가 종료되었고 최적의 에포크는 66번이 된다.

 

아까 갭(train score -  test score)이 가장 작았던 구간의 에포크와 비슷하게 떨어진다.

 

마무리

사실 여러 방법을 해보니 오히려 그래프를 그려서 빨리 찾는 게 낫겠다라는 생각이 든다.

하지만, 이런 삽질이 쌓이면 다 도움이 되지 않을까하며 포스팅을 마친다.

 

 

 

 

 

** 사실과 다른 내용이 있을 수 있습니다. 언제든지 피드백 부탁드립니다!

반응형

댓글