🔥알림🔥
① 테디노트 유튜브 - 구경하러 가기!
② LangChain 한국어 튜토리얼 바로가기 👀
③ 랭체인 노트 무료 전자책(wikidocs) 바로가기 🙌
④ RAG 비법노트 LangChain 강의오픈 바로가기 🙌
⑤ 서울대 PyTorch 딥러닝 강의 바로가기 🙌

4 분 소요

조기 종료(Early Stopping)는 학습시 일정기간(여기서 기간은 보통 N번의 Epoch을 기준으로 합니다)동안 Loss 나 Score 기준으로 개선이 일어나지 않으면 학습을 조기에 종료해 주는 기능입니다. 만약, 20번의 Epoch 동안 학습이 진행한다고 가정했을 때, 아래 그림처럼 Epoch=5 이후에 지속해서 Loss가 상승하면서 개선이 안 일어나면 굳이 남은 학습을 지속할 이유가 없습니다.

early-stopping

이럴때 조기종료 기능으로 중간에 N번의 Epoch 동안 개선이 일어나지 않으면 종료해 주면 됩니다. 여기서, “개선이 일어나지 않은 N번의 Epoch”patience 라고 합니다.

조기종료(EarlyStopping) 코드 구현

import numpy as np

class EarlyStopping:
    def __init__(self, patience=3, delta=0.0, mode='min', verbose=True):
        """
        patience (int): loss or score가 개선된 후 기다리는 기간. default: 3
        delta  (float): 개선시 인정되는 최소 변화 수치. default: 0.0
        mode     (str): 개선시 최소/최대값 기준 선정('min' or 'max'). default: 'min'.
        verbose (bool): 메시지 출력. default: True
        """
        self.early_stop = False
        self.patience = patience
        self.verbose = verbose
        self.counter = 0
        
        self.best_score = np.Inf if mode == 'min' else 0
        self.mode = mode
        self.delta = delta
        

    def __call__(self, score):

        if self.best_score is None:
            self.best_score = score
            self.counter = 0
        elif self.mode == 'min':
            if score < (self.best_score - self.delta):
                self.counter = 0
                self.best_score = score
                if self.verbose:
                    print(f'[EarlyStopping] (Update) Best Score: {self.best_score:.5f}')
            else:
                self.counter += 1
                if self.verbose:
                    print(f'[EarlyStopping] (Patience) {self.counter}/{self.patience}, ' \
                          f'Best: {self.best_score:.5f}' \
                          f', Current: {score:.5f}, Delta: {np.abs(self.best_score - score):.5f}')
                
        elif self.mode == 'max':
            if score > (self.best_score + self.delta):
                self.counter = 0
                self.best_score = score
                if self.verbose:
                    print(f'[EarlyStopping] (Update) Best Score: {self.best_score:.5f}')
            else:
                self.counter += 1
                if self.verbose:
                    print(f'[EarlyStopping] (Patience) {self.counter}/{self.patience}, ' \
                          f'Best: {self.best_score:.5f}' \
                          f', Current: {score:.5f}, Delta: {np.abs(self.best_score - score):.5f}')
                
            
        if self.counter >= self.patience:
            if self.verbose:
                print(f'[EarlyStop Triggered] Best Score: {self.best_score:.5f}')
            # Early Stop
            self.early_stop = True
        else:
            # Continue
            self.early_stop = False

예시 데이터 (Epoch 별 Loss)

losses = [
    0.1,  # save
    0.25, # patience #1
    0.33, # patience #2
    0.09, # save
    0.4,  # patience #1
    0.08, # save
    0.5,  # patience #1
    0.55, # patience #2
    0.6,  # patience #3 <== Early Stop
    0.05, 
    0.89, 
    0.99 
]
es = EarlyStopping(patience=3, 
                   delta=0, 
                   mode='min', 
                   verbose=True
                  )
for loss in losses:
    es(loss)
    # Early Stop Check
    if es.early_stop:
        break
[EarlyStopping] (Update) Best Score: 0.10000
[EarlyStopping] (Patience) 1/3, Best: 0.10000, Current: 0.25000, Delta: 0.15000
[EarlyStopping] (Patience) 2/3, Best: 0.10000, Current: 0.33000, Delta: 0.23000
[EarlyStopping] (Update) Best Score: 0.09000
[EarlyStopping] (Patience) 1/3, Best: 0.09000, Current: 0.40000, Delta: 0.31000
[EarlyStopping] (Update) Best Score: 0.08000
[EarlyStopping] (Patience) 1/3, Best: 0.08000, Current: 0.50000, Delta: 0.42000
[EarlyStopping] (Patience) 2/3, Best: 0.08000, Current: 0.55000, Delta: 0.47000
[EarlyStopping] (Patience) 3/3, Best: 0.08000, Current: 0.60000, Delta: 0.52000
[EarlyStop Triggered] Best Score: 0.08000

(옵션) patience

  • loss or score가 개선된 후 기다리는 기간

  • default: 3

예시 데이터 (Epoch 별 Loss)

losses = [
    0.1,  # save
    0.25, # patience #1
    0.33, # patience #2
    0.09, # save
    0.4,  # patience #1
    0.08, # save
    0.5,  # patience #1
    0.55, # patience #2
    0.6,  # patience #3 
    0.05, # save
    0.89, # patience #1 
    0.99  # patience #2 
]

기본 사용 방법

es = EarlyStopping(patience=5, 
                   delta=0, 
                   mode='min', 
                   verbose=True
                  )

Training Loop 안에서 es(loss) 를 실행 후 early_stop 여부를 체크합니다.

for loss in losses:
    es(loss)
    # Early Stop Check
    if es.early_stop:
        break
[EarlyStopping] (Update) Best Score: 0.10000
[EarlyStopping] (Patience) 1/5, Best: 0.10000, Current: 0.25000, Delta: 0.15000
[EarlyStopping] (Patience) 2/5, Best: 0.10000, Current: 0.33000, Delta: 0.23000
[EarlyStopping] (Update) Best Score: 0.09000
[EarlyStopping] (Patience) 1/5, Best: 0.09000, Current: 0.40000, Delta: 0.31000
[EarlyStopping] (Update) Best Score: 0.08000
[EarlyStopping] (Patience) 1/5, Best: 0.08000, Current: 0.50000, Delta: 0.42000
[EarlyStopping] (Patience) 2/5, Best: 0.08000, Current: 0.55000, Delta: 0.47000
[EarlyStopping] (Patience) 3/5, Best: 0.08000, Current: 0.60000, Delta: 0.52000
[EarlyStopping] (Update) Best Score: 0.05000
[EarlyStopping] (Patience) 1/5, Best: 0.05000, Current: 0.89000, Delta: 0.84000
[EarlyStopping] (Patience) 2/5, Best: 0.05000, Current: 0.99000, Delta: 0.94000

(옵션) delta

  • 개선시 인정되는 최소 변화 수치

  • default: 0.0

예시 데이터 (Epoch 별 Loss)

losses = [
    0.1,  # save
    0.25, # patience #1
    0.33, # patience #2
    0.09, # 개선은 되었으나 delta=0.02 이상 줄이지 못하였으므로.. Early Stop!
    0.4,  
    0.08, 
    0.5,  
    0.55, 
    0.6,  
    0.05, 
    0.89, 
    0.99  
]
es = EarlyStopping(patience=3, 
                   delta=0.02, 
                   mode='min', 
                   verbose=True
                  )
for loss in losses:
    es(loss)
    # Early Stop Check
    if es.early_stop:
        break
[EarlyStopping] (Update) Best Score: 0.10000
[EarlyStopping] (Patience) 1/3, Best: 0.10000, Current: 0.25000, Delta: 0.15000
[EarlyStopping] (Patience) 2/3, Best: 0.10000, Current: 0.33000, Delta: 0.23000
[EarlyStopping] (Patience) 3/3, Best: 0.10000, Current: 0.09000, Delta: 0.01000
[EarlyStop Triggered] Best Score: 0.10000

(옵션) mode

  • 개선시 최소/최대값 기준 선정

  • 선택 가능 옵션: 'min', 'max'

  • default: 'min'

Epoch별 Score (예시)

scores = [
    0.1,  # save
    0.25, # save
    0.33, # save
    0.09, # patience #1
    0.4,  # save
    0.08, # patience #1
    0.5,  # save
    0.55, # save
    0.6,  # save
    0.05, # patience #1
    0.89, # save
    0.99  # save
]
es = EarlyStopping(patience=3, 
                   delta=0.0, 
                   mode='max', 
                   verbose=True
                  )
for score in scores:
    es(score)
    # Early Stop Check
    if es.early_stop:
        break
[EarlyStopping] (Update) Best Score: 0.10000
[EarlyStopping] (Update) Best Score: 0.25000
[EarlyStopping] (Update) Best Score: 0.33000
[EarlyStopping] (Patience) 1/3, Best: 0.33000, Current: 0.09000, Delta: 0.24000
[EarlyStopping] (Update) Best Score: 0.40000
[EarlyStopping] (Patience) 1/3, Best: 0.40000, Current: 0.08000, Delta: 0.32000
[EarlyStopping] (Update) Best Score: 0.50000
[EarlyStopping] (Update) Best Score: 0.55000
[EarlyStopping] (Update) Best Score: 0.60000
[EarlyStopping] (Patience) 1/3, Best: 0.60000, Current: 0.05000, Delta: 0.55000
[EarlyStopping] (Update) Best Score: 0.89000
[EarlyStopping] (Update) Best Score: 0.99000

댓글남기기